Commit Graph
3 Commits
Author SHA1 Message Date
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