mdformat parity
cargo xtask mdformat-parity compares mdwright against mdformat (with the GFM, frontmatter, footnote, and MkDocs
plugins) over an isolated corpus copy. The goal is classified compatibility, not byte identity. Every mdwright/mdformat
output difference is either fixed, configured, or recorded below as intentional; otherwise the command fails as a
release gate.
Use [fmt] profile = "mdformat" to ask "how close can mdwright get to mdformat while keeping verified rewrites?" The
profile keeps mdformat's default wrap = keep; a project that wants mdformat with a column limit must set wrap
explicitly. When wrap is an integer, mdwright enforces that line budget for breakable prose in every profile. The
default stable wrap strategy uses mdformat-compatible soft-break reflow. The mdformat profile also defaults list
continuation indentation to four spaces.
Status values
open-bug: known unresolved gap; reported as a failing release gate.intentional-divergence: mdwright deliberately keeps a different byte style while preserving semantics.upstream-parser-limitation: difference pinned to parser behaviour outside mdwright.configured: caused by mdwright project configuration, usually generated-doc excludes.fixed: should no longer appear; the xtask fails if it does.
Class is free-text and groups rows by root cause. style-option-mismatch covers remaining wrap or indentation policy
differences; mdformat-semantic-drift covers cases where mdformat's output is not semantically equivalent to the
source; intentional-policy covers generated files excluded by configuration.
Classifications
The table below is parsed by xtask::mdformat_parity::load_classifications: each row must have exactly seven cells.
Path patterns support *, **, and prefix/** globs; find_classification returns the first matching row, so
specific paths come first and catch-all ** rows last. Formatter divergences are owned by the formatter team;
generated-doc exclusions are owned by docs.
| Corpus | Path | Construct | Class | Status | Owner | Resolution |
|---|---|---|---|---|---|---|
| external | jupyter_book_minimal/admonitions.md | MyST directives | mdformat-semantic-drift | intentional-divergence | formatter | mdwright preserves MyST directive structure; mdformat with --no-validate rewrites this fixture in a way mdwright's semantic oracle rejects. |
| external | jupyter_book_minimal/asides.md | MyST directives | mdformat-semantic-drift | intentional-divergence | formatter | Same shape as admonitions.md. |
| external | jupyter_book_minimal/directives.md | MyST directives | mdformat-semantic-drift | intentional-divergence | formatter | Same shape as admonitions.md. |
| external | jupyter_book_minimal/blocks.md | MyST and Pandoc blocks | mdformat-semantic-drift | intentional-divergence | formatter | mdwright preserves MyST and Pandoc block structure; mdformat with --no-validate rewrites this fixture in a way mdwright's semantic oracle rejects. |
| mdwright-docs | src/SUMMARY.md | nested list indentation | style-option-mismatch | intentional-divergence | formatter | mdwright preserves the existing two-space mdBook summary nesting; mdformat rewrites nested bullets to four spaces. |
| mdwright-docs | src/extending/lint-rules.md | list continuation indentation | style-option-mismatch | intentional-divergence | formatter | The repository policy keeps marker-width continuation; fmt.lists.continuation-indent = "four-space" provides the mdformat spelling when requested. |
| mdwright-docs | src/configuration.md | generated docs | intentional-policy | configured | docs | Generated by cargo xtask doc-config; excluded so source docs and generator drift checks do not fight. |
| mdwright-docs | src/reference/cli.md | generated docs | intentional-policy | configured | docs | Generated by cargo xtask doc-cli. |
| mdwright-docs | src/reference/diagnostic-schema.md | generated docs | intentional-policy | configured | docs | Generated from diagnostic schema tests. |
| mdwright-docs | src/rules/** | generated rule docs | intentional-policy | configured | docs | Generated by cargo xtask doc-rules; rule pages intentionally contain lint violations. |
| mdwright-docs | ** | prose wrap | style-option-mismatch | intentional-divergence | formatter | Integer wrap now enforces a line budget for breakable prose in every profile. mdwright may wrap lines that mdformat leaves above the configured width. |
| release-prose-corpus | ** | prose wrap line budget | style-option-mismatch | intentional-divergence | formatter | mdwright enforces wrap = 120 for breakable prose lines. mdformat 1.0.0 leaves the observed over-budget source lines unchanged. |
| release-math-corpus | **/*-template.md | mdformat semantic drift | mdformat-semantic-drift | intentional-divergence | formatter | mdwright preserves the source semantics; mdformat changes the rendered HTML on some template files in this corpus. |
| release-math-corpus | ** | math-heavy prose and list reflow | style-option-mismatch | intentional-divergence | formatter | mdwright treats ordinary paragraph newlines as soft breaks and enforces over-budget breakable lines. mdformat leaves some over-budget lines unchanged. |
| external | ** | prose wrap | style-option-mismatch | intentional-divergence | formatter | Same as the mdwright-docs catch-all. Single oversized atomics may still exceed the target by policy. |
Release use
Run against the pinned mdformat baseline:
cargo xtask mdformat-parity \
--corpus-root docs \
--corpus-name mdwright-docs \
--mdwright-config .mdwright.toml \
--mdformat-config xtask/fixtures/mdformat-parity/mdformat.toml
The mdformat config lives under xtask/fixtures/ because it is an oracle fixture, not the repository's own formatter.
Output lands at target/mdwright/parity/mdformat-parity.{json,md}. A clean release run has no unclassified differences,
no semantic drift, no parse errors, no idempotence failures, and no rows marked open-bug.