What happens
A page title containing : is written to frontmatter unquoted:
---
title: Deploy Runbook: Part 2
page_id: 123
---
That is not valid YAML. VSCode's YAML extension flags it, and any other tool that reads these files with a real YAML parser fails on it — yq, a linter, a static-site build over the same tree, another editor's frontmatter plugin. gopkg.in/yaml.v3 reports mapping values are not allowed in this context.
markfluence itself reads it back correctly, which is why this has gone unnoticed: frontmatter.Extract splits each line at the first : only, so title: a: b round-trips as a: b for us and breaks for everyone else. There is a test pinning the current output — internal/frontmatter/frontmatter_test.go, {"colon value bare", "title", "a: b", "", "title: a: b"}.
Why it happens
renderValue in internal/frontmatter/frontmatter.go decides whether to quote by asking "would our own ParseValue round-trip this?" rather than "is this valid YAML?". A colon round-trips through our parser, so it is never quoted.
The colon is one member of a larger class
The same predicate lets all of these through unquoted, and each is either invalid YAML or reads back as the wrong type:
| written |
a real YAML parser sees |
title: a: b |
parse error |
title: true |
boolean true, not the string |
title: 123 |
integer 123 |
title: null / title: ~ |
null |
title: no |
boolean false |
title: [draft] Foo |
flow sequence |
title: @home / *star / &anchor / %pct / - dash / ` |
pipe` |
parent has the same exposure, since it can hold a relative .md path.
Scope
markfluence has no YAML dependency today — internal/frontmatter is a hand-rolled flat-key parser. Fixing this by widening the quoting predicate means hand-maintaining a YAML plain-scalar rule set in a package with no YAML parser to check itself against, which is how the version that forgets % ships. The alternative is to adopt a real YAML library and let it decide quoting.
Related: #100 (markfluence.yaml project-wide settings) needs a real YAML parser anyway.
What happens
A page title containing
:is written to frontmatter unquoted:That is not valid YAML. VSCode's YAML extension flags it, and any other tool that reads these files with a real YAML parser fails on it —
yq, a linter, a static-site build over the same tree, another editor's frontmatter plugin.gopkg.in/yaml.v3reportsmapping values are not allowed in this context.markfluence itself reads it back correctly, which is why this has gone unnoticed:
frontmatter.Extractsplits each line at the first:only, sotitle: a: bround-trips asa: bfor us and breaks for everyone else. There is a test pinning the current output —internal/frontmatter/frontmatter_test.go,{"colon value bare", "title", "a: b", "", "title: a: b"}.Why it happens
renderValueininternal/frontmatter/frontmatter.godecides whether to quote by asking "would our ownParseValueround-trip this?" rather than "is this valid YAML?". A colon round-trips through our parser, so it is never quoted.The colon is one member of a larger class
The same predicate lets all of these through unquoted, and each is either invalid YAML or reads back as the wrong type:
title: a: btitle: truetrue, not the stringtitle: 123123title: null/title: ~title: nofalsetitle: [draft] Footitle: @home/*star/&anchor/%pct/- dash/ `parenthas the same exposure, since it can hold a relative.mdpath.Scope
markfluence has no YAML dependency today —
internal/frontmatteris a hand-rolled flat-key parser. Fixing this by widening the quoting predicate means hand-maintaining a YAML plain-scalar rule set in a package with no YAML parser to check itself against, which is how the version that forgets%ships. The alternative is to adopt a real YAML library and let it decide quoting.Related: #100 (
markfluence.yamlproject-wide settings) needs a real YAML parser anyway.