diff --git a/scripts/check-test-source-alias.mjs b/scripts/check-test-source-alias.mjs
index ed760b803f..0503dda71d 100644
--- a/scripts/check-test-source-alias.mjs
+++ b/scripts/check-test-source-alias.mjs
@@ -502,21 +502,55 @@ const KNOWN_UNALIASED_TEST_IMPORTS = {
// ── workspace enumeration ───────────────────────────────────────────────────
-/** Directory globs from pnpm-workspace.yaml, which are all `
/*`. */
-const WORKSPACE_PARENT_DIRS = [
- 'packages',
- 'packages/apps',
- 'packages/adapters',
- 'packages/connectors',
- 'packages/drivers',
- 'packages/plugins',
- 'packages/qa',
- 'packages/services',
- 'packages/triggers',
- 'apps',
- 'examples',
+/**
+ * The workspace globs from pnpm-workspace.yaml, spelled AS GLOBS.
+ *
+ * ## Why the `/*` is written out rather than left to the comment (#9955)
+ *
+ * This array IS this gate's declared population: every package it walks lives
+ * directly under one of these parents. `scripts/pm/dispatch-gates.mjs` derives
+ * the gate list a dispatch brief pastes by scanning each gate's module body for
+ * the path literals it operates on — so this array is the only thing that tells
+ * that tool which cards should be sent here.
+ *
+ * Its covering rule refuses a literal with NO path separator (`packages`,
+ * `apps`, `examples`) as too generic, deliberately and measured: admitting bare
+ * top-level words takes that tool from 19k watch-hint pairs to 158k, because
+ * `packages` is a path COMPONENT in dozens of gates that never read the root.
+ * The sanctioned escape is for a gate to declare its own subtree in a spelling
+ * with a separator in it, which is what these entries now do.
+ *
+ * Written as bare directory names, 8 of the 11 entries carried a separator and
+ * 3 did not, so the derivation's answer for this gate was decided by WHERE a
+ * package happens to sit: measured on this tree, 1832 of the 4844 tracked files
+ * under packages/ derived this gate, and the ones that did not were exactly the
+ * flat `packages/` layouts plus all of apps/ and examples/. A new test in
+ * a nested package named this gate; the identical test in a flat one did not,
+ * and nothing in the output said so. That is worse than an honest blind spot —
+ * it works for a third of the tree, so it reads as working.
+ *
+ * The dropped `/*` is re-derived below, so the walk is unchanged and there is
+ * no second list to keep in sync. Keep the separator in every entry: a tidy-up
+ * back to bare directory names re-opens the blind spot silently, and the
+ * self-test case at the bottom of this file is what makes that loud instead.
+ */
+const WORKSPACE_PARENT_GLOBS = [
+ 'packages/*',
+ 'packages/apps/*',
+ 'packages/adapters/*',
+ 'packages/connectors/*',
+ 'packages/drivers/*',
+ 'packages/plugins/*',
+ 'packages/qa/*',
+ 'packages/services/*',
+ 'packages/triggers/*',
+ 'apps/*',
+ 'examples/*',
];
+/** The parent directories those globs enumerate — each glob minus its leaf. */
+const WORKSPACE_PARENT_DIRS = WORKSPACE_PARENT_GLOBS.map((glob) => glob.replace(/\/\*$/, ''));
+
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.turbo', '.next', '.cache']);
const TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$/;
const SOURCE_FILE = /\.[cm]?[jt]sx?$/;
@@ -2644,6 +2678,26 @@ function selfTest() {
'an empty tree did not trip the published-subpath census guard — a population that silently went to zero',
);
rmSync(empty, { recursive: true, force: true });
+
+ // ── the declared population must stay READABLE by the dispatch deriver ─
+ //
+ // scripts/pm/dispatch-gates.mjs decides which cards are told to run this
+ // gate by scanning this file's module body for the path literals it
+ // operates on, and its covering rule refuses a literal carrying no path
+ // separator (after the leading ./ or ../ an extractor strips) as too
+ // generic. WORKSPACE_PARENT_GLOBS is this gate's WHOLE declared
+ // population, so an entry that loses its separator takes every package
+ // under that parent out of the derived gate list SILENTLY: the gate keeps
+ // working, CI keeps failing on it, and no dispatch brief sends anyone
+ // here. That is what the bare spelling cost, measured in that constant's
+ // docblock (#9955). Asserted here rather than left to review because the
+ // regression is a tidy-up nobody would flag.
+ for (const glob of WORKSPACE_PARENT_GLOBS) {
+ expect(
+ glob.replace(/^(?:\.\.?(?:\/|$))+/, '').includes('/'),
+ `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`,
+ );
+ }
} finally {
rmSync(root, { recursive: true, force: true });
}
diff --git a/scripts/check-type-source-resolution.mjs b/scripts/check-type-source-resolution.mjs
index f0c30bbc49..6b83912690 100644
--- a/scripts/check-type-source-resolution.mjs
+++ b/scripts/check-type-source-resolution.mjs
@@ -305,21 +305,55 @@ const KNOWN_DIST_RESOLVED_TYPE_IMPORTS = {
// ── workspace enumeration ───────────────────────────────────────────────────
-/** Directory globs from pnpm-workspace.yaml, which are all `/*`. */
-const WORKSPACE_PARENT_DIRS = [
- 'packages',
- 'packages/apps',
- 'packages/adapters',
- 'packages/connectors',
- 'packages/drivers',
- 'packages/plugins',
- 'packages/qa',
- 'packages/services',
- 'packages/triggers',
- 'apps',
- 'examples',
+/**
+ * The workspace globs from pnpm-workspace.yaml, spelled AS GLOBS.
+ *
+ * ## Why the `/*` is written out rather than left to the comment (#9955)
+ *
+ * This array IS this gate's declared population: every package it walks lives
+ * directly under one of these parents. `scripts/pm/dispatch-gates.mjs` derives
+ * the gate list a dispatch brief pastes by scanning each gate's module body for
+ * the path literals it operates on — so this array is the only thing that tells
+ * that tool which cards should be sent here.
+ *
+ * Its covering rule refuses a literal with NO path separator (`packages`,
+ * `apps`, `examples`) as too generic, deliberately and measured: admitting bare
+ * top-level words takes that tool from 19k watch-hint pairs to 158k, because
+ * `packages` is a path COMPONENT in dozens of gates that never read the root.
+ * The sanctioned escape is for a gate to declare its own subtree in a spelling
+ * with a separator in it, which is what these entries now do.
+ *
+ * Written as bare directory names, 8 of the 11 entries carried a separator and
+ * 3 did not, so the derivation's answer for this gate was decided by WHERE a
+ * package happens to sit: measured on this tree, 1832 of the 4844 tracked files
+ * under packages/ derived this gate, and the ones that did not were exactly the
+ * flat `packages/` layouts plus all of apps/ and examples/. A new test in
+ * a nested package named this gate; the identical test in a flat one did not,
+ * and nothing in the output said so. That is worse than an honest blind spot —
+ * it works for a third of the tree, so it reads as working.
+ *
+ * The dropped `/*` is re-derived below, so the walk is unchanged and there is
+ * no second list to keep in sync. Keep the separator in every entry: a tidy-up
+ * back to bare directory names re-opens the blind spot silently, and the
+ * self-test case at the bottom of this file is what makes that loud instead.
+ */
+const WORKSPACE_PARENT_GLOBS = [
+ 'packages/*',
+ 'packages/apps/*',
+ 'packages/adapters/*',
+ 'packages/connectors/*',
+ 'packages/drivers/*',
+ 'packages/plugins/*',
+ 'packages/qa/*',
+ 'packages/services/*',
+ 'packages/triggers/*',
+ 'apps/*',
+ 'examples/*',
];
+/** The parent directories those globs enumerate — each glob minus its leaf. */
+const WORKSPACE_PARENT_DIRS = WORKSPACE_PARENT_GLOBS.map((glob) => glob.replace(/\/\*$/, ''));
+
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'coverage', '.turbo', '.next', '.cache']);
const SOURCE_FILE = /\.[cm]?[jt]sx?$/;
@@ -1313,6 +1347,26 @@ function selfTest() {
const emptyResult = check(empty, {});
expect(has(emptyResult.failures, 'the scan is broken'), 'an empty tree did not trip the census guard');
rmSync(empty, { recursive: true, force: true });
+
+ // ── the declared population must stay READABLE by the dispatch deriver ─
+ //
+ // scripts/pm/dispatch-gates.mjs decides which cards are told to run this
+ // gate by scanning this file's module body for the path literals it
+ // operates on, and its covering rule refuses a literal carrying no path
+ // separator (after the leading ./ or ../ an extractor strips) as too
+ // generic. WORKSPACE_PARENT_GLOBS is this gate's WHOLE declared
+ // population, so an entry that loses its separator takes every package
+ // under that parent out of the derived gate list SILENTLY: the gate keeps
+ // working, CI keeps failing on it, and no dispatch brief sends anyone
+ // here. That is what the bare spelling cost, measured in that constant's
+ // docblock (#9955). Asserted here rather than left to review because the
+ // regression is a tidy-up nobody would flag.
+ for (const glob of WORKSPACE_PARENT_GLOBS) {
+ expect(
+ glob.replace(/^(?:\.\.?(?:\/|$))+/, '').includes('/'),
+ `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`,
+ );
+ }
} finally {
rmSync(root, { recursive: true, force: true });
}