Skip to content

fix(recover): do not purge memories that have no source - #9

Merged
mthamil107 merged 1 commit into
mainfrom
fix/recover-unsourced-purge
Aug 27, 2026
Merged

fix(recover): do not purge memories that have no source#9
mthamil107 merged 1 commit into
mainfrom
fix/recover-unsourced-purge

Conversation

@mthamil107

Copy link
Copy Markdown
Owner

The defect

classify() normalised an absent source to "unknown", which then failed the
trusted-set membership test and was classified PURGE.

Because --dry-run is opt-in on the CLI, memorywire recover --agent X --store Y
applies by default. So running it against a store written before 0.2.0 — when
--source landed on remember — soft-deleted every row, while the report explained
each one as untrusted source 'unknown'.

source is an optional field. Absence of provenance is not evidence of untrusted origin.

The fix

  • An unsourced record is kept, with the verdict reason clean (no source recorded),
    so it stays visible in the report rather than being silently skipped.
  • It is still passed through the content detectors — a directive hiding in an unsourced
    memory is quarantined (soft-delete, restorable) exactly as before.
  • Records carrying an explicitly untrusted source are unaffected.
  • The previous behaviour is available as opt-in: purge_unsourced (default False) on
    classify(), Recoverer and the MCP recover tool, and --purge-unsourced on the CLI.

Verification

Regression tests cover the disaster case directly — three memories with no source,
recover with no dry-run, assert all three survive — plus the opt-in path, the
detector path on unsourced records, and that explicitly-untrusted records still purge
regardless of the flag.

Reproduced end to end against a real store before and after:

3 memories written with no source
memorywire recover                     -> kept (clean): 3    # store survives
memorywire recover --purge-unsourced   -> purged: 3          # opt-in still works

323 unit tests pass; ruff check and ruff format clean.

Docs

docs/recovery.md gains a "Memories with no source" section, corrects the verdict table
(the purge row described an explicitly-untrusted value while the code fired on absence),
and notes the asymmetry that --dry-run is opt-in on the CLI while the MCP tool defaults
dry_run to true.

Reported by an external implementer building an adapter against the wire format.

An absent `source` was normalised to "unknown", failed the trusted-set
membership test, and was classified PURGE. Running `memorywire recover`
(which applies by default on the CLI, where --dry-run is opt-in) against a
store written before 0.2.0 — when --source landed on `remember` — therefore
soft-deleted every row while reporting "untrusted source 'unknown'".

Absence of provenance is not evidence of untrusted origin. Unsourced records
are now kept with the verdict reason "clean (no source recorded)", so they
stay visible in the report rather than being silently skipped, and are still
passed through the content detectors — a directive hiding in an unsourced
memory is quarantined as before. Records with an explicitly untrusted source
are unaffected.

The previous behaviour is available as opt-in via purge_unsourced (default
False) on classify(), Recoverer and the MCP recover tool, and
--purge-unsourced on the CLI.

Regression test covers the disaster case directly: three memories with no
source, recover with no dry-run, all three survive; plus the opt-in path.

Documented in docs/recovery.md, along with the CLI/MCP dry-run asymmetry.
@mthamil107
mthamil107 merged commit 1eaaa0a into main Aug 27, 2026
10 checks passed
@mthamil107
mthamil107 deleted the fix/recover-unsourced-purge branch August 27, 2026 11:07
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