Skip to content

fix: prevent markdown fence balancer from corrupting sequential code blocks#26785

Merged
dsyme merged 7 commits intomainfrom
fix/markdown-fence-balancer-nesting-v2
Apr 17, 2026
Merged

fix: prevent markdown fence balancer from corrupting sequential code blocks#26785
dsyme merged 7 commits intomainfrom
fix/markdown-fence-balancer-nesting-v2

Conversation

@dsyme
Copy link
Copy Markdown
Collaborator

@dsyme dsyme commented Apr 17, 2026

Problem

The balanceCodeRegions() function in markdown_code_region_balancer.cjs was incorrectly detecting "true nesting" in markdown that contained sequential code blocks with no actual nesting. This caused safe-output bodies (e.g., create-issue, create-pull-request) to have their fenced code blocks corrupted.

Example: A PR body with a ```cpp block followed by a bare ``` block would get transformed into a single ````-fenced block, swallowing the closing/opening fences in between.

Observed in: fsprojects/PX4-Autopilot#39

Root cause

The isTrueNesting condition was:

const isTrueNesting = closerCount > openerCount + 1;

When openerCount is 0 (no intermediate language-tagged fences between opener and closers), this fires for any 2+ bare closers — treating perfectly valid sequential code blocks as nested content and wrapping them in longer fences.

Fix

Added a guard requiring at least one intermediate language-tagged opener for nesting detection:

const isTrueNesting = openerCount > 0 && closerCount > openerCount + 1;

Without intermediate openers, multiple bare closers are sequential code blocks (greedy matching per CommonMark), not nested content.

Testing improvements

  • Fixed 3 tests that encoded the broken behavior as correct expectations
  • Unskipped 6 tests that were masked with it.skip / TODO comments — all pass now
  • Added 5 real-world regression tests including the exact pattern from the bug report
  • Added 2 true-nesting tests verifying nesting IS still detected with intermediate language openers
  • Added idempotency test ensuring balance(balance(x)) === balance(x)
  • Added 2 invariant tests for "never worse than input" and "never changes balanced content"

Test count: 66 passing + 6 skipped → 82 passing, 0 skipped

…blocks

The balanceCodeRegions() function was incorrectly detecting 'true nesting'
when a document contained sequential code blocks without intermediate
language-tagged openers. For example, a ```cpp block followed by a bare
``` block would be merged into a single ```` block, corrupting the
markdown by treating the closing/opening fences as content.

Root cause: the isTrueNesting condition fired when closerCount > openerCount + 1,
but when openerCount was 0 (no intermediate language-tagged fences), this
triggered for any 2+ bare closers.

Fix: require openerCount > 0 for nesting detection. Without intermediate
language-tagged openers, multiple bare closers are sequential code blocks
(greedy matching per CommonMark), not nested content.

Also unskips 6 tests that were masked by this bug, adds real-world regression
tests including the exact pattern from the production bug, and adds
idempotency and invariant tests. Test count: 66 passing + 6 skipped -> 82
passing, 0 skipped.
Copilot AI review requested due to automatic review settings April 17, 2026 01:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes the markdown fence “nesting” detection in balanceCodeRegions() so sequential fenced code blocks (e.g., cpp … followed by ) are no longer incorrectly merged/corrupted by fence-length escalation.

Changes:

  • Tightens “true nesting” detection by adding a guard requiring at least one intermediate language-tagged opener.
  • Updates/expands the Vitest suite: corrects prior expectations that encoded broken behavior, unskips previously skipped cases, and adds multiple real-world regression scenarios plus invariants/idempotency tests.
Show a summary per file
File Description
actions/setup/js/markdown_code_region_balancer.cjs Adjusts the “true nesting” condition used to decide when to lengthen fences and treat intermediate fences as content.
actions/setup/js/markdown_code_region_balancer.test.cjs Updates expectations around greedy matching/sequential blocks and adds extensive regression + invariant coverage.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (2)

actions/setup/js/markdown_code_region_balancer.test.cjs:1041

  • This “true nesting” test only asserts isBalanced(result) === true and doesn’t check any nesting/escaping outcome (e.g., outer fence length increase / preservation of inner fence lines as content). As written, it can pass even if the nesting-escape path never runs. Consider asserting a concrete expected output or at least checking for lengthened outer fences on an input that requires escaping.
      it("should handle markdown block containing a language-tagged inner block", () => {
        // This IS true nesting: ```markdown contains ```python inside it
        // The balancer should handle this by making the result balanced
        const input = `\`\`\`markdown
Here's an example:

actions/setup/js/markdown_code_region_balancer.test.cjs:1055

  • This test also only checks isBalanced(result) and doesn’t assert that the balancer performed the intended nesting handling (such as escaping by lengthening the outer fence). Since isBalanced() currently ignores the info string when determining closers, this assertion may not catch regressions in nesting logic. Consider asserting a specific expected output or checking for a longer outer fence.
      it("should handle nested documentation example with multiple inner blocks", () => {
        // Markdown block containing multiple language-tagged inner blocks
        const input = `\`\`\`markdown
## Usage

  • Files reviewed: 2/2 changed files
  • Comments generated: 3

Comment thread actions/setup/js/markdown_code_region_balancer.cjs Outdated
Comment thread actions/setup/js/markdown_code_region_balancer.test.cjs Outdated
Comment thread actions/setup/js/markdown_code_region_balancer.test.cjs
dsyme and others added 6 commits April 17, 2026 17:28
… matching

The isTrueNesting branch was unreachable: directClosers are filtered to
have hasOpenerBetween === false, so openerCount over i+1..lastDirectCloser
is always 0. Remove the dead code path, fenceLengthAdjustments map, and
the fence-length rewriting pass. The balancer now uses straightforward
CommonMark greedy matching throughout.

-95 lines of dead/unreachable code removed.
Closing fences must be bare (no info string/language tag) per CommonMark.
isBalanced() and countCodeRegions() were not checking this, giving false
confidence in test assertions. Update both functions and fix tests for
ambiguous nesting cases to assert the correct invariant (no worse than
input) instead of asserting balanced output.
@dsyme dsyme merged commit f48ade2 into main Apr 17, 2026
57 checks passed
@dsyme dsyme deleted the fix/markdown-fence-balancer-nesting-v2 branch April 17, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants