feat(repair): general-purpose repair for crossed summary ownership - #126
Merged
Merged
Conversation
planTopologyRepair (src/repair/topology.ts) plans a regeneration-free repair for the stores topologyPolicy 'reject' refuses, and scripts/repair-topology.ts applies it (dry-run by default; --apply writes and re-opens the store under 'reject' to verify). - lossless (default): a crossed summary keeps the run of children carrying the most leaves and detaches the rest as roots; a crossed L1 keeps its largest run and releases the stray messages from its chunk record; single-source parents dissolve; touched ancestors get sourceRange recomputed; kv-stable resolutions are clamped to the leaf's remaining chain. An ancestor left with a hole is repaired the same way on the next pass, so the pyramid unravels around each fragment (reported as depthLostLeaves). - compact: additionally adopts unparented hole owners one level down and re-homes detached fragments under an adjacent summary at the same level, keeping the pyramid's depth at the price of prose that never covered the moved content (reported as proseGapLeaves). - --release-head removes detached opening L1s and uncompressed prefix records so the head window takes those messages back verbatim (CM ≥ #123 anchors the head to coverage). scripts/audit-topology.ts prints summary/chunk/link counts and warns when it has nothing to audit. Tests: planner fixtures for the #122 and #95 shapes, crossed L1s, re-homing, dissolution, and an end-to-end run on a persisted crossed store. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The repair script's verification re-open (and the audit script) opened the store under a bare strategy config: with no head window, rebuildChunks chunked the released opening messages into a new record, which the resident compressed into an L1 at its next boot — the head never took them back. `auditOnly` skips frontier chunking, the merge-threshold pass and the persisted-queue rewrite; both scripts set it. Test: a plain open under a foreign config mints records, an audit-only open adds nothing. `--release-head=all` also releases pre-existing prefix L1s (parented ones are detached first; a parent left with one source dissolves), bounded by `--release-head-limit <n>` messages. A run whose only change is a release is no longer treated as "nothing to do". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…to re-fold Dissolves each crossed summary (level ≥ 2) and its ancestors, and, for a child run that would be left without an unparented same-level neighbour, the tower over the smaller adjacent neighbour, so the region becomes contiguous unparented runs the merge ladder re-folds bottom-up with real summarizer calls. Crossed L1s split as before. The region is bounded to the crossed towers, not the crossed summary's span: a far stray no longer dissolves everything between it and its era. Reports dissolvedForRebuild and exposedL1Leaves; the CLI prints the drain-autobiographical invocation to run on the stopped store before restarting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Follow-up to #124, which refuses stores with crossed ownership but shipped no way to repair them.
What
planTopologyRepair(src/repair/topology.ts) is a pure planner over the persisted states (summaries, chunk records, resolutions, message listing);scripts/repair-topology.ts <store> --namespace <ns> [--apply] [--release-head] [--mode lossless|compact] [--json]applies it. Dry-run by default;--applywrites the three states and re-opens the store undertopologyPolicy: 'reject', so the run fails if the audit still finds a crossed summary. No summary is regenerated and no model call is made.Two modes, because the trade-off is real and belongs to the operator:
sourceRangerecomputed; kv-stable resolutions are clamped to the leaf's remaining chain. An ancestor left with a hole is repaired the same way on the next pass, so the pyramid unravels around each fragment — no prose ever claims content it did not see, but those leaves render at a shallower level until the merge ladder folds them again. Reported asdepthLostLeaves.proseGapLeaves. Adoption is chosen over detaching when it moves no more leaves, or when some fragment has no home.--release-head: detached opening L1s and uncompressed prefix records are removed with their records, so the head window takes those messages back verbatim (CM ≥ fix(strategy): stop the head window ratcheting down under calibration drift (#122) #123 anchors the head to coverage; bounded at 2×headWindowTokens).scripts/audit-topology.tsnow prints summary, chunk and chunk→L1 link counts and warns when it has nothing to audit (a copy that carries no state reads as clean otherwise).Validation
npm test: 884/884.remaining: 0) in 1–5 passes;--applyon a copy re-opens under'reject'. For the Head window ratchet: calibration rises peel the head into late, out-of-order L1s that merge across eras #122-shape store (five one-message L1s over the opening)lossless --release-headdetaches the five, releases six records, and the host then renders the opening verbatim in the head. For the large stores the costs are large in either mode (thousands of leaves lose a fold level, or thousands sit under prose that never saw them) — regenerating the affected merges with the summarizer is the path that is both compact and faithful, and is not attempted here.🤖 Generated with Claude Code