Skip to content

Commit 24d8bc8

Browse files
claude[bot]claude
andauthored
fix(gates): declare the workspace parents as globs, so the dispatch deriver can read the whole population (#10540)
`scripts/pm/dispatch-gates.mjs` decides which cards are told to run a gate by scanning that gate's module body for the path literals it operates on, and its covering rule refuses a literal with no path separator as too generic (measured and deliberate: admitting bare top-level words takes it from 19k watch-hint pairs to 158k). `check-test-source-alias.mjs` and `check-type-source-resolution.mjs` each declared their whole population as `WORKSPACE_PARENT_DIRS`, of whose 11 entries 8 carried a separator and 3 did not (`packages`, `apps`, `examples`). So the derivation's answer was decided by WHERE a package happens to sit: measured, 1832 of the 4844 tracked files under packages/ derived check:test-source-alias and the rest did not, the misses being exactly the flat `packages/<pkg>` layouts plus all of apps/ and examples/. Same test file, two layouts, two different answers, with nothing in the output saying so. Spelling the array as the pnpm-workspace globs it already documents itself to be (`packages/*`, `apps/*`, …) and re-deriving the directory names from it puts a separator in every entry. The walk is unchanged — both gates still report the same package census — and there is no second list to keep in sync. Measured on this tree, whole-corpus, before -> after: matched (family, tracked-file) pairs 37903 -> 43356 (+5453) check:test-source-alias 1833 -> 5116 (+3283) check:type-source-resolution 2946 -> 5116 (+2170) The two deltas sum to the total, so no other family moved; exactly two families resolve to the two changed scripts, measured in both trees. For scale, the alternative #9626 measured and refused (admitting bare top-level literals globally) was +139084, and took `packages/spec/src/index.ts` from 7 matched families to 34. This takes it from 13 to 14. Each gate's self-test now pins the readability property, because the regression is a tidy-up nobody would flag: the live gate stays green when an entry loses its separator, so only an assertion can be loud about it. Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt Co-authored-by: Claude <noreply@anthropic.com>
1 parent 13fa51e commit 24d8bc8

2 files changed

Lines changed: 134 additions & 26 deletions

File tree

scripts/check-test-source-alias.mjs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -502,21 +502,55 @@ const KNOWN_UNALIASED_TEST_IMPORTS = {
502502

503503
// ── workspace enumeration ───────────────────────────────────────────────────
504504

505-
/** Directory globs from pnpm-workspace.yaml, which are all `<dir>/*`. */
506-
const WORKSPACE_PARENT_DIRS = [
507-
'packages',
508-
'packages/apps',
509-
'packages/adapters',
510-
'packages/connectors',
511-
'packages/drivers',
512-
'packages/plugins',
513-
'packages/qa',
514-
'packages/services',
515-
'packages/triggers',
516-
'apps',
517-
'examples',
505+
/**
506+
* The workspace globs from pnpm-workspace.yaml, spelled AS GLOBS.
507+
*
508+
* ## Why the `/*` is written out rather than left to the comment (#9955)
509+
*
510+
* This array IS this gate's declared population: every package it walks lives
511+
* directly under one of these parents. `scripts/pm/dispatch-gates.mjs` derives
512+
* the gate list a dispatch brief pastes by scanning each gate's module body for
513+
* the path literals it operates on — so this array is the only thing that tells
514+
* that tool which cards should be sent here.
515+
*
516+
* Its covering rule refuses a literal with NO path separator (`packages`,
517+
* `apps`, `examples`) as too generic, deliberately and measured: admitting bare
518+
* top-level words takes that tool from 19k watch-hint pairs to 158k, because
519+
* `packages` is a path COMPONENT in dozens of gates that never read the root.
520+
* The sanctioned escape is for a gate to declare its own subtree in a spelling
521+
* with a separator in it, which is what these entries now do.
522+
*
523+
* Written as bare directory names, 8 of the 11 entries carried a separator and
524+
* 3 did not, so the derivation's answer for this gate was decided by WHERE a
525+
* package happens to sit: measured on this tree, 1832 of the 4844 tracked files
526+
* under packages/ derived this gate, and the ones that did not were exactly the
527+
* flat `packages/<pkg>` layouts plus all of apps/ and examples/. A new test in
528+
* a nested package named this gate; the identical test in a flat one did not,
529+
* and nothing in the output said so. That is worse than an honest blind spot —
530+
* it works for a third of the tree, so it reads as working.
531+
*
532+
* The dropped `/*` is re-derived below, so the walk is unchanged and there is
533+
* no second list to keep in sync. Keep the separator in every entry: a tidy-up
534+
* back to bare directory names re-opens the blind spot silently, and the
535+
* self-test case at the bottom of this file is what makes that loud instead.
536+
*/
537+
const WORKSPACE_PARENT_GLOBS = [
538+
'packages/*',
539+
'packages/apps/*',
540+
'packages/adapters/*',
541+
'packages/connectors/*',
542+
'packages/drivers/*',
543+
'packages/plugins/*',
544+
'packages/qa/*',
545+
'packages/services/*',
546+
'packages/triggers/*',
547+
'apps/*',
548+
'examples/*',
518549
];
519550

551+
/** The parent directories those globs enumerate — each glob minus its leaf. */
552+
const WORKSPACE_PARENT_DIRS = WORKSPACE_PARENT_GLOBS.map((glob) => glob.replace(/\/\*$/, ''));
553+
520554
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.turbo', '.next', '.cache']);
521555
const TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$/;
522556
const SOURCE_FILE = /\.[cm]?[jt]sx?$/;
@@ -2644,6 +2678,26 @@ function selfTest() {
26442678
'an empty tree did not trip the published-subpath census guard — a population that silently went to zero',
26452679
);
26462680
rmSync(empty, { recursive: true, force: true });
2681+
2682+
// ── the declared population must stay READABLE by the dispatch deriver ─
2683+
//
2684+
// scripts/pm/dispatch-gates.mjs decides which cards are told to run this
2685+
// gate by scanning this file's module body for the path literals it
2686+
// operates on, and its covering rule refuses a literal carrying no path
2687+
// separator (after the leading ./ or ../ an extractor strips) as too
2688+
// generic. WORKSPACE_PARENT_GLOBS is this gate's WHOLE declared
2689+
// population, so an entry that loses its separator takes every package
2690+
// under that parent out of the derived gate list SILENTLY: the gate keeps
2691+
// working, CI keeps failing on it, and no dispatch brief sends anyone
2692+
// here. That is what the bare spelling cost, measured in that constant's
2693+
// docblock (#9955). Asserted here rather than left to review because the
2694+
// regression is a tidy-up nobody would flag.
2695+
for (const glob of WORKSPACE_PARENT_GLOBS) {
2696+
expect(
2697+
glob.replace(/^(?:\.\.?(?:\/|$))+/, '').includes('/'),
2698+
`workspace parent ${glob} carries no path separator, so scripts/pm/dispatch-gates.mjs refuses it as too generic and every package under it drops out of the derived gate list`,
2699+
);
2700+
}
26472701
} finally {
26482702
rmSync(root, { recursive: true, force: true });
26492703
}

scripts/check-type-source-resolution.mjs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -305,21 +305,55 @@ const KNOWN_DIST_RESOLVED_TYPE_IMPORTS = {
305305

306306
// ── workspace enumeration ───────────────────────────────────────────────────
307307

308-
/** Directory globs from pnpm-workspace.yaml, which are all `<dir>/*`. */
309-
const WORKSPACE_PARENT_DIRS = [
310-
'packages',
311-
'packages/apps',
312-
'packages/adapters',
313-
'packages/connectors',
314-
'packages/drivers',
315-
'packages/plugins',
316-
'packages/qa',
317-
'packages/services',
318-
'packages/triggers',
319-
'apps',
320-
'examples',
308+
/**
309+
* The workspace globs from pnpm-workspace.yaml, spelled AS GLOBS.
310+
*
311+
* ## Why the `/*` is written out rather than left to the comment (#9955)
312+
*
313+
* This array IS this gate's declared population: every package it walks lives
314+
* directly under one of these parents. `scripts/pm/dispatch-gates.mjs` derives
315+
* the gate list a dispatch brief pastes by scanning each gate's module body for
316+
* the path literals it operates on — so this array is the only thing that tells
317+
* that tool which cards should be sent here.
318+
*
319+
* Its covering rule refuses a literal with NO path separator (`packages`,
320+
* `apps`, `examples`) as too generic, deliberately and measured: admitting bare
321+
* top-level words takes that tool from 19k watch-hint pairs to 158k, because
322+
* `packages` is a path COMPONENT in dozens of gates that never read the root.
323+
* The sanctioned escape is for a gate to declare its own subtree in a spelling
324+
* with a separator in it, which is what these entries now do.
325+
*
326+
* Written as bare directory names, 8 of the 11 entries carried a separator and
327+
* 3 did not, so the derivation's answer for this gate was decided by WHERE a
328+
* package happens to sit: measured on this tree, 1832 of the 4844 tracked files
329+
* under packages/ derived this gate, and the ones that did not were exactly the
330+
* flat `packages/<pkg>` layouts plus all of apps/ and examples/. A new test in
331+
* a nested package named this gate; the identical test in a flat one did not,
332+
* and nothing in the output said so. That is worse than an honest blind spot —
333+
* it works for a third of the tree, so it reads as working.
334+
*
335+
* The dropped `/*` is re-derived below, so the walk is unchanged and there is
336+
* no second list to keep in sync. Keep the separator in every entry: a tidy-up
337+
* back to bare directory names re-opens the blind spot silently, and the
338+
* self-test case at the bottom of this file is what makes that loud instead.
339+
*/
340+
const WORKSPACE_PARENT_GLOBS = [
341+
'packages/*',
342+
'packages/apps/*',
343+
'packages/adapters/*',
344+
'packages/connectors/*',
345+
'packages/drivers/*',
346+
'packages/plugins/*',
347+
'packages/qa/*',
348+
'packages/services/*',
349+
'packages/triggers/*',
350+
'apps/*',
351+
'examples/*',
321352
];
322353

354+
/** The parent directories those globs enumerate — each glob minus its leaf. */
355+
const WORKSPACE_PARENT_DIRS = WORKSPACE_PARENT_GLOBS.map((glob) => glob.replace(/\/\*$/, ''));
356+
323357
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.turbo', '.next', '.cache']);
324358
const SOURCE_FILE = /\.[cm]?[jt]sx?$/;
325359

@@ -1313,6 +1347,26 @@ function selfTest() {
13131347
const emptyResult = check(empty, {});
13141348
expect(has(emptyResult.failures, 'the scan is broken'), 'an empty tree did not trip the census guard');
13151349
rmSync(empty, { recursive: true, force: true });
1350+
1351+
// ── the declared population must stay READABLE by the dispatch deriver ─
1352+
//
1353+
// scripts/pm/dispatch-gates.mjs decides which cards are told to run this
1354+
// gate by scanning this file's module body for the path literals it
1355+
// operates on, and its covering rule refuses a literal carrying no path
1356+
// separator (after the leading ./ or ../ an extractor strips) as too
1357+
// generic. WORKSPACE_PARENT_GLOBS is this gate's WHOLE declared
1358+
// population, so an entry that loses its separator takes every package
1359+
// under that parent out of the derived gate list SILENTLY: the gate keeps
1360+
// working, CI keeps failing on it, and no dispatch brief sends anyone
1361+
// here. That is what the bare spelling cost, measured in that constant's
1362+
// docblock (#9955). Asserted here rather than left to review because the
1363+
// regression is a tidy-up nobody would flag.
1364+
for (const glob of WORKSPACE_PARENT_GLOBS) {
1365+
expect(
1366+
glob.replace(/^(?:\.\.?(?:\/|$))+/, '').includes('/'),
1367+
`workspace parent ${glob} carries no path separator, so scripts/pm/dispatch-gates.mjs refuses it as too generic and every package under it drops out of the derived gate list`,
1368+
);
1369+
}
13161370
} finally {
13171371
rmSync(root, { recursive: true, force: true });
13181372
}

0 commit comments

Comments
 (0)