index/index.js:1185:Wt re-put guard tests t.blob === bhl && t.commit === chl,
but index/index.js:442:Wt idxWriter.put auto-commits every ix.mem rows (256
on the incremental open), so a seal can fall between one rev's BLOB and CMMT
put. A run killed there leaves top.commit null; the next run re-derives the
same (path, blob, commit), the guard misses, and a SECOND rev is minted for
it. Confirmed end-to-end on a fixture: BLOB revs 0,1,2,3, CMMT revs 0,1,3
— a permanent hole at rev 2. index/index.js:525:Wt lastRev assumes a dense
chain and stops at the hole, so view/list.js:52:cn's dir fuse freezes on a stale
commit and loadDir mints duplicates on top. Found in the 2026-09-18 index
review.
Repro: plant one torn BLOB row, bring up, show the duplicate rev and the hole.
Guard on the blob plus an explicit revValAt(phl, t.rev, K_CMMT) probe, and
write one rev's rows under a single batch slot so a seal never splits a rev.
No index format change; the sealed-prefix contract (LITE-006:55:2d) stays.
A torn rev is finished, not re-minted: the CMMT row rules.
idxWriter grew open/close, a hold count the auto-seal
respects; emit writes one rev's rows between the two, so
a seal lands BETWEEN revs and can no longer split one.top test and adds an
exact revValAt(phl, t.rev, K_CMMT) probe when that top
carries the blob but no commit: a row on disk means the rev
is whole, a miss means an older seal split it.emitDir needs none of it: a dir rev IS one row, already
atomic w.r.t. the batch.topemit — production code
written for one test alone.