fix(ingestion): decide quoting with YAML itself, not a colon heuristic (#90)
CI / Lint / Test / Vet (push) Successful in 29s
CI / Mirror to GitHub (push) Successful in 4s

`title: "Foo" — bar` opens with a quote and is not quoted; the previous
prefix check read it as already-quoted and left two notes broken. Probing
the value as the right-hand side of a mapping answers the real question —
does this load as written — and leaves anything that already parses alone,
whatever it parses into.

Repairs the last two quotable files in mathias/brain. Only wiki/index.md
remains unparseable, for an unrelated reason: a truncated UTF-8 sequence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
This commit is contained in:
2026-08-18 00:10:11 +02:00
co-authored by Claude Opus 5
parent 7d1213451c
commit 3da0eca9b4
2 changed files with 43 additions and 11 deletions
+13 -11
View File
@@ -101,21 +101,23 @@ func quoteScalars(block []byte) []byte {
return []byte(strings.Join(lines, "\n"))
}
// needsQuoting reports whether a scalar value must be quoted to parse as the
// string it plainly is. Only the colon case is handled: it is the one this
// package exists for, and every other special case risks rewriting a value
// that was deliberately structured (a nested mapping, an anchor, a number).
// needsQuoting reports whether a scalar value has to be quoted to load as the
// string it plainly is.
//
// The test is YAML's own parser rather than a pattern: ask whether the value
// stands alone as the right-hand side of a mapping. Heuristics get this wrong
// in both directions — `title: "Foo" — bar` opens with a quote but is not
// quoted, and a value may contain a colon inside quotes and be perfectly fine.
//
// Anything that already parses is left alone, whatever it parses into — a flow
// collection, a number, a bool. It is not broken, and quoting would change its
// type.
func needsQuoting(value string) bool {
if strings.HasPrefix(value, "'") || strings.HasPrefix(value, `"`) {
return false
}
if strings.HasPrefix(value, "|") || strings.HasPrefix(value, ">") {
return false // block scalar; its content is not parsed as YAML
}
if strings.HasPrefix(value, "{") || strings.HasPrefix(value, "[") {
return false // flow collection; quoting would silently make it a string
}
return strings.Contains(value, ": ") || strings.HasSuffix(value, ":")
var probe map[string]any
return yaml.Unmarshal([]byte("v: "+value+"\n"), &probe) != nil
}
// block returns the raw YAML between the opening and closing fences.