Found while demonstrating the NO_PROBE fix: the gate graded selftest-sweep FAULT and
printed 'sweep: 15 tools, 14 OK, 0 fatal' beside it, because pick_detail took the
first hint-matching line and 'fatal' matched a summary asserting zero of them.
- pick_detail: zero-count summaries (' 0 fatal' / ' 0 fault') are second-pass only,
so a clean run keeps its sweep line and a red run names the red axis
- selftest 24 -> 26 cells: c13 a zero-fault summary is not a reason line,
c13b a clean run still gets its sweep line; both advertised counts updated
The read/malformed axes only covered tools named in READ_PROBES / NOT_UTF8_PROBES /
MALFORMED_PROBES. A 15th tool dropped into tools/ produced two advisory NO_PROBE rows
and the sweep still printed '15 tools, 14 OK, 0 fatal' with rc 0: measured on a copy of
the 14 tracked tools plus one probe-free tool, pre-fix rc 0 / post-fix rc 1.
- NO_PROBE joins FATAL_READ; the two rows now say 'ungraded, fatal'
- axis_faults() is the single red predicate, used by report_read
- selftest 28 -> 30 cells: c29 an unprobed tool is NO_PROBE on both axes and counts,
c30 every FATAL_READ verdict is red and OK is not
- two stale counts corrected in this tool's own docstring: 'ten'/'Nine tools must
refuse at rc 2' -> eleven (measured: 11 of 14 tools carry rc 2 probes)
index-current (queue_index --check), append-audit (queue_append_audit --guard
--to HEAD), selftest-sweep (skippable with --fast, printed SKIP and counted as
skipped). rc 0 graded clean / 1 graded a fault / 2 could not grade, with 2
outranking 1 so an ungradable leg cannot be read as a certified run; the fault
count is still printed beside it. Tools resolve from --repo, not from this
file's directory, so a worktree is graded with its own instruments.
selftest 24/24 (stub legs through the real CLI: rc mapping incl. retired rc 3,
missing tool, timeout, --fast, --json, missing repo, fixed order). Added to
both sweep probe axes (read-refusal 19/19, malformed 17/17, 14 tools 14 OK) and
the sweep's stale "15 such probes / nine tools" prose corrected to 17 / ten.
Green at 019b75c: 3/3 pass, rc 0. Red paths on a full 785-commit clone:
0 -> 1 (working-tree append, no re-render) -> 0 (--write) -> 1 (committed
stale: both legs faulted) -> 2 (tool absent; index fault still printed).
Queue tick appended and index re-rendered in this same commit.
The 15:15Z block adopted "append and re-render in the same commit" as a rule
and left enforcement to hand probes; that loop produced three wrong-slice
counts in one week. tools/queue_append_audit.py (8a450a3) grades each
queue-touching commit in its own tree. Measured at 8a450a3: default range 9
commits -- 6 PASS, 3 STALE (f2c3067, ece5228, 067afdf), 0 UNGRADABLE -- with
328 earlier queue-touching commits counted and not listed; --all lists all 337
(328 UNGRADABLE); --guard reads 1 PASS at c034175, rc 0. Reply 24's corrected
set reproduces from the commits by a second route, with its two rc-2 commits
(12b4497, 0bdb9d4) landing in UNGRADABLE as it said. The tool's fixture also
found and fixed a --write IndexError in queue_index.py (zero-byte document, rc
1) for any queue with no nested records; the real render did not move.
This commit is the append + the re-render, and --check reads rc 0 at it.
WHY: docs/queue-index.md pins WORK-QUEUE.md by sha256, so an append that
skips the re-render is STALE by construction, and nothing obliged a commit to
re-render. f2c3067 and 067afdf each left the gate red until a later repair
(2h37m for the second). Enforcing that rule by hand produced three wrong-slice
counts in one week; the 15:16Z correction names the cause -- a --check in a
working tree at HEAD says nothing about a cited revision.
queue_append_audit.py materialises each commit's own tree with git archive and
runs THAT tree's own tools/queue_index.py --check inside it. Verdicts: PASS
(rc 0), STALE (rc 1 with the tool's STALE line), UNGRADABLE (gate absent at
that rev, or the tool refused rc 2 -- kept out of the stale bucket), ERROR (a
crash wearing the verdict code, or a timeout). House contract: 0 clean, 1 a
graded fault, 2 could not grade. --guard grades the newest queue-touching
commit only, for wiring into a tick's check.sh. Default range begins at the
commit that introduced the index; --all lists the pre-gate commits instead of
hiding them. selftest: 25 cells, 25 passed, 0 failed; the tool advertises that
count on its own --selftest usage line.
Measured at b7e2bfd, /root/troupe-standup: 337 commits touch the queue; the
default range is 9 commits -- 6 PASS, 3 STALE (f2c3067, ece5228, 067afdf),
0 UNGRADABLE -- with 328 earlier commits counted and not listed. That is reply
24's corrected set exactly (its 2 rc-2 commits, 12b4497 and 0bdb9d4, fall
before the index and are UNGRADABLE under --all, not stale). --guard at HEAD
reads 1 PASS, rc 0.
The tool's own fixture found a real defect in queue_index.py: build_doc indexed
payload["nested"][0] unconditionally, so --write died with IndexError -- a
zero-byte document and rc 1, the code that means "graded a fault" -- on any
queue whose ids all carry headings. Fixed in nested_prose(); the non-empty
branch reproduces the old sentence byte for byte, so --check is rc 0 on the
real document and queue_index --selftest is 54/54 before and after.
selftest_sweep.py gains the new reader on BOTH input axes (missing repo path,
not-a-repo) instead of the NO_PROBE advisory: 13 tools, 13 OK, 0 fatal;
read-refusal 18 probes 18 ok, malformed-input 16 probes 16 ok.
The block names its HEAD in bullet 1 but not in the scope sentence; this repo's rule is that a claim citing a mutable count names its revision. Appended to the check-in rather than rewritten in the ledger, so history stands and the frame is recorded.
Measured at 69f3b56: queue_index --check -> rc 1, STALE at line 59, Q45 7594-9953/337280 in the document vs 7594-9962/340848 in the queue, pinned queue sha 34f1523c.. vs real 7e222ce2...
Cause by commit: git show --stat 067afdf = WORK-QUEUE.md, 1 file, 9 insertions. The index pins the queue sha256, so any queue write without a same-commit re-render is red by construction.
Controls (detached worktrees, same tool): 067afdf rc 1, ece5228 rc 1, 39312c2 rc 0. Since the index existed at 1423c4d: 6 queue-touching commits, 3 re-rendered, 3 left the pin stale.
Fix: --write, 9 insertions/9 deletions across 5 hunks (git diff -U1) of the generated index; no tool changed.
Also recorded in the ledger block: doc_census_check --fail --no-run grades 0 claims at 69f3b56 (no oracle), so it must not be wired as a gate before a vocabulary cache exists.
The full doc-census gate at 0b67585 went red on this tool's docstring, not on
its behaviour: STALE "tools/checkin_check.py:46 files claimed 70 at HEAD --
printed 71". Two unpinned mutable counts over checkins/ were behind it.
1. The A1 rationale said "52 of 70 files faulty ... 12 real name faults".
Neither figure is reproducible: checkin_check prints 57 missing-heading
files and BAD_NAME 25 at c374a0a, c213a5d, edaef63 and 588784d alike, and
70 files only at c374a0a (the rest carry 69). The sentence now states the
measured triple and pins it to c374a0a, the last revision that touches this
tool. checkin_check takes no --rev, so a rev-named block is REV_UNCHECKED by
the census tool's own rule -- visible, never graded, and it stops drifting
to STALE as check-ins land.
2. The READ-ONLY paragraph said "the 12 BAD_NAME files": the tool counts
BAD_NAME 25 at every revision tested and at HEAD. That sentence is about
policy, not a census, so the count is removed rather than re-pinned --
"the badly named files are the principals' history".
Measured after both edits: selftest 27/27 rc 0; targeted doc-census gate
--tools tools/checkin_check.py rc 0; full 12-tool gate and selftest sweep
rerun at the commit this message lands in (artifacts under
/root/.hermes/wizard-outputs/volt/2026-10-18-vocab-minimal/).
Two defects left open by the 30c8120 vocab gate, both closed here and both
measured rather than asserted.
1. --save-vocab wrote every key the legs printed (422 at 30c8120), including
207 hex-like digests and append_only_audit's diffstat words. Those are
permissions no claim can ever spend: vocab_has() asks keys_for(unit, ...)
over the units ONE docstring claims, so the reachable key set is fixed by
that docstring. spendable_keys() is that same matcher, and the saved file is
now the intersection with it -- 422 keys observed on this tree, 24 written,
the other 398 unspendable by construction.
2. A runless split in a lowercase word printed nothing at all: unit_scope
answered UNCHECKED_NO_ORACLE and the printer showed only the LABEL_ONLY
class, so the vocab-precision fixture's case E (5 above / 7 above, no
vocabulary) exited 0 with no line. Silence is not a verdict; both runless
classes now print ADVISORY with their values and neither is graded.
Measured: fixture A..E rc vector [1,1,0,0,0] before and after, with case E
printing nothing before and its advisory line after; the scoping is
OUTCOME-NEUTRAL by exhaustive check -- for all 96 units the 12 tracked tools
claim, "named by the vocabulary" is the same answer before and after; the fast
gate is rc 0 at 30c8120 on the old 422-key file and rc 0 on the scoped one; and
the fca3f2e 441/471 tooth is still rc 1 from the cache. It does NOT close the
cache's over-reach -- a word the docstring really does claim twice is still
spendable -- and a selftest cell now pins that limit instead of leaving it in a
report. Selftest 72 -> 80 cells, 80/80 rc 0.
Reply 22 ruled the asymmetry a defect and asked that `--fail` never return 1 on
a split it could not verify. 814acc4 met that by making every runless split
ADVISORY -- which also empties the fast gate, measured: on two fixtures (a STALE
claim, an internal split) `--fail --no-run` returns 0 for both, and no path can
return 1, because gate_faults() reads only CHECKABLE splits and STALE verdicts
and neither exists without a run.
So the fast mode gets the oracle it can have. `--save-vocab PATH` (full run)
writes the census vocabulary the run OBSERVED -- unit keys per tool, read off
leg stdout, never off docstrings -- and `--no-run --vocab PATH` grades a split
in a unit that vocabulary names as CACHE_CHECKABLE. What the cache proves is
unit-hood; the graded disagreement is internal to the docstring, so no value is
needed, and no STALE claim can enter this way (pinned by fixture).
Measured at fca3f2e, tools/citation_resolve_check.py, one claim:
--no-run --fail rc 0 ADVISORY UNRESOLVED: 471@82, 441@89
--no-run --vocab V --fail rc 1 0.205s, SPLIT ... (CACHE_CHECKABLE,
vocabulary at fca3f2e): 471@82, 441@89
--fail rc 1 SPLIT (CHECKABLE) + STALE :89
Selftest 60 -> 72 cells (5 vocabulary cells, 6 process-exit cells);
sweep 12/12, read-refusal 17/17, malformed-input 15/15, rc 0.
rc 2 covers an unreadable --vocab and --save-vocab without a run.
A split enters the graded list only via CHECKABLE (a leg ran and printed the
unit with one value). Every other reason is printed with its values as
ADVISORY and never reaches the gate, so --fail --no-run can no longer return 1
on a split it had no oracle for. Ruling: reply 22, 2026-10-18.
Measured at fca3f2e before/after, same tool and rev:
--fail --no-run rc 1, SPLIT UNRESOLVED (LABEL_ONLY): 471@82, 441@89
--fail --no-run rc 0, ADVISORY UNRESOLVED (LABEL_ONLY): ... reported, not graded
--fail (full) rc 1 both times: SPLIT (CHECKABLE) + STALE :89 -- teeth kept.
Selftest 52 -> 60 cells (6 mode cells, 2 process-exit cells); sweep 12/12.
`--fail` moves the verdict into the exit code: rc 0 ran and graded no fault,
rc 1 ran and graded a STALE claim or an INTERNAL SPLIT, rc 2 could not grade
(usage, a --tools filter matching nothing tracked, unreadable input). Before
this, the process exited 0 while printing STALE rows, so nothing could gate on
the one class the tool exists to catch.
Armed at HEAD, the gate went red on its own first run -- on a false claim, not
a defect: `queue_claim_check.py`'s axis list says `39 ACS-only lines carry no
Q<digits> token` (and 12, and 1) and the token read as a unit was the
adjective `ACS` in `ACS-only`; with no oracle to test the label against,
--no-run split 39/12/1. The reader now refuses a unit immediately followed by
`-`, the same refusal it already made for a hyphen behind the number
(`120-char`).
Measured cost of the tightening at bacdead, from --json before/after: 6 claims
no longer read -- queue_claim_check 23 -> 19, checkin_check 8 -> 7,
queue_index 10 -> 9 -- 5 NOT_PRINTED and 1 REV_UNCHECKED, every one of them the
first half of a compound (`ACS-only`, `check-in`, `self-test`, `follow-on`).
The invariant that licenses it holds: MATCHES 26 before and after, identical
rows. A genuinely hyphenated unit (`citations-per-block`) is no longer read;
the tool states that limit in its docstring rather than discovering it.
selftest 41 -> 52 cells (5 decision cells, 4 process-exit cells that prove a
helper returning 1 is not a gate); sweep 12/12 OK, read-refusal 17/17,
malformed 15/15; `--fail` at HEAD: 0 INTERNAL SPLIT, 0 STALE, rc 0; the same
gate on fca3f2e: 1 split, 1 STALE, rc 1.
The full census at 4ce1aed (the run my 2026-10-18 check-in owed) reported two
STALE claims against checkin_check.py:59 -- "read as UTF-8. Never 1, never a
traceback." -- where the 8 is the tail of UTF-8 and the 1 is an exit code.
Both are false defect claims.
Measured over all 12 committed docstrings: 140 claims read, 95 real. The rest
were welded hex (83acffe0 -> 83 acffe0, 11), ordinals/sentence ends (1.
INTERNAL, 8. Never, 5), compound adjectives (120-char, 5), list markers (0
ran, 4) and trailing commas (1, which -> claim 1 of unit which, 8).
Three rules: sep is one space or a closing delimiter + space; num is
[0-9]+(?:,[0-9]{3})*, so a trailing comma is punctuation; ordinal_at() refuses
a line-leading number followed by 2+ spaces. Six cells: 35 -> 41.
Same revision, same docstrings, after: 145 -> 105 verdict rows, STALE 2 -> 0,
NOT_PRINTED 87 -> 52, and the 26 MATCHES are the same 26 rows (lost: [],
gained: []) -- no adjudicated verification was dropped.
selftest 41/41 rc 0; selftest_sweep 12 tools 12 OK 0 fatal; read-refusal 17/17;
malformed-input 15/15.
Named, not fixed: doc_census_check returns 0 with STALE > 0 and has no --fail,
so a gate cannot fail on the class it exists to catch.
The read-refusal axis measured missing paths and non-UTF-8 bytes. The class in
between was unmeasured: a .json that is not JSON, a {} where an audit file should
be, a directory that is not a git repo, a queue with no records -- the inputs
where a tool is most tempted to print 0 and exit 0, which reads like a clean pass
(the doc_census_check --tools bug, 76043b7).
15 probes over all 12 tools, each graded against the answer its OWN contract
states (nine refuse at rc 2; check_lint grades MISSING at 0/1, checkin_check
advisory at 0, queue_claim_check 0 opening records at 0). judge_malformed adds
RC_WRONG, UNEXPLAINED next to TRACEBACK/SILENT, and requires a word, so a silent
0 cannot pass.
15/15 at HEAD -- no tool was wrong, none was measured. Against 461eede's copies
the same axis reads 7 fault (3 tracebacks, 3 wrong codes, 1 NO_TOOL). Against a
copy with one bug reintroduced into append_only_owners.py it reads RC_WRONG while
that tool's own 15-cell selftest stays green.
queue_claim_check.py's pinned docstring figures (ACS=72, BQ=80, overlap=20,
BQ-only=60, symmetric difference=112, "72 as 80 minus 8") no longer matched
what --census prints at HEAD abe08a9 (WORK-QUEUE.md 9,953 lines /
1,229,541 B): ACS=73, BQ=85, overlap=21, BQ-only=64, symmetric difference=116
("73 as 85 minus 12"). Doc census flagged the STALE claim
("difference claimed 112 at HEAD -- printed 116").
Matcher code unchanged; per-axis breakdown (18/46/39/12/1) already matched
measurement. Docstring block realigned to re-measured values and now cites the
re-measurement so the block is self-auditing.
--census: exit 0, all pinned figures identical to printed output.
--selftest: exit 0, 16/16 fixtures pass.
Measured at 39312c2 with doc_census_check --tools tools/queue_index.py: two
STALE claims and one INTERNAL SPLIT. ":5" said the queue is 9,901 lines (it
was 9,937 before this run's append, 9,953 after) and ":25" said Q53 is first
named "46 lines before" it is opened -- a difference, which the census read as
a line-count claim of 46 against a 9,953-line queue.
Both are fixed the way this archive already requires (name the rev with the
number): ":5" now reads "9,953 lines long at 39312c2", so the claim is
checkable at the revision it describes rather than stale at every later one,
and ":25" states the two line numbers instead of their difference.
No behaviour change: selftest 54/54, sweep 11 tools / 11 OK / 0 fatal,
read-refusal 15/15.
Records the unit landed at 6ee8fce and appends the queue tick block; the index
is regenerated in the same commit so --check is rc 0 here (49 sections, 45 ids,
9 nested ids, 9953 lines / 1229541 bytes).
The carry discharged was my own, filed at 2026-10-17-volt.md:525-527 and left
standing by reply 18: check_lint and census_row_delta used rc 1 for both "a
graded fault" and "I could not read your file". Measuring all eleven tools
found six, across three codes (two raw tracebacks at rc 1, four named messages
at rc 1, two queue readers at rc 3). Decision: rc 0 clean / rc 1 graded fault /
rc 2 could not grade, rc 3 retired. The gate is the durable half: a 15-probe
read-refusal axis in selftest_sweep.py, which reads 11 faults against HEAD's
own tool copies and 0 faults here.
Also repair reply 18's attribution FYI (.mailmap folds volt's five spellings to
Volt <volt@agents.invalid>: five lines/31 commits -> one line/31; the commit
carries its identity via -c, not by rewriting the shared repo config).
Six tools answered an input they could not read with a code that means
something else: check_lint and census_row_delta exited 1 (the code that
means "the grading found a fault"), the two append-only tools exited 1 out
of a raw traceback, selftest_sweep exited 1 with "0 tools, nothing
discovered", and the two queue readers exited 3 -- one meaning, three
codes. Measured before the change: 11 of 15 broken-input probes were
mis-coded (2 tracebacks, 9 wrong code).
The contract is now three codes and no others: rc 0 ran clean, rc 1 ran
and graded a fault, rc 2 could not grade (usage, precondition, unreadable
input). rc 3 is retired.
And the class is gated, not merely fixed: selftest_sweep.py gains a
read-refusal axis -- 15 probes (11 missing-input, 4 non-UTF-8) run
against the real CLI in a throwaway cwd, graded OK / RC_NOT_2 / TRACEBACK
/ SILENT, fatal on any of the last three. On HEAD's tools the axis reads
11 faults and exits 1; on this tree it reads 15 ok and exits 0.
Also: doc_census_check.py advertises its own count (33 cells) so the sweep
has no advisory row left (11 tools, 11 OK); .mailmap folds volt's five
commit spellings (18+7+3+1+1 = 30 commits) to Volt <volt@agents.invalid>,
the identity 016d26e lost to the repo's Timmy config.
Selftests: 18/15/33/37/54/16/22, 0 failed. Sweep 11 tools, 11 OK, 0 fatal.
queue_index --check rc 0 (49 sections, 45 ids, 9 nested). landing_check
346/346.
Five tracebacked on input they could not read (rc 1, the code this corpus uses for
"a finding"); two -- citation_hash_class.py, citation_resolve_check.py -- printed a
clean zero census over a missing repo or bogus --rev and exited 0, because git() in
both discards the return code. Those two are the gates a reviewer runs to decide
whether a citation defect exists, so a green that never looked is the hazard.
queue_index.py / queue_claim_check.py: read errors -> rc 3 (the claim tool's docstring
already promised `3 queue unreadable` while its code raised UnicodeDecodeError).
citation_hash_class.py / citation_resolve_check.py: precondition(repo, rev) -> rc 2,
matching doc_census_check.py and landing_check.py. check_lint.py / census_row_delta.py:
named message, no traceback, rc 1 kept (filed: usage error and graded fault still
share that code).
175 insertions(+), 14 deletions(-). Selftests 54/54, 16/16, 37/37, 33/33, 19/19, 11/11;
sweep 11 tools/10 OK/0 fatal; --check rc 0 at 9 nested ids; 9 broken-input probes ->
0 tracebacks; copy-vs-copy A/B 7 SAME / 2 DIFFERS (both intended added cells).
run.md's earlier "7 SAME, 0 DIFFERS" claim was wrong and is corrected in the artifact.