fix(markdown): treat # lines inside closed frontmatter as YAML comments, not headings (#2153)

`parseMarkdown` previously walked the lines after the opening `---` and
recorded the first `^#{1,6}\s`-shaped line as a `headingBeforeClose`,
then flagged MISSING_CLOSE when that index came before the actual closing
fence. YAML allows `#` comment lines anywhere inside the document, so a
template that leads with annotation comments inside the fence (e.g. a
`# Research Template` header before the keys) hit a false-positive
MISSING_CLOSE even though the closing `---` was present.

Fix: only walk for the closing `---`. When it is found, content between
the fences is YAML; `#` lines are comments, not headings. When the close
is genuinely missing, surface the first heading-shaped line as a
where-it-went-off-the-rails hint (this path was already correct; we keep
it for the genuine missing-close case).

Two regression tests added to `test/markdown-validation.test.ts`:
- `#` comment lines at the top of a closed frontmatter
- `#` comment lines interleaved with keys

All 68 tests across the four markdown/frontmatter test files stay green.
This commit is contained in:
Brett
2026-07-17 11:39:15 -07:00
committed by GitHub
parent 7ffac65c62
commit a31f16f471
2 changed files with 51 additions and 19 deletions
+19 -19
View File
@@ -213,39 +213,39 @@ function collectValidationErrors(
return;
}
// 3. MISSING_CLOSE — find the next `---` after the opener. If a markdown
// heading appears before it, that's a strong signal the closing
// delimiter is missing (the heading was meant to be in the body).
// 3. MISSING_CLOSE — find the next `---` after the opener.
let closeLine = -1;
let headingBeforeClose = -1;
for (let i = firstNonEmpty + 1; i < lines.length; i++) {
const t = lines[i].trim();
if (t === '---') {
if (lines[i].trim() === '---') {
closeLine = i;
break;
}
if (/^#{1,6}\s/.test(t) && headingBeforeClose === -1) {
headingBeforeClose = i;
}
}
if (closeLine === -1) {
// No closing fence found. Surface the first heading-shaped line as a
// hint for where the parser thinks the frontmatter went off the rails —
// only useful when the close is genuinely missing, since YAML allows
// `#` comment lines inside a closed fence (see comment below).
let headingHint = -1;
for (let i = firstNonEmpty + 1; i < lines.length; i++) {
if (/^#{1,6}\s/.test(lines[i].trim())) {
headingHint = i;
break;
}
}
errors.push({
code: 'MISSING_CLOSE',
message:
headingBeforeClose >= 0
? `No closing --- before heading at line ${headingBeforeClose + 1}`
headingHint >= 0
? `No closing --- before heading at line ${headingHint + 1}`
: 'No closing --- delimiter found',
line: headingBeforeClose >= 0 ? headingBeforeClose + 1 : firstNonEmpty + 1,
line: headingHint >= 0 ? headingHint + 1 : firstNonEmpty + 1,
});
return;
}
if (headingBeforeClose >= 0 && headingBeforeClose < closeLine) {
errors.push({
code: 'MISSING_CLOSE',
message: `Heading at line ${headingBeforeClose + 1} found inside frontmatter zone (closing --- comes after)`,
line: headingBeforeClose + 1,
});
}
// Closing fence found. Content between opening and closing is YAML, which
// permits `#` comment lines anywhere — those are not markdown headings
// and must not raise MISSING_CLOSE.
// 4. EMPTY_FRONTMATTER — open and close present but nothing meaningful between.
const fmBody = lines.slice(firstNonEmpty + 1, closeLine).join('\n').trim();
+32
View File
@@ -50,6 +50,38 @@ describe('parseMarkdown validation surface', () => {
const e = parsed.errors!.find(e => e.code === 'MISSING_CLOSE');
expect(e).toBeDefined();
});
test('YAML # comment at top of closed frontmatter does NOT trigger MISSING_CLOSE', () => {
// Real-world repro: research-note templates often lead with `#` comment
// lines as YAML comments inside the fence. The parser previously read
// these as markdown H1s and false-positived MISSING_CLOSE even when the
// closing `---` was present.
const md = `${fence}
# Research Template
# This file serves as a template for all research findings
research_id: "R19"
title: "iOS App Clip security limitations"
${fence}
body`;
const parsed = parseMarkdown(md, undefined, { validate: true });
expect(parsed.errors!.map(e => e.code)).not.toContain('MISSING_CLOSE');
});
test('YAML # comments interleaved with keys do NOT trigger MISSING_CLOSE', () => {
const md = `${fence}
type: concept
# section: identifiers
research_id: "R19"
# section: routing
slug: research/r19
${fence}
body`;
const parsed = parseMarkdown(md, undefined, { validate: true });
expect(parsed.errors!.map(e => e.code)).not.toContain('MISSING_CLOSE');
});
});
describe('YAML_PARSE', () => {