Found while widening scripts/check-dispatcher-error-vocabulary.mjs for #13790 (PR #14625). Out of scope there: it is a defect in the gate's shared textual scanners, not in any one shape, and it costs the objlithelper shape that shipped before it exactly the same way.
The defect
enclosingOpeners, sliceBalanced and scanTopLevel all skip a string the same way — on seeing a quote character, advance to the next occurrence of that same character:
else if (c === "'" || c === '"' || c === '`') {
const quote = c;
i += 1;
while (i < src.length && src[i] !== quote) i += src[i] === '\\' ? 2 : 1;
}
A nested template literal breaks that. In
`Unknown key${n > 1 ? 's' : ''} ${keys.map((k) => `\`${k}\``).join(', ')} — the toggle body is { enabled?: boolean }`
the scanner enters at the outer backtick and leaves at the first inner backtick, so from there on it is parsing string bytes as code and code bytes as string. The bracket stack desynchronises and stays desynchronised for the rest of the file.
What it costs, measured
packages/runtime/src/domains/automation.ts carries such a template at line 1285. Downstream of it, enclosingOpeners returns no enclosing bracket for code: positions it should answer for, so any shape guarded by that primitive silently declines them:
|
|
code: positions in that file after line 1285 that enclosingOpeners cannot place |
2 |
| of those, real stamps of the identical validation-catalog ternary |
2 (:1384, :1399) |
Both are code: <x> === undefined ? 'required' : 'invalid_type' in the flow-clone door — the same shape, same values and same ADR-0112 D6 verdict as the instance at :1125 that the gate does see. So today the under-count changes no verdict and hides no unregistered wire code. What it changes is that a count the gate prints as a total is really a floor, and nothing said so until now.
Repo-wide, over packages/** non-test source (2186 files), enclosingOpeners returns no bracket for 85 of the 902 code: tokens in the tree — 33 of the 346 that #13790's shape anchor reaches. Not all of those are desync victims: some code: bytes really are inside a string, and that is the correct answer. The point is that the primitive cannot tell a caller which of the two it is answering — "genuinely in a string" and "the scanner lost its place 400 lines ago" come back identically, as undefined.
Why it is not a one-shape problem
What a fix would owe
The scanners would need a template-literal mode that tracks ${ … } nesting rather than seeking a matching backtick — a real change to three shared primitives that every shape and every census figure in the gate depends on. Which means it owes a re-derivation of the pinned counts in OBJECT_LITERAL_CODE_HELPER_BLINDNESS and INLINE_LITERAL_EXPRESSION_CENSUS, and a --self-test case per primitive with a nested-template fixture and a positive control. That is a card, not a rider on a shape widening.
Unassigned, for triage. Filed under Prime Directive #10 rather than folded into PR #14625.
Generated by Claude Code
Found while widening
scripts/check-dispatcher-error-vocabulary.mjsfor #13790 (PR #14625). Out of scope there: it is a defect in the gate's shared textual scanners, not in any one shape, and it costs theobjlithelpershape that shipped before it exactly the same way.The defect
enclosingOpeners,sliceBalancedandscanTopLevelall skip a string the same way — on seeing a quote character, advance to the next occurrence of that same character:A nested template literal breaks that. In
`Unknown key${n > 1 ? 's' : ''} ${keys.map((k) => `\`${k}\``).join(', ')} — the toggle body is { enabled?: boolean }`the scanner enters at the outer backtick and leaves at the first inner backtick, so from there on it is parsing string bytes as code and code bytes as string. The bracket stack desynchronises and stays desynchronised for the rest of the file.
What it costs, measured
packages/runtime/src/domains/automation.tscarries such a template at line 1285. Downstream of it,enclosingOpenersreturns no enclosing bracket forcode:positions it should answer for, so any shape guarded by that primitive silently declines them:code:positions in that file after line 1285 thatenclosingOpenerscannot place:1384,:1399)Both are
code: <x> === undefined ? 'required' : 'invalid_type'in the flow-clone door — the same shape, same values and same ADR-0112 D6 verdict as the instance at:1125that the gate does see. So today the under-count changes no verdict and hides no unregistered wire code. What it changes is that a count the gate prints as a total is really a floor, and nothing said so until now.Repo-wide, over
packages/**non-test source (2186 files),enclosingOpenersreturns no bracket for 85 of the 902code:tokens in the tree — 33 of the 346 that #13790's shape anchor reaches. Not all of those are desync victims: somecode:bytes really are inside a string, and that is the correct answer. The point is that the primitive cannot tell a caller which of the two it is answering — "genuinely in a string" and "the scanner lost its place 400 lines ago" come back identically, asundefined.Why it is not a one-shape problem
objlithelper(Widencodehelperto the object-literal stamp position — the blast radius is now measured, and 4 undischargeableunresolvedfindings are the blocker #13233) is guarded byenclosingOpeners. It has carried this since it landed.objlitexpr([finding] An INLINE literal ternary at an object-literalcode:is reached by no vocabulary shape — the third stamp position, censused at zero #13790) inherits it, and its census records the reading as a declared lower bound (positionsBehindScannerDesync) rather than silently rounding it away.check:error-code-casingis unaffected: its patterns are plain regexes with no bracket tracking.What a fix would owe
The scanners would need a template-literal mode that tracks
${…}nesting rather than seeking a matching backtick — a real change to three shared primitives that every shape and every census figure in the gate depends on. Which means it owes a re-derivation of the pinned counts inOBJECT_LITERAL_CODE_HELPER_BLINDNESSandINLINE_LITERAL_EXPRESSION_CENSUS, and a--self-testcase per primitive with a nested-template fixture and a positive control. That is a card, not a rider on a shape widening.Unassigned, for triage. Filed under Prime Directive #10 rather than folded into PR #14625.
Generated by Claude Code