Four shapes let a heading name something that never runs: a test inside a raw string literal, one behind an unenabled cfg feature, one behind cfg(target_arch = "wasm32"), and one behind #[ignore]. All four cleared the scrape; injecting them together made the previous checker print "543 entries, all name a test that exists", exit 0. --check-evidence now takes the Rust names from `cargo test --workspace -- --list` minus its `--ignored` twin. All 539 live headings resolve against a real listing, so the stricter source costs nothing today. rust_tests is rust_citations: the scrape survives only for the reverse direction, where a false positive is loud. Prose headings become a third kind, counted and printed, so retiring a claim by rewriting it as prose moves a number instead of passing in silence (improvise-9za9). No gate can read prose for truth; the reviewer is the mechanism, and the diff is what to read. context/code/tooling.md is new and is the file four docstrings in check-comment-runs.py already pointed at. design-principles §7 stopped saying the check runs in one direction, which improvise-lyyc had already made false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
6.1 KiB
The repository's own checkers
scripts/ holds the gates this repo runs on itself. They are code, and they are
held to the rule they exist to enforce: a check nobody has seen fail is
indistinguishable from one that always passes, so each of them carries a
self-test whose cases are injected faults
(doctrine(check_the_checker)).
Four docstrings in scripts/check-comment-runs.py point here. This is the file
they mean.
1. The comment cap
check-comment-runs.py fails a .rs file carrying more than two consecutive
comment lines. The rule is context/design-principles.md §6: reasoning lives in
context/ prose, where it can be read, corrected and audited, not in a comment
that nothing checks against the code.
The whole difficulty is deciding what is a comment line. Ten silent successes have been found in this one function, every one of them failing in the direction that looks like passing — a file that should have been flagged and was not:
"test" in linematched#[cfg(test)], so every cfg-gated fixture cleared a gate written to reject exactly those. Anchor the match; an unanchored substring is a filter that over-matches, and a filter that over-matches fails in the direction that looks like success. (The same class as the bd ID audit's exclusion list, and as_JS_TEST's exclusion of.skip/.only.)- A
proptest!flag was set and never cleared, so the firstproptest!in a file made every laterfn— helpers included — count as a test. - An attribute holding a bracket inside a string (
#[cfg(feature = "[")]) left the bracket depth unbalanced and exempted the rest of the file. Every question is therefore asked of the scanner, never of the raw line: the scanner knows Rust strings, raw strings, char literals and nested block comments, and the attribute depth is counted over code text only. - A fence that opens and never closes exempted everything after it.
Two rules that are decisions rather than accidents. A fence **suspends** the run rather than ending it, so two lines above a fence and two below is a run of four — a reader meets it as one uninterrupted block. And only a *runnable* fence earns the exemption: rustdoc executes a bare or rust and does not execute text, ignore or no_run, so the rest is prose wearing a fence.
2. The evidence check
--check-evidence holds context/test-evidence/ and the tests it describes to
each other, in two directions:
- Orphans. Every heading shaped like a test name must name a test that runs.
- Unstated. Every test whose doc comment cites
context/test-evidence/must have an entry there.
Neither direction is "every test has evidence". Most tests have none and should not be forced to have one; what is checkable is a claim, and both directions are claims. The failure they close is the same one a dead ticket ID is: a pointer that reads as a promise the thing exists.
The names come from cargo, not from the source
A heading is diffed against cargo test --workspace -- --list, minus the names
that --list --ignored reports. It used to be scraped out of .rs text, and
four shapes defeated that, all of them "evidence for a check that never runs"
(improvise-rm4s):
| shape | why the scrape accepted it |
|---|---|
#[test] fn x inside a raw string literal |
the scanner reads inside strings |
#[cfg(feature = "…")] #[test] |
the feature is never enabled |
#[cfg(target_arch = "wasm32")] #[test] |
cargo test --workspace is native |
#[test] #[ignore] |
it compiles, and never runs |
The third is the realistic one here: this repo has real wasm32-only code and a
wasm CI job that only runs cargo check. Adopting the stricter source cost
nothing — all 539 headings resolved against a real listing on the commit that
made the change — which is the moment to tighten a gate rather than the moment
to argue about it.
Demonstrated before the fix, by injecting all four shapes into src/draw.rs and
context/test-evidence/tui.md at once: the previous checker printed 543
entries, all name a test that exists, exit 0. The current one names all four.
What this costs: --check-evidence now needs a toolchain and a compiled
workspace, about three seconds warm. --rust-tests FILE takes a saved listing
for a caller that has one. The comment-cap check is unaffected and needs
neither.
What it does not close: citing is still scraped, so a citation inside a
string literal would be reported as a test owing an entry. That is a false
positive in the safe direction — loud, not silent — and it has no instance.
A demotion is a reviewer's job, not a gate's
A heading is classified by shape: a bare identifier is a Rust test, a quoted
string a JS test, anything else is prose about the section (Helper — x,
Note — y, a file's own scope). So any orphaned heading can be retired by
rewriting it with a space and a dash, and nothing can check whether the prose is
honest — no mechanism could, because the question is whether a sentence is true
(improvise-9za9).
Two things make that tolerable. The unsafe direction is the one that fails
loudly: prose that looks like an identifier is classified as a Rust claim and
reported as an orphan, so the error you can make by accident is the noisy one.
And prose headings are counted and printed beside the claims, so a
retirement moves a number in the gate's own output instead of passing in
silence. The live instance — PR #69 demoting a heading while PR #72 restored its
coverage under a different name — is what says the reviewer is the mechanism:
git diff context/test-evidence/ shows a ### identifier line becoming prose,
and that diff is the thing to read.
3. The others
scripts/kb is the only writer of prolog-sessions/kb.pl; its own reasoning is
in context/code/kb-tool.md. scripts/node-test.sh, scripts/kb-test.sh,
scripts/pr-watch-test.sh and scripts/assemble-stamp-test.sh are the same
shape as this file's subject: a guard, plus a suite that makes the guard refuse
what it claims to refuse. Each is wired into .github/workflows/ci.yml in the
same job as the thing it guards, because a check that only runs locally is a
convention.