fix(frontmatter): consolidate 3 independent fence-parsers into one (#139)
CI / Lint / Test / Vet (push) Successful in 32s
CI / Mirror to GitHub (push) Successful in 4s

Co-authored-by: Mathias <mthbqv@gmail.com>
This commit was merged in pull request #139.
This commit is contained in:
2026-09-23 13:07:32 +00:00
committed by mathias
parent c6341f020a
commit 3d5af85713
4 changed files with 121 additions and 38 deletions
+13 -19
View File
@@ -13,6 +13,7 @@ import (
"github.com/mathiasbq/hyperguild/ingestion/internal/capture"
"github.com/mathiasbq/hyperguild/ingestion/internal/claim"
"github.com/mathiasbq/hyperguild/ingestion/internal/classification"
"github.com/mathiasbq/hyperguild/ingestion/internal/frontmatter"
"github.com/mathiasbq/hyperguild/ingestion/internal/notes"
)
@@ -331,27 +332,20 @@ func checkClaimEnvelope(brainDir, path, oldStr, newStr string) error {
// frontmatterBlock returns the raw text between doc's opening and closing
// `---` fences, or ok=false if doc has no (properly closed) fence.
func frontmatterBlock(doc string) (block string, ok bool) {
// Round 4 found a note with CRLF fence lines ("---\r\n") silently
// defeats HasPrefix(doc, "---\n") on the precondition read — not an
// adversarial old_str/new_str, just an encoding difference — which
// makes checkClaimEnvelope no-op entirely. Round 5 found the same
// mechanism one layer earlier: a leading UTF-8 BOM (a live, plausible
// byte sequence — Notepad's default "UTF-8" save and PowerShell's
// pre-v6 Out-File/Set-Content both emit one) defeats the same prefix
// check before CRLF normalization even applies. Strip both, here, so
// every caller (hadFence/hasFence detection and frontmatterScalars)
// agrees regardless of encoding.
doc = strings.TrimPrefix(doc, "\xef\xbb\xbf")
doc = strings.ReplaceAll(doc, "\r\n", "\n")
if !strings.HasPrefix(doc, "---\n") {
// hyperguild#138: fence detection itself is frontmatter.Split, shared
// with internal/api and internal/frontmatter — the CRLF/BOM guards that
// used to live here (added piecemeal across rounds 4 and 5 of #123's
// review) are now the shared parser's job, so every caller inherits a
// fix instead of needing bug-for-bug parity edits.
b, _, ok := frontmatter.Split([]byte(doc))
if !ok {
return "", false
}
rest := doc[len("---\n"):]
end := strings.Index(rest, "\n---\n")
if end < 0 {
return "", false
}
return rest[:end], true
// Split's block keeps the "\n" immediately before the closing fence
// (by design — see its own doc comment); this function never included
// it. Split guarantees ok=true only when "\n---\n" matched, so this is
// always exactly one byte, never ambiguous.
return strings.TrimSuffix(string(b), "\n"), true
}
// frontmatterScalars extracts top-level "key: value" scalar pairs from