Closes#128, filed while fixing the same bug class in the sibling internal/wiki package (#126, review round 3). This package (internal/api/frontmatter.go) was the original the reimplementation was modeled on and had the same gap.
Bug 1 — indented list items misparsed as keys
An indented YAML block-list item containing a colon (e.g. a title with a subtitle inside an aliases: list) gets cut at the colon and treated as a fake top-level key, since strings.Cut doesn't know a line is a continuation just because TrimSpace(key) happens to be non-empty. Re-rendering strips the item's indentation, de-indenting it out of its list. This package backs UpdateNote/writeHallNote — live on the brain_update write path.
Fixed the same way as the wiki package: check indentation before calling Cut at all.
Bug 2 — found via this fix's own test fixture, not speculative
render() wrote "key: " (trailing space) for a key with an empty value (the same aliases: block-list header) instead of bare "key:" — a different byte sequence than the original, breaking this type's documented purpose as a byte-preserving line view. No current caller sets an empty-value key via set(), so it hadn't shown up on a set()-driven path, but it fires on every ordinary parse-then-render round-trip of a note that already has one — confirmed directly in this fix's own test output before writing the fix.
Verification
task check exit 0. Both fixes flip-tested independently — disabling either turns its corresponding test red, the other stays green. Full module suite green.
Closes #128, filed while fixing the same bug class in the sibling `internal/wiki` package (#126, review round 3). This package (`internal/api/frontmatter.go`) was the original the reimplementation was modeled on and had the same gap.
## Bug 1 — indented list items misparsed as keys
An indented YAML block-list item containing a colon (e.g. a title with a subtitle inside an `aliases:` list) gets cut at the colon and treated as a fake top-level key, since `strings.Cut` doesn't know a line is a continuation just because `TrimSpace(key)` happens to be non-empty. Re-rendering strips the item's indentation, de-indenting it out of its list. This package backs `UpdateNote`/`writeHallNote` — live on the `brain_update` write path.
Fixed the same way as the `wiki` package: check indentation before calling `Cut` at all.
## Bug 2 — found via this fix's own test fixture, not speculative
`render()` wrote `"key: "` (trailing space) for a key with an empty value (the same `aliases:` block-list header) instead of bare `"key:"` — a different byte sequence than the original, breaking this type's documented purpose as a byte-preserving line view. No current caller sets an empty-value key via `set()`, so it hadn't shown up on a `set()`-driven path, but it fires on every ordinary parse-then-render round-trip of a note that already has one — confirmed directly in this fix's own test output before writing the fix.
## Verification
`task check` exit 0. Both fixes flip-tested independently — disabling either turns its corresponding test red, the other stays green. Full module suite green.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---
_Created via git-mcp on behalf of @mathias_
hyperguild#128, filed while fixing the same bug class in the sibling
internal/wiki package's parseFrontmatterBlock (hyperguild#126 review
round 3). This package was the original parseFrontmatter/set()/render()
shape that reimplementation was modeled on, and had the same gap: an
indented YAML block-list item containing a colon (e.g. a title with a
subtitle inside an aliases: list) got cut at the colon and treated as a
fake top-level key, since strings.Cut doesn't know a line is a
continuation just because TrimSpace(key) happens to be non-empty.
Re-rendering then stripped the item's indentation, de-indenting it out
of the list it belongs to. This package backs UpdateNote/writeHallNote,
so it's on the live brain_update write path.
Fixed the same way as the wiki package: check indentation before
calling Cut at all -- a line starting with a space or tab is a
continuation, full stop, regardless of what it contains.
Second, related bug found via the fix's own test fixture, not
speculatively: render() wrote "key: " (trailing space) for a key with
an empty value (the same "aliases:" block-list header line) instead of
bare "key:" -- a different byte sequence than the original, breaking
this type's whole documented purpose (a byte-preserving line view, not
a YAML re-marshal). No current caller sets an empty-value key via
set(), so this hadn't shown up in a set()-driven path, but it fires on
every ordinary parse-then-render round-trip of a note that already has
one -- confirmed directly via this fix's own test output before writing
a fix for it.
Flip-tested both independently: disabling either fix turns its
corresponding test red, the other stays green.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mathias
merged commit 7ed2a7d3b4 into main2026-09-23 08:12:06 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #128, filed while fixing the same bug class in the sibling
internal/wikipackage (#126, review round 3). This package (internal/api/frontmatter.go) was the original the reimplementation was modeled on and had the same gap.Bug 1 — indented list items misparsed as keys
An indented YAML block-list item containing a colon (e.g. a title with a subtitle inside an
aliases:list) gets cut at the colon and treated as a fake top-level key, sincestrings.Cutdoesn't know a line is a continuation just becauseTrimSpace(key)happens to be non-empty. Re-rendering strips the item's indentation, de-indenting it out of its list. This package backsUpdateNote/writeHallNote— live on thebrain_updatewrite path.Fixed the same way as the
wikipackage: check indentation before callingCutat all.Bug 2 — found via this fix's own test fixture, not speculative
render()wrote"key: "(trailing space) for a key with an empty value (the samealiases:block-list header) instead of bare"key:"— a different byte sequence than the original, breaking this type's documented purpose as a byte-preserving line view. No current caller sets an empty-value key viaset(), so it hadn't shown up on aset()-driven path, but it fires on every ordinary parse-then-render round-trip of a note that already has one — confirmed directly in this fix's own test output before writing the fix.Verification
task checkexit 0. Both fixes flip-tested independently — disabling either turns its corresponding test red, the other stays green. Full module suite green.Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Created via git-mcp on behalf of @mathias