docs: consolidate voice/writing-style/terminology guidance into the brand MCP (supersedes #19777) - #20664
docs: consolidate voice/writing-style/terminology guidance into the brand MCP (supersedes #19777)#20664CamSoper wants to merge 9 commits into
Conversation
Make the Pulumi brand guide (served by the public pulumi-brand MCP server) the source of truth for voice and writing-style, and have the docs STYLE-GUIDE and content skills consult it instead of restating it. - STYLE-GUIDE.md: trim to Hugo/repo mechanics + a pointer to the brand guide's voice/writing-style sections; add a precedence rule (the brand guide wins on any overlap, including social/SEO/AEO). - AGENTS.md: point content rules at the brand guide; standardize headings on sentence case at every level; precedence rule. - glow-up: require the pulumi-brand MCP (fail loud if unreachable) and defer voice/prose/terminology to it. - prose-patterns: source-of-truth note pointing at brand writing-style. - new-doc / new-blog-post: suggest sentence-case titles; point at the guide. - .vale.ini: put punctuation outside quotes; note the sentence-case standard. - claude-code-review.yml / content-review-article.yml: wire the public brand MCP (mcp_config + get_guidelines/search_guidelines) so CI review can consult voice/writing-style. SEO and social guidance intentionally stay in this repo's skills for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… input) The pinned anthropics/claude-code-action@v1 rejects a top-level mcp_config input (logged 'Unexpected input(s) mcp_config' and ignored it). v1 expects custom MCP servers in claude_args via --mcp-config; the config merges with the action's built-in GitHub MCP server. Move the pulumi-brand HTTP server there and keep its tools in --allowed-tools. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fallout from rebasing onto master: Pulumi.HeadingSentenceCase and its .vale.ini entry both described Title Case H1 as the convention, which now contradicts AGENTS.md and STYLE-GUIDE.md. Restate the comments as what the scoping actually is — sentence case is the standard at every level; the rule stays scoped to H2+ so it doesn't flag the backlog of Title Case front-matter titles. No rule behavior changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
…es table The brand guide's new Names & terminology section (the `terminology` section on the brand MCP server) is now the canonical reference for product names and retired names. Bring the offline Vale mirror up to date with it: - Nomenclature: stop canonicalizing toward "Pulumi Insights" — a retired name the rule was actively reinforcing — and canonicalize "pulumi discovery" to Pulumi Discovery instead. - Substitutions: add the retired-names rows that have a single fixed replacement (Pulumi Copilot -> Pulumi Neo, Pulumi Insights -> Pulumi Discovery, Insights account/scan/supergraph/resources -> their Discovery equivalents, self-managed backend -> DIY backend). - DeprecatedProductNames: generalize from "Pulumi Service" to all retired names whose replacement is context-dependent (bare Insights, bare Copilot, IAS, Policy Violations), switching the rule to case-sensitive so generic "insights" never flags and excluding GitHub/Microsoft/VS Code Copilot. - Exempt the generated CLI command pages: the CLI still ships `pulumi insights ...` commands, so their help text legitimately carries the old names; terminology fixes belong upstream in pulumi/pulumi. - Point every rule message/link that cited a STYLE-GUIDE.md section this PR deletes at the brand guide section that now owns the rule. All tokens validated against fixtures (positive and negative cases) with vale 3.9.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
…-sync review Extend the brand-MCP consolidation to the brand guide's new Names & terminology section, and make "keep the Vale mirror in sync" an automated process instead of a comment: - STYLE-GUIDE.md: route naming questions (canonical names, preferred terms, retired names) to Names & terminology; drop the Preferred terminology table it duplicated; document the sync manifest and workflow under Automated checks. - AGENTS.md: name the terminology section explicitly and rewrite the Naming bullet around the retired-names table. - glow-up: load terminology alongside voice and writing-style. - styles/Pulumi/BRAND-SYNC.yaml: manifest mapping each mirrored Vale rule to the brand section it tracks, with last_synced. - .github/workflows/brand-style-sync.yml + brand-vale-sync skill: weekly drift check of the mirror against the live brand MCP server (retired names first). Drift -> draft PR with a guide-says/mirror-said/now audit trail; no drift -> last_synced bump. Fails loudly if the MCP is unreachable rather than syncing from memory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
91ff9d5 to
9fd3bf8
Compare
Every workflow that invokes claude-code-action now carries the public pulumi-brand MCP server (--mcp-config in claude_args) with its get_guidelines/search_guidelines tools allowed, so any Claude run that touches content — blog known-issues review, broken-link fixes, social-copy review, re-entrant review updates, and ad-hoc @claude help — can consult the brand guide's voice, writing-style, and terminology sections instead of relying on trained recollection of Pulumi style. Previously wired: claude-code-review, content-review-article, brand-style-sync. Added here: blog-review-index, check-links, claude-social-review, claude-update, claude. claude-new and review-existing-content invoke no Claude step themselves — they dispatch workflows that are already wired. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
There was a problem hiding this comment.
✅ No issues found
About Unblocked
Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.
📖 Documentation — Learn more in our docs.
💬 Ask questions — Mention @unblocked to request a review or summary, or ask follow-up questions.
👍 Give feedback — React to comments with 👍 or 👎 to help us improve.
⚙️ Customize — Adjust settings in your preferences.
Pre-merge Review — Last updated 2026-08-24T23:01:17ZTip Summary: This is a repo-governance PR, not user-facing content: it makes the Pulumi brand guide (via the public brand MCP server) the single source of truth for voice, writing style, and terminology, strips the duplicated guidance out of Review confidence:
Investigation log
🔍 Verification trail65 claims extracted · 14 verified · 0 unverifiable · 1 contradicted
🚨 Outstanding in this PRThese must be resolved or refuted before merging.
|
Extend Pulumi.HeadingSentenceCase to heading.h1, closing the H2+ carve-out
now that the brand guide states the standard explicitly ("Pulumi has no
title-case convention" — Names & terminology). In-body H1s are rare (3 in
the whole tree), because the site's real H1s are front-matter title:/h1:
fields — which Vale never sees, since it skips YAML front matter.
So the same rule is applied to those too: scripts/lint/
frontmatter-title-case.py extracts each target's title fields, runs them
through Pulumi.HeadingSentenceCase as synthetic one-line headings (same
check, same proper-noun exceptions — no duplicated logic to drift), and maps
findings back to the source file and line. Wired into `make lint-prose`
after the Vale pass; advisory and changed-files-scoped like everything else
there, so the backlog (~1,600 Title Case titles today, 676 of them in
historical blog posts) migrates one touched page at a time instead of as a
wall of findings. The gen-docs CLI pages keep their exemption.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
Co-authored-by: unblocked[bot] <98133410+unblocked[bot]@users.noreply.github.com>
Addresses the pre-merge review. Three defects, all in rules this PR added or
touched, plus the content backlog behind the outstanding finding.
Blocker backlog (outstanding finding). `self-managed backend` is genuinely in
the brand guide's retired-names table, so the row stays — but its 53
occurrences are migrated to `DIY backend` (40 non-blog; the repo already
preferred that term 95:53, and links to `#using-a-diy-backend`). The token
also missed the plural, since `\b` doesn't split `backend`/`backends`; both
forms are now covered. Backlog is zero, so the row keeps error level.
Automation safety. The brand guide's own preamble carves out two cases a
blanket swap gets wrong: "historical usage is fine" and "some replacements
can't be automated". Both occur here — content/case-studies/unity.md says
"Pulumi Neo ... replacing Pulumi Copilot" (the swap yields "Neo replacing
Neo") and carries a `{#copilot}` heading anchor, and "Insights" means the
Discovery product in most sentences but the "Discovery & governance" category
in others. So renamed-product rows move to a new advisory tier,
styles/Pulumi/RetiredNames.yml (warning, off the blocker list, still a
deterministic fix so review-existing-content's judgment gate applies). Rows
graduate to Substitutions once their backlog hits zero, the way
`self-managed backend` just did. BRAND-SYNC.yaml records the tier rule so the
weekly sync job files new entries correctly.
Unreliable lookbehinds. Vale drops a negative lookbehind that starts its
scoped window, so the same sentence flags or not depending on unrelated text
length: with `(?<!VS Code )\bCopilot\b`, one line is clean under an
8-character H1 and flags "VS Code Copilot" under a 17-character one.
Lookaheads are stable across the same sweep. DeprecatedProductNames drops
both lookbehind tokens (bare Insights and bare Copilot are now documented as
intentionally unmirrored) and keeps only lookahead and literal tokens.
Also fixes two pre-existing false positives the audit surfaced:
`language-native package` fired on "language-native package managers" (npm,
PyPI, Maven) in 13 of 13 cases at blocker tier, now excluded by lookahead;
and generic "Pulumi deployments"/"Pulumi Service" prose is corrected or, for
the literal Azure DevOps group name the product creates, exempted per-file.
Net effect on identical content: 31 blocker findings under these rules vs 46
under master's.
Low-confidence findings: the four sibling files still teaching "H1 = Title
Case" (new-changelog, case-studies archetype, questions-index) now match the
brand standard, and STYLE-GUIDE's glossary link points at the canonical
/docs/reference/glossary/ rather than the alias.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx
|
@claude #update-review All findings addressed in 🚨
|
| ignorecase: true | ||
| action: | ||
| name: replace | ||
| swap: | ||
| '\bPulumi Copilot\b': Pulumi Neo | ||
| '\bPulumi Insights\b': Pulumi Discovery | ||
| '\bInsights account\b': cloud account | ||
| '\bInsights scan\b': Discovery scan | ||
| '\bInsights supergraph\b': resource graph | ||
| '\bInsights resources\b': discovered resources | ||
| '\bPolicy Violations\b': policy findings |
There was a problem hiding this comment.
The old DeprecatedProductNames.yml explicitly used ignorecase: false and documented:
"Policy Violations": the retired console page name; the page is now "policy findings" (lowercase "policy violations" is fine in prose for what a policy check surfaces, and stays unflagged via case sensitivity).
By moving this token to RetiredNames.yml with ignorecase: true (needed for the other product-name tokens), the rule now matches lowercase "policy violations" in ordinary prose — e.g., "Remediating policy violations" in content/docs/insights/policy/policy-packs/authoring.md, "Each policy violation creates an issue" patterns, and similar natural English usage throughout the docs.
The backlog count of 3 (line 31) was calculated under case-sensitive matching; the actual case-insensitive hit count will be larger and dominated by false positives on legitimate English usage.
Consider either:
- Splitting
Policy Violationsinto its own case-sensitive rule (or back intoDeprecatedProductNames.yml), or - Adding
ignorecase: falseto a separate Vale rule just for this token, or - Changing the swap key to anchor on the Title Case form only:
'\bPolicy Violations\b'with a separateignorecase: falseoverride (Vale doesn't support per-token case settings, so it would need its own file).
📜 Review history
Important Please don't hide, resolve, or delete this comment! It breaks things! 📖 How pre-merge review works — the full lifecycle, short-circuits, and escape hatches. |
|
🤖 Review updated on @pulumi-bot's request. |
Proposed changes
Rebase of #19777 onto current
master(conflicts resolved), extended to cover the brand guide's new Names & terminology section — the canonical product names plus the "Retired and disallowed names" table — and to make the brand MCP the source of truth operationally, not just by declaration.Same intent as #19777: the Pulumi brand guide (public
pulumi-brandMCP server athttps://brand.pulumi.com/mcp) is the source of truth for voice, writing style, and now terminology;STYLE-GUIDE.mdand the content skills consult it rather than restate it, with a precedence rule (brand guide wins on overlap).What's here beyond the original #19777:
Nomenclature.ymlwas auto-canonicalizing prose toward "Pulumi Insights" — a retired name. Now:Substitutions.yml(auto-fixable, blocker tier) carries every retired-name row with a single fixed replacement: Pulumi Copilot → Pulumi Neo, Pulumi Insights → Pulumi Discovery, Insights account/scan/supergraph/resources → their Discovery equivalents, self-managed backend → DIY backend.DeprecatedProductNames.yml(flag-only, human picks the replacement) generalizes from "Pulumi Service" to all context-dependent retired names: bare Insights, bare Copilot (excluding GitHub/Microsoft/VS Code Copilot), IAS, Policy Violations. Case-sensitive so generic "insights" never flags.pulumi insights ...commands; terminology fixes there belong upstream in pulumi/pulumi.message:/link:that cited a STYLE-GUIDE section this PR deletes now cites the owning brand-guide section instead.HeadingSentenceCasenow coversheading.h1(the H2+ carve-out is gone), and since the site's real H1s are front-mattertitle:/h1:fields that Vale can't see, a new helper (scripts/lint/frontmatter-title-case.py, wired intomake lint-prose) runs those fields through the same Vale rule as synthetic headings and maps findings back to the source line — same check, same proper-noun exceptions, nothing duplicated to drift. Advisory and changed-files-scoped, so the measured backlog (~1,600 Title Case titles, 676 of them historical blog posts) migrates one touched page at a time instead of as a wall of findings.styles/Pulumi/BRAND-SYNC.yamlrecords which brand section each mirrored Vale rule tracks (last_syncedincluded). A new weekly workflow (brand-style-sync.yml, following the newbrand-vale-syncskill) re-reads those sections via the brand MCP, compares them to the mirror — retired names first, since renamed products rot fastest — and opens a draft PR with a guide-says / mirror-said / now audit trail when they drift, or alast_syncedbump when they don't. It fails loudly if the MCP is unreachable rather than syncing from memory.claude-code-actionnow wire thepulumi-brandserver viaclaude_args --mcp-configwithget_guidelines/search_guidelinesallowed: the two from docs: consolidate voice/writing-style guidance into the brand MCP #19777 (claude-code-review,content-review-article), the newbrand-style-sync, plusblog-review-index,check-links,claude-social-review,claude-update, andclaude(ad-hoc @claude).claude-newandreview-existing-contentare dispatchers with no Claude step of their own — the jobs they dispatch are wired.Conflict resolutions from the rebase (same calls as before, re-applied against newer master): kept master's
allowed_bots(workprentice), model flags (claude-opus-5 --effort low),applied[]verdict schema, broad-Bash rationale, and its Cross-reference sections / Brand assets / image-filename additions; took the branch's brand-MCP wiring and STYLE-GUIDE rewrite. The branch's execution-log fix + revert commits dropped out entirely — master fixed that upload path properly itself (runner.temp). The two substantive divergences from #19777 stand as before and need an author's confirmation: precedence (brand guide wins; master's "this guide wins here" section dropped) and heading case (sentence case at every level — verified against the brand guide: "Always use sentence case for headings" with no H1 carve-out, and the terminology section's explicit "Pulumi has no title-case convention").Recommendations beyond this PR (deliberately not implemented here):
brand.pulumi.com/api/terminology.json) that a build step compiles intoSubstitutions.yml/DeprecatedProductNames.yml/Nomenclature.yml. The sync workflow then becomes a freshness check only. Needs apulumi/marketing-webchange, so out of scope here.pulumiservice). The Vale exemption documents the status quo; it shouldn't be permanent.Unreleased product version (optional)
N/A
Related issues (optional)
Supersedes #19777.
🤖 Generated with Claude Code
https://claude.ai/code/session_015KpCy5B47c2oNqEeX1Znwx