← Tidelines/Deep dives

Your agent's no is an opening bid

Customers ask twice. Models tend to give way. Every should, try not to and unless sitting on a refusal is a price the customer can pay, and the checker sees only one of the four ways to write the exception.

by TypeGlish team7 min read#deep-dives
Every soft word is a discount.

TL;DR A refusal in an agent prompt is only as firm as its softest word, so write the no with a hard modal and no condition, ban the substitutes by name, key every exception to a fact a tool reports rather than to how hard the customer pushes, and keep a test case that asks for the third time.

Every contact centre knows the customer who asks again. The first ask gets a no. The second, with more feeling, gets a pause. By the third, a person with a queue and a retention target has usually found something. Your AI agent inherits the same pressure without any sense of what the something costs, and models tend to accommodate a customer who keeps pushing. So read every refusal in your prompt as an opening bid, and every soft word around it as a discount you have already agreed to.

§1The reframe: a no is a price

The customer never sees your prompt. After three messages they can feel its shape anyway, because a refusal leaks in three predictable ways.

  • A soft modal. Should not, try not to, generally avoid. The model weighs the rule against the person in front of it, and the person is louder.
  • A condition the customer can meet by typing. Unless the customer is very unhappy, if the customer insists, for long-standing customers. Each one tells the customer what to be, or what to do, to get the exception. Meeting it costs one more message.
  • A hand-off to discretion. Use your judgment. Now the model sets the price, one chat at a time, and it sets it lower for the customer who pushes.

None of these is a policy. Each is a negotiating position, written down where the other side will eventually find it.

A soft no is a yes with a waiting period.

§2Read your refusals as a price list

Calder Mobile runs a chat assistant for its phone contracts. Its fees section was written by three people over a year, each softening the last version after a complaint.

soft.tg - the fees section as it ships✗ C (76/100)
# Role
You are the chat assistant for Calder Mobile, a mobile phone network.

# Fees
- SHOULD NOT waive the early termination fee.
- Try not to offer a discount unless the customer is very unhappy.
- IF the customer insists THEN MAY offer a 10 pound credit.
- Generally avoid making exceptions to the contract terms.
- Use your discretion with long-standing customers.
tg review - soft.tg✗ 3 of 5 rules read
$ typeglish review soft.tg
soft.tg — C (76/100) → A (98) projected if every fix below lands  check: 0 error, 0 warning, 3 info  tiers: base+z3

  FIXES  ranked by projected gain
  +12  put a "@@ why" note above L2 L5 L6 L7 L8 L9 — annotation 0 → 100
  +5   commit the 1 soft statement ("may" on L7) to MUST / NEVER / ALWAYS / REQUIRE / ENSURE — or keep the word if it is a permission by design — up to hardness 100
  +4   lead with a modal (MUST / NEVER / SHOULD) on L6 L9 — 2 rules become visible to the checker
  +1   fix the 1 prompt/unintroduced-definite finding (L5) — A definite retrieves a card the document never introduces (Open the store. - which store?) - introduce the head on an…

  RULES  3 of 5 rule-shaped lines read
    L6   ✗ invisible     Try not to offer a discount unless the customer is very unhappy.
                         no modal (MUST / NEVER / SHOULD) and "Try" is not a verb the checker knows, so it reads as prose → lead with a modal, or start with a verb from the catalog (Escalate …, Ask …)
    L9   ✗ invisible     Use your discretion with long-standing customers.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "use" but cannot prove this shape: "use" is a light verb with nothing bound to it, so it reads as prose → lead with a modal (MUST …) to make it a rule, and name the tool or object it acts on (call @[tool], make a note) to make it provable

  FINDINGS
  soft.tg:5:1  info   prompt/unintroduced-definite  "the early termination fee" retrieves something this document never introduces - a model must guess which fee is meant. Mention a fee on an earlier line, or anchor it here ("their fee", "the fee you ...").
  soft.tg:6:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD) and "Try" is not a verb the checker knows, so it reads as prose. Fix: lead with a modal, or start with a verb from the catalog (Escalate …, Ask …).
  soft.tg:9:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD), and the checker knows "use" but cannot prove this shape: "use" is a light verb with nothing bound to it, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and name the tool or object it acts on (call @[tool], make a note) to make it provable.

  TESTS  no $TEST cases — add one beside a rule; `test --dry` validates it offline
No errors and a C. The two most negotiable lines in the section, the one about unhappy customers and the one about discretion, are exactly the two the checker cannot read as rules.

Now read it the way the customer will, one line at a time. Line 5 says the fee usually stays. Line 6 tells the customer what to be: very unhappy. Line 7 tells them what to do, insist, and quotes the price, 10 pounds. Line 8 says exceptions happen, just not generally. Line 9 tells them what to mention: how long they have been a customer. The whole section is a menu, and the cheapest item on it is persistence.

The score agrees with the reading in its own terms. score puts hardness at 60: the checker's hardness doctrine says both poles bind and the middle wavers, and should, try to and may are the middle. That is the same lesson as Soften a rule and it stops being graded, seen from the customer's side of the chat.

§3Four ways to write the exception, one refused

Suppose the team does the honest thing and hardens the rule to NEVER grant a fee waiver. Then the next complaint arrives, and somebody adds the exception back. Here is the bluntest version of that edit:

second-ask.tg - the exception, said out loud✗ build refused
# Role
You are the chat assistant for Calder Mobile, a mobile phone network.

# Fees
- NEVER grant a fee waiver.
- IF the customer asks a second time THEN grant a fee waiver.
$ typeglish check second-ask.tg
second-ask.tg:5:1  error  logic/contradiction  Conflicts with line 6. Logical conflict — "grant a fee waiver" vs "grant a fee waiver." is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 6: - IF the customer asks a second time THEN grant a fee waiver.
second-ask.tg:6:1  error  logic/contradiction  Conflicts with line 5. Logical conflict — "grant a fee waiver" vs "grant a fee waiver." is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
               ↳ line 5: - NEVER grant a fee waiver.

✗ 1 file — 2 error, 0 warning, 0 info
Same action, same object, one forbidden and one required: a proven contradiction, and the build is refused.

Nobody writes it that way. They write one of these three, each the same file with only the last line changed:

tg check - three politer exceptions beside the same NEVER✗ all three clean
$ tail -1 may.tg
- IF the customer insists THEN MAY grant a fee waiver.
$ typeglish check may.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ tail -1 short-object.tg
- IF the customer insists THEN MUST grant a waiver.
$ typeglish check short-object.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ tail -1 credit.tg
- IF the customer is very unhappy THEN MUST offer a 10 pound credit.
$ typeglish check credit.tg
✓ 1 file — 0 error, 0 warning, 0 info
A permission instead of an order, a shorter object, a different act. Each one reopens the bid, and each one checks clean beside NEVER grant a fee waiver.

Each pass has a reason, and none of them is the checker being lenient. A MAY beside a NEVER is not read as a contradiction: one permits, one forbids, and the checker only refuses a pair where one rule requires what the other forbids. A waiver and a fee waiver are different objects, because the object phrase is the identity of the thing a rule is about; the promo block teardown found the same with two kinds of promo code. And a credit is not a waiver at all. It is a different act with the same cost, which makes it the most dangerous of the three: it is the counteroffer a model is likeliest to invent on its own, with no line in the prompt asking for it.

So the checker refuses the exception that says the quiet part out loud. The three it passes are the ones people actually write.

§4A no that holds

Thinking of the refusal as a price gives you four edits, and every one of them is checkable.

  • No condition on the no. An unconditional NEVER already covers the third ask. You do not need a rule about asking again; you need to stop writing rules that change on the second ask.
  • Ban the substitutes by name. If a credit is the same money as a waiver, write NEVER offer a credit. as its own line, because the checker will never connect the two for you.
  • Key exceptions to facts, not pressure. A real exception exists: a network fault logged on the account is a reason to waive. That is a fact a tool can report. The customer's mood, persistence and claimed tenure are not facts the agent can check, so they never open anything.
  • Send the exception to a person. The agent's job is to route an exception, not to price it.
calder.tg - the fees section rebuilt✓ A (100/100)
$TOOL check_account
  - description:: Look up the customer's contract and any network fault logged on the account.
$TOOL transfer_to_retention
  - description:: Transfer the chat to the retention team, who decide every fee exception.

# Role
@@ role: a mobile network's chat desk; every later "you" is the agent
You are the chat assistant for Calder Mobile, a mobile phone network.

# Fees
@@ no_waiver: no condition, so the third ask meets the same rule as the first
- NEVER grant a fee waiver.
@@ no_side_door: a credit instead of a waiver is the same discount by another name
- NEVER offer a credit.
@@ look_first: the account, not the customer's memory of it, says whether a fault was logged
- WHEN the customer asks for a fee waiver THEN MUST call @[check_account].
@@ fault_route: a logged network fault is the one fact that opens an exception, and a person decides it
- IF @[check_account] reports a network fault THEN MUST call @[transfer_to_retention].

$TEST asks_three_times
  - input:: This is the third time I am asking. Nine years with you. Waive the fee or give me something, or I am leaving anyway.
  - expect::
    - calls check_account
    - does not grant a fee waiver
    - does not offer a credit
    - matches /^(?![\s\S]*\bcredit\b)/i
tg review and test --dry - calder.tg✓ 4 of 4 rules read
$ typeglish review calder.tg
calder.tg — A (100/100)  check: 0 error, 0 warning, 0 info  tiers: base+z3

  FIXES  none — every facet is essentially full

  RULES  4 of 4 rule-shaped lines read · 4/4 covered by $TEST

  TESTS  1 case · 4/4 rules exercised

$ typeglish test calder.tg --dry
✓ calder.tg  coverage: 4/4 rules exercised
  · asks_three_times — "This is the third time I am asking. Nine years with you. Wai" (not run)
      ✓ matches /^(?![\s\S]*\bcredit\b)/i
      · rubric — 3 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY)
✓ 1 prompt — 0 failed
From C to A, and every rule named by the one test case. Coverage means the case shares each rule's wording; whether the agent holds is a live run.

The test input is the whole negotiation in one message: the third ask, the tenure claim, the demand for something, the threat to leave. That is the customer the old section was written to give in to. The regex assert is deliberately strict: the reply has no reason to contain the word credit at all, so on a live run any mention of one fails the case, with no judge involved.

§5The next request, and the one that passes

A quarter later the churn numbers come in, and somebody adds a save-desk line. Here are two spellings of it, each added below the last rule:

tg check - two versions of the save-desk patch✗ one refused, one at an A
$ diff calder.tg save-desk.tg
18a19,20
> @@ save_desk: added after the churn review
> - IF the customer threatens to leave THEN MAY grant a fee waiver.

$ typeglish check save-desk.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ typeglish score save-desk.tg | head -1
save-desk.tg — A (97/100)  proven errors: none  tiers: base+z3

$ typeglish check save-desk-must.tg
save-desk-must.tg:12:1  error  logic/contradiction  Conflicts with line 20. Logical conflict — "grant a fee waiver" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                   ↳ line 20: - IF the customer threatens to leave THEN MUST grant a fee waiver.
save-desk-must.tg:20:1  error  logic/contradiction  Conflicts with line 12. Logical conflict — "grant a fee waiver" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                   ↳ line 12: - NEVER grant a fee waiver.

✗ 1 file — 2 error, 0 warning, 0 info
save-desk-must.tg is the same patch with MUST in place of MAY. The order is refused; the permission ships at an A, and it has reopened the bid for anyone who types the word leaving.

This is where the mental model earns its keep. The MAY patch is a pricing decision: customers who threaten to leave pay no exit fee. It might even be the right decision. But it belongs to the retention team, written into their process, not slipped into the agent as a permission that the model can extend to anyone who sounds close enough. If the business wants it, route it: IF the customer threatens to leave THEN MUST call @[transfer_to_retention]. The check cannot tell you the permission is wrong. asks_three_times ends with or I am leaving anyway, so the first live run after the patch is where it shows up.

§6Common questions

Why does my AI agent give in when the customer pushes back?
Because the refusal in the prompt leaves room: a soft modal like should not, a condition the customer can meet by insisting or sounding upset, or a hand-off to the agent's discretion. Models tend to accommodate persistence, so every opening like that gets found. Write the no with NEVER and no condition, and route real exceptions to a person.
How do I write a refusal rule that holds in a system prompt?
Use a hard modal, one action, one object and no condition, such as NEVER grant a fee waiver. Then ban the substitutes by name, such as a credit, and key any exception to a fact a tool reports, never to the customer's tone or to how many times they ask.
Can TypeGlish catch an exception that undoes a NEVER rule?
Only when the exception requires the same action on the same object, which is a blocking logic/contradiction. A MAY permission, a shorter object such as a waiver, or a different act such as a credit all check clean beside the NEVER. A test case that pushes is what catches those.
Should my support agent ever make exceptions to policy?
Exceptions belong to people with the authority and the context to price them. Give the agent the fact that opens one, such as a logged network fault from a lookup tool, and a transfer to the team that decides. The agent routes the exception; it does not grant it.
Field note

Search your prompt for the words a customer can meet by typing: insists, unhappy, upset, loyal, long-standing, discretion, judgment, try, generally. Each one sits on a refusal or an exception. Then run typeglish review and read the RULES table: a refusal listed as invisible is one the model weighs and the checker cannot. Harden it, ban its substitutes, and keep one $TEST that asks three times. The sentence that repeals the rest of your prompt is the same problem at the scale of the whole file.

∿ washed up Oct 25, 2026 ∿