← Tidelines/Best practices

Rewrite: a brand voice that cancels itself out

Every agent prompt has a tone section, and it is the one section nobody reviews. It is also where the most literal contradiction in the file usually lives, written in adjectives instead of rules, which is why it never looks like one.

by TypeGlish team7 min read#best-practices
Professional. And casual.

TL;DR Adjectives in a tone section are claims on axes, not decoration: our tone is professional plus our tone is casual is a provable contradiction the checker blocks with logic/contradiction, and an off-vocabulary word like snappy is a claim it cannot see at all (prompt/undefined-adjective). Keep one value per axis, name the midpoint with $CONFIG adjective if the middle is what you mean, and turn the rest into measurable rules. Here that takes one prompt from F (36/100) to A (92/100).

Tone sections get written once, usually by copying the brand guidelines, and then never touched again. They read beautifully. They are also the least reviewed part of any agent prompt, because an adjective feels like a description rather than an instruction, and nobody argues with a description. The model does not read it that way. Every adjective in that block is a constraint competing with every other one, and when two of them point in opposite directions the model resolves the conflict per conversation, which arrives in your QA queue as "the bot sounds weirdly stiff sometimes." Here is a tone section with that defect, what the checker makes of it, and the rewrite.

§1The before

A support prompt for a coffee equipment retailer. The tone block is five lines lifted almost verbatim from a brand deck, which is exactly how these things get into a prompt.

voice.tg - before✗ F (36/100)
# Role
You are a support agent for Northwind, a home coffee equipment retailer.

# Tone of voice
- Our tone is professional.
- Our tone is casual.
- Our voice is snappy.
- MUST sound warm and approachable at all times.
- AVOID sounding robotic.

# Constraints
- MUST answer questions about brewing equipment and orders.
Read it as prose and nothing is wrong. Read it as a spec and there are two values for one property, one word the checker has never heard of, and two lines that ask for a quality with nothing observable attached.
tg check - output
voice.tg:5:1  error  logic/contradiction  Contradiction - single-valued "we.tone" is
  assigned 2 different values ("professional", "casual").
voice.tg:6:1  error  logic/contradiction  Contradiction - single-valued "we.tone" is
  assigned 2 different values ("professional", "casual").
voice.tg:7:1  error  prompt/undefined-adjective  "snappy" is not in the vocabulary -
  the claim is INVISIBLE to the checker. Define it:
$CONFIG adjective snappy
  - axis:: <name>
  - opposite:: <pole>
...or relax the dial ($CONFIG lexicon -> undefined_adjectives).
voice.tg:8:1  warn   prompt/unmeasurable  Unmeasurable rule - no observable action or
  bound, so nothing can check compliance.

 1 file - 3 error, 1 warning, 0 info
Three errors and a warning out of five lines of tone. The score puts it at F (36/100) with consistency 0, because a proven contradiction caps the grade no matter how good the rest of the file is.

§2Two adjectives, one axis

The first two lines are the interesting failure. Nothing about "professional" and "casual" looks like a rule collision, and if you put them in a table of MUST and NEVER statements neither one would show up. But an adjective in TypeGlish is a claim about a property on a named axis, and professional and casual are the two ends of one axis: formality. The prompt asserts both. There is no reply that satisfies it.

This is worth sitting with, because it is the same defect as the modal conflicts in your prompt argues with itself, just wearing different clothes. The model does what it always does with a contradiction: picks a side per conversation, weighted by whatever the current exchange resembles. Your tone drifts, and the prompt looks innocent because the two lines that fight each other are adjectives that both sound like things you want.

An adjective is not a description. It is a claim, and two claims on one axis is a coin flip you shipped.

To be clear about the scope of this: the checker is not banning adjective stacks. Words on different axes coexist happily, because they constrain different properties, and a tone section made of those compiles clean.

two axes, no collision✓ compiles
# Role
You are a support agent for Northwind, a home coffee equipment retailer.

# Tone of voice
- The tone is warm.
- The tone is professional.
Warmth and formality are two different properties, so warm and professional are not competing for one slot. The checker only objects when two words claim the same one.

There is a quieter version of the same problem, worth knowing because it survives review even more easily: two adjectives on one axis pointing the same way, at different strengths. "Very formal" up top and "formal" further down is not a conflict, it is a restatement, and the weaker line only subtracts certainty from the stronger one.

same axis, same direction✗ logic/axis-strength
# Role
You are a support agent for Northwind, a home coffee equipment retailer.

# Tone of voice
- The tone is very formal.
- The tone is formal.
The checker's words: One truth per axis - "very formal" already pins "tone.formality / formal-informal"; "formal" is dead weight. Keep the stronger. Same logic as restating a rule at two strengths, which is the subject of say it once, or say it's optional.

§3The word the checker can't see

Line 7 fails differently, and this is the one that changes how you write these sections. Our voice is snappy is not a contradiction. It is a claim on an axis that does not exist, so the checker has nowhere to put it: it cannot tell you that snappy conflicts with something, and it cannot tell you that it doesn't. prompt/undefined-adjective is an error by default for exactly that reason. A claim the checker can only hold with one hand is worse than no claim, because you think you constrained something.

You have three honest ways out, and picking between them is the actual editorial work in a tone section.

  • Define it - if the word carries real meaning for your brand, give it an axis and an opposite with $CONFIG adjective. Now it is a first-class claim, and it can conflict with things, which is the point.
  • Replace it with the behavior you meant - most of the time "snappy" is not a style claim at all. It is a length rule wearing an adjective. Write the rule.
  • Relax the dial - $CONFIG lexicon can drop undefined_adjectives to warn or off. Legitimate for a prose-heavy persona block; a bad default for a constraints section.

§4The rewrite

Now the interesting question. The tone block did mean something: whoever wrote "professional" and "casual" was not confused, they wanted the middle of that axis, the register of a good human agent who is neither stiff nor chummy. Asserting both ends is a bad way to ask for the middle. Naming the middle is a good one: define an adjective on the formality axis with a weight, and then make one claim.

Everything else in the block splits cleanly into "measurable rule" or "delete." Snappy becomes a sentence cap. "Not robotic" becomes the concrete thing that actually reads as human: use the customer's name. "Warm and approachable at all times" was already flagged as unmeasurable, and once greeting-by-name and a three-sentence cap exist, it has nothing left to add.

voice.tg - after✓ compiles · A (92/100)
<$CONFIG>
  $CONFIG adjective conversational
    - axis:: tone.formality
    - weight:: 0.45
</$CONFIG>

# Role
@@ role: the domain "helpful" is measured against
You are a support agent for Northwind, a home coffee equipment retailer.

# Tone of voice
@@ tone: one named point on the formality axis, so an edit that moves it is a red build
- The tone is conversational.

# Constraints
@@ brevity: "snappy" is a feeling, three sentences is a bound
- MUST reply in at most 3 sentences.
@@ greeting: what "not robotic" was reaching for
- MUST greet the customer by name in the first message.
@@ scope: brewing and orders are the domain, everything else is a handoff
- MUST answer questions about brewing equipment and orders.
Five tone lines became one tone line and two rules. weight:: 0.45 is the midpoint claim: conversational sits just off center on the formal-informal axis, which is the register the original block was gesturing at. F (36/100) to A (92/100), and the file is shorter.

The definition is not documentation. Once conversational lives on tone.formality, it behaves like any other value on that axis, which means the next person who drops "and keep it formal" into the tone block gets a red build instead of a silent drift.

the definition is load-bearing✗ won't build
<$CONFIG>
  $CONFIG adjective conversational
    - axis:: tone.formality
    - weight:: 0.45
</$CONFIG>

# Role
You are a support agent for Northwind, a home coffee equipment retailer.

# Tone of voice
- The tone is conversational.
- The tone is formal.
The checker: Contradiction - "tone" is "conversational" and "formal", and "tone.formality" cannot hold both sides of its axis. A word you defined yourself is now provable against the rest of the file. That is the whole return on defining it.
Field note

Adjective axes, $CONFIG adjective, and the undefined_adjectives dial are part of the lexical plane the checker runs on every file, and every figure above is re-run by CI on each commit, so the grades and the diagnostics are facts rather than screenshots. If the split in §4 looks familiar, it is the same move as the rewrite of a bloated support prompt applied to style instead of policy: the vague line either becomes measurable or becomes nothing. And the rules that come out of the split are worth pinning, because a tone rule with no test is exactly the kind of thing the score cannot feel, as the leave-one-out ablation found the hard way.

FAQCommon questions

Why does the checker say my tone section contradicts itself?
Because two of your adjectives sit on opposite ends of the same axis. TypeGlish reads an adjective as a claim about a named property, so "our tone is professional" and "our tone is casual" are two values for one single-valued slot, and logic/contradiction blocks the build. It is the same defect as two conflicting MUST rules, just spelled in adjectives. Keep the value you actually mean, and write real exceptions as scoped rules instead of a second adjective.
Can an AI agent be professional and casual at the same time?
Not as two separate claims, because they are two points on one formality axis and the model can only land on one of them per reply. If the midpoint is genuinely what you want, name the midpoint instead of asserting both ends: define an adjective on that axis with a weight, for example conversational at 0.45 between formal and informal, and then say the tone is conversational. Now there is one value in the file, and a later edit that adds a second one fails the build.
What does prompt/undefined-adjective mean?
It means you used an adjective the checker has no axis for, so the claim is invisible to it: it cannot tell whether the word conflicts with anything else in the prompt. "Our voice is snappy" is the example in this post. You have three options: define the word with $CONFIG adjective (give it an axis and an opposite), replace it with a measurable rule such as "MUST reply in at most 3 sentences", or relax the dial with $CONFIG lexicon if you want undefined adjectives to warn instead of error.
How do I write a brand voice section an agent will actually follow?
Split it in two. Keep at most one adjective per axis for the genuine style claim, so nothing cancels, and convert every adjective that was really describing behavior into a rule with an observable bound: snappy becomes at most 3 sentences, not robotic becomes greet the customer by name. Adjectives that survive the split are style; rules that come out of it are the part a checker and a test can hold you to. In this post that split moved the prompt from F (36/100) to A (92/100).
∿ washed up Jul 25, 2026 ∿