fix(markdown): lift under-indented fence bodies into numeric list items - #232
Open
detail-app[bot] wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Closes #219
Bug
nestNumericListFences(the streamed-markdown preprocessor infrontend/src/lib/services/numericListFenceNesting.ts) computes a single fixedbodyIndentationprefix for every fence body line asmax(0, continuationColumn − openingFenceColumn). When the opening fence rails are already indented to (or beyond) the list-item continuation column but the literal code body sits at a lower column (e.g. rails at column 3 under1., body flush at column 0), that prefix collapses to empty, so the under-indented body lines pass through unchanged.markedthen closes the list item mid-fence, producing user-facing corruption:<pre><code>inside the list item renders empty.<p>paragraph.2. Step two) is rendered as a top-level indented code block instead of an<ol><li>, breaking the rest of the list.The defect is a logic gap, not a by-design refusal: the existing suite already rescued an identical flush-left body when the opening fence was at column 0 (
bodyIndentation = 3), but failed it solely because the opening fence moved to the continuation column (bodyIndentation = 0).Fix
Re-indent each fence body line based on its own leading spaces so the emitted line reaches at least the list-item continuation column, instead of relying on one fixed prefix derived only from the opening fence's position:
The padding is layered on top of the existing uniform-shift
bodyIndentationprefix and clamps to0whenever the old formula already placed the line at or beyond the continuation column, so it only changes lines the old formula left below the continuation boundary. This is a deliberate surgical clamp rather than themax(continuation, lineIndent)re-placement suggested in the report, which would compress already over-indented bodies (e.g. a 5-space body under a flush-left fence would drop to 2 spaces) and regress existing behavior — a regression guard for that alternative is included in the tests.Testing
numericListFenceNesting.test.tssuite (12 tests) and the full frontend vitest suite (356 tests across 31 files) are green;svelte-checkreports 0 errors/0 warnings;oxlint/eslint/ast-greplint is clean;oxfmtformat check passes; theviteproduction build succeeds.numericListFenceNesting.test.ts, each guarding a distinct footgun: the exact bug reproducer (rails at the continuation column, body flush-left, across both fence markers, LF/CRLF/CR, and streaming + non-streaming), a mixed-indentation body (some lines below the rails, one above) in a single block, the opening-column symmetry (the same flush-left body is rescued whether the opening fence is at column 0 or at the continuation column), and an over-indented body preserved unchanged under a flush-left fence (pins the surgical fix against the naive clamp alternative).stream.provider.fatal-errorbefore any markdown is generated: retrieval requires Qdrant collections that are absent locally and an embedding gateway that returns401: Invalid API Keyfor a dummy credential, and populating those collections needs a realOPENAI_API_KEYfor the private gateway, which is not available in this environment. Even with a valid key, the model's markdown output is non-deterministic so the exact reproducer shape cannot be forced through the chat UI. The committed renderer-level tests feed the exact reproducer markdown directly intoparseMarkdown(preprocess →marked→ DOMPurify) and assert the expected<ol><li>…<pre><code>…</code></pre></li><li>…</li></ol>DOM, which is the deterministic substitute.Automatic Fixes PRs can be configured here.