Skip to content

feat(cli): add --supersedes to --store and surface write-time warnings - #42

Merged
DottytheHomeless merged 1 commit into
mainfrom
feat/cli-supersedes-and-write-warnings
Sep 12, 2026
Merged

feat(cli): add --supersedes to --store and surface write-time warnings#42
DottytheHomeless merged 1 commit into
mainfrom
feat/cli-supersedes-and-write-warnings

Conversation

@DottytheHomeless

Copy link
Copy Markdown
Contributor

Why

docs/configuring-your-agent.md tells agents: "When you find an outdated memory, store a corrected version with supersedes: [\"<old id>\"]. Replace specific entries; never wholesale-rewrite the store."

That instruction was only reachable through MCP store_memory. An agent wired to the CLI had no way to execute it — so corrections piled up as new rows sitting next to the stale ones they were meant to replace, and the stale ones stayed live and recallable.

A rule whose action has no interface in the daily path is a rule that never fires.

What

--supersedes 123,456 on --store. Fails the whole write if any target is not live (deleted, already superseded, or nonexistent):

$ node index.mjs --store "..." --supersedes 2755,99999999
Error: --supersedes target(s) not live (deleted, already superseded, or nonexistent): 99999999
$ echo $?
1

A partial supersede is worse than none — the write lands, the stale version stays recallable, and the correction looks applied. So the CLI pre-checks with getMemoriesByIds and refuses before writing rather than letting storeMemory point at whatever still exists.

Three guards the CLI was dropping. storeMemory reports metaDowngrade, supersedeShrink and quotaRejected through opts.out, but the CLI never passed an out — so they only reached the log stream. The shrink guard in particular exists to tell the caller what the new version stopped carrying; discarding that silently defeats its purpose. Now:

stored: 2760
superseded: 2755, 2757
⚠ supersede shrink — the new version stopped carrying:
  - {"id":"2755","oldLen":938,"newLen":502,"ratio":0.54,"dropped":[...],"droppedCount":3}

Tests

New cli-supersedes.test.mjs, 12 assertions on a temp DB — happy path, chain pointer, both refusal paths, already-superseded target, and that refused calls (including a partly-dead target list) write nothing and leave live targets untouched. supersede-shrink and meta-gate suites still pass.

🤖 Generated with Claude Code

The correction rule ("store a corrected version with supersedes, don't
rewrite the store") was only reachable through MCP store_memory. Agents
wired to the CLI had no way to execute it, so corrections piled up as new
rows next to the stale ones they were meant to replace.

--supersedes takes comma-separated rowids and fails the whole write if any
target is not live (deleted, already superseded, or nonexistent). A partial
supersede is worse than none: the write lands, the stale version stays
recallable, and the correction looks applied.

Also surfaces three guards the CLI was dropping into the log stream only —
metaDowngrade, supersedeShrink and quotaRejected. The shrink guard in
particular exists to tell the caller what the new version stopped carrying;
silently discarding that defeats its purpose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@DottytheHomeless
DottytheHomeless merged commit 2d85389 into main Sep 12, 2026
2 checks passed
@DottytheHomeless
DottytheHomeless deleted the feat/cli-supersedes-and-write-warnings branch September 12, 2026 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant