Skip to content

Replace markdownlint-cli2 with rumdl for Markdown linting (GH-1352) - #1360

Open
laughingman7743 wants to merge 3 commits into
masterfrom
docs/1352-rumdl
Open

laughingman7743 wants to merge 3 commits into
masterfrom
docs/1352-rumdl

Conversation

@laughingman7743

@laughingman7743 laughingman7743 commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

WHAT

Replace markdownlint-cli2 with rumdl for just docs lint / just docs format.

  • .mise.toml: "npm:markdownlint-cli2" = "0.18.1" → rumdl = "0.3.2" (mise registry, aqua:rvben/rumdl).
  • justfile: _docs-lint → mise exec -- rumdl check, _docs-format → mise exec -- rumdl check --fix.
  • .rumdl.toml replaces .markdownlint-cli2.jsonc (removed). It started from rumdl import .markdownlint-cli2.jsonc and keeps the same rule settings and comments: MD013, MD014, MD041 off, MD004 dash, MD007 indent 2 (fixed), MD024 siblings only, MD033 allowed elements.
  • The rule set stays that of markdownlint 0.38 (the version markdownlint-cli2 0.18.1 runs): enable lists exactly its rules minus the three disabled ones, so rumdl-only rules such as MD057 or MD064 do not run. Options whose rumdl defaults differ from markdownlint's are set to markdownlint's values (MD001 body headings only, MD010 code blocks and tab width, MD026/MD036 CJK punctuation, MD027 list items, MD051 case-sensitive fragments, MD052 ignored x label, MD053 ignored // definitions). MD044 code-blocks is left at rumdl's default because no names are configured.
  • Known remaining MD025 differences that rumdl 0.3.2 options cannot remove:
    • markdownlint recognizes quoted or capitalized front matter title keys ("title":, Title:), while rumdl recognizes only title:. None of the linted files has front matter.
    • markdownlint skips MD025 when prose precedes the first H1, while rumdl reports a second H1 in that case too (allow-document-sections and allow-with-separators do not change this). rumdl is stricter here, and no linted file currently triggers it.
  • The file set is unchanged. include carries over the globs; the root glob is written /*.md because rumdl matches an unanchored *.md in every directory. The old ignores are dropped: rumdl respects .gitignore, which already covers docs/_build, .venv, .tox, .pytest_cache, and .serena, and none of them match the include globs anyway.
  • docs/**/*.md uses the MyST flavor, so MyST directive fences such as ```{toctree} are not reported by MD040.
  • .github/workflows/docs-lint.yaml: the paths trigger watches .rumdl.toml instead of .markdownlint-cli2.jsonc.
  • AGENTS.md and the development-workflow skill name the new tool, config file, and command.

MD041 stays disabled: with the MyST flavor, rumdl still reports it for all 16 docs/ pages whose first content (after the license comment) is a (label)= target.

WHY

Closes #1352.
mise's npm backend needs Node.js, which .mise.toml never declared, so Markdown linting silently relied on a global Node.js (present on ubuntu-latest, but not necessarily for contributors).
rumdl is a single binary, so the project no longer needs Node.js for anything.

TEST

Tested commit: dfbab73.

  • master comparison: markdownlint-cli2 reports 0 errors in 27 files. rumdl check . with only the imported config reports 11 MD040 findings, all MyST directive fences ({toctree}, {note}) in docs/; the MyST flavor resolves them.
  • File set: rumdl lints 26 files, the same as markdownlint-cli2 minus CLAUDE.md, a symlink to AGENTS.md.
  • Seeded file with violations of each configured rule (MD004, MD007, MD033, MD024 siblings, MD040, MD003, MyST fence, $ ls): both tools report the same configured rules. Differences: rumdl does not report MD032 when a list switches from * to - markers without a blank line (markdownlint treats them as two lists), and it additionally reports MD025 for a second setext H1.
  • Rule set: the enable list equals the rule keys of the installed markdownlint 0.38.0 config schema minus MD013/MD014/MD041. Option defaults were compared against that schema's defaults. A probe with a case-mismatched fragment link, a tab inside a code block, an unused reference definition, a double space between sentences, and a lazy list continuation gets the same results from both tools (MD051, MD010, MD053; nothing for the last two).
  • just docs format behavior: on a file with one fixable and one unfixable issue, rumdl check --fix fixes the first and exits 1, as markdownlint-cli2 --fix does. Unlike markdownlint-cli2, it also auto-fixes MD040 by adding text to a fence without a language.
  • just docs lint: no issues in 26 files. just docs format: no diff.
  • env -i shell with no node on PATH: mise exec -- rumdl check passes.
  • mise exec -- rumdl check .agents/skills: no issues in 4 files.
  • just lint: passes.
  • No AWS tests: no Python code changes. The Docs Lint workflow runs on this PR because .mise.toml, justfile, and **.md changed.

🤖 Generated with Claude Code

markdownlint-cli2 ran through mise's npm backend, which needs a Node.js
runtime that .mise.toml never declared. rumdl is a single binary in the
mise registry, so `mise install` and `just docs lint` no longer depend on
a global Node.js.

The rule settings and globs carry over from .markdownlint-cli2.jsonc into
.rumdl.toml. docs/ uses the MyST flavor so directive fences such as
```{toctree} are not reported as code blocks without a language.

Closes #1352

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread justfile

_docs-format:
mise exec -- markdownlint-cli2 --fix
mise exec -- rumdl check --fix

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review round one — implementation behavior: CLEAN

Base d2c63f61bf19660395860056cd3f537261fbc698, head 07f826c9c140da4ce484ea16f30fb75a97244d70.

Covered: .mise.toml, justfile recipes, .rumdl.toml (rules, include globs, MyST per-file flavor), removed .markdownlint-cli2.jsonc, docs-lint.yaml path filter, AGENTS.md, development-workflow skill.

Checks:

  • just docs format failure path: on a seeded file with one fixable (MD040) and one unfixable (MD033) issue, rumdl check --fix fixes the first and exits 1, so the recipe still fails like markdownlint-cli2 --fix when issues remain.
  • MD007 style = "fixed": a - sublist indented 3 spaces under an ordered item is not reported, matching markdownlint (which applies MD007 only to bullets nested in bullets).
  • File set and excluded build output: 26 files, docs/_build and the other old ignores are gitignored and rumdl respects .gitignore.
  • .rumdl_cache/ is created in the checkout but carries its own .gitignore, so it never shows as untracked.

Behavior difference recorded in the PR body: rumdl check --fix adds text to fences without a language (MD040), which markdownlint-cli2 does not auto-fix; repository files have none, so just docs format makes no change.

No tests are added: the change is tool configuration with no Python behavior; the Docs Lint workflow is the check.

Comment thread .rumdl.toml Outdated
"MD014",
# MyST `(label)=` ref targets must come before the first heading, so the
# first line of many docs/*.md files is not an H1
"MD041",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review round two — claims and operational behavior: FINDINGS (fixed in the PR body)

Base d2c63f61bf19660395860056cd3f537261fbc698, head 07f826c9c140da4ce484ea16f30fb75a97244d70.

Claims checked:

  • rumdl is in the mise registry as aqua:rvben/rumdl: mise registry rumdl → aqua:rvben/rumdl github:rvben/rumdl.
  • The project no longer needs Node.js: no package.json, .nvmrc, or other npm/Node reference remains in tracked files (git grep); a run with no node on PATH passes mise exec -- rumdl check.
  • Unanchored *.md matches every directory: with "*.md" rumdl lints 39 files including .github/ and .agents/; with "/*.md" it lints 26.
  • Old ignores are covered by .gitignore (git check-ignore for docs/_build, .venv, .tox, .pytest_cache, .serena); node_modules does not match any include glob.
  • docs-lint.yaml runs on this PR: the Docs Lint lint job passed on this head.
  • AGENTS.md / skill: the documented commands (mise install, just docs lint, just docs format, mise exec -- rumdl check .agents/skills) were run and succeed. No other doc names markdownlint (git grep -i markdownlint is empty apart from the removed config).

Finding (fixed): the PR body said MD041 is still reported in 19 files. That count came from the unanchored config, which included 3 .github/ templates. With the final config it is 16, all docs/ pages whose first content is a (label)= target. The PR body now says so and also records that rumdl check --fix auto-fixes MD040, which round one referred to.

rumdl enables rules beyond markdownlint 0.38 (such as MD064) and some
shared rules have different defaults (MD051 ignore-case, MD010
code-blocks), so files markdownlint accepted could fail and some
violations it reported could pass. Enable exactly the markdownlint 0.38
rules and set the differing options to markdownlint's defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread .rumdl.toml Outdated
"cloudformation/**/*.md",
"/*.md",
]
disable = [

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review (relayed): FINDINGS

Reviewer: Codex CLI 0.166.0 (codex exec -s read-only, model gpt-6-astra, session 01a1abd1-3db0-7df0-b0f8-f1284ae7859c). Static review of a detached snapshot at the head. It was given the diff and the intended behavior only, without the PR text, commit messages, or self-review records, and was not permitted to edit, build, run tests, or access GitHub; it ran read-only rumdl check probes.
Base d2c63f61bf19660395860056cd3f537261fbc698, head 07f826c9c140da4ce484ea16f30fb75a97244d70.

Covered: exact diff and merge-base config; file selection, anchoring, ignores and symlinks; MyST flavor; MD007/MD024/MD033 and inherited defaults; recipe exit behavior; mise/CI wiring and path filters; tracked docs/skills references.

  1. P2: shared-rule defaults are not preserved (.rumdl.toml:20). Options left unset inherit rumdl's defaults. ## Basic usage followed by [Usage](#Basic-usage) now passes because MD051 matches case-insensitively, while markdownlint rejected it. MD010 also stops checking tabs inside code blocks. Restore the previous options, including MD051.ignore-case = false and MD010.code-blocks = true.
  2. P2: additional rules introduce new lint failures (.rumdl.toml:20). Disabling only MD013/MD014/MD041 leaves rumdl-only rules enabled, including MD057 and MD064. One sentence. Another sentence. passed before and now exits 1 with MD064. Select the previous rule set explicitly or disable the additional rules.

Non-actionable notes: 26 selected files and the four agent skills pass; root anchoring and the benchmark depth restriction work; CLAUDE.md (a symlink) is skipped while its target AGENTS.md is checked; MD007/MD024/MD033 behave as intended; both recipes propagate failures; CI wiring and path filters are consistent; no tracked references to the old tool remain.

Author verification: both findings reproduced. rumdl config shows MD051.ignore-case = true and MD010.code-blocks = false by default, and rumdl defines rules MD057 and MD060–MD095 that markdownlint 0.38 lacks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repair: a9ec6a3 (base d2c63f61bf19660395860056cd3f537261fbc698, head a9ec6a35)

  • Finding 2: global.enable now lists exactly the rules of markdownlint 0.38.0, the version markdownlint-cli2 0.18.1 runs, minus MD013/MD014/MD041. The disable list is gone, and the reasons for the three omissions are kept as comments. Checked with a script against the rule keys of the installed markdownlint@0.38.0 config schema: missing = MD013/MD014/MD041, extra = none.
  • Finding 1: every rumdl default for those rules was compared with the schema defaults, and each one that differs is now set to markdownlint's value: MD010 (code-blocks = true, spaces-per-tab = 1), MD026/MD036 (CJK punctuation), MD027 (list-items = true), MD051 (ignore-case = false), MD052 (ignore = ["x"]), MD053 (ignored-definitions = ["//"]). MD044 code-blocks is left alone because no names are configured.
  • Validation: a probe file with a case-mismatched fragment, a tab in a code block, an unused definition, a sentence double space, and a lazy list continuation gets the same results from both tools (MD051, MD010, MD053; nothing for the last two). just docs lint, just docs format (no diff), rumdl check .agents/skills, and just lint pass.

Self-review of the repair. Round one (behavior): the enable list is an allowlist; rumdl config shows it loaded from .rumdl.toml with an empty disable. Round two (claims): the comment saying markdownlint 0.38 is what cli2 0.18.1 runs is taken from the installed package's package.json; the PR body now describes the rule set and the option alignment. An independent follow-up on this repair comes next.

markdownlint's MD001 checks only body headings, while rumdl counts a
front matter title as a level-1 heading by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread .rumdl.toml
[MD033]
allowed-elements = ["details", "summary", "br", "kbd", "sub", "sup", "div", "img", "p", "a"]

# Options whose rumdl defaults differ from markdownlint's

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent follow-up review (relayed): FINDINGS

Reviewer: Codex CLI 0.166.0 (codex exec -s read-only, model gpt-6-astra, session 01a1abd9-5fc4-7781-b81c-47a83008f2e4). Static review with read-only rumdl check probes; given the repair diff 07f826c9..a9ec6a35 and the earlier findings, without the PR text or self-review records; no edits, builds, tests, or GitHub access.
Base d2c63f61bf19660395860056cd3f537261fbc698, head a9ec6a35b4ff886e90a9b55c7c2f8c24220fe07e.

Covered: repair diff, previous configuration, installed markdownlint 0.38 source/schema, rumdl 0.3.2 effective configuration, file selection, MyST settings, comments. rumdl check --no-cache --deny-config-warnings passes all 26 files.

Result: the allowlist finding is resolved. enable selects exactly the 49 expected rules, and all eight added option assignments are valid and match markdownlint's defaults (including ignored_labels → ignore).

  1. P2: MD001's differing default remains unset (.rumdl.toml:58). rumdl defaults front-matter-title to true; markdownlint 0.38's MD001 considers only body headings. Front matter title: Example followed by ### First section gains an MD001 failure. [MD001] front-matter-title = false removes it.
  2. P3: MD025 default title matching is not equivalent (.rumdl.toml:58). markdownlint's default matcher recognizes quoted and case-varied title keys; rumdl 0.3.2 does not report MD025 for "title": Example or Title: Example followed by # Main heading. Copying markdownlint's regex into rumdl's front-matter-title did not restore detection.

Notes: MD044 code-blocks differs, but names = [] makes it inactive. File selection and MyST settings are unchanged by the repair.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repair: dfbab73

  • Finding 1 (fixed): reproduced with a probe (front matter title: Example + ### First section): rumdl reported MD001 and markdownlint-cli2 0.18.1 did not. Added [MD001] front-matter-title = false, after which rumdl reports nothing. just docs lint, just docs format (no diff), and just lint pass.
  • Finding 2 (deferred, documented): rumdl 0.3.2 has no option that reproduces markdownlint's MD025 title-key pattern (the reviewer found that copying the regex does not work). The difference only affects front matter with quoted or capitalized title keys, and none of the 26 linted files has front matter (checked the first line of each). The PR body now lists it as a known remaining difference instead of claiming full parity.

Self-review of the repair. Behavior: a one-option change to a rule that is already enabled; the file set and other rules are untouched. Claims: the PR body now names MD001 among the aligned options and states the MD025 limitation.

Comment thread .rumdl.toml
enable = [
"MD001", "MD003", "MD004", "MD005", "MD007", "MD009", "MD010", "MD011",
"MD012", "MD018", "MD019", "MD020", "MD021", "MD022", "MD023", "MD024",
"MD025", "MD026", "MD027", "MD028", "MD029", "MD030", "MD031", "MD032",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent follow-up review 2 (relayed): FINDINGS (deferred, documented)

Reviewer: Codex CLI 0.166.0 (codex exec -s read-only, model gpt-6-astra, session 01a1abe5-152c-79f3-b80e-72061eaa38c1). Static review with read-only, cache-disabled rumdl check probes; given the repair diff a9ec6a35..dfbab738; no edits, builds, tests, or GitHub access.
Base d2c63f61bf19660395860056cd3f537261fbc698, head dfbab738.

Covered: base-to-head and repair diffs; all 49 enabled-rule defaults against the local markdownlint 0.38 sources; the effective rumdl configuration; the default and skill lint scopes (26 + 4 files pass).

  • MD001 repair verified: front matter titles no longer affect the first body heading, and body heading-level skips are still detected. No side effects.
  • Front matter deferral judged reasonable: none of the 26 default files has front matter. The four skill files have front matter with only name/description keys.
  • P2: MD025 differs for documents without front matter (.rumdl.toml:25). With Preamble. followed by # Section one and # Section two, markdownlint 0.38 does not report MD025 because prose precedes the first H1, while rumdl reports line 5, also under the MyST flavor. allow-document-sections = true does not resolve it.
  • No other result-changing default mismatch was found. MD044 code-blocks differs but is inactive with empty names.

Author disposition: reproduced; allow-document-sections and allow-with-separators both still report it. Deferred: rumdl 0.3.2 has no option for this, rumdl is the stricter side, and no linted file triggers it. The PR body now lists it under the known remaining MD025 differences. No code change, so dfbab738 remains the reviewed head.

@laughingman7743
laughingman7743 marked this pull request as ready for review November 5, 2026 13:35

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace markdownlint-cli2 with rumdl to drop the implicit Node.js dependency

1 participant