| name | wiki-frontmatter-fence-repair |
| description | Restore YAML frontmatter fences stripped by bulk wiki edits. |
| category | wiki |
Wiki Frontmatter Fence Repair
When to Use
Lint jumps from 0 to dozens/hundreds of NO FRONTMATTER: entities/... errors (and
NO FRONTMATTER on raw article: ... warnings) after a bulk write operation (a backfill
script, batch ingest, source_url rewrite). The YAML frontmatter CONTENT is intact but
the --- delimiters are gone.
Root cause — how lint detects frontmatter
scripts/wiki-lint.mjs uses:
const FM_RE = /^---\n([\s\S]*?)\n---/;
readFrontmatter() does content.match(FM_RE). The ^ anchor means a file MUST start
with the opening --- on line 1. If a bulk script strips BOTH the opening AND closing
--- fences (e.g. re-emits the YAML block without delimiters), the file starts with
source_url:/title:/type: directly → regex fails → readFrontmatter returns null
→ NO FRONTMATTER.
Distinguishing the fence-strip (vs. genuinely missing FM)
- File no longer starts with
---; first line is a YAML key.
- Frontmatter content is fully intact — only the two
--- delimiters are gone.
git diff typically shows BOTH a value backfill (e.g. empty source_url: " → real URL)
AND the --- lines removed.
- Scope is large and mechanical (dozens–hundreds of files, all the same shape).
Fix — restore fences for lint-flagged files ONLY
Do NOT blanket-rewrite all modified files. Operate only on files lint flags, so legitimate
fence-free files (Dashboard.md, hot-context.md cache, etc.) stay untouched.
Robust restore algorithm
Handles two boundary shapes a naive "insert at first blank line" misses:
- Files with a leading blank line before the FM block.
- Files where the FM block runs straight into the body with NO blank separator
(last key line immediately followed by
# heading / text).
lines = content.split('\n')
if lines[0] === '---': already has opening fence → skip
skip leading blank lines
start = first non-blank line; require it matches /^[A-Za-z_][\w-]*:/ (YAML key)
end = run of lines matching KEY_RE OR list-item /^\s*- /
FM block = lines[start..end]
rebuild = ['---', ...FM, '---', ...body(lines[end..])]
Key REs:
KEY_RE = /^[A-Za-z_][\w-]*:/
LIST_RE = /^\s*- / (for sources: list items)
Gotchas / pitfalls
Verified example (2026-08-19)
218 files (114 entity errors + 104 raw warnings) all from one source_url backfill that
stripped fences. Restored all 218 → lint back to 0 errors. Warning count also dropped
(320 → 209) because the 114 raw NO FRONTMATTER warnings resolved too.
Related
- User-owned
obsidian-wiki-lint and wiki-maintenance cover other lint repair patterns
(broken wikilinks, missing CLOSING ---, tag corruption). This skill is the specific
"both fences stripped at scale" recovery. Prefer patching those if they become
curator-managed.