Skip to content

fix: handle CommonMark fenced code boundaries - #12373

Open
adityat210 wants to merge 1 commit into
deepset-ai:mainfrom
adityat210:fix/markdown-fence-boundaries
Open

fix: handle CommonMark fenced code boundaries#12373
adityat210 wants to merge 1 commit into
deepset-ai:mainfrom
adityat210:fix/markdown-fence-boundaries

Conversation

@adityat210

Copy link
Copy Markdown

Related Issues

  • None. This was found during a repository audit.

Proposed Changes

MarkdownHeaderSplitter previously matched an entire fenced code block with a regex whose closing delimiter had to be identical to the opener. Valid Markdown with a longer closing fence, up to three spaces of fence indentation, or no closing fence therefore exposed hash-prefixed code lines to the header matcher.

This PR:

  • replaces the whole-block backreference regex with a linear line-state scanner;
  • accepts closing fences that use the same marker and are at least as long as the opener;
  • accepts the CommonMark allowance of up to three leading spaces;
  • treats an unclosed fence as continuing to the end of the document;
  • preserves the existing same-marker and minimum-length safeguards;
  • adds regression coverage for backtick and tilde fences, each supported indentation, EOF handling, and shorter non-closing delimiters.

The behavior aligns with the CommonMark fenced code block specification.

Measured on the three minimal reproductions:

Case Before headers After headers
Longer closing fence Real, fake, Next Real, Next
Three-space-indented fence Real, fake, Next Real, Next
Unclosed fence Real, fake Real

False header metadata across these cases drops from 3 to 0. There is no public API change.

How did you test it?

  • Targeted component suite in a Hatch-managed Python 3.10 environment: 46 passed in 0.29s.
  • hatch run fmt-check haystack/components/preprocessors/markdown_header_splitter.py test/components/preprocessors/test_markdown_header_splitter.py
  • hatch run pre-commit run --files haystack/components/preprocessors/markdown_header_splitter.py test/components/preprocessors/test_markdown_header_splitter.py releasenotes/notes/markdown-fence-boundaries-20f1dacc1efec6c8.yaml
  • hatch run reno lint
  • Manual before/after reproduction for longer, indented, and unclosed fences.

Notes for the reviewer

Checklist

  • I have read the contributors guidelines and code of conduct.
  • There is no related issue to update.
  • I have added focused unit tests and documented the non-obvious behavior.
  • The PR title uses a conventional commit type.
  • I generated and completed a release note.
  • I ran the relevant pre-commit hooks and fixed all findings.

@vercel

vercel Bot commented Aug 16, 2026

Copy link
Copy Markdown

@adityat210 is attempting to deploy a commit to the deepset Team on Vercel.

A member of the Team first needs to authorize it.

@CLAassistant

CLAassistant commented Aug 16, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@adityat210
adityat210 marked this pull request as ready for review August 19, 2026 17:29
@adityat210
adityat210 requested a review from a team as a code owner August 19, 2026 17:29
@adityat210
adityat210 requested review from anakin87 and removed request for a team August 19, 2026 17:29
@HaystackBot

Copy link
Copy Markdown
Contributor

Hi @adityat210, thanks a lot for your contribution! 🙏

We noticed that the Contributor License Agreement (CLA) check (license/cla) hasn't passed yet, so we've temporarily moved this PR to draft and paused the review assignment.

To get your PR reviewed, please sign the CLA via the link in the license/cla check below (or in the CLA bot comment). As soon as the check turns green, this PR will automatically be marked ready for review again and a reviewer will be re-assigned.

@HaystackBot
HaystackBot removed the request for review from anakin87 August 19, 2026 17:54
@HaystackBot HaystackBot added the cla-pending PR is in draft until the contributor signs the CLA label Aug 19, 2026
@HaystackBot
HaystackBot marked this pull request as draft August 19, 2026 17:55
@adityat210
adityat210 marked this pull request as ready for review August 19, 2026 18:25
@HaystackBot
HaystackBot requested a review from anakin87 August 19, 2026 18:29
@HaystackBot

Copy link
Copy Markdown
Contributor

Thanks for signing the CLA, @adityat210! 🎉 This PR is now ready for review again and the reviewer has been re-assigned.

@HaystackBot HaystackBot removed the cla-pending PR is in draft until the contributor signs the CLA label Aug 19, 2026

@anakin87 anakin87 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

I found 2 minor opportunities for improvement.

fixes:
- |
Fixed ``MarkdownHeaderSplitter`` treating hash-prefixed lines inside indented, unclosed, or
longer-delimiter fenced code blocks as Markdown headers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's also specify that now an unclosed fence is treated as extending to the end of the document, so hash lines after it are no longer split into chunks.

# CommonMark allows up to three spaces before an opening fence. A backtick fence's info
# string cannot itself contain a backtick; that condition is checked while scanning.
self._code_block_open_pattern = re.compile(r" {0,3}(?P<fence>`{3,}|~{3,})(?P<info>[^\r\n]*)")
self._code_block_close_pattern = re.compile(r" {0,3}(?P<fence>`{3,}|~{3,})[ \t]*")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
self._code_block_close_pattern = re.compile(r" {0,3}(?P<fence>`{3,}|~{3,})[ \t]*")
self._code_block_close_pattern = re.compile(r" {0,3}(?P<fence>`{3,}|~{3,})\s*")

I'd keep using \s* as the previous regex: this should work well with documents containing page breaks.

Please also add a test similar to this:

def test_page_break_after_closing_fence(self):
    text = "# Real Header\n```python\n# not a header\n```\f\n## Real Sub\nContent.\n"
    ...

@github-actions github-actions Bot added the type:documentation Improvements on the docs label Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:tests type:documentation Improvements on the docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants