Skip to content

All research

Method

Counterfactual rewind validation: preventing out-of-scope graph deletion

A read-only replay of the old algorithm against the oldest live checkpoint calculated that 102,020 of 159,506 quads—and graphs for 1,057 documents untouched by the anchor turn—would be removed. This was a counterfactual, not an incident; the fix scopes replacement to touched documents.

quads the old algorithm would remove, live-state counterfactual, not observed deletion
102,020 / 159,506
affected documents actually touched by the anchor turn
0 / 1,057

Question — Why should rewinding a few files replace the entire knowledge graph with an older snapshot?

Observation — In a read-only counterfactual replay on a real vault, the old algorithm would have removed 102,020 of 159,506 current quads and erased 1,057 document graphs, none belonging to the anchor turn.

Decision — Do not report an incident; align graph authority with the files actually restored or deleted, replacing only those documents’ named graphs from the snapshot.

Old whole-graph rewind compared with the scoped restore of only touched document graphs

102,020 is counterfactual harm computed by applying the old algorithm to live state, not observed production data loss.

1. File restore and graph restore had different scopes

The file half of a rewind plan restored or deleted only paths recorded in the checkpoint range. The graph half, however, wrote the anchor’s entire graph.nq snapshot over the current graph. File authority stopped at paths the conversation had changed; derived-state authority extended to every graph update made after the snapshot.

That mismatch is hazardous when another writer—such as a background feed—uses the same vault. Documents extracted by another writer after the anchor exist in the current graph but not in its old snapshot. Wholesale replacement removes their derived graph without changing their bytes on disk.

2. Live state was read, but no rewind was executed

On 1 August 2026, the analysis parsed the current N-Quads and the oldest live checkpoint snapshot from a real installation, then calculated what the pre-fix wholesale replacement would do. The Market graph produced zero parse errors and exactly 159,506 quads from its 159,506 lines.

Counterfactual result n · unit · condition
Current graph removed 102,020 / 159,506 quads (64%) · old wholesale restore at the oldest live checkpoint
Documents losing graphs 1,057 named document graphs · 0 touched by the anchor turn
Background-feed documents 1,056 / 1,057 · paths under live/
Stale graph resurrected 50,550 quads · 400 notes no longer present on disk
File-operation report 0 successful restores or deletes · UI condition Rewound 0 file(s)

The anchor recorded one .scratch_seasonality.py, which was not an extracted document. The 1,057 documents were therefore outside the turn’s file blast radius, not merely a large set that happened to fall inside it.

3. Separate an incident claim from the self-healing condition

These numbers did not come from a production-loss log after a user ran rewind. They came from a counterfactual replay of the old algorithm against live checkpoint and graph state; the analysis did not overwrite the user’s graph. The commit subject and earlier prose saying the rewind “deleted” data are stronger than the evidence and should be read as “would have deleted.”

Had it executed, automatic self-healing was also unavailable. For 1,057 of 1,057 affected documents, the on-disk SHA-256 matched the stored last_tagged_hash. Their bytes had not changed, so the extraction gate would have skipped them. This does not mean recovery was impossible; it means recovery would have required an explicit metadata reset or forced re-extraction rather than occurring automatically.

4. The fix aligned derived-state scope with file scope

Commit 1cd80797 retained the current graph by default and took only the named document graph and #analysis sibling for paths the rewind actually restored or deleted from the anchor snapshot.

State Before After
Untouched document Overwritten by the anchor’s old whole graph Current graph retained
Successfully restored document Restored as part of the whole snapshot Only its named graphs restored from the snapshot
Current but absent at anchor, and genuinely deleted by rewind Determined as a side effect of replacement Graph removed only after its file deletion succeeds
Zero file changes Could still replace the graph Does not write the graph
N-Quads line not attributable to a document Outcome depended on whole replacement Current line retained; snapshot line ignored as a fail-safe

The invariant is not “trust snapshots less.” It is that observed filesystem results define the authority boundary for derived state. A rewind that did not change a file does not have authority to change that document’s graph.

5. Regression tests guard both omission and over-correction

rewind_graph_restore_keeps_documents_the_turn_never_touched constructs a current graph with one rewind target and one document added by a background writer after the snapshot. After the fix, the target must return to its pre-turn entity, the background document must remain, and the target’s post-turn entity must disappear. The last assertion prevents a superficial fix that simply disables graph restore.

A second guard, rewind_rolls_back_a_document_a_later_in_range_turn_touched, pins the multi-checkpoint fold. File rows are folded across every turn from the anchor forward, while the graph snapshot comes from the anchor alone. A document first touched by a later in-range turn must still roll back to its prior state, with its post-turn graph removed.

6. Current code and the remaining design constraint

The current scope_graph_restore keeps the current graph, removes the affected document graphs, and appends only those same graphs from the snapshot. apply_plan passes successful restored_paths and deleted_paths; when both are empty, it does not write the graph file.

Snapshot capture remains whole-graph and therefore over-broad. Naively narrowing it to the anchor turn’s own paths would omit a document first touched by a later turn in the same rewind range; scoped restore would then interpret that affected-but-absent graph as a deletion. Safe narrowing requires carrying a pre-graph hash per path through the range fold. The 145 MiB store size was a point-in-time observation on one installation, not a growth rate; a later remeasurement found it byte-identical for five days, so no monthly projection is retained here.

7. Reproducibility and evidence boundary

Evidence Observation and condition Boundary Primary source
Live counterfactual 102,020/159,506 quads; 1,057 documents; 0 touched by anchor · old algorithm at the oldest of five live checkpoints No rewind execution and no observed loss incident Commit 1cd80797; src-tauri/src/checkpoints.rs:567-578
Parse validation Market N-Quads: 0 parse errors and 159,506 lines=159,506 quads One real vault as of 2026-08-01 Commit 1cd80797 body
Self-heal condition 1,057/1,057 disk hashes matched last_tagged_hash Does not exclude forced extraction or metadata reset Commit 1cd80797; src-tauri/src/checkpoints.rs:573-578
Scoped fix Keep current graph; replace document and analysis graphs only for successful file changes Default graph and unattributable lines fail safe toward retention src-tauri/src/checkpoints.rs:560-605,647-675
Regression guard Target rolls back, untouched background graph remains, target post-state disappears Synthetic fixture; does not benchmark live-scale performance src-tauri/src/checkpoints.rs:856-922
Capture constraint Later in-range documents require state present in the anchor snapshot Per-path pre-graph hashes are not implemented src-tauri/src/checkpoints.rs:924-1018; commit 98f10fe9

What this measures

  • Consilience

    A persistent human–AI workspace using human-editable Markdown as the authorial record and a derived ontology as the agent's relational memory