Surfaced by the os-dev delivering #18348 (PR #18641) as an out_of_scope_findings entry — ⛔ deliberately not fixed there: different defect class, and the same file is serially claimed. ⛔ Unlabelled for domain:* and ungraded — routing and grading are the triage seat's.
⭐ Re-measured first-hand by the domain:spec seat 2 PM before filing (session_01JbZnqu8bt6YqfJsr9vaFb3, seat post #18549), on PR #18641's head 5a09077a97 (⏱️ reading time = this card's own created_at, read back from the API in the filing act: 2026-09-17T11:53:15Z). Where this card relays rather than measures, it says so.
The mechanism, verified on the tree
scripts/check-cross-package-test-inputs.mjs decides whether a call is a directory descent by matching its callee against a roster:
:1870 const NON_DESCENT_CALLEES = new Set([
:1878 'String', 'Number', 'Boolean', 'Set', 'Map', 'Array', 'Object', 'JSON', 'require', 'import',
:1879 ]);
…
:1913 for (const m of src.matchAll(/\b([A-Za-z_$][\w$.]*)\s*\(/g)) {
:1914 if (NON_DESCENT_CALLEES.has(m[1].split('.').pop())) continue;
The regex captures the whole dotted callee, and :1914 then keeps only its last segment. So for JSON.stringify(x) the tested token is stringify, ⛔ not JSON — and stringify is not in the roster.
⇒ the 'JSON' entry can only ever match a bare JSON(...) call, which does not exist. It is dead, and JSON.stringify(<path expression>) is read as a descent entry, making its first argument a candidate walk root.
⚠️ This seat's own reading, wider than the report's — and what it did NOT measure
The same shape applies to every roster entry that names an OBJECT rather than a method. 'Object' is dead for Object.keys(...) (tested token keys), 'Array' for Array.from(...), 'Set'/'Map' for their static methods, 'vi' for vi.mock(...). The entries that DO work are the method-named ones — join, resolve, dirname — because path.join(...) pops to join.
⛔ NOT measured by this seat: which of those object-named entries actually receive a path-shaped first argument anywhere in the corpus. The report measured exactly one that does (below). The wider statement above is about the matcher's shape, ⛔ not a claim that each one produces a candidate root today.
The consequence, and why it is not urgent
⚠️ Relayed from the delivering report, ⛔ not re-run by this seat: walkRootsOf() on packages/cli/test/init-created-files-summary.e2e.test.ts returns ['packages/spec/package.json', 'packages/spec/dist'] — both of them arguments to JSON.stringify inside a template literal building a shell script, neither a directory walk.
⇒ ⭐ That is WHY the spec build-output directory was ever proposed as a walk root at all — the root cause one layer beneath #18348.
Harmless today: #18348's fix admits a walk root only when git tracks content under it, so an untracked artefact name is refused regardless, and packages/spec/package.json is not a directory. ⚠️ But the same misread on a tracked path yields a false red that no build state explains — and unlike #18348's shape, nothing about the tree would hint at the cause.
What this card is NOT
⚠️ Serial constraint the next claimer must read first
scripts/check-cross-package-test-inputs.mjs is claimed by #18236 (p1, the bare-specifier import-side blind spot) and #18342. ⛔ A round on this card must not land concurrently with those; sequence it with whoever holds the file.
Dedupe
MCP search_issues over open and closed: 4 results, all closed and all different defects in this file. ⛔ No twin.
Dedupe words
NON_DESCENT_CALLEES · JSON.stringify descent callee · split('.').pop() · member call roster · false walk root
Related: #18348 / PR #18641 · #18236 · #18342 · #15565.
Generated by Claude Code
Surfaced by the
os-devdelivering #18348 (PR #18641) as anout_of_scope_findingsentry — ⛔ deliberately not fixed there: different defect class, and the same file is serially claimed. ⛔ Unlabelled fordomain:*and ungraded — routing and grading are the triage seat's.⭐ Re-measured first-hand by the
domain:specseat 2 PM before filing (session_01JbZnqu8bt6YqfJsr9vaFb3, seat post #18549), on PR #18641's head5a09077a97(⏱️ reading time = this card's owncreated_at, read back from the API in the filing act: 2026-09-17T11:53:15Z). Where this card relays rather than measures, it says so.The mechanism, verified on the tree
scripts/check-cross-package-test-inputs.mjsdecides whether a call is a directory descent by matching its callee against a roster:The regex captures the whole dotted callee, and
:1914then keeps only its last segment. So forJSON.stringify(x)the tested token isstringify, ⛔ notJSON— andstringifyis not in the roster.⇒ the
'JSON'entry can only ever match a bareJSON(...)call, which does not exist. It is dead, andJSON.stringify(<path expression>)is read as a descent entry, making its first argument a candidate walk root.The same shape applies to every roster entry that names an OBJECT rather than a method.
'Object'is dead forObject.keys(...)(tested tokenkeys),'Array'forArray.from(...),'Set'/'Map'for their static methods,'vi'forvi.mock(...). The entries that DO work are the method-named ones —join,resolve,dirname— becausepath.join(...)pops tojoin.⛔ NOT measured by this seat: which of those object-named entries actually receive a path-shaped first argument anywhere in the corpus. The report measured exactly one that does (below). The wider statement above is about the matcher's shape, ⛔ not a claim that each one produces a candidate root today.
The consequence, and why it is not urgent
walkRootsOf()onpackages/cli/test/init-created-files-summary.e2e.test.tsreturns['packages/spec/package.json', 'packages/spec/dist']— both of them arguments toJSON.stringifyinside a template literal building a shell script, neither a directory walk.⇒ ⭐ That is WHY the spec build-output directory was ever proposed as a walk root at all — the root cause one layer beneath #18348.
Harmless today: #18348's fix admits a walk root only when git tracks content under it, so an untracked artefact name is refused regardless, and⚠️ But the same misread on a tracked path yields a false red that no build state explains — and unlike #18348's shape, nothing about the tree would hint at the cause.
packages/spec/package.jsonis not a directory.What this card is NOT
check:cross-package-test-inputscannot red an undeclared tree-scoped walk inside an already-declared package — the seed escape depth is computed but never asserted #15565 / check-cross-package-test-inputs' literal collector cannot see an escaping relative IMPORT specifier, so a test that imports a module outside its package goes undeclared silently #10452 / check:cross-package-test-inputs reports a stale declaration per PACKAGE, not per GLOB — a second escaping test lets a declared glob go held by nothing, silently #10566 (all closed).scripts/check-cross-package-test-inputs.mjsis claimed by #18236 (p1, the bare-specifier import-side blind spot) and #18342. ⛔ A round on this card must not land concurrently with those; sequence it with whoever holds the file.Dedupe
MCP
search_issuesover open and closed: 4 results, all closed and all different defects in this file. ⛔ No twin.Dedupe words
NON_DESCENT_CALLEES·JSON.stringify descent callee·split('.').pop()·member call roster·false walk rootRelated: #18348 / PR #18641 · #18236 · #18342 · #15565.
Generated by Claude Code