From ae479a3ec9c82caf9d6538e27e4c93e30c6d3136 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Mon, 22 Jun 2026 21:19:56 +0000 Subject: [PATCH 1/3] fix(.github/workflows/ci.yaml): preserve Vale severity in CI annotations The Vale prose lint step in CI ran 'vale --output=line' through a GitHub Actions problem matcher at .github/vale-problem-matcher.json. The matcher hard-codes severity=warning at the top level, and Vale's line format strips per-finding severity, so every Vale finding rendered as a GitHub 'warning' annotation, even error-level findings from Coder.BrandNames. Switch to 'vale --output=JSON' and pipe through jq to emit GitHub workflow commands directly. Mapping: Vale 'suggestion' -> ::notice::, Vale 'warning' -> ::warning::, Vale 'error' -> ::error::. URL-encode message bodies for '%', carriage return, and newline per the GitHub Actions workflow command spec. Delete the now-unused .github/vale-problem-matcher.json. The CI step stays continue-on-error: true so Vale annotations remain advisory and never block merge. See DOCS-426. --- .github/vale-problem-matcher.json | 18 ------------------ .github/workflows/ci.yaml | 22 +++++++++++++++++++--- 2 files changed, 19 insertions(+), 21 deletions(-) delete mode 100644 .github/vale-problem-matcher.json diff --git a/.github/vale-problem-matcher.json b/.github/vale-problem-matcher.json deleted file mode 100644 index bedf0e0b3e3da..0000000000000 --- a/.github/vale-problem-matcher.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "vale", - "severity": "warning", - "pattern": [ - { - "regexp": "^(.+):(\\d+):(\\d+):([^:]+):(.+)$", - "file": 1, - "line": 2, - "column": 3, - "code": 4, - "message": 5 - } - ] - } - ] -} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 321c647bdf61b..39ac276db1b96 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -234,9 +234,25 @@ jobs: echo "No changed Markdown files under docs/ on disk; skipping Vale." exit 0 fi - echo "::add-matcher::.github/vale-problem-matcher.json" - printf '%s\n' "$files" | xargs -d '\n' mise exec "aqua:errata-ai/vale" -- vale --no-exit --output=line - echo "::remove-matcher owner=vale::" + # Vale's --output=line strips per-finding severity, so the + # previous problem-matcher approach collapsed every finding to + # a single hard-coded severity. Use --output=JSON instead and + # emit GitHub workflow commands directly so error/warning/ + # suggestion render with their actual Vale severities. URL- + # encode message bodies for `%`, `\r`, and `\n` per the + # GitHub Actions workflow command spec. See DOCS-426. + printf '%s\n' "$files" \ + | xargs -d '\n' mise exec "aqua:errata-ai/vale" -- vale --no-exit --output=JSON \ + | jq -r ' + to_entries[] + | .key as $file + | .value[] + | (if .Severity == "suggestion" then "notice" + elif .Severity == "warning" then "warning" + else "error" end) as $level + | (.Message | gsub("%"; "%25") | gsub("\r"; "%0D") | gsub("\n"; "%0A")) as $msg + | "::\($level) file=\($file),line=\(.Line),col=\(.Span[0]),title=\(.Check)::\($msg)" + ' - name: Save Vale styles # Only the default branch is trusted to write the cache, so PR From eb7e3262a057525b5b9510e71d18614e72f1434a Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Mon, 22 Jun 2026 21:20:52 +0000 Subject: [PATCH 2/3] demo(docs/.style): add three-severity Vale annotation rendering canary Adds three throwaway Coder.Demo* rules and a single demo doc that triggers each rule exactly once. Together with the matcher fix in the previous commit, this PR's CI surfaces three GitHub annotations in three distinct severities (notice, warning, error). Used to visually verify that severity rendering works correctly after the fix. Files: - docs/.style/styles/Coder/DemoError.yml (level: error) - docs/.style/styles/Coder/DemoWarning.yml (level: warning) - docs/.style/styles/Coder/DemoSuggestion.yml (level: suggestion) - docs/.style/_vale-annotation-demo.md (one trigger phrase per rule) Each rule uses Vale's 'existence' extension type to match a single literal token (vale-demo-{error,warning,suggestion}-marker) that appears only in the demo doc. The demo doc carries a NOTE callout explaining its purpose. Demo content drops in a follow-up commit on this branch before merge; only the matcher fix lands. Or, on @vigilante's call, the demo stays as a permanent canary that monitors severity rendering across future CI changes. See DOCS-426. --- docs/.style/_vale-annotation-demo.md | 25 +++++++++++++++++++++ docs/.style/styles/Coder/DemoError.yml | 11 +++++++++ docs/.style/styles/Coder/DemoSuggestion.yml | 11 +++++++++ docs/.style/styles/Coder/DemoWarning.yml | 11 +++++++++ 4 files changed, 58 insertions(+) create mode 100644 docs/.style/_vale-annotation-demo.md create mode 100644 docs/.style/styles/Coder/DemoError.yml create mode 100644 docs/.style/styles/Coder/DemoSuggestion.yml create mode 100644 docs/.style/styles/Coder/DemoWarning.yml diff --git a/docs/.style/_vale-annotation-demo.md b/docs/.style/_vale-annotation-demo.md new file mode 100644 index 0000000000000..facbf2487eab7 --- /dev/null +++ b/docs/.style/_vale-annotation-demo.md @@ -0,0 +1,25 @@ +# Vale annotation rendering demo + +> [!NOTE] +> This page exists only to verify how GitHub renders Vale annotations +> at each severity level. +> The three `Coder.Demo*` rules under +> [`docs/.style/styles/Coder/`](styles/Coder/) fire on the marker +> strings below. +> This page and its rules disappear in a follow-up commit on the same +> PR once the rendering check completes; see DOCS-426. + +## Markers + +Each marker fires exactly one Vale annotation when this page lints: + +- Suggestion (rendered as GitHub `notice`): + vale-demo-suggestion-marker. +- Warning (rendered as GitHub `warning`): + vale-demo-warning-marker. +- Error (rendered as GitHub `error`): + vale-demo-error-marker. + +The rules use Vale's `existence` extension type and target a single +literal token each, so each marker produces a single annotation at its +exact location. diff --git a/docs/.style/styles/Coder/DemoError.yml b/docs/.style/styles/Coder/DemoError.yml new file mode 100644 index 0000000000000..366736701a5fa --- /dev/null +++ b/docs/.style/styles/Coder/DemoError.yml @@ -0,0 +1,11 @@ +# DemoError - canary rule used to verify how GitHub renders error-level +# Vale annotations in PR diffs. Fires on the literal phrase +# `vale-demo-error-marker`, which appears only in +# docs/.style/_vale-annotation-demo.md. Demo rules drop in a follow-up +# commit before the parent PR merges; see DOCS-426. +extends: existence +message: "[Demo] Error-level Vale annotation." +link: https://github.com/coder/coder/blob/main/docs/.style/_vale-annotation-demo.md +level: error +tokens: + - vale-demo-error-marker diff --git a/docs/.style/styles/Coder/DemoSuggestion.yml b/docs/.style/styles/Coder/DemoSuggestion.yml new file mode 100644 index 0000000000000..dd6dda6a34220 --- /dev/null +++ b/docs/.style/styles/Coder/DemoSuggestion.yml @@ -0,0 +1,11 @@ +# DemoSuggestion - canary rule used to verify how GitHub renders +# suggestion-level Vale annotations in PR diffs. Fires on the literal +# phrase `vale-demo-suggestion-marker`, which appears only in +# docs/.style/_vale-annotation-demo.md. Demo rules drop in a follow-up +# commit before the parent PR merges; see DOCS-426. +extends: existence +message: "[Demo] Suggestion-level Vale annotation." +link: https://github.com/coder/coder/blob/main/docs/.style/_vale-annotation-demo.md +level: suggestion +tokens: + - vale-demo-suggestion-marker diff --git a/docs/.style/styles/Coder/DemoWarning.yml b/docs/.style/styles/Coder/DemoWarning.yml new file mode 100644 index 0000000000000..bd03e1c8db36f --- /dev/null +++ b/docs/.style/styles/Coder/DemoWarning.yml @@ -0,0 +1,11 @@ +# DemoWarning - canary rule used to verify how GitHub renders +# warning-level Vale annotations in PR diffs. Fires on the literal phrase +# `vale-demo-warning-marker`, which appears only in +# docs/.style/_vale-annotation-demo.md. Demo rules drop in a follow-up +# commit before the parent PR merges; see DOCS-426. +extends: existence +message: "[Demo] Warning-level Vale annotation." +link: https://github.com/coder/coder/blob/main/docs/.style/_vale-annotation-demo.md +level: warning +tokens: + - vale-demo-warning-marker From c774ad3fbe99e2edf7eb65fe807297033eb83fd4 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Mon, 22 Jun 2026 21:29:52 +0000 Subject: [PATCH 3/3] feat(.github/workflows/ci.yaml): block merge on Vale error-level findings Drop `continue-on-error: true` and `vale --no-exit` from the Vale prose lint step so error-level findings fail the step, fail the required `lint-docs` job, and block merge. Warning- and suggestion-level findings still render as PR annotations but do not affect the exit code, matching Vale's native exit semantics (only errors trigger non-zero exit). `set -o pipefail` is required so Vale's exit code propagates through the jq pipe; without it, jq's success masks Vale's non-zero exit. Pairs with DOCS-425's "rules ship clean" doctrine: any rule enabled in `.vale.ini` ships with zero baseline error-level violations, so the new merge gate fires only when a PR introduces new prose that violates an enabled rule. The three-severity demo doc deliberately includes one error-level finding, so this PR's `lint-docs` job is expected to fail until the demo content drops in the follow-up cleanup commit before merge. --- .github/workflows/ci.yaml | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 39ac276db1b96..4a01e04dcf523 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -162,15 +162,19 @@ jobs: - name: Check docs run: pnpm check-docs - # Vale prose linter, advisory only. Scoped to changed Markdown under - # docs/. Every Vale step is `continue-on-error` so this section can - # never block the required `lint-docs` job: a `vale sync` network - # blip or a baseline rule violation surfaces as an annotation, not a - # merge gate. Only markdownlint/table-formatter above stay blocking. - # `vale --no-exit` additionally keeps the baseline error count from - # un-overridden upstream Google rules from failing the step. Lives - # here rather than a standalone workflow so docs lint stays in the - # single required CI umbrella (see #25608). See DOCS-40. + # Vale prose linter. Scoped to changed Markdown under docs/. The + # surrounding setup steps (cache restore, sync, cache save) stay + # `continue-on-error` so cache or network flakes never block merge; + # only the prose lint step itself is allowed to fail the job. + # Severity model: error-level Vale findings exit non-zero and fail + # the step, which fails the required `lint-docs` job and blocks + # merge. Warning- and suggestion-level findings render as PR + # annotations but do not affect the exit code. This matches Vale's + # native exit semantics (only errors trigger non-zero exit) and the + # repo's style-guide doctrine that any rule we enable ships with + # zero baseline noise. Lives here rather than a standalone workflow + # so docs lint stays in the single required CI umbrella (see + # #25608). See DOCS-40, DOCS-425, DOCS-426. - name: Detect changed Markdown id: changed-md continue-on-error: true @@ -216,16 +220,18 @@ jobs: - name: Vale prose lint if: steps.changed-md.outputs.any_changed == 'true' - continue-on-error: true env: ALL_CHANGED_FILES: ${{ steps.changed-md.outputs.all_changed_files }} # Non-interactive: let mise auto-install the pinned Vale on first use. MISE_YES: "1" run: | + # pipefail propagates Vale's exit code through the jq pipe so + # an error-level finding fails the step. Without it, jq's + # success would mask Vale's non-zero exit. + set -o pipefail # all_changed_files is ACMRD and so lists paths this PR deleted. - # Vale errors on a missing file (--no-exit only suppresses alert - # exits, not runtime errors), so keep only docs/ paths still on - # disk. See DOCS-40. + # Vale errors on a missing file, so keep only docs/ paths still + # on disk. See DOCS-40. files=$(printf '%s\n' "$ALL_CHANGED_FILES" \ | tr ',' '\n' \ | grep -E '^docs/' \ @@ -241,8 +247,12 @@ jobs: # suggestion render with their actual Vale severities. URL- # encode message bodies for `%`, `\r`, and `\n` per the # GitHub Actions workflow command spec. See DOCS-426. + # Vale exits non-zero only on error-level alerts (warnings and + # suggestions never trigger non-zero exit), so dropping + # --no-exit lets error-level findings fail the step while + # leaving lower-severity findings advisory. See DOCS-425. printf '%s\n' "$files" \ - | xargs -d '\n' mise exec "aqua:errata-ai/vale" -- vale --no-exit --output=JSON \ + | xargs -d '\n' mise exec "aqua:errata-ai/vale" -- vale --output=JSON \ | jq -r ' to_entries[] | .key as $file