fix(#2977): tolerate a leading UTF-8 BOM before the frontmatter fence (#3076)

* test(#2977): prove extractFrontmatter returns {} on a leading BOM

Failing-first regression for #2977. extractFrontmatter's startsWith('---') byte-0
check fails on any leading byte, so a UTF-8 BOM (Windows PowerShell/Out-File, several
editors) makes every frontmatter field silently disappear. Rows 1-2 assert BOM-prefixed
frontmatter parses identically to no-BOM (incl. BOM+CRLF); Row 3 guards the no-frontmatter
negative space; Row 4 covers STATE/PLAN/SUMMARY/UAT artifact shapes; Row 5 is the control.

* fix(#2977): tolerate a leading UTF-8 BOM before the frontmatter fence

extractFrontmatter's byte-0 startsWith('---') fence check failed on any leading
byte, so a UTF-8 BOM (\uFEFF) — written by default by Windows PowerShell
`>`/Out-File (PS 5.1) and several editors — made every frontmatter field
silently disappear. STATE.md/ROADMAP.md/PLAN.md/UAT.md/SUMMARY.md all lost
phase/status/name with no error, no warning.

Strip a single leading BOM before the fence check. The rest of the function is
unchanged — the BOM is one codepoint, and removing it restores byte-0 alignment.
Negative space preserved: BOM + no-frontmatter and BOM + thematic-break Markdown
both still return {} (no false diagnostic). Scope: BOM only; the generalized
'arbitrary content before the fence' fork (tolerate vs diagnose) is a separate
product-intent decision, surfaced in the PR.

* chore(#2977): add changeset fragment

* chore(#2977): backfill changeset PR number 3076

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-05 02:18:25 -04:00
committed by GitHub
parent 7e1004d89e
commit e6fcf14d02
3 changed files with 95 additions and 0 deletions

View File

@@ -204,6 +204,18 @@ function parseYamlRegion(yaml: string): Frontmatter {
* hold only an in-memory string; those dedup on a content digest instead.
*/
function extractFrontmatter(content: string, sourcePath?: string): Frontmatter {
// #2977: tolerate a single leading UTF-8 BOM (\uFEFF), which Windows tooling
// (PowerShell `>`/`Out-File` on PS 5.1, several editors) writes by default. Without this
// strip, the byte-0 `startsWith('---')` fence check below fails on the BOM and the whole
// parse collapses to {} — every frontmatter field silently disappears, and the engine
// proceeds as though the file had no frontmatter at all. The BOM is a single codepoint;
// stripping it here restores byte-0 alignment so the rest of the function is unchanged.
// Scope: BOM only. Arbitrary non-BOM content before the fence (leading whitespace/blank
// line/comment) is a separate product-intent decision (tolerate vs diagnose) left to a
// future change — this fix does not broaden the byte-0 fence rule beyond the BOM.
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
// Match frontmatter only at byte 0 — a `---` block later in the document
// body (YAML examples, horizontal rules) must never be treated as frontmatter.
const headerEnd = content.startsWith('---\r\n') ? 5 : content.startsWith('---\n') ? 4 : -1;