feat(docs): add content linting — Vale, markdownlint, cspell - #1324
Conversation
- Custom ACP Vale style with rules for active voice, sentence length, heading style, terminology, jargon, and readability - ACP vocabulary (accept/reject lists for product-specific terms) - markdownlint config tuned for Starlight (allow HTML figures, disable line length, allow duplicate headings in siblings) - cspell config with ACP tech dictionary (~90 custom words) - GHA workflow: docs-lint.yml triggers on PRs touching docs/ - Makefile target: make docs-lint - Fix 9 markdownlint errors in existing docs (blank lines around lists, ordered list prefix) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughIntroduces documentation linting infrastructure via GitHub Actions workflow, Makefile target, and configurations for Vale, markdownlint, and cspell. Adds Vale style rules for documentation quality checks (active voice, heading style, jargon, readability, sentence length, terminology). Includes minor Markdown formatting adjustments to existing documentation files. Changes
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
✅ Deploy Preview for cheerful-kitten-f556a0 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| name: Lint Documentation | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: '22' | ||
| cache: 'npm' | ||
| cache-dependency-path: docs/package-lock.json | ||
|
|
||
| - name: Install Vale | ||
| run: | | ||
| curl -sfL https://github.com/errata-ai/vale/releases/download/v3.12.1/vale_3.12.1_Linux_64-bit.tar.gz | tar xz -C /usr/local/bin vale | ||
|
|
||
| - name: Install npm tools | ||
| run: npm install -g markdownlint-cli2 cspell | ||
|
|
||
| - name: Run Vale | ||
| working-directory: docs | ||
| run: vale src/content/docs/ | ||
|
|
||
| - name: Run markdownlint | ||
| working-directory: docs | ||
| run: markdownlint-cli2 "src/content/docs/**/*.md" | ||
|
|
||
| - name: Run cspell | ||
| working-directory: docs | ||
| run: cspell lint --no-progress "src/content/docs/**/*.md" | ||
|
|
||
| - name: Build docs (structural validation) | ||
| working-directory: docs | ||
| run: | | ||
| npm ci | ||
| npx playwright install --with-deps chromium | ||
| npm run build |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
.github/workflows/docs-lint.yml (1)
25-30: Harden tool installation for reproducibility and supply-chain safety.Line 27 executes a downloaded archive without checksum verification, and Line 30 installs unpinned global npm packages (
latestdrift). Prefer checksum validation and pinned versions (ornpm ci+npm execfrom locked devDependencies).Suggested patch
- name: Install Vale run: | - curl -sfL https://github.com/errata-ai/vale/releases/download/v3.12.1/vale_3.12.1_Linux_64-bit.tar.gz | tar xz -C /usr/local/bin vale + curl -fsSLO https://github.com/errata-ai/vale/releases/download/v3.12.1/vale_3.12.1_Linux_64-bit.tar.gz + echo "<vale_tarball_sha256> vale_3.12.1_Linux_64-bit.tar.gz" | sha256sum -c - + tar xzf vale_3.12.1_Linux_64-bit.tar.gz + install -m 0755 vale /usr/local/bin/vale - name: Install npm tools - run: npm install -g markdownlint-cli2 cspell + run: npm install -g markdownlint-cli2@<pinned-version> cspell@<pinned-version>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/docs-lint.yml around lines 25 - 30, The "Install Vale" step currently streams and extracts a remote tarball without verifying integrity and the "Install npm tools" step installs unpinned global packages; update the "Install Vale" step to download the artifact to a file, validate it against a known checksum (e.g., sha256) before extraction, and fail the job if the checksum does not match, and update the "Install npm tools" step to avoid global unpinned installs by either pinning package versions (e.g., markdownlint-cli2@<version> cspell@<version>) or, preferably, add these tools to devDependencies and use npm ci followed by npm exec (or npx) to run them from the lockfile so builds are reproducible and supply-chain safer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/docs-lint.yml:
- Around line 8-13: Update the "Lint Documentation" job (job key `lint`, name
"Lint Documentation") to explicitly scope the workflow token by adding a
`permissions` block with least privilege (e.g., `contents: read`) and pin any
used GitHub Actions to commit SHAs rather than floating versions; also review
the job steps for any secret exposures and ensure secrets are referenced only
via `secrets.*`.
- Around line 15-20: Replace the mutable action tags with pinned commit SHAs for
the GitHub Actions steps that use actions/checkout@v6 and actions/setup-node@v6:
locate the steps referencing "uses: actions/checkout@v6" and "uses:
actions/setup-node@v6", query their release tag SHAs (e.g., via the repository
releases page or git ls-remote) and replace the `@v6` short tag with the full
40-character commit SHA for each action, ensuring the workflow now references
the immutable commit SHAs instead of the version tags.
---
Nitpick comments:
In @.github/workflows/docs-lint.yml:
- Around line 25-30: The "Install Vale" step currently streams and extracts a
remote tarball without verifying integrity and the "Install npm tools" step
installs unpinned global packages; update the "Install Vale" step to download
the artifact to a file, validate it against a known checksum (e.g., sha256)
before extraction, and fail the job if the checksum does not match, and update
the "Install npm tools" step to avoid global unpinned installs by either pinning
package versions (e.g., markdownlint-cli2@<version> cspell@<version>) or,
preferably, add these tools to devDependencies and use npm ci followed by npm
exec (or npx) to run them from the lockfile so builds are reproducible and
supply-chain safer.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f3a6dbb5-78ce-4b09-8dec-65267762fb44
📒 Files selected for processing (17)
.github/workflows/docs-lint.ymlMakefiledocs/.cspell.jsondocs/.markdownlint.jsondocs/.vale.inidocs/.vale/styles/ACP/ActiveVoice.ymldocs/.vale/styles/ACP/HeadingStyle.ymldocs/.vale/styles/ACP/Jargon.ymldocs/.vale/styles/ACP/Readability.ymldocs/.vale/styles/ACP/SentenceLength.ymldocs/.vale/styles/ACP/Terminology.ymldocs/.vale/styles/config/vocabularies/ACP/accept.txtdocs/.vale/styles/config/vocabularies/ACP/reject.txtdocs/src/content/docs/concepts/workspaces.mddocs/src/content/docs/features/session-sharing.mddocs/src/content/docs/guides/migrating-shared-sessions.mddocs/src/content/docs/workflows/spec-kit.md
| jobs: | ||
| lint: | ||
| name: Lint Documentation | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
|
|
There was a problem hiding this comment.
Scope workflow token permissions explicitly.
permissions is not set, so token scope depends on repository defaults. For this lint-only job, set least privilege (e.g., contents: read).
Suggested patch
jobs:
lint:
name: Lint Documentation
+ permissions:
+ contents: read
runs-on: ubuntu-latestAs per coding guidelines, ".github/workflows/**/*.{yml,yaml}: - Pin action versions to SHA. Verify secrets are not exposed and permissions are scoped."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| jobs: | |
| lint: | |
| name: Lint Documentation | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| jobs: | |
| lint: | |
| name: Lint Documentation | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/docs-lint.yml around lines 8 - 13, Update the "Lint
Documentation" job (job key `lint`, name "Lint Documentation") to explicitly
scope the workflow token by adding a `permissions` block with least privilege
(e.g., `contents: read`) and pin any used GitHub Actions to commit SHAs rather
than floating versions; also review the job steps for any secret exposures and
ensure secrets are referenced only via `secrets.*`.
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v6 | ||
| with: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for repo in checkout setup-node; do
echo "== actions/${repo} =="
for tag in v6 v5 v4; do
printf " %-3s -> " "$tag"
resp="$(curl -fsSL "https://api.github.com/repos/actions/${repo}/git/ref/tags/${tag}" || true)"
if echo "$resp" | jq -e '.ref and .object.sha' >/dev/null 2>&1; then
echo "$resp" | jq -r '"\(.ref) \(.object.sha)"'
else
echo "NOT_FOUND"
fi
done
doneRepository: ambient-code/platform
Length of output: 478
Pin GitHub Actions to immutable commit SHAs.
Lines 16 and 19 use mutable major version tags (actions/checkout@v6, actions/setup-node@v6) instead of pinned commit SHAs. This violates the workflow hardening guideline and can break unexpectedly if the tag is retagged or removed.
Obtain the commit SHAs from GitHub's releases page (e.g., https://github.com/actions/checkout/releases/tag/v6) or via git ls-remote https://github.com/actions/checkout refs/tags/v6, then replace with the full 40-character SHA.
Patch structure (replace SHA placeholders with actual commit hashes)
- - name: Checkout
- uses: actions/checkout@v6
+ - name: Checkout
+ uses: actions/checkout@<commit-sha>
- - name: Setup Node.js
- uses: actions/setup-node@v6
+ - name: Setup Node.js
+ uses: actions/setup-node@<commit-sha>Per guideline: ".github/workflows/**/*.{yml,yaml}: Pin action versions to SHA."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/docs-lint.yml around lines 15 - 20, Replace the mutable
action tags with pinned commit SHAs for the GitHub Actions steps that use
actions/checkout@v6 and actions/setup-node@v6: locate the steps referencing
"uses: actions/checkout@v6" and "uses: actions/setup-node@v6", query their
release tag SHAs (e.g., via the repository releases page or git ls-remote) and
replace the `@v6` short tag with the full 40-character commit SHA for each action,
ensuring the workflow now references the immutable commit SHAs instead of the
version tags.
Summary
docs-lint.yml) triggers on PRs touchingdocs/— runs all three linters + Astro build in ~30 secondsmake docs-lintWhat the linters catch
Test plan
vale src/content/docs/— 0 errors, 11 warnings, 10 suggestionsmarkdownlint-cli2 "src/content/docs/**/*.md"— 0 errorscspell lint "src/content/docs/**/*.md"— 0 errorsnpm run build— passes (28 pages)🤖 Generated with Claude Code
Summary by CodeRabbit
Chores
Documentation