You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A --- rule on the first body line is read as a second frontmatter block: body text is deleted by ExtractBody/RemoveFrontmatter or merged into the header #140
TrySplitFrontmatterBlocks (Frontmatter/Frontmatter.cs ~371-396) loops while (IsDelimiterAt(next)). So after the header closes, a --- on the very next line opens another block, and that block runs to the next --- line anywhere in the document. The fix for #132 stopped horizontal rules further down the body being treated as delimiters, but a rule placed directly under the header is still consumed.
Repro
This was reproduced against current main (13a0d6f) using scratch MSTest methods.
Case A: the text between the rules isn't a YAML mapping, so it is lost.
---
title: A
---
---
Important paragraph
---
More
ExtractBody returns "More".
RemoveFrontmatter returns "More\n".
ReplaceFrontmatter, AddFrontmatter and CombineFrontmatter all emit the header followed by only More.
"Important paragraph" and both rules are silently deleted.
Case B: the text happens to parse as YAML, so it moves into the header.
---
title: My Post
---
---
Note: read this first
---
# Heading
CombineFrontmatter returns ---\ntitle: My Post\nNote: read this first\n---\n\n# Heading\n. A body paragraph has become a header property.
For comparison, ---\ntitle: A\n---\n---\n\nIntro text\n, which has no second rule, keeps its body correctly.
Why it matters
Placing a thematic break directly under the header is common Markdown. Running any of the rewriting APIs over such a document destroys or relocates content with no error. This isn't #139: that issue is about CombineFrontmatter dropping an unparseable block, whereas this bug is in the splitter and affects every API, including ExtractBody and RemoveFrontmatter.
Suggested fix / acceptance criteria
Accept a block after the first only if its content parses as a non-empty YAML mapping. Otherwise stop splitting and leave the --- in the body. This fixes Case A.
Case B is ambiguous, because consecutive blocks are supported on purpose (DelimiterLineTests.cs:55). Decide on a tighter rule, for example rejecting a follow-on block whose content is surrounded by blank lines, and add a regression test for it.
Add a test for Case A in which ExtractBody returns the full body, including both rules.
Priority: High. Every rewriting API, plus ExtractBody/RemoveFrontmatter, silently deletes body content, or moves it into the header, whenever a thematic break sits directly under the header. That is common Markdown and nothing reports it.
Area / suggested owner:TrySplitFrontmatterBlocks in Frontmatter.cs.
Duplicates / in progress: None found. No open PR covers this.
Next step: Accept a follow-on block only if it parses as a non-empty YAML mapping, which fixes Case A. Case B needs an owner decision on the tighter rule, because stacked blocks are supported on purpose (DelimiterLineTests.cs:55).
Activity
matt-edmondson commentedon Sep 27, 2026
Triage
ExtractBody/RemoveFrontmatter, silently deletes body content, or moves it into the header, whenever a thematic break sits directly under the header. That is common Markdown and nothing reports it.TrySplitFrontmatterBlocksinFrontmatter.cs.CombineFrontmatter) and shares the splitter with HasFrontmatter misses an opening---with trailing whitespace (or a leading BOM), so AddFrontmatter stacks a second header above the real one #141, so the three should be handled together.DelimiterLineTests.cs:55).Generated by Claude Code