Context
Successor to #16443, carrying the residual @neo-gpt-emmy recorded when she approved PR #16444 as Approve+Follow-Up under the temporary incident exception (review).
Filed at @tobiu's direction while the GPT family is near its rate limit: an approval given under a firefighting threshold carries conditions, and those conditions should not depend on anyone remembering them after the PR closes. Low priority — the gate delivered by #16443 is green and useful as-is.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-04T07:29Z; no equivalent found. A2A in-flight claim sweep (all read-states): no competing [lane-claim].
The Problem
lint-retry-bounds.mjs discards every continuation line of a multi-line template literal:
if (wasOpen) return;
That guard predates stripLiterals learning to preserve ${…} substitutions. Once it did, the guard began throwing away exactly the code that repair existed to keep. A real growth expression written across a template boundary — `…${base * 2 ** n}…` — is matched, preserved, and dropped one line later. Discovered and discarded in the same pass, which is worse than never scanning it.
And removing the guard does not fix it. That was measured on PR #16444 rather than assumed. Lifting it immediately surfaces ai/demo-agents/dev.mjs:258 as a candidate whose match is **AI Generated PR** — markdown bold inside literal text, precisely what stripLiterals exists to blank, and a case this suite already pins with a dedicated test.
Traced from the file start:
NEO_CODE_BLOCK_1
The template opened at dev.mjs:118 is never seen to close, so by 258 the scanner still believes it is inside one. stripLiterals(line, true) enters with quote = '\'`, meets 258's opening backtick, reads it as a close, and emits the remainder of the line as code.
So if (wasOpen) return masks two defects: the escape above, and a stripLiterals multi-line state-tracking bug. Lifting it trades a known false negative for a false positive — the worse of the two, because a gate that cries wolf is one contributors learn to route around, and that reasoning is the module's stated premise for why it reports unclassified and never unbounded.
The guard was therefore retained on #16444 with the measurement recorded at the site. The escape is explicitly not closed.
The Architectural Reality
ai/scripts/lint/lint-retry-bounds.mjs — stripLiterals (the quote/substDepth state machine), discoverCandidates (the wasOpen guard and the inTemplate carry between lines), findGrowthMatches.
ai/demo-agents/dev.mjs:118 and :258 — the live reproduction; 118 opens a template the scanner never sees close, 258 is where the consequence surfaces.
test/playwright/unit/ai/scripts/lint/lintRetryBounds.spec.mjs — already pins "literal contents cannot match — markdown bold is not an exponent", which is the test the false positive would break.
The Fix
Track multi-line template state correctly across lines, so a continuation line can be scanned for preserved substitution code without the literal text leaking through as code. Then remove the wasOpen guard, because it exists only to compensate for the mis-tracking.
Then complete the witness-contract truth-fold deferred from #16444: choose explicitly between strong resolvable references and source-keyed inline/rationale semantics, and state the chosen contract in one place (registry $schema + the validator's JSDoc) so a witness author is not guessing.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Error Semantics |
Docs |
Evidence |
stripLiterals template state |
this ticket |
Carries open/closed template state correctly across lines |
Unresolvable state ⇒ treat the line as literal (skip), never as code |
module JSDoc |
dev.mjs:258 is not a candidate |
| continuation-line scanning |
this ticket |
Scans preserved ${…} code on continuation lines |
— |
module JSDoc |
a growth expression across a template boundary IS a candidate |
| witness contract |
registry $schema + validateEntry |
One stated contract for what a witness may be |
An entry that does not satisfy it fails, with the rule named |
registry $schema |
a prose witness and a path witness both resolve deterministically |
Decision Record impact
none — a scanner correctness fix inside an existing lint.
Acceptance Criteria
Out of Scope
- Widening or narrowing the discovery
PATTERNS. The population under scan does not change here.
- Re-litigating the admission boundary settled on #16444 (vocabulary annotates, it does not admit).
Avoided Traps
- Removing
wasOpen alone. Measured on #16444: it converts a false negative into a false positive. The state fix must land first or with it.
- Pinning the fix with only the FN fixture. That is what makes the naive removal look correct — the FP fixture is what catches it, which is why both are required rather than recommended.
Related
- Delivered gate: #16443 via PR #16444 (approved at
50d455c89c)
- The retained guard and its measurement: the comment block at the
wasOpen site
Retrieval Hint: stripLiterals multi-line template state tracking wasOpen continuation line false positive markdown bold
Origin Session ID: 8132018f-e94a-4ef5-bce4-ec788ef56b7e
Context
Successor to #16443, carrying the residual @neo-gpt-emmy recorded when she approved PR #16444 as Approve+Follow-Up under the temporary incident exception (review).
Filed at @tobiu's direction while the GPT family is near its rate limit: an approval given under a firefighting threshold carries conditions, and those conditions should not depend on anyone remembering them after the PR closes. Low priority — the gate delivered by #16443 is green and useful as-is.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-04T07:29Z; no equivalent found. A2A in-flight claim sweep (all read-states): no competing
[lane-claim].The Problem
lint-retry-bounds.mjsdiscards every continuation line of a multi-line template literal:if (wasOpen) return;That guard predates
stripLiteralslearning to preserve${…}substitutions. Once it did, the guard began throwing away exactly the code that repair existed to keep. A real growth expression written across a template boundary —`…${base * 2 ** n}…`— is matched, preserved, and dropped one line later. Discovered and discarded in the same pass, which is worse than never scanning it.And removing the guard does not fix it. That was measured on PR #16444 rather than assumed. Lifting it immediately surfaces
ai/demo-agents/dev.mjs:258as a candidate whose match is**AI Generated PR**— markdown bold inside literal text, precisely whatstripLiteralsexists to blank, and a case this suite already pins with a dedicated test.Traced from the file start:
NEO_CODE_BLOCK_1
The template opened at
dev.mjs:118is never seen to close, so by 258 the scanner still believes it is inside one.stripLiterals(line, true)enters withquote = '\'`, meets 258's opening backtick, reads it as a close, and emits the remainder of the line as code.So
if (wasOpen) returnmasks two defects: the escape above, and astripLiteralsmulti-line state-tracking bug. Lifting it trades a known false negative for a false positive — the worse of the two, because a gate that cries wolf is one contributors learn to route around, and that reasoning is the module's stated premise for why it reportsunclassifiedand neverunbounded.The guard was therefore retained on #16444 with the measurement recorded at the site. The escape is explicitly not closed.
The Architectural Reality
ai/scripts/lint/lint-retry-bounds.mjs—stripLiterals(thequote/substDepthstate machine),discoverCandidates(thewasOpenguard and theinTemplatecarry between lines),findGrowthMatches.ai/demo-agents/dev.mjs:118and:258— the live reproduction; 118 opens a template the scanner never sees close, 258 is where the consequence surfaces.test/playwright/unit/ai/scripts/lint/lintRetryBounds.spec.mjs— already pins "literal contents cannot match — markdown bold is not an exponent", which is the test the false positive would break.The Fix
Track multi-line template state correctly across lines, so a continuation line can be scanned for preserved substitution code without the literal text leaking through as code. Then remove the
wasOpenguard, because it exists only to compensate for the mis-tracking.Then complete the witness-contract truth-fold deferred from #16444: choose explicitly between strong resolvable references and source-keyed inline/rationale semantics, and state the chosen contract in one place (registry
$schema+ the validator's JSDoc) so a witness author is not guessing.Contract Ledger Matrix
stripLiteralstemplate state${…}code on continuation lines$schema+validateEntry$schemaDecision Record impact
none— a scanner correctness fix inside an existing lint.Acceptance Criteria
${…}substitution on a continuation line of a multi-line template IS discovered. A false-positive fixture: markdown bold inside multi-line template literal text is NOT discovered.discoverCandidates, not unit calls tostripLiterals— the escape lived in the seam between them.ai/demo-agents/dev.mjsproduces no candidate at line 258, and the live tree stays fully classified (exit 0).wasOpenguard is removed, not merely narrowed — retaining it means the state tracking is still not trusted.validateEntry's behaviour matches it.Out of Scope
PATTERNS. The population under scan does not change here.Avoided Traps
wasOpenalone. Measured on #16444: it converts a false negative into a false positive. The state fix must land first or with it.Related
50d455c89c)wasOpensiteRetrieval Hint:
stripLiterals multi-line template state tracking wasOpen continuation line false positive markdown boldOrigin Session ID: 8132018f-e94a-4ef5-bce4-ec788ef56b7e