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.

  1. 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.

  2. 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.

  3. 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-check page (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.

  4. 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.
  5. 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 of AGENTS.md/CLAUDE.md, next to the existing pointers to SPEC.md and BUILDLOG.md.

  6. 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-accent readout 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.md stays the source of truth; this table is generated from data that mirrors it, not the other way around, so update both together.

ApprovedForbiddenAudienceSeverityNotes
recipepost, article, blog entrycriticalThe unit of content — the collection is literally named "recipes" (src/content/recipes/).
patternframework, strategy, playbook (generic)a recipe's ## Pattern sectioncriticalThe reusable technique behind a recipe. Exception: fine as a proper noun naming an actual shipped system ("an AEO framework").
resultoutcome, impact, takeawaycriticalFrontmatter field + section header (## Real result) — always paired with the actual number.
Exit VelocitiesEV, "Exit Velocity" (as the site name)criticalProper noun. Always both words, always capitalized, never abbreviated.
exit velocityhomepage definition device onlypreferredLowercase, singular — reserved for the dictionary-style device. Not a stand-in for the site name.
channelpillar (in reader-facing copy)homepage, nav, reader-facing copypreferred"Pillar" is the internal/schema word. Readers see "channel" (CH.01–CH.03).
pillarchannel (in code/docs)frontmatter, content.config.ts, docs/preferredMirror of the row above — the internal term stays internal.
shipped in production / actually shippedlaunched, released, live (as the credibility claim)preferredThe 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 assistantdata-prov-note, data-prompt, authorship creditcriticalName the specific model that did the work. Generic "AI" only for the industry/category in the abstract.
Tools usedtech stack, stackrecipe sidebar / frontmatterpreferredMatches the "tools" frontmatter field and the ToolsUsed component name.
Cloudflare Workers (static assets)Cloudflare Pages (for hosting, going forward)new copy onlycriticalHosting migrated off classic Pages 2026-07-19. Two older recipes still say "Pages" — known debt, not a model to copy.
the authornav links, CTAs ("About the author →")preferredReserve for link/nav labels only. Prose bylines use "Nathaniel Stich" or "Nathaniel" by name.

Off-brand — never do this

critical
RuleGuidanceWatch for
Adjective-only claimsEvery 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-mongeringNever 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 competitorFactual, evidence-based comparisons with real numbers are fine. Sarcasm or trash talk about a named competitor is not.
Hiding the seamsNever 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
RuleGuidanceWatch for
Ad-tech / SaaS marketing fillerCut 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 fillerCut 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-punctuationNumbers carry the proof; don't decorate them with fire emoji or exclamation points.🔥, 🚀, 🙌, "Happy Users", "5-Star Rated"
Testimonial-speakQuotes and claims read like evidence, not ad copy."10/10, would recommend!", "Game changer!"

Patterns — watch the cadence

preferred
RuleGuidanceWatch for
Em dashesFine as the default aside/pivot punctuation, sparingly. More than two in a passage — audit it.
Numbered build sequencesThe default shape for a recipe's Pattern section when it's procedural. Prose pages stay in paragraphs.
Stat-first proof listsLead 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
RuleGuidanceWatch for
Evidence over adjectiveReplace abstract claims with the specific number — every claim defensible in an interview.
Direct address, third-person bylineRecipe/chrome prose speaks to "you." Authorship is credited in third person. First-person is reserved for op-eds.
Confident, not hypedState the system and the result plainly. Let the number carry the weight.
Procedural inside a recipe, reflective outside oneA recipe's build steps are numbered and imperative; About/Manifesto stay narrative.
Show the workName 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.

  1. Create the file. ~/.claude/skills/voice-rules/SKILL.md for a personal skill available in every project on your machine, or .claude/skills/voice-rules/SKILL.md inside one repo if the check should only fire there.
  2. Write the frontmatter as a trigger, not a title. name: voice-rules plus a description phrased 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.
  3. 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.)
  4. 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.

  1. 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.
  2. 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.md here — often the same file, symlinked, so every tool reads the same source). Add a line like “Drafting or editing any site copy? Also read docs/VOICE.md” to the section an agent already reads first.
  3. 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.
  4. 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.

  1. 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.
  2. Scaffold the server. npm install @modelcontextprotocol/sdk zod, then start one file with McpServer from the SDK.
  3. Expose the doc as a resourcevoice://rules — that reads and returns the file live, so any client can pull the current rules without hardcoding them into a prompt.
  4. Expose the enforcement prompt. Register a check-voice prompt 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.
  5. 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-vocabulary tool 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.
  6. 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.
  7. Register it with a clientclaude_desktop_config.json’s mcpServers entry for Claude Desktop, or .mcp.json for 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 .mdx against 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.md already 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.

Nathaniel Stich writes Exit Velocities — recipes drawn from AI/growth systems actually shipped in production, not opinion pieces.

About the author