Skip to content

Commit a422f04

Browse files
fix(scripts): dispatch-gates derives the name-fold loader gate for every file under the roots it walks (#24263)
Fixes #24037 Clause-②: no The dispatch derivation now names `node scripts/check-position-name-fold-loaders.mjs` (and its `--self-test`) for a change to any file under the roots that gate walks. The fix is in the gate's own source: its roots become the `ROOT_DIR_WATCH_HINTS` literal the derivation already reads, and the walk is derived from that literal. `scripts/pm/dispatch-gates.mjs` and `scripts/pm/dispatch-gates.data.mjs` are **unchanged**: no new gate, row, ratchet, `--tier` mandate or constant (ruling 6243472660, term 3). ## What changed - **`scripts/check-position-name-fold-loaders.mjs`.** The walked roots are now one exported literal, `ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**', 'apps/**', 'scripts/**', '.github/**']`. `SCAN_ROOTS` is derived from it, so the walk and the declaration are one list and cannot drift. The one root not carried over is `tools`: the tree has no `tools/` directory (0 tracked files). The scan count is unchanged at 10263 files before and after. A declared root the tree does not have would print as a dead lead on every card the gate matches. - **`scripts/pm/bare-root-worklist.mjs`.** The four `REFUSE-WIDE` verdicts on this gate's `SCAN_ROOTS` rows (packages, examples, apps, scripts; measured 2026-08-31) are withdrawn. Once `SCAN_ROOTS` is derived, the bare-word literals they judged no longer exist, so the self-test's STALE remedy applies: "Delete the entry; do not re-point it at another row". A comment in their place names the ruling and the measured consumer. The declaration battery registers two cases per recorded row, so its floor follows the row count: 128 to 126 (134 to 126 on a run). One docblock sentence that cited the withdrawn 99% row is annotated. **This file is outside the claim's declared file surface.** The edit is forced: with the gate change alone, this self-test exits 1 with 5 failures (the STALE case plus four attribution cases). - **`scripts/pm/dispatch-gates.self-test.mjs`.** One fast-tier block, against the live tree, for both invocations of the gate. It reads one probe per declared subtree root off the family's own extracted hints, plus the exact path PR #24028 edited (`scripts/cross-package-test-inputs.mjs`), and each must be MATCHED. In-place ablation: with the subtree hints stripped, the PR #24028 path and a brand-new package file both read `silent`. A floor case requires at least five subtree roots, so the loop cannot run zero times. ## The mechanism, measured (the card marked it "likely, not confirmed") - **Half right.** The extractor (`extractWatchHints`) keys on **no** constant name and **no** `export`. It reads every quoted, path-shaped literal in the module body. The gate's roots were invisible because they were **bare single-segment words**, which `looksPathy` refuses: `extractWatchHints("const X = 'packages';")` yields `[]`. `'.github'` is dotted and was already read. So `SCAN_ROOTS` being private was not the cause; the spelling was. `check:watch-hint-literal` constrains the declaration to a literal array of admissible literals, and the new one passes (`--list` shows it). - **The miss was recorded, not accidental.** `bare-root-worklist.mjs` held four `REFUSE-WIDE` verdicts on exactly these rows: the declaration would be true, and was refused for the precision of the matched column. Three things decide it here: - Ruling 6243472660 term 3 names this conversion for this gate. - The worklist's own CONTRADICTED and STALE remedy text allows withdrawing the verdict when the declaration is deliberate and overrides the refusal. - This row has the measured consumer that the worklist's 2026-08-26 re-adjudication used as its criterion: PR #24028, a local union of 98/98 that went red in CI. - **Precision cost.** The gate is now MATCHED for every tracked file under the five roots: 10343 of 12129 tracked files (85%; packages 9545, examples 265, apps 51, scripts 437, .github 45), in both invocations. One run takes 2.4 s wall. ## Derivation, before and after | probe | `f5ef054a` | this branch | |:--|:--|:--| | `--commands scripts/cross-package-test-inputs.mjs` (the PR #24028 miss) | 28 commands, no name-fold gate | 30 commands; the diff is exactly the two name-fold commands | | `--commands` on the four card paths (`dispatch-gates.mjs`, `.data.mjs`, `.self-test.mjs`, the gate) | 33 commands | n/a (the derivation files are not in this diff) | | `--commands` with no paths (this diff, 3 files) | — | 32 commands, listed under Verification | ## Census (acceptance 2) **Population, by reading.** Every gate's population constant, read by `node scripts/pm/bare-root-worklist.mjs`, the derivation-coupled instrument for this exact species (a population constant holding a bare top-level word the extractor reads nothing for). Its `--wide` sweep is the unrestricted contrast, and its CENSUS map holds the fs-trace census of computed roots. Each root's placement below is the derivation's own `placeFamily` verdict for a brand-new file under that root, read on this branch. Queries over `scripts/*.mjs` and `scripts/pm/*.mjs` on this branch (files hit; controls first): - control `isEntrypoint`: 213 · control `readFileSync`: 255 - `readdirSync|readdir\(`: 146 · `\bwalk[A-Za-z]*\(`: 99 · `'ls-files'`: 32 · `SCAN_ROOTS?\b`: 25 · `\bROOTS\b`: 22 - `^(export )?const [A-Z_]*_WATCH_HINTS =` (existing declarations under `scripts/`): 60 at `f5ef054a`, 61 here. `check:watch-hint-literal --list` prints 75 declarations repo-wide. - Bare-root worklist (restricted): 82 rows, 47 gate source files at `f5ef054a`; 78 rows, 46 files here. Wide contrast sweep: 140 pairs, 84 files at `f5ef054a`; 136 pairs, 83 files here. The diff in both sweeps is exactly the four name-fold rows. **Answer: yes, other gates keep roots the same undeclared way. One is converted in this PR; every other is already read or is listed below with its reason. No row was added to `dispatch-gates.data.mjs`.** | how the roots are held today | gates (source file · root) | action | |:--|:--|:--| | bare words, private; now the declared literal | `check-position-name-fold-loaders` · packages, examples, apps, scripts (plus `.github`, already read) | **converted in this PR** | | bare word beside a declared literal at the same root (REACHABLE: a brand-new file under the root derives the gate) | `check-doc-formula-expressions` (packages/lint) · content, docs, skills; `check-agent-test-spelling` · scripts, skills; `check-cli-command-ids` · scripts; `check-corpus-claim-drift` · skills; `check-doc-authoring` · packages, content, docs, skills; `check-doc-route-spelling` · skills; `check-examples-live-imports` · examples; `check-org-identifier` · apps, examples, packages; `check-role-word` · skills; `check-scripts-symbol-anchors` · scripts; `check-skill-compatibility-version` · skills; `check-skill-frame-sync` · skills; `check-skill-identifier-liveness` · skills | already read | | declared literal, narrower than the root (DECLARED-NARROWER; the gate reads only that subtree) | `build-skill-docs`, `build-skill-references` (packages/spec) · skills; `check-declaration-mirrors` · scripts; `check-driver-conformance` DRIVERS_DIR · packages; `check-dual-build-cjs-loads` · packages; `check-i18n-coverage` · examples; `check-logger-receiver-detach` · apps, examples, packages; `check-objectql-double-limit` · packages; `check-ratchet-remedy-authority` · scripts; `check-where-matcher-conformance` · packages; `pm/check-skill-id-lint` · skills | already read | | bare word; a precise spelling exists and is deferred by a recorded verdict for want of a consumer (SPELLABLE-UNDECLARED) | `check-adr-0087-registration` · apps, examples, packages; `check-skill-compatibility-version` PACKAGE_ROOTS · apps, examples, packages; `check-i18n-bundles`, `check-i18n-coverage`, `check-i18n-stale-fill` · packages; `check-skills-token-ratchet` · skills | **listed.** The verdict is recorded; declaring re-decides it and needs its own measured consumer | | bare word; the root IS the population and the declaration is refused for precision (REFUSE-WIDE) | `check-dispatcher-error-vocabulary`, `check-error-code-casing`, `check-error-status-conformance`, `check-runtime-services-index`, `check-single-authz-resolver`, `check-skill-identifier-liveness` IMPL_ROOTS, `measure-durability-swallow-family` · packages; `check-engine-double-contract` · examples, packages | **listed.** Recorded verdict, no measured consumer on any of these rows (see open question). `check-error-status-conformance` is derived by its content kind instead | | bare word, REFUSE-WIDE, and the gate is on the `wide-population` channel (maintainer ruling 2026-09-05) | `check-optional-error-sink-contract` · packages; `check-resume-authority-declared` · examples, packages; `check-verify-stand-in-erasure` · examples, packages | **listed.** Declared wide by ruling; a subtree hint would contradict the marker | | bare word; the population is a file-kind filter no glob spells (REFUSE-UNSPELLABLE) | `check-driver-conformance` CASE_SETS_DIR, `check-examples-live-imports` PACKAGES_ROOT · packages; `check-meta-type-normalized` · packages (wide channel); `check-runner-env-posture` · apps, examples, packages (wide channel) | **listed.** Its roots cannot be read that way | | bare word, judged by nobody (UNJUDGED; the map's owner judges these) | `check-doc-security-posture` (packages/lint) · skills; `check-live-db-isolation` · apps, examples, packages (wide channel); `check-vendor-version-stamps` · apps, examples, packages, scripts; `check-whole-set-label-write` · scripts (MATCHED through other hints). Measured "not a population": `check-console-injection` ×2, `check-doc-frontmatter` · docs, `check-doc-security-posture` · docs | **listed.** The worklist forbids the pass that sees a row from judging it | | computed root, no constant (fs-trace CENSUS, 13 families) | wide channel: `check:route-envelope`, `check:live-db-isolation`, `check:startup-registry-verdict`, `check:wildcard-fallthrough`, `check:init-service-contract`, `check:settings-bind-window`; always-runs: `check:driver-memory-census`; brand-new package file `silent`: `check:parse-guard`, `check:cli-command-ids`, `check:type-check-coverage`, `check:type-check-debt`, `check:error-code-provenance`; MATCHED: `check:org-identifier` | **listed.** A `join(ROOT, 'packages')` or loop literal is not a literal the extractor can read, so these cannot be read that way | | computed root outside both recognisers (the `--wide` sweep's 34 extra files, each read) | `check-cross-package-test-inputs` (a loop over packages/apps/examples; its population is test files, and a new test file under any of the three derives it through the test-file convention trigger, probed with `examples/app-crm/src/zz-probe.test.ts`); `check:skill-examples`, `check:yaml-examples` (a `dir:` property, `path.resolve(REPO_ROOT, 'skills')`); `check:empty-state` (a resolved packages root; undetermined); `check:object-def-param-keys` (`git ls-files packages`); `check:service-providers` (`MANIFEST_PARENTS`); `check:docs-audit-scope` (`walkSrc(join(root, 'packages'))`); `check:console-sha` (a dist path); `check:tenant-chokepoint` (join-built file paths); `measure-test-shard-timings --self-test` (a loop literal); `check:durability-log-level` (derived by its content kind and its declared scan surface) | **listed.** Computed spellings the extractor cannot read. Of the 34 extra files: 8 are the CENSUS families in the row above; `check-self-test-wired` walks `scripts` and declares `scripts/**/*.mjs`, `.mts` and `.sh` (already read); these 11 are listed; the other 14 hold the word as a data key, a baseline path built by `join`, a self-test fixture or path parsing, not a walked root | **The three earlier instances the card names, read today** (`--commands` on real files): - `check:error-status-conformance`: derived for `packages/metadata/src/loaders/ambiguous-metadata-stem.ts` through its HTTP-status content kind. Its `SCAN_ROOT packages` row stays REFUSE-WIDE. - `check:durability-log-level`: derived for `packages/metadata/src/loaders/database-loader.ts` through its durability content kind and its declared scan surface. - `check:i18n-coverage`: derived for `examples/app-showcase/src/data/analytics/showcase.cube.ts` through its declared examples hints. Its packages half stays SPELLABLE-UNDECLARED and is reached through the extract-config convention trigger. ## Verification All readings below are on this branch's final head, **`cf4e110f`** (`git rev-parse --short HEAD`), with no commit after them. - **Derived local subset.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` with no paths (the change set came from git: 3 files vs merge base `f5ef054a7`) derived 32 commands. Each was run with its exit code captured before any pipe, and all 32 exited **0**. `pnpm check:declared-population-live` was also run beyond the union, because this diff adds a declaration; it exited 0 (268 of 330 families declare a population, all live). - **Reconciliation.** `--ran` printed `✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero — all 32 recorded an exit code and none of them is 3)`, with exit 0. - **Verdict lines for the families this diff moves:** - `node scripts/check-position-name-fold-loaders.mjs` → `10263 file(s) scanned under packages, examples, apps, scripts, .github; 8 reference(s): 6 test, 2 declared instrument, 0 loader` (exit 0). The same 10263 at `f5ef054a` with `tools` still in the list. - `… --self-test` → `39 cases pass` (exit 0). - `node scripts/pm/bare-root-worklist.mjs --self-test` → `OK self-test: 78 live row(s), 56 unreachable as spelled, 43 recorded verdict(s) — none stale, none missing, none contradicted` (exit 0). With the gate change alone it exits 1 with 5 failures. - `pnpm check:watch-hint-literal` → `75 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 50 …` (exit 0). - `pnpm check:pm-dispatch-gates` (the fast tier, as CI runs it on a PR) → `✓ dispatch-gates self-test (fast tier): 1902 cases pass` (exit 0, 384 s). - **Full battery, both tiers** (beyond what a PR runs; it is what `main`'s push and hourly runs execute): `node scripts/pm/dispatch-gates.mjs --self-test` run detached → `fast 1902 case(s) in 394.0s · slow 260 case(s) in 1170.8s`, `✓ dispatch-gates self-test: 2162 cases pass.`, exit 0, 1566 s wall. - **Other local gates, all exit 0:** `check-ci-filter-parity`, `check-closing-keyword-parity` (both), `check-comment-mask-corpus`, `check-declaration-mirrors` (both), `check-scripts-symbol-anchors` (both), `check-self-test-wired` (both), `check-self-test-workflow-commands` (both), `check-whole-set-label-write` (both), `check:agent-test-spelling`, `check:bash32-floor`, `check:cli-command-ids`, `check:cross-package-test-inputs`, `check:driver-memory-census`, `check:entry-guard`, `check:gitlink-declared`, `check:nul-bytes` (12120 text files, no control bytes), `check:parse-guard`, `check:pnpm-filter-targets`, `check:pnpm-flag-spellings`, `check:ratchet-remedy-authority`, `check:refd-timer-probe`. - **Not run locally, CI's to report:** the full `check:*` farm in `Lint & Repo Gates` and the other required contexts. No package is touched, so there is no build closure, package `test` or `typecheck` to owe. ## Acceptance notes - The `tools` root dropped from the walk is a behaviour delta only for a tree that grows a top-level `tools/`. Today it is a no-op: 10263 files are scanned before and after. A future `tools/` would need a declared root, as any other new root would. - The derivation files need no change: the extractor reads the new literal as it stands. The self-test block is the only `dispatch-gates*` edit, and #24073's job-condition rendering is untouched. - Test files `git grep` finds naming an edited script: none read one. `packages/spec/src/system/compliance-families-retirement.test.ts` and `packages/types/src/driver-error-classification.callers.test.ts` mention the dispatch-gates self-test in prose only, so they are not owed a run. - Not published: `scripts/**` is in no package's `files[]`, so `skip-changeset`. --- _Generated by [Claude Code](https://claude.ai/code/session_01QBcLjVCmcWbZEk6qhYMib2)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 77e9f79 commit a422f04

3 files changed

Lines changed: 83 additions & 28 deletions

File tree

‎scripts/check-position-name-fold-loaders.mjs‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,27 @@ const DECLARED_INSTRUMENTS = [
111111
'scripts/check-position-name-fold-loaders.mjs',
112112
];
113113

114-
const SCAN_ROOTS = ['packages', 'examples', 'apps', 'scripts', 'tools', '.github'];
114+
/**
115+
* The roots the walk visits, spelled as the subtree population this gate
116+
* DECLARES to `scripts/pm/dispatch-gates.mjs` (the `ROOT_DIR_WATCH_HINTS`
117+
* idiom; `check:watch-hint-literal` holds it to a literal array the extractor
118+
* admits). A literal is how the declaration reaches the derivation: the
119+
* extractor reads source text, and a bare single-segment word (`'packages'`)
120+
* yields no hint at all, so while the walk read a private list of bare words a
121+
* card editing any file under these roots was told nothing about this gate and
122+
* met it first in CI (#24037).
123+
*
124+
* ONE list, two readers: {@link SCAN_ROOTS} is DERIVED from it, so the walk and
125+
* the declaration cannot drift apart. ⛔ Never hand-copy the roots back into a
126+
* second literal — the declaration would then be free to say one thing while
127+
* the walk does another, which is the silent-green shape the derivation exists
128+
* to refuse. Every root is live in the tree: a root the tree does not have
129+
* scans nothing and would be printed by the derivation as a dead lead.
130+
*/
131+
export const ROOT_DIR_WATCH_HINTS = ['packages/**', 'examples/**', 'apps/**', 'scripts/**', '.github/**'];
132+
133+
/** The directories the walk descends from, read off {@link ROOT_DIR_WATCH_HINTS}. */
134+
const SCAN_ROOTS = ROOT_DIR_WATCH_HINTS.map((hint) => hint.replace(/\/\*\*$/, ''));
115135

116136
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', '.turbo', '.next', 'coverage', '.git']);
117137

‎scripts/pm/bare-root-worklist.mjs‎

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -942,31 +942,16 @@ const TRIAGE = new Map([
942942
+ 'skills, IS declared beside the constant: that one is small, is the surface the gate '
943943
+ 'exists for, and naming a skills-only card is the discoverability this idiom is for',
944944
}],
945-
['scripts/check-position-name-fold-loaders.mjs SCAN_ROOTS packages', {
946-
verdict: 'REFUSE-WIDE',
947-
why: 'every text file under the root is walked and any one of them could name the #13419 '
948-
+ 'fixture — 5562 of 5625 (99%), measured 2026-08-31. The population is not a subset of the '
949-
+ 'root that a narrower glob could describe; it IS the root. A true declaration here would '
950-
+ 'name this gate on every card touching a package, which is the precision trade this '
951-
+ 'verdict refuses',
952-
}],
953-
['scripts/check-position-name-fold-loaders.mjs SCAN_ROOTS examples', {
954-
verdict: 'REFUSE-WIDE',
955-
why: '240 of 243 (99%). Refused with its packages half rather than split from it: the loader '
956-
+ 'this gate hunts is a COMPOSITION site, and nothing says one lands under examples/ rather '
957-
+ 'than packages/ — declaring only the small root would read as a claim about where that '
958-
+ 'happens, which is exactly what is not known',
959-
}],
960-
['scripts/check-position-name-fold-loaders.mjs SCAN_ROOTS apps', {
961-
verdict: 'REFUSE-WIDE',
962-
why: '36 of 40 (90%) — same trade, same reason as the examples half',
963-
}],
964-
['scripts/check-position-name-fold-loaders.mjs SCAN_ROOTS scripts', {
965-
verdict: 'REFUSE-WIDE',
966-
why: '295 of 298 (99%). This root is where the gate own allowed readers live '
967-
+ '(DECLARED_INSTRUMENTS), so it is walked wholesale for the same reason as the others: a '
968-
+ 'new reader is found, then classified, never assumed absent',
969-
}],
945+
// WITHDRAWN 2026-10-18 (#24037): the four REFUSE-WIDE rows of
946+
// check-position-name-fold-loaders.mjs SCAN_ROOTS (packages 99%, examples
947+
// 99%, apps 90%, scripts 99%, measured 2026-08-31). Ruling 6243472660 (the
948+
// maintainer's letter B on #24171, term 3) orders that gate's roots made the
949+
// ROOT_DIR_WATCH_HINTS declaration the derivation reads, on a MEASURED
950+
// consumer — PR #24028 reconciled its local gate union complete and met this
951+
// gate red in CI. The gate now declares the whole roots and derives
952+
// SCAN_ROOTS from that literal, so the bare words these rows judged no longer
953+
// exist and the verdicts left with them (the STALE remedy below). The
954+
// measured reasons stand in this file's history at f5ef054a.
970955
['scripts/check-single-authz-resolver.mjs SCAN_ROOTS packages', {
971956
verdict: 'REFUSE-WIDE',
972957
why: 'walks every non-test TS source under the root — 1898 of 4903 (39%). True, and it would '
@@ -1349,7 +1334,8 @@ const TRIAGE = new Map([
13491334
* `check:type-check-coverage`/`check:type-check-debt` 55.8%). Each is measured on this tree by the SAME method as
13501335
* every other row here, and the ratio does not change the verdict either
13511336
* direction — existing `TRIAGE` rows already span 39% (`check:authz-resolver`)
1352-
* to 99% (`scripts/check-position-name-fold-loaders.mjs`) under the identical
1337+
* to 99% (`scripts/check-position-name-fold-loaders.mjs`, a row since withdrawn
1338+
* when that gate declared its roots — #24037) under the identical
13531339
* REFUSE-WIDE trade, wide or narrow within that span is still the same
13541340
* width-alone trade. Recorded as measured rather than clipped to the summary
13551341
* range; the discrepancy against the issue's own "32–52%" framing is stated
@@ -2113,7 +2099,10 @@ const SELF_TEST_BATTERIES = Object.freeze({
21132099
// ⭐ 43 → 128 (#15926): the loop now runs over EVERY recorded row rather than
21142100
// the eleven DECLARED-NARROWER ones, and the RED end of the new direction is
21152101
// driven on constructed gates beside it. A FLOOR, measured on a run.
2116-
"The DECLARATION a row describes, read from the gate's own SOURCE": 128,
2102+
// 128 → 126 (#24037): the loop registers two cases per recorded row, and the
2103+
// four check-position-name-fold-loaders rows were withdrawn (134 → 126 on a
2104+
// run) — fewer rows to judge, not cases that stopped registering.
2105+
"The DECLARATION a row describes, read from the gate's own SOURCE": 126,
21172106
'CENSUS_REFUSE_WIDE, pinned on its OWN terms (#14695)': 5,
21182107
});
21192108

‎scripts/pm/dispatch-gates.self-test.mjs‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3851,6 +3851,52 @@ export function selfTest({ tier = 'full' } = {}) {
38513851
);
38523852
}
38533853

3854+
// ── The name-fold loader gate's WALKED roots, declared (#24037) ───────────
3855+
//
3856+
// `check-position-name-fold-loaders.mjs` reads every text file under its
3857+
// roots, and kept them as a private list of bare words, which the extractor
3858+
// admits as nothing. A card editing a file under those roots therefore
3859+
// derived no command for it: PR #24028 reconciled its local union complete
3860+
// and met this gate red in CI, over a line it added to
3861+
// `scripts/cross-package-test-inputs.mjs`. The gate now declares the roots
3862+
// with the ROOT_DIR_WATCH_HINTS idiom and derives its walk from that literal,
3863+
// with no change to the extractor.
3864+
//
3865+
// Pinned against the LIVE tree, for the reason the scripts/** case above
3866+
// states. The probes are read off the family's own extracted hints rather
3867+
// than retyped here, so every root the gate declares gets one, and the first
3868+
// probe is the exact path the measured miss edited. The ablation strips the
3869+
// subtree hints and the verdict must leave MATCHED, so these cases cannot pass
3870+
// through another literal that happens to cover a probe.
3871+
const NAME_FOLD = 'scripts/check-position-name-fold-loaders.mjs';
3872+
const nameFoldFamilies = discoverFamilies().byCheck;
3873+
for (const key of [NAME_FOLD, `${NAME_FOLD} --self-test`]) {
3874+
const entry = nameFoldFamilies.get(key);
3875+
t(`${key} is discovered at all — the pins below mean nothing without this`, Boolean(entry));
3876+
const roots = (entry?.hints ?? []).filter((h) => h.endsWith('/**')).map((h) => h.slice(0, -'/**'.length));
3877+
// The floor: the five roots the walk reads at this commit. A loop over an
3878+
// emptied hint set runs zero cases and prints nothing, which reads as a pass.
3879+
t(`${key} declares at least the five subtree roots its walk reads — read: [${roots.join(', ')}]`, roots.length >= 5);
3880+
const probes = ['scripts/cross-package-test-inputs.mjs', ...roots.map((r) => `${r}/the-one-nobody-has-written-yet.ts`)];
3881+
for (const probe of probes) {
3882+
const verdict = entry ? classifyEntry(entry, [probe]).verdict : null;
3883+
t(`a change to ${probe}, under a root this gate walks, derives ${key} — read: ${verdict}`, verdict === 'matched');
3884+
}
3885+
const undeclared = entry ? { ...entry, hints: entry.hints.filter((h) => !h.endsWith('/**')) } : null;
3886+
const residual = undeclared
3887+
? ['scripts/cross-package-test-inputs.mjs', 'packages/the-one-nobody-has-written-yet/src/index.ts']
3888+
.map((p) => classifyEntry(undeclared, [p]).verdict)
3889+
: [];
3890+
t(
3891+
`…and it is the subtree declaration doing it: strip it and ${key} is NOT MATCHED for the measured miss `
3892+
+ `or a brand-new package file — read: [${residual.join(', ')}]`,
3893+
Boolean(undeclared)
3894+
&& undeclared.hints.length < entry.hints.length
3895+
&& residual.length === 2
3896+
&& residual.every((v) => ['silent', 'undetermined'].includes(v)),
3897+
);
3898+
}
3899+
38543900
// ── The MANIFEST population of check:merge-driver (#15501) ────────────────
38553901
//
38563902
// That family's `git-merge-regen --self-test` refuses a generator with no

0 commit comments

Comments
 (0)