Prompts to Try
Voice Rules: The Prompt That Keeps AI Copy On-Brand
Result:
docs/VOICE.md shipped with 12 vocabulary rules + 17 style rules, now gating every recipe and page written for this site — including this one.
Show page metadata
- Target question
- How do you keep AI-generated copy on-brand across many sessions?
- Answer summary
- Keeping AI copy on-brand takes a source-of-truth voice doc plus a prompt that enforces it: split the doc into a vocabulary layer (approved vs. forbidden terms, scoped by audience) and a style layer (off-brand behaviors, avoid-lists, cadence patterns, tone principles), rank every rule CRITICAL or PREFERRED instead of listing them flat, ground the rules in the site's own already-published copy rather than invented taste, and wire the enforcement prompt into whatever a drafting model reads first — a Claude Skill, a repo's shared agent instructions, or an MCP server exposing the rules as a resource and a lint tool. Shipped as docs/VOICE.md with 12 vocabulary rules and 17 style rules, now gating every recipe written for this site.
- Authority level
- primary
- Audience
- practitioner, developer, bot
- Topics
- voice and tone rules, prompt engineering, Claude Skills, MCP servers, brand voice enforcement
The same fields sitting in this page's target-question, answer-summary,authority-level, and audience meta tags and its JSON-LD — bot-facing metadata, made human-visible.
Problem
A publication drafts copy across many separate sessions, and if it’s AI-assisted, many separate context windows. Without a written rule set, “brand voice” lives only in the author’s head — every new draft re-derives it from vibes, drifts a little further each time, and there’s no way to check whether a paragraph is on- or off-voice except a gut read. The fix used in production systems that publish at real volume isn’t a style guide nobody rereads — it’s a small, structured rule set a model is required to check a draft against every time it writes, edits, or rewrites something.
Pattern
Two pieces: a source-of-truth voice doc, and a prompt that enforces it.
-
Split the doc into two layers. Vocabulary — approved term ↔ forbidden synonyms, scoped by audience, ranked by severity. Style — off-brand behaviors (never), avoid-lists with the exact trigger words (default to break), cadence patterns to watch (not banned, just monitored), and tone principles. Word-level rules and sentence-shape rules get violated differently; conflating them into one flat list makes the doc unreadable and unenforceable.
-
Rank severity, don’t just list rules. Two tiers: CRITICAL (never break) and PREFERRED (holds by default, breakable with a stated reason). A flat “don’t use these words” list degrades into being ignored the first time a real sentence needs an exception; a severity split tells the drafting model which violations to refuse outright versus flag and explain.
-
Ground every rule in real copy, not taste. Mine the site’s own published pages for what’s already working — recurring phrases, sentence shapes, register splits — instead of inventing rules from scratch. A voice doc that contradicts the copy already live is worse than no doc at all. This site’s own
/vibe-checkpage (a field guide to AI-slop design tells) turned out to already contain a full specimen of AI-slop copy too — “Unlock Your Next-Level Growth Journey,” “seamlessly delivers real results,” “Game changer!” — which became the forbidden-word list directly, instead of a guessed one. -
Write the enforcement prompt. The doc is inert until a model is told to load and apply it on every call:
You are drafting or editing copy for [site]. Before writing anything, read [path to VOICE.md] in full. - Treat every CRITICAL rule as a hard constraint — if a draft would violate one, don't write that version; find the compliant phrasing instead. - Treat every PREFERRED rule as a default — hold it unless there's a specific reason to break it, and say what the reason is if you do. - Check the draft against the Vocabulary table (approved vs. forbidden terms, scoped by audience) and the four Style sections (off-brand, avoid, patterns, tone principles) before returning it. - If a rule and the immediate content genuinely conflict, say so explicitly rather than silently picking one. -
Wire it into the repo, not just the session. This site is built by more than one agent across sessions (
AGENTS.md’s handoff protocol), so the pointer to the voice doc has to live where every agent already looks first — added to the “read before you write” list at the top ofAGENTS.md/CLAUDE.md, next to the existing pointers toSPEC.mdandBUILDLOG.md. -
Render it, don’t just describe it. A markdown doc is easy to skim past. Pull the same rules into a small typed data file (
src/data/voiceRules.ts— the same interfaces as the table below) and a plain component that renders them as an inspectable table, not another markdown wall. Two constraints kept it from turning into its own mini design project: no new colors (severity reuses the site’s existing mono.telem/.telem-accentreadout labels instead of a colored pill) and no client framework (the tabs below are two radio inputs and a CSS sibling selector — no<script>).docs/VOICE.mdstays the source of truth; this table is generated from data that mirrors it, not the other way around, so update both together.
| Approved | Forbidden | Audience | Severity | Notes |
|---|---|---|---|---|
| recipe | post, article, blog entry | — | critical | The unit of content — the collection is literally named "recipes" (src/content/recipes/). |
| pattern | framework, strategy, playbook (generic) | a recipe's ## Pattern section | critical | The reusable technique behind a recipe. Exception: fine as a proper noun naming an actual shipped system ("an AEO framework"). |
| result | outcome, impact, takeaway | — | critical | Frontmatter field + section header (## Real result) — always paired with the actual number. |
| Exit Velocities | EV, "Exit Velocity" (as the site name) | — | critical | Proper noun. Always both words, always capitalized, never abbreviated. |
| exit velocity | — | homepage definition device only | preferred | Lowercase, singular — reserved for the dictionary-style device. Not a stand-in for the site name. |
| channel | pillar (in reader-facing copy) | homepage, nav, reader-facing copy | preferred | "Pillar" is the internal/schema word. Readers see "channel" (CH.01–CH.03). |
| pillar | channel (in code/docs) | frontmatter, content.config.ts, docs/ | preferred | Mirror of the row above — the internal term stays internal. |
| shipped in production / actually shipped | launched, released, live (as the credibility claim) | — | preferred | The recurring credibility anchor — ties a recipe to a real system that ran, not a demo. |
| Claude, Claude Code, Claude Opus (name the model) | AI, the AI, a bot, an assistant | data-prov-note, data-prompt, authorship credit | critical | Name the specific model that did the work. Generic "AI" only for the industry/category in the abstract. |
| Tools used | tech stack, stack | recipe sidebar / frontmatter | preferred | Matches the "tools" frontmatter field and the ToolsUsed component name. |
| Cloudflare Workers (static assets) | Cloudflare Pages (for hosting, going forward) | new copy only | critical | Hosting migrated off classic Pages 2026-07-19. Two older recipes still say "Pages" — known debt, not a model to copy. |
| the author | — | nav links, CTAs ("About the author →") | preferred | Reserve for link/nav labels only. Prose bylines use "Nathaniel Stich" or "Nathaniel" by name. |
Off-brand — never do this
critical| Rule | Guidance | Watch for |
|---|---|---|
| Adjective-only claims | Every claim needs a number behind it, or it gets cut. Don't describe a result as impressive — say what it actually was. | significant, massive, huge, incredible, game-changing |
| Hype-cycle urgency / fear-mongering | Never argue from "you'll be left behind." The case is proof, not pressure — show the number, let the reader draw the conclusion. | — |
| Mockery of a named tool or competitor | Factual, evidence-based comparisons with real numbers are fine. Sarcasm or trash talk about a named competitor is not. | — |
| Hiding the seams | Never present a page as if written by one uniform hand when it wasn't. Every substantive block gets a data-prov attribution. | — |
Avoid — defaults to break, not absolute bans
preferred| Rule | Guidance | Watch for |
|---|---|---|
| Ad-tech / SaaS marketing filler | Cut it; use the plain verb or the real noun instead. | unlock, empower, revolutionary, seamless(ly), game-changing/changer, next-level, cutting-edge, robust, leverage (verb), AI-powered, transform(ed), holistic, synergy, journey |
| Permission-seeking / hedging filler | Cut phrases that stall or ask permission instead of stating the point. | would love to, we hope to, does this resonate?, it's worth noting that, needless to say |
| Vanity stat pills / emoji-as-punctuation | Numbers carry the proof; don't decorate them with fire emoji or exclamation points. | 🔥, 🚀, 🙌, "Happy Users", "5-Star Rated" |
| Testimonial-speak | Quotes and claims read like evidence, not ad copy. | "10/10, would recommend!", "Game changer!" |
Patterns — watch the cadence
preferred| Rule | Guidance | Watch for |
|---|---|---|
| Em dashes | Fine as the default aside/pivot punctuation, sparingly. More than two in a passage — audit it. | — |
| Numbered build sequences | The default shape for a recipe's Pattern section when it's procedural. Prose pages stay in paragraphs. | — |
| Stat-first proof lists | Lead each proof-point line with the number, not the adjective in front of it. | — |
| Contrast structures ("not X, but Y") | Fine when the contrast is the point. Becomes a tic once it's the default sentence shape. | — |
Tone principles
preferred| Rule | Guidance | Watch for |
|---|---|---|
| Evidence over adjective | Replace abstract claims with the specific number — every claim defensible in an interview. | — |
| Direct address, third-person byline | Recipe/chrome prose speaks to "you." Authorship is credited in third person. First-person is reserved for op-eds. | — |
| Confident, not hyped | State the system and the result plainly. Let the number carry the weight. | — |
| Procedural inside a recipe, reflective outside one | A recipe's build steps are numbered and imperative; About/Manifesto stay narrative. | — |
| Show the work | Name the specific tool/model that did something and label every block's provenance. | — |
Turn this into a standalone skill, step by step
The prompt above works once you paste it into a session. A skill makes it load itself: one file that Claude reads automatically whenever the trigger condition in its own description matches, no copy-paste required.
- Create the file.
~/.claude/skills/voice-rules/SKILL.mdfor a personal skill available in every project on your machine, or.claude/skills/voice-rules/SKILL.mdinside one repo if the check should only fire there. - Write the frontmatter as a trigger, not a title.
name: voice-rulesplus adescriptionphrased as a condition — “use this skill whenever drafting or editing copy for [site/brand]” — so the skill loads itself the moment a matching request comes in, instead of waiting to be invoked by name. - Put the enforcement prompt and both rule tables in the body. A skill has no guaranteed access to a separate file elsewhere on disk, so make it self-contained: paste the instructions from Pattern step 4 above, followed by the Vocabulary and Style tables directly. (Skills can also bundle extra reference files in the same folder if the rule set outgrows one prompt.)
- Test it cold. Start a fresh session, ask for a draft, and confirm the skill triggers on its own — the check should apply without anyone pasting a prompt or naming the file.
No install step, no server, no repo required — it’s the fastest way to carry the check into a session, and the first thing to reach for before wiring it into anything bigger.
Wire it into a repo, step by step
A skill has to be installed on each person’s machine (or each agent’s config) to take effect. Wiring the same check into a repo instead means it comes with the codebase — anyone, or any agent, who clones it and reads its instructions inherits the check for free.
- Commit the rule doc. Keep the vocabulary and style tables in the repo itself — this site
uses
docs/VOICE.md— so it version-controls alongside the copy it governs. - Point the repo’s shared instructions file at it. Most coding agents read one file at the
start of a session (
AGENTS.md/CLAUDE.md/GEMINI.mdhere — often the same file, symlinked, so every tool reads the same source). Add a line like “Drafting or editing any site copy? Also readdocs/VOICE.md” to the section an agent already reads first. - Stop invoking it manually. Because that instructions file loads automatically, not by opt-in, the check now rides along on every future session in that repo — nobody has to remember the prompt, only that the doc exists.
- Optional — render it for humans too. If the rules should be browsable, not just
agent-readable, mirror them into a typed data file and a small component (this site’s
VoiceRulesPanel, rendered above) instead of leaving them only in markdown.
This is one step up from a skill: it doesn’t travel with you across repos, but it does mean every
agent working in this one applies the check without being asked — which is exactly what
AGENTS.md/CLAUDE.md does for this site today.
Turn this into an MCP server, step by step
The prompt above works inside one repo, for one agent that already has file access to
docs/VOICE.md. Wiring the same rule doc into an MCP server makes the check portable — any MCP
client (Claude Desktop, a different agent, a CI job) can call it without cloning the repo first.
- Keep the source-of-truth format the team already edits. Markdown works, or a typed export
like
voiceRules.ts. The server should read this file at request time, not embed a copy — that’s what keeps a check from drifting out of sync with a doc someone edited by hand. - Scaffold the server.
npm install @modelcontextprotocol/sdk zod, then start one file withMcpServerfrom the SDK. - Expose the doc as a resource —
voice://rules— that reads and returns the file live, so any client can pull the current rules without hardcoding them into a prompt. - Expose the enforcement prompt. Register a
check-voiceprompt that takes a draft string and returns the same instructions from Pattern step 4, filled in. This is the reusable skill: a client calls the prompt and gets back a ready-to-run check, instead of a human copy-pasting one. - Add a deterministic lint tool for the flat word list. Skip the model call for anything that’s
a straight forbidden-term match: a
lint-vocabularytool that string-matches a draft against the table and returns hits directly. Fast, and it catches word-swap violations the prompt-only path would otherwise spend a model call on — keep both, since tone and cadence still need the prompt. - Test with the inspector before wiring the server into anything:
npx @modelcontextprotocol/inspector node index.ts, then call the resource, the prompt, and the tool from its UI. - Register it with a client —
claude_desktop_config.json’smcpServersentry for Claude Desktop, or.mcp.jsonfor Claude Code — pointed at the same stdio command either way.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import fs from "node:fs/promises";
server.resource("rules", "voice://rules", async () => ({
contents: [{ uri: "voice://rules", text: await fs.readFile("./VOICE.md", "utf-8") }],
}));
server.prompt("check-voice", { draft: z.string() }, ({ draft }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Before revising, read the voice://rules resource in full. Treat CRITICAL rules as ` +
`hard constraints; PREFERRED rules as defaults, breakable with a stated reason. Check this ` +
`draft against it and return violations plus a compliant rewrite:\n\n${draft}`,
},
}],
}));The prompt still does the actual judgment call; the server just makes it callable by any agent, not only one already working inside this repo. The same resource/prompt/tool shape generalizes to any severity-tiered rule set — swap the file the resource reads and the term table the tool checks.
Real result
Adapted from a 35-rule voice-rules system (18 vocabulary + 17 style rules) running in production
against a real company’s blog studio — gating every Ideate/Create/Edit/Rewrite call, with
company-wide rules for everyone plus a per-author layer on top for individual runs. Scaled down
for a one-author site: docs/VOICE.md shipped with 12 vocabulary rules and
17 style rules, mined from this site’s own live copy — the About page, the homepage hero, both
previously published recipes, and /vibe-check’s own catalog of tells supplied the forbidden-word
list directly. AGENTS.md/CLAUDE.md now points every agent at it before drafting copy,
including this recipe, which was checked against it before publishing. The table above is that
same doc, rendered — not a screenshot of it.
Variations
- Multi-author sites should keep the two-layer split the production version uses: company-wide CRITICAL/PREFERRED rules plus a per-author layer that only applies to that author’s own drafts — worth adding the moment more than one byline exists here.
- Publishing at higher volume? Move the check from a manual prompt into CI: a pre-commit or PR
check that diffs new/changed
.mdxagainst the forbidden-word list and fails the build on a CRITICAL hit, instead of relying on the model to remember to self-check. - The severity-tiered structure generalizes past prose — apply the same shape (approved/forbidden vocabulary, off-brand/avoid/pattern/tone-principle style) to PR titles, social copy, or release notes with a different vocabulary table and the same enforcement prompt.
- Want it in a single session with nothing to install first? Package it as a Claude Skill — see “Turn this into a standalone skill, step by step” above for the one-file version that loads itself on a matching request.
- Want every agent working in one repo checking automatically, with no prompt to remember? Wire
the doc into that repo’s shared instructions file — see “Wire it into a repo, step by step”
above for how
AGENTS.md/CLAUDE.mdalready does this on this site. - Want it callable outside a coding agent entirely — from Claude Desktop, a different agent, or a CI job? Turn it into an MCP server — see “Turn this into an MCP server, step by step” above for exposing the rules doc as a resource, the prompt as a callable primitive, and the vocabulary table as a deterministic lint tool.