← Tidelines/Guides

Building a warranty-claim agent, prompt-first

Warranty is where a support agent stops answering questions and starts making calls that cost money: is the appliance still covered, does the damage count, what does the customer get. The brief makes all three sound like reading comprehension. Two of them are a lookup, and the third is a diagnosis nobody in the chat can make.

by TypeGlish team9 min read#guides
Covered is a lookup. Accidental is a diagnosis.

TL;DR A warranty-claim agent should never compute coverage or judge the cause of a fault: a lookup tool owns the warranty clock, every covered fault becomes a claim the repair centre assesses, and an exclusion such as accidental damage belongs in a WHEN condition, where the checker can see it contradict the no-refusal rule.

Brennock sells kitchen appliances online: kettles, toasters, stand mixers, the odd espresso machine. Its chat assistant already handles order questions, and warranty is the next queue the support lead wants off the team's plate. The brief from operations is seven lines long and reads like a summary of the warranty terms, which is what it is. Here it is as a prompt, then as a spec.

§1The brief, checked as written

brief.tg - the seven lines as sent✗ C (70/100)
# Role
You are the warranty assistant for Brennock, an online shop for kitchen appliances.

# Constraints
- All products come with a 2 year warranty.
- Check whether the product is still under warranty.
- Always ask for proof of purchase.
- Accidental damage is not covered, so don't accept those claims.
- Offer a repair or a replacement.
- If the appliance is a safety risk, escalate.
- Be empathetic.
An operations lead's brief, typed out. As warranty policy it is ordinary.
tg check and tg score on the brief - output✗ 1 of 7 lines read
$ npx typeglish check brief.tg | tail -1
✓ 1 file — 0 error, 1 warning, 5 info

$ npx typeglish score brief.tg
brief.tg — C (70/100)  proven errors: none  tiers: base+z3
  planes  runtime 78 (what the model reads) · hygiene 46 (source only)
  facets  enforceability 47 x.21 · hardness 100 x.12 · directness 85 x.08 · consistency 100 x.17 · structure 91 x.12 (hygiene) · annotation 0 x.12 (hygiene) · style 53 x.08 · security 100 x.08
  lever   annotation 0/100 (up to +12 overall) — 8 statements without a @@ note (L2 L5 L6 L7 L8 L9 L10 L11) — put a "@@ why" line directly above each
  rules   1 of 7 rule-shaped lines read · 1 unmeasurable
    L5   ✗ invisible     All products come with a 2 year warranty.
                         no modal (MUST / NEVER / SHOULD) and "All" 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 …)
    L6   = fact          Check whether the product is still under warranty.
                         a declared fact, not a rule — the world model reads it; it has no modal to enforce
    L8   = fact          Accidental damage is not covered, so don't accept those claims.
                         a declared fact, not a rule — the world model reads it; it has no modal to enforce
    L9   ✗ invisible     Offer a repair or a replacement.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "offer" but cannot prove this shape: two objects or actions joined by "or" — it proves one at a time, so it reads as prose → lead with a modal (MUST …) to make it a rule, and one object per line: split into two rules to make it provable
    L10  ✗ invisible     If the appliance is a safety risk, escalate.
                         the condition has no THEN branch, so nothing is required when it holds → add THEN MUST / NEVER / SHOULD <verb> …
    L11  ~ unmeasurable  Be empathetic.
                         no observable action or bound, so nothing can check compliance → name a concrete action or a bound ("at most 3 sentences")
  L11  prompt/unmeasurable          −1  Unmeasurable rule — "be <quality>" names no observable action, so nothing can 
  L8  prompt/agentless-passive     −0.25  The doer is deleted (agentless passive) - who performs this? Name the actor or
  L10  typeglish/if-then            −0.25  IF needs a THEN — write IF <condition> THEN <action>.
No errors, so a pipeline that gates on check lets this through. The only line the checker reads as a rule is Always ask for proof of purchase, and §2 argues it is the wrong rule.

Most of the table is the usual work: lead with a modal, give the if its THEN, split the or, replace be empathetic with something a reader could check. Two rows deserve a slower read. The line that describes the agent's whole job, Check whether the product is still under warranty, is filed as a = fact: the checker reads the clause the product is still under warranty as a statement about the world, so the core instruction of the prompt carries no rule at all. The exclusion line is a fact too. Neither is a checker failure so much as a sign of what kind of document this is. Warranty terms are written to describe cover. An agent prompt has to say who decides it.

The brief hides three decisions: whether the appliance is still covered, whether this fault counts, and what the customer gets. The rest of this post gives each one an owner, and only some of those owners are the model.

§2The warranty clock belongs to a tool

All products come with a 2 year warranty, read next to check whether it is still under warranty, asks the model to do date arithmetic. It needs the purchase date, or the delivery date if that is when Brennock's cover starts. It needs today's date, which it does not have unless something passes it in. Then it has to subtract and compare, from evidence like I bought it around Christmas. Typing the rule properly makes it visible to the checker, and the checker earns its keep the week Brennock Care launches with five years of cover:

clock.tg - the clock in the prompt, plus the new plan✗ logic/contradiction
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are the warranty assistant for Brennock, an online shop for kitchen appliances.

# Constraints
@@ two_years: the standard warranty, typed out from the brief
- NEVER open a warranty claim for an appliance older than 24 months.
@@ care_plan: added the week Brennock Care launched, with five years of cover
- WHEN the customer has Brennock Care THEN MUST open a warranty claim for an appliance older than 24 months.
tg check - clock.tg✗ 2 errors, build refused
$ npx typeglish check clock.tg
clock.tg:7:1  error  logic/contradiction  Conflicts with line 9. Logical conflict — "open a warranty claim for an appliance older than 24 months" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
          ↳ line 9: - WHEN the customer has Brennock Care THEN MUST open a warranty claim for an appliance older than 24 months.
clock.tg:9:1  error  logic/contradiction  Conflicts with line 7. Logical conflict — "open a warranty claim for an appliance older than 24 months" is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
          ↳ line 7: - NEVER open a warranty claim for an appliance older than 24 months.

✗ 1 file — 2 error, 0 warning, 0 info
A real catch: the ban has no condition, so it forbids the Care claim the second line requires. The suggested fix, scoping the ban, would build. It would also leave the subtraction with the model, now with two periods to choose between.

Scoping the ban is the right fix for the logic and the wrong fix for the design. Next quarter a refurbished line ships with twelve months of cover, and the prompt grows a third number and a third condition. The warranty period is a fact about one appliance on one date, and the system that knows the purchase date, the plan and today's date is the order system. So the clock moves there: a check_warranty tool takes an order number or a serial number and answers covered, expired or no record. The final prompt does not contain a number of months anywhere. That is the general case Bake-off: the number in the prompt vs. the number at runtime makes for prices and limits; a warranty period is the same kind of number, with a date attached.

The lookup also settles the one line the checker did read. Brennock sold the appliance and emailed the receipt, so Always ask for proof of purchase sends a customer digging through their inbox for a document the company already holds. Proof of purchase matters when the lookup comes back empty, for a gift or an appliance bought from another retailer, and that is where the final spec asks for it: WHEN @[check_warranty] returns no record THEN MUST ask for proof of purchase.

§3Accidental damage is a diagnosis

The exclusion is real policy. Warranties like Brennock's cover manufacturing faults, not a kettle knocked off the counter. The question the brief skips is who applies it. In a chat, the only evidence is the customer's account. I dropped it and now the lid won't close may be a hinge that was failing before the drop. It just stopped working may be a mixer that sat in a puddle. The person who can tell is a technician with the appliance on the bench, so the agent's rule is the opposite of the brief's: it never refuses a claim, and every covered fault goes to the repair centre with the customer's description attached.

That rule will be tested by the next edit. Somebody reads the warranty terms, finds the exclusion missing, and puts it back. Here is the same exclusion written two ways, each beside the no-refusal rule:

damage-object.tg - the exclusion folded into the object✗ A (100/100), contradiction inside
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are the warranty assistant for Brennock, an online shop for kitchen appliances.

# Constraints
@@ no_refusal: the cause of a fault is decided at the bench, not in the chat
- NEVER refuse a warranty claim.
@@ damage_exclusion: the brief's fourth line, made into a rule
- MUST refuse a warranty claim for accidental damage.
damage-when.tg - the same exclusion as a condition✗ logic/contradiction
# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are the warranty assistant for Brennock, an online shop for kitchen appliances.

# Constraints
@@ no_refusal: the cause of a fault is decided at the bench, not in the chat
- NEVER refuse a warranty claim.
@@ damage_exclusion: the same exclusion, with the condition moved into a WHEN
- WHEN the customer reports accidental damage THEN MUST refuse the warranty claim.
tg check and tg score - both spellings✗ one caught, one at an A
$ npx typeglish check damage-object.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score damage-object.tg | head -1
damage-object.tg — A (100/100)  proven errors: none  tiers: base+z3

$ npx typeglish check damage-when.tg
damage-when.tg:7:1  error  logic/contradiction  Conflicts with line 9. Logical conflict — "refuse the warranty claim." vs "refuse a warranty claim." is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 9: - WHEN the customer reports accidental damage THEN MUST refuse the warranty claim.
damage-when.tg:9:1  error  logic/contradiction  Conflicts with line 7. Logical conflict — "refuse the warranty claim." vs "refuse a warranty claim." is both required and forbidden. Keep one, or scope the two rules so they cannot both apply (IF <condition> THEN ...).
                ↳ line 7: - NEVER refuse a warranty claim.

✗ 1 file — 2 error, 0 warning, 0 info
Same policy, same two rules. Written as a condition, the exclusion meets the ban on the same object and the build is refused. Folded into the object, a warranty claim for accidental damage is a different phrase from a warranty claim, nothing is compared, and the pair scores a perfect 100.

The checker compares the object phrases as written, so where the condition sits decides whether it can see the conflict. That is a cousin of the blind spot in We put the condition last. The checker stopped defending the rule., and the house habit that follows is the same: every exclusion goes in a WHEN, never in a trailing for phrase. Written that way, the contradiction is the checker telling you something true. You cannot both refuse accidental-damage claims in the chat and leave every claim to the bench. Pick one, on purpose. This spec picks the bench, so the exclusion stays out of the prompt and lives in the repair centre's assessment, which is the only place it can be applied on evidence.

§4Repair, replace, and the smell of burning

Offer a repair or a replacement was invisible for the reason the table gave: two objects joined by or, the shape Or is two rules covers. Splitting it does not help, because neither half is the agent's to offer. Which remedy the customer gets is the outcome of the assessment, and can you just send me a new one? is the most common thing a warranty customer types. The rule the agent needs is a fence: NEVER confirm a replacement. One drafting note: the first version said promise, which is not a verb the checker knows, so the line read as a rule but could not be proven. Confirm is in the catalog, and it is also the more precise word for what goes wrong.

If the appliance is a safety risk, escalate had no THEN, and its condition is a judgement the customer will never phrase that way. Nobody types my toaster is a safety risk. They type it's smoking, it smells like burning, there were sparks, it gave me a shock. Each of those is its own WHEN, each opens with the instruction that matters before any warranty question, unplug the appliance, and they sit at the top of the constraints. The spec below carries smoke and a burning smell; sparks and shocks follow the same pattern.

The last case is the one the brief did not mention: the lookup says expired. It is tempting to have the agent close the conversation there. Do not. In many countries, including the UK and the EU member states, consumer law gives the buyer rights that do not end when a retailer's warranty does, and a chatbot that answers an expired lookup with there is nothing we can do is making a legal statement on Brennock's behalf. The expired arm transfers the customer to a human agent, who can offer a paid repair or look at the case properly.

§5The spec, checked and tested

Everything above, assembled. Two tools carry the two decisions the model should not make, the context introduces the assessment the rules point at, and the eight constraints are ordered by urgency: safety, then the lookup, then the three outcomes of the lookup, then the two fences.

warranty.tg - the shipped spec✓ A (99/100)
$TOOL check_warranty
  - description:: Look up an appliance by order number or serial number. Returns covered, expired or no record.
  - input::
    - reference:: string: the order number or the serial number.

$TOOL open_claim
  - description:: Open a warranty claim for the Brennock repair centre to assess.
  - input::
    - reference:: string: the order number or the serial number.
    - fault:: string: the fault in the customer's words.

# Role
@@ role: one sentence names the speaker, so every later "you" is the agent
You are the warranty assistant for Brennock, an online shop for kitchen appliances.

# Context
@@ assessment_fact: introduces the assessment the claim rules point at
Every warranty claim gets an assessment at the Brennock repair centre.

# Constraints
@@ smoke_first: a safety report outranks the warranty question
- WHEN the customer reports smoke THEN MUST tell the customer to unplug the appliance.
@@ smell_first: a burning smell gets the same first instruction as smoke
- WHEN the customer reports a burning smell THEN MUST tell the customer to unplug the appliance.
@@ status_from_tool: the warranty clock is a date sum, and the tool owns the dates
- WHEN the customer reports a fault THEN MUST call @[check_warranty].
@@ covered_claim: a covered fault becomes a claim, not a verdict
- WHEN @[check_warranty] returns covered THEN MUST call @[open_claim].
@@ receipt_only_when_missing: Brennock issued the receipt, so ask only when the lookup finds nothing
- WHEN @[check_warranty] returns no record THEN MUST ask for proof of purchase.
@@ expired_handoff: an expired warranty is not the end of the customer's rights
- WHEN @[check_warranty] returns expired THEN MUST transfer the customer to a human agent.
@@ no_refusal: the cause of a fault is decided at the bench, not in the chat
- NEVER refuse a warranty claim.
@@ no_remedy_promise: repair or replacement is the outcome of the assessment
- NEVER confirm a replacement.

$TEST smoking_toaster
  - input:: My toaster started smoking this morning. Order BR-55102. Is it still under warranty?
  - expect::
    - tells the customer to unplug the appliance before anything else
    - calls check_warranty for BR-55102
    - contains "unplug"

$TEST dropped_kettle
  - input:: I dropped my kettle and now the lid hinge won't close. Can you just send me a new one?
  - expect::
    - asks for an order number or a serial number
    - does not refuse a warranty claim
    - does not confirm a replacement
    - matches /^(?![\s\S]*not covered)/i
Two tools, one fact, eight rules, two cases. No number of months appears anywhere; the dropped kettle is the contact that tempts the model to apply the exclusion itself, so it gets a deterministic assert that the words not covered never appear.
tg check, score and test --dry - warranty.tg✓ A (99/100), 8 of 8 read
$ npx typeglish check warranty.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score warranty.tg
warranty.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 93 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   8 of 8 rule-shaped lines read

$ npx typeglish test warranty.tg --dry
✓ warranty.tg  coverage: 8/8 rules exercised
  · smoking_toaster — "My toaster started smoking this morning. Order BR-55102. Is " (not run)
      ✓ contains "unplug"
      · rubric — 2 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY)
  · dropped_kettle — "I dropped my kettle and now the lid hinge won't close. Can y" (not run)
      ✓ matches /^(?![\s\S]*not covered)/i
      · rubric — 3 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY)
✓ 1 prompt — 0 failed

Now the honest part. 8/8 rules exercised is a claim about shared wording, and two cases do not test eight rules. Delete the expectation tells the customer to unplug the appliance before anything else and coverage drops to 5/8, with the transfer rule listed as uncovered beside the two safety rules: a line about unplugging a toaster was being credited to the expired handoff and the words they share are the customer. No case can script what check_warranty returns, either, so the expired and no-record paths are tested where they are decided, against the tool.

And the line from §3 is still a live risk. Add MUST refuse a warranty claim for accidental damage. to this file and here is what the gate says:

the shipped spec plus the folded exclusion - output✗ A (98/100), 9/9 covered
$ npx typeglish check w2.tg
✓ 1 file — 0 error, 0 warning, 0 info

$ npx typeglish score w2.tg | head -1
w2.tg — A (98/100)  proven errors: none  tiers: base+z3

$ npx typeglish test w2.tg --dry | head -1
✓ w2.tg  coverage: 9/9 rules exercised
The point it loses is the missing @@ note on the new line. Coverage goes up: the expectation does not refuse a warranty claim shares the new rule's words, so the test written to stop refusals is counted as exercising the rule that demands them.

What catches that edit is the dropped_kettle case on a live run, where a model following the new line says not covered and the lookahead fails, and a reviewer who knows the house habit from §3: exclusions go in a WHEN or they do not go in.

§6Common questions

How do I write a system prompt for an AI warranty claims agent?
Take the decisions out of the model's hands first. A lookup tool that takes an order number or a serial number decides whether the appliance is still covered, a claim tool hands every covered fault to the people who assess it, and the prompt holds the rules around those two calls: safety reports first, ask for proof of purchase only when the lookup finds no record, never refuse a claim, never confirm a replacement. Then add test cases for the contacts that tempt the model to decide on its own, such as a customer who says they dropped the appliance.
Should an AI support agent decide whether a warranty claim is covered?
Not on the cause of the fault. Whether a broken hinge is a manufacturing defect or accidental damage is decided by someone looking at the appliance, and the only evidence in a chat is the customer's account of what happened. Let the agent check the dates through a tool and open the claim, and let the repair centre apply the exclusions.
Why does my support bot ask customers for a receipt we already have?
Because the prompt says to always ask for proof of purchase, and the model follows it even when the order is in your own system. Scope the rule to the case that needs it: look the appliance up first, and ask for proof of purchase only when the lookup returns no record, as it will for a gift or an appliance bought from another retailer.
Can TypeGlish catch a warranty exclusion that contradicts my other rules?
Only when the exclusion is written as a condition. WHEN the customer reports accidental damage THEN MUST refuse the warranty claim contradicts NEVER refuse a warranty claim, and the checker blocks the build with logic/contradiction. MUST refuse a warranty claim for accidental damage says the same thing with the condition folded into the object, and it checks clean at an A, because the checker compares the object phrases as written.
Field note

Before the warranty queue goes live, open the warranty terms and write every exclusion in them as one WHEN ... THEN MUST refuse the warranty claim line in a scratch copy of the prompt, then run typeglish check. Each logic/contradiction against NEVER refuse a warranty claim is an exclusion your agent would otherwise be applying on its own, from a chat transcript. Decide each one on purpose, delete the scratch lines, and keep the list: it is the brief for the repair centre's half of the system.

∿ washed up Oct 22, 2026 ∿