Uh oh!
There was an error while loading. Please reload this page.
fix(scripts): scan a template's ${...} as code so a nested backtick cannot flip js-comment-mask's parity - #10632
Conversation
…cannot flip the mask's parity scanSource walked a template literal's interpolation as plain literal text and stopped the span at the next backtick. An interpolation is code: it can hold a nested template (how this tree formats a list of names), a backtick inside a regex or a string, or a brace inside a string. The nested opener was read as the outer template's closer, and the phantom span ran to the next backtick anywhere in the file. Measured against @typescript-eslint/parser's comment ranges over 4,733 files: 16 files disagreed before (15 in the FABRICATES direction the module's own header calls the worse one, up to 10,252 comment bytes read as live code in one file), 0 after. The interpolation is now scanned by the same loop, with the same string, regex and comment branches, and its bytes are still reported as the enclosing template's literal content -- the documented flag is unchanged, so both consumers of `literal` (check-parse-guard, check-entry-guard) see what they saw. Seven shapes pinned in --self-test, each asserting both sides (a comment that must go, live code that must stay); all 22 cases verified byte-for-byte against the parser. The header's "cannot fabricate a lead" guarantee was measured false and is replaced by what can be re-derived. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
…utation can kill
The `${x ? '}' : '{'}` spelling passed with the brace counting deleted AND with
the pre-fix scanner -- it asserted "no error" and pinned nothing. Replaced with
`${fmt({ a: 1 }, '`')}`, which carries a nested brace and a quoted backtick in
one interpolation: deleting the `{` counting ends the interpolation at the
object literal's `}`, the quoted backtick is then read as the template's
closer, and the docblock below survives the mask.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt…nested template
Deleting the `{` counting inside `${...}` passed all 22 cases and the whole
4,733-file sweep -- the tree does not currently write that shape, so nothing
held it. `${fmt({ a: 1 }, `\``)}` does: without the counting the interpolation
ends at the object literal's `}`, the nested template's delimiters are then
read in body position, and the docblock below survives the mask.
Also records what the mutation runs showed about depth: matched backticks pair
off whatever a scan believes about nesting, so nested-in-nested ALONE is green
under every mutation. Only nesting that meets an escape or a quoted backtick
discriminates.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt…ansource-nested-template
…at it missed
The header pointed at "the sweep" without saying how to run one. It now names
the corpus, the parser and the comparison, so the 16-file census is
re-derivable from the file itself rather than from a PR description.
Also records the result that ranks the two instruments: deleting the `{`
counting inside `${...}` passed every pinned case AND the whole 4,739-file
sweep, because the tree does not happen to write that shape. The case that
holds it now was written from the mutation, not from the corpus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wtos-zhuang
commented
Aug 21, 2026
PM review — ACCEPT, arming when its last checks clear. ⭐ The surviving mutant and the "live but unexploited" verdict are the two best things in tonight's batch.Head The census
4,739 files, 0 unparseable, oracle-diffed byte-for-byte. The card's 16 / 15 / 2 split reproduces exactly while per-file byte counts moved and the corpus grew 4,679 → 4,739 — which is the right shape for a re-derivation: the structure holds, the numbers are today's. ⭐ "The defect was live but unexploited" — and you proved the negative rather than asserting itNo consumer gate verdict moved. That could mean the fix is inert, or that the harness cannot see the mask at all. My brief demanded a positive control and you built the decisive one:
⇒ the harness is mask-sensitive, so "nothing moved" is a real negative: the fabricated spans happened not to contain any pattern a gate looks for. ⭐ That is the difference between "we got lucky" and "we don't know," and it is stated as the former with evidence. You also listed the 8 invocations that don't move even under M7 — genuinely insensitive to the mask — so the coverage claim is bounded rather than implied. And the note that the adoption check reaches the mask only through ⭐ M6: a surviving mutant, reported rather than hidden
Keeping the case for depth coverage and writing into the file that no mutation kills it is the honest resolution. A test suite that quietly contains an unkillable case is a suite whose strength nobody can assess; one that says so is documentation. Same discipline on M3: it passed all 22 cases and the entire 4,739-file sweep before that case existed — "the tree does not write the shape. The case was written from the mutation, not from the corpus." Saying where a test came from is rare and valuable. ⭐ M2 pre-empts the reviewer's obvious objection with a measurement
That is the question I would have asked — "why scan the interpolation at all rather than just count braces?" — answered before I asked it, with the file that breaks it named. And the census caught it before the self-test did, which is itself an argument for having both.
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#10427
scanSourcewalked a template literal's${...}as plain literal content and stopped the span at the next backtick. An interpolation is code: it can hold a nested template (how this tree formats a list of names), a backtick inside a regex or a string, or a brace inside a string. The nested opener was read as the outer template's closer, and every backtick after it flipped parity — the last one opening a phantom span that ran to the next backtick anywhere in the file.Re-derived census — the oracle is
@typescript-eslint/parser's comment rangesMeasured at
3f111c885eover 4,739 files (node_modules/dist/.next/build/.turbo/coverageexcluded), diffing this scan's comment mask against the parser's comment ranges byte-for-byte:Worst offenders before:
check-durability-degradation-log-level.mjs10,252 ·spec/src/ui/view.zod.ts9,405 ·pm/check-half-states.mjs8,973 ·runtime/src/domains/automation.ts7,226 ·check-runtime-services-index.mjs4,728. The BLINDS side ispm/check-governed-prose.mjs, where the flip lands on a/**and opens a phantom block comment over lines 254–339 — ~85 lines of live code blanked for every gate that reads it.Reproduced on the real trigger first (re-located, still
scripts/pm/check-governed-prose.mjs:219): before, that file had 75 disagreeing lines; after, 0. The issue's reduced case went fromscanSource=0comment bytes to27, which is what the parser reports.The fix
The interpolation is scanned by the same loop, with the same string, regex and comment branches, and a stack of open templates whose
bracescount says whether the scanner is in a template's body or inside${...}. The documented flag is unchanged — an interpolation's bytes are still reported as the enclosing template's literal content, now flushed in one pass at the end, because the span is only known once its closing brace is found. Both consumers ofliteral(check-parse-guard,check-entry-guard, each computingcomment || literal) see exactly what they saw.What it changes for consumers: nothing today, with a positive control
Every gate that imports this module was run against the fixed scanner and against the pre-fix one — 21 invocations, including
check:entry-guard,check:parse-guard, and the three gates that actually invokeeslint-fatal-guard's adoption check (check:query-options-erasure,check:slot-lookup,eslint-stack-headroom). No verdict moved; every gate's output was byte-identical.That is a negative result, so it gets a control: replacing the scanner's return with an empty comment array moves 13 of the 21 (12 flip green→red). The harness is genuinely mask-sensitive, so "nothing moved" means the fabricated spans happened not to contain a trigger — the defect was live, unexploited. Eight invocations do not move even under the control (
eslint-fatal-guard --self-test,cross-package-test-inputs,examples-live-imports,docs-audit-scope,ts-parse --self-test,check:query-options-erasureproper,check:slot-lookup,eslint-stack-headroom), i.e. their corpora are insensitive to the mask today; the adoption check reaches the mask throughcheck:query-options-erasure --self-test, which does move.Self-test: 8 new pinned shapes, each asserting both sides
#10608 records that this module's
--self-testruns in no workflow, so CI cannot catch a regression here and the cases carry the whole weight. Each pins a genuine comment that must go (GHOST) and live code that must stay (REAL), and all 23 cases were verified byte-for-byte against@typescript-eslint/parser— the expected answer is the language's, not this implementation's.Every new case was ablated against seven mutations of the fix, each confirmed on disk by anchor count before and after:
{counting deletedTwo of those rows are results, not bookkeeping:
quoteIdentinpackages/cliwrites`\`${name.replace(/`/g, '``')}\``, where the backtick lives inside a regex and inside a string. The census caught it; the self-test now does too.{counting passed all 22 cases and the full 4,739-file sweep — the tree does not currently write that shape, so nothing held it. Added${fmt({ a: 1 },`)}, which kills it.Docblock
Two claims in the header were measured false and are corrected rather than deleted: the
${...}-is-literal reasoning (true of the flag, false of the scan) and the "cannot fabricate a lead" guarantee — 15 of the 16 disagreeing files sat in exactly that direction. A failure direction is a property of an implementation, not of an intention, and it took an independent parser over the whole tree to find out which way this module actually failed.Verification
node scripts/js-comment-mask.mjs --self-test— 23 cases pass3f111c885e— 16 files → 0pnpm check:cross-package-test-inputs(the familydispatch-gates.mjsderives for this diff),pnpm check:nul-bytes,pnpm check:entry-guard— green underos-verify-lock.sh(VERDICT command-exit 0)check-declaration-mirrors— green; the hand-writtenscripts/js-comment-mask.d.mtsneeded no change (no exported signature moved)eslint scripts/js-comment-mask.mjs --no-inline-config— exit 0skip-changeset:scripts/**-only, publishes nothing (AGENTS.md:943, precedent PR #10502).Generated by Claude Code
Generated by Claude Code