#!/usr/bin/env python3 """doc_census_check.py -- does a tool's own docstring census match the census that tool PRINTS? WHY THIS EXISTS This archive's three scanners each carry a census table in their module docstring: counts that a reader cites, that no command checks. The docstring is prose; prose is not executed. On 2026-10-16 the control plane returned exactly this defect against a sibling scanner, in its own words: "The scanner's own docstring carries a number that does not match the census it prints -- the exact class the tool exists to catch." tools/citation_resolve_check.py said `471 UNRESOLVED` on one line and `441 UNRESOLVED` nine lines later, over a census the tool prints as 471. Nothing was checking the docstring against the output beside it. That is this tool's whole job. WHAT IT MEASURES (read-only: this tool only writes --json) Two independent verdicts per tool, and they do not share evidence. 1. INTERNAL SPLIT -- one unit, one scope, two values. Within one docstring, every numeric claim of the form ` ` is collected; two claims of ONE unit that a single revision scope covers, carrying two different values, is a split. This catches the 441/471 instance. A split is GRADED only when a leg verified the unit (`CHECKABLE`: the run printed it with exactly one value); every other split -- no oracle (`LABEL_ONLY`, `--no-run`), a unit no leg prints (`NOT_A_CENSUS_UNIT`), a unit the tool prints per section (`SECTION_SPLIT_BY_TOOL`) -- is printed with its values as ADVISORY and never reaches the gate. 2026-10-18: the old build failed `--fail --no-run` on the 471/441 split at fca3f2e (rc 1) while the full run could not see either value, so rc 1 meant "could not check this". 2. ORACLE -- the tool is RUN, at the revision its own docstring names, and each claim is compared against the value that run prints for that unit. MATCHES claim == printed value STALE claim != printed value, and the printed unit is not declared clone-local -- a defect claim CLONE_LOCAL claim != printed value but the tool's own output marks that unit clone-local ("blobs in THIS clone's local object store"), so drift is the documented expectation, not a defect. Reported, never counted as one. AMBIGUOUS the tool prints that unit with 2+ values at that rev (e.g. `citations` prints once per section), so there is no single oracle value; both are printed and no claim is made NOT_PRINTED the unit does not appear in this tool's output at all A claim is ` ` with the unit word ADJACENT, which is the archive's own notation. A bare number is not a claim and is not checked. TWO ORACLE LEGS, AND WHY THE SECOND ONE EXISTS Leg 1 is the tool's own run at the block's revision, exactly as above. Leg 2 is the SAME TOOL'S `--selftest`, added 2026-10-16 after the control plane returned a docstring claim of `13 cells` against a selftest that prints `18 cells` -- the defect class this tool exists to catch, sitting inside its own default scope and still read as NOT_PRINTED, because no leg ever passed `--selftest` and a cell count is printed by nothing else. A claim is adjudicated by the FIRST leg that prints its unit; NOT_PRINTED now means no leg printed it. A tool that takes no `--selftest` simply has one leg -- a fact about the tool, not a defect in it. A leg that exits non-zero having printed nothing is reported RUN_FAILED, never read as an empty census, and it adjudicates nothing either: the block's claims are still read off any surviving leg -- leg 2 -- and are left unadjudicated only when no leg survives, because a failed run is not a reason to stop looking. A revision that will not materialise fails the same way rather than producing a census at all: an empty queue is a census of zeros, and reading it as that revision's census is the phantom this rule refuses. A HEX RUN INTRODUCED AS A DIGEST IS NOT A REVISION. A block that quotes a truncated sha256 in its prose is a size, not a place: read as a rev it files the block under a revision that cannot exist, fails that leg, and then adjudicates every claim in the block again beside the real ones. Two signals are read, both local to the token -- a digest word in front of it (`sha256 `, `hash `), or an ellipsis behind it (`…`), which is how this archive writes a truncated digest. See `block_revs`. The run leg is `