Skip to content

[finding] NON_DESCENT_CALLEES is tested against the LAST dotted segment, so every object-named entry is dead — JSON.stringify(<path>) reads as a directory descent and its argument becomes a candidate walk root #18643

Description

@os-bill

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions