Why your agent handles a 150-dollar refund two ways
The authority table is the part of a support prompt everybody trusts, because it came out of a spreadsheet finance signed. Copy it into rules and the edges stop being edges: two tiers claim the same amount, and the model breaks the tie in whichever direction the conversation is leaning.
TL;DR Two tiers that share an amount give that amount two handlers, and prompt/tier-overlap proves it: warn for an overlapping range, info for a single shared boundary point. Make the bounds meet exactly once (up to $150 against over $150), and once you have three or more tiers put them in an ordered $IF chain, where first-true-wins makes the arms exclusive by construction.
A furniture retailer’s returns desk had a clean authority table and a QA problem nobody could reproduce. Refunds around the hundred-and-something mark were sometimes settled on the spot and sometimes handed to a team leader, on identical complaints, in the same week. The prompt had not changed. Neither had the model. The table in the prompt had a band in the middle that belonged to both rows, and every reply in that band was a coin toss the agent was never told it was making.
§1The table that reads fine
Here is the file, cut to the three rules that matter. Two tiers, one tool, one length bound. Read it as a spreadsheet and it is the spreadsheet finance signed.
<$CONFIG> $IMPORT tool issue_refund </$CONFIG> # Role You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints You MUST approve a refund up to $150 with @[issue_refund]. You MUST escalate a refund over $100 to a team leader. You MUST keep every reply to at most 3 sentences.
$ typeglish check tiers.tg tiers.tg:10:1 warn prompt/tier-overlap Tier overlap - the $100–$150 range sits in BOTH tiers: "approve" (line 7) and "escalate … to the team leader". A value there has two handlers and the model must guess. Make the tiers exclusive ("over $100"), or state which rule wins. ✓ 1 file - 0 error, 1 warning, 0 info $ typeglish score tiers.tg tiers.tg - B (81/100) proven errors: none tiers: base+z3 planes runtime 92 (what the model reads) · hygiene 50 (source only) facets enforceability 100 x.21 · hardness 100 x.12 · directness 100 x.08 consistency 100 x.17 · structure 100 x.12 (hygiene) · annotation 0 x.12 (hygiene) style 25 x.08 · security 100 x.08
consistency 100, the facet that measures rules against each other, is untouched: nothing here is a contradiction, because both rules can be satisfied at once and the file is perfectly satisfiable. The loss is style 25, which is where prompt/* advisories are priced. An overlap is not a broken prompt. It is an undecided one.A contradiction is two rules that cannot both hold. An overlap is two rules that both hold, and only one of them can act.
§2Two seams, two severities
Real tables have three rows, not two, and a three-row table has two seams. Give the desk a decline tier as well and the checker separates the two kinds of seam by how much of the scale they cost you.
<$CONFIG> $IMPORT tool issue_refund </$CONFIG> # Role You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints You MUST approve a refund up to $150 with @[issue_refund]. You MUST escalate a refund between $150 and $500 to a team leader. You MUST decline a refund over $400 in a first contact. You MUST keep every reply to at most 3 sentences.
up to $150 and between $150 and $500 touch at exactly one value, and only a refund of exactly $150 is affected. The second is the untidy kind: $400 against $500 leaves a hundred-dollar band with two owners, which is the same defect as before wearing a different pair of numbers.$ typeglish check three.tg three.tg:10:1 info prompt/tier-overlap Tier overlap - exactly $150 sits in BOTH tiers: "approve" (line 7) and "escalate … to the team leader". A value there has two handlers and the model must guess. Make the tiers exclusive ("over $150"), or state which rule wins. three.tg:11:1 warn prompt/tier-overlap Tier overlap - the $400–$500 range sits in BOTH tiers: "escalate … to the team leader" (line 8) and "decline". A value there has two handlers and the model must guess. Make the tiers exclusive ("over $400"), or state which rule wins. ✓ 1 file - 0 error, 1 warning, 1 info $ typeglish score three.tg three.tg - C (78/100) proven errors: none tiers: base+z3 L11 prompt/tier-overlap −1 Tier overlap - the $400–$500 range sits in BOTH L10 prompt/tier-overlap −0.25 Tier overlap - exactly $150 sits in BOTH tiers
info and costs a quarter point, because exactly one value is undecided. An overlapping range is warn and costs a full point, because an unknown number of values are. The message names the handler on each side by its verb rather than by quoting your line, which is how it manages to say escalate … to the team leader when neither rule contains that phrase.The severity split is also where the gate lives, and it is not the flag most teams reach for. check --strict escalates IMPORTANT correctness findings, and a prompt/ advisory is not one: the strict run on three.tg prints the same 0 error, 1 warning, 1 info and still exits 0. The score is the instrument that moves. score tiers.tg --min B exits 0 at B (81/100) with one range overlap in the file, while score three.tg --min B exits 1 at C (78/100). If your prompts carry authority tables, the CI line that catches this is a score floor, the same conclusion Your prompt is a default and its exceptions reached about the rest of the advisory family.
§3Why you have never seen this finding
Now the part worth knowing before you go looking for it in your own files, because there is a good chance the checker has been silent on a table you would recognise instantly. The finding has to work out which handler owns each tier, and it reads that off the front of the line. Put a bullet there and it cannot.
<$CONFIG> $IMPORT tool issue_refund </$CONFIG> # Role You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints - MUST approve a refund up to $150 with @[issue_refund]. - MUST escalate a refund over $100 to a team leader. - MUST keep every reply to at most 3 sentences.
tiers.tg, the same overlapping band from $100 to $150, and 0 error, 0 warning, 0 info. This is the shape almost every agent prompt in production is written in.Four controlled variants of the same two tiers put the boundary exactly on the front of the line, and it is the doer that decides:
- Fires.
You MUST approve a refund up to $150.besideYou MUST escalate a refund over $100 to a team leader.The pronoun subject leads, the modal follows, and the verb behind it is the handler. - Fires.
Approve a refund up to $150.besideEscalate a refund over $100 to a team leader.A bare imperative puts the handler first. - Silent.
MUST approve a refund up to $150.besideMUST escalate a refund over $100 to a team leader.No subject, and the first word of both lines is the same modal. - Silent. Either of the firing pairs with a
-bullet in front of each rule.
That is a narrower surface than the concept, and it is worth reading as an authoring rule rather than a gap. A tier row that does not say who acts is a row whose owner your reader has to infer too, which is the same argument prompt/unregistered-doer and prompt/agentless-passive make one line at a time. Name the doer and the tier gets an owner, in the file and in the checker at once.
Four more shapes are silent for reasons that are about the arithmetic rather than the grammar, and each one is a real edit somebody makes:
- Both tiers guarded. Lead both rules with
WHEN a customer asks for money back THEN …and the pair goes quiet. One guard is fine; two guards are read as two scopes. - One handler for both tiers.
approveup to $150 andapproveover $100 is silent, because the same handler taking a value twice is not a fork in the road. - A floor instead of a ceiling.
at least $100in place ofover $100is silent on the identical amounts. - The wrong currency.
150 poundsand100 poundsis silent. Dollars and percent are the units the tier reader knows, which is the same narrowness Why your agent approves refunds it shouldn't ran into from the other direction.
§4Make the boundary belong to one rule
The message offers two fixes and only one of them is a fix. State which rule wins is good advice for a human reader and does nothing here: appending instead to the escalate rule leaves the same 1 warning on the same band. What clears it is making the bounds meet exactly once, which for two tiers is one word.
<$CONFIG> $IMPORT tool issue_refund </$CONFIG> # Role @@ role: returns desk for a furniture retailer, with the spend authority written down You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints @@ own_tier: the amount this desk can settle without a second pair of eyes You MUST approve a refund up to $150 with @[issue_refund]. @@ handoff_tier: at the ceiling the tier changes, so the boundary belongs to exactly one rule You MUST escalate a refund over $150 to a team leader. @@ brevity: three sentences keeps a chat reply scannable You MUST keep every reply to at most 3 sentences. $TEST at_the_ceiling - input:: I want my money back on the 150 dollar side table. - expect:: - contains "refund" $TEST over_the_ceiling - input:: I want my money back on the 900 dollar sofa. - expect:: - contains "team leader"
0 error, 0 warning, 0 info at A (100/100), from B (81/100), with style 25 to 100 and annotation 0 to 100. up to $150 is inclusive and over $150 is exclusive, so $150 has exactly one owner and there is no value between them. test --dry reports coverage: 2/3 rules exercised, and the case that matters is the one sitting on the boundary, because the boundary is where the next edit will land.Two tiers is the easy case. Three is where prose bounds genuinely run out, and it is worth being honest about why rather than reaching for a fourth number. A middle tier needs a floor and a ceiling, its floor has to be the tier below’s ceiling, and English bounds on the same amount are either both inclusive or both exclusive: there is no way to write from just above $150 to $400 without inventing $151. When the host already knows the amount, the shape that has no seams at all is the deterministic chain, because first-true-wins does the exclusion for you.
<$CONFIG> $IMPORT tool issue_refund $REQUIRE variable refund_total: number </$CONFIG> # Role @@ role: returns desk for a furniture retailer, with the spend authority written down You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints @@ authority: the host knows the amount, so the compiler picks the tier and ships one $IF @{refund_total} is at most 150: You MUST approve the refund with @[issue_refund]. $ELSE IF @{refund_total} is at most 400: You MUST escalate the refund to a team leader. $ELSE: You MUST decline the refund and offer a repair. @@ brevity: three sentences keeps a chat reply scannable You MUST keep every reply to at most 3 sentences.
0 error, 0 warning, 0 info at A (92/100), from C (78/100). The three thresholds still touch, and it no longer matters: an arm is tested only if every arm above it was false, so at most 400 means at most 400 and more than 150 without anybody writing $151.$ typeglish build three-v2.tg --vars '{"refund_total":90}' ✓ built .typeglish/dist/three-v2.txt ← three-v2.tg (b3ef6453103d, full) $ typeglish build three-v2.tg --vars '{"refund_total":250}' ✓ built .typeglish/dist/three-v2.txt ← three-v2.tg (ccf288cdf099, full) $ typeglish build three-v2.tg --vars '{"refund_total":700}' ✓ built .typeglish/dist/three-v2.txt ← three-v2.tg (3f10b604054e, full) # the 250 artifact, in full: # Role You are a customer support agent for Larkfield Home, an online furniture retailer. # Constraints You MUST escalate the refund to a team leader. You MUST keep every reply to at most 3 sentences.
typeglish build three-v2.tg with no --vars writes 553ffa910c2b, and that artifact ships all three arms to the model as conditional prose, which is the seam back again with a different name. Bind the variable, or gate the template.The habit that falls out of this is cheap and it is not about TypeGlish. When you copy an authority table into a prompt, read the edges out loud, one row at a time: up to one hundred and fifty, over one hundred and fifty. Every edge should be said twice and mean one thing. If a row says over one hundred while the row above it says up to one hundred and fifty, you are not looking at a table any more, you are looking at a range with two owners, and the agent will pick one per conversation for as long as nobody reads it aloud.
§5Common questions
- Why does my AI agent treat the same refund amount differently on different contacts?
- Because two of your rules both cover that amount.
Approve a refund up to $150andescalate a refund over $100overlap on everything from $100 to $150, so a $120 request satisfies the approve rule and the escalate rule at once and nothing in the prompt says which wins. TypeGlish reports it asprompt/tier-overlap: the $100-$150 range sits in BOTH tiers, a value there has two handlers and the model must guess. The fix is to make the bounds meet exactly once,up to $150againstover $150, which checks at0 error, 0 warning, 0 info. - How do I write refund approval limits in a system prompt?
- Write each tier as a bound on an obligation, name the doer, and let the two bounds share one edge with no slack:
You MUST approve a refund up to $150 with @[issue_refund]besideYou MUST escalate a refund over $150 to a team leader. That pair is0 error, 0 warning, 0 info, and with a@@note per rule and two$TESTcases the file scoresA (100/100). Once you have three or more tiers, prose bounds run out: the third tier will always overlap the second unless you nest it, so declare the amount as a typed input and put the tiers in an ordered$IFchain, where first-true-wins makes the arms exclusive by construction. - Why has the checker never flagged my tier table?
- Almost certainly because your rules are bullets.
prompt/tier-overlapreads the handler off the front of the line, so it needs a subject or an imperative there.You MUST approve a refund up to $150besideYou MUST escalate a refund over $100 to a team leaderis 1 warning; the identical pair written- You MUST approve …orMUST approve …is0 error, 0 warning, 0 info.Approve a refund up to $150as a bare imperative fires. Four more shapes are silent too: both rules guarded withWHEN, both tiers handled by the same verb, a floor writtenat least $100, and any amount in pounds rather than dollars or percent. - Does check --strict catch a tier overlap?
- No.
prompt/tier-overlapstays a warning undercheck --strict, which escalateslogic/correctness defects rather thanprompt/advisories, socheckexits 0 either way. The gate that catches it is the score. An overlapping range costs 1 point of style and a shared boundary point costs 0.25, so a two-rule overlap still lands atB (81/100)and passesscore --min B, while the three-tier version carrying one range and one point drops toC (78/100)and exits 1. If tier tables matter in your prompts, gate onscore --min B, not on--strict.
The two severities inside one code are the most useful thing here, and they are a judgement about how much of the scale you have lost rather than about how wrong you were. A shared boundary point is one undecided value and reads as info; an overlapping range is an unknown number of them and reads as warn. Nothing in either case is a contradiction, which is why consistency stays at 100 through all of it: both rules really can hold, and that is precisely the problem.