CORPUS_RE invents a corpus tree that can never exist
- Type: bug (Track T —
tools/testmgr.py) - Found: 2026-07-31, corpus audit while enrolling the xeon watcher box.
The defect
CORPUS_RE = re.compile(r"library_candidates/([^/\s\"']+)")
The character class excludes /, whitespace and quotes — but not ). So it
matches the prose inside a shell SKIP message in Makefile:2426:
else echo "stb_sprintf_probe: SKIP (no library_candidates/stb)"; fi
and extracts the corpus name stb). library_candidates/stb) is never a
directory, so the self-skip check marks the job skip permanently — on every
host, whether or not the corpus is fetched.
Measured on xeon: after install_lib_candidates.sh all fetched all 19 trees,
exactly one job still skipped:
!! CORPUS MISSING — 1 job(s) will SKIP, not run.
!! stb) 1 job(s)
!! Fetch them: tools/install_lib_candidates.sh stb)
The remedy it prints is itself invalid — install_lib_candidates.sh dies with
unknown candidate 'stb)'.
Blast radius — worse than one corpus probe
The affected job is test-core#403, and it bundles two sources:
test-core#403 corpus 5 lines test/cswitch_noncompound_duff_b207.c
test/gamelib/stb_sprintf_probe.c
cswitch_noncompound_duff_b207.c is the non-compound switch body + Duff's
device regression test (bug-c-switch-nonblock-and-duffs-device). It has no
corpus dependency whatsoever, and it has not run on any watcher host since the
stb probe was appended to the same target.
It is invisible in tstate, which is the real problem
tools/twatch.py:518 records a skipped job as "pass":
now = {job_key(j): ("pass" if j["status"] == "skip" else j["status"]) ...}
So borg.json carries
test-core#src:test/cswitch_noncompound_duff_b207.c = "pass" for a job that
never executed. The run-time warning is loud; the published state is
silently green. Any cross-host comparison reads it as covered.
Fix
- Add
)(and;) to the excluded set, or better, only scan recipe paths rather than free text — e.g. require the match be followed by/or a word boundary that is not punctuation:re.compile(r"library_candidates/([A-Za-z0-9_.+-]+)"). Verified against every current reference: the cleaned pattern yields exactly the 16 real trees and no phantoms. - Split the stb probe out of the b207 job, or teach the chunker not to merge a corpus-guarded recipe line with unguarded regression tests — one absent corpus should never take an unrelated test down with it.
- Give
skipits own status in tstate instead of laundering it topass. Green must mean "ran and passed". A separateskipcount per host also makes host-to-host coverage differences visible at cutover time, which is exactly when they matter.
Item 3 changes the tstate schema, so it wants a deliberate migration rather than a drive-by — but items 1 and 2 are self-contained.
Log
- 2026-08-03 — resolved, commit c7400944e. Items 1 and 2 only; item 3 (skip published as pass) is split out as [[bug-t-tstate-launders-skip-into-pass]] — it changes the tstate schema.
REOPENED 2026-09-06 — the same defect, one character narrower
The 2026-07-31 fix tightened the class to [A-Za-z0-9_.+-]+, which removed )
and kept .. A sentence-ending full stop after a corpus path is therefore
still swallowed. test-zlib's recipe carries a shell-comment line
: ' other zlib header still resolves out of $(ZLIB_SRC).'; \
which make -n hands the detector as ... out of library_candidates/zlib..
The capture is zlib., library_candidates/zlib. is not a directory, and the
job self-skips on every host as corpus absent: library_candidates/zlib.
— fetched or not.
It cost seventeen minutes of coverage on the release box, not eight days
Measured from seven's own reports in devdocs/progress/tstate/reports/:
| when | sha | tier | skips / holes | test-zlib |
|---|---|---|---|---|
| 18:02:17Z | c69b52b |
full | 6 / 1 | RAN (present as a red job) |
| 18:20:46Z | 2523453c4 |
— | — | the comment line lands |
| 18:37:24Z | 6d04b14 |
full | 7 / 2 | SKIPPED, "corpus absent" |
2523453c4 is "fix(T): test-zlib's unity runner was invalid C — the compiler
was right" — a commit whose entire purpose was to make this row measurable.
The fix hid the row it fixed, and the very next tier recorded the skip. The
corpus itself had been present on that box since 2026-08-29 and never moved, so
every reading that starts from "the corpus is missing" is chasing a fact that
was never true.
Why the devtest could not catch it — two independent reasons
tools/testmgr_corpus_skip_devtest.py's case_real_makefile_yields_only_real_trees
was written for exactly this and passed throughout.
- Wrong population. It scans the Makefile SOURCE, where the line still
reads
$(ZLIB_SRC).; the stringlibrary_candidates/zlib.exists only inmake -nOUTPUT, which is what the detector actually reads. A control drawn from the wrong population passes and certifies the broken instrument. - Wrong alphabet. Its phantom filter tests for
()[]{};"'`$. A full stop is in none of them — it could not have failed on this shape even with the right input.
The fix
CORPUS_RE may no longer capture a name ENDING in a dot. An interior dot is
left alone: no corpus has one today, and forbidding it would be the opposite
defect the day someone fetches lua5.4. Repaired at the INSTRUMENT rather than
at the comment, because the comment is one writer away from coming back and the
next prose sentence to end in a corpus path lands somewhere with no repro.
Three devtest cases added, labelled by what each can actually observe: the trailing-dot case is the regression control and was verified to FAIL under the pre-fix regex; the interior-dot case passes under the pre-fix regex too and its docstring says so, so nobody counts it as a control it cannot be.
The residual — CORRECTED, and the correction is the more useful half
What I first wrote here was wrong and had exactly the shape I had corrected
somebody else's version of three hours earlier: a true general claim carrying a
false supporting instance. I said the five uncounted gtk jobs on the
18:37:24Z report were recipe self-skips, invisible because _self_skipped
returns a line beginning with the target name. frankB checked and they are not.
Verified here first-hand rather than relayed:
- None of those seven skips is a recipe self-skip. The report's own reason list (lines 25-27) has three groups, all harness-originated.
- The five gtk jobs carry
host dev dependency absent: …/compiler/gtk.h, emitted attestmgr.py:1849, which is in none ofSKIP_HOLE_PREFIXES.testmgr.py:1543has the same problem in the other direction:"host tool absent:".startswith("tool absent:")is False. - So the 2-vs-7 gap is a prefix mismatch between emitter and classifier, both
inside
testmgr.py, and both emitters' own text says verbatim "That is coverage this box is not providing, not a verdict on the tree." The emitter says hole, the classifier says not-a-hole, and the report published both.
frankB holds that fix. SKIP_HOLE_PREFIXES must NOT be loosened — the
comment above it records that folding a recipe's own guard into corpus-absence
is the seven-week (corpus absent) bug the tuple exists to end.
The general statement survives and is now its own ticket with no instance
claimed: a recipe self-skip can never be a hole BY CONSTRUCTION, which is right
for a recipe guarding an optional probe and wrong for test-zlib's
gcc oracle not found. That wants a channel from the recipe, not a looser
match in the harness — bug-t-a-recipe-cannot-declare-its-own-skip-a-coverage-hole.