Checks that a package's README.md keeps the shape of an introduction and a
map: what the package is, why it exists, how to install it, one basic-usage
snippet, and links to the rest grouped by the reader's question.
Nothing else in the gate reads the README as a whole, so a README drifts into a manual one helpful section at a time. This stage checks the shape, not the prose: it reads headings, paragraphs, code blocks and list indentation, and it never judges a sentence.
✓ README: 180 lines, every part present (0.0s)
✓ README: 2 warnings: no_why at README.md:1, over_ceiling at README.md:251 (0.0s)
✗ README: 2 problems (0.0s)Each finding sits at a file:line in the README:
README.md
1 [warning] no H2 section reads as the Why: none starts with "Why", "What this is for" or "The problem" (no_why)Rules
no_what- the README has no H1, or no prose paragraph under it before the next heading, or that first paragraph runs overwhat_max_lineslines. Badge, image, HTML, blockquote, list and table blocks between the H1 and the paragraph are skipped: they are not the What.no_why- no H2 starts with "Why", "What this is for" or "The problem".no_install- no H2 starts with "Install", or the one that does carries no dependency snippet: a code block holding a{:package, ...}tuple.no_basic_usage- no H2 starts with "Quick start" (or "Quickstart"), "Basic usage", "Usage" or "A first", or the first one that does holds no code block, more than one, or a block over forty lines. The section runs to the next H1 or H2, so its H3s are part of it.ungrouped_documentation- an H2 starting with "Documentation" holds a link that is not on a nested list item: every link sits under a group item (- Learn, then the links indented beneath it). The finding is at the first such link and counts the rest.over_ceiling- the README runs over the line ceiling, 250 by default. The finding is at the first line past it.bad_manifest-.claude/diataxis.mdexists and its front matter is unclosed, or itsreadme_max_linesis not a positive integer. The default ceiling applies, so a broken manifest never stops the stage.
Headings are matched on the start of their text, ignoring case. Headings and blocks inside fenced code are not read as structure.
What it does not check
Whether a link resolves. That is the Doc links stage's check
(ExQuality.Stages.DocLinks), and this stage does not repeat it: enable
both for a README that is shaped right and links right.
What it reads
README.md at the project root, and the front matter of
.claude/diataxis.md when that file exists, for one key:
---
readme_max_lines: 300
---It builds nothing and runs no tool.
Severity
Findings are warnings by default: the stage reports them, names each one in
its summary line and in the JSON report, and passes. severity: :error
makes every finding an error and fails the stage on any of them; that is
the per-project flip once a README has the shape.
Opt-in
The stage is off by default, so no consumer's gate changes on upgrade.
Enable it in .quality.exs:
readme: [enabled: :auto] # on when README.md exists
readme: [enabled: true] # forced
readme: [enabled: :auto, severity: :error] # fail on any finding
Summary
Functions
Runs the README stage.
Functions
@spec run(keyword()) :: ExQuality.Stage.result()
Runs the README stage.
Config options
enabled-false(default) |:auto(on whenREADME.mdexists) |true(forced)severity-:warning(default) |:errorwhat_max_lines- the longest the first paragraph under the H1 may run (default: 6)