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
- 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).
- 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.
- 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.)
- Sidecar lifecycle: orphaned sidecars on
.md delete/rename; copying a .md doesn't bring its sidecar; whether sidecars are committed / need a .gitignore note.
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.
- 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.
- 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.
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
createinjectspage_id/space/parent(and nowtitle/page_width) into the.mdfrontmatter. 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.mdclean.Proposed model (from the design discussion)
<file>.md.meta.yaml, flatkey: valueYAML (no---fences), canonical field order — reusing the existing frontmatter value quoting/parsing.--frontmatter file|sidecaroncreate/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.createvalidate,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.MarkdownFilegains its metadata location + sidecar path;Body= the full.mdwhen metadata is in the sidecar (soMdToConfluence, which usesmd.Body/md.Filename, is unaffected).--frontmatterflag + write-target routing increate/update/fix; read-path swap ininfo(andread).internal/convert/links.gobuildPageMap: the sibling scan reads each sibling.md'spage_id/title; it must consult sidecars too. (buildAnchorMaponly reads body headings, so it's unaffected.)Sharp edges / open questions
--frontmatter sidecarstrips 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..mdhas both in-file frontmatter and a sidecar, does the wholecreate/updatefail, or doesbuildPageMapskip/prefer? (It currently silently skips unreadable siblings.).mddelete/rename; copying a.mddoesn't bring its sidecar; whether sidecars are committed / need a.gitignorenote.readis stdout-only so sidecars don't apply there; the export subcommand (#37) would honor--frontmatterand is the natural place a sidecar gets written on the way out.create --title(the override flags already landed), e.g.create README.md --space ENG --title "..." --frontmatter sidecarleaves the README untouched and writes coordinates toREADME.md.meta.yaml.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.