The #383 cycle settled three questions and left every answer implicit in
the code, where it reads as a choice someone made rather than a rule
someone must keep. A later change can quietly restore any of the three to
a judgement call. DESIGN.md now states all three.
An announcement filter announces by default. Enumerating error codes
selects the wording of a particular announcement, or excuses one proven
benign; it is never the condition for being announced at all. The
announced set therefore grows every time the enum does while the benign
set stays closed, so a rejection added later announces unless someone
deliberately excuses it, instead of falling silent by omission.
load_old_index (src/cli/context.cpp) is the standing example, with
IndexVersionMismatch the one excused code. The site keeps its one-line
comment and DESIGN.md carries the class law; the law ranges over filters
whose default outcome would otherwise be silence, not over any dispatch
switch whose every arm acts.
ErrorCode is an in-process vocabulary. No value is serialized, mapped
back from persisted bytes, or emitted as a stable token in machine-read
output -- the on-disk record carries its own magic, version and layout
instead. Enumerators may therefore be added, renamed or deleted freely,
and have been: IndexTruncated and InvalidFormat are both gone from the
enum. That freedom lasts exactly as long as the in-process boundary
holds, which is the reason to write it down rather than leave it for
whoever first wants to print a code to rediscover, or to violate.
Atomicity entitles the reader to a conclusion, and that conclusion is
what makes announcing damage honest rather than presumptuous. Because an
interrupted write leaves the previous record in place instead of a prefix
of the new one, a record that fails validation was damaged after putup
wrote it, by something other than putup's own writer -- so the reader may
announce damage rather than suspect a half-finished write of its own. The
entitlement is wholeness, not freshness: which whole record survives a
crash turns on directory-entry durability, which is the filesystem's
affair. It covers the files written through pup::platform::atomic_write,
not putup's outputs generally, since build outputs are written by user
commands under no such regime. A whole record from outside the readable
version window is not damage either.
Doc-only, and nothing further is owed: the code fix shipped earlier in
the cycle -- src/index/reader.cpp already returns IndexDamaged for the
too-small and bad-magic rows -- and REQ-READ-ANNOUNCE-DAMAGE
(spec/requirements/record-read.ears.md) already pins the observable half
with four discharges. `make spec-check` passes at 105 requirements and 0
gaps. No suite was run and none is needed; nothing under src/ or test/
changed.
Each claim was checked against the line it describes rather than against
the cycle's own notes, and a second model reviewed the prose and swept
independently for any site that persists or prints an ErrorCode as a
machine-read token, finding none. One leg is not verified here: that a
Win32 replacement leaves the previous record whole rests on external
documentation, because no crash-injection test for atomic_write exists on
either platform. The prose claims wholeness rather than strict
MoveFileExW atomicity, so it is not hedged -- but nothing in this tree
exercises it.
The stale "Write process" list under ### IndexWriter is deliberately left
untouched. It misplaces both destination and layer: serialization builds
a buffer in memory (src/index/writer.cpp) and the temporary file is
created inside atomic_write, so the checksum and footer are produced
during serialization rather than after any file exists. That defect is
independent of this change and gets its own issue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wx1bWwRf23eFT82y9D641
Doc-only. The #383 cycle settled three questions and left every answer
implicit in the code, where it reads as a choice someone made rather than a
rule someone must keep. This states all three in DESIGN.md, at the sections
that already own them. 29 added lines, one file, no deletions.
An announcement filter announces by default (§ Error Handling).
Enumerating error codes selects the wording of a particular announcement, or
excuses one proven benign; it is never the condition for being announced at
all. The announced set grows every time the enum does while the benign set
stays closed, so a rejection added later announces unless deliberately
excused.
load_old_index(src/cli/context.cpp) is the standing example,with
IndexVersionMismatchthe one excused code. The law ranges overfilters whose default outcome would otherwise be silence — not over any
dispatch switch whose every arm acts.
ErrorCodeis an in-process vocabulary (§ Error Handling). Nothing isserialized, mapped back from persisted bytes, or emitted as a stable token
in machine-read output; the on-disk record carries its own magic, version
and layout. Enumerators may be added, renamed or deleted freely and have
been —
IndexTruncatedandInvalidFormatare both gone from the enum.That freedom lasts exactly as long as the in-process boundary holds, which
is the reason to write it down rather than leave it to be rediscovered, or
violated, by whoever first wants to print a code.
Atomicity entitles the reader to a conclusion (§ IndexWriter). Because
an interrupted write leaves the previous record in place instead of a prefix
of the new one, a record that fails validation was damaged after putup
wrote it, by something other than putup's own writer — so the reader may
announce damage instead of suspecting a half-finished write of its own. The
entitlement is wholeness, not freshness; it covers files written through
pup::platform::atomic_write, not build outputs, which user commands writeunder no such regime.
Verification
make spec-check: pass, 105 requirements, 0 gaps.src/ortest/changed.cycle's notes:
src/index/reader.cpp(IndexDamagedon the too-small andbad-magic rows),
src/cli/context.cpp(the filter's default arm),src/index/writer.cppand bothatomic_writeimplementations,spec/requirements/record-read.ears.md(REQ-READ-ANNOUNCE-DAMAGE).persisting or printing an
ErrorCodeas a machine-read token — the claimlikeliest to be false — and found none.
One leg is not verified here
That a Win32 replacement leaves the previous record whole rests on
external documentation, not on anything runnable in this tree: no
crash-injection test for
atomic_writeexists on either platform. The proseclaims wholeness rather than strict
MoveFileExWatomicity, so it is nothedged — but nothing here exercises it. Pinning that leg is crash injection,
a code-and-test task, out of scope for a doc-only change.
Deliberately not fixed
The "Write process" list under § IndexWriter is stale and left untouched. It
misplaces both destination and layer: serialization builds a buffer in
memory and the temporary file is created inside
atomic_write, so thechecksum and footer are produced during serialization rather than after any
file exists. Independent of this change; a follow-up issue covers the whole
list.
🤖 Generated with Claude Code
https://claude.ai/code/session_014wx1bWwRf23eFT82y9D641