How to write order-cancellation rules for an AI support agent
“Cancel my order” sounds like the simplest contact a support agent gets. It is one of the few where the agent changes something it cannot change back, about a parcel it cannot see, for a customer who will hold it to every word. Here is how to write the rules so each of those is somebody’s job.
TL;DR Let a cancel tool own the dispatch cutoff, confirm the order number before it fires, route anything already dispatched to a return, and have the agent report only what the tool returned, refund date included.
Most cancellation sections in agent prompts are pasted from the help page: one line of policy, one line of process, a retention nudge from the growth team, and a refund estimate. Each line made sense to the customer it was written for. Read by an agent with a tool, almost every line asks it to know something only the warehouse knows.
§1The section as pasted
Fernlea Outdoor sells tents, packs and boots online. Its chat assistant has one tool, cancel_order, and this section:
# Role You are the chat assistant for Fernlea Outdoor, an online outdoor-gear retailer. # Cancellations - Orders can be cancelled until they are dispatched. - WHEN the customer asks to cancel an order THEN MUST call @[cancel_order]. - IF the order has shipped THEN NEVER call @[cancel_order]. - Try to save the order by offering a discount. - Refunds usually arrive within 3 to 5 days. $TOOL cancel_order - description:: Cancel an order that has not been dispatched. - input:: - order_number:: the order to cancel.
$ typeglish review naive.tg
naive.tg — C (71/100) → A (99) projected if every fix below lands check: 0 error, 0 warning, 6 info tiers: base+z3
FIXES ranked by projected gain
+12 put a "@@ why" note above L2 L5 L6 L7 L8 L9 — annotation 0 → 100
+6 lead with a modal (MUST / NEVER / SHOULD) on L5 L8 L9 — 3 rules become visible to the checker
+4 commit the 1 soft statement ("try to" on L8) to MUST / NEVER / ALWAYS / REQUIRE / ENSURE — or keep the word if it is a permission by design — up to hardness 100
+3 use the imperative or a named doer on L5 — up to directness 100
+1 fix the 1 prompt/hedging finding (L8) — A hedge (try to, if possible) softens an instruction; commit to a modal.
+1 fix the 1 prompt/agentless-passive finding (L5) — The doer is deleted (agentless passive) or demoted to a by-phrase - name the actor or use the imperative.
+1 fix the 1 prompt/unregistered-doer finding (L8) — A bare generic doer (Users should ...) never enters the world model - the imperative or a definite party registers wh…
RULES 2 of 5 rule-shaped lines read
L5 ✗ invisible Orders can be cancelled until they are dispatched.
no modal (MUST / NEVER / SHOULD) and "Orders" 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 …)
info, so the section builds. Three of the five lines reach the model as prose the checker cannot read: the policy, the save attempt, and the refund estimate.The two lines it can read are the interesting ones. Line 6 says to call the tool when the customer asks. Line 7 says never to call it when the order has shipped. The checker reads both, finds no conflict, and is right: with different conditions, they do not collide on paper. In a chat they collide all the time, because the agent has no way to tell whether line 7’s condition holds.
§2The cutoff belongs to the warehouse
IF the order has shipped is a fact about a pallet in a building. The agent sees a chat window. To evaluate the condition it has to guess, and the clues on hand are bad ones: the customer saying it hasn’t shipped yet, has it?, an estimated delivery date, or an order placed only an hour ago. Any of those can be wrong by the time the van leaves. So the agent says done, cancelled, and the parcel arrives two days later.
The fix is to stop asking the agent the question at all. The cancel tool is the one party that can answer it at the moment it matters, so give it the job. Its description says what it does with each item: cancel what is still in the warehouse, and report what has already left. The agent’s rule loses its condition and gains a pointer. (Building a warranty-claim agent, prompt-first does the same with a warranty clock: the tool owns the date, the prompt owns the response.)
A dispatched item is then a different job, not a refusal. That needs a second tool, and here the checker helps by refusing to let you assume one:
$ sed -n '10,11p' s3.tg @@ dispatched: a parcel on a van is a return, not a cancellation - WHEN @[cancel_order] reports a dispatched item THEN MUST call @[start_return]. $ typeglish check s3.tg s3.tg:11:65 error structure/undefined-tool-inline @[start_return] — no $TOOL or $IMPORT tool named "start_return" in this file. Define it ("$TOOL start_return") or import it ("$IMPORT tool start_return") so the pointer has a target. ✗ 1 file — 1 error, 0 warning, 0 info
start_return, which means somebody has to find out whether the platform can open a return from chat. If it cannot, you learn that before launch instead of from a customer.Never give the agent a condition only the warehouse can evaluate.
§3Confirm first, as its own trigger
A cancellation is a write the agent cannot take back. Customers with two open orders say cancel my order all the time, and the wrong one stopped is a worse contact than the one you started with. So the agent confirms which order before the tool fires. The natural way to write that does not hold up:
$ grep MUST s2.tg - MUST confirm the order number before you call @[cancel_order]. $ typeglish review s2.tg RULES 1 of 1 rule-shaped line read · 1 not provable L5 ~ unprovable MUST confirm the order number before you call @[cancel_order]. the checker knows "confirm" but cannot prove this shape: it stops reading at "before" — the clause after it is not part of the provable action → if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule
Do what the message says and split it into two events: the customer asks, so the agent confirms; the customer confirms, so the agent calls the tool. Each rule now has its own trigger, and there is no reading of the pair where the tool fires on the first message. (Your agent can now do something it cannot undo is the checklist for every write action like this one, not just cancellations.)
§4Say what the tool said, and nothing it did not
The last three lines of the pasted section are about what the agent says after the decision, and each one has the agent saying something no system confirmed.
A partly dispatched order is partly cancelled. A customer orders a tent and a pair of boots; the boots ship from another warehouse that morning. Your order has been cancelled is now false for half of it. Make the split a rule: when the tool reports a dispatched item, list every dispatched item, and start the return for them.
The save attempt is a pricing decision. Try to save the order by offering a discount is the line the checker flags as prompt/hedging, and for a good reason: a hedge the model may or may not follow means some customers are offered money to stay and others are not, for the same request. Pick one. This guide picks the ban, because a customer cancelling inside the window is using a right, and a discount at that point is a price on it. If your business decides the other way, write a MAY with a fixed amount and a test, not a try to.
The refund date comes from the system. Refunds usually arrive within 3 to 5 days is a typical week. The customer hears a promise about their money. The tool knows the actual date from the payment provider, so the rule is to quote that one.
# Role @@ role: the desk this file speaks for You are the chat assistant for Fernlea Outdoor, an online outdoor-gear retailer. # Cancellations @@ confirm_first: a cancellation cannot be undone, so the customer names the order before anything happens - WHEN the customer asks to cancel an order THEN MUST confirm the order number with the customer. @@ tool_owns_cutoff: only the warehouse knows what has left the building; the tool answers, the agent never guesses - WHEN the customer confirms the order number THEN MUST call @[cancel_order]. @@ dispatched: a parcel on a van is a return, not a cancellation - WHEN @[cancel_order] reports a dispatched item THEN MUST call @[start_return]. @@ name_the_split: a partly dispatched order is partly cancelled, so the reply says which part - WHEN @[cancel_order] reports a dispatched item THEN MUST list every dispatched item. @@ no_save_discount: the customer is inside the window; a discount here is a price for their right - NEVER offer a discount to the customer. @@ refund_date: the date comes from the payment system, not from a typical week - MUST quote the refund date you get from @[cancel_order]. @@ brevity: a chat bubble, not an essay - MUST answer in at most 3 sentences. $TOOL cancel_order - description:: Cancel every item of an order that has not left the warehouse. Returns the cancelled items, any dispatched items, and the refund date. - input:: - order_number:: the order to cancel. $TOOL start_return - description:: Open a return for dispatched items and email the customer a prepaid label. - input:: - order_number:: the order with dispatched items. $TEST cancel_ask - input:: I want to cancel my order, I bought the wrong size tent. - expect:: - confirms the order number before anything is cancelled - does not offer a discount - matches /^(?!.*[Dd]iscount)/ - at most 3 sentences $TEST half_shipped - input:: Yes, it is FO-55102. Cancel all of it please. - expect:: - calls cancel_order - lists every dispatched item - quotes the refund date from the tool $TEST already_gone - input:: The tracking says it left yesterday but I still want to cancel. - expect:: - calls start_return - matches /^(?!.*[Dd]iscount)/
$ typeglish check fixed.tg ✓ 1 file — 0 error, 0 warning, 0 info $ typeglish score fixed.tg fixed.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 94 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 · 7/7 covered by $TEST $ typeglish test fixed.tg --dry ✓ fixed.tg coverage: 7/7 rules exercised · cancel_ask — "I want to cancel my order, I bought the wrong size tent." (not run) ✓ matches /^(?!.*[Dd]iscount)/ ✓ at most 3 sentences · rubric — 2 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY) · half_shipped — "Yes, it is FO-55102. Cancel all of it please." (not run) · rubric — 3 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY) · already_gone — "The tracking says it left yesterday but I still want to canc" (not run) ✓ matches /^(?!.*[Dd]iscount)/ · rubric — 1 prose expectation, judged on a live run (needs ANTHROPIC_API_KEY) ✓ 1 prompt — 0 failed $ typeglish build fixed.tg && cat .typeglish/dist/fixed.txt ✓ built .typeglish/dist/fixed.txt ← fixed.tg (24e5752bd47d, full) # Role You are the chat assistant for Fernlea Outdoor, an online outdoor-gear retailer. # Cancellations - WHEN the customer asks to cancel an order THEN MUST confirm the order number with the customer. - WHEN the customer confirms the order number THEN MUST call cancel_order. - WHEN cancel_order reports a dispatched item THEN MUST call start_return. - WHEN cancel_order reports a dispatched item THEN MUST list every dispatched item. - NEVER offer a discount to the customer. - MUST quote the refund date you get from cancel_order. - MUST answer in at most 3 sentences.
The three tests map to the three contacts that go wrong in production: the first ask, where the agent must confirm and must not haggle; the split order, where it must name what is still coming; and the order that has already left, where it must start a return instead of saying no. The already_gone input is worth keeping word for word. A customer who quotes the tracking page is giving the agent the exact clue it must not act on.
§5Common questions
- How do I let my AI support agent cancel orders safely?
- Give it a cancel tool that owns the dispatch cutoff, so the warehouse system decides what can still be stopped. Then write three rules: confirm the order number when the customer asks to cancel, call the tool when they confirm, and report exactly what the tool returned.
- Why does my agent tell customers an order is cancelled when it has already shipped?
- Usually because the prompt says something like
IF the order has shipped THEN NEVER cancel it, and the agent cannot see whether it has shipped. It guesses from the conversation or the estimated delivery date. Let the cancel tool report dispatched items, and route those to a return. - Should my agent try to save an order with a discount before cancelling?
- Decide, and write the decision as a rule. Try to save the order is a hedge the model may or may not act on, so different customers get different prices for the same request. This guide picks
NEVER offer a discount to the customerand tests for it. - What should my agent say about when the refund arrives?
- Only the date the cancel tool returns. A line like refunds usually arrive within 3 to 5 days is a typical week, not this customer’s refund, and the agent will quote it as a promise.
Before you wire the cancel tool, ask the warehouse team one question: at what moment does an order stop being cancellable? The honest answer is rarely “when it ships”. It is usually when it is picked, or when the label prints, and it is often different per warehouse. Whatever the answer, it goes in the tool, not the prompt. The prompt’s job is the conversation around the tool, and typeglish review is a quick way to find the lines still trying to do the tool’s job: on the pasted section, two of the three invisible lines were policy the tool should own.