Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(docs/.style): enable Coder.GerundHeading - #25502

Merged
nickvigilante merged 11 commits into
mainfrom
vigilante/docs-191-add-coder-gerundheading-rule
Jul 23, 2026
Merged

feat(docs/.style): enable Coder.GerundHeading#25502
nickvigilante merged 11 commits into
mainfrom
vigilante/docs-191-add-coder-gerundheading-rule

Conversation

@nickvigilante

@nickvigilante nickvigilante commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Coder.GerundHeading, a warning-level Vale rule that flags headings and titles whose first word ends in -ing (a gerund or present participle used as a verb form, like Installing or Configuring).

Task headings read better in the imperative (Install Coder); concept headings read better as nouns (Installation). The choice is context-dependent, so the rule is a warning: it annotates without blocking CI.

The style-guide section this rule enforces already lives on main at capitalization-and-punctuation.md#no-gerund-leading-headings. This PR adds the matching rule and nothing else: the net diff is a single file.

What's in this PR

  • docs/.style/styles/Coder/GerundHeading.yml (new). Heading-scoped existence rule, anchored regex ^[A-Z][a-z]+ing\b, level: warning.
  • exceptions: mirror the style guide's Exceptions section: -ing words that name a feature, category, or attribute (Logging, Monitoring, Networking, Tracing, Troubleshooting, Pricing, Billing, ...) plus words that only look like gerunds (Bring, String, ...).

This branch was rebuilt onto main's restructured docs/.style/ (the single style-guide.md became a style-guide/ directory and Vale moved into ci.yaml), which is why the diff is now just the rule.

Scope: rule only

The rule ships as a warning, so it surfaces the existing -ing task headings (~200) as advisory annotations rather than failing CI. De-gerunding those headings (imperative rewrites plus internal anchor fixes) is a corpus-wide content change and lands in a dedicated follow-up PR, tracked separately. Splitting keeps this PR to the rule and keeps the content churn reviewable on its own.

Decision log

existence + scope: heading, not sequence + tag: VBG. Vale's POS-tagging sequence rules are hardcoded to sentence scope and never reach heading text, so a VBG sequence rule fires on paragraphs and stays silent on H1-H6. Google's and Microsoft's heading rules all use the existence+regex pattern; this rule follows it.

Exceptions align to the committed style guide, not the original branch design. The first draft of this rule intentionally left concept-noun gerunds (Logging, Monitoring, ...) in the flagged set. Since then, main's style guide declared exactly those as non-violations. The rule now excepts them so the rule and the guide agree. An excepted first word is allowed everywhere, which is a deliberate precision trade-off for a first-word regex: Monitoring Coder (a task) is not flagged, but the standalone concept heading Monitoring stays clean.

Severity = warning. The imperative-vs-noun choice is judgment-bound, which is the case the warning tier exists for: strong guidance, legitimate human-judgment exceptions, no CI block.

Verification. make lint/prose loads the rule cleanly; the excepted words (Troubleshooting, Monitoring, Networking, Logging, Contributing, Styling, Scaling, Routing, Pricing, Billing, Tracing) each produce zero findings.


Opened via Coder Agents on @nickvigilante's behalf.

Adds a private contributor-tooling directory at docs/.style/ that holds:
- README.md explaining the convention and the no-deploy guarantee
- style-guide.md as a scaffold for the canonical prose style guide
- styles/Coder/ as the home for custom Vale rules (filled by follow-ups)

Defense-in-depth tweaks to .github/workflows/deploy-docs.yaml exclude
docs/.style/** from the push trigger and from the surgical-reindex git
diff. coder.com/docs route discovery is already manifest-driven, so
nothing under docs/.style/ becomes a route or an Algolia record.

Also:
- .github/.linkspector.yml: skip external-link checks under docs/.style/
- AGENTS.md: point agents at the new style guide
- .claude/docs/DOCS_STYLE_GUIDE.md: cross-link to the canonical prose
  guide; this file remains the structure/research companion

The Vale configuration that consumes docs/.style/styles/ lands in a
follow-up PR (DOCS-40).

Closes DOCS-180.
Lands the Vale prose linter as a non-blocking docs CI step. Builds on the
scaffold from DOCS-180 (#25466):

- .vale.ini at the repo root configures Google + curated write-good
  + cherry-picked alex rules. Disables Google.Spacing (false positives
  on codersdk type names in the auto-generated API reference),
  Google.EmDash (conflicts with our em-dash ban), Google.Latin (i.e.
  and e.g. are fine), write-good.Passive and write-good.E-Prime
  (judgment-heavy).
- mise.toml pins Vale 3.7.1 via aqua.
- Makefile adds build/vale-$VERSION install, docs/.style/.vale-synced
  sentinel that runs 'vale sync' once per .vale.ini change, and a
  lint/prose target wrapped in '|| true' for v1 non-blocking severity.
- .github/workflows/docs-ci.yaml adds a 'prose' step that lints only
  the changed Markdown files under docs/, with continue-on-error: true
  and a cache for the synced styles and binary.
- .gitignore excludes the synced upstream styles and the sentinel.
- .markdownlint-cli2.jsonc ignores the synced styles so local
  markdownlint runs do not lint upstream READMEs.
- docs/.style/README.md and style-guide.md document how to run Vale
  locally and what the active rule set is.

Severity policy (v1): every rule lands at 'warning'. CI is non-blocking
through continue-on-error: true. A rule promotes to 'error' only when
(a) it is objectively correct and (b) the existing-content violation
count reaches zero. Judgment rules stay at 'suggestion'.

Local run on the full docs corpus produces 0 blocking failures, 391
errors, 5202 warnings, 7682 suggestions across 458 files in ~20s.

Refs DOCS-40.
… filter

The 'docs/**.md' glob in tj-actions/changed-files skips dot-prefixed
directories by default, so the changed-md-docs filter silently dropped
docs/.style/README.md and docs/.style/style-guide.md. The Vale prose
step never fired on PR #25467 as a result.

Drop the second changed-files step and post-filter the changed-md
output in shell. grep '^docs/' keeps only docs paths; grep -v
'^docs/.style/styles/' excludes the synced upstream packages. The
early exit handles PRs that only touched non-docs markdown.

Refs DOCS-40.
Vale's release archive places 'vale' at the archive root with no
leading ./ (unlike typos), so 'tar -xzf - ./vale' matched nothing and
produced 'tar: ./vale: Not found in archive' on CI. Switch to 'vale'.

The local Makefile invocation worked before because build/vale-3.7.1
was already present from the spike artifacts. CI hit the cold path
and exposed the bug.
Resolves the 12 findings from review id 4314529409 on PR #25467.

DEREM-1 (P2): fix `./build/vale-*/vale` glob in docs/.style/README.md;
  the binary is the file itself, not a directory.
DEREM-2 (P3): drop the inaccurate `make lint` claim in style-guide.md;
  point readers at README's Running Vale section instead.
DEREM-3 (P2): split the prose step into `Prepare Vale styles` (no
  continue-on-error) and `prose` (no continue-on-error; --no-exit), so
  the job fails on sync failures but lints non-blocking.
DEREM-4 (P2): replace `|| true` with Vale's native --no-exit in the
  Makefile and rewrite the severity-policy comment to match the
  measured exit-code semantics (Vale exits non-zero only on error
  alerts, regardless of MinAlertLevel).
DEREM-5 (P2): add `Coder` to BasedOnStyles so the empty starter style
  is loaded and ready to receive rule files.
DEREM-6 (P3): replace hardcoded per-package paths in .gitignore and the
  workflow cache step with `styles/*` plus a `!styles/Coder` negation,
  reducing the places future packages need to be listed.
DEREM-7 (P3): already addressed by c3e9e2f (prose filter matches
  docs/.style paths) and 6054d9e (tar layout).
DEREM-8 (P3): remove the duplicate Running Vale block in style-guide.md
  and cross-reference README.md as the single source.
DEREM-9 (P3): add .github/vale-problem-matcher.json and wire the prose
  step with `::add-matcher::`/`--output=line`/`::remove-matcher::` so
  alerts surface as inline PR annotations.
DEREM-10 (P4): trim the Active rule set section in style-guide.md to a
  policy summary plus a pointer to .vale.ini.
DEREM-11 (Nit): rephrase the Makefile sync-sentinel comment to describe
  Make's behavior instead of suggesting `touch`.
DEREM-12 (Nit): swap "(Vale spike, 2026-05-18)" for the more neutral
  "(measured 2026-05-18)" in the Google.Spacing rationale.

Two-layer defense on the cache step protects the hand-authored
Coder rules: the negation excludes styles/Coder from the cached paths,
and the cache key hashes styles/Coder/** so any rule change invalidates
the cache even if the negation behavior ever regresses
(actions/toolkit#713, actions/cache#494).

Verification:
- make lint/actions: clean (zizmor, actionlint).
- make fmt/markdown: no changes.
- make lint/markdown: 0 errors across 463 files.
- make lint/prose: exit 0; baseline 391 errors / 5201 warnings / 7673
  suggestions across 458 files (consistent with the pre-review run).
Three follow-ups from review id 4320840969.

DEREM-13 (P3): drop the redundant `grep -v '^docs/\.style/styles/'`
filter from the prose step. Synced upstream packages are gitignored, so
tj-actions/changed-files never lists them; the filter only ever rejects
tracked Coder/**.md files, which is the opposite of what we want. The
inline comment now documents why no second filter exists.

DEREM-14 (P3): fix the README's --no-exit explanation. The DEREM-4 fix
chain reached .vale.ini, the Makefile, and style-guide.md but missed
docs/.style/README.md. The README now matches the rest: --no-exit
suppresses the exit from the baseline error count produced by
un-overridden Google error-level rules, not from warnings/suggestions
(which never trigger non-zero exit regardless of --no-exit).

DEREM-15 (Nit): same model fix in the docs-ci.yaml prose-step comment.

Verification: make lint/actions, make fmt/markdown, make lint/markdown,
make lint/prose all clean. Vale baseline unchanged (391 errors / 5201
warnings / ~7673 suggestions across 458 files).
DEREM-16 (Nit) from review id 4321386259. `./build/vale-* docs/` is fine
when only one vale-X.Y.Z binary exists in build/, but if a developer
bumps the version in mise.toml without running `make clean`, multiple
binaries coexist and the glob expands to multiple positional args,
breaking the command. Low-risk path because three conditions have to
align (version bump + no clean + manual invocation), but worth a sentence
so a developer who hits it knows to `make clean`.
Adds a warning-level Vale rule that flags headings beginning with an
-ing word (typically a gerund or present participle, like 'Installing'
or 'Configuring').

The rule uses extends: existence with scope: heading and a regex
anchored to the start of the heading text, since Vale's sequence rule
type (which would let us condition on the POS tag VBG) is documented
as sentence-scoped and does not honor scope: heading. Google and
Microsoft style packages both use existence + scope: heading + regex
for all their heading-targeted rules; this rule follows the same
pattern.

A small exceptions list covers words that end in 'ing' but are not
gerunds (Bring, String, Spring, King, Ring, Sting, Sing, Thing, Wing).
Concept-noun gerunds (Logging, Networking, Monitoring, Troubleshooting)
are intentionally NOT in the exceptions list: those headings often
read better as imperatives or full nouns and writers should see the
warning and decide.

Adds a 'Headings' section to docs/.style/style-guide.md with the
'Gerund headings' subsection explaining the rule, examples, and how to
silence individual instances.

Updates docs/.style/styles/Coder/README.md to list DOCS-191.

Closes DOCS-191.
@github-actions

Copy link
Copy Markdown
Contributor

Docs preview

📖 View docs preview for docs/.style/style-guide.md

…-code reason sequence rules cannot reach headings

After a question from a reviewer about whether this rule should
use `extends: sequence` with the VBG POS tag rather than a regex
existence rule, dig into Vale 3.14.1 source and add the
architectural reason to the rule's YAML comment.

Two facts in Vale's source force the existence-based approach for
heading-targeted rules:

  1. internal/check/sequence.go:75 ends NewSequence with
     `rule.Definition.Scope = []string{"sentence"}`. The Run
     method's comment at line 247 reads
     "This is *always* sentence-scoped." Any user-supplied
     `scope:` on a sequence rule is silently overwritten.

  2. internal/lint/ast.go::lintScope dispatches heading content
     with scope `text.heading.h2.md` to lintBlock directly,
     skipping the lintProse path. Only lintProse calls nlp.Compute,
     which is the function that produces `sentence.*`-scoped
     sub-blocks. Heading text therefore never appears as a block
     whose scope contains `sentence`.

Empirically verified against Vale 3.14.1: an `extends: sequence`
rule with `pattern: '\\w+ing'` + `tag: VBG` + `scope: heading`
fires on paragraph text but is silent on H1-H6.

Behavior of the rule is unchanged. Only the YAML comment is
updated to capture the investigation so the next reviewer doesn't
have to redo it.
@github-actions github-actions Bot added the stale This issue is like stale bread. label Jun 3, 2026
@github-actions github-actions Bot closed this Jun 7, 2026
@nickvigilante nickvigilante reopened this Jun 17, 2026
@nickvigilante nickvigilante removed the stale This issue is like stale bread. label Jun 17, 2026
@nickvigilante

Copy link
Copy Markdown
Contributor Author

Reopening. The stale bot auto-closed this on 2026-06-07 from inactivity, but the work is still active, tracked in Linear as DOCS-191 (In Review) under the Vale / docs style-guide initiative. This PR is stacked on vigilante/docs-40-... (the Vale CI PR, #25467), so it was sequenced behind that base rather than abandoned. I've removed the stale label and reset the timer; checks here may stay amber until the base (#25467) lands.

Reopened by Coder Agents on behalf of @nickvigilante.

Base automatically changed from vigilante/docs-40-add-vale-to-docs-ci-with-starter-microsoftgoogle-style-coder to main June 22, 2026 18:14
@datadog-coder

This comment has been minimized.

@github-actions github-actions Bot added the stale This issue is like stale bread. label Jun 30, 2026
@github-actions github-actions Bot closed this Jul 4, 2026
@nickvigilante nickvigilante reopened this Jul 6, 2026
@nickvigilante nickvigilante removed the stale This issue is like stale bread. label Jul 6, 2026
@nickvigilante
nickvigilante requested a review from bpmct July 8, 2026 14:27
Catch up to main's restructured docs/.style/ (style-guide directory, Vale
in ci.yaml). Tree matches main; the rule lands in the next commit.
@nickvigilante nickvigilante changed the title feat(docs): add Coder.GerundHeading Vale rule to flag -ing-leading headings feat(docs/.style): enable Coder.GerundHeading Jul 8, 2026
@nickvigilante
nickvigilante marked this pull request as ready for review July 8, 2026 15:50
@github-actions github-actions Bot added the stale This issue is like stale bread. label Jul 16, 2026
@github-actions github-actions Bot closed this Jul 19, 2026
@nickvigilante nickvigilante reopened this Jul 19, 2026
@nickvigilante nickvigilante removed the stale This issue is like stale bread. label Jul 19, 2026
@nickvigilante
nickvigilante enabled auto-merge (squash) July 23, 2026 17:57
@nickvigilante
nickvigilante merged commit 66a55e1 into main Jul 23, 2026
88 of 91 checks passed
@nickvigilante
nickvigilante deleted the vigilante/docs-191-add-coder-gerundheading-rule branch July 23, 2026 20:38
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants