#3235 · one dispatch engine for the test suite
A test file's coverage no longer depends on where it sits or who remembered to wire it.
BEFORE — two philosophies
LOCAL npm test → run-all-tests.sh 133 HAND-WIRED run_test lines vs 231 test files on disk wiring itself was a bug class: "bash <path>" / "node <path>" tripped the [[ -f ]] guard into silent SKIP — 3 real incidents (#3231, #3236, #3220 found 17 files never wired at all) CI per-dir glob, everything runs
AFTER — one engine
LOCAL npm test → run-all-tests.sh v4 22 run_dir calls → the SAME scripts/ci/run-tests.sh glob CI uses. A new test file runs everywhere the moment it exists. No wiring step, no interpreter prefixes, no double-runs. CI unchanged (same engine)
Correction shipped inside this PR's framing
The earlier "123 of 133 lines carry || true so the local suite
cannot fail" reading was WRONG: the v3 runner had no set -e, tallied every
failure into TOTAL_FAILED, and exited 1 at the end. The || trues were
vestigial. The real defects were the roster gap (~100 files running only in CI) and the
wiring-to-forget class — which is what this PR removes. v4 also upgrades to full
set -euo pipefail with recorded (never swallowed) exit captures.
Deliberate non-glob residue — every exception is explicit and justified
| exception | reason |
|---|---|
| tests/fixtures never globbed | test-helpers.sh is a sourced library; standalone exit 0 = fake coverage |
| tests/orphans runs advisory (env) | detector-vs-ratchet split: reachability RATCHET (29 baseline) is the gate; the detector fails on all 29 accepted ones |
| scorecard + ci/lint.sh explicit | not test-*.sh, the glob cannot see them; scorecard stays WARN-only |
| vitest local-only block | CI runs it in 4 dedicated shards |
The ratchet keeps its job — smaller and sharper
test-test-discovery.mjs: in-glob files are executed BY CONSTRUCTION, so the orphan ratchet now guards only the residue outside GLOBBED_DIRS. Held at 1/1 baseline after the rewrite.
Riding along in the same train
| item | what |
|---|---|
| warn ratchet | two full-suite runs confirm the real count; baseline tightened from 348 accordingly |
| budget slack | fattest model-facing descriptions trimmed; token caps ratcheted back down per the "never raise to clear a regression" rule |