Skip to content

ADR-0003: Non-Destructive Frontmatter Updates via YAML Document API

Status

Accepted

Context

PALEE stores metadata (mastery scores, SM-2 parameters, topic IDs, dependencies) inside Markdown frontmatter headers (---\n...\n---). Users frequently customize their notes with YAML comments, custom tags, custom properties (Dataview, Obsidian properties), and multi-line block scalars.

Standard YAML parsers (e.g. JSON.parse, basic string splitters, or naive YAML dumpers) reorder keys, strip comments, and format scalars destructively.

Decision

We utilize the yaml package's Document manipulation API in src/storage/frontmatter.ts.

Key design choices:

  1. Parse the extracted frontmatter block into a YAML Document using parseDocument(raw).
  2. Apply mutations only to explicit PALEE managed keys using doc.set(key, value).
  3. Call doc.toString() to serialize the updated YAML header while preserving:
    • Existing comments and indentation.
    • Key ordering and unknown custom metadata.
    • Exact Markdown body content byte-for-byte.

Consequences

  • Positive:
    • Guarantees 100% preservation of user-authored comments and custom frontmatter properties.
    • Seamless interoperability with Obsidian plugins, Dataview, and frontmatter schemas.
  • Negative / Tradeoffs:
    • Slight CPU overhead parsing YAML Documents compared to naive string matching, mitigated by memory caching.

Alternatives Considered

  1. gray-matter / js-yaml Full Serialization:

    • Description: Popular YAML frontmatter parser returning plain JavaScript objects and reserializing via js-yaml.dump().
    • Pros: Ubiquitous in JavaScript static site generators.
    • Why Rejected: Deserializes frontmatter into a plain JS object and reserializes via js-yaml.dump(). This strips user-authored YAML comments, reformats multi-line strings destructively, reorders properties, and modifies whitespace in the Markdown body.
  2. Regular Expression / Naive Substring Replacement:

    • Description: Finding and replacing specific keys like topic_mastery: 0.8 using regex substitution.
    • Pros: Minimal CPU overhead.
    • Why Rejected: Extremely brittle when handling multi-line lists (e.g. depends_on: [A, B]), nested YAML objects, inline comments, or escaped characters.
  3. Sidecar JSON Metadata Files (.palee/metadata/<id>.json):

    • Description: Keeping notes 100% pure markdown and storing all PALEE metadata in sidecar files.
    • Pros: Zero frontmatter modifications to user notes.
    • Why Rejected: Breaks user visibility and interoperability with Obsidian plugins (Dataview, Breadcrumbs, Properties view) that rely on standard note frontmatter.

Released under the MIT License.