ruvnet / ruvnet/ruflo

ruflo doctor leaves an orphaned WAL sidecar that blocks all subsequent memory writes

Open
#3,161 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
72.8k
Forks
8.6k
Avg merge
2d 23h
Merged PRs (30d)
82

Description

# `ruflo doctor` leaves an orphaned WAL sidecar that blocks all subsequent memory writes

**Package:** `ruflo@3.38.20` (`@claude-flow/cli@3.38.20`)
**Repo:** https://github.com/ruvnet/claude-flow — file an issue at https://github.com/ruvnet/claude-flow/issues
**Related:** #2735 (the WAL-sidecar safety gate), #3024 (native bridge disabled on Windows)

## Summary

`ruflo doctor` opens the memory database with a native better-sqlite3 WAL
connection and exits without checkpointing, leaving a **0-byte** `memory.db-wal`
and a `memory.db-shm` on disk. The #2735 safety gate treats the mere *presence*
of those sidecars as proof of a live native writer, so every later
`ruflo memory store` in that project is refused — permanently, until the
sidecars are removed by hand.

The gate's own docstring states the assumption it relies on:

> sidecars ... removed only on the last connection's clean close

`doctor` violates that assumption. The sidecars are orphans, not evidence of a
live holder, so the heuristic misfires and the CLI reports a condition that is
not true.

## Environment

| | |
|---|---|
| ruflo | 3.38.20 |
| Node | v24.20.0 |
| npm | 11.19.0 |
| OS | Windows 11, 10.0.26200, win32 x64 |
| Install | local (`npm install ruflo@3.38.20`); also reproduces via `npx ruflo@latest` |

Reproduces with the background daemon **stopped**, so the daemon is not involved.

## Reproduction

```bash
# 0. start from a checkpointed database (no sidecars)
rm -f .swarm/memory.db-wal .swarm/memory.db-shm

# 1. a write succeeds
ruflo memory store --namespace repro --key antes --value v
# -> [OK] Data stored successfully

# 2. run doctor
ruflo doctor >/dev/null 2>&1
# -> leaves wal=0 bytes, shm=32768 bytes

# 3. the identical write now fails
ruflo memory store --namespace repro --key depois --value v
```

Step 3 output:

```
[ERROR] memory database has an active native WAL connection (found -wal/-shm
sidecar files) — refusing an unsafe sql.js whole-image write. Retry once the
native writer completes, or restore the native better-sqlite3 bridge.
Bridge unavailable: AgentDB native bridge disabled on Windows after #3024;
set CLAUDE_FLOW_ENABLE_NATIVE_BRIDGE_ON_WINDOWS=1 to opt in
```

Deleting the two sidecars restores writes immediately. No other command in the
CLI surface leaves them behind: bisecting `doctor`, `config list`, `status`,
`mcp status`, `daemon status`, `agent list`, `swarm status`, `session list` and
`hooks list` against a cleaned database, only `doctor` does.

## Evidence that no writer is actually live

A `wal_checkpoint(TRUNCATE)` against the same database, at the moment the write
is being refused, reports no contention and removes both sidecars:

```
{"busy":0,"log":0,"checkpointed":0}
```

`busy: 0` is SQLite reporting that no other connection holds the WAL. The
0-byte `-wal` says the same thing: there are no pending frames to recover.

## Root cause

`node_modules/@claude-flow/cli/dist/src/memory/memory-initializer.js:78`

```js
function hasNativeWalSidecars(dbPath) {
try {
return fs.existsSync(`${dbPath}-wal`) || fs.existsSync(`${dbPath}-shm`);
}
catch {
return true;
}
}
```

The gate is **presence-based**, not **liveness-based**. It cannot distinguish a
live native holder from an orphan left by an unclean close, so any command that
leaks sidecars bricks the write path for the whole project directory.

## Impact

- `memory store` — and every feature built on it — fails for the rest of the
project's life until a user manually deletes internal database files.
- The error message misdirects: it blames a live writer and the missing Windows
native bridge, so users chase `CLAUDE_FLOW_ENABLE_NATIVE_BRIDGE_ON_WINDOWS=1`,
which does not address the cause. Whether that variable "works" only reflects
whether sidecars happen to be present, making the failure look intermittent.
- `ruflo doctor`, the command users are told to run when something is wrong, is
what breaks it.

## Suggested fixes

1. **Make `doctor` close cleanly.** Run `PRAGMA wal_checkpoint(TRUNCATE)` and
close every connection it opens. This alone fixes the reported symptom.
2. **Make the gate liveness-based.** Probe with `wal_checkpoint(TRUNCATE)` and
treat `busy === 0` as "no live holder, safe to proceed" — the sidecars are
also removed as a side effect. This keeps #2735's guarantee (a real holder
still returns `busy !== 0` and the write is still refused) while removing the
false positive, with no platform-specific process scanning.
3. **Improve the message.** When the `-wal` is 0 bytes and a checkpoint reports
`busy: 0`, say the sidecars are stale and name the recovery, instead of
pointing at a native writer that does not exist.

## Workaround for users

Checkpoint and close before writing; this is safe because a real writer makes
`busy !== 0` and nothing is removed:

```js
const Database = require('better-sqlite3');
const db = new Database('.swarm/memory.db');
const r = db.pragma('wal_checkpoint(TRUNCATE)')[0];
db.close();
if (r.busy !== 0) throw new Error('a live writer holds the WAL — do not delete sidecars');
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with node_modules/@claude-flow/cli/dist/src/memory/memory-initializer.js:78 and reproduce the failure using the documented memory store, doctor, and follow-up store commands. Trace the database connections opened by ruflo doctor and the WAL-sidecar safety check. Done means doctor no longer leaves blocking orphan sidecars, writes succeed afterward, and a live native writer is still refused.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, sqlite, typescript
Domain
cli, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.