Detail Bug Report
https://app.detail.dev/org_befd6425-a158-4e24-9d4d-1e5c08769515/bugs/bug_82ed997f-5a37-479d-9d2e-f5e12428c09c
Introduced in 724c810 by @WilliamAGH on Sep 1, 2026
Summary
- Context:
nestNumericListFences (in frontend/src/lib/services/numericListFenceNesting.ts) re-indents streamed/plain fenced code blocks so that CommonMark attaches them to a preceding numeric list item; parseMarkdown runs this on every message before lexing. All HTML evidence below is from marked@15.0.12 (the lock resolution of marked@^15.0.0), gfm: true, breaks: true.
- Bug: In commit
724c810e the maximum indentation used to detect a closing fence during the open-fence pass was changed to awaitedContinuationIndentation + COMMONMARK_MAX_FENCE_INDENTATION and no longer subtracts the bodyIndentation that was applied to body lines, so a bare ``` (or ~~~) line used as code content inside a reparented fence is misclassified as the closing fence whenever its source indentation falls in the window (openingSourceIndentation + 3, awaitedContinuationIndentation + 3].
- Actual vs. expected: The fence should remain open and the bare-fence line kept as literal content (the pre-
724c810e behavior and the plain-marked behavior); instead the block is closed prematurely, the inner ``` and everything after it up to the real close are dropped/repurposed, and trailing content leaks out of the list item.
- Impact: Code-fence content shown inside numeric list items in chat is silently dropped / split out of the list item. For deep numeric markers (e.g.
14., 123., …) the misclassification window widens, so even a moderately indented demonstration of nested fences loses content and produces stray empty <pre><code> blocks.
Code with Bug
frontend/src/lib/services/numericListFenceNesting.ts:
bodyIndentation = " ".repeat(
Math.max(0, awaitedContinuationIndentation - fenceCandidate.markerIndex),
);
targetIndentation = awaitedContinuationIndentation;
maximumSourceIndentation =
awaitedContinuationIndentation + COMMONMARK_MAX_FENCE_INDENTATION; // <-- BUG 🔴 ignores bodyIndentation shift applied to body lines
fenceState.open(fenceCandidate.marker);
Explanation
nestNumericListFences shifts code-fence body lines by bodyIndentation to reparent a fence under a numeric list item while preserving indentation relative to the opening fence.
Closing-fence detection, however, still needs to use the source-frame CommonMark rule: a closing fence may be indented at most 3 spaces past the opening fence’s source column. Before 724c810e, maximumSourceIndentation effectively enforced this as openingSourceIndentation + 3 by subtracting bodyIndentation.length. After 724c810e, the cutoff became awaitedContinuationIndentation + 3 (larger whenever the opening fence is re-indented), so a bare fence line that is actually intended as code content (e.g. an indented ``` inside the block) falls into the widened window and is incorrectly treated as the closer.
Empirical confirmation (authority is plain marked@15.0.12 on the unmodified source):
Input:
1. Example of nested:
```text
```
more
```
Current behavior after preprocessing: the ``` line is consumed as the close, producing empty/stray blocks and leaking more out of the list item:
<ol>
<li>Example of nested:<pre><code class="language-text">
</code></pre>
</li>
</ol>
<p>more</p>
<pre><code>
</code></pre>
Plain marked@15.0.12 without preprocessing keeps ```\nmore as code-block content (closing fences can’t be indented >3 past a column-0 opener), demonstrating the preprocessing step is reinterpreting content boundaries rather than only reparenting structure.
Codebase Inconsistency
The in-tree test added with the change (numericListFenceNesting.test.ts:103-124) asserts that a bare fence at source columns 4–6 closes a block whose opening fence is at column 0. Plain marked@15.0.12 on the same source treats columns 4–6 as content, so the test codifies a non-CommonMark behavior and locks in the misclassification.
Recommended Fix
Restore the body-indentation correction so closing-fence detection remains relative to the opening fence’s source indentation:
maximumSourceIndentation =
awaitedContinuationIndentation +
COMMONMARK_MAX_FENCE_INDENTATION -
bodyIndentation.length;
Update the test added in 724c810e to expect the bare fence at offsets 1–4 (columns 4–7) to be preserved as content (plain marked@15.0.12 keeps all of them as content).
History
This bug was introduced in commit 724c810. The change removed - bodyIndentation.length from the maximumSourceIndentation calculation in the open-fence pass, widening the closing-fence cutoff and causing bare fence markers used as content to be misclassified as closers; the commit also added a test that codifies this widened cutoff. The prior behavior (from e57f73b1) computed maximumSourceIndentation = awaitedContinuationIndentation + COMMONMARK_MAX_FENCE_INDENTATION - bodyIndentation.length, keeping the cutoff relative to the opening fence.
Detail Bug Report
https://app.detail.dev/org_befd6425-a158-4e24-9d4d-1e5c08769515/bugs/bug_82ed997f-5a37-479d-9d2e-f5e12428c09c
Introduced in 724c810 by @WilliamAGH on Sep 1, 2026
Summary
nestNumericListFences(infrontend/src/lib/services/numericListFenceNesting.ts) re-indents streamed/plain fenced code blocks so that CommonMark attaches them to a preceding numeric list item;parseMarkdownruns this on every message before lexing. All HTML evidence below is frommarked@15.0.12(the lock resolution ofmarked@^15.0.0),gfm: true, breaks: true.724c810ethe maximum indentation used to detect a closing fence during the open-fence pass was changed toawaitedContinuationIndentation + COMMONMARK_MAX_FENCE_INDENTATIONand no longer subtracts thebodyIndentationthat was applied to body lines, so a bare```(or~~~) line used as code content inside a reparented fence is misclassified as the closing fence whenever its source indentation falls in the window(openingSourceIndentation + 3, awaitedContinuationIndentation + 3].724c810ebehavior and the plain-markedbehavior); instead the block is closed prematurely, the inner```and everything after it up to the real close are dropped/repurposed, and trailing content leaks out of the list item.14.,123., …) the misclassification window widens, so even a moderately indented demonstration of nested fences loses content and produces stray empty<pre><code>blocks.Code with Bug
frontend/src/lib/services/numericListFenceNesting.ts:Explanation
nestNumericListFencesshifts code-fence body lines bybodyIndentationto reparent a fence under a numeric list item while preserving indentation relative to the opening fence.Closing-fence detection, however, still needs to use the source-frame CommonMark rule: a closing fence may be indented at most 3 spaces past the opening fence’s source column. Before
724c810e,maximumSourceIndentationeffectively enforced this asopeningSourceIndentation + 3by subtractingbodyIndentation.length. After724c810e, the cutoff becameawaitedContinuationIndentation + 3(larger whenever the opening fence is re-indented), so a bare fence line that is actually intended as code content (e.g. an indented ``` inside the block) falls into the widened window and is incorrectly treated as the closer.Empirical confirmation (authority is plain
marked@15.0.12on the unmodified source):Input:
Current behavior after preprocessing: the
```line is consumed as the close, producing empty/stray blocks and leakingmoreout of the list item:Plain
marked@15.0.12without preprocessing keeps```\nmoreas code-block content (closing fences can’t be indented >3 past a column-0 opener), demonstrating the preprocessing step is reinterpreting content boundaries rather than only reparenting structure.Codebase Inconsistency
The in-tree test added with the change (
numericListFenceNesting.test.ts:103-124) asserts that a bare fence at source columns 4–6 closes a block whose opening fence is at column 0. Plainmarked@15.0.12on the same source treats columns 4–6 as content, so the test codifies a non-CommonMark behavior and locks in the misclassification.Recommended Fix
Restore the body-indentation correction so closing-fence detection remains relative to the opening fence’s source indentation:
Update the test added in
724c810eto expect the bare fence at offsets 1–4 (columns 4–7) to be preserved as content (plainmarked@15.0.12keeps all of them as content).History
This bug was introduced in commit 724c810. The change removed
- bodyIndentation.lengthfrom themaximumSourceIndentationcalculation in the open-fence pass, widening the closing-fence cutoff and causing bare fence markers used as content to be misclassified as closers; the commit also added a test that codifies this widened cutoff. The prior behavior (frome57f73b1) computedmaximumSourceIndentation = awaitedContinuationIndentation + COMMONMARK_MAX_FENCE_INDENTATION - bodyIndentation.length, keeping the cutoff relative to the opening fence.