Skip to content

Commit afef7d8

Browse files
os-elonclaude
andauthored
fix(tooling): declare check:where-matcher's test-file population where dispatch-gates can read it (#13231)
`scripts/pm/dispatch-gates.mjs` derives a card's gate list from the path literals in each gate's own source. `check-where-matcher-conformance.mjs` declared its population twice and the derivation could read neither: once in prose (the extractor masks comment ranges by construction) and once as `const SCAN_ROOT = 'packages'`, a single-segment literal `looksPathy` refuses as no hint at all. Measured before this change, `extractWatchHints` over the gate returned exactly one hint — `scripts/where-matcher-conformance.baseline.json`, the gate's own ledger. So the derivation could name the gate only for a change set that edits the set of files ALREADY KNOWN to be wrong, and never for a NEW silently-wrong matcher anywhere under the root it scans: the exact inverse of what it guards. Declares `packages/**/*.test.ts` beside `SCAN_ROOT` under the `ROOT_DIR_WATCH_HINTS` idiom, matching the identically-populated sibling `check-objectql-double-limit.mjs` (byte-identical `testFilesUnder`, identical `SCAN_ROOT`, identical call site). Measured at 2889 of the 2889 files the gate's own walk admits — set-equal in both directions — against 5509 tracked files under the bare root. Pinned in the gate's own `--self-test`, in the four directions the sibling pins. The PM's bare-root worklist row is re-decided from REFUSE-UNSPELLABLE to DECLARED-NARROWER with the same `package test files` spelling, re-measured on this tree rather than carried from the sibling. Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw Co-authored-by: Claude <noreply@anthropic.com>
1 parent 91ec33d commit afef7d8

2 files changed

Lines changed: 135 additions & 4 deletions

File tree

scripts/check-where-matcher-conformance.mjs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,55 @@ const repoRoot = resolve(__dirname, '..');
232232
const BASELINE_PATH = 'scripts/where-matcher-conformance.baseline.json';
233233
const SCAN_ROOT = 'packages';
234234

235+
/**
236+
* ## The dispatch-gates declaration -- the `ROOT_DIR_WATCH_HINTS` idiom (#13163)
237+
*
238+
* `scripts/pm/dispatch-gates.mjs` derives WHICH gates a card must run by matching the
239+
* path literals in each gate's source against the card's changed files. This gate
240+
* declared its population TWICE and the derivation could read NEITHER of them:
241+
*
242+
* 1. IN PROSE, in the discovery section above -- "a function in `packages/**\/*.test.ts`
243+
* whose body...". `extractWatchHints` masks comment ranges by construction, so a
244+
* population documented in a comment is invisible to it BY DESIGN. ⛔ Rewording that
245+
* comment fixes nothing. (The separator is escaped only so the glob cannot close
246+
* this block comment -- the same spelling rule the PM's bare-root worklist follows.)
247+
* 2. IN CODE, as `SCAN_ROOT` above -- a bare single-segment word, which `looksPathy`
248+
* refuses as no hint at all (a measured refusal: admitting the bare-top-level-word
249+
* class costs +139084 fabricated pairs, and it stays). The one narrow re-admission,
250+
* `moduleRelativeDirectoryHint`, resolves a single-segment literal against the
251+
* SCRIPT'S OWN directory, so `'packages'` resolves under `scripts/`, which is not
252+
* tracked ⇒ null.
253+
*
254+
* Measured before this declaration, `extractWatchHints` over this file returned exactly
255+
* ONE hint: `scripts/where-matcher-conformance.baseline.json`, this gate's own ledger.
256+
* ⇒ the derivation could name this gate only for a change set that edits the set of
257+
* files ALREADY KNOWN to be wrong, and never for a NEW silently-wrong matcher anywhere
258+
* under the root it scans -- the exact inverse of what it guards. The live specimen is a
259+
* PR that derived 30 of 30 green gates locally, whole-repo lint included, and still went
260+
* RED in CI on a test file it ADDS.
261+
*
262+
* The spelling below is measured on this tree at 2889 of the 2889 files this gate's own
263+
* `testFilesUnder()` walk admits -- set-equal in BOTH directions against `hintCovers`,
264+
* nothing walked left uncovered and nothing covered left unwalked, so 100% precise and
265+
* complete -- against 5509 tracked files under the bare root. Its liveness and precision
266+
* carry a second, independent pin in the PM's bare-root worklist, whose recorded verdict
267+
* for this row moves to DECLARED-NARROWER with this change.
268+
*
269+
* ⛔ It must be spelled as a LITERAL, not built from `SCAN_ROOT` -- the hint extractor
270+
* reads source text, so a computed template of the root would produce no hint and leave
271+
* the gate exactly as invisible. Both directions are pinned in `--self-test` below. A
272+
* declaration that can drift from the scan is worse than none -- it replaces a silent
273+
* gate with a lying one.
274+
*
275+
* ⚠️ This does NOT retire the convention-KIND entry that names this gate for "adds or
276+
* edits a test file" in the dispatch tool: that entry is a different authority, pinned by
277+
* that tool's own self-test, and it reaches a card dispatched BEFORE its code exists,
278+
* which no path derivation can. What changes is that the high-signal MATCHED column --
279+
* the one a dev pastes and runs -- now names this gate for the test files it really
280+
* walks, instead of only for edits to its own baseline.
281+
*/
282+
const ROOT_DIR_WATCH_HINTS = ['packages/**/*.test.ts'];
283+
235284
// ---------------------------------------------------------------------------
236285
// The probe vocabulary. Field names are deliberately synthetic so no matcher
237286
// can special-case them (several doubles branch on `organization_id`, `id`,
@@ -872,6 +921,58 @@ function selfTest() {
872921
expect('a fallen count is an error (ratchet down)', reconcile(fakeMeasured, { 'a.test.ts': { silent: 2 } }).length === 1);
873922
expect('a stale entry is an error', reconcile(new Map(), { 'gone.test.ts': { silent: 1 } }).length === 1);
874923

924+
// -- the dispatch-gates declaration (#13163's landing obligation) ---------
925+
//
926+
// Enforcement cannot hold either half here: the declaration is read by ANOTHER TOOL
927+
// (the PM's dispatch derivation), so a wrong or stale one runs green in this file
928+
// forever and pays itself out as a dev dispatched on a test-file card with this gate
929+
// missing from the brief -- the CI round trip #13163 was filed for. A missing
930+
// declaration is a silent gate; a surplus one is a LYING gate, and the price the
931+
// derivation records for a fabricated lead is higher than for a missing one. Driven
932+
// through this gate's OWN corpus walk, never a copy of its regex.
933+
const DECLARED_TAIL = '.test.ts';
934+
const walked = testFilesUnder(join(repoRoot, SCAN_ROOT))
935+
.map((abs) => relative(repoRoot, abs).replace(/\\/g, '/'));
936+
// The needle is ASSEMBLED, never spelled. Written as a literal here it would appear in
937+
// this assertion's own source text, so `includes` would find it in the CHECK rather
938+
// than in the declaration and stay green with the declaration deleted -- a phantom pin.
939+
const declNeedle = `'${SCAN_ROOT}/` + '*'.repeat(2) + `/*${DECLARED_TAIL}'`;
940+
const ownSource = readFileSync(fileURLToPath(import.meta.url), 'utf8');
941+
expect(
942+
'the declaration is spelled as a LITERAL in this source, not computed -- the hint '
943+
+ 'extractor reads source text, so a computed root would build no hint at all',
944+
ownSource.includes(declNeedle),
945+
);
946+
expect(
947+
'the declared hint is rooted at the population constant this gate actually walks',
948+
ROOT_DIR_WATCH_HINTS.length === 1 && ROOT_DIR_WATCH_HINTS[0].startsWith(`${SCAN_ROOT}/`),
949+
);
950+
expect(
951+
'the corpus walk is non-empty, so the two directions below judge something',
952+
walked.length > 0,
953+
);
954+
expect(
955+
'nothing is declared that this gate does not walk -- every file testFilesUnder admits '
956+
+ 'lies under the declared root and ends in the declared extension',
957+
ROOT_DIR_WATCH_HINTS.every((h) => h.endsWith(DECLARED_TAIL))
958+
&& walked.every((f) => f.startsWith(`${SCAN_ROOT}/`) && f.endsWith(DECLARED_TAIL)),
959+
);
960+
// ...and the declared filter is a NARROWING rather than the bare root wearing a glob:
961+
// a non-test sibling sitting in the same directory as an admitted file is NOT admitted.
962+
const admitted = new Set(walked);
963+
const siblingDir = walked.length ? dirname(join(repoRoot, walked[0])) : null;
964+
const nonTestSibling = siblingDir
965+
? readdirSync(siblingDir)
966+
.map((e) => relative(repoRoot, join(siblingDir, e)).replace(/\\/g, '/'))
967+
.find((f) => !f.endsWith(DECLARED_TAIL) && statSync(join(repoRoot, f)).isFile())
968+
: null;
969+
expect('a non-test sibling exists to prove the filter discriminates', Boolean(nonTestSibling));
970+
expect(
971+
'and the declaration is a NARROWING, not the bare root wearing a glob -- that sibling '
972+
+ 'is under the same root and is NOT in the walk',
973+
Boolean(nonTestSibling) && !admitted.has(nonTestSibling),
974+
);
975+
875976
if (failures.length > 0) {
876977
console.error(`✗ check-where-matcher-conformance --self-test (${failures.length} failure(s)):\n`);
877978
for (const f of failures) console.error(` • ${f}`);

scripts/pm/bare-root-worklist.mjs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,19 @@ const POPULATION_CONSTANT = /^(?:[A-Z0-9_]*_ROOTS?|[A-Z0-9_]*_DIRS?|POPULATION|[
189189
* covered. A withdrawn verdict that deleted the row would land it back as an
190190
* untriaged FRESH row on the next run, which is the assertion below saying so.
191191
*
192+
* ⭐ An EIGHTEENTH row was re-decided on 2026-08-29 under that same authorisation
193+
* sentence and no wider one: `check:where-matcher SCAN_ROOT packages`, whose refusal
194+
* rested on the identical retired collapse and whose population is the identical
195+
* `.test.ts` corpus as the `check:objectql-double-limit` row. It was left standing on
196+
* 2026-08-26 because it had no recorded consumer, which is the criterion that split that
197+
* class in two; #13163 is that consumer, and it is MEASURED rather than argued — the
198+
* derivation reached this gate for 0 of the test corpus, so a dev adding a
199+
* silently-wrong matcher ran a 30-of-30 green local union and lost a CI round anyway. So
200+
* this row takes the DECLARED-NARROWER half of the split rather than
201+
* SPELLABLE-UNDECLARED, and its gate now carries the declaration. ⛔ Its numbers are
202+
* re-measured on the 2026-08-29 tree and NOT carried from its sibling, which this
203+
* docblock forbids by name.
204+
*
192205
* ⚠️ One row of that seventeen was re-measured into a DIFFERENT population, not
193206
* merely fresher digits: #12392 (PR #12423, `69d0e18`) made
194207
* `check-skills-token-ratchet`'s walk RECURSIVE over whole skill directories, so
@@ -325,6 +338,27 @@ const TRIAGE = new Map([
325338
+ 'bare root is still not covered — the spelling reaches no arbitrary file at the top of '
326339
+ 'the root — which is what this verdict says and is correct, not outstanding debt',
327340
}],
341+
['check:where-matcher SCAN_ROOT packages', {
342+
verdict: 'DECLARED-NARROWER',
343+
spelling: 'package test files',
344+
why: 'REFUSED as unspellable on the reading that every glob form of this population '
345+
+ 'collapses to a malformed double-separator prefix reaching nothing. #12300 retired that '
346+
+ 'collapse — a glob in a non-final segment is MATCHED now — so the refusal was FALSE of '
347+
+ 'this tree, in the same way and for the same reason as its identically-populated sibling '
348+
+ 'check:objectql-double-limit above. Re-measured HERE rather than inherited from that row: '
349+
+ 'the recorded spelling reaches 2889 of the 2889 files this gate own testFilesUnder() walk '
350+
+ 'admits, SET-EQUAL in both directions — nothing walked left uncovered, nothing covered '
351+
+ 'left unwalked — so 100% precise and complete, against 5509 tracked files under the bare '
352+
+ 'root. Declared beside SCAN_ROOT under the ROOT_DIR_WATCH_HINTS idiom for consumer '
353+
+ '#13163, the measured downstream pull this row lacked when the seventeen were '
354+
+ 're-adjudicated: before it, extractWatchHints over the gate returned ONE hint, the gate '
355+
+ 'own baseline JSON, so the derivation could name this gate only for a change set editing '
356+
+ 'the files ALREADY KNOWN to be wrong and never for a NEW silently-wrong matcher — the '
357+
+ 'inverse of what it guards, paid as a CI round trip by a PR that derived 30 of 30 green '
358+
+ 'gates locally. The row STAYS in the sweep because the bare root is still not covered — '
359+
+ 'the spelling reaches no arbitrary file at the top of the root — which is what this '
360+
+ 'verdict says and is correct, not outstanding debt',
361+
}],
328362
['check:skill-refs SKILLS_DIR skills', {
329363
verdict: 'DECLARED-NARROWER',
330364
spelling: 'skill reference folders',
@@ -451,10 +485,6 @@ const TRIAGE = new Map([
451485
why: 'test files only, 2510 of 4903 (51%) — and already refused in that gate own docblock, '
452486
+ 'measured there at 76 real couplings out of 4861 (1.6%)',
453487
}],
454-
['check:where-matcher SCAN_ROOT packages', {
455-
verdict: 'REFUSE-UNSPELLABLE',
456-
why: 'test files only, 2510 of 4903 (51%)',
457-
}],
458488
['check:runner-env-posture SCANNED_ROOTS packages', {
459489
verdict: 'REFUSE-UNSPELLABLE',
460490
why: 'non-test source beneath a `src` SEGMENT — 1812 of 5241 (35%), re-derived from the gate '

0 commit comments

Comments
 (0)