feat: strip third-party rules; enable per-rule only (#26586)

Closes DOCS-425.

## Summary

Collapse `.vale.ini` to load only the Coder rule package. Drop `Packages
= Google, alex, write-good`. Replace `BasedOnStyles = Google,
write-good, Coder` with `BasedOnStyles = Coder`. Drop every `Google.X`,
`write-good.X`, and `alex.X` per-rule line. Add a rule-rollout doctrine
under `docs/.style/README.md`.

## Why

The previous config carried roughly 12,000 baseline findings across
`docs/`: 412 errors / 5380 warnings / 6247 suggestions, almost entirely
from third-party rules whose false-positive patterns Vale cannot
distinguish from author intent.

- `Google.Headings` false-positives on every acronym and product name:
VM, AWS, GCP, Coder, Vale, JetBrains, VS Code.
- `Google.Will` fires on legitimate event-sequencing prose.
- `Google.Acronyms` fires on widely-known terms the audience reads
fluently (AWS, RDP, VPC).
- `alex.*` rules shipped in DOCS-40 without a corpus cleanup commit.

When CI surfaces false positives, engineers stop reading annotations. PR
#25501 review surfaced this concretely on `Google.Headings`. The fix is
a tight, trustworthy ruleset rather than tuning around individual false
positives.

## Doctrine

Full text in `docs/.style/README.md`. Summary:

| Element | Value |
| --- | --- |
| PR title | `feat(docs/.style): enable <RuleName>` |
| Commits | (1) corpus-wide cleanup, (2) rule enable + `style-guide.md`
section + custom YAML if applicable |
| Acceptance | zero baseline findings at merge, at the rule's chosen
severity |
| Severity | deliberate per-rule choice: `error` blocks merge; `warning`
and `suggestion` annotate without failing CI |
| False-positive policy | one confirmed FP after enable, refine or
revert; applies regardless of severity |

Applies equally to Coder-authored rules and third-party rules.
Third-party rules return through the same per-rule pattern after their
corpus is clean.

### Severity ladder

The three-severity ladder is deliberate. Some rules catch hard policy
where any violation is wrong (brand names, banned first-person pronouns,
em-dashes); those ship at `error` and block merge. Other rules catch
strong guidance with legitimate human-judgment exceptions (`disabled` as
a technical state vs. ableist usage); those ship at `warning` and
annotate without failing CI. Soft guidance (noun-as-adjective patterns
like `desired state`, wordiness) ships at `suggestion` as a `notice`
annotation.

The cleanup discipline applies at every severity. A rule landing at
`warning` or `suggestion` still ships with zero baseline findings; the
rule's purpose is to catch new violations, not to surface a backlog of
existing ones. Standing backlogs train contributors to ignore the
annotation channel.

The `error`-blocks-merge half of this contract lands operationally via
PR [#26587](https://github.com/coder/coder/pull/26587) (DOCS-426), which
removes `continue-on-error: true` and `vale --no-exit` from the CI step.

## Effect on the corpus baseline

| Metric | Before | After |
| --- | --- | --- |
| Errors | 412 | 0 |
| Warnings | 5380 | 0 |
| Suggestions | 6247 | 0 |
| Files | 465 | 465 |

Verified locally with `mise exec aqua:errata-ai/vale -- vale --no-exit
docs/`.

## Functional state after merge

The CI `Vale prose lint` step stays advisory (`continue-on-error: true`,
`--no-exit`) until PR #26587 lands. With no rules loaded except Coder's
package (currently empty on `main`), the step is effectively a no-op
until `Coder.BrandNames` lands via PR #25501 (DOCS-34). At that point
the lint step becomes a `Coder.BrandNames`-only check. Subsequent
per-rule PRs extend coverage one rule at a time per the doctrine, each
rule choosing the severity that matches its policy strictness.

The Makefile target `docs/.style/.vale-synced: .vale.ini` still runs
`vale sync`, which is now a no-op because `Packages` is empty. The
previously-synced `docs/.style/styles/{Google,alex,write-good}/`
directories remain on developers' disks (they're gitignored) but are no
longer loaded by Vale.

## Sequencing

1. **This PR merges first**
2. PR #26587 (DOCS-426) installs the CI merge gate and the
severity-rendering fix
3. PR #25501 (DOCS-34) rebases onto main, drops its now-redundant
`Google.Parens = NO` change, lands `Coder.BrandNames` as the first
concrete rule
4. DOCS-424 (Vale rule audit) is complete; per-rule re-enablement work
begins per the doctrine

<details>
<summary>Decision log</summary>

- **Strip everything vs. partial disable**: chose full strip because
each third-party rule loaded by default is a tacit endorsement. The
doctrine requires every enabled rule to be deliberate. A partial disable
still loads styles whose other rules haven't been audited.
- **`alex.*` rules**: yanked in this PR. They were enabled in DOCS-40
without a corpus cleanup commit. The "audit then keep" call returns them
via dedicated per-rule PRs once the audit confirms baseline violation
counts and the doctrine accepts them.
- **`Packages` directive dropped**: with no third-party rules loaded,
`vale sync` had no work to do. Removing the directive avoids implying we
intend to re-add packages without a per-rule PR. The directive returns
when a future PR opts in a Google or write-good rule.
- **Doctrine location**: under `docs/.style/README.md` rather than a
dedicated `docs/.style/RULE_ROLLOUT.md`. Keeps the contributor-facing
entry point single, and the section sits alongside the existing "Editing
the style guide" and "Editing the content guidelines" sections.
- **Three-severity ladder vs. error-only**: chose deliberate per-rule
severity because the rule catalogue contains rules at different policy
strictness. Forcing every rule to `error` would either reject useful
warning- and suggestion-level rules (noun-as-adjective patterns,
wordiness guidance) or push them onto an inappropriate gate. The CI
severity rendering and merge-gate work in PR #26587 was built
specifically to support this ladder.

</details>

---
*Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on
Nick's behalf.*
This commit is contained in:
Nick Vigilante
2026-06-23 12:15:49 -04:00
committed by GitHub
parent fccb238ec7
commit bdf0e417b1
2 changed files with 75 additions and 80 deletions
+19 -80
View File
@@ -1,90 +1,29 @@
# Vale configuration for Coder documentation.
#
# Curated cherry-pick of Google's developer-docs style, write-good (wordiness),
# and a hand-picked subset of alex (inclusive-language). The choice of base
# styles and disabled rules is documented in DOCS-40 and reproducible via
# `make lint/prose`.
# Rule rollout doctrine. Every rule listed below ships clean: zero
# baseline findings across `docs/` at enable time. Severity is a
# deliberate per-rule choice:
#
# Severity policy. Rules sit at a level that reflects two things
# together: false-positive rate against real Coder docs and the gravity of
# the rule. Low FPs plus high gravity argues for `error`; lower gravity or
# more judgment calls argue for `warning` or `suggestion`. v1 lands most
# rules at `warning` and the wordiness rules at `suggestion`. Promote a
# rule to `error` only when (a) its false-positive rate against real
# content is effectively zero and (b) the existing-content violation count
# for that rule is also zero.
# - `error` hard policy; CI blocks merge on violations.
# - `warning` strong guidance; surfaces annotations without
# failing CI.
# - `suggestion` soft guidance; surfaces `notice` annotations.
#
# About Vale's exit code: Vale exits non-zero only when error-level alerts
# are found, regardless of `MinAlertLevel`. The Makefile invokes Vale with
# `--no-exit` to suppress that exit while the un-overridden Google
# error-level rules still produce a baseline error count. Real failures
# (bad config, missing files) still propagate. See
# DOCS-40 for the rollout plan.
# To add a rule, follow the per-rule PR template in
# docs/.style/README.md ("Adding a Vale rule"). Third-party rules
# from Google, alex, and write-good are not enabled by default; each
# returns via the same per-rule PR pattern after its corpus is clean.
#
# The styles themselves live under docs/.style/styles/ after `vale sync`,
# which the Makefile target invokes once per .vale.ini change. They are
# gitignored to keep the repo lean.
# About Vale's exit code: Vale exits non-zero only when error-level
# alerts are found. Warning and suggestion annotate without
# affecting exit. Real runtime failures (bad config, missing files)
# propagate regardless of severity.
#
# The Coder rule package lives under docs/.style/styles/Coder/ and is
# the single source loaded by default.
StylesPath = docs/.style/styles
MinAlertLevel = suggestion
# Packages drives `vale sync`. Pin upstream tags here when reproducibility
# matters more than getting upstream fixes; the unpinned form pulls the
# latest release of each package on `vale sync`.
Packages = Google, alex, write-good
[*.md]
BasedOnStyles = Google, write-good, Coder
# --- Google curation -------------------------------------------------------
# Google.EmDash conflicts with our em-dash ban (see scripts/check_emdash.sh
# and DOCS-44). The repo-level ban covers Unicode U+2014/U+2013 plus the
# ` -- ` ASCII fallback; Google.EmDash would double-flag and use prose
# different from our policy.
Google.EmDash = NO
# Google.Latin flags i.e. and e.g. for non-native readers. Coder docs assume
# a technical audience that reads these fluently; leaving the rule on
# produces noise without value.
Google.Latin = NO
# Soften two high-volume Google rules. The signal-to-noise ratio is low
# at the default warning level.
Google.Parens = suggestion
Google.WordList = warning
# Google.Spacing flags fully-qualified Go type names like
# `codersdk.WorkspaceAgent` as "should have one space." It produces ~4,500
# errors against `docs/reference/api/schemas.md` alone (measured
# 2026-05-18) because codersdk type names match the pattern. The
# architectural decision for generated content is to fix the upstream
# Go generators (clidocgen, apidocgen, auditdocgen, metricsdocgen)
# rather than add Vale path exclusions; this disable buys time until
# those generator changes land. Re-enable then, ideally promoted to
# `error`.
Google.Spacing = NO
# --- write-good curation ---------------------------------------------------
# Passive voice is contextually correct often enough that flagging every
# instance teaches nothing. E-Prime forbids forms of "to be" entirely,
# which is incompatible with normal technical writing.
write-good.Passive = NO
write-good.E-Prime = NO
# Keep the three rules that catch real wordiness problems. Wordiness and
# ThereIs are judgment calls (suggestion); Weasel is sharper (warning).
write-good.TooWordy = suggestion
write-good.Weasel = warning
write-good.ThereIs = suggestion
# --- alex curation ---------------------------------------------------------
# alex is loaded a la carte rather than via BasedOnStyles. ProfanityMaybe
# and ProfanityUnlikely fire on technical terms like `execute`, `kill`,
# `failed`, and `attack`; ProfanityLikely is much more conservative and
# safe to keep on.
alex.Ablist = warning
alex.Condescending = warning
alex.LGBTQ = warning
alex.ProfanityLikely = warning
alex.Race = warning
alex.Suicide = warning
BasedOnStyles = Coder
+56
View File
@@ -82,3 +82,59 @@ with another style or contributing doc in the repo, it governs.
Open a PR against `docs/.style/style-guide.md`. Follow-up PRs add each
rule and the matching style-guide section together.
## Adding a Vale rule
Each rule in the repo-root `.vale.ini` ships clean: zero baseline
findings against the current `docs/` corpus.
The PR that adds a rule is the rule's complete unit:
1. **Cleanup commit**: fix every existing-content violation of the new
rule so `make lint/prose` reports zero findings for it.
The cleanup ships in the same PR as the enable, ordered first.
2. **Enable commit**: add the rule to `.vale.ini` at its chosen
severity, write a corresponding section under
`docs/.style/style-guide.md`, and add the custom rule YAML under
`docs/.style/styles/Coder/` if applicable.
The rule's `message:` field points at the relevant `style-guide.md`
anchor.
Severity is a deliberate per-rule choice:
- `error` blocks merge. Use for hard policy where any violation is
wrong: brand-name casing, first-person pronouns we ban outright,
em-dash bans.
- `warning` surfaces an annotation without failing CI. Use for strong
guidance with legitimate human-judgment exceptions: terms that need
context (`disabled` as a technical state vs. ableist usage),
judgment-bound style preferences.
- `suggestion` surfaces a `notice` annotation. Use for soft guidance
where the right fix is contextual: noun-as-adjective patterns like
`desired state`, wordiness, optional sentence reshaping.
The severity choice and the cleanup discipline are independent. A
rule landing at `warning` or `suggestion` still ships with zero
baseline findings; the rule's purpose is to catch new violations, not
to surface a backlog of existing ones. A rule that surfaces a standing
backlog teaches contributors to ignore the annotation channel, which
erodes trust in CI regardless of the severity at which the noise
arrives.
PR title: `feat(docs/.style): enable <RuleName>`.
False-positive policy: one confirmed false positive after enable,
either refine the rule or revert.
We do not maintain rules that occasionally cry wolf, regardless of
severity.
If a policy is judgment-bound (passive voice, weasel words,
sentence-case headings on a corpus with many proper nouns), write a
Coder-authored rule with the precision the situation needs instead of
accepting an imprecise third-party rule at any severity.
This applies equally to Coder-authored rules (under
`docs/.style/styles/Coder/`) and third-party rules from Google, alex,
and write-good.
Third-party rules are not loaded by default.
Each returns through the same per-rule PR pattern after its corpus is
clean.