Skip to content

Explore: sidecar frontmatter files (in-file XOR sidecar) so .md stays clean #38

Description

@willkg

Status: scoping / feasibility. Captures the design and sharp edges for supporting markfluence metadata in a sidecar file instead of in-file frontmatter, so a markdown file (e.g. a project README) can stay pristine and remain viable outside Confluence while markfluence still tracks its page coordinates.

Motivation

Today create injects page_id/space/parent (and now title/page_width) into the .md frontmatter. For a README or other markdown meant to live on its own, that pollutes the file. Also, other tools that do things with frontmatter may interfere with markfluence. A sidecar lets markfluence persist coordinates beside the file, losing no information while keeping the .md clean.

Proposed model (from the design discussion)

  • Exactly one metadata location per file: in-file frontmatter XOR a sidecar. Having both is an error (strict).
  • Relax flag (escape hatch): read metadata solely from the sidecar and ignore any in-file frontmatter — for a file that legitimately carries foreign frontmatter (Jekyll/Hugo) alongside a markfluence sidecar. It does not delete the in-file block.
  • Sidecar file: <file>.md.meta.yaml, flat key: value YAML (no --- fences), canonical field order — reusing the existing frontmatter value quoting/parsing.
  • Write target: --frontmatter file|sidecar on create/update/fix. Omitted → write to the file's existing location, never migrate (avoids silently yanking a sidecar's metadata back into the .md). Passing it explicitly migrates: write the chosen location and remove metadata from the other.
  • Reads (create validate, update, fix, info, and the sibling scan) resolve the single location and error if both exist (unless the relax flag is set).

Feasibility — touch-points

  • internal/frontmatter: a location-aware loader (Load(filename) → metadata + which location, error on both), sidecar read/write, a "strip in-file block" op, and location-aware persist. MarkdownFile gains its metadata location + sidecar path; Body = the full .md when metadata is in the sidecar (so MdToConfluence, which uses md.Body/md.Filename, is unaffected).
  • Commands: --frontmatter flag + write-target routing in create/update/fix; read-path swap in info (and read).
  • internal/convert/links.go buildPageMap: the sibling scan reads each sibling .md's page_id/title; it must consult sidecars too. (buildAnchorMap only reads body headings, so it's unaffected.)

Sharp edges / open questions

  1. Whole block vs markfluence-keys-only: does the sidecar hold the entire frontmatter block (simplest; matches the either/or model), or only markfluence's keys (title/space/parent/page_id/page_width) so foreign keys can stay in-file? The latter abandons the clean either/or and needs a defined key set. The relax flag assumes the former (sidecar is authoritative, in-file ignored).
  2. Migration deletes the other location. --frontmatter sidecar strips the in-file block entirely — dangerous for a file with foreign frontmatter you want to keep. Presumably you'd use the relax flag instead of migrating in that case. Naming + exact semantics of the relax flag (read-only? affects write target?) TBD.
  3. Sibling ambiguity during conversion: if a sibling .md has both in-file frontmatter and a sidecar, does the whole create/update fail, or does buildPageMap skip/prefer? (It currently silently skips unreadable siblings.)
  4. Sidecar lifecycle: orphaned sidecars on .md delete/rename; copying a .md doesn't bring its sidecar; whether sidecars are committed / need a .gitignore note.
  5. read is stdout-only so sidecars don't apply there; the export subcommand (#37) would honor --frontmatter and is the natural place a sidecar gets written on the way out.
  6. Title for a pristine README: works today via create --title (the override flags already landed), e.g. create README.md --space ENG --title "..." --frontmatter sidecar leaves the README untouched and writes coordinates to README.md.meta.yaml.
  7. Back-compat: default behavior (no flag, in-file frontmatter) is unchanged.

Decision

Not committing to build this yet — this issue records the model and edges so we can decide whether the added complexity across the frontmatter core + every write path is worth it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions