diff --git a/.claude/docs/DOCS_STYLE_GUIDE.md b/.claude/docs/DOCS_STYLE_GUIDE.md index d9ec7165cb..e5c7a60b30 100644 --- a/.claude/docs/DOCS_STYLE_GUIDE.md +++ b/.claude/docs/DOCS_STYLE_GUIDE.md @@ -1,13 +1,19 @@ # Documentation Style Guide -This guide documents prose, structure, and formatting patterns for documentation files in the `docs/` directory. It complements, and does not replace, the canonical content rules. +This guide documents structure, research, and content patterns for documentation files in the `docs/` directory. It complements, and does not replace, the canonical content rules or the prose style guide. > [!IMPORTANT] > **What belongs in the docs (and what doesn't)** is governed by > [`docs/.style/content-guidelines.md`](../../docs/.style/content-guidelines.md). > Read that first. When this style guide conflicts with the content -> guidelines, the content guidelines govern. This file covers prose, -> formatting, and structural conventions only. +> guidelines, the content guidelines govern. +> +> **For prose rules**, the canonical Coder documentation style guide lives +> at [`docs/.style/style-guide.md`](../../docs/.style/style-guide.md) and +> will be enforced by the Vale rules under `docs/.style/styles/Coder/`. +> That guide is currently a scaffold; continue using the **Writing Style** +> section below until it is populated. This file also remains authoritative +> for structure, research, and content patterns. See [CONTRIBUTING.md](../../docs/about/contributing/CONTRIBUTING.md) for general contribution guidelines. diff --git a/.github/.linkspector.yml b/.github/.linkspector.yml index 25af1ebe41..012d9e7697 100644 --- a/.github/.linkspector.yml +++ b/.github/.linkspector.yml @@ -6,6 +6,9 @@ excludedDirs: - docs/reference # Older changelogs may contain broken links - docs/changelogs + # Contributor-facing style guide and Vale config. Not deployed to + # coder.com/docs; chasing external links here is overkill. + - docs/.style ignorePatterns: - pattern: "localhost" - pattern: "example.com" diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index dd569cdd52..e72ab7b613 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -45,7 +45,14 @@ on: # Intentionally only docs/**. Edits to this workflow file must not # auto-trigger a production reindex; use workflow_dispatch instead. # See DOCS-121 (incident) and DOCS-124 (fix). + # + # docs/.style/** is contributor tooling and never deploys to + # coder.com/docs. Negating it here skips the workflow on .style-only + # commits. GitHub Actions only suppresses when every changed file + # matches a negation, so mixed commits still trigger; the surgical + # diff step below drops .style paths from the payload. - "docs/**" + - "!docs/.style/**" release: # Fires when a draft release is published, when a release goes from # prerelease to non-prerelease, or when a release is created already @@ -174,7 +181,11 @@ jobs: # + save) from deleted/renamed-old-side (delete only), and # so paths containing whitespace or quotes survive intact. DIFF_FILE=$(mktemp) - git diff --name-status -z "$BEFORE_SHA" "$AFTER_SHA" -- 'docs/**/*.md' > "$DIFF_FILE" + # 'docs/**/*.md' to keep markdown-only paths, ':(exclude)docs/.style/**' + # so contributor-tooling pages never reach the surgical-reindex payload + # on mixed commits. The trigger filter already short-circuits .style-only + # pushes; this is defense in depth. + git diff --name-status -z "$BEFORE_SHA" "$AFTER_SHA" -- 'docs/**/*.md' ':(exclude)docs/.style/**' > "$DIFF_FILE" # Parse the NUL-delimited diff into \t lines. # `--name-status -z` uses NUL between fields and between # records, with a special twist for renames: the record is diff --git a/.github/workflows/docs-preview.yaml b/.github/workflows/docs-preview.yaml index 8f00114e65..9b96a536d7 100644 --- a/.github/workflows/docs-preview.yaml +++ b/.github/workflows/docs-preview.yaml @@ -24,6 +24,12 @@ on: - reopened paths: - "docs/**" + # docs/.style/** is contributor tooling and never deploys to coder.com. + # Skipping the workflow on .style-only PRs avoids posting a preview + # link that 404s, since manifest-driven coder.com routing rejects + # paths under .style. Mixed PRs still trigger; the selection logic + # below filters .style files out of the preview-target pick. + - "!docs/.style/**" concurrency: group: docs-preview-${{ github.event.pull_request.number }} @@ -69,11 +75,18 @@ jobs: "repos/${REPO}/pulls/${PR_NUMBER}/files" \ --jq '.[] | select(.status != "removed") | .filename') - # Pick the first Markdown file under docs/. `|| true` keeps - # the pipeline from failing when grep finds no matches or - # head triggers SIGPIPE under `set -o pipefail`. + # Pick the first Markdown file under docs/, excluding the + # contributor-tooling subtree at docs/.style/**. Mixed PRs that + # touch both .style and a public docs page should land the + # preview link on the public page; .style-only PRs already get + # short-circuited by the trigger filter above and never reach + # this code path. + # + # `|| true` keeps the pipeline from failing when grep finds no + # matches or head triggers SIGPIPE under `set -o pipefail`. first_doc=$(printf '%s\n' "$all_files" \ | grep -E '^docs/.*\.md$' \ + | grep -v '^docs/\.style/' \ | head -n 1) || true if [ -z "$first_doc" ]; then diff --git a/AGENTS.md b/AGENTS.md index 2ccd9249c3..90b488dcc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,7 @@ Rule #1: If you want exception to ANY rule, YOU MUST STOP and get explicit permi - Docs content scope: Use [Coder Docs Content Guidelines](docs/.style/content-guidelines.md) to decide whether a piece of content belongs in `docs/` at all. The Documentation Style Guide above covers prose and formatting; the content guidelines govern scope and routing and supersede the style guide on conflicts. - Compatibility: `.agents/docs` symlinks to `.claude/docs` for agent runtimes that look there. - Frontend: Read [Frontend Development Guidelines](site/AGENTS.md) before changing anything under `site/`. +- Docs prose: When editing anything under `docs/`, see the prose style guide at [`docs/.style/style-guide.md`](docs/.style/style-guide.md). It is currently a scaffold; until it is populated, use the **Writing Style** section in [`.claude/docs/DOCS_STYLE_GUIDE.md`](.claude/docs/DOCS_STYLE_GUIDE.md). That file also covers structure, research, and content patterns. ## Foundational rules diff --git a/docs/.style/README.md b/docs/.style/README.md index 6d00f5485e..2356bd919a 100644 --- a/docs/.style/README.md +++ b/docs/.style/README.md @@ -9,22 +9,24 @@ Nothing under this directory is published to | Path | Purpose | |-------------------------|---------------------------------------------------------------------| | `content-guidelines.md` | Canonical content rules: what belongs in `docs/`, what doesn't, why | +| `style-guide.md` | Canonical prose style guide for `docs/` | +| `styles/Coder/` | Custom Vale rules specific to Coder (product voice, terms) | See [`content-guidelines.md`](content-guidelines.md) for the canonical rules on what content belongs in `docs/` and what should be routed elsewhere (blog, changelog, Support KB, etc.). -> [!NOTE] -> This directory is the home for the docs scaffold being built out under -> DOCS-180. The prose style guide and the Vale rules that enforce it land -> in that work and will appear in this table when they merge. +See [`style-guide.md`](style-guide.md) for the prose style guide. The +`styles/Coder/` directory holds the custom Vale rules that enforce parts +of the guide; Vale's `StylesPath` in the repo-root `.vale.ini` (added in +a follow-up PR) points at `docs/.style/styles/`. ## Why a hidden directory The leading dot mirrors the `.github/`, `.vscode/`, and `.claude/` convention already used in this repo for tooling-internal directories. -The structural Markdown linters still pick it up; coder.com's docs site -does not. +Vale and the structural Markdown linters still pick it up; coder.com's +docs site does not. ## How exclusion from coder.com works @@ -38,10 +40,14 @@ manifest-driven: do not become routes. - The Algolia surgical indexer at [`coder/coder.com:src/utils/algoliaDocs/surgical.ts`](https://github.com/coder/coder.com/blob/master/src/utils/algoliaDocs/surgical.ts) - explicitly skips paths that are not in the manifest. + explicitly skips paths that are not in the manifest, incrementing + `pathsSkipped`. Net result: not adding anything from `docs/.style/` to `docs/manifest.json` -gives us no route, no Algolia record, and no sidebar entry. +gives us no route, no Algolia record, and no sidebar entry. Two +defense-in-depth changes in `.github/workflows/deploy-docs.yaml` keep the +deploy workflow from running on `.style`-only commits and exclude the +directory from the surgical-reindex payload on mixed commits. ## What still runs against this directory @@ -50,9 +56,28 @@ gives us no route, no Algolia record, and no sidebar entry. `markdownlint-cli2 --fix $(find docs -name '*.md')`. - `make fmt/markdown` (markdown-table-formatter) reflows tables here for the same reason. +- Vale, once `.vale.ini` lands in a follow-up PR, lints the entire + `docs/**/*.md` set including `docs/.style/style-guide.md`. + +## What does not run against this directory + +- `linkspector`: excluded via `excludedDirs` in `.github/.linkspector.yml`. + External-link checking is overkill for contributor tooling. +- The `deploy-docs` workflow: its `paths:` filter negates `docs/.style/**`, + and the surgical-reindex git-diff invocation excludes the same path. See + `.github/workflows/deploy-docs.yaml`. +- The `docs-preview` workflow: its `paths:` filter negates `docs/.style/**`, + so `.style`-only PRs produce no preview comment. The selection logic also + skips `.style` files when picking the preview target on mixed PRs. See + `.github/workflows/docs-preview.yaml`. ## Editing the content guidelines Open a PR against `docs/.style/content-guidelines.md`. The rules in that file apply to humans and AI-assisted workflows alike; when it conflicts with another style or contributing doc in the repo, it governs. + +## Editing the style guide + +Open a PR against `docs/.style/style-guide.md`. Follow-up PRs add each +rule and the matching style-guide section together. diff --git a/docs/.style/style-guide.md b/docs/.style/style-guide.md new file mode 100644 index 0000000000..19ed1874e2 --- /dev/null +++ b/docs/.style/style-guide.md @@ -0,0 +1,103 @@ +# Coder documentation style guide + +This is the canonical style guide for the Coder documentation. It is the +source of truth that the Vale rules in `docs/.style/styles/Coder/` enforce. + +Status: scaffold. Sections below are populated by follow-up PRs; this +page starts as a table of contents and grows as those PRs land. + +## How to use this guide + +This page is a scaffold while follow-up PRs land. Sections marked "To be +filled in" are placeholders. For anything not yet covered, see the +public summary at +[`docs/about/contributing/documentation.md`](../about/contributing/documentation.md). + +- **Contributors**: read the section that matches what you are writing. + Each rule notes the Vale rule ID, if any, so you can reproduce the + warning locally. +- **Reviewers**: cite the section in a review comment. Reviews are easier + when the guidance is in one place. +- **AI agents**: read this page in full before editing anything under + `docs/`. The Coder Agents and Claude Code guides + ([`AGENTS.md`](../../AGENTS.md), + [`.claude/docs/DOCS_STYLE_GUIDE.md`](../../.claude/docs/DOCS_STYLE_GUIDE.md)) + link here. + +## Voice and tone + +To be filled in by follow-up PRs. Planned coverage: + +- Active voice +- Second person +- Plural nouns and pronouns where number is uncertain +- Product voice (`stop` over `kill`, `turn off` over `disable` in + user-facing copy) +- Limiting "we" + +## Word choice + +To be filled in by follow-up PRs. Planned coverage: + +- Inclusive-language substitutions +- HashiCorp casing +- Dev Container terminology +- "Setup" vs "set up" and Quickstart casing +- "Next steps" vs "Learn more" +- Weasel words + +## Capitalization and punctuation + +To be filled in by follow-up PRs. Planned coverage: + +- Sentence case in titles and headings +- General capitalization policy +- Em-dash and en-dash ban (use comma, semicolon, or period) + +## Formatting + +To be filled in by follow-up PRs. Planned coverage: + +- Bold for UI elements +- Italics for parameter names and version variables +- Code font for user input, command-line utility names, filenames, + environment variables, HTTP verbs and status codes, placeholder + variables +- Code blocks with explicit language fences + +## Vale enforcement + +The repo-root `.vale.ini` configures Vale to read styles from +`docs/.style/styles/`. The starter configuration combines: + +- Google's developer-docs base style +- A curated subset of `alex` (inclusive-language) +- A curated subset of `write-good` (wordiness) +- Coder-specific custom rules in `docs/.style/styles/Coder/` + +The rationale for the cherry-picked base styles and the severity +policy lives in the follow-up PR that lands `.vale.ini`. + +## Editor setup + +To be filled in by a follow-up PR. Will cover VS Code, Cursor, +JetBrains, and Neovim. + +## Relationship to `docs/about/contributing/documentation.md` + +A public-facing prose summary lives today at +[`docs/about/contributing/documentation.md`](../about/contributing/documentation.md). +A follow-up PR will redirect that page to this guide; until then, +follow the public summary for anything the scaffolded sections above do +not yet cover. New prose rules land here; the public page is frozen +pending the redirect. + +## Third-party references + +When this guide does not cover something, consult: + +| Type of guidance | Reference | +|---------------------|-----------------------------------------------------------------------------------------| +| Spelling | [Merriam-Webster](https://www.merriam-webster.com/) | +| Style, nontechnical | [The Chicago Manual of Style](https://www.chicagomanualofstyle.org/home.html) | +| Style, technical | [Microsoft Writing Style Guide](https://learn.microsoft.com/en-us/style-guide/welcome/) | diff --git a/docs/.style/styles/Coder/README.md b/docs/.style/styles/Coder/README.md new file mode 100644 index 0000000000..2a70166eeb --- /dev/null +++ b/docs/.style/styles/Coder/README.md @@ -0,0 +1,40 @@ +# Coder custom Vale rules + +Custom Vale rules specific to Coder live here. Each rule is a YAML file +that Vale loads through the `BasedOnStyles = Coder` setting in the +repo-root `.vale.ini`. + +This directory is intentionally empty for now. Follow-up PRs add rules +incrementally. Planned starter rules: + +- Dev Container terminology +- HashiCorp casing +- Limit "we" +- Setup vs set up, Quickstart casing +- Next steps vs Learn more +- Vale substitution rule scaffold +- Weasel words +- Em-dash and en-dash mirror in Vale +- Inclusive-language substitutions +- Product-voice rules + +## Authoring a new rule + +1. Write a YAML file under this directory. Name it after the rule's + intent, for example `InclusiveLanguage.yml` or `ProductVoice.yml`. +2. Each rule's `message:` should link to the matching section in + `docs/.style/style-guide.md`, ideally with a deep-link anchor, so a + contributor reading a Vale warning can jump straight to the guidance. +3. Land at `level: warning` first. Promote to `level: error` only after + both conditions hold: + - The rule is objectively correct (typo, brand-name casing, banned + substitution). + - The existing-content violation count for the rule reaches zero. +4. A follow-up PR will add a parity CI check that verifies every rule + here has a matching section in `style-guide.md`. Add the section in + the same PR as the rule. + +## Reference + +- Vale docs: +- Vale rule types: