ruflo-adr: adr-verify turns read failures and the default 20-row limit into a healthy graph
- Dominant language
- TypeScript
- Stars
- 72.7k
- Forks
- 8.6k
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 83
Description
## Summary
`plugins/ruflo-adr/scripts/verify.mjs` currently converts every failed or malformed `memory list` call into `[]`, then treats that empty array as authoritative graph state. It also omits `--limit`, while the current `ruflo memory list` default is 20.
The result is a verifier that can print a clean `0 ADRs / 0 edges / 0 cycles` report after failing to read the store, and that cannot verify a graph larger than 20 rows per namespace.
This is present byte-for-byte on current `main` (validated 2026-08-31) and in the released `ruflo-adr` 0.4.1 copies used by both Claude Code and Codex.
## Exact cause
```js
function memoryListJson(namespace) {
const r = spawnSync('npx', [
CLI_PKG, 'memory', 'list',
'--namespace', namespace, '--format', 'json',
], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', cwd: ROOT });
if (r.status !== 0) return [];
const m = /\[[\s\S]*\]/.exec(r.stdout || '');
if (!m) return [];
try { return JSON.parse(m[0]); } catch { return []; }
}
```
Four distinct states collapse to the same value:
1. namespace is genuinely empty;
2. the child cannot start or times out;
3. the command exits nonzero;
4. stdout is truncated or malformed.
The caller then computes empty sets and only exits 1 for cycles/dangling refs. A read failure therefore exits 0.
Separately, `ruflo memory list --help` documents `--limit` with default 20. The verifier never supplies it and does not page. In the observed repository the dry-run corpus is 107 ADR records and 460 edges, so most of the graph is outside the verifier's read window.
This can both hide defects after row 20 and invent dangling references when an edge is in the first page but its target record is not.
## Reproduction contract
A regression test can put a fake `npx` first on `PATH` and run the unmodified verifier:
- fake exits 1 for both list calls;
- fake exits 0 but prints non-JSON;
- fake sleeps past a bounded timeout;
- fake returns 25+ records with a cycle/dangling reference after row 20.
Today the first three produce a valid-looking empty report and exit 0. The fourth cannot be observed because no explicit all-row/page request is made.
## Required behavior
Please make reads a discriminated result, never an ambiguous array:
```ts
type ReadResult =
| { ok: true; entries: MemoryEntry[]; complete: true }
| { ok: false; namespace: string; error: string };
```
Then:
1. set a bounded timeout and report spawn errors, signals, nonzero status, and parse failures with the namespace and exact diagnostic;
2. request the complete namespace explicitly—prefer a native `--all`/cursor contract; as an immediate compatibility measure use a documented high explicit limit and reject any response that cannot prove completeness;
3. do not compute graph health unless **both** namespace reads are successful and complete;
4. emit structured `readErrors` in JSON mode and a prominent failure in Markdown mode;
5. exit nonzero on any incomplete/unreadable input, independent of `VERIFY_STRICT`;
6. keep a genuinely empty, successfully-read pair of namespaces valid.
## Acceptance tests
- nonzero child exit => verifier exit 1, exact namespace/error retained, no “healthy 0/0” claim;
- child timeout/signal/spawn error => exit 1;
- malformed or non-array JSON => exit 1;
- 25+ records and edges are all evaluated;
- a cycle/dangling ref after row 20 is detected;
- a successful complete empty read remains `0/0` and exits 0;
- both Markdown and JSON modes have identical exit semantics;
- `ADR_ROOT` continues to select the intended project database.
Related: #2666 (graph reconciliation), #3097 (import false success and root ambiguity), #2781 (writer/reader store split).
Contributor guide
Research direction
Start in plugins/ruflo-adr/scripts/verify.mjs and run the fake-npx reproduction cases described in the issue, including nonzero, malformed, timeout, and more-than-20-record responses. Trace how memory list results feed Markdown and JSON reports, then verify that complete reads detect later cycles or dangling references, failed reads produce structured errors, both modes exit consistently, and ADR_ROOT still selects the intended database.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, testing, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100