fix(consolidation): a dry run must not advance the dedup census window - #204
Merged
acidkill merged 1 commit intoSep 7, 2026
Conversation
_dedup rotates its anchor window between runs by persisting a cursor in Brain.metadata, and it advanced that cursor unconditionally — outside the `if not dry_run` guard that protects everything else in the pass. So `smem consolidate --dry-run` wrote to the brain after all: the next real run started one window later, and the slice the dry run had merely looked at was skipped rather than compared. The cursor is the one piece of state the census keeps between runs, and moving it is a write. It now moves only when the run is real. Two tests pin it: a dry run over a truncated census leaves the stored cursor where it found it (fails on main with "a dry run wrote the cursor back"), and a real run still rotates it, so the guard removes the write and not the rotation.
acidkill
approved these changes
Sep 7, 2026
acidkill
left a comment
Owner
There was a problem hiding this comment.
Reviewed the full diff. Dry run no longer advances the dedup census cursor; positive control keeps the real-run rotation honest. Green CI.
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.
Summary
smem consolidate --dry-runno longer writes to the brain: the dedup census advances its window cursor only when the run is real.Why
_dedupcaps the anchors it compares and rotates the window between runs by keeping a cursor inBrain.metadata. That rotation is a good idea, and it is the reason a brain that has outgrown the cap still gets a full census over several passes instead of comparing the same prefix forever. The cursor was advanced unconditionally, outside theif not dry_runguard that protects everything else in the pass, so a dry run persisted the next window before returning. The following real run then started one window later, and the slice the dry run had merely looked at was skipped rather than compared. The blind spot grew in proportion to how often anyone inspected the brain, which is an unkind way to treat the cautious. The cursor is the one piece of state this census keeps between runs, and moving it is a write;--dry-runundertakes not to make any.Changes
engine/consolidation.py:_advance_dedup_cursoris called only whennot dry_run. Nothing else moves.dedup_window_startis still reported, so a dry run's summary still says which window it inspected, and a real run rotates exactly as before.tests/unit/test_dedup_alias_edges.py: two tests over a truncated census. The dry-run test asserts the stored cursor is unchanged and thatsave_brainwas never called; the real-run test asserts the cursor still advances by the window size. The second one is a positive control — it passes both before and after the fix, which is how you can tell the guard removed the write and not the rotation.Test plan
pytest tests/unit/test_dedup_alias_edges.py— 48 passed, against 46 onmain; the delta is exactly the two tests added here.test_dry_run_does_not_advance_the_windowfails onmainwithAssertionError: a dry run wrote the cursor back. It fails again ifengine/consolidation.pyalone is reverted whilst the new tests are kept, so it measures this hunk rather than itself.pytest tests/ -m "not stress" -n 4— 7285 passed, 48 skipped, 1 xfailed. Two tests intests/unit/test_dashboard_brains_scope.pyfail here and fail identically, by name, onmain(7283 passed): they want a live SurrealDB and collide with one another under-n. Both pass when that file is run on its own.ruff check src/ tests/clean;ruff format --check src/ tests/reports 739 files already formatted.mypy src/ --ignore-missing-imports— success, no issues found in 354 source files.main.CHANGELOG.mduntouched — left to the release entry, as with fix(storage): bind datetimes in time comparisons so they select by value #191–fix(memory): refresh content-derived fields on compress, restore, and refine #193.Related issues
None filed. It is a one-line guard, so a PR seemed kinder than a ticket.
Verified by
@RobertSigmundsson