Skip to content

feat(repair): general-purpose repair for crossed summary ownership - #126

Merged
antra-tess merged 3 commits into
mainfrom
feat/topology-repair
Sep 26, 2026
Merged

antra-tess merged 3 commits into
mainfrom
feat/topology-repair

Conversation

@antra-tess

Copy link
Copy Markdown
Contributor

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; --apply writes the three states and re-opens the store under topologyPolicy: '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:

  • lossless (default): a crossed summary keeps the run of children carrying the most leaves and detaches the rest as roots (they keep their prose and their leaves); a crossed L1 keeps its largest run and releases the stray messages from its 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 — 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 as depthLostLeaves.
  • compact: additionally, a summary whose holes are owned one level down by roots (the Demand-path merges (enqueueMergeForRange, produce level ≥ 2) still bypass the strict-adjacency grammar — can mint a non-contiguous L2 that kv-unified then rejects forever #95 shape) adopts them, and detached fragments are re-homed under an adjacent summary at the same level (preferring the same parent), iterating while progress is made. Depth is kept; the adopting prose never covered the moved content. Reported as 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.ts now 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

🤖 Generated with Claude Code

antra-tess and others added 3 commits September 25, 2026 18:00
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>
@antra-tess
antra-tess merged commit b19b895 into main Sep 26, 2026
5 checks passed
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