feat(docs): add Coder.BrandNames Vale rule, enforce HashiCorp casing#25501
feat(docs): add Coder.BrandNames Vale rule, enforce HashiCorp casing#25501nickvigilante wants to merge 5 commits into
Conversation
Docs preview📖 View docs preview for |
|
Reopening. The stale bot auto-closed this on 2026-06-07 from inactivity, but the work is still active, tracked in Linear as DOCS-34 (In Review) under the Vale / docs style-guide initiative. This PR is stacked on Reopened by Coder Agents on behalf of @nickvigilante. |
Five Markdown files used "Hashicorp" instead of "HashiCorp"; the docs/manifest.json sidebar entry for the Vault integration page also carried "Hashicorp" in title and description. HashiCorp's brand uses mixed case (capital H, capital C); the URLs and the Terraform provider addresses (e.g. hashicorp/vault) stay lowercase by convention. This commit fixes the prose-only instances and the sidebar entry so the Coder.BrandNames Vale rule landing in the next commit lints a clean corpus. Path on docs/admin/integrations/vault.md is unchanged; the sidebar title change does not affect the page URL on coder.com.
First concrete rule under the Coder style. Lands as a bundled substitution rule that will hold every brand whose owner publishes a canonical casing; HashiCorp is the first entry, with GitHub, OpenTofu, Kubernetes, Terraform, JetBrains, and VS Code following under DOCS-188. Rule design: - extends: substitution, level: error. The brand owner spells it HashiCorp; any other casing in prose is wrong, not a judgment call. Vale's substitution rule type skips inline code, fenced code blocks, and URLs by default, so hashicorp/kubernetes (Terraform provider source) and developer.hashicorp.com stay untouched. - ignorecase: false and nonword: false. The wrong cases we want to flag are spelled Hashicorp (capital H, lowercase c) or HASHICORP; lowercase hashicorp in prose is fine since the corpus only uses it in URL paths. - The cleanup committed first (d71156c) drove the existing-content violation count to zero, so the rule can ship at error from day one. Style guide entry: - A new ## Word choice → ### Brand names section in style-guide.md documents the policy, lists HashiCorp as the first enforced brand, and references DOCS-188 for the rollup. - The "Active rule set" bullet for the Coder style is updated to note that Coder.BrandNames is live (no longer "empty in v1"). Verification: - make lint/prose: 391 errors / 5201 warnings / 7681 suggestions across 458 files. Baseline error count unchanged from before the rule landed; the cleanup commit drove the would-be 7 new errors to zero. Suggestions increased by 8 (new prose triggers Google.Contractions, Google.Acronyms, etc., all expected). - make lint/markdown: 0 errors across 463 files. Closes DOCS-34.
881d678 to
9b1b787
Compare
…ted by Coder.BrandNames
The previous commit ("feat(docs/.style/styles/Coder): add Coder.BrandNames Vale rule") ships the first concrete rule under this directory, which obsoletes two claims in the local README:
1. "This directory is intentionally empty for now" no longer holds; BrandNames.yml ships in this PR.
2. The "HashiCorp casing" bullet under planned starter rules is now implemented and documented in docs/.style/style-guide.md under "### Brand names".
Rewrite the lead-in to point readers at the YAML files in the directory and at the style-guide.md sections for the user-facing policy each rule enforces. Drop the HashiCorp casing bullet. The remaining planned-coverage entries stay so the README still serves as the rule-authoring landing page.
…files Pre-review pass on every Markdown file this PR modified. Style-guide infrastructure (docs/.style/style-guide.md, docs/.style/styles/Coder/README.md) gets the full treatment: convert every paragraph and list to semantic line breaks per sembr.org (one sentence per line, with optional breaks at major clause boundaries); fix Google.Will, Google.We, Google.Contractions, Google.Passive, and Google.WordList warnings where the rewrite preserves the original meaning. Replace 'To be filled in by follow-up PRs. Planned coverage:' with 'Coming in follow-up PRs:' (active voice, no 'will'). Wrap the literal 'we' mention in backticks so the meta-discussion does not trip Google.We. The five product docs touched only by the HashiCorp casing swap get sembr applied to the swap paragraph only, per scoping discussion with @nickvigilante: multiple-kube-clusters.md, opentofu.md, workspace-tags.md, templates/index.md, template-from-scratch.md. Pre-existing Vale findings on those product docs (passive voice, weasel words, parens, etc.) stay in place; cleanup belongs in a separate sembr/Vale rollout ticket.
Group with the other disabled Google rules (EmDash, Latin, Spacing). Parens are a normal English punctuation device for clarification, examples, and disambiguation; the rule fires on every use without distinguishing stylistic intent from misuse, so its signal-to-noise ratio is effectively zero even at suggestion level. Net effect on the corpus: drops ~1800 noise findings from `make lint/prose` (8042 suggestions before, 6247 after) without weakening any policy. Move Google.WordList down next to its own rationale comment now that the previous 'soften two high-volume Google rules' grouping no longer applies.
|
/coder-agents-review Filed via Coder Agents on Nick's behalf. |
|
Chat: Review posted | View chat Review history
deep-review v0.9.0 | Round 1 | Last posted: Round 1, 12 findings (2 P2, 5 P3, 1 P4, 2 Nit, 2 Note), COMMENT. Review Finding inventoryFindings
Round logRound 1Panel (12 reviewers: Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Gon, Leorio, Luffy, Robin, Zoro, Razor, Kite). Netero: no findings. Robin, Mafuuu: no findings. 2 P2, 5 P3, 1 P4, 1 Nit posted. 1 P3, 2 Nit, 2 Note in body/not posted. Reviewed against 966dd89..e6c7dd0. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
The BrandNames rule is well-built. The substitution mechanism is the right tool, the ignorecase: false + defensive HASHICORP swap is precise, the cleanup-then-rule commit ordering gives each commit a clean state, and the style-guide section gives contributors a clear workflow for adding future brands. The corpus is verified clean. Kite: "fires on Hashicorp and HASHICORP in prose, skips inline code and URLs, zero self-violations on the style guide's backtick-wrapped demo tokens."
Severity count: 2 P2, 5 P3, 1 P4, 2 Nit, 2 Note.
The most common finding across reviewers (7 of 12 flagged independently): the PR description says "Two commits, in this order" but the branch has five. Three undisclosed commits carry the majority of changed lines, and one of them (Google.Parens disable) is a standalone Vale policy change.
coderd/telemetry/telemetry.go:1361
Note [CRF-11] Sibling Hashicorp instance in Go code outside PR scope. This line contains Hashicorp's documentation (wrong casing). The PR scope is docs-only, which is a reasonable boundary, and the Vale rule only applies to Markdown. But a repo-wide grep for the wrong casing would have caught this. (Mafu-san)
🤖
🤖 This review was automatically generated with Coder Agents.
| # Google.Parens flags every paren use as "use parentheses judiciously." | ||
| # Parens are a normal English punctuation device for clarification, | ||
| # examples, and disambiguation. The rule cannot distinguish stylistic | ||
| # intent from misuse, so even at suggestion level its signal-to-noise | ||
| # ratio is effectively zero. Disable. | ||
| Google.Parens = NO |
There was a problem hiding this comment.
P2 [CRF-1] PR description says "Two commits, in this order so each is independently valid" and lists exactly two. The branch has five:
| # | Commit | Described? |
|---|---|---|
| 1 | Fix HashiCorp casing in prose and sidebar | Yes |
| 2 | Add Coder.BrandNames Vale rule + style guide section | Yes |
| 3 | Scrub README planned-rules list | No |
| 4 | Semantic line breaks + copyediting (63 ins / 89 del across 7 files) | No |
| 5 | Disable Google.Parens in .vale.ini (this block) | No |
Commit 5 changes Google.Parens from suggestion to NO, dropping ~1800 suggestion-level findings from make lint/prose. The commit message is honest and the rationale is sound, but this is an independent Vale severity-policy decision invisible to reviewers who scope from the description. The verification numbers in the PR description ("7681 suggestions") may reflect the pre-Parens-disable state.
Update the PR description to account for all five commits, or split the Google.Parens disable into its own PR. (Mafu-san P2, Hisoka P3, Luffy P3, Kite P3)
🤖
| # | ||
| # Adding a brand: append a key/value to the swap table below and audit | ||
| # the docs corpus for the wrong-casing variant. Keep the message template | ||
| # unchanged; the `%s` token interpolates the matched (wrong) text. |
There was a problem hiding this comment.
P2 [CRF-2] The comment says "the %s token interpolates the matched (wrong) text" (singular). The message template has two %s tokens with different semantics: the first interpolates the correct (replacement) form, the second interpolates the matched (wrong) form. A contributor adapting this template or debugging a message that reads backward gets a wrong mental model.
Suggestion:
# unchanged; the first %s interpolates the correct (replacement) text
# and the second interpolates the matched (wrong) text.
(Leorio)
🤖
|
|
||
| Use each brand's canonical casing in prose. | ||
| URLs and code references (Terraform provider source addresses, GitHub paths) keep the lowercase form their owner uses. | ||
| Vale's `substitution` rule skips inline code and links by default, so those aren't affected. |
There was a problem hiding this comment.
P3 [CRF-3] This says the substitution rule "skips inline code and links." Vale's substitution rules skip link URLs but check link display text. The PR's own cleanup confirms this: [Hashicorp Vault](...) in link display text was flagged and fixed.
The BrandNames.yml comment (line 3) uses the correct wording: "it skips fenced code blocks, inline code, and URLs." The style guide should match. Replace "links" with "link URLs" or adopt the BrandNames.yml phrasing. (Razor)
🤖
| # Level: error. Each swap targets a brand whose owner publishes a | ||
| # canonical casing; "Hashicorp Vault" instead of "HashiCorp Vault" is | ||
| # objectively wrong, not a judgment call. The existing-content violation | ||
| # count is zero (cleanup landed in the previous commit), so this rule |
There was a problem hiding this comment.
P3 [CRF-4] "cleanup landed in the previous commit" is a temporal reference that dissolves after merge. After a squash merge, "the previous commit" doesn't exist. After a rebase merge, the SHA changes. The factual claim (violation count is zero) stands on its own. Drop the parenthetical:
# count is zero, so this rule can ship at error from day one.
(Leorio)
🤖
| action: | ||
| name: replace | ||
| swap: | ||
| Hashicorp: HashiCorp |
There was a problem hiding this comment.
P3 [CRF-5] The swap table catches Hashicorp and HASHICORP but omits hashicorp (all lowercase). The PR adds HASHICORP defensively despite zero corpus occurrences, but omits hashicorp citing 49 URL/code instances. However, this rule extends: substitution, and Vale's substitution scope already skips fenced code blocks, inline code, and URLs by default (as the comment on lines 3-7 documents). Adding hashicorp: HashiCorp would catch the one case the current table misses (lowercase in prose, which is always wrong for a proper noun) with zero false-positive risk.
The stated fallback (Vale.Spelling, DOCS-187) doesn't exist yet. Until it ships, a contributor who writes "hashicorp" in body text gets no lint signal.
swap:
hashicorp: HashiCorp
Hashicorp: HashiCorp
HASHICORP: HashiCorp(Zoro P3, Hisoka Note)
🤖
| link: https://github.com/coder/coder/blob/main/docs/.style/style-guide.md#brand-names | ||
| level: error | ||
| ignorecase: false | ||
| nonword: false |
There was a problem hiding this comment.
P4 [CRF-8] No regression fixture for the rule. If someone later breaks the swap YAML or removes an entry, there's no automated check that the rule still fires on known-bad input. The "test" is the absence of violations in the corpus, which is indistinguishable from a broken rule that fires on nothing. Not this PR's debt (no Vale rule test infrastructure exists in the repo), but worth noting as the Coder/ style directory grows. (Bisky)
🤖
| - Limit `we` | ||
| - Setup vs set up, Quickstart casing | ||
| - Next steps vs Learn more | ||
| - Vale substitution rule scaffold |
There was a problem hiding this comment.
Nit [CRF-9] "Vale substitution rule scaffold" is stale or ambiguous. BrandNames.yml shipped in this PR as a substitution rule. A contributor reading this planned-coverage list would reasonably wonder what scaffold work remains. Remove the bullet or rename it to distinguish it from the rule that just shipped. (Gon)
🤖
| @@ -0,0 +1,28 @@ | |||
| # Coder.BrandNames - enforce canonical brand-name casing in prose. | |||
There was a problem hiding this comment.
P3 [CRF-6] The PR description's verification section says the rule "flags the 7 instances the cleanup commit fixes." Vale scopes to [*.md] only (.vale.ini line 47); it can only flag the 5 Markdown instances. The 2 manifest.json fixes are manual edits outside Vale's scope. The claim conflates Vale-flagged instances with manually-fixed entries. The correct statement is: Vale flags 5 instances; 2 additional instances in manifest.json are fixed by hand. (Mafu-san)
🤖
| # examples, and disambiguation. The rule cannot distinguish stylistic | ||
| # intent from misuse, so even at suggestion level its signal-to-noise | ||
| # ratio is effectively zero. Disable. | ||
| Google.Parens = NO |
There was a problem hiding this comment.
Nit [CRF-10] The PR title scope docs does not contain every changed file. .vale.ini is at the repo root, outside docs/. AGENTS.md: "When you use a scope, it must be a real filesystem path containing every changed file." The two CI title-check failures (82815991380, 82816044244) may be flagging this mismatch. Either drop the scope (feat: add Coder.BrandNames...) or use a scope that covers all paths. (Kite)
🤖
| extends: substitution | ||
| message: "Use '%s' instead of '%s' (brand-name casing)." | ||
| link: https://github.com/coder/coder/blob/main/docs/.style/style-guide.md#brand-names | ||
| level: error |
There was a problem hiding this comment.
Note [CRF-12] Worth knowing: make lint/prose uses --no-exit (Makefile:854), so Coder.BrandNames at error level surfaces as a CI annotation but does not fail the build or block merge. The enforcement depends on human reviewers noticing the annotation. This is a system-level constraint from the 391 pre-existing errors in upstream Google rules, not a flaw of this PR. (Pariston)
🤖
Lands the first concrete rule under the
Coderstyle:Coder.BrandNames, a bundledsubstitutionrule that enforces canonical brand casing in prose. HashiCorp is the first entry; DOCS-188 extends it with GitHub, OpenTofu, Kubernetes, Terraform, JetBrains, and VS Code.Base branch is #25467 (DOCS-40 Vale wiring), not
main. Will rebase ontomainafter that PR merges. Depends onBasedOnStyles += Coderfrom #25467 to load the rule and ondocs/.style/styles/Coder/from #25466.What changes
Two commits, in this order so each is independently valid:
docs: fix HashiCorp casing in prose and sidebar— 5 Markdown files + 2docs/manifest.jsonentries. Drives existing-content violations to zero.feat(docs/.style/styles/Coder): add Coder.BrandNames Vale rule—Coder.BrandNames.ymlwith the HashiCorp swap atlevel: error, plus a new### Brand namessubsection instyle-guide.md.Severity rationale
errorfrom day one. HashiCorp's brand owner publishes a canonical casing; any other casing in prose is wrong, not a judgment call. Matches theerror = low FPs × high gravityframework. False-positive rate is effectively zero because Vale'ssubstitutionrule skips inline code, fenced code blocks, and URLs by default —hashicorp/kubernetes(Terraform provider source) anddeveloper.hashicorp.comstay untouched.Verification
make lint/prose: 391 errors / 5201 warnings / 7681 suggestions across 458 files. Baseline error count unchanged;Coder.BrandNamesfires zero times against the cleaned-up corpus. The+8 suggestionsis the new style-guide prose tripping pre-existing Google rules (Contractions, Acronyms, etc.).make lint/markdown: 0 errors across 463 files.make pre-commit-light: passed (6s).docs/and confirmed it flags the 7 instances the cleanup commit fixes, then re-ran against the post-cleanup state and confirmed zero alerts.Implementation plan and decision log
Why bundle into Coder.BrandNames rather than one file per brand
Vale's convention (mirrored by
Google.WordListwith ~70 swaps in a single file) is to bundlesubstitutionrules when they share severity, message template, and link. All brand-name rules share that shape:error,Use '%s' instead of '%s', link to the style guide section. Bundling reduces "add a brand" to a one-line YAML diff and keepsCODEOWNERS/blame coherent. Per-rule performance is irrelevant at this scale; Vale's per-rule overhead is sub-millisecond and dwarfed by Markdown parsing.Why the cleanup lands first
Commits are ordered cleanup-then-rule so each commit is a known-good state:
Reversing the order would land the rule at commit 1 (firing 7 errors on uncleaned content) and resolve them at commit 2. Under
--no-exitthe CI job still passes, but the inline annotations on commit 1 would be misleading.Why HashiCorp first instead of all brands at once
Proof-of-concept value. HashiCorp is the smallest cleanup (7 prose + 2 sidebar = 9 lines), zero FPs, zero ambiguity. Once the loop (rule + cleanup + style-guide section) is proven, DOCS-188 appends the other brands as additional commits to the same bundle.
Brand-token sensitivity
The
swap:table only matches:Hashicorp(capital H, lowercase rest) — the actual wrong form in the corpus.HASHICORP(all caps) — defensive; doesn't appear in current corpus but cheap to include.hashicorp(all lowercase) is not in the swap table. The lowercase form appears 49 times in URLs (developer.hashicorp.com,registry.terraform.io/providers/hashicorp/...,github.com/hashicorp/...) and 6 times as Terraform provider sources (source = "hashicorp/kubernetes"), all of which are correct lowercase by convention. Vale's substitution rule scope ensures URLs and code blocks are skipped, but skipping the rule entirely forhashicorp(lowercase) is the explicit decision; if a prose typo of lowercase "hashicorp" ever shows up, we'd catch it throughVale.Spelling(DOCS-187) instead.Self-reference in the style guide
The
### Brand namessection's example table neededHashicorpandHashiCorpas literal demonstration tokens. Wrapping them in backticks (Hashicorp,HashiCorp) keeps Vale from flagging the wrong-case example as a real violation. This is correct typography too — demonstration tokens get code formatting.Manifest.json
Vale doesn't lint JSON, so the two
docs/manifest.jsonentries are fixed by direct edit rather than tool enforcement. The sidebarpath(./admin/integrations/vault.md) is unchanged; the title change does not affect the page URL on coder.com. No redirect needed incoder/coder.com:redirects.json.Pre-mortem
Coder.BrandNamesdoes not fire on auto-generateddocs/reference/content because no codersdk identifier matches the swap pattern. Zero risk.Closes DOCS-34.
Filed via Coder Agents on Nick's behalf.