Skip to content

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Jan 4, 2026

Summary

Adds a new crates/frontmatter crate that provides a generic Document<T> type for parsing and serializing markdown files with YAML frontmatter. The crate supports native serde integration, allowing Document<T> to be embedded in other serde formats (JSON, YAML, etc.).

This consolidates the frontmatter handling pattern currently duplicated across granola, hooks, and deeplink2 plugins.

API:

// Parse markdown with frontmatter
let doc: Document<MyStruct> = Document::from_str(markdown)?;

// Serialize back to markdown
let markdown = doc.to_string()?;

// Works with HashMap for dynamic frontmatter
let doc: Document<HashMap<String, String>> = Document::from_str(markdown)?;

Key feature: YAML keys are automatically sorted alphabetically (recursively for nested structures) to ensure deterministic output regardless of insertion order.

Updates since last revision

  • Added consistent alphabetical sorting for all YAML frontmatter keys
  • Sorting is recursive (nested maps are also sorted)
  • Added 2 new tests (test_sorted_keys, test_nested_sorted_keys) - now 12 tests total

Review & Testing Checklist for Human

  • Verify struct field ordering is acceptable: The sorting converts to serde_yaml::Value before serializing, which means struct fields are also sorted alphabetically (e.g., {title, tags} becomes {tags, title}). If preserving struct field declaration order matters, this needs adjustment.
  • Verify output format compatibility: The serialization outputs ---\n{yaml}---\n\n{content}. Existing code uses format!("---\n{}---\n", yaml) (single newline after closing delimiter). Confirm this difference is acceptable.
  • Test roundtrip with real-world content: The parsing logic strips leading whitespace and handles newlines in specific ways. Verify content with multiple paragraphs, trailing newlines, etc. roundtrips correctly.
  • Empty frontmatter edge case: When frontmatter is empty (---\n---), it parses "{}" which works for HashMap/Default types but may fail for required-field structs.

Suggested test plan:

  1. Run cargo test -p frontmatter (12 tests pass)
  2. Try parsing some existing exported markdown files from granola to verify compatibility
  3. Verify the alphabetical key ordering meets your needs for diffs/version control

Notes

  • This PR adds the crate but does not refactor existing consumers (granola, hooks, deeplink2) to use it - that can be a follow-up
  • CRLF line endings and UTF-8 BOM are not explicitly handled

Link to Devin run: https://app.devin.ai/sessions/37d3adedfd034b8896917d3f15bda735
Requested by: yujonglee (@yujonglee)

@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for hyprnote canceled.

Name Link
🔨 Latest commit b381db2
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6959fe265d489b0008ef4bd3

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for hyprnote-storybook canceled.

Name Link
🔨 Latest commit b381db2
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6959fe2631b48600084e9106

@netlify
Copy link

netlify bot commented Jan 4, 2026

Deploy Preview for howto-fix-macos-audio-selection canceled.

Name Link
🔨 Latest commit b381db2
🔍 Latest deploy log https://app.netlify.com/projects/howto-fix-macos-audio-selection/deploys/6959fe26abcf15000857c786

@yujonglee yujonglee merged commit 12c195c into main Jan 4, 2026
26 of 27 checks passed
@yujonglee yujonglee deleted the devin/1767503622-frontmatter branch January 4, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants