Commit Graph
16 Commits
Author SHA1 Message Date
volt c3d70567a6 volt: verify gains a seventh leg -- doc-commands, graded at WORKTREE
The gate had six legs and none of them read a docstring's own command line.
doc_command_check.py existed since 838750c but only as a tool: nothing on the
gate ran it, so a documented flag its parser never learned could land and
nobody read it. This is that leg.

  doc-commands   tools/doc_command_check.py --rev WORKTREE --fail
                 WORKTREE, not HEAD: the commit window is where a docstring is
                 edited. Measured at the worktree this lands in: 67 commands
                 over 19 oracle tools, 67 OK, rc 0.

doc_command_check.py learns --rev WORKTREE: tracked paths from `git ls-files`
(an untracked tools/x.py is not part of the commit this mode grades), bytes
from disk. Outside a work tree it is rc 2, not an empty clean run. Selftest
15 -> 18 cells: one document edited on disk but not committed is rc 0 at HEAD
and rc 1 at WORKTREE, and the flag it names is the edited one.

Three tools stopped defaulting to a fixed path on this host. verify.py,
selftest_sweep.py, write_probe.py, citation_resolve_check.py,
path_claim_check.py and doc_census_check.py now resolve DEFAULT_REPO from the
file's own location, so a bare run in a clone grades the clone.

Three selftests refuse unreadable input at rc 2 instead of reporting it as
failed content: tool_map (not a work tree), write_probe (no table), and
citation_resolve_check (no corpus). Each cell below them graded `repo`, so an
absent tree read as "your table is wrong".

Evidence, all measured on this tree before the commit:
  verify.py --selftest              55 cells, 55 passed   (docstring says 55)
  doc_command_check.py --selftest   18 cells, 18 passed   (baseline pinned 18)
  tool_map.py --repo . --rev WORKTREE --check docs/TOOLS.md   rc 0
  verify.py --repo .                7 legs, 6 pass, 1 fault -- selftest-sweep,
                                    tool_map's drift cell, which renders HEAD
                                    and so cannot pass inside a commit window
tools/selftest_baseline.tsv re-pins doc_command_check.py 15 -> 18 cells.
2026-10-22 22:05:55 +00:00
forge 95c6f90d4d forge: doc_census_check main() 3 scans -> 1 Counter pass, byte-identical 2026-10-21 18:51:31 +00:00
volt b0529e4592 tools: the write guard reaches the three flags named something other than --json
The write-axis leg grades ONE surface per tool: `--json PATH`. Measured at
c3ffee9 with caller_write_flags.py (asset below), tools/ offers 22 flags a
caller can aim for a write, across 21 tools: 19 are `--json` -- guarded, and
graded by the leg -- and three are named otherwise, none of them wrapped:

  tools/queue_index.py      :581   --write       BARE
  tools/tool_map.py         :510   --write       BARE
  tools/doc_census_check.py :1831  --save-vocab  BARE

BARE = `open(path, "w")` with no try. Aimed at a target inside a directory that
does not exist, each raised FileNotFoundError at rc 1 -- the exact defect class
2556dbb removed from 18 tools, still present on the surfaces the leg cannot
see because it asks for `--json`.

Each write is now wrapped in the same guard the `--json` path already uses: one
line on stderr naming the path, rc 2 ("could not grade"), no traceback. Nothing
else changed: no argument, no output format, no exit code on any other path.

VERIFIED, both directions, in a throwaway worktree (never this tree):
  /root/.hermes/wizard-outputs/volt/2026-10-21-write-axis-bites/write_flag_probe.py
  --rev c3ffee9   3 refusal rows TRACEBACK, rc 1   -> rc 1 (RED)
  (this tree)     3 refusal rows OK, rc 2, one stderr line naming the path,
                  and all 3 still deliver at a writable target (rc 0, file
                  non-empty)                      -> rc 0 (GREEN)
Evidence: evidence/write_flag_red.json, evidence/write_flag_green.json.

Selftests, unchanged counts: queue_index 58/58, tool_map 16/16,
doc_census_check 104/104 (including its gate e2e --save-vocab cells).
repo gate: 6 legs, 6 pass, 0 fault, rc 0 (selftest-sweep 21 tools 0 fatal).

REMAINING BLIND SPOT, stated so it is not read as closed: write_probe.py's
WRITE_PROBES table still probes `--json` only, so the next tool to add a
`--write` is ungraded by the gate until that table grows a second axis. The
static audit above (asset, 22/22 classified, 0 bare after this commit) is the
interim instrument.
2026-10-21 14:42:32 +00:00
volt 2556dbb0aa tools: the write path -- an unwritable --json is rc 2, one line, never a traceback
Measured at 67f94cc: 19 of the 20 tracked tools answered a --json target inside a nonexistent directory with an uncaught FileNotFoundError at rc 1. Only queue_append_audit.py already refused (rc 2). A traceback is fatal under this repo's contract, and rc 1 is the code that means a graded fault -- so a caller could not tell 'the grade is bad' from 'the artifact was never written'.

Landed: the grade first, its stdout report standing; the write wrapped, one stderr line naming the path, rc 2 (could not grade). gate_blame.py also moves its --json write ahead of the all-UNGRADABLE early return, so a write refusal is not shadowed by the cannot-grade return (an extra ']' and a mis-indented try in that hunk were fixed before commit; the module compiled and its selftest was 21/21).

tools/write_probe.py is the instrument: one probe per tracked tool, verdicts TRACEBACK / RC_OTHER / SILENT / UNNAMED / NO_REACH / OK, NO_PROBE fatal on the same rule as the read axis, and it probes itself. It is leg 6 of verify.py, and selftest_sweep.py now carries both a read probe and a malformed probe for it.

Verification at this commit follows in the next commit with docs/TOOLS.md and the queue record.
2026-10-21 14:15:19 +00:00
volt 495a591e08 doc_census_check (volt): a number ending a line and its unit opening the next is ONE claim -- joined only when structural, spent only when a leg prints that unit, every unbound pair named; 16-module-docstring A/B 134->140 (+6, -0), 4-tool census +1 MATCHES and 0 verdicts moved, selftest 82->104 cells 2026-10-20 14:35:07 +00:00
timmy a2d9bc0654 tools (volt): doc_census_check.py -- a colon before the number is a line cite, not a count
Timmy's reply 31 logged the defect: `--fail` on `tools/queue_claim_check.py` was
red with 2 STALE (22 claims, 0 split, 0 leg failures), because that docstring
records its own past behaviour as "graded `:7303` CLOSED on Mercury's `:9869`"
and "printed `open set: Q37:5278`". The claim reader took 7303 for a count of
CLOSED rows (printed 13) and of open rows (printed 2). Both are queue LINE
NUMBERS in backticks -- the shape CLAIM_EQ already refuses one rule over
("`WORK-QUEUE.md:8586` is a citation") and the value/unit form had no guard for.

Measured over every tracked docstring at HEAD: 123 claims read, 11 of them
carrying a ':' immediately before the number, across four tools --
citation_hash_class.py (:8467 defect), citation_resolve_check.py (four: :8003
cites, :6348 column, :9 reads, :8530 Mercury), queue_claim_check.py (five, the
two false STALEs among them), queue_index.py (:7246 prose). No one of them names
a unit the tool prints. ':' joins the CLAIM lookbehind.

Same four tools, `--fail`, before -> after:
  claims     8/26/22/9 -> 7/22/17/8   (65 -> 54, the 11 cites)
  STALE      2 -> 0 ;  rc 1 -> 0
  MATCHES    19 -> 19 ; AMBIGUOUS 10 -> 10 ; REV_UNCHECKED 3 -> 3
No verified row moved, which is the invariant the hyphen rule shipped under.

Selftest 80 -> 82 cells; sweep's advertised count 80 -> 82 in the USAGE block.
The two new cells are RED->GREEN measured on both builds, one call apart: old
reads [('CLOSED',7303)] and [('while',5278),('rows',13)], new reads [] and
[('rows',13)]. verify.py 3/3 legs rc 0; selftest 82/82.
2026-10-19 04:53:38 +00:00
volt 0b67585331 tools (volt): doc_census_check -- the cache writes only what a claim can spend, and the fast mode says what it cannot grade
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.
2026-10-18 12:40:19 +00:00
volt ecdaecd7cb tools (volt): doc_census_check -- a cached vocabulary gives the fast mode its teeth back
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.
2026-10-18 12:40:19 +00:00
volt fcfb323180 tools (volt): doc_census_check -- rc 1 must mean 'verified faulty', not 'could not check'
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.
2026-10-18 12:40:19 +00:00
volt 26056d2a21 tools (volt): doc_census_check -- the gate, and a hyphenated unit is not a unit
`--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.
2026-10-18 07:35:53 +00:00
volt ac0e211ad1 tools (volt): doc_census_check -- the claim separator was a class, not an adjacency
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.
2026-10-18 04:55:56 +00:00
volt 76043b7a3a tools (volt): doc_census_check -- a --tools filter that matches nothing is rc 2, not '0 tools scanned' + rc 0; 35 cells 2026-10-18 00:26:14 +00:00
volt 6ee8fcee5a tools (volt): the "could not grade" class gets one code -- rc 2 -- and a gate that measures it on every tool
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.
2026-10-17 22:08:19 +00:00
volt f051659581 tools (volt): a sha256 prefix in a block's prose is not that block's revision
The two-leg tool read every REV_LONG token in a docstring block as a revision of
this repository. A block that quotes the queue's size as "/ sha256
`fbe51445a89f81ab…`" therefore named a revision that cannot exist: the leg raised
`git show fbe51445a89f81ab:WORK-QUEUE.md -> rc=128`, the block was counted as a
leg failure, and -- because the oracle loop runs once per rev -- each of its
claims was emitted AGAIN beside the real ones. Two spurious failures and six
duplicate rows, in the tool whose whole job is to make a docstring figure
adjudicable.

WHAT IS LANDED (908 -> 952 lines, 45,034 -> 47,501 B, sha256 5c69c2d2 ->
c90ffab4; the diff is one new helper, three call sites, three selftest cells and
the docstring paragraph that states the rule):

1. `block_revs(text)` -- the revisions a block names, with hex runs read as
   digests excluded. Two signals, both local to the token: a digest word in
   front of it (`sha256 <hex>`, `hash <hex>`, `sha1`/`md5`/`digest`), or an
   ellipsis behind it (`<hex>…`), which is how this archive writes a truncated
   digest. A FULL sha256 was never at risk: REV_LONG is {7,40} and a 64-hex run
   has no word boundary inside it.
2. `check_tool` (the live path) and `_fixture_checks` (the fixture) both call it,
   so the fixture and the tool read revs by the same rule.
3. Three selftest cells: each signal exercised alone, with the ordinary citation
   between them as the negative control.
4. The docstring says so. It also states the one silent downgrade this rule can
   cause -- a revision cited WITH an ellipsis is read as a digest and the block
   falls back to HEAD -- and that side is measured, not guessed: `grep -nEo
   '[0-9a-f]{7,40}(…|\.\.\.)' tools/*.py` returns 8 tokens at HEAD and every one
   is a digest or an oid quoted as a token, never a revision.

MEASURED DELTA, all 7 tools at HEAD, before = c90ffab4's predecessor run
(census-final.json) vs after (census-after.json), compare_rows.py:

  rows          111 -> 96   (removed 18, added 3, changed 0)
  leg failures    2 -> 0
  verdicts      8 AMBIGUOUS / 16 MATCHES / 75 NOT_PRINTED / 2 REV_UNCHECKED /
                10 SELF_NOT_RUN
            ->  8 AMBIGUOUS / 16 MATCHES / 60 NOT_PRINTED / 2 REV_UNCHECKED /
                10 SELF_NOT_RUN
  per tool      citation_hash_class.py 28 -> 19 rows, queue_claim_check.py
                34 -> 28 rows, the other five unchanged

Every removed row is a duplicate emitted under a digest; every added row is the
same claim re-scoped to the rev the block actually names. No verdict CHANGED --
the fix removes rows, it does not re-judge any. The queue block is now read at
`28ca8e5` and `29a4872`, the two commits it cites, instead of at those commits
plus the two digests it quotes.

TWO INSTRUMENTS, NOT ONE. `--selftest` is 33 cells / 33 passed / 0 failed, rc 0
(30 -> 33 with the three new cells), and it is the instrument that pins the rule
in isolation. The rule's effect on live text is pinned separately by the scoped
live run, 1.8 s: `--tools tools/queue_claim_check.py` yields 0 leg failures and
28 rows, against 2 and 34 for the same file the commit before. A cell for the
rule plus a run of the rule, because the last defect in this tool was a rule its
own fixture could not see.

FOUND WHILE MEASURING, and worth a line because it cost a red run: an auxiliary
script named `inspect.py` written beside a copy of the tool shadows the stdlib
`inspect` for anyone importing it from that directory (`dataclasses` does), and
the tool died with a traceback that named argparse. The copy is `census_rows.py`
now; the lesson is that the scratch directory is sys.path[0] for the scripts in
it.
2026-10-16 22:55:27 +00:00
volt 0031a7831f tools (volt): the two-leg doc_census_check -- leg 2 is the tool's own --selftest, and the seed defect was invisible without it
The scanner reads a tool's module docstring, compares every `<value> <unit>`
claim against the census that tool PRINTS, and until this commit it had one
oracle: the tool's own run. So a claim about a number the run does not print
read NOT_PRINTED -- and `13 cells` against a selftest that prints `18 cells`,
the exact defect class this tool was built to catch, sat inside its own default
scope and was never adjudicated.

WHAT IS LANDED (907 -> 908 lines; the only edit in this commit is the splice
below, byte-measured in the check-in):

1. Leg 2 -- the tool's own `--selftest` -- is a second oracle leg, carrying the
   tag `selftest`. A claim is adjudicated by the FIRST leg that prints its unit;
   NOT_PRINTED now means no leg printed it. A tool with no --selftest simply has
   one leg: a fact about the tool, not a defect in it.
2. REV_UNCHECKED -- neither a `no-rev` leg (it measured the working tree) nor
   the selftest leg (it measured the tool's own fixtures) is a verifier for a
   block that names a revision. Such a claim is reported, never counted as a
   match and never counted as a defect.
3. SELF_NOT_RUN -- this scanner is in its own default scope, and running it on
   itself spawns it again without a floor, so its own claims are printed as rows
   and excluded from the run leg.
4. RUN_FAILED, and the fall-through: a leg that produced no census adjudicates
   nothing, and a surviving leg still answers the block's claims. A recipe whose
   revision will not materialise raises rather than handing the tool the empty
   file it left behind -- an empty queue is a census of zeros, and a census of
   zeros reads as that revision's census.
5. A recipe for append_only_owners.py (tag `no-rev`), and the argparse
   fall-through: a tool that rejects `--repo R --rev REV` is re-run with exactly
   the tokens argparse named dropped, tagged `no-rev`.

MEASURED DELTA, same 6 tools at HEAD, HEAD tool vs this one (compare.py on
head6.json / cand6.json): 101 verdict rows, 13 MATCHES -> 16, 80 NOT_PRINTED ->
75, 8 AMBIGUOUS -> 8, 0 -> 2 REV_UNCHECKED. Five rows change and three of them
are substantive: append_only_audit.py `18 cells` NOT_PRINTED -> MATCHES (leg 2);
append_only_owners.py `2 folded` / `10 raw` NOT_PRINTED -> REV_UNCHECKED (the
honest non-verdict); queue_claim_check.py `0 bold` twice NOT_PRINTED -> MATCHES
by notation (the docstring phrase `column-0 bold` is read as the claim `0 bold`;
0 = 0 on a number that counts nothing -- do not cite the new total without that
split).

THE SPLICE THIS COMMIT FIXES, and why it is in the same commit: the working-tree
revision carried a paragraph merged from two drafts -- "answers that with an
argparse diagnosis and no a census at all, silently. It is now re-run ..." --
and a docstring that does not say what the code does is the defect this tool
exists to catch. The correction is documentation only; behaviour is unchanged
and the selftest is 30 cells, 30 passed, 0 failed, rc 0, before and after. The
tool never checks its own docstring (SELF_NOT_RUN), so this one was read by hand.
2026-10-16 22:38:20 +00:00
timmy f5403b2510 tools (volt): the docstring-vs-printed-census checker, and the defect it was built to catch fixed
tools/doc_census_check.py (548 L / 25,058 B / sha256 dbbbdc01bb19dbcbbe87141c90a80e0bff19c98557c80f91c81347c53c98f1c5) checks a scanner's module docstring against the census that scanner PRINTS, two independent ways: (1) INTERNAL SPLIT -- one unit, one revision scope, two values in one paragraph, caught with no subprocess at all; (2) ORACLE -- the tool is re-run at the revision its docstring names and every '<value> <unit>' claim is compared with the value that run prints (MATCHES / STALE / CLONE_LOCAL / AMBIGUOUS / NOT_PRINTED). A unit the tool prints with two values is SECTION_SPLIT_BY_TOOL and no claim is made about it; a unit the tool never prints is NOT_A_CENSUS_UNIT and no claim is made about it either. Selftest 14 fixtures, 14 passed, rc 0; the only write path is --json.

THE DEFECT IT CATCHES, AND THE FIX: the control plane returned it against this lane's own resolver (reply 04, d355395) -- tools/citation_resolve_check.py:82 stated 471 UNRESOLVED while :89 stated 441 UNRESOLVED, over a census the tool prints as 471. That is the first finding of the new tool on its first run (INTERNAL SPLIT UNRESOLVED [babdadc] 471@82 / 441@89; STALE UNRESOLVED claimed 441 at babdadc, printed 471). HERE IT IS FIXED, my file and my edit: 2 insertions / 2 deletions, both on the one line; byte count unchanged at 20,460 B because 441 and 471 are the same width; sha256 7d063b39e2e4f9e5691b720cb57895b29450ece77051a7db6a8584dfd7173237 -> 40314b34f9860c4c42623f669c1339ed1d7b0ffc5fe604d56d6e9112b893e3a6. The fix is docstring-only and changes no measurement: the tool's own selftest is 11 fixtures / 11 passed / rc 0 both ways, and the census at babdadc is byte-for-byte the same after the edit as before it (297 files; EXPLICIT 410 = 113 RESOLVES / 0 STALE_LINE / 297 EXTERNAL_PATH; BARE 1612 = 119 AS_NAMED / 716 AS_SELF / 51 AMBIGUOUS / 255 EXTERNAL_NAMED / 471 UNRESOLVED; 6,961 mentions; 101 distinct targets).

MEASURED COVERAGE, stated narrow rather than rounded up: at d355395 the run read 92 claims across the three scanners -- 12 verified against the run they cite, 8 in units the tool prints with two values (no oracle), 71 in units the tool never prints. The oracle reported exactly one bad claim on that corpus, and it was the returned defect. A WIDER RULE WAS TRIED FIRST AND FALSIFIED BY ITS OWN FIRST RUN: tools/num_agreement_check.py, block-scope same-unit-two-values over the whole archive, flagged 447 restatements at first and 117 after an isolation rule (240 chars) -- too coarse to be read, and it is NOT landed; the file is kept out of the tree as evidence. Read-only in fact: only write path is --json, git status --porcelain clean after every run. No other file touched, no secret printed. Volt <volt@agents.invalid>.
2026-10-16 06:34:08 +00:00