Choose a Markdown linter and formatter
Following wrapping feedback from @josephlou5, I propose adopting markdownlint with the 20 rules that already pass across all 114 documentation inputs. That needs no initial formatting cleanup.
This should be tool + config, not doc + best intentions. Contributors should have a command or editor setup that produces the formatting we expect, without choosing settings themselves or waiting for review feedback.
Commit the configuration, pin the version, and make installation easy through mise and Nix. Local installation stays optional; CI runs the checks either way. Then enable more rules in small changes toward the defaults or an agreed subset. Revisit Rumdl once it handles our conflict examples correctly.
Requirements
- Preserve code examples, links, and MkDocs syntax during the migration, and support MDX for Starlight.
- Start with rules that pass today; review further rules and their cleanup separately.
- Check line lengths. Rewrapping every existing paragraph is optional.
- Share one configuration between the CLI, editors such as VS Code and Vim/Neovim, and GitHub Actions.
- Keep installation optional and simple. Prefer Rust or a standalone binary over another runtime dependency.
I've used both markdownlint and Panache and found their configuration straightforward. Both projects have responded quickly to issues and PRs.
markdownlint-cli2 (Node.js)
Pros: individual rule selection, line-length checks, automatic fixes, and VS Code, Vim/Neovim, and GitHub Actions integrations. We have a verified configuration that passes without changing files.
Cons: requires Node.js for the CLI and does not rewrap paragraphs. Some autofixes break MDX, so we should test each additional rule before enabling it.
Rumdl (Rust)
Pros: no Node.js dependency; markdownlint-style rules plus additional checks; MkDocs/MDX modes; VS Code and Neovim/LSP support and a GitHub Action.
Cons: Rumdl fails on four valid pages containing conflict examples, even with every lint rule disabled. I’ve reported this in rumdl#887; we should fix it before adopting Rumdl. Some wrapping and link checks also need work.
Its similar rule set makes a later switch plausible, but we should check the behavior differences against our configuration.
Panache (Rust)Pros: no Node.js dependency; full formatting and wrapping; VS Code and Neovim support; dedicated GitHub Action.
Cons: more formatting changes than we need now, no advertised MDX support, and changes to rendered admonitions, tabs, code, and links in the tested configurations. Its Pandoc/Quarto support is useful, but doesn't justify adopting it for this first step. See its tool comparison.
Prettier (Node.js)Pros: MDX support, prose wrapping, editor integrations, and CLI checks for CI.
Cons: requires Node.js, applies broader formatting changes, and broke Starlight builds in both tested wrapping configurations.
Other optionsRemark-lint can use the site's MDX and directive parsers, but requires Node.js and several plugins.
Mdformat uses Python and has MkDocs plugins. I haven't established an MDX setup for it.
Investigation notesThe investigation covered all 113 distinct Markdown/MDX source files, plus the generated CLI reference shared by both sites. This includes MkDocs, Starlight, root documents, templates, and other READMEs. Symlinks count once; Rustdoc comments were outside the investigation.
Both unchanged sites build locally. The initial markdownlint configuration enables 20 rules, passes all 114 inputs, and changes no files with --fix.
| Tested configuration | Files changed | Lines added / removed |
|---|---|---|
| markdownlint, proposed 20-rule configuration | 0 | +0 / −0 |
| markdownlint, default autofixes | 46 | +657 / −904 |
| Rumdl, default rules with MkDocs/MDX settings | 55 | +1,584 / −1,766 |
| Panache, GFM preserving existing wrapping | 53 | +1,123 / −891 |
| Panache, GFM with 80-column wrapping | 107 | +4,548 / −4,214 |
| Panache, Pandoc with 80-column wrapping | 107 | +7,840 / −7,592 |
| Prettier, preserving existing wrapping | 76 | +5,695 / −5,771 |
| Prettier, prose wrapping at 80 columns | 106 | +9,974 / −9,190 |
Panache used custom settings and skipped MDX files and the generated CLI reference. Rumdl skipped four pages containing conflict examples.
Specific failures in the tested versions:
- markdownlint-cli2 0.23.2: broad fixes broke an existing MDX page. A separate test deleted a link definition used inside an MDX component.
- Rumdl 0.2.73: the conflict safeguard reports
Unresolved merge conflict; formatting skipped until conflict markers are removed. The line-length check missed prose inside a<TabItem>; broad checks produced false anchor errors, and fixes damaged an include path. - Panache 3.11.0: the GFM preserve configuration passed both builds but changed rendered admonitions, tabs, code, and links.
- Prettier 3.9.7: prose wrapping broke a caution block and both MDX pages. Preserving wrapping also left a Starlight build failure.
Prepared with Codex assistance, including validation against saved test results. I reviewed the evidence, confirmed the recommendation, and chose the wording.
Source: jj-vcs/jj