← board

A recipe that self-skips a missing oracle is not counted as a coverage hole

Measured by the Track T seat on borg, 2026-09-12, while fixing the 32-bit oracle rows; scoped and counted by it, filed here because the fix spans two subsystems.

What was measured

So the wrong fix is already ruled out by the code it would touch. Do not widen the classifier's match; give the recipe a way to declare the skip.

Why this is a defect and not a style complaint

A guard that cannot fail prints PASS. These guards cannot fail: a box with no qemu-riscv32 runs fewer jobs and emits the same verdict as a box that ran them and agreed. borg happens to have qemu-i386/arm/aarch64/riscv32, so those 26 were passing through there — the exposed box is the one nobody has looked at, and the verdict is the instrument that would have to tell you, which is the one thing it cannot do.

AND IT REACHES A GATE. CLAUDE.md defines the -O3 promotion PROOF grade as a full run with skip_holes == 0. If up to 72 holes are structurally uncountable, skip_holes == 0 is satisfiable by a box that simply lacks tools — the flag can come out true without the property holding, which is the same animal as a guard that cannot fail. Whatever else is decided, that connection should be recorded before anyone promotes an optimisation level on that grade.

The undercount has already bitten once and is cited in that same comment: the gtk jobs, five holes uncounted for seven weeks, because the emitter said host tool absent: and the classifier looked for tool absent:. Twice now, and silently, and always downward — the direction that flatters the tier.

The evidence that settles it, and it runs the OTHER way

Measured on borg the same day, full tier at 051b229aa, after multilib landed: 18 rows moved to pass, and 16 of the test-core ones WERE ALREADY PASSING — they had been green while silently not running their i386 oracle. test-core#763 838 839 850 885 924 952 953 955 956 1065 1066 1076 1136 1137 1139 1151 plus test-emit-obj#02, covering cfnptr/cfnsr, casmgnu386, c_uapilay_gcc32, pcdecl and c_obj_data_dup.

So the pass count barely moved and the coverage behind it changed completely. That is the worst available shape for this class: the verdict is not merely unable to report the hole, it reports the same number before and after the hole is closed. A tier diff — the instrument anyone would reach for — cannot see it either. What improved is what the greens mean, and nothing in the run says so.

The same run reported skips 0 and coverage_holes 0. On that day's evidence those two zeros mean "borg happens to have every qemu", not "the tier verified everything" — and they would read identically on a box that had none.

The shape of the fix

A channel from the recipe to the classifier: a recognised skip line a recipe emits to mean "I skipped and it IS a coverage hole", then route the 72 guards through it so skip_holes counts them.

Not taken on a peer's say-so. This edits ~72 Makefile recipes plus a testmgr channel, which is well past what the borg seat was handed; it asked for the owner's word before picking it up, which is correct. Track T owns the tool, so the lane is T's — the question for the owner is only whether it is worth the span now.

Positive control, when it is built: a box with a tool removed must report a nonzero skip_holes for exactly the jobs that guard on it. A run where skip_holes stays 0 after removing a qemu is the instrument failing, not a pass.

2026-09-22 (frankb-8e) — the 72 is 80 now, and BOTH rows stand

grep -c 'NOT verified' Makefile answers 80 at fda77c48b8ee. Same command, same file, against the 72 this ticket measured on 2026-09-12 — so this is growth, not a correction, and I am carrying both rows rather than replacing the number: 72 @ 2026-09-12, 80 @ fda77c48b8ee, 8 sites added in ten days. The direction strengthens the ticket (more structurally uncountable holes, not fewer) and it means any figure derived from 72 — including the skip_holes == 0 argument — should be re-derived rather than scaled.

A SKIP CODE ALREADY EXISTS AND THIS TICKET'S REMEDY SHOULD NOT INVENT A SECOND. tools/gate.sh:104 special-cases exit 77 (autotools SKIP) for tools/selfhost_fixedpoint.sh. testmgr.py does not honour it — no 77 anywhere in that file — and gate.sh maps it to return 0, a pass. So the state this ticket wants is not "skip", which exists, but skip AND count as a coverage hole; and the sibling bug-t-a-probe-that-exits-2-to-say-its-instrument-is-broken-is-published-as-a-compiler-red wants the same new state from the opposite direction (its probe exits 2 rather than 0 precisely to avoid laundering, and is published as a compiler red). Read that ticket before routing the 80 guards through anything: one channel serves both, three spellings would not.