styleclaims fails CI when the Coder documentation style guide claims a prose rule is enforced by tooling that isn't actually enabled.
It runs as make lint/style-claims (part of make lint and make lint-light).
Each rule section in docs/.style/style-guide/ ends with an annotation naming what enforces it.
Those annotations drifted from the configuration: 8 cited third-party Google.* rules that the repo-root .vale.ini never loads, and 2 cited Coder.* rules that have no YAML under docs/.style/styles/Coder/.
A reader who trusts an annotation believes a rule is checked when it isn't, which is worse than an unenforced rule the guide is honest about.
- A
Coder.*rule cited as active with no rule file. The rule doesn't exist; the citation needs(planned). - A third-party rule cited as active.
.vale.inisetsBasedOnStyles = Coder, and third-party rules return one per PR after their corpus is clean, so anyGoogle.*,alex.*, orwrite-good.*citation is planned until that happens. - A rule with no style guide section. A rule file exists but no annotation cites it as active, so a rule landed without the section the per-rule PR pattern requires.
- Checks-table drift. A severity that disagrees with the rule file's
level:, a row for a rule that doesn't exist, a missing row for a rule that does, or a scope cell that doesn't read the way.vale.iniconfigures the rule. The scope check compares the rendered text, not the set of paths, so a cell that says "and" where the config says "except" is caught along with a cell that omits or invents a path. - A rule cited as enforcement while
.vale.inileaves it off. A rule file can exist and still run nowhere. - Coverage-table drift. A per-section or total count that disagrees with the annotations.
- A rule heading with no footer. A
##or###heading that states a rule but carries no annotation, so the coverage table can't count it.
The checker verifies a Coder.* citation against the rule files, so a typo is caught.
A third-party citation is verified only against the loaded styles: those rule files are vendored rather than kept in this repository, so the guarantee is weaker on that side.
An annotation starts with one of Enforced, Documentation-only, Adapted, or Vale rule, and runs to the line ending in *.
A citation counts as a claim only when its sentence contains enforc or names a Vale rule.
A sentence that mentions a rule without claiming it runs, such as the note explaining why Google.Passive stays out of the package, is ignored.
Mark each planned citation individually.
(planned) after the citation, or Planned Vale rule before it, both work:
*Enforced by `Coder.BrandNames`.*
*Enforced by `Coder.LearnMore` (planned).*
*Enforced by `Google.Gender` (planned) and `Google.GenderBias` (planned).*
*Enforced by `scripts/check_emdash.sh` (existing CI script) and `Coder.EmDash` (planned).*
*Documentation-only. No Vale rule.*A trailing clause such as (both planned) that covers several citations at once isn't understood; mark each one.
A marker that appears before any citation introduces the list that follows, as in "Planned Vale rules" followed by names, and it carries until a later clause re-asserts enforcement.
So "Planned A, enforced by B." reads A planned and B active.
Each annotation is classified once for the coverage table, so the table counts rule sections rather than linter rule names. One annotation footer is one rule section: a heading without a footer, such as an example, a navigation heading, or the out-of-scope Color contrast section, isn't a rule of the guide and isn't counted.
The checker also reports a ## or ### heading that states a rule but carries no footer, so a rule can't drop out of the table by never being annotated.
A footer belongs to the nearest heading above it, and a heading that has sub-headings is treated as covered by them, so a parent rule whose footer sits after a scoping sub-heading isn't flagged.
A heading is also exempt when it is the Learn more navigation section, or when it carries a note that opens with Out of scope or Not a rule, which is how a section that organizes a page without stating a rule declares itself.
Headings deeper than ### are detail inside a section, not rules.
A Markdown file that has no footers and no coverage row, such as editor-setup.md, isn't a rule section and isn't scanned.
- Documentation-only: the annotation declares the section
Documentation-only. This wins over everything else, so a section that only cross-references a rule another section owns counts under that other section and no rule is counted twice. The match is case-sensitive; a lowercase "documentation-only" describes part of a section, not the section. - Tool-checked: cites as active an enabled Coder rule, a third-party rule whose style
.vale.iniloads,markdownlint, anMD###rule, orscripts/check_emdash.sh. A Coder rule that exists but is disabled everywhere doesn't count: it checks no file. - Planned: cites only planned checkers.
go run ./scripts/styleclaimsIt takes no arguments and runs from the repository root.
Findings print as file:line: message, or as file: message when the problem is the file as a whole, such as a rule with no style guide section or a missing coverage row.
Any finding exits 1.
The set of style guide sections comes from the directory, not from a list in the source. A Markdown file is a rule section when it carries at least one enforcement footer, or when the coverage table gives it a row, so a new subpage is validated as soon as either is true.