Four findings against the previous commit. Each fix carries a regression that
fails against the landed code, and each was mutation-checked back to it.
Admission projected nothing. The replay-ceiling check read the published root,
so it decided about a transaction it had not counted: one object present, a
two-object transaction against a ceiling of two committed and the store then
failed to reopen. The open group was the same hole one step along. Admission
now projects sealed runs, unsealed layers, the open group, and the incoming
transaction.
The byte ceiling was unguarded. Recovery rebuilds into one delta that refuses
on either ceiling, so narrow frames across namespaces passed admission and
failed to reopen on `max_active_index_bytes`. Both are checked, and the
projection counts namespaces because the encoding pays a section header per
namespace. `index::encoded_bytes_for` is that arithmetic extracted, so this
file does not carry a copy of the encoding's shape.
A recovered run's locations did not resolve, and this reshaped the slice. An
`IndexLocation` names a logical generation, and a run is the first thing here
that persists one across a session — sound only for a generation that is
stable, which is a segment's alone. The active tail's is assigned from
`max(manifest, .seg, .idx) + 1`, so it moves whenever any artifact appears
(the run's own manifest suffices), and recovery seals a journal holding
frames at that counter rather than at the generation the tail had. The
previous reopen test could not see it: its lookups were answered by the
replay delta shadowing the run. Coverage is now an oldest-first prefix of
layers whose every entry is segment-backed, which makes the broken run
unwritable rather than untested. The cost — sealing lags one session behind
until frames leave `active/` — is recorded in scope §6.5.
Preserving the tail's generation across the seal was attempted and withdrawn.
`recovery_generation` is at once the new manifest's generation and the sealed
segment's logical generation, so the real fix separates those two numbers in
A2's recovery core, and that belongs with checkpointing rather than inside a
B1 integration commit. The first attempt also targeted the wrong branch: a
journal holding frames is replaced, not kept. `active_tail_logical_generation`
is left extracted at the one path that already used that formula so the two
ways of numbering an active tail are visible together.
The run ceilings counted every shard, where recovery enforces them against one
shard's manifest — a four-shard root with `max_index_runs = 1` refused the
second shard its first run. Counted per shard now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKzM69CHmBuDcA3qN1jFdh