(motir-core) `coverage-gate-globs` is a whole-tree scan MOTIR-3144's lane left behind — it globs 483 files in 0.8 s and times out at 15 s on a loaded coverage shard, twice in three days
Repo · motir-core (tests/coverage-gate-globs.test.ts, tests/helpers/structuralGuardLane.ts, tests/ci-structural-guards-lane.test.ts).
MOTIR-3144 diagnosed the class — whole-tree structural guards are lint, not tests; they flake because they run inside the sharded database suite and inherit its contention — and shipped the Structural guards lane that removed it for ten guards. tests/coverage-gate-globs.test.ts is the same class and is not in the lane. It has flaked twice in three days since.
The two sightings
| first | second | |
|---|---|---|
| date | 2026-08-23 | 2026-08-25 |
| pull request | motir-core#2259 | motir-core#2282 |
| run · job | 32628202745 · 97166825214 | 32906725560 · 97992458441 |
| shard | Vitest (1/3) | Vitest (1/3) |
| error | Test timed out in 15000ms | Test timed out in 15000ms |
| the rest of the shard | 386 passed, 5635 tests passed | 395 passed, 5742 tests passed |
| the diff | docs/**, scripts/**, code comments | design assets, one CLAUDE.md paragraph, one edited test file |
Same test, same case, same shard, zero assertion failures either time.
The measurement that names it environmental — and it is one command
The whole file pays ONE memoised glob (reportableFiles() in tests/helpers/coverageGate.ts), and the first it is what pays it:
npx tsx -e "const {reportableFiles}=await import(process.cwd()+'/tests/helpers/coverageGate.ts');
const t=Date.now();const f=await reportableFiles();console.log(f.length,Date.now()-t)"
483 files in 785 ms on the branch; 483 files in 986 ms on origin/main. Same input, same cost — so the second sighting's diff did not touch the scan, and neither did the first: coverage.include reaches no path either diff edited. A 0.8 s scan blowing a 15 s budget is a contention multiplier over 19x, which is inside the range MOTIR-3144's own fourth instance measured.
Two variables the second sighting rules out that the first could not: no test file was added or removed, so shard membership was identical to main's; and every other check on that commit was green, E2E shards included.
Why the lane's own guard does not catch it
tests/ci-structural-guards-lane.test.ts fails when a guard imports a scanner helper and is in neither the lane nor DATABASE_BOUND_GUARDS. coverage-gate-globs imports tests/helpers/coverageGate.ts, which is not on that list — it globs with tinyglobby rather than parsing with the TypeScript compiler API. So the membership test keys on the IMPLEMENTATION a guard happens to use, not on the property that makes it flake: doing whole-tree filesystem work under a 15 s budget on a coverage-instrumented shard. That is the same shape as the class itself — a check that enumerates the instances it knows about.
What to build
- Move
tests/coverage-gate-globs.test.tsinto theStructural guardslane (vitest.guards.config.tsviatests/helpers/structuralGuardLane.ts), and exclude it from the root config exactly as the ten already there are. It needs no database and no--coverage; it readsvitest.config.tsand the filesystem. - Widen
ci-structural-guards-lane's membership test from the IMPORT to the PROPERTY. A guard that reaches the filesystem for a whole-tree answer belongs in the lane whichever helper it uses —tinyglobby,fast-glob,readdirSync, or the compiler API. Enumerate the entry points rather than the one scanner family, and state in the test why each is a carrier. - SWEEP for the others while you are in there and report the list, whether or not any move: the two sightings are the ones that happened to fail, not the population.
⚠️ Do NOT raise the budget. MOTIR-3144 settled that: a budget derived from a quiet-box measurement cannot cover a multiplier that has been observed above 14x, and raising it lowers the probability while leaving the class.
Acceptance criteria
tests/coverage-gate-globs.test.tsruns in theStructural guardsjob and not in the sharded Vitest run — asserted by the lane's own membership test, not by inspection.pnpm test:guardsis green and its wall-clock is reported in the PR body, before and after.ci-structural-guards-lane's membership test fails for a guard that does whole-tree filesystem work through any entry point, demonstrated with a synthetic case rather than asserted — the same demonstrated, not assumed bar the loading-boundary guard meets.- The sweep of step 3 is in the PR body: every
tests/**file that reaches the filesystem for a whole-tree answer, with its lane and a verdict per file. A file left where it is carries a reason. - No assertion changes. This card moves guards and widens a membership check; it does not weaken what anything checks.
pnpm test, the coverage lane and the guards job are all green — all three, because this class has twice hidden in exactly the lane nobody re-read.
Context refs
tests/coverage-gate-globs.test.ts·tests/helpers/coverageGate.ts— the guard and its memoised glob.tests/helpers/structuralGuardLane.ts·vitest.guards.config.ts·tests/ci-structural-guards-lane.test.ts— the lane, its config, and the membership test to widen.vitest.config.ts— the 15 stestTimeoutthe budget is measured against, and thecoverage.includeset the guard reads.- MOTIR-3144 — the class, the lane, and the three instances that produced it. MOTIR-2815 (coverage instrumentation) · MOTIR-3067 (contention) · MOTIR-3167 (a timer bomb on the same global timeout).
motir-metamemoriesast-guard-times-outandmotir-core-ci-e2e-flake-log— both sightings, with the numbers above.