Skip to content

Commit 181001f

Browse files
fix(dispatch-gates): a package-root vitest config or manifest edit derives check-console-intercept-disarm (#29556)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ87YEMjdviwYR817rnQYfmc8)_ Fixes #29353 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`check-console-intercept-disarm` 在自己源码里声明它真正读的文件(各工作区包根的 `package.json` 与 `vitest.config.*`),`dispatch-gates` 据此把它派给改这些文件的卡;残差台账里它的两行随之删除。 - 为什么改:改包根 vitest 配置的卡,本地派生清单里没有这道门,只能等 CI 变红才知道(PR #29345 为此多跑了一轮)。 - 风险与回滚:只动工具脚本,不发布任何包;回滚即 revert 本 PR,派生回到原先的"无路径"状态。 - 先看:`scripts/check-console-intercept-disarm.mjs`(新声明与漂移检查)、`scripts/pm/dispatch-gates.self-test.mjs`(新增的实树钉子)、`scripts/pm/dispatch-gates.data.mjs`(删掉的两行)。 - 两分钟验证:`node scripts/pm/dispatch-gates.mjs --commands --paths packages/services/service-sms/vitest.config.ts`,预期输出里出现这道门的两条命令(主扫描与 `--self-test`)。 - 需要你做的:无,席位落地。 ## What changes - **Landing is the gate's own file, not only `dispatch-gates.data.mjs`.** The order expected the data table; measured, the derivation reads a gate's population only from the gate's own source (the `ROOT_DIR_WATCH_HINTS` idiom), and `dispatch-gates.data.mjs`'s header says it is "not a watch surface". So the producer-side fix is a declaration in `scripts/check-console-intercept-disarm.mjs`: - `ROOT_DIR_WATCH_HINTS`: one pair per workspace glob the gate parses, `DIR/*/package.json` and `DIR/*/vitest.config.*`. A glob in a non-final segment makes the derivation judge the hint as a pattern, one path segment per star, so a test file or a config below the package root is not covered. - A drift check: `main()` exits 1 when the declaration and the parsed `pnpm-workspace.yaml` globs disagree in either direction (`watchHintDrift`), and the gate's self-test holds the real tree to it plus both drift directions on fixtures. A literal array, as `check:watch-hint-literal` requires. - `workspaceGlobs()` is split out of `workspacePackageDirs()` with no change in behaviour; the three gates that import `workspacePackageDirs` are untouched. - `scripts/pm/dispatch-gates.data.mjs`: the two `ROOT_WALK_RESIDUE_LEDGER` rows for the gate and its `--self-test` are retired, since both families have left the population. A note records why, beside the two earlier departures. - `scripts/pm/dispatch-gates.self-test.mjs`: the card's acceptance, pinned against the live tree. Every package-root vitest config (80) and the manifest beside it places both invocations. A new package's config and manifest do too. No tracked test file under a package (6926) places either, and neither does a brand-new test file or a config below a package root. ## Why The rows stated the population correctly and drew the wrong conclusion: an EDITED package-root config moves the gate as surely as a new package does. Because the rows only exempted the gate from the whole-tree channel, no path derived it at all. ## Tests - Reverse check, with the gate file restored to `399df5bb` (tree only, then restored from HEAD; `git diff HEAD` empty, blob hash matched): 80 of 80 package-root configs place neither invocation, and the test-file probe reads `undetermined`. With the fix, 0 of 80 are missed, and test files read `silent`. - `node scripts/check-console-intercept-disarm.mjs --self-test`: `self-test OK: 15 cases + real-tree population floor + watch-hint declaration.` Scan: `OK: 80 vitest-running package(s)`. ## Verification Final union on head `1b3ea66a` (base `399df5bb`): `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 32 commands from the 3 changed paths; all 32 exit 0. `--ran` reconciliation: "32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED". `check:pm-dispatch-gates`: "1945 cases pass" (fast tier). The first pass on `a39185ba` was 31 of 32: `check:watch-hint-literal` refused the `Object.freeze` wrapper, fixed in `1b3ea66a`. <details><summary>32 commands and exit codes on 1b3ea66</summary> ```text node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-console-intercept-disarm.mjs :: exit 0 node scripts/check-console-intercept-disarm.mjs --self-test :: exit 0 node scripts/check-declaration-mirrors.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs --self-test :: exit 0 node scripts/check-self-test-wired.mjs :: exit 0 node scripts/check-self-test-wired.mjs --self-test :: exit 0 node scripts/check-self-test-workflow-commands.mjs :: exit 0 node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0 node scripts/check-whole-set-label-write.mjs :: exit 0 node scripts/check-whole-set-label-write.mjs --self-test :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:bash32-floor :: exit 0 pnpm check:ci-filter-parity :: exit 0 pnpm check:cli-command-ids :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:entry-guard :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:parse-guard :: exit 0 pnpm check:pm-dispatch-gates :: exit 0 pnpm check:pnpm-filter-targets :: exit 0 pnpm check:pnpm-flag-spellings :: exit 0 pnpm check:ratchet-remedy-authority :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:scripts-symbol-anchors :: exit 0 pnpm check:watch-hint-literal :: exit 0 ``` </details> ## Acceptance notes - Not derivable, by construction: `pnpm-workspace.yaml` and the root `vitest.preset.mts` also move this gate, but neither carries a separator, so no spelling of either is a hint the derivation admits. Edits to those two files still learn of the gate from CI. - Out of scope, reported to the seat and not fixed here: the gate's workspace parse stops at the first comment line inside the `packages:` list, so the four `ui` members below it are outside its population. Measured: 40 `ui` workspace packages run vitest and none sets the disarm. The declaration follows what the gate reads today, and its drift check will ask for the `ui` pairs on the day the parse reads them. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. (CI-owned run) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset. (publishes nothing: `scripts/**` only, `skip-changeset`) - [x] 6. `packages/spec` touched: n/a. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JQwxbXV6v4rbiDvvaaabhb --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbXV6v4rbiDvvaaabhb)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 389de4a commit 181001f

3 files changed

Lines changed: 251 additions & 34 deletions

File tree

‎scripts/check-console-intercept-disarm.mjs‎

Lines changed: 177 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,56 @@ const VITEST_CONFIG_NAMES = [
115115
'vitest.config.cjs',
116116
];
117117

118+
// The population this gate reads, declared for `scripts/pm/dispatch-gates.mjs`
119+
// (the ROOT_DIR_WATCH_HINTS idiom), so a card that edits a package-root
120+
// manifest or vitest config is told about this gate before CI is. `scan()`
121+
// below opens exactly two files per workspace package: its `package.json` and
122+
// its package-root vitest config. Nothing else under a package moves the
123+
// verdict, so a new test file under an existing package derives nothing.
124+
//
125+
// One pair per glob `workspaceGlobs()` reads, in that order. A `<dir>/*` glob
126+
// is spelled `<dir>/*/package.json` and `<dir>/*/vitest.config.*` — the glob in
127+
// a non-final segment makes the derivation judge it as a pattern, one path
128+
// segment per `*`. A literal member `<dir>` would be spelled `<dir>/package.json`
129+
// and `<dir>/vitest.config` (`watchHintsForGlob`): with no glob ahead of it a
130+
// trailing `.*` is collapsed to `<dir>/vitest.config.`, which names nothing,
131+
// while the bare stem reaches every one of VITEST_CONFIG_NAMES through the
132+
// derivation's module-extension equality. None is read today: the parse stops
133+
// at the first comment line in the `packages:` list, so the `ui` members below
134+
// it are not in this gate's population, and are not declared here either.
135+
//
136+
// ⛔ A literal array, never computed from the globs (`check:watch-hint-literal`):
137+
// the derivation reads source text, so a computed list contributes no hint.
138+
// Hand-kept, and `main()` holds it to the workspace file: a glob in
139+
// `pnpm-workspace.yaml` with no pair here, or a pair here for a glob that file
140+
// no longer lists, is a finding (`watchHintDrift`). Not readable from the
141+
// repo-root files themselves — `pnpm-workspace.yaml` and `vitest.preset.mts`
142+
// carry no separator, so no spelling of them is a hint the derivation admits.
143+
export const ROOT_DIR_WATCH_HINTS = [
144+
'packages/*/package.json',
145+
'packages/*/vitest.config.*',
146+
'packages/apps/*/package.json',
147+
'packages/apps/*/vitest.config.*',
148+
'packages/drivers/*/package.json',
149+
'packages/drivers/*/vitest.config.*',
150+
'packages/plugins/*/package.json',
151+
'packages/plugins/*/vitest.config.*',
152+
'packages/qa/*/package.json',
153+
'packages/qa/*/vitest.config.*',
154+
'packages/triggers/*/package.json',
155+
'packages/triggers/*/vitest.config.*',
156+
'packages/services/*/package.json',
157+
'packages/services/*/vitest.config.*',
158+
'packages/adapters/*/package.json',
159+
'packages/adapters/*/vitest.config.*',
160+
'packages/connectors/*/package.json',
161+
'packages/connectors/*/vitest.config.*',
162+
'apps/*/package.json',
163+
'apps/*/vitest.config.*',
164+
'examples/*/package.json',
165+
'examples/*/vitest.config.*',
166+
];
167+
118168
const DISARM_RE = /\bdisableConsoleIntercept\s*:\s*true\b/;
119169
const REARM_RE = /\bdisableConsoleIntercept\s*:\s*false\b/;
120170
const PROJECTS_RE = /\bprojects\s*:\s*\[/;
@@ -207,6 +257,31 @@ const REMEDY = ` // Late console writes must not redden a green suite (#10374
207257
* instead of silently emptying its population.
208258
*/
209259
export function workspacePackageDirs(root) {
260+
const dirs = [];
261+
for (const glob of workspaceGlobs(root)) {
262+
if (glob.endsWith('/*')) {
263+
const base = join(root, glob.slice(0, -2));
264+
if (!existsSync(base)) continue;
265+
for (const entry of readdirSync(base, { withFileTypes: true })) {
266+
if (!entry.isDirectory()) continue;
267+
const dir = join(base, entry.name);
268+
if (existsSync(join(dir, 'package.json'))) dirs.push(dir);
269+
}
270+
} else if (!glob.includes('*')) {
271+
const dir = join(root, glob);
272+
if (existsSync(join(dir, 'package.json'))) dirs.push(dir);
273+
} else {
274+
throw new Error(
275+
`pnpm-workspace.yaml glob '${glob}' is wider than '<dir>/*' — teach ` +
276+
`workspacePackageDirs() the new shape before trusting this gate again`,
277+
);
278+
}
279+
}
280+
return dirs;
281+
}
282+
283+
/** `pnpm-workspace.yaml`'s `packages:` globs, in file order. Throws when none parse. */
284+
export function workspaceGlobs(root) {
210285
const raw = readFileSync(join(root, 'pnpm-workspace.yaml'), 'utf8');
211286
const globs = [];
212287
let inPackages = false;
@@ -227,27 +302,51 @@ export function workspacePackageDirs(root) {
227302
if (globs.length === 0) {
228303
throw new Error(`no package globs parsed from ${join(root, 'pnpm-workspace.yaml')}`);
229304
}
230-
const dirs = [];
231-
for (const glob of globs) {
232-
if (glob.endsWith('/*')) {
233-
const base = join(root, glob.slice(0, -2));
234-
if (!existsSync(base)) continue;
235-
for (const entry of readdirSync(base, { withFileTypes: true })) {
236-
if (!entry.isDirectory()) continue;
237-
const dir = join(base, entry.name);
238-
if (existsSync(join(dir, 'package.json'))) dirs.push(dir);
239-
}
240-
} else if (!glob.includes('*')) {
241-
const dir = join(root, glob);
242-
if (existsSync(join(dir, 'package.json'))) dirs.push(dir);
243-
} else {
244-
throw new Error(
245-
`pnpm-workspace.yaml glob '${glob}' is wider than '<dir>/*' — teach ` +
246-
`workspacePackageDirs() the new shape before trusting this gate again`,
247-
);
248-
}
305+
return globs;
306+
}
307+
308+
/**
309+
* The watch-hint pair one workspace glob owes `ROOT_DIR_WATCH_HINTS`, spelled
310+
* the way that declaration's comment explains. A glob wider than `<dir>/*` owes
311+
* nothing here: `workspacePackageDirs()` already refuses it, loudly.
312+
*/
313+
export function watchHintsForGlob(glob) {
314+
if (glob.endsWith('/*')) return [`${glob}/package.json`, `${glob}/vitest.config.*`];
315+
if (!glob.includes('*')) return [`${glob}/package.json`, `${glob}/vitest.config`];
316+
return [];
317+
}
318+
319+
/**
320+
* Where `ROOT_DIR_WATCH_HINTS` and the workspace globs disagree: `missing` are
321+
* pairs a listed glob owes and the declaration lacks (a package there edits its
322+
* config and no dispatch derivation names this gate); `stale` are declared
323+
* hints no listed glob owes (a lead for a population this gate no longer reads).
324+
*/
325+
export function watchHintDrift(globs, hints = ROOT_DIR_WATCH_HINTS) {
326+
const owed = globs.flatMap(watchHintsForGlob);
327+
return {
328+
missing: owed.filter((h) => !hints.includes(h)),
329+
stale: hints.filter((h) => !owed.includes(h)),
330+
};
331+
}
332+
333+
function watchHintDriftFindings(root) {
334+
const { missing, stale } = watchHintDrift(workspaceGlobs(root));
335+
const findings = [];
336+
if (missing.length > 0) {
337+
findings.push(
338+
`ROOT_DIR_WATCH_HINTS lacks ${missing.join(', ')} — pnpm-workspace.yaml lists the glob, so a ` +
339+
`card editing a package-root manifest or vitest config there is not told about this gate ` +
340+
`until CI. Add the pair to the declaration in scripts/check-console-intercept-disarm.mjs.`,
341+
);
249342
}
250-
return dirs;
343+
if (stale.length > 0) {
344+
findings.push(
345+
`ROOT_DIR_WATCH_HINTS declares ${stale.join(', ')}, which no pnpm-workspace.yaml glob owes — ` +
346+
`remove it from the declaration in scripts/check-console-intercept-disarm.mjs.`,
347+
);
348+
}
349+
return findings;
251350
}
252351

253352
/** @returns {{findings: string[], vitestPackages: number}} */
@@ -325,11 +424,12 @@ function main() {
325424
let result;
326425
try {
327426
result = scan(REPO_ROOT);
427+
result.driftFindings = watchHintDriftFindings(REPO_ROOT);
328428
} catch (error) {
329429
console.error(`check-console-intercept-disarm: MEASUREMENT FAILED — ${error.message}`);
330430
process.exit(2);
331431
}
332-
const { findings, vitestPackages } = result;
432+
const { findings, vitestPackages, driftFindings } = result;
333433
if (vitestPackages === 0) {
334434
console.error(
335435
'check-console-intercept-disarm: MEASUREMENT FAILED — the scan found ZERO ' +
@@ -343,8 +443,14 @@ function main() {
343443
`check-console-intercept-disarm: ${findings.length} package(s) exposed to the ` +
344444
`vitest late-console teardown race (#10374):\n\n${findings.join('\n\n')}\n`,
345445
);
346-
process.exit(1);
347446
}
447+
if (driftFindings.length > 0) {
448+
console.error(
449+
`check-console-intercept-disarm: the declared watch population has drifted from ` +
450+
`pnpm-workspace.yaml:\n\n${driftFindings.join('\n\n')}\n`,
451+
);
452+
}
453+
if (findings.length > 0 || driftFindings.length > 0) process.exit(1);
348454
console.log(
349455
`OK: ${vitestPackages} vitest-running package(s), every one disarms console ` +
350456
`interception at the package root.`,
@@ -644,6 +750,54 @@ check(() => {
644750
}
645751
});
646752

753+
// The declared watch population (ROOT_DIR_WATCH_HINTS) against the REAL
754+
// workspace file, and the drift reading in both directions. Fixture globs are
755+
// assembled from segments so no fixture string reads as a path literal of
756+
// this gate's own.
757+
const realDrift = watchHintDrift(workspaceGlobs(REPO_ROOT));
758+
check(() => {
759+
if (realDrift.missing.length > 0 || realDrift.stale.length > 0) {
760+
failures += 1;
761+
console.error(
762+
`self-test FAIL: ROOT_DIR_WATCH_HINTS has drifted from pnpm-workspace.yaml — ` +
763+
`missing: ${realDrift.missing.join(', ') || 'none'}; stale: ${realDrift.stale.join(', ') || 'none'}.`,
764+
);
765+
}
766+
});
767+
const extraGlob = ['fixture-root', '*'].join('/');
768+
const addedGlob = watchHintDrift([...workspaceGlobs(REPO_ROOT), extraGlob]);
769+
check(() => {
770+
const want = watchHintsForGlob(extraGlob);
771+
if (want.length !== 2 || addedGlob.missing.join('|') !== want.join('|') || addedGlob.stale.length !== 0) {
772+
failures += 1;
773+
console.error(`self-test FAIL: a workspace glob the declaration lacks is not read as missing: ${JSON.stringify(addedGlob)}`);
774+
}
775+
});
776+
const droppedGlob = watchHintDrift(workspaceGlobs(REPO_ROOT).filter((g) => g !== workspaceGlobs(REPO_ROOT)[0]));
777+
check(() => {
778+
const want = watchHintsForGlob(workspaceGlobs(REPO_ROOT)[0]);
779+
if (droppedGlob.stale.join('|') !== want.join('|') || droppedGlob.missing.length !== 0) {
780+
failures += 1;
781+
console.error(`self-test FAIL: a declared pair no workspace glob owes is not read as stale: ${JSON.stringify(droppedGlob)}`);
782+
}
783+
});
784+
check(() => {
785+
// A literal member is spelled with the bare config STEM, never a trailing
786+
// `.*` (which the derivation collapses to a name no file has); a `<dir>/*`
787+
// member keeps the `.*`, and a wider glob owes nothing.
788+
const literal = watchHintsForGlob('fixture-root');
789+
const starred = watchHintsForGlob(extraGlob);
790+
const wide = watchHintsForGlob(['fixture-root', '**'].join('/'));
791+
if (
792+
literal[1] !== ['fixture-root', 'vitest.config'].join('/') ||
793+
starred[1] !== [extraGlob, 'vitest.config.*'].join('/') ||
794+
wide.length !== 0
795+
) {
796+
failures += 1;
797+
console.error(`self-test FAIL: watchHintsForGlob spelling — ${JSON.stringify({ literal, starred, wide })}`);
798+
}
799+
});
800+
647801
// ── The floor: every declared battery RAN, and ran its cases (#13489) ────
648802
//
649803
// Evaluated after every battery has had its chance and BEFORE the verdict, so
@@ -692,7 +846,7 @@ check(() => {
692846
}
693847

694848
if (failures > 0) process.exit(1);
695-
console.log(`self-test OK: ${cases.length} cases + real-tree population floor.`);
849+
console.log(`self-test OK: ${cases.length} cases + real-tree population floor + watch-hint declaration.`);
696850

697851
return SELF_TEST_VERDICT;
698852
}

‎scripts/pm/dispatch-gates.data.mjs‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -278,23 +278,22 @@ export const PATH_LIST_MARKER_KEYS = Object.freeze(['inherited-population', 'sel
278278
* reds too: a stale exclusion is an exclusion nobody is measuring any more.
279279
*/
280280
export const ROOT_WALK_RESIDUE_LEDGER = [
281-
[
282-
'scripts/check-console-intercept-disarm.mjs',
283-
'its `scan(REPO_ROOT)` walks `workspacePackageDirs(root)` — every workspace PACKAGE ROOT\'s package.json and '
284-
+ 'vitest.config.*, read off pnpm-workspace.yaml. That is workspace-wide but it is not every file: a new test '
285-
+ 'file under an existing package does not move it, a new PACKAGE does. Declaring the whole tree would put it '
286-
+ 'on every card on a population it does not read.',
287-
],
288-
[
289-
'scripts/check-console-intercept-disarm.mjs --self-test',
290-
'the same gate file, reached through its self-test invocation; the reading above is the whole of it.',
291-
],
292281
[
293282
'scripts/check-skill-frame-freshness.mjs --self-test',
294283
'lint.yml runs the SELF-TEST HALF and never the scan — its step is named that, and the step comment states why '
295284
+ '(the pnpm script would drag the scan in with it). The repo-root default parameter belongs to the scan half '
296285
+ 'CI does not schedule, so a whole-tree row for this family would advertise work no workflow performs.',
297286
],
287+
// ⚖️ `scripts/check-console-intercept-disarm.mjs` and its `--self-test` LEFT this
288+
// population under #29353 and their two rows are gone with them — a listed
289+
// family that stops being a member reds here. They left BY PATH: the gate now
290+
// declares what its scan opens, a package-root `package.json` and vitest
291+
// config under each workspace glob, as `ROOT_DIR_WATCH_HINTS` in its own
292+
// source, and its scan reds when that declaration drifts from
293+
// `pnpm-workspace.yaml`. The rows had stated the population right and drawn
294+
// the wrong conclusion from it: an EDITED package-root config moves the gate
295+
// as surely as a new package does, so a card editing one learned of the gate
296+
// only from CI (PR #29345 paid one fix round).
298297
// ⚖️ `check:pm-half-states` LEFT this population under #16904's D2 and its row
299298
// is gone with it — a listed family that stops being a member reds here, and
300299
// a stale exclusion is an exclusion nobody measures. It is now placeable BY

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7988,6 +7988,70 @@ export function selfTest({ tier = 'full' } = {}) {
79887988
);
79897989
}
79907990

7991+
// ── A package-root config edit derives the console-intercept gate (#29353) ──
7992+
//
7993+
// The card's acceptance, held against the LIVE tree. The gate's scan opens a
7994+
// package-root `package.json` and vitest config under each workspace glob and
7995+
// nothing else, and it declares exactly that population in its own source, so
7996+
// BOTH invocations CI runs (scan and `--self-test`, one lint.yml step) are
7997+
// placed by every such file, while a test file under an existing package —
7998+
// tracked, or one a card adds — places neither. The rows it left in
7999+
// ROOT_WALK_RESIDUE_LEDGER are held gone by the stale limb above.
8000+
{
8001+
const gate = 'scripts/check-console-intercept-disarm.mjs';
8002+
const invocations = [gate, `${gate} --self-test`];
8003+
const entries = invocations.map((c) => liveDiscovery.byCheck.get(c));
8004+
t('both console-intercept invocations are discovered families', entries.every(Boolean));
8005+
const tracked = [...liveTree.files];
8006+
const trackedSet = new Set(tracked);
8007+
// Package roots read off the tree, not off a copy of the workspace globs:
8008+
// a tracked vitest config whose own directory carries a tracked manifest,
8009+
// under one of the three workspace top-level roots.
8010+
const rootConfigs = tracked.filter((f) => {
8011+
const m = /^((?:packages|apps|examples)\/(?:[^/]+\/)?[^/]+)\/vitest\.config\.[cm]?[jt]s$/.exec(f);
8012+
return m && trackedSet.has(`${m[1]}/package.json`);
8013+
});
8014+
t(
8015+
`control: the live tree carries package-root vitest configs to probe with (${rootConfigs.length})`,
8016+
rootConfigs.length >= 60,
8017+
);
8018+
const unplacedBy = (entry, probe) => placeFamily(entry, [probe]).verdict !== 'matched';
8019+
const missedConfigs = rootConfigs.filter((f) => entries.some((e) => e && unplacedBy(e, f)));
8020+
t(
8021+
'every package-root vitest config places both invocations — the card PR #29345 paid a CI round for' +
8022+
(missedConfigs.length ? ` — not placed by: ${missedConfigs.slice(0, 5).join(', ')}` : ''),
8023+
entries.every(Boolean) && missedConfigs.length === 0,
8024+
);
8025+
const manifests = rootConfigs.map((f) => f.replace(/\/[^/]+$/, '/package.json'));
8026+
const missedManifests = manifests.filter((f) => entries.some((e) => e && unplacedBy(e, f)));
8027+
t(
8028+
'and so does the package-root package.json beside each — the other file the scan opens' +
8029+
(missedManifests.length ? ` — not placed by: ${missedManifests.slice(0, 5).join(', ')}` : ''),
8030+
entries.every(Boolean) && missedManifests.length === 0,
8031+
);
8032+
// A card's file surface is often paths that do not exist yet: a NEW package.
8033+
const newPackage = ['packages', 'plugins', 'plugin-not-yet-written'].join('/');
8034+
t(
8035+
'a NEW package\'s root config and manifest place both invocations too',
8036+
entries.every((e) => e && !unplacedBy(e, `${newPackage}/vitest.config.ts`) && !unplacedBy(e, `${newPackage}/package.json`)),
8037+
);
8038+
const testFiles = tracked.filter((f) => /^(?:packages|apps|examples)\/.+\.(?:test|spec)\.[cm]?[jt]sx?$/.test(f));
8039+
t(
8040+
`control: the live tree carries test files under packages to probe with (${testFiles.length})`,
8041+
testFiles.length >= 1000,
8042+
);
8043+
t(
8044+
'no test file under an existing package places either invocation, tracked or brand new',
8045+
entries.every((e) => e
8046+
&& placeFamily(e, testFiles).verdict !== 'matched'
8047+
&& unplacedBy(e, ['packages', 'services', 'service-sms', 'src', 'not-yet-written.test.ts'].join('/'))),
8048+
);
8049+
t(
8050+
'nor does a vitest config BELOW a package root — the scan deliberately reads the root one only',
8051+
entries.every((e) => e && unplacedBy(e, ['packages', 'services', 'service-sms', 'test', 'fixtures', 'vitest.config.ts'].join('/'))),
8052+
);
8053+
}
8054+
79918055
// ── The CI-MEASURED-ONLY shape (#14004) ───────────────────────────────────
79928056
//
79938057
// The two markers above are DECLARATIONS a gate carries. This one is the

0 commit comments

Comments
 (0)