A record too small to hold a header, and one whose magic says it is not a putup
index at all, are damage by any reading -- but both carried InvalidFormat, the
same code as "this record's version is one I do not read", which is the expected
benign outcome on every upgrade. load_old_index announced only checksum and
layout failures, so a build over a truncated or overwritten record printed
nothing and read as a first build: the failure IndexDamaged was introduced to
end (#381), still live at three of its five sites.
Give each open-time rejection a code that says which of the two happened: damage
by any reading takes IndexDamaged, and a version outside the readable window
takes IndexVersionMismatch, a code that was declared and never produced. The
too-small and magic checks stay ahead of the version check because no version of
the format is shorter than header plus footer and none lacks the magic, so
neither can be a record of a version this binary merely does not read.
Then invert the announcement: silence is now the enumerated case
(IndexVersionMismatch alone) rather than the announced set, so a rejection the
reader gains later is loud by default instead of silent by default -- the
property whose absence is why #381 fixed one row and left three. The remaining
arm covers IoError, where the record may be intact and the path or its
permissions is the problem, and deliberately does not call it damage.
InvalidFormat and IndexTruncated have no producers after this and never had a
consumer or a test; ErrorCode is in-process only, so deleting them costs
nothing. InvalidState moves to the general block, where it belongs.
REQ-READ-ANNOUNCE-DAMAGE is widened by complement rather than by enumeration:
it names the benign exception and lets damage be everything else, so it stays
true as damage modes are added.
Closes #383.
Summary
Two commits, split because two files carry both changes:
Announce record damage that is not a layout failure — a record too
small to hold a header, and one whose magic says it is not a putup index
at all, are damage by any reading, but both carried
InvalidFormat, thesame code as "this record's version is one I do not read" (the expected,
benign outcome on every upgrade). Each open-time rejection now gets a code
that says which happened:
IndexDamagedfor damage by any reading,IndexVersionMismatchfor a version outside the readable window. Theannouncement is inverted so silence is the enumerated case
(
IndexVersionMismatchalone) instead of the announced set, so arejection the reader gains later is loud by default.
InvalidFormatandIndexTruncatedare deleted (no producers, no consumers, no tests).REQ-READ-ANNOUNCE-DAMAGEis widened by complement rather thanenumeration.
Delete
is_valid_index, a third encoding of the readable window — noproduction caller, only its own test; it re-answered the same question as
open_indexwith two different definitions (exact version match insteadof the
[INDEX_LAYOUT_FLOOR, INDEX_VERSION]window, and a size floor ofheader alone without the footer).
RED was quoted (unit + E2E failures showing the wrong error codes and a
silent rebuild) before any production code changed. The whole suite passes
in one process: 427
~[e2e]cases (167220 assertions) + 394[e2e]cases(2936 assertions).
format-check,spec-check, andiwyuare clean;tidyshows only pre-existing warnings on lines this change does not touch.Pair-partner review (Sonnet, cross-model per charter) found and fixed one
real issue: two added comments were two lines each, over CLAUDE.md's
one-line why-comment cap — both compressed, with the dropped context moved
into this commit's body. Everything else it checked (check ordering, the
switch's reachable-code enumeration, the
InvalidFormat/IndexTruncated/is_valid_indexgrep, three trap claims,spec-check, theDESIGN.mdrow,and the commit split staging) came back sound.
make checkis red on this branch, but it is also red on stockmainat42912d05eon this machine (verified against agit archivecontrol tree):the 32-way sharded E2E runner fails one shard per run, alternating between
two tests that each pass standalone and pass when the whole suite runs in
one process. Pre-existing, unrelated to this change.
Closes #383
Test plan
production code changed
~[e2e]+[e2e])make format-check,make spec-check,make iwyucleanmake tidy— only pre-existing warnings, none on touched linesmake checkred-on-main confirmed via stock control tree — pre-existing sharded-runner flake, not this change