AtomiCloud's markdown resolver
Merges multiple Markdown files that conflict on the same path by splitting each file on H1 (# ) boundaries, resolving conflicts per section, then reassembling. Each H1 section is treated as an independent unit — sections unique to one file are always included, while sections shared across multiple files are resolved using a configurable strategy.
| Key | Type | Default | Description |
|---|---|---|---|
sectionOrder |
string | alphabetical |
Strategy for ordering sections in the final output. See Strategies for options. |
contentOrder |
string | lowest-layer-first |
Strategy for resolving conflicts when multiple files define the same H1 section. |
Both sectionOrder and contentOrder accept one of four values:
| Value | Behavior |
|---|---|
alphabetical |
Sort by section header name (A–Z) or content text (A–Z) |
reverse-alphabetical |
Sort by section header name (Z–A) or content text (Z–A) |
lowest-layer-first |
Sort by origin layer ascending (layer 0 before layer 1) |
highest-layer-first |
Sort by origin layer descending (layer 1 before layer 0) |
For sectionOrder, the sort key is the section header name (or layer for layer-based strategies). For contentOrder, the sort key is the section's content text (or layer for layer-based strategies).
- Parse — Each input file is split on H1 lines (
# ...). Text before the first H1 becomes a preamble section with an empty header. - Deduplicate — Sections with the same header are grouped. Unique sections are kept as-is.
- Resolve conflicts — When a header appears in multiple files, all content blocks from all versions are concatenated (separated by blank lines) and sorted based on
contentOrder. Content is never dropped. Ties are broken by layer ascending, then template name ascending. - Order — All resolved sections are sorted based on
sectionOrder. - Reconstruct — Sections are concatenated with blank lines between them. Trailing whitespace is normalized per line.
- Single file passthrough — If only one input file is provided, it is returned as-is with trailing whitespace normalized.
- Content before first H1: Stored as preamble section (empty header), participates in ordering and conflict resolution.
- Empty content section: Header-only sections are preserved.
- No H1 at all: Entire file is treated as preamble.
- All files empty: Returns empty string.
CyanPrint may invoke the resolver with files in any order. The resolver produces identical output regardless of input ordering by:
- Sorting inputs before processing (layer ascending, then template name ascending) to establish a deterministic processing order.
- Deterministic conflict resolution: When multiple files contribute the same section, all content blocks are concatenated and sorted per
contentOrder; ties are broken by layer then template name. - Deterministic section ordering: The
sectionOrderstrategy sorts all sections into a final order independent of input sequence. - Unique section deduplication: Each header appears exactly once in the output.
Input files (all sharing the same path):
| Origin Template | Origin Layer | Content |
|---|---|---|
| template-a | 0 | # Alpha\n\nFrom A. |
| template-b | 1 | # Beta\n\nFrom B. |
Config:
sectionOrder: alphabetical
contentOrder: lowest-layer-firstResolved output:
# Alpha
From A.
# Beta
From B.
Both sections are unique, so both are included. Ordered alphabetically by header.
Input files (all sharing the same path):
| Origin Template | Origin Layer | Content |
|---|---|---|
| template-a | 0 | # Overview\n\nAlpha version. |
| template-b | 1 | # Overview\n\nBeta version. |
Config:
sectionOrder: alphabetical
contentOrder: lowest-layer-firstResolved output:
# Overview
Alpha version.
Beta version.
Both files define # Overview. With contentOrder: lowest-layer-first, both content blocks are concatenated, ordered by layer ascending (layer 0 first, layer 1 second).
Input files (all sharing the same path):
| Origin Template | Origin Layer | Content |
|---|---|---|
| template-a | 0 | # Introduction\n\nIntro from A.\n\n# Setup\n\nSetup A. |
| template-b | 1 | # Introduction\n\nIntro from B.\n\n# Config\n\nCfg B. |
Config:
sectionOrder: alphabetical
contentOrder: lowest-layer-firstResolved output:
# Config
Cfg B.
# Introduction
Intro from A.
Intro from B.
# Setup
Setup A.
# Introduction appears in both files — both content blocks are concatenated, ordered by layer ascending. # Config and # Setup are unique. Sections ordered alphabetically.
Reference this resolver in a template's cyan.yaml:
resolvers:
- resolver: atomi/md:1
config:
sectionOrder: alphabetical
contentOrder: lowest-layer-first
files: ['**/*.md']