wiki notes with unquoted colon in title: produce invalid YAML frontmatter (8 files found) #90

Closed
opened 2026-07-27 12:29:29 +00:00 by mathias · 2 comments
Owner

Found while repairing #86's stacked-frontmatter bug. Independent of stacking — these files already had a single frontmatter block, but their title: (or other scalar) line contains an unescaped : and isn't quoted, so yaml.safe_load chokes with "mapping values are not allowed here".

Example (wiki/homelab/failures/dispatch-headless-bash-denied-git-via-gitea-mcp.md):

title: Headless dispatch runs: Bash is denied — use gitea MCP for all git ops, CI is the only test signal

8 files repo-wide currently affected (ran a YAML-parse pass over every wiki/**/*.md frontmatter block in mathias/brain):

  • wiki/homelab/failures/dispatch-headless-bash-denied-git-via-gitea-mcp.md
  • wiki/homelab/failures/dispatch-headless-go-race-needs-gcc-cgo.md
  • wiki/homelab/hypotheses/ecosystem-consistency-audit-2026-07.md
  • wiki/hyperguild/decisions/retro-skill-semantic-read-after-write-staleness-constraint.md
  • (4 more — re-run the scan below to get the current list, some of these were written after this issue was filed)

Root cause

buildFrontmatter (ingestion internal/pipeline/build.go) already quotes title/aliases via yamlScalar() for LLM-extracted pages. These 8 are hand-written or promoted notes where the frontmatter was authored directly (not through BuildPages), so nothing enforced quoting.

Suggested scope

  • Add a frontmatter-quoting lint check (or auto-fix) that brain-gardener (or a similar CI-time pass) can run — same idea as its existing fix(malformed)/fix(stale-date) classes.
  • Repair the 8 existing files.

Repro / verification script

import yaml, glob
bad = []
for f in glob.glob('wiki/**/*.md', recursive=True):
    with open(f, encoding='utf-8') as fh:
        text = fh.read()
    lines = text.split('\n')
    if not lines or lines[0].strip() != '---':
        continue
    i = 1
    while i < len(lines) and lines[i].strip() != '---':
        i += 1
    try:
        yaml.safe_load('\n'.join(lines[1:i]))
    except Exception:
        bad.append(f)
print(bad)
Found while repairing #86's stacked-frontmatter bug. Independent of stacking — these files already had a single frontmatter block, but their `title:` (or other scalar) line contains an unescaped `:` and isn't quoted, so `yaml.safe_load` chokes with "mapping values are not allowed here". Example (`wiki/homelab/failures/dispatch-headless-bash-denied-git-via-gitea-mcp.md`): ```yaml title: Headless dispatch runs: Bash is denied — use gitea MCP for all git ops, CI is the only test signal ``` 8 files repo-wide currently affected (ran a YAML-parse pass over every `wiki/**/*.md` frontmatter block in `mathias/brain`): - wiki/homelab/failures/dispatch-headless-bash-denied-git-via-gitea-mcp.md - wiki/homelab/failures/dispatch-headless-go-race-needs-gcc-cgo.md - wiki/homelab/hypotheses/ecosystem-consistency-audit-2026-07.md - wiki/hyperguild/decisions/retro-skill-semantic-read-after-write-staleness-constraint.md - (4 more — re-run the scan below to get the current list, some of these were written after this issue was filed) ## Root cause `buildFrontmatter` (ingestion `internal/pipeline/build.go`) already quotes title/aliases via `yamlScalar()` for LLM-extracted pages. These 8 are hand-written or promoted notes where the frontmatter was authored directly (not through `BuildPages`), so nothing enforced quoting. ## Suggested scope - Add a frontmatter-quoting lint check (or auto-fix) that brain-gardener (or a similar CI-time pass) can run — same idea as its existing `fix(malformed)`/`fix(stale-date)` classes. - Repair the 8 existing files. ## Repro / verification script ```python import yaml, glob bad = [] for f in glob.glob('wiki/**/*.md', recursive=True): with open(f, encoding='utf-8') as fh: text = fh.read() lines = text.split('\n') if not lines or lines[0].strip() != '---': continue i = 1 while i < len(lines) and lines[i].strip() != '---': i += 1 try: yaml.safe_load('\n'.join(lines[1:i])) except Exception: bad.append(f) print(bad) ```
Author
Owner

Done — lint shipped, 17 files repaired, 1 left that is not a quoting problem

Code (3da0eca, 7c9293f): ingestion/internal/frontmatter — Lint, Fix, Scan, plus cmd/frontmatterlint.

task lint:frontmatter          # report
task lint:frontmatter FIX=1    # repair in place

Content (mathias/brain a5865ca, 86ccf37): 17 files repaired — the 8 in this issue plus 9 written since.

The count was 18, not 8

Re-scanning found 17 broken frontmatter blocks (this issue's 4 named ones among them) and one file broken for a different reason. Every repaired value was verified to round-trip byte-identically and every body left untouched, checked by a separate script rather than by reading the diff.

Two design calls worth recording

Sequence items are deliberately not repaired. - Headless dispatch: denied is valid YAML — it loads as {"Headless dispatch": "denied"} rather than a string. That is a silent type corruption, not a parse failure, and repairing it would rewrite notes that currently load fine. Left as a documented gap in the Lint test table; worth its own issue if aliases turning into maps ever bites something.

The colon heuristic was wrong and the first pass shipped it. Two files resisted repair:

title: "Evaluation Driven Generation" — a TDD/BDD-style meta-prompt scaffold

That opens with a double quote and keeps going past the closing one. A "starts with a quote ⇒ already quoted" check reads it as fine and leaves it broken forever. Replaced with a probe that asks YAML itself whether the value stands alone as the right-hand side of a mapping — which answers the actual question and also leaves flow sequences, numbers and bools untouched because they already parse. tags: [prompting, llm] is still a sequence afterward, verified.

Fix refuses to return a document that still fails to parse, so an unfixable file comes back byte-identical rather than half-repaired. Scan reports an unreadable file as a finding rather than aborting the walk — one poisoned note must not hide the others, which is exactly what happened with wiki/index.md below.

Verified: brain-sync merges, it does not clobber

Worth recording because it gated whether this repair was safe to push at all. mathias/brain is written from koala on a 15-minute cycle, so a content fix pushed from a laptop could plausibly have been reverted by the next sync. It was not — the sync landed a brain-sync: merge origin/main commit on top and all 15 repairs survived. Confirmed by re-running the lint against the post-sync tree, not by reasoning about it.

Left open, needs a human — wiki/index.md is not a quoting bug

first bad byte at offset 590:
  b'...|Local Model Agentic Coding Capability \xe2\x80\xe2\x80\xa6\n- [[brain-wiki'

\xe2\x80 then another \xe2\x80\xa6 — a truncated UTF-8 sequence where an em-dash or ellipsis was cut mid-character. The file is generated, so patching the bytes would only paper over whatever produced them. Filed separately as #92 rather than fixed here: it is a writer-side truncation bug, and this issue is about frontmatter quoting.

Closing this one.

## Done — lint shipped, 17 files repaired, 1 left that is not a quoting problem **Code** (`3da0eca`, `7c9293f`): `ingestion/internal/frontmatter` — `Lint`, `Fix`, `Scan`, plus `cmd/frontmatterlint`. ```sh task lint:frontmatter # report task lint:frontmatter FIX=1 # repair in place ``` **Content** (`mathias/brain` `a5865ca`, `86ccf37`): 17 files repaired — the 8 in this issue plus 9 written since. ### The count was 18, not 8 Re-scanning found 17 broken frontmatter blocks (this issue's 4 named ones among them) and one file broken for a different reason. Every repaired value was verified to round-trip byte-identically and every body left untouched, checked by a separate script rather than by reading the diff. ### Two design calls worth recording **Sequence items are deliberately not repaired.** `- Headless dispatch: denied` is *valid* YAML — it loads as `{"Headless dispatch": "denied"}` rather than a string. That is a silent type corruption, not a parse failure, and repairing it would rewrite notes that currently load fine. Left as a documented gap in the `Lint` test table; worth its own issue if aliases turning into maps ever bites something. **The colon heuristic was wrong and the first pass shipped it.** Two files resisted repair: ```yaml title: "Evaluation Driven Generation" — a TDD/BDD-style meta-prompt scaffold ``` That opens with a double quote and keeps going past the closing one. A "starts with a quote ⇒ already quoted" check reads it as fine and leaves it broken forever. Replaced with a probe that asks YAML itself whether the value stands alone as the right-hand side of a mapping — which answers the actual question and also leaves flow sequences, numbers and bools untouched because they already parse. `tags: [prompting, llm]` is still a sequence afterward, verified. `Fix` refuses to return a document that still fails to parse, so an unfixable file comes back byte-identical rather than half-repaired. `Scan` reports an unreadable file as a finding rather than aborting the walk — one poisoned note must not hide the others, which is exactly what happened with `wiki/index.md` below. ### Verified: brain-sync merges, it does not clobber Worth recording because it gated whether this repair was safe to push at all. `mathias/brain` is written from koala on a 15-minute cycle, so a content fix pushed from a laptop could plausibly have been reverted by the next sync. It was not — the sync landed a `brain-sync: merge origin/main` commit on top and all 15 repairs survived. Confirmed by re-running the lint against the post-sync tree, not by reasoning about it. ### Left open, needs a human — `wiki/index.md` is not a quoting bug ``` first bad byte at offset 590: b'...|Local Model Agentic Coding Capability \xe2\x80\xe2\x80\xa6\n- [[brain-wiki' ``` `\xe2\x80` then another `\xe2\x80\xa6` — a truncated UTF-8 sequence where an em-dash or ellipsis was cut mid-character. The file is generated, so patching the bytes would only paper over whatever produced them. **Filed separately as #92** rather than fixed here: it is a writer-side truncation bug, and this issue is about frontmatter quoting. Closing this one.
Author
Owner

Correction to the comment above: the wiki/index.md truncation bug is #93, not #92.

Correction to the comment above: the `wiki/index.md` truncation bug is **#93**, not #92.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/hyperguild#90