Skip to content

Markdown formatting tool — Obsidian-safe formatter choice

Status: open/accepted

  • rumdl (Rust linter-formatter, ruff model) is the markdown formatter — the only surveyed tool whose table formatting (MD060) is opt-in and off by default, satisfying the Standard’s hand-formatted-tables rule.
  • One tool covers the whole formatter track: MD013 reflow = true wraps prose at 100 columns and the MD040 fix tags bare fences text — no custom wrap script needed.
  • Prettier and dprint stay disqualified (unavoidable table column-padding); the markdownlint-cli2 + wrap-script hybrid is the specified fallback; dprint a tertiary watch.
  • Empirically verified: byte-identical fixture round-trip; forensic full-corpus pass (660 files) with wikilink/embed multiset, tables, and frontmatter byte-identical; idempotent second pass.
  • Accepted caveats: two narrow reflow bugs (embed-glue — zero corpus instances; anchor displacement — three lines hand-fixed before the reformat), tracked for upstream filing via B-1ATA.
  • T-0008 wired it (PR #356): pinned bunx rumdl@0.2.9 in the lefthook markdown-fmt step, justfile recipes, and the /markdown-format skill; check + fixture-pin quality gates; and the one-time corpus reformat (~7,000 fixes across 433 files).

D-0008-markdown-standard settled the rule set: prose wrapped at 100 columns, ATX headings, - bullets, explicit ordered lists, code fences with language tags, tables hand-formatted and never reflowed, frontmatter preserved byte-for-byte by the formatter (schema validator owns frontmatter shape). Obsidian features — wikilinks (), block-ids (^anchor), transclusions (file), frontmatter as Properties — MUST survive the formatter without byte-level change to their syntax.

T-0007 tasked this ADR with selecting the tool that enforces those rules mechanically and slots into the lefthook pre-commit gate already used by the project. T-0008 wired the chosen tool; this ADR set the choice so T-0008 could act without re-deciding.

AxisWhat it measures
Formats vs lintsWrites files (formatter) or only diagnoses (linter)
Frontmatter safetyDoes it alter YAML frontmatter — quote style, list indentation, key order?
Wikilink safetyDoes text survive byte-for-byte?
Transclusion safetyDoes file survive? Line merging is a dealbreaker.
Block-id safetyDoes line-terminal ^anchor survive?
Table handlingDoes it reflow or pad table columns? (dealbreaker: must leave tables untouched)
Bun/CI fitInstallable via Bun/npx without Node-isms; CI-clean
Pre-commit perfSingle-file ≤500ms; full corpus ≤30s
MaintenanceActive releases as of 2026
ConfigurabilityCan it express all formatter-track rules from D-0008-markdown-standard?

Recommendation: rumdl — a Rust linter-formatter (the ruff model) whose table rule (MD060) is opt-in and ships disabled, making it the only surveyed formatter that leaves tables untouched by default. Prettier and dprint remain disqualified: both column-pad tables on every write with no disable option (Prettier 3.8.3, dprint markdown plugin 0.22.1 — confirmed empirically; not a config gap).

rumdl covers the entire formatter track in one binary, including the two rules markdownlint cannot auto-fix: prose wrap at 100 columns (MD013 with reflow = true) and code-fence language tags (its MD040 fix inserts text, the Standard’s fallback tag). No companion script is needed. Config under Recommendation detail; empirical evidence under Options considered.

Two reflow caveats are accepted and tracked (detail in the rumdl section): an embed sharing a paragraph with over-100-column prose is glued into the reflowed text (zero corpus instances), and a line-terminal ^anchor on an over-100-column line is displaced to a continuation line (three corpus instances — hand-fixed before the one-time reformat). Both are upstream-reportable bugs in a project with a fast fix record.

Runner-up: markdownlint-cli2 --fix plus a custom Bun prose-wrap script as two sequential lefthook commands. Kept fully specified under Recommendation detail as the fallback — rumdl uses markdownlint’s rule numbering and imports markdownlint configs, so the exit ramp is cheap if rumdl’s single-maintainer pre-1.0 risk materializes.

dprint stays a tertiary watch: revisit if dprint/dprint-plugin-markdown#180 ships a table-preserve option.

The core constraint from D-0008-markdown-standard is that tables MUST NOT be reflowed. Both dominant formatters violate this:

  • Prettier column-pads tables on every format pass. There is no config option to skip table alignment (issue #12074, open since 2022, no resolution).
  • dprint pads columns identically. "textWrap": "maintain" governs prose wrapping, not table formatting; no table-disable option exists (issue #180, open).

A tool with no table-untouched mode cannot be the primary formatter. rumdl inverts the default — table formatting is opt-in, off out of the box — so the dealbreaker axis passes without configuration tricks. One tool covering lint, format, and prose wrap also removes the custom-script maintenance surface the hybrid would have carried: the reason the Custom-script option is rejected below applies to the hybrid’s wrap script too, just at smaller scale.

Cross-tool summary; per-tool score tables with evidence follow.

ToolFormats vs lintsFrontmatterWikilinksTransclusionsBlock-idsTablesBun/CI fitMaintenanceVerdict
rumdl 0.2.8Both (lint + fmt)PassPassPass*Acceptable*Strong (untouched)Strong (bunx)Acceptable (pre-1.0, fast cadence)Recommended
Prettier 3.8.3FormatterFAILPassFAILPassFAILStrongStrongDisqualified
dprint 0.54.0FormatterPassPassPassPassFAILAcceptableStrongDisqualified (tertiary watch)
markdownlint-cli2 0.22.0Linter + limited fixStrongStrongStrongStrongStrong (no reflow)StrongStrongFallback (hybrid component)
mdformat 1.0.0FormatterAcceptable (plugin)Acceptable (plugin)UnknownUnknownPoorPoor (Python)AcceptableRejected
remark-cli 12Formatter (plugins)Acceptable (plugin)Acceptable (plugin)UnknownUnknownUnknownAcceptableAcceptableRejected

* rumdl reflow caveats (zero and three corpus instances respectively): see the rumdl section’s Transclusion and Block-id rows.

AxisScoreNotes
Formats vs lintsFormatterWrites files
Frontmatter safetyFAILRewrites YAML quote style ('5'"5"), re-indents block lists with 2-space indent prefix, moves list items. Verified empirically.
Wikilink safetyPass preserved in prose; merged PR #9275 (2020) protects them from wrap-splits. Confirmed in round-trip test.
Transclusion safetyFAILCollapses adjacent lines on separate lines into one space-separated line. Breaks Obsidian transclusion. Confirmed empirically.
Block-id safetyPass^anchor at line-end preserved.
Table handlingFAILColumn-pads and aligns all pipe tables. No option to disable. Issues #12074, #17148 both open. Confirmed empirically.
Bun/CI fitStrongbunx prettier works; no Node-isms needed.
Pre-commit perfStrong5 files: ~200ms; 399-file corpus: ~3.9s.
MaintenanceStrongv3.8.3 released April 2026; actively maintained.
ConfigurabilityPoorCannot leave tables untouched; cannot skip frontmatter YAML re-serialization.

Verdict: Disqualified. Three dealbreaker failures: table alignment, frontmatter mutation, transclusion line-collapse.

Empirical evidence — Prettier round-trip

Section titled “Empirical evidence — Prettier round-trip”

Test file had YAML frontmatter with schema_version: '5' and 'M-0001' in a block list, two transclusion lines, and a pipe table. After bunx prettier --write with proseWrap: always, printWidth: 100:

schema_version: '5'
schema_version: "5"
created: '2026-05-30'
created: "2026-05-30"
- test
- test
- '`M-0001`'
- "`M-0001`"
`D-0008-markdown-standard`
`S-0007-markdown-formatting`
`D-0008-markdown-standard` `S-0007-markdown-formatting`
| Tool | Stack | Notes |
|---|---|---|
| Tool | Stack | Notes |
| ----- | ----- | -------------- |

Frontmatter quote style changed, block-list indentation shifted, two transclusion lines collapsed to one, table column-padded.

dprint 0.54.0 + markdown plugin 0.22.1 (Rust)

Section titled “dprint 0.54.0 + markdown plugin 0.22.1 (Rust)”
AxisScoreNotes
Formats vs lintsFormatterWrites files
Frontmatter safetyPassYAML frontmatter passed through byte-for-byte; quote styles and block-list style preserved. Confirmed empirically.
Wikilink safetyPass preserved in prose. textWrap: maintain does not split wikilinks. Confirmed empirically.
Transclusion safetyPassAdjacent lines preserved on separate lines. Confirmed empirically.
Block-id safetyPassLine-terminal ^anchor preserved.
Table handlingFAILColumn-pads and aligns pipe tables, identical behavior to Prettier. No disable option. Issue #180 open. Confirmed empirically.
Bun/CI fitAcceptablebrew install dprint or download binary; not npm-native. Requires separate install step in CI.
Pre-commit perfStrong5 files: ~35ms; 399-file corpus: ~104ms. 10–40× faster than Prettier.
MaintenanceStrongv0.22.1 released May 2026; Rust-compiled, stable release cadence.
ConfigurabilityAcceptabletextWrap: maintain preserves prose wrapping; lineWidth sets target. Cannot disable table formatting.

Verdict: Disqualified on the table axis alone. All Obsidian features (frontmatter, wikilinks, transclusions, block-ids) survive intact — dprint is the closest runner-up. If issue #180 ships a "tables": "preserve" option, dprint becomes the recommended tool and this ADR should be revisited.

Same test file as Prettier test. After dprint fmt with textWrap: maintain, lineWidth: 100:

← blank line added after frontmatter (harmless)
| Tool | Stack | Notes |
|---|---|---|
| Tool | Stack | Notes |
| ----- | ----- | -------------- |

Only change beyond the blank line: table column-padding. Frontmatter untouched, wikilinks untouched, transclusion lines preserved on separate lines, block-ids preserved.

A Rust linter-formatter (rumdl.dev) on the ruff model — all 53 markdownlint rules plus 22 more, most auto-fixable via rumdl fmt, TOML config, markdownlint config import, and a built-in obsidian flavor.

AxisScoreNotes
Formats vs lintsBothLinter with auto-fix (rumdl fmt / check --fix); one tool covers both roles of the hybrid.
Frontmatter safetyPassByte-for-byte (quote styles, block-list indentation) with MD071 (blank-line-after-frontmatter) disabled. Confirmed empirically.
Wikilink safetyPassWikilink/embed multiset across all 660 repo .md files byte-identical after 7,075 fixes. Reflow wraps around , never splits; an unwrappable aliased-link line is flagged, not mangled. Confirmed empirically.
Transclusion safetyPassAdjacent standalone lines preserved (the Prettier failure). Caveat: an embed in the same paragraph as over-100-column prose is glued into the reflowed text with no space — zero corpus instances; upstream-reportable. Confirmed empirically.
Block-id safetyAcceptableAnchor syntax never altered. Reflow displaced 3/46 line-terminal ^summary anchors (on >100-column lines: C-0005, C-0007, P-0003) to continuation lines — Obsidian needs end-of-line placement for paragraphs. Hand-fix three lines before the corpus reformat.
Table handlingStrongMD060 (table format) is opt-in, default-off — corpus tables byte-identical including |---| delimiters. Only formatter surveyed to pass this axis. Confirmed empirically.
Bun/CI fitStrongnpm-native: bunx rumdl works; also brew / cargo / pip / static binary. (rumdl init is interactive — avoid in CI.)
Pre-commit perfStrong660-file corpus: check 264ms cold / 58ms warm (incremental cache), full fmt 631ms — internal timings; ~1–1.7s wall through the bunx shim. Single file ~25ms.
MaintenanceAcceptablePre-1.0 (0.2.8; 0.2.9 shipped the same day), single maintainer, very fast cadence. The reflow-collapse bug family (#597–#600, #639) is all closed; #235 added wikilink-awareness to link rules. markdownlint rule numbering + rumdl import keep the fallback cheap.
ConfigurabilityStrongExpresses every formatter-track rule, including prose wrap (MD013 reflow) and the MD040 auto-fix (inserts text; 447 corpus instances). Bonus: MD057 relative-link validation (71 corpus hits) covers the broken-link capability the Standard deferred. Two default-config traps must be set — see the config sketch.

Verdict: Recommended. Only formatter that passes the table axis; only tool covering the full formatter track without a companion script. Two narrow reflow wounds (embed-glue, anchor displacement) have zero and three corpus instances respectively.

Same synthetic Obsidian-features fixture as the Prettier and dprint tests. After rumdl fmt with the config sketch under Recommendation detail, the round-trip is byte-identical — frontmatter, wikilinks, adjacent transclusion lines, block-id, and table all untouched. The only clean round-trip of the four formatters tested.

Beyond the original methodology, a forensic full-corpus pass (rumdl fmt over a git-baselined /tmp/ copy of all 660 tracked .md files, 2026-06-06):

  • Wikilink/embed multiset before vs after: byte-identical (7,075 fixes applied across ~500 files).
  • Tables and frontmatter: byte-identical.
  • Block anchors: 3 line-terminal ^summary anchors displaced (the three >100-column lines named above); all others untouched.
  • Reflow stress fixture: wikilinks wrapped around, never split; embed-in-paragraph glue bug reproduced (absent from the corpus).
  • Default-config traps caught and neutralized: MD025’s front-matter-title default reads frontmatter title: plus the required body H1 as two H1s and demoted headings in 116 files (front-matter-title = "" fixes it; markdownlint ships the same default — see the fallback config); MD046’s consistent default converted one fence to an indented block (style = "fenced" fixes it).
  • A multi-line paragraph containing any over-100-column line is rewrapped as a unit, so deliberate one-field-per-line layouts (machine-written dedup-search blocks) merge into flowing prose. Renders identically; source-line layout is lost.
  • Idempotent: a second fmt pass produced zero changes.
AxisScoreNotes
Formats vs lintsLinter + limited fix--fix corrects auto-fixable rules; does NOT reflow prose or touch tables.
Frontmatter safetyStrongTreats frontmatter as opaque; does not re-serialize.
Wikilink safetyStrongLinter does not parse or transform wikilinks; they pass through.
Transclusion safetyStrongSame — no AST transform of syntax.
Block-id safetyStrongNo transformation of ^anchor.
Table handlingStrongDoes not reflow tables. Lint rules (MD055, MD056) flag structural issues without rewriting.
Bun/CI fitStrongbunx markdownlint-cli2; npm-native; no extra install.
Pre-commit perfStrongLint-only is fast; ~100–200ms for a typical change set.
MaintenanceStrongv0.22.0 on Arch Linux as of 2026-06; 126k weekly npm downloads; healthy release cadence.
ConfigurabilityAcceptableCovers: ATX headings (MD003), list markers (MD004), ordered list style (MD029), code fence language (MD040 — flag-only: cannot insert a tag, and the corpus has 447 bare fences), trailing whitespace (MD009), final newline (MD047). Cannot auto-wrap prose at 100 columns — that requires a formatter or separate script. Needs MD025.front_matter_title: "" — frontmatter title: plus the required body H1 otherwise flags every entity file (confirmed empirically via rumdl’s identical default).

Verdict: Passes all Obsidian-feature safety axes. Cannot auto-format prose line wrapping without a companion formatter. Primary component of the fallback hybrid.

AxisScoreNotes
Formats vs lintsFormatterWrites files
Frontmatter safetyAcceptable (plugin)Requires mdformat-frontmatter plugin (v2.1.2, May 2026); without it, frontmatter is parsed as body and mangled.
Wikilink safetyAcceptable (plugin)Requires mdformat-wikilink plugin (Oct 2025); core mdformat escapes to \[\[…\]\].
Transclusion safetyUnknownNo confirmed plugin covers file transclusion form; mdformat_obsidian 0.1.0 exists but is alpha-quality.
Block-id safetyUnknownNo confirmed handling of line-terminal ^anchor.
Table handlingPoorRequires mdformat-gfm for tables; behavior with mixed-width cells (wikilinks in cells) is not verified.
Bun/CI fitPoorPython/pip dependency in a Bun/TypeScript project. Adds a uv or pip step to CI.
Pre-commit perfAcceptablePython startup adds 300–600ms per run.
MaintenanceAcceptable1.0.0 released Oct 2025; plugin ecosystem fragmented across multiple PyPI packages.
ConfigurabilityPoorPlugin composition required for every Obsidian feature; each plugin is a separate maintenance surface.

Verdict: Too many unknowns on Obsidian transclusion and block-id handling. Python dependency is a poor fit for this Bun/TypeScript project. Rejected.

remark-cli 12 / remark-stringify (Node / unified.js)

Section titled “remark-cli 12 / remark-stringify (Node / unified.js)”
AxisScoreNotes
Formats vs lintsFormatter (with plugins)remark --output writes files; remark-stringify serializes the AST.
Frontmatter safetyAcceptable (plugin)Requires remark-frontmatter; without it, frontmatter is parsed as body.
Wikilink safetyAcceptable (plugin)Requires remark-wiki-link or remark-obsidian; core remark parses as text and may escape it.
Transclusion safetyUnknownNo well-maintained plugin covering file verified; community plugins are early-stage.
Block-id safetyUnknownNot covered by any documented plugin.
Table handlingUnknownremark-gfm adds table support; reflow behavior depends on stringify settings.
Bun/CI fitAcceptablenpm-native but requires assembling a multi-plugin pipeline.
Pre-commit perfAcceptable200–400ms for a typical change set.
MaintenanceAcceptableremark-cli 12.0.0 (Sep 2023); ecosystem is large but plugin quality varies. Core is stable; Obsidian plugins are niche.
ConfigurabilityHigh (in theory)Can author custom plugins; high ceiling, high floor. Custom plugin cost is the maintenance surface of a custom solution.

Verdict: Obsidian transclusion and block-id safety unconfirmed. Plugin assembly cost is high. Rejected in favor of markdownlint-cli2 whose Obsidian-safety profile is empirically verified.

Verdict: Maximum control but maximum maintenance cost. Rejected outright: reinvents what linters already provide, and the “maintenance overhead” axis is the primary reason to use a tool rather than write one.

One lefthook pre-commit command; no custom script.

.rumdl.toml (live at the repo root; empirically tested against the full corpus — see the rumdl evidence under Options considered):

[global]
flavor = "obsidian"
# MD033 HTML: reviewer-enforced. MD041 first-line-H1: frontmatter precedes.
# MD045 flags `embeds` as alt-less images. MD071 inserts a blank line
# after frontmatter. MD060 (table format) is opt-in and stays off.
# MD057/MD051/MD052 (broken links, fragments, labels) and MD024 (duplicate
# sibling headings) stay disabled until a follow-up link-repair task.
extend-disable = ["MD033", "MD041", "MD045", "MD071", "MD024", "MD051", "MD052", "MD057"]
# Build artifacts (regenerated, never formatted), the generated Astro
# site mirror, and the markdown-fixtures gate's fixture files.
exclude = [
"docs/index.md",
"docs/glossary.md",
"docs/references.md",
"sites/df-docs/src/content/docs",
"plugin/lib/services/gate/tests/fixtures/markdown",
]
[MD003]
style = "atx"
[MD004]
style = "dash"
[MD013]
line-length = 100
reflow = true
# S-0007 exempts headings, fenced code, and tables from line length.
headings = false
code-blocks = false
tables = false
[MD025]
front-matter-title = "" # body H1 + frontmatter title: is the house convention
[MD029]
style = "ordered"
[MD046]
style = "fenced" # default "consistent" converted a fence to an indented block

Per-file MD013/MD032 carve-outs exempt lines that are a single irreducible inline-code span (a shell command, type signature, or path in backticks); the live file enumerates them.

Lefthook integration (addition to lefthook.yml):

pre-commit:
commands:
markdown-fmt:
run: bunx rumdl@0.2.9 fmt {staged_files}
glob: "*.md"
stage_fixed: true

Pin the version: pre-1.0 cadence is fast (0.2.8 and 0.2.9 shipped on consecutive days); bumps are deliberate, not implicit.

quality_checks additions (sdlc.yaml):

- bunx rumdl@0.2.9 check docs plugin
- bun plugin/cli/sdlc.ts gate markdown-fixtures

check exits 0 over the corpus: the residual findings (71 broken relative links via MD057, 26 duplicate sibling headings via MD024, over-long unwrappable inline-code lines via MD013) are triaged in .rumdl.toml — the link rules disabled pending a follow-up repair sweep, the irreducible lines carved out per file. gate markdown-fixtures pins the formatter’s reflow behavior against this decision’s stress fixtures, so a rumdl version or config bump that changes reflow fails the gate instead of mangling the corpus.

One-time corpus reformat (shipped via T-0008, PR #356):

  1. Hand-fixed the three displaced-anchor lines (C-0005, C-0007, P-0003-pragmatic-architecture) before the reformat.
  2. Ran bunx rumdl@0.2.9 fmt docs plugin once: ~7,000 fixes across 433 files, wikilink/embed multiset preserved.
  3. MD040 auto-tagged 447 bare fences with text; upgrade to real language tags opportunistically.

markdownlint-cli2 + custom prose-wrap script (fallback hybrid)

Section titled “markdownlint-cli2 + custom prose-wrap script (fallback hybrid)”

Two lefthook pre-commit commands run sequentially:

  1. markdownlint-cli2 —fix — auto-corrects list markers, heading style, trailing whitespace, final newline. MD040 (code-fence language) is flag-only; the 447 bare fences need a hand pass.
  2. bun run plugin/scripts/wrap_prose.ts — custom Bun script that wraps prose paragraphs at 100 columns while skipping YAML frontmatter blocks, table rows, fenced code blocks, headings, lines containing wikilinks/transclusion targets, and lines ending in a block-id ( ^anchor) — the displacement hazard confirmed in the rumdl corpus test applies to any wrap implementation.
config:
MD003:
style: atx # ATX headings only
MD004:
style: dash # - list markers only
MD009: true # no trailing whitespace
MD013: false # line length: prose-wrap script owns this
MD025:
front_matter_title: "" # body H1 + frontmatter title: is the house convention
MD029:
style: ordered # 1. 2. 3. explicit numbering
MD033: false # HTML: reviewer-enforced, not formatter
MD040: true # fenced code requires language tag (flag-only)
MD041: false # first line need not be H1 (frontmatter precedes)
MD047: true # files end with single newline
globs:
- "docs/**/*.md"
- "plugin/skills/**/*.md"
- "plugin/lib/**/*.md"
fix: true

Lefthook integration (additions to lefthook.yml):

pre-commit:
commands:
markdown-lint-fix:
run: bunx markdownlint-cli2 --fix {staged_files}
glob: "*.md"
markdown-prose-wrap:
run: bun run plugin/scripts/wrap_prose.ts {staged_files}
glob: "*.md"

Watch dprint/dprint-plugin-markdown#180. If a "tables": "preserve" option ships, dprint’s Obsidian-feature safety is already confirmed — but it would still need a prose-wrap answer (textWrap: maintain does not enforce the 100-column rule), so it competes with the fallback hybrid, not with rumdl.

  • T-0008 authored .rumdl.toml, wired the lefthook command, the quality_checks lines, and the just fmt-markdown/check-markdown recipes, hand-fixed the three anchor lines, and ran the one-time corpus reformat (~7,000 fixes across 433 files). The /markdown-format skill is the on-demand, PR-producing surface.
  • No custom prose-wrap script. Prose wrap, list markers, heading style, code-fence language tags, trailing whitespace, and final newline are all rumdl auto-fixes; authors get them on commit and can run bunx rumdl fmt <file> manually.
  • Tables, wikilinks, transclusions, block-ids, and frontmatter are untouched by the toolchain (verified by full-corpus round-trip); they remain the author’s and the schema validator’s domain.
  • MD057 relative-link validation comes free in check mode — it covers the broken-link capability D-0008-markdown-standard deferred — but stays disabled in .rumdl.toml until a follow-up task repairs the 71 existing broken links.
  • The rumdl version is pinned; bumps are deliberate. If the single-maintainer risk materializes, fallback to the hybrid is cheap (markdownlint rule numbering, rumdl import).
  • Two upstream bug reports to file against rvben/rumdl (embed-glue, anchor displacement), tracked as B-1ATA; drop the reflow caveats when they land.
  • Skill prose (plugin/skills/**/*.md) stays in scope at full strictness per the Standard’s applies_to.paths; irreducible inline-code lines get per-file carve-outs in .rumdl.toml. (rumdl processes .md only, so .eta template bodies are out of scope by construction.)
  • Implementation and one-time corpus reformat: T-0008.
  • Broken-link checking as a gate — rumdl’s MD057 makes it available, but it stays disabled until a follow-up link-repair task; enabling it is not part of this decision.
  • Spell-checking / SDLC-casing enforcement (reviewer-enforced per the Standard).
  • Upstream fixes: file the embed-glue and anchor-displacement reflow bugs against rvben/rumdl (tracked as B-1ATA); if reflow becomes Obsidian-aware, drop the displaced-anchor caveat.
  • ADR filename follows D-0002-entity-identifier-shape: D-0011- prefix plus slug markdown-formatting-tool.
  • Round-trip tests run 2026-06-05 in an isolated /tmp/ directory against five real corpus files plus a synthetic Obsidian-features fixture. No formatter output was committed to the repo.
  • Prettier version under test: 3.8.3 (via bunx). dprint version: 0.54.0 (CLI) + markdown plugin 0.22.1 (WASM). rumdl version: 0.2.8 via bunx rumdl@latest; 0.2.9 released during testing — later corpus passes may have resolved it, with consistent results.
  • Performance numbers: dprint corpus pass 104ms vs Prettier 3.9s on 399 files; single-file dprint ~12ms vs Prettier ~100ms. Both well under the 30s / 500ms budgets.
  • Corpus-size labels: the Prettier/dprint numbers were measured on the docs/ tree as of 2026-06-05 (399 files); the rumdl numbers cover every tracked .md in the repo (660 files as of 2026-06-06).
  • rumdl tests run 2026-06-06 in an isolated /tmp/ directory: the synthetic-fixture round-trip, a reflow stress fixture, and a forensic full-corpus fmt pass over a git-baselined copy (multiset and placement diffs for wikilinks, embeds, anchors, tables, frontmatter; idempotence check). No formatter output was committed to the repo.
  • Registration: decisions surface via the generated docs/index.md (sdlc docs generate); no D-ORMG roster row is needed.

← Back to Decisions