ci: format Markdown with mdformat instead of Prettier - #16
Merged
Conversation
Closes #8. `npx prettier@3.9.6 --check` refetched Prettier from the npm registry on every run, and that fetch sat silent for exactly 301 seconds — npm's default fetch-timeout expiring, the retry then succeeding — in a job that takes twelve seconds when it is clean. #8 measured three stalls in six runs. The ten-minute bound in #7 cannot catch it, and never could: a run that recovers at 301s is under the bound by design. mdformat is a Python tool, so setup-node leaves the workflow and the pin moves into requirements-dev.txt, which the pip Dependabot entry already configured in .github/dependabot.yml picks up. Prettier's pin could not be handled that way, which is why it sat in a `run:` line as a known hand edit. requirements-dev.txt is separate from requirements.txt on purpose. That file carries the contrail pin a sync installs, and sync.yml, check-instance.yml and check-template.yml all read it; a Markdown formatter has no business in there, and an instance pulling Markdown config should never touch its own pin. markdownlint-cli2 stays. Its action is JavaScript, so GitHub runs it on the runner's own Node with no toolchain to install and nothing fetched — it already sat before setup-node, which is the proof. It also honours the per-file markdownlint-configure-file comment that keeps README.md at 100 columns, which no Python linter offers. mdformat needs two extension plugins to match what Prettier did without being asked. mdformat-gfm pads table cells, byte-identically, so no table moves. mdformat-frontmatter keeps the YAML block at the top of a Markdown file intact; without it mdformat does not recognise frontmatter at all and rewrote .claude/skills/two-audience-docs/SKILL.md into a horizontal rule and a heading, which would have broken the skill silently. `number = true` is load-bearing: without it every ordered list is renumbered to `1.`. .mdformat.toml's `exclude` errors below Python 3.13, so markdown.yml pins that version; a runner provides any version on request, so this costs an instance nothing. check-template.yml guarded that markdown.yml's config ships beside it. That guard now names .mdformat.toml and requirements-dev.txt, the latter because a missing pin file fails the job outright rather than quietly checking the wrong thing. Matches atdr/contrail, where the same swap landed first. The config files are copies across the two repos and every instance, so they have to move together. The mechanical reformat is the next commit, so this one stays reviewable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011mJ5NaA5DdPxqa1Mb4UJhT
Mechanical, kept out of the previous commit so that one stays reviewable. No prose changed. One line: a blank line between two adjacent HTML comments at the foot of README.md. The markdownlint-configure-file comment that holds this file at 100 columns still works — markdownlint reports 0 issues in 4 files. Every table is byte-identical, and the YAML frontmatter in .claude/skills/two-audience-docs/SKILL.md is untouched, both because of the plugins pinned in requirements-dev.txt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011mJ5NaA5DdPxqa1Mb4UJhT
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
setup-nodeleavesmarkdown.ymlentirely.requirements-dev.txt, which the pip Dependabot entry already configured in.github/dependabot.ymlpicks up.Matches atdr/contrail#55, which should land first.
docs/contrail-gh.mdupstream says these config files are copies across this repo, contrail and every instance, so a gap means the repos disagree about what correct Markdown is.Why
#8 measured it:
npx prettier@3.9.6 --checkrefetched Prettier from the npm registry on every run, and the fetch sat silent for exactly 301 seconds — npm's defaultfetch-timeoutexpiring, the retry then succeeding — in a job that takes twelve seconds clean. Three stalls in six runs. #7's ten-minute bound cannot catch it and never could: a run that recovers at 301s is under the bound by design.Prettier's pin could not be handled by Dependabot, because it has no first-party action, so it sat in a
run:line as a documented wart. That is now gone too.markdownlint-cli2 stays because its action is JavaScript: GitHub runs it on the runner's own Node with no toolchain to install and nothing fetched. It already sat before
setup-node, which is the proof. It also honours the per-filemarkdownlint-configure-filecomment that keepsREADME.mdat 100 columns, which no Python linter offers.Changes
Tooling (
c4eb64a)markdown.yml:setup-node+npx prettier@3.9.6out;setup-pythonat 3.13 andpip install -r requirements-dev.txt, thenmdformat --check .requirements-dev.txtadded, holdingmdformat,mdformat-gfmandmdformat-frontmatter.prettierrc.jsonand.prettierignoredeleted;.mdformat.tomladded as their successorcheck-template.yml: the guard that markdown.yml's config ships beside it now names.mdformat.tomlandrequirements-dev.txtREADME.mdandAGENTS.mdupdated, including all three copies of the template-update recipe that list the config files by nameMechanical pass (
43aaaca) — one line, a blank line between two adjacent HTML comments at the foot ofREADME.md.Why
requirements-dev.txtrather thanrequirements.txtrequirements.txtcarries the contrail pin a sync installs, andsync.yml,check-instance.ymlandcheck-template.ymlall read it. A Markdown formatter has no business in there, and an instance pulling Markdown config should never have its own version pin touched as a side effect. Verified that Dependabot's pip ecosystem does bumprequirements-dev.txtalongsiderequirements.txtbefore relying on it.It is in the
check-template.ymlguard because a missing pin file fails the job outright, rather than quietly checking the wrong thing the way a missing.markdownlint-cli2.yamlwould.One thing worth a reviewer's attention
mdformat silently destroyed the YAML frontmatter in
.claude/skills/two-audience-docs/SKILL.md. It rewrote the block into a horizontal rule and a## name: two-audience-docs description: …heading, which would have broken the skill with nothing to complain about. Prettier understood frontmatter natively; mdformat does not recognise it at all withoutmdformat-frontmatter. Caught by reading the mechanical diff before committing it. The plugin is pinned andrequirements-dev.txtsays why..mdformat.toml'sexcludekey errors below Python 3.13, which is whymarkdown.ymlpins that version. A runner provides any version on request, so this costs an instance nothing.Test Plan
mdformat --check .exits 0, and re-running changes nothing, proving idempotencemarkdownlint-cli2 v0.23.2reports 0 issues in 4 files against mdformat's output — the two tools agree rather than fight.claude/skills/two-audience-docs/SKILL.mdis byte-identical after a format pass, withmdformat-frontmatterinstalledmdformat-gfmpads cells byte-identically to PrettierREADME.md'smarkdownlint-configure-filecomment still holds it at 100 columns after the blank line was inserted above itcheck-template.ymlguard now names are presentcheck-template / template contractgreen on this PR — passed in 7s, so the edited guard finds.mdformat.tomlandrequirements-dev.txtwhere it now expects themmarkdown / lintgreen on this PR — 7s and 10s across two runs, against the baseline The Prettier fetch stalls for five minutes on most runs #8 measured of 12s when the npm fetch was clean and 301s when it was not. That is the whole point of the change, and it is now bounded by a pip install of 64 KB rather than an npm fetch of 8 MB🤖 Generated with Claude Code
https://claude.ai/code/session_011mJ5NaA5DdPxqa1Mb4UJhT