fix(vault): parse block-style YAML lists in frontmatter (not just inline arrays)#142
Conversation
…ine arrays) parseFrontmatterText only recognised inline arrays (tags: [a, b]); block sequences (tags:\n - a\n - b) emitted by Obsidian's Properties editor and several writers were silently dropped. That made required list fields such as tags vanish, so o2b brain doctor reported spurious 'signal missing field: tags' errors on otherwise-valid sig-*.md files. Teach the parser to assemble dash-item blocks under an empty-value key into an array, while preserving the original empty-string behaviour for genuine null scalars (key: with no following dash list). Inline arrays are unchanged. Adds regression tests covering block lists, quoted items, null-scalar disambiguation, and interleaving with inline arrays.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough
ChangesFrontmatter parsing
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/core/vault.ts`:
- Line 130: Update the lookahead condition around nextMeaningful to use the
newly extracted module-level regex instead of calling isDashItem. Preserve the
existing null guard and ensure the regex check maintains the same dash-item
detection behavior.
- Around line 93-105: Update the list-item matcher used by the vault parser to
accept an isolated “-” after trimming while continuing to reject dash-prefixed
strings without whitespace, such as “-foo”. Move this relaxed regular expression
to module scope and reuse it from the parsing loop instead of recreating the
arrow-function matcher on each parse; preserve existing item extraction and
quote-stripping through the blockKey metadata path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0d6fbf03-3217-4869-bf3e-d65d033f0de8
📒 Files selected for processing (2)
src/core/vault.tstests/core/vault.test.ts
|
Thank you for this fix, it is genuinely useful. Block-style lists are exactly what Obsidian's Properties editor emits, so this closes a real gap between the Obsidian-native promise and what the parser actually accepted, and the disambiguation of a bare One request: could you take a look at the two CodeRabbit comments when you get a chance? They point at a real edge case - a bare |
Per CodeRabbit review on itechmeat#142: a bare '-' (or '- ') list item failed the dash regex (which required >=1 space) and silently reset blockKey, dropping the remainder of the block list. Move the matcher to module scope as DASH_ITEM_RE = /^-(?:\s+(.*))?$/ (matches '- foo', '- ', and bare '-' but NOT '-foo', which has no whitespace), and push dash[1] ?? '' so empty items surface as '' instead of aborting the list. Update the lookahead to reuse DASH_ITEM_RE. Adds regression tests: empty-item is not dropped, and '-foo' is not parsed as a list item.
|
Grateful for the review and the approval. Both CodeRabbit points are now addressed in commit 496b785: • Empty list items no longer silently dropped. The dash matcher is now module-scoped as DASH_ITEM_RE = /^-(?:\s+(.*))?$/, which matches - foo, - , and a bare -,while still rejecting -foo (no whitespace → not a list item). Empty items surface as "" rather than resetting blockKey and aborting the rest of the list. Added two regression tests: an empty-item block list is not dropped (["brain", "", "brain/signal"]), and a dash-prefixed string without whitespace (-foo) is correctly not treated as a list item. All suites green: 31 in vault.test.ts, 66 across signal/doctor, plus tsc --noEmit and the formatter. Ready for re-review / merge. Great repo ... enjoying it ... many thanks! Chris N. (lymeswold), London, UK. |
|
Thanks for the feedback and the kind words, Chris @lymeswold! Really glad you're enjoying the repo. |
|
Just a heads up on CI: the validate job is currently red, but it's not from my change. The only failing test is temporalReplace logs and temporal-replace event (tests/core/brain/lifecycle/temporal-replace.test.ts:127 — expect(events.length).toBe(1), received 0). My PR only touches src/core/vault.ts and tests/core/vault.test.ts; the failing test lives in temporal-replace.ts, which doesn't import from vault.ts. More telling: main itself is red on the same job (run 29675080196, same 4b8100c head) with the identical failure - so it's a pre-existing breakage on main, independent of this branch (6357/6358 pass here). I've left that test alone since it's outside scope and you own. But happy to rebase / force-push once main is green, or fold in a fix if you'd prefer it to travel with this PR. |
Problem
o2b's frontmatter reader,parseFrontmatterTextinsrc/core/vault.ts, only recognises inline YAML arrays:It has no code path for block-style (standard YAML) lists:
The
tags:line has an empty value and the- brainlines are notkey: valuepairs, so the key is silently skipped. The parser returns notags, and the signal validator then throwssignal missing field: tags— even though the field is plainly present.When it occurs
Any time a
Brain/**/*.mdfile uses block-style lists. Concrete triggers:In practice this produces a swarm of
signal-invaliderrors fromo2b brain doctoron otherwise-valid files.Reproduction (before fix)
Create
Brain/inbox/sig-test.md:o2b brain doctorreportssignal-invalid: signal missing field: tags.Fix
Teach the parser to assemble dash-item blocks under an empty-value key into an array, while preserving the original empty-string behaviour for a genuine null scalar (
key:with no following dash list, so it still round-trips as""). Inline-array parsing is unchanged — purely additive.Test coverage (tests/core/vault.test.ts)
key:not followed by a dash list stays an empty string (disambiguation — the tricky part).All existing
vault/signal/doctorsuites pass (29 in vault.test.ts; 66 across signal+doctor). Formatter hook (oxfmt) satisfied.Prior art check
No existing issue or PR covers this: searching upstream issues for
"signal missing field"andparseFrontmatter blockreturns 0 results, and the recent frontmatter PRs (#139 write-path integrity, #132 ingestion robustness, #76 write-time governance) do not touchparseFrontmatterText's block-sequence limitation.Summary by CodeRabbit
key:followed by- itementries) and convert them into arrays./or commas, and kept quoted/comma-containing entries intact.