Skip to content

ci: block casing mistakes in changed docs lines - #775

Open
hlenke wants to merge 3 commits into
mainfrom
docs/casing-lint
Open

hlenke wants to merge 3 commits into
mainfrom
docs/casing-lint

Conversation

@hlenke

@hlenke hlenke commented Oct 30, 2026

Copy link
Copy Markdown
Member

Adds a casing check to static-docs-checks that fails a PR when a line it adds or changes breaks the docs casing rules. Existing text elsewhere in a file never fails the check.

Rules (also in AGENTS.md under "Casing and terminology", so agents follow them while writing):

  • Headings, frontmatter title/sidebarTitle and component title="…" use sentence case. Acronyms and mixed-case names pass; other names that keep their capitals are listed in styles/Checkly/proper-nouns.txt.
  • Check and monitor types: Browser check, API check, Multistep check, URL monitor, Heartbeat monitor and the rest. "Playwright Check Suite" and "Agentic Check" stay as product names.
  • Single feature instances are lowercase ("a status page", "each private location").
  • Third-party names use official casing (GitHub, Node.js, OpenTelemetry, Opsgenie…).
  • Code, commands, URLs, bold UI labels and quoted text are skipped.

How: a styles/Checkly Vale style for the term rules, and .github/scripts/check-casing.mjs for sentence case. Run it locally with npm run lint:casing, or npm run lint:casing -- --all <file.mdx> for whole files.

Tested:

🤖 Generated with Claude Code

Adds a Checkly Vale style (check and monitor types, third-party names, single
feature instances) and a script that checks headings, frontmatter titles and
component titles for sentence case. Both run only on the lines a PR adds or
changes, so existing text never fails a PR. The rules are in AGENTS.md under
"Casing and terminology"; run them locally with npm run lint:casing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
checkly-422f444a 🟢 Ready View Preview Oct 30, 2026, 9:29 PM

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hlenke hlenke left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs changes · The check's logic is sound. Resolve the merge conflict with main's anchor check, and fix the one rule that suggests the wrong replacement.

Blocker: merge conflict. GitHub reports CONFLICTING. Since the merge base (d7a8349b), main landed 96f0763d ("ci: fail PRs on links to missing #anchors"). That commit inserts the Test anchor checker and Broken-anchors scan steps right where this PR adds Casing check in .github/workflows/static-docs-checks.yml. It also adds check:anchors/test:anchors next to this PR's lint:casing in package.json. Keep both sides in both files.

CI hasn't exercised the rules yet. In this PR's static-docs-checks run, the Casing check step downloaded Vale 3.25.1 fine, then printed "Casing check: no changed .mdx lines to check." The PR changes no .mdx file, so the green check says nothing about the rules. The PR description lists local runs (a sample page, #774, today's open PRs). The other gate scripts on main ship with node --test suites (test:anchors, test:css-urls, test:api-docs). This one becomes a required check that blocks PRs, so consider a small fixture test for the sentence-case function and one Vale rule per file. Optional, but it keeps a regex change from silently blocking every PR.

What I checked in the logic, all fine:

  • Changed-line detection parses git diff --unified=0 hunk headers from the merge base. Pure deletions add no lines. fetch-depth: 0 in the workflow makes merge-base "$BASE_SHA" HEAD resolvable.
  • api-reference/ and api-reference-old/ are skipped.
  • Frontmatter and fenced code are skipped for Vale, and matches inside **bold**, inline code, and quotes are dropped.
  • .github/vale/checkly.ini runs only BasedOnStyles = Checkly from ../../styles. The repo's .vale.ini names its styles explicitly (Vale, Google, proselint, write-good, alex), so the new styles/Checkly/ folder doesn't change the existing Vale runs.
  • The $1 replacements in FeatureInstances.yml are supported (Vale substitution docs).
  • A match that already equals its replacement raises no alert, so 'Open ?[Tt]elemetry' doesn't flag a correct "OpenTelemetry". See the same/matchToken check in internal/check/substitution.go.

Inline: 1 Important, 1 Wording.

What I checked
  • Read every file in the diff from merge base d7a8349b with git show/git diff: check-casing.mjs, checkly.ini, the workflow, AGENTS.md, package.json, Brands.yml, CheckTypes.yml, FeatureInstances.yml, and proper-nouns.txt. I did not run any code from the PR.
  • git diff <base> origin/main on the touched files: only 96f0763d, which changed the workflow and package.json.
  • gh pr checks 775: all pass, and vale-spellcheck was skipped. Read the static-docs-checks log for the Casing check step.
  • origin/main:.vale.ini and the existing styles/ tree.
  • Vale docs and source for substitution capture groups and the skip-when-equal check (links above).
  • Could not verify: the false-positive rate on real .mdx changes. I relied on the PR description's local runs, since CI had no .mdx to check.

swap:
'(?:Browser|browser) Checks': Browser checks
'(?:Browser|browser) Check': Browser check
'browser checks?(?= (?:in|on|with|run|that|for)\b)': Browser check

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: the optional s? makes this rule suggest the singular for plurals. With ignorecase: false and a literal replacement, "browser checks in…" produces "Use 'Browser check' instead of 'browser checks'", and an author who applies it verbatim drops the plural. The other type rules list the plural and singular as separate entries, so do the same here:

Suggested change
'browser checks?(?= (?:in|on|with|run|that|for)\b)': Browser check
'browser checks(?= (?:in|on|with|run|that|for)\b)': Browser checks
'browser check(?= (?:in|on|with|run|that|for)\b)': Browser check

Comment thread styles/Checkly/Brands.yml
'OpsGenie': Opsgenie
'Pagerduty|PagerDUTY': PagerDuty
'Hashicorp': HashiCorp
'Kubernetes(?=s)|kubernetes(?= (?:cluster|deployment|pod))': Kubernetes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wording (optional): the first alternative, Kubernetes(?=s), can never raise an alert. It only matches when the text already reads "Kubernetes", and Vale skips a match that equals its replacement. Only the lowercase alternative does anything. If that's the intent, drop the dead part:

Suggested change
'Kubernetes(?=s)|kubernetes(?= (?:cluster|deployment|pod))': Kubernetes
'kubernetes(?= (?:cluster|deployment|pod))': Kubernetes

This branch was successfully deployed

1 active deployment
staging — d7266899 Deployed Oct 30, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant