Reports and scoring
The report object
audit() always resolves to the same structure, whatever options you passed.
A category entry carries its own score and its finding counts:
A page entry carries its findings:
Rendering
render is synchronous and takes a report you already have, so you can audit once and emit all three.
HTML is one self-contained file. Inline styles, no external requests, no fonts to load. It is the format to hand to someone who will not read JSON.
Markdown is per-category headings with a findings table and the score summary. Good for pasting into an issue or a pull request comment.
JSON is the report object, stringified. For pipelines, dashboards, and diffing one run against the next.
How the score is calculated
Two different formulas run, and the difference is deliberate.
Page and category scores
Findings are weighted by severity:
A pass earns full weight. A warn earns a quarter of it. A fail earns nothing. Findings with status info are excluded from scoring entirely, since they report a value rather than a verdict.
A page with no scoreable findings scores 100.
The overall score
The headline number does not average the categories, because an average buries a handful of real errors under a pile of passing checks. Instead it starts at 100 and deducts.
For each rule, the worst status it reached on each page is counted, then:
with these weights:
So one error rule failing on every page costs 22 points on its own. The same rule failing on a quarter of the site costs 5.5. This is why the overall score sits lower than the category scores, and why it moves when you fix something real.
Reading findings
A rule that fails on every page is a template problem and one fix clears it. A rule that fails on three pages is content work. Sorting by count tells you which is which.