feat(ingestion): lint and repair unparseable note frontmatter (#90)
Hand-authored and promoted notes skip pipeline.BuildPages, so nothing quotes their scalars. A title carrying an unescaped ": " yields a block yaml.Unmarshal rejects, and every consumer that reads frontmatter drops the note silently — 18 files repo-wide today, up from the 8 in #90. frontmatter.Lint reports it; frontmatter.Fix quotes only what quoting can rescue and refuses to return a document that still fails to parse, so an unfixable file comes back byte-identical rather than half-repaired. Scan walks a tree and reports an unreadable file as a finding instead of aborting, so one poisoned note cannot hide the rest. Sequence items are deliberately left alone: "- foo: bar" is valid YAML that silently becomes a map, which is a type corruption rather than a parse failure, and repairing it would rewrite notes that currently load. task lint:frontmatter # report task lint:frontmatter FIX=1 # repair in place Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
// Command frontmatterlint reports markdown notes whose YAML frontmatter does
|
||||
// not parse, and optionally repairs the ones that only need quoting.
|
||||
//
|
||||
// go run ./cmd/frontmatterlint ../../brain # report
|
||||
// go run ./cmd/frontmatterlint -fix ../../brain # repair in place
|
||||
//
|
||||
// Exit codes: 0 clean (or everything repaired), 1 findings remain, 2 the scan
|
||||
// itself failed.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mathiasbq/hyperguild/ingestion/internal/frontmatter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fix := flag.Bool("fix", false, "repair files that quoting can rescue, in place")
|
||||
flag.Parse()
|
||||
|
||||
root := "."
|
||||
if flag.NArg() > 0 {
|
||||
root = flag.Arg(0)
|
||||
}
|
||||
|
||||
findings, err := frontmatter.Scan(root, *fix)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "frontmatterlint:", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var unresolved int
|
||||
for _, f := range findings {
|
||||
if f.Repaired {
|
||||
fmt.Printf("repaired %s\n", f.Path)
|
||||
continue
|
||||
}
|
||||
unresolved++
|
||||
fmt.Printf("BROKEN %s: %v\n", f.Path, f.Err)
|
||||
}
|
||||
|
||||
if unresolved > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\n%d file(s) with unparseable frontmatter", unresolved)
|
||||
if !*fix {
|
||||
fmt.Fprint(os.Stderr, " — re-run with -fix")
|
||||
}
|
||||
fmt.Fprintln(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("✓ frontmatter: %d file(s) repaired, none broken\n", len(findings))
|
||||
}
|
||||
Reference in New Issue
Block a user