← Tidelines/Best practices

Why your agent never offers the credit you allowed

Somebody wrote the agent a spend authority, in the language a policy document uses for spend authority, and then watched a quarter go by with the credit almost never applied. The word doing the damage is the one that sounded the most official.

by TypeGlish team7 min read#best-practices
Permission is not a trigger.

TL;DR MAY is graded as the soft middle (hardness 0) and sits on the obligation axis only: MUST, ALWAYS and SHOULD beside a MAY are each a blocking logic/force-subsumption, while NEVER, MUST NOT, DO NOT, AVOID and SHOULD NOT beside the same MAY are 0 error, 0 warning, 0 info, and a money bound written on a MAY is not read at all. Write the case as a guarded MUST and the ceiling as an unguarded bound.

A meal-kit company gave its support agent three pieces of discretion: a goodwill credit, a waived delivery fee, a free skip. All three were written the way a floor manager would write them for a person. Three months later the review found the credit had been applied on a handful of contacts out of thousands, and on those contacts it had been applied to complaints that did not merit it. Nobody had changed the prompt. The prompt had never said when.

§1The permission that reads like authority

Here is the file, cut to the five rules that matter. It is a good prompt by most measures: it has a role, it has a tool, it has a real bound on reply length, and it routes cancellations. Nothing in it is wrong.

credits.tg - three permissions and two rules✓ compiles
<$CONFIG>
  $IMPORT tool apply_credit
</$CONFIG>

# Role
You are a customer support agent for Kettleman's, a meal-kit subscription service.

# Constraints
- MAY apply a goodwill credit of at most 15 dollars with @[apply_credit].
- MAY waive one delivery fee for a late box.
- MAY skip the next box at no charge.
- MUST keep every reply to at most 3 sentences.
- WHEN a customer asks to cancel THEN transfer the customer to a retention specialist.
Read the three permissions as a specification and ask what a conforming reply looks like. Every reply conforms. There is no transcript you could hand this file that would fail it.
tg check and score credits.tg - output
$ typeglish check credits.tg
 1 file - 0 error, 0 warning, 0 info

$ typeglish score credits.tg
credits.tg - C (78/100)  proven errors: none  tiers: base+z3
  planes  runtime 87 (what the model reads) · hygiene 50 (source only)
  facets  enforceability 90 x.21 · hardness 40 x.12 · directness 98 x.08
          consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene)
          style 100 x.08 · security 100 x.08
hardness 40 is the number to look at, and it is arithmetic rather than opinion: two of the five rules commit to a pole and three do not. Isolate a single MAY rule in a file of its own and it scores C (75/100) with hardness 0, against B (87/100) with hardness 100 for the same action written MUST. The permission is not marked down for being weak. It is priced at nothing.

That is the honest reading of what you wrote. You may apply a credit says applying one is not forbidden. It does not say a spoiled box is the case for it, it does not say a late delivery is not, and it does not say what happens when neither applies. So the model does the only thing left, which is to decide per conversation, weighting whatever the current complaint most resembles. On a support queue that resolves to almost never, punctuated by a few contacts where the customer pushed hard enough to look like the shape of a credit.

A permission has no truth condition, so there is no reply that violates it and no reply that satisfies it.

§2Half an axis

Now the part that is genuinely surprising, and the reason a permission is worse than a soft rule rather than merely weaker. TypeGlish does relate MAY to the other modals, but only in one direction. Put the strongest possible prohibition next to it and nothing happens.

credits-forbid.tg - a permission and a prohibition, same action✓ compiles
# Role
You are a customer support agent for Kettleman's.

# Constraints
- MAY apply a goodwill credit.
- NEVER apply a goodwill credit.
tg check - the prohibition, then the obligation
$ typeglish check credits-forbid.tg
 1 file - 0 error, 0 warning, 0 info

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

$ typeglish check credits-oblige.tg
credits-oblige.tg:5:3  error  logic/force-subsumption  Conflicts with line 5. Subsumed on the
  obligation axis: the stronger rule already guarantees this one ("must" entails "may" for
  the same action), so it adds nothing but waver. Delete it, or give it a genuinely distinct
  scope.

 1 file - 1 error, 0 warning, 0 info
Same two lines, same action, one word different on the second rule. NEVER is silent and MUST refuses to compile. The prover is not being inconsistent: a permission asserts nothing about the world, so a prohibition has nothing to contradict, while an obligation genuinely does make the permission redundant.

Ten controlled pairs over one action settle where the boundary is, and it falls exactly on the deontic sign. Every rule that obligates is a blocking error beside a MAY. Every rule that forbids is clean beside it.

  • Blocking, all logic/force-subsumption: MAY with MUST, MAY with SHOULD, ALWAYS with MAY. The message names the entailment each time, including "should" entails "may" for the same action.
  • Clean, 0 error, 0 warning, 0 info: MAY with NEVER, with MUST NOT, with DO NOT, with AVOID, with SHOULD NOT.
  • For contrast, the pair with no MAY in it: MUST beside NEVER is 2 error, reported once on each participating line.

So the file that reads most obviously broken to a human is the one nothing reports. If your prompt has drifted into the agent may issue a credit in one section and never issue a credit without approval in another, that is a real policy disagreement between two authors, and it will pass every gate you own. You said only. Your agent escalated anyway. is the same story told about a quantifier: the words that feel like they close a door are usually the ones with nothing behind them.

§3The bound goes with the modal

There is one more thing a permission takes with it when it leaves the graded plane, and it is the part most likely to be load-bearing in your file: the number. A bounded permission is the standard advice for writing latitude down, and it is what the rewrite that replaces a discretion clause reaches for. It is a real improvement on use your best judgment, because at least a human reading it knows the ceiling. It is worth knowing exactly how far it gets you mechanically, which is not far.

credits-bound.tg - a ceiling on a MAY, and a floor on a MUST✓ compiles
# Role
You are a customer support agent for Kettleman's.

# Constraints
- MAY apply a goodwill credit of at most 15 dollars.
- MUST apply a goodwill credit of at least 50 dollars.
Fifteen dollars is the ceiling finance agreed. Fifty is what somebody wrote after a bad week. Both lines are in the file and the file is fine.
tg check - change one word, get the proof back
$ typeglish check credits-bound.tg
 1 file - 0 error, 0 warning, 0 info

$ typeglish check credits-bound-must.tg
credits-bound-must.tg:5:1  error  logic/numeric  Conflicts with line 5. Numeric conflict -
  "at most 15 dollars" and "at least 50 dollars" can't both hold.
credits-bound-must.tg:6:1  error  logic/numeric  Conflicts with line 4. Numeric conflict -
  "at most 15 dollars" and "at least 50 dollars" can't both hold.

 1 file - 2 error, 0 warning, 0 info
The only edit between the two files is MAY to MUST on line 5. The amounts are identical, the actions are identical, and one file is clean while the other is two blocking errors. Z3 reads a bound when the rule carrying it is a rule; a bound attached to a permission never enters the arithmetic. Soften a rule and it stops being graded measures the same effect across a whole prompt, and finds the loss is the proof surface rather than the score.

This is the practical version of the point. Writing at most 15 dollars feels like the responsible thing to do, and it is, for the person reading it. What it does not do is make the fifteen a number anything can defend. The next person to add an amount to that file adds it into an empty room.

§4Write the trigger, not the licence

The rewrite is mechanical once you see what a permission was standing in for. Every MAY in a real agent prompt is two decisions the author declined to write down: the case in which the action is right, and the ceiling on how far it goes. Write them as two statements. The case is a guarded obligation, the ceiling is an unguarded bound on an obligation.

credits-v2.tg - the same three powers, as rules✓ A (98/100)
<$CONFIG>
  $IMPORT tool apply_credit
</$CONFIG>

# Role
@@ role: subscription support, with the spend authority written down
You are a customer support agent for Kettleman's, a meal-kit subscription service.

# Constraints
@@ credit_trigger: a spoiled box is the case the credit exists for
- WHEN a customer reports a spoiled box THEN you MUST apply a goodwill credit with @[apply_credit].
@@ credit_ceiling: 15 dollars is the limit finance signed off on
- MUST apply a goodwill credit of at most 15 dollars.
@@ fee_waiver: a late box is our fault, so the fee is not the customer's problem
- WHEN a box arrives late THEN you MUST waive a delivery fee for that box.
@@ skip_authority: a free skip is cheaper than a cancellation
- WHEN a customer asks to skip a box THEN you MUST skip it at no charge.
@@ brevity: three sentences keeps a chat reply scannable
- MUST keep every reply to at most 3 sentences.
@@ cancel_route: retention owns the save conversation
- WHEN a customer asks to cancel THEN you MUST transfer the customer to a retention specialist.
0 error, 0 warning, 0 info at A (98/100), from C (78/100), with hardness 40 to 100 and enforceability 90 to 95. No policy changed. The guard is what stops the obligation being an instruction to hand every caller a credit, and the ceiling rule stays unguarded on purpose, because a bound that only applies in one case is a bound the next amount can walk around.

The payoff is not the grade. It is the edit that has not happened yet. Eight months from now somebody appends one line to the Constraints section, in the same house style as the lines above it, after a week of spoiled deliveries:

tg check - one appended line, two files
# appended to both files:
#   - MUST apply a goodwill credit of at least 25 dollars.

$ typeglish check credits-v3.tg      # the rewrite
credits-v3.tg:13:1  error  logic/numeric  Conflicts with line 13. Numeric conflict -
  "at most 15 dollars" and "at least 25 dollars" can't both hold.
credits-v3.tg:23:1  error  logic/numeric  Conflicts with line 8. Numeric conflict -
  "at most 15 dollars" and "at least 25 dollars" can't both hold.

 1 file - 2 error, 0 warning, 0 info

$ typeglish check credits-edit.tg    # the original, with its bounded MAY
 1 file - 0 error, 0 warning, 0 info
Same edit, same amounts, same intent, two outcomes. The rewrite refuses to build and names both lines. The permission version takes the new floor on board without a word and ships a prompt carrying a fifteen-dollar ceiling and a twenty-five-dollar floor for the model to reconcile in the moment.

Two habits fall out of this, and they are cheap. Grep your agent prompts for may and read each hit as a question: in which case? If you can answer it, that answer is the rule and the may was a placeholder for it. If you cannot answer it, you have found a policy nobody has decided yet, which is worth more than the line was. And when the answer is genuinely at the agent's discretion, within a limit, keep the limit on an obligation and write the discretion as the guard, because the guard is the part a future edit has to argue with.

§5Common questions

Why does my AI agent never use the discount I allowed it to offer?
Because a permission is not a trigger. MAY apply a goodwill credit tells the model that applying one is not forbidden; it never tells the model when applying one is the right move, so the decision falls back to whatever the conversation happens to resemble. That reads as never on most contacts and always on a few. The checker prices it the same way: a one-rule file whose only rule is a MAY scores C (75/100) with hardness 0, against B (87/100) with hardness 100 for the identical action written MUST. Write the case instead of the licence: WHEN a customer reports a spoiled box THEN you MUST apply a goodwill credit.
What is the difference between MAY and SHOULD in a system prompt?
SHOULD recommends and MAY only permits, and TypeGlish puts them on the same obligation axis with SHOULD above MAY. That is provable in one file: MAY apply a goodwill credit beside SHOULD apply a goodwill credit is a blocking logic/force-subsumption error reading "should" entails "may" for the same action, so it adds nothing but waver. The practical difference for an agent prompt is that SHOULD at least states a default the model can follow, while MAY states none. Neither is graded as a hard rule, so if the behaviour matters, neither is the word you want.
Does MAY conflict with NEVER in a TypeGlish prompt?
No, and that is the finding worth knowing. MAY apply a goodwill credit beside NEVER apply a goodwill credit is 0 error, 0 warning, 0 info, unchanged under check --strict. The same holds for MUST NOT, DO NOT, AVOID and SHOULD NOT. A permission makes no claim that the action happens, so nothing that forbids the action can contradict it. Obligations are the other half: MUST, ALWAYS and SHOULD beside the same MAY are each a blocking logic/force-subsumption error. The permission axis is one-sided, so the flattest policy contradiction in your file can sit in it undetected.
How do I write discretionary behaviour in an agent prompt so it still checks?
Split the permission into the two things it was standing in for: the case where the action is right, and the ceiling on how far it can go. The case becomes a guarded obligation (WHEN a customer reports a spoiled box THEN you MUST apply a goodwill credit) and the ceiling becomes an unguarded bound on an obligation (MUST apply a goodwill credit of at most 15 dollars). A bound written on a MAY is not read: MAY apply a goodwill credit of at most 15 dollars beside MUST apply a goodwill credit of at least 50 dollars is 0 error, 0 warning, 0 info, while making both sides MUST is 2 blocking logic/numeric errors. Five rules rewritten that way move a meal-kit support prompt from C (78/100) to A (98/100), and the amount edit that lands eight months later is 2 blocking errors instead of silence.
Field note

logic/force-subsumption is a blocking error rather than a warning, which reads as harsh until you notice what it is protecting. A rule fully guaranteed by a stronger one on the same axis is not merely redundant text: it is a second, weaker statement of a policy, sitting in the file for a future editor to keep instead of the strong one. typeglish --explain logic/force-subsumption puts it in one line: it adds nothing but waver.

∿ washed up Aug 24, 2026 ∿