> Markdown rendition of https://typeglish.dev/blog/renamed-customer-ablation ("We renamed the customer. The contradictions walked through. - Tidelines"). Canonical page: https://typeglish.dev/blog/renamed-customer-ablation · All pages: https://typeglish.dev/llms.txt

[← Tidelines](https://typeglish.dev/blog)/ Deep dives Oct 6, 2026

# We renamed the customer. The contradictions walked through.

The voice team says caller, product says user, compliance says account holder, collections says bill payer. We took a clean energy-supplier agent, gave its customer each of those names in one rule at a time, and then added an edit that contradicts that rule.

by **TypeGlish team** 7 min read #deep-dives

Five names, one customer.

**TL;DR** The contradiction check compares the words of a rule's action, not the person they mean: `MUST transfer the caller to a live agent` and `NEVER transfer the customer to a live agent` check clean, grade A (99/100), and build. Across six rules and five names, a rename inside a `WHEN` or `IF` condition cost nothing (10 of 10 contradicting edits still caught), and a rename inside the action cost everything (0 of 20 caught). Use one noun for the customer, everywhere.

Every contact centre has more names for the customer than it has teams. The IVR script was written for a *caller*. The app team's tickets talk about the *user*. Compliance writes *account holder* because that is the person the data belongs to, and collections writes *bill payer* because that is the person who owes the money. When those teams each paste a few lines into the same agent prompt, the model gets four names for the person in the chat, and reads them as one. This run measures whether the checker does too.

## §1 The baseline

A chat assistant for a household energy supplier: one tool, one length bound, six guarded rules. Every one of the six mentions the customer in its condition, and four of them mention the customer again in the action.

base.tg - an energy-supplier chat agent, one name for the customer ✓ A (99/100)

```typeglish
$TOOL find_account
  - description:: Find an energy account by its account number.
  - input::
    - account_number:: string: the account number.

# Role
@@ role: one chat assistant for household energy customers
You are Ada, the chat support assistant for Fenwick Energy.

# Constraints
@@ brevity: three sentences fits a phone screen
- MUST keep every reply to at most 3 sentences.
@@ lookup: never answer about an account from memory
- WHEN the customer gives an account number THEN MUST call @[find_account].
@@ gas: a smell of gas is an emergency, not a ticket
- WHEN the customer reports a smell of gas THEN MUST give the customer our gas emergency number.
@@ arrears: a payment plan comes before any talk of collection
- IF the customer is behind on payments THEN MUST offer the customer a payment plan.
@@ reading: a misheard digit becomes a wrong bill
- WHEN the customer submits a meter reading THEN MUST read the reading back to the customer.
@@ security: no balance before verification
- IF the customer has not passed security checks THEN NEVER share their account balance.
@@ human: a request for a person is always honoured
- WHEN the customer asks for a human THEN MUST transfer the customer to a live agent.
```

The lookup and security rules name the customer only in their condition. The gas, arrears, reading and human rules name the customer in the condition and again as the object of the action.

tg check, score and build - base.tg

```
$ npx typeglish check base.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score base.tg
base.tg — A (99/100)  proven errors: none  tiers: base+z3
  planes  runtime 99 (what the model reads) · hygiene 100 (source only)
  facets  enforceability 100 x.21 · hardness 100 x.12 · directness 89 x.08 · consistency 100 x.17
          structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08
  rules   7 of 7 rule-shaped lines read

$ npx typeglish build base.tg
✓ built .typeglish/dist/base.txt ← base.tg (0b8dedc676f0, full)
```

Seven of seven rules read, enforceability 100, consistency 100. Every rule in the file is one the prover holds, and every rule is one it will compare against the next edit.

## §2 Thirty renames

Each variant is `base.tg` with one rule rewritten in another team's vocabulary: every *customer* on that line becomes *caller*, *user*, *account holder*, *bill payer* or *member*. Six rules, five names, thirty files. Nothing else changes.

Then each variant gets the kind of line that arrives later, written by somebody who calls the customer the customer: the plain opposite of the renamed rule, added at the top of the section with its own `@@` note. *NEVER transfer the customer to a live agent* against the human rule, *NEVER offer the customer a payment plan* against the arrears rule, *MUST share their account balance* against the security rule, and so on. Against `base.tg`, each of the six edits is a blocking `logic/contradiction` and the build is refused.

the results - each rule renamed five ways, then contradicted ✗ 10 of 30 defended

```
# rule      customer named in    renamed alone          renamed + contradicting edit
lookup      condition            clean A (99)            logic/contradiction F (83)   5 of 5 caught
security    condition            clean A (98-99)         logic/contradiction F (81-82)  5 of 5 caught
gas         condition + action   clean A (98-99)         passes A (98-99)              0 of 5 caught
arrears     condition + action   clean A (98-99)         passes A (98-99)              0 of 5 caught
reading     condition + action   clean A (98-99)         passes A (98-99)              0 of 5 caught
human       condition + action   clean A (98-99)         passes A (98-99)              0 of 5 caught
```

"Clean" here means no error or warning. Some renames drew one info line, `prompt/unintroduced-definite`, because *the account holder*, *the bill payer* or *the member* is a noun the file never introduced (and on the gas rule, `prompt/declared-later` once the edit sat above it). *Caller* and *user* drew nothing at all, in any position. None of the info lines is about the conflict.

No variant changed the grade by more than a point on its own, which is the expected half of the result: a rule about the caller is a perfectly good rule. The other half is the one that matters. Where the new name sat in the condition, all ten contradicting edits were still caught. Where it sat in the action, all twenty shipped, at an A.

## §3 The overnight edit

Take the human rule, the one that matters most to a customer who is stuck. The voice team wrote it, so it says *caller*. Months later the overnight shift is cut, and somebody in ops adds one line at the top of the section: nobody is on after 10pm, so the bot should stop promising a person.

the later edit, and the rule as the voice team wrote it

```
@@ overnight: nobody is on shift after 10pm
- NEVER transfer the customer to a live agent.
  ...
@@ human: a request for a person is always honoured
- WHEN the caller asks for a human THEN MUST transfer the caller to a live agent.
```

Those two lines cannot both hold for anybody who asks for a human, and the ops line was meant to win only at night, which the file does not say. In `base.tg` that unresolved decision is refused. In the voice team's version it is not noticed.

tg check, score and build - the same edit, two names ✗ one refused, one shipped

```
$ npx typeglish check base-plus-edit.tg
base-plus-edit.tg:12:1  error  logic/contradiction  Conflicts with line 26. Logical conflict — "transfer the
  customer to a live agent." is both required and forbidden. Keep one, or scope the two rules
  so they cannot both apply (IF <condition> THEN ...).
                   ↳ line 26: - WHEN the customer asks for a human THEN MUST transfer the customer to a live agent.
  ...
✗ 1 file — 2 error, 0 warning, 0 info

$ npx typeglish build base-plus-edit.tg
typeglish build: base-plus-edit.tg refused — nothing written

$ npx typeglish check renamed-plus-edit.tg --strict
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish review renamed-plus-edit.tg
renamed-plus-edit.tg — A (99/100)  check: 0 error, 0 warning, 0 info  tiers: base+z3

  FIXES  none — every facet is essentially full

  RULES  8 of 8 rule-shaped lines read

$ npx typeglish build renamed-plus-edit.tg
✓ built .typeglish/dist/renamed-plus-edit.txt ← renamed-plus-edit.tg (a4096c19eced, full)
```

The renamed file is not merely passing. It exits 0 under `--strict`, keeps the A (99/100) it had before the edit, reads all eight rules as provable, and `review` has nothing to suggest. The grade, the rules table and the fix list are all silent at once.

And here is what ships, both lines of it, a few lines apart in the artifact the model reads:

the artifact - renamed-plus-edit.txt

```
$ grep -n "live agent" .typeglish/dist/renamed-plus-edit.txt
5:- NEVER transfer the customer to a live agent.
12:- WHEN the caller asks for a human THEN MUST transfer the caller to a live agent.
```

A model reading this will take the caller to be the customer, so it receives a requirement and a prohibition on the same act and settles them one conversation at a time. That is the failure described in [Your prompt argues with itself](https://typeglish.dev/blog/prompt-contradictions), with every check green.

Stripped down to the two rules, the pair is small enough to run yourself.

pair-same.tg - one name ✗ logic/contradiction

```typeglish
# Role
You are Ada, the chat support assistant for Fenwick Energy.

# Constraints
- WHEN the customer asks for a human THEN MUST transfer the customer to a live agent.
- NEVER transfer the customer to a live agent.
```

pair-renamed.tg - two names ✓ clean, B (87/100)

```typeglish
# Role
You are Ada, the chat support assistant for Fenwick Energy.

# Constraints
- WHEN the caller asks for a human THEN MUST transfer the caller to a live agent.
- NEVER transfer the customer to a live agent.
```

The first grades F (70/100) and refuses to build. The second checks with 0 error, 0 warning, 0 info, grades B (87/100) and builds. The B is the missing `@@` notes, not the conflict.

## §4 Where the name sits

The table in §2 renamed every *customer* on a line at once. Splitting the human rule apart shows which of the two mentions carries the weight, and how far a phrase can drift before the check loses it. Same base file, same overnight edit, one rewrite of line 26 at a time.

the results - the human rule, rewritten five ways, against the overnight edit

```
# line 26 as rewritten                                                   check
WHEN the caller asks for a human THEN MUST transfer the customer to a live agent.   logic/contradiction F (83)
WHEN the customer asks for a human THEN MUST transfer the caller to a live agent.   passes A (99)
WHEN the customer asks for a human THEN MUST transfer them to a live agent.         passes A (99)
WHEN the customer asks for a human THEN MUST transfer the chat to a live agent.     passes A (99)
WHEN the customer asks for a human THEN MUST transfer the customer to a human agent. passes A (99)
```

The condition is free text to the prover: an unconditional `NEVER` conflicts with the action whatever the condition says, so renaming inside it changed nothing. The action is compared as a phrase, and a synonym, a pronoun, a different object or a different destination each made it a different action.

Some variation does survive. In a two-rule file against `MUST transfer the customer to a live agent`, the prohibitions *NEVER transfer a customer*, *NEVER transfer customers* and *NEVER transfer the Customer* were all caught: articles, plurals and capitals fold. Changing the noun itself does not: *the client* escaped. So did *NEVER offer a payment plan* against *MUST offer the customer a payment plan*: dropping the recipient was enough. And a declaration saying the two names are one person, *The caller is the customer.*, did not join them for the check either.

That is a narrower kind of matching than verbs get. In [the escalation bake-off](https://typeglish.dev/blog/escalate-words-vs-tool), *escalate to a human agent* and *hand off to a human agent* folded into one action, because the verb catalog groups them. There is no such catalog for the people in your prompt, and there could not be a safe one: whether *the caller* and *the account holder* are the same person depends on your business, not on English.

> The model folds every name into the person in the chat. The checker keeps every name it has not been told is the same word.

## §5 One noun, and a reason to keep two

The fix is a vocabulary decision, made once. Pick the noun, say so in the Role annotation where every editor will see it, and rewrite the pasted lines into it. With one name back in the file, the overnight edit is refused again, and the refusal forces the decision nobody wrote down: a person when someone is on shift, something else when nobody is. That is a state the host knows, so it becomes an input, and each deployed prompt carries one of the two rules.

fixed.tg - one name, and the shift as a typed switch ✓ A (99/100)

```typeglish
$REQUIRE variable live_agents: one of staffed, closed

# Role
@@ role: one chat assistant; every rule calls the person in the chat the customer
You are Ada, the chat support assistant for Fenwick Energy.

# Constraints
@@ arrears: a payment plan comes before any talk of collection
- IF the customer is behind on payments THEN MUST offer the customer a payment plan.
@@ human: a person when one is on shift, a callback when nobody is
$IF @{live_agents} is equal to staffed:
  - WHEN the customer asks for a human THEN MUST transfer the customer to a live agent.
$ELSE:
  - NEVER transfer the customer to a live agent.
  - WHEN the customer asks for a human THEN MUST offer the customer a callback.
```

tg build - one artifact per shift ✓ two builds

```
$ npx typeglish build fixed.tg --vars '{"live_agents":"staffed"}'
✓ built .typeglish/dist/fixed.txt ← fixed.tg (17aed6a8261c, full)

$ tail -2 .typeglish/dist/fixed.txt
- IF the customer is behind on payments THEN MUST offer the customer a payment plan.
- WHEN the customer asks for a human THEN MUST transfer the customer to a live agent.

$ npx typeglish build fixed.tg --vars '{"live_agents":"closed"}'
✓ built .typeglish/dist/fixed.txt ← fixed.tg (97995b864270, full)

$ tail -2 .typeglish/dist/fixed.txt
- NEVER transfer the customer to a live agent.
- WHEN the customer asks for a human THEN MUST offer the customer a callback.
```

The losing arm never reaches the model. The overnight case for this kind of agent has its own checklist in [Your agent works nights. Nobody else does.](https://typeglish.dev/blog/agent-after-hours)

There is one case for keeping a second noun, and it is the case where the checker's literal reading is right. On a voice line the person calling is not always the person who owns the account: a partner, a carer, a neighbour reading out a meter. Rules that should apply to one of them and not the other need two names on purpose, guarded so they cannot both apply, which is the subject of [why your agent gives the balance to whoever calls](https://typeglish.dev/blog/agent-third-party-caller). The defect is not two nouns. It is two nouns for one person.

The gate has to come from outside the checker, because nothing inside it measures this: the grade, the rules table and `review` were all clean on the contradicted file. The cheapest gate is the dumbest one. List the names your teams actually use and fail CI when one of them appears in the prompt source, with an allow-list for the deliberate second person. And when you write a rule that overrides another, copy the object phrase from the rule you are overriding rather than retyping it; `typeglish score --json` lists every rule's line and text under `rules`, so the phrase to copy is one command away.

## §6 Common questions

**Does TypeGlish know that the caller and the customer are the same person?**

No. In this run it folded articles, plurals and capitals, so a customer, customers and the Customer all matched the customer, but the caller, the user, the account holder, the bill payer, the member and them were each a different object. A declaration such as The caller is the customer did not join them either. Two rules about one person written with two nouns are two rules about two people, as far as the contradiction check is concerned.

**Why did my prompt pass the check when one rule forbids what another rule requires?**

One likely reason is that the two rules name the object of the action differently. NEVER transfer the customer to a live agent beside WHEN the customer asks for a human THEN MUST transfer the customer to a live agent is a blocking logic/contradiction and the build is refused. Change the second rule to transfer the caller and the same file checks clean, grades A (99/100), builds, and typeglish review reports no fixes. The object phrase has to match for the conflict to be found: the caller, them, the chat and a human agent instead of a live agent all escaped in this run.

**Does it matter what I call the customer inside a WHEN or IF condition?**

Not for an unconditional edit. Renaming the customer inside the guard of two rules, under five different names, left every one of ten contradicting edits caught, because a NEVER with no condition conflicts with the action whatever the guard says. The name only stopped the check when it sat in the action itself: four rules, five names, twenty edits, none caught.

**How do I stop different teams calling the customer different things in one agent prompt?**

Pick one noun, write it in the Role annotation so every editor sees it, and copy the object phrase from the existing rule when you write a rule that overrides it. The checker will not flag a synonym for you, so a one-line grep in CI for the names your teams use (caller, user, account holder, bill payer, member) is the cheapest gate. Keep a second noun only when it really is a second person, such as a third party on the line who is not the account holder.

Field note

Vocabulary drift is what a prompt looks like after it has been merged, not after it has been written. Nobody chooses *caller* over *customer*; it arrives with the IVR script, the way *user* arrives with a product spec and *account holder* with a compliance review. The checker does exactly what it says: it proves conflicts between the rules as written, and two different nouns are two different rules as written. That makes the noun part of the contract, the same as the verb and the modal. When two prompts become one, run the merge through [the checklist for merging two agent prompts](https://typeglish.dev/blog/merge-two-agent-prompts), and add one line to it: say the customer's name out loud, once, and make every rule use it.

∿ washed up Oct 6, 2026 ∿
