fix(prune): never delete records that are recorded dedup survivors (#80)#95
Merged
Merged
Conversation
`recall prune` deleted records with no awareness of `dedup_lineage`. Pruning a record that is a recorded survivor orphaned every duplicate marked under it — the duplicates stayed hidden from search while their visible representative was gone (the same visible-survivor decay class as #63, via a different door). Fix (conservative guard, consistent with PR #79's destructive-mode survivor guard and ADR-0003): prune now excludes recorded survivors (active `dedup_lineage`, status `marked`/`deleted`) from deletion for the three dedup-able pruned tables — messages, decisions, breadcrumbs. Withheld rows are counted and surfaced in the summary ("N kept as dedup survivors"), so the exclusion is never silent. `notRecordedSurvivorSql` is the survivor-side mirror of `notMarkedDuplicateSql`, appended to BOTH the count and DELETE so the reported count matches what is removed. Docs: completes the ADR-0003 user-facing dedup-safety prose — (a) mark→delete is non-escalating; (b) the prune survivor guard; (c) the semantic-pass asymmetry (a sticky survivor acquires no new semantic duplicates after #79; the exact pass is unaffected). Tests: tests/commands/prune.test.ts pins survivor protection (status marked and deleted), non-survivor pruning, reverted-carries-no-obligation, and the never-silent summary; mutation-verified. Closes #80
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #80
What
recall prunedeleted records with no awareness ofdedup_lineage. Pruning a record that is a recorded survivor orphaned every duplicate marked under it — the duplicates stayed hidden from search while their visible representative was gone. Same visible-survivor decay class as #63, through a different door.The fix is the conservative guard option from the issue, consistent with PR #79's destructive-mode survivor guard and the stance recorded in ADR-0003 ("prune must not silently orphan a survivor's marked duplicates"). I did not choose re-point/unmark: that is #63's option (b) chain-compression, which PR #79 explicitly deferred, and unmarking would mutate dedup state as a side-effect of prune.
Changes
src/lib/dedup.ts—notRecordedSurvivorSql(tableExpr, idExpr), the survivor-side mirror ofnotMarkedDuplicateSql. Active lineage isstatus IN ('marked','deleted')— the same setloadRecordedSurvivorstreats as binding (revertedcarries no obligation).src/commands/prune.ts— appends the guard to the three dedup-able pruned tables (messages, decisions, breadcrumbs) on both the count and the DELETE, so the reported count matches what is removed. Withheld rows are surfaced in the summary (N kept as dedup survivors) — never silent (mirrors PR fix(dedup): make recorded survivors sticky across runs #79'sstickySkipped).sessions/extraction_*are not dedup tables;loa_entries/learningsare never pruned.docs/cli-reference.md— completes the ADR-0003 user-facing dedup-safety prose: (a) mark→delete is non-escalating; (b) the new prune survivor guard; (c) the semantic-pass asymmetry — a sticky survivor acquires no new semantic duplicates after fix(dedup): make recorded survivors sticky across runs #79, exact pass unaffected.tests/commands/prune.test.ts(new) — pins the behavior.Tests
tests/commands/prune.test.ts(5 tests):protects a recorded survivor message and still prunes a non-survivor— survivor kept, plain pruned, duplicate never orphaned, summary reportskept as dedup survivorsprotects a survivor decision and prunes a non-survivor decision'deleted'-status lineage also protects its survivor (breadcrumbs)'reverted' lineage carries no obligation — a former survivor is prunabledry-run reports protected survivors without deleting anythingVerification
bun run lintclean.bun test tests/commands/prune.test.ts: 5 pass;tests/commands/dedup.test.ts: 22 pass (no regression from the new helper).NOT EXISTSover an empty set) fails 4 of 5 prune tests (therevertedtest correctly still passes — reverted is never protected). Restored → green.47bc38b) before this branch — unrelated to prune/dedup.