← Tidelines/Best practices

The tone is casual. Your tone is formal. Both ship.

Two sentences in one file, exact opposites, and the whole toolchain has nothing to say. Not because the checker missed it, but because the two sentences are about two different objects, and one of them is not your agent.

by TypeGlish team9 min read#best-practices
Whose tone is it?

TL;DR An attribute claim is stored against its owner, so Our tone is casual lands on we.tone and Your tone is formal lands on you.tone, and a prompt holding both is 0 error, 0 warning, 0 info at B (84/100) with consistency 100; put both on one owner and the identical policy is 2 blocking logic/contradiction errors at F (67/100). The one spelling that warns you is the ownerless The tone is casual, which prints prompt/unowned-attribute, and taking that finding's second suggested repair makes the file score higher and go silent while the contradiction is still in it.

Your prompt has a tone section. Somebody in brand wrote it, or it was pasted out of a tone-of-voice deck, and it is the one part of the file nobody reviews in a pull request because it does not look like logic. It is logic. Every line in it assigns a value to a slot, the same way at most 3 sentences does, and the compiler will prove two of those lines cannot both hold. It will just never do it across the boundary you actually have.

§1A file that contradicts itself in silence

Cobble Lane sells homeware online. Its chat support prompt is eleven lines. The Role block was written by the prompt engineer; the Voice block was pasted in from the brand guide six weeks later, by somebody else, in the brand guide's own voice.

voice.tg - formal in one section, casual in the next✓ B (84/100)
# Role
You are a chat support agent for Cobble Lane, an online homeware retailer.
Your tone is formal.

# Voice
Our tone is casual.
The response's length is brief.

# Constraints
- MUST keep every response to at most 3 sentences.
- ALWAYS ask for an order number.
- IF a customer is angry THEN escalate to a team leader.

Formal and casual are the two poles of one axis. The compiler knows that: it has a formality axis, and it will refuse a file that puts both values in one slot. Run it.

tg check, --strict and score - voice.tg✗ nothing to report
$ typeglish check voice.tg
 1 file - 0 error, 0 warning, 0 info

$ typeglish check voice.tg --strict
 1 file - 0 error, 0 warning, 0 info

$ typeglish score voice.tg
voice.tg - B (84/100)  proven errors: none  tiers: base+z3
  planes  runtime 95 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 83 x.21 · hardness 100 x.12 · directness 100 x.08
          consistency 100 x.17 · structure 100 x.12 · annotation 0 x.12
          style 100 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall)
Not one finding. --strict escalates nothing, because there is nothing to escalate. And consistency 100, which is the facet whose entire job is measuring the rules against each other, on a file whose second sentence and fifth sentence are opposites.

The artifact ships both, one after the other, in the order they were written.

tg build voice.tg - what the model reads
$ typeglish build voice.tg
 built .typeglish/dist/voice.txt ← voice.tg (c302f88c4fc4, full)

  # Role
  You are a chat support agent for Cobble Lane, an online homeware retailer. Your
  tone is formal.

  # Voice
  Our tone is casual. The response's length is brief.

  # Constraints
  - MUST keep every response to at most 3 sentences.
  - ALWAYS ask for an order number.
  - IF a customer is angry THEN escalate to a team leader.
Two instructions, forty words apart, that a human reviewer resolves in half a second and a model resolves per reply. This is what a wandering agent voice looks like at the source: not a model being inconsistent, a document being inconsistent and nobody having said so.

§2The owner is part of the address

The reason is one line of diagnostic text you only see when the file does break. Write both claims about the same owner and the checker names the slot outright.

tg check voice-you.tg - the same policy, one owner✗ F (67/100)
$ typeglish check voice-you.tg      # "Our tone is casual." -> "Your tone is casual."
voice-you.tg:3:1  error  logic/contradiction  Conflicts with line 5. Contradiction -
  "you.tone" is assigned "formal" and "casual", opposite sides of formality (one
  truth per dimension).
voice-you.tg:6:1  error  logic/contradiction  Conflicts with line 3. Contradiction -
  "you.tone" is assigned "formal" and "casual", opposite sides of formality (one
  truth per dimension).

 1 file - 2 error, 0 warning, 0 info    # exit 1, build refused

$ typeglish score voice-you.tg
voice-you.tg - F (67/100)  proven errors - grade capped at F  tiers: base+z3
  planes  runtime 73 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 83 x.21 · hardness 100 x.12 · directness 100 x.08
          consistency 0 x.17 · structure 100 x.12 · annotation 0 x.12
you.tone. That is the slot, and the dot is doing all the work: the owner is not context for the claim, it is half the key the claim is filed under. consistency goes 100 to 0 and the grade is capped at F on a one-word edit that changed no policy whatsoever.

So the question is never do these two sentences disagree. It is are these two sentences about the same object, and English is very relaxed about that in exactly the places prompts get written. Here is the whole space: one policy pair, casual against formal, in every spelling a real prompt uses.

tg check and score - eight ownerships of one contradiction✗ four of eight are silent
the pair                                      slot        tg check              tg score
Your tone is casual.   / Your tone is formal.   you.tone    2 error               F (50/100)
The tone is casual.    / The tone is formal.    the tone    2 error + 2 info      F (39/100)
Our tone is casual.    / Our tone is formal.    we.tone     2 error               F (50/100)
The response's tone .. / The response's tone .. response.   2 error               F (54/100)

The tone is casual.    / Your tone is formal.   two slots   clean + 1 info        C (73/100)
Our tone is casual.    / The tone is formal.    two slots   clean + 1 info        C (73/100)
Our tone is casual.    / Your tone is formal.   two slots   clean                 C (79/100)
The response's tone .. / Your tone is formal.   two slots   clean                 C (79/100)

$ typeglish check r1.tg
r1.tg:2:1  error  logic/contradiction  Conflicts with line 3. Contradiction - "you.tone"
  is assigned "casual" and "formal", opposite sides of formality (one truth per dimension).
$ typeglish check r3.tg
r3.tg:2:1  error  logic/contradiction  Conflicts with line 3. Contradiction - "we.tone"
  is assigned "casual" and "formal", opposite sides of formality (one truth per dimension).
$ typeglish check r4.tg
r4.tg:2:1  error  logic/contradiction  Conflicts with line 3. Contradiction - "response.tone"
  is assigned "casual" and "formal", opposite sides of formality (one truth per dimension).
Four slot names for one English word: you.tone, we.tone, response.tone, and the unowned the tone. Matched pairs are all blocking and all capped at F. Mixed pairs are all clean, and the two worst rows, the ones with no finding at all, score six points higher than the mixed row that does warn.

Read the bottom half again with a prompt review in mind. Our tone is casual beside Your tone is formal is not a contrived pairing. It is the single most common shape in commercial prompt files, because a brand guide is written in the first person plural and a system prompt is written in the second person singular, and the two documents get stapled together by a person who is thinking about the words rather than the referents.

Two sentences only argue if they are about the same thing. Your prompt has three names for your agent and only one of them is you.

§3The finding that fires, and the fix that hides it

One of the eight rows does report something. Write the tone claim with no owner at all and the checker notices the hole.

tg check voice-bare.tg - the ownerless spelling✗ B (83/100)
$ typeglish check voice-bare.tg      # "Our tone is casual." -> "The tone is casual."
voice-bare.tg:6:1  info   prompt/unowned-attribute  Unowned attribute - "the tone"
  names a slot but not its owner, so a model must guess whose tone this is. Own it
  ("Your tone is ...") or name the owner ("The response's tone is ...").

 1 file - 0 error, 0 warning, 1 info

$ typeglish score voice-bare.tg
voice-bare.tg - B (83/100)  proven errors: none  tiers: base+z3
  L6  prompt/unowned-attribute  -0.25
An info, worth a quarter of a point. It is the only signal in this entire family, and it fires on the least misleading spelling of the defect, because a slot with no owner is at least visibly missing one.

Now do the thing the diagnostic tells you to do, and take its second suggestion rather than its first.

applying the repair the checker offered✗ the score went up
  before  The tone is casual.            0 error, 0 warning, 1 info   B (83/100)  style 89
  after   The response's tone is casual. 0 error, 0 warning, 0 info   B (84/100)  style 100

  # and the file still says, four lines up:  Your tone is formal.
One point up, the info gone, style from 89 to 100, and the contradiction untouched. The repair was honest advice about a real hole; it just does not know that the owner you name has to be the owner the rest of the file already uses. Naming a different one is a clean way to make the finding disappear without fixing anything.

This is the practical lesson and it generalises past tone. When a diagnostic offers you two repairs, one of them is usually the one that makes the file quieter and one is the one that makes the file right, and they are not always the same repair. The score cannot referee that, because a slot with a named owner genuinely is better written than a slot with none. It is better written and still wrong.

§4One slot, one owner, one value

The fix is not clever. Decide who owns each attribute, use that owner everywhere, and delete the duplicates instead of rewording them. For an agent prompt the owner is you, because that is the object every rule in the file is already addressed to, and because the Role declaration introduces it on the line above.

voice-shipped.tg - one tone claim, on the owner the file already has✓ A (96/100)
# Role
@@ role: the owner every attribute below attaches to
You are a chat support agent for Cobble Lane, an online homeware retailer.
@@ tone: one slot, one owner, one value - the brand guide won, the legacy formal went
Your tone is casual.

# Constraints
@@ brevity: the measurable version of "keep it brief", so a $TEST can assert it
- MUST keep every response to at most 3 sentences.
@@ order_number: the order number is what unlocks the account context
- ALWAYS ask for an order number.
@@ anger_escalation: a heated customer gets a human
- IF a customer is angry THEN escalate to a team leader.
tg score and build - voice-shipped.tg✓ A (96/100)
$ typeglish score voice-shipped.tg
voice-shipped.tg - A (96/100)  proven errors: none  tiers: base+z3
  planes  runtime 95 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 83 x.21 · hardness 100 x.12 · directness 100 x.08
          consistency 100 x.17 · structure 100 x.12 · annotation 100 x.12
          style 100 x.08 · security 100 x.08

$ typeglish build voice-shipped.tg
 built .typeglish/dist/voice-shipped.txt ← voice-shipped.tg (e08734e99a96, full)

  # Role
  You are a chat support agent for Cobble Lane, an online homeware retailer. Your
  tone is casual.

  # Constraints
  - MUST keep every response to at most 3 sentences.
  - ALWAYS ask for an order number.
  - IF a customer is angry THEN escalate to a team leader.
B (84/100) to A (96/100), and the whole # Voice section is gone. The response's length is brief did not need a better owner, it needed deleting: at most 3 sentences two lines down is the same claim with a number on it, and a soft duplicate of a measurable rule is dead weight in front of the model.

Note what the grade is actually rewarding. consistency was 100 before and 100 after, because nothing was ever proven either way; the twelve points came from annotation and from removing a line the model did not need. That is the honest reading of this defect: the score cannot see it, before or after, and the reason to fix it is that your agent's replies stop wandering, not that a number moves.

§5The audit, in one pass

This takes about ten minutes on a real file and it is worth doing before your next tone pass, not after.

the four-column pass over every attribute claim
  line   the claim                        owner        slot
  3      Your tone is formal.             you          you.tone       <- conflict
  6      Our tone is casual.              we           we.tone        <- conflict
  7      The response's length is brief.  the response response.length <- soft dup
  10     MUST ... at most 3 sentences.    (a rule)     -

  the words worth grepping for:
  tone  voice  register  style  length  manner  approach  personality
  the owners worth grepping for:
  your  our  the  its  the response's  the agent's  the assistant's  the reply's
Two greps and a column of owners. Any slot appearing under more than one owner is a finding no command will give you, and in a prompt that has been edited by more than one team it is close to guaranteed, because each team writes in the person its own documents are written in.

Two adjacent habits make it stick. Put every attribute claim directly under the Role declaration that introduces its owner, so a second author has to walk past the existing tone line to add another one. And prefer a measurable rule to an attribute wherever one exists: at most 3 sentences cannot be duplicated by a differently-owned adjective, because a bound lives in the numeric layer where the prover reaches it regardless of who is speaking. Which adjectives sit on which axis, and what to do with one the compiler has never heard of, is the subject of a brand voice that cancels itself out; this post is the layer underneath it, where the axis is fine and the owner is not.

The general shape is worth keeping past tone. Every proof in this compiler is a comparison between two claims in one slot, and a slot is an address with several parts: the owner, the attribute, sometimes the scope. Change any part and the two claims stop meeting. That is the same mechanism by which a qualifier hides a contradiction, which you did not narrow that rule, you moved it takes apart from the other end, and it is why a clean check is a statement about what was compared rather than about what is true.

§6Common questions

Why does my agent's tone change between replies?
Because two lines of your prompt are telling it two different things and nothing has ever compared them. A tone claim is stored against an owner, so Our tone is casual lands on we.tone and Your tone is formal lands on you.tone, and two values in two different slots are not a conflict at all: that file is 0 error, 0 warning, 0 info, identical under --strict, and scores B (84/100) with consistency 100. Grep your prompt for tone, voice, register, length and style, list the owner of each claim, and if you find more than one owner for a slot you have found the reason the replies wander.
What does prompt/unowned-attribute mean?
It means you wrote The tone is casual and never said whose tone. The message reads the tone names a slot but not its owner, so a model must guess whose tone this is, and it offers two repairs: own it (Your tone is ...) or name the owner (The response's tone is ...). It is an info, worth -0.25 on the score ledger, and it is the only spelling of an ownerless attribute that says anything at all. Our tone is casual has an owner, we, so the finding does not fire, and that file is completely silent while carrying exactly the same defect.
Does TypeGlish catch a contradiction between my Role section and my tone section?
Only when both claims land on the same slot. Your tone is formal against Your tone is casual is 2 blocking logic/contradiction errors naming you.tone assigned formal and casual, opposite sides of formality, at F (67/100) with consistency 0 and the build refused. The identical policy written as Our tone is casual against Your tone is formal is 0 error, 0 warning, 0 info at B (84/100). Same two words, same two lines, and the only difference is whether the two sentences are about the same object. The prover is not being lenient, it is being asked about two different things.
How do I write a tone section an agent will actually follow?
One slot, one owner, one value, and then measurable rules for everything else. Pick the owner the rest of the file already uses, which for an agent prompt is you, put the tone claim on the line under the Role declaration so the owner is introduced before it is used, and delete every other copy rather than rewording it. On the prompt in this post, collapsing a Role line and a Voice section into a single Your tone is casual and turning the vague length claim into an existing at most 3 sentences rule took B (84/100) to A (96/100), with the artifact 61 bytes shorter and containing exactly one sentence about tone.
Field note

The tell that you have this defect is a team reaching for the inference settings. Wandering voice is a symptom people attribute to temperature, because that is the dial with the word randomness next to it, and turning it down does make the replies more similar to each other without making them more like the voice you asked for. Before you touch a sampling parameter, count the sentences in your prompt that assign a personality to something, then count how many distinct things they assign it to. A Role line, a pasted brand block, a QA rubric and a channel override is four owners for one slot and something like a hundred tokens of tone instruction on every turn, most of it arguing. If that second count is not one, the fix is in the file and it is free.

∿ washed up Sep 8, 2026 ∿