Home › Use Cases › Recompute the refund owed when an F&I product is cancelled on one contract
Use caseUC0339
🧪 Use-case kit · runnable

Recompute the refund owed when an F&I product is cancelled on one contract

A small, forkable project that does one job end to end. Run once for real, and every figure on these pages captured from that run.

The business caseThe problem this solves

When a customer cancels a vehicle service contract, a GAP waiver or a prepaid maintenance plan part-way through its term, a refund of the unearned part of the price is owed. How much is unearned falls out of the contract — its price, its term in months and in miles, the method it refunds on, its cancellation fee and the cap on it — once two things are known that the contract cannot supply: WHEN the cancellation takes effect, and WHAT THE ODOMETER SAID at that date. Both live in a packet: three printed dates of which two can never be the effective date, an event that may have been reported and never happened, and an evidence block whose STATUS column reads ACCEPTED over figures that were transposed, estimated, read off another vehicle or keyed twice. The dealer's own system reads the columns and prints a refund; on this corpus it is wrong on 53 of 62 packets. Opening one cancellation packet, deciding which of three printed dates governs and whether the event on the block actually happened, reading the note under every ACCEPTED odometer row, converting an imported cluster's kilometres by hand, multiplying the earned fraction out on the contract's own method, capping the fee and checking whether it is waived at all, and comparing the answer with the refund the dealer's system already printed.

Audience

An F&I administrator's cancellation desk and the dealer group's business office, working the day's cancellation queue before an accountant approves any payout. Every number on these pages came from one real run of this code, not from a vendor page.

The inputThe actual cancellation packet

The corpus is 62 cancellation packet, 0.18 MB (txt 62). It is generated because it has to be. A cancellation packet is a customer's own contract, a lender's payoff and a dealership's trading record all on one page, and the exact shapes this kit measures — a total-loss claim that was denied and the unit returned, a business manager asking for a fee to be waived and a finance manager named — are the rows a dealer group would least want published. The compensation is a key DERIVED from the structure the packets were rendered from, re-derived a second time by an independent checker at 0 disagreements and red-proven eight ways.

The corpus

  • The 62 cancellation packetgenerated from a fixed seed, so no real record, person or institution appears in it.
  • Where each came fromwritten for this kit rather than collected — the corpus is generated in the kit's own repository, so there is no third-party data in it.

Swap this folder for your own material and the kit is pointed at your cancellation packet. That is the whole change — there is no database to migrate.

One cancellation packet, as the model receives itCRF-0001.txt · 1 of 62
==============================================================================
F&I PRODUCT CANCELLATION PACKET                             CRF-0001
Dealer: DLR-4100 - Northgate Motors (invented)
Contract: RIC-700000   Product: VSC - vehicle service contract   Procedure: CR-2026
==============================================================================

CONTRACT AND PRODUCT AS THE ADMINISTRATOR HOLDS IT
  product                                 VSC
  product price                       2525.00
  term months                              84
  term miles                           100000
  contract date                    2023-03-07
  in-service odometer                     390
  refund method              pro-rata-greater
  cancellation fee                      75.00
  fee cap                               50.00
  payoff outstanding                  2000.00
  tolerance pct                          0.75   pct of the product price
  threshold cost                        50.00   or more
  threshold pct                          2.00   pct or more
  packet                       single-product

CANCELLATION REQUEST AS SUBMITTED
  request signed                   2024-12-10
  dealer received                  2024-12-16
  administrator stamped            2024-12-21
  reason as boxed            customer-request
  odometer as boxed                     28390

ODOMETER AND EVENT EVIDENCE AS RECORDED
  EVIDENCE   DATE           READING  SOURCE              STATUS    REF          MEMO
  ODO-0101   2024-08-30        8790  state-inspection    ACCEPTED  SI-60000     cluster reads miles
  ODO-0102   2024-10-10       14390  repair-order        ACCEPTED  RO-41001     cluster reads miles

Abridged — the file continues.

The outcomeWhat a good result looks like

One packet in, one row out: when the cancellation takes effect, the odometer at that date, which evidence rows the block has wrong with each one quoted verbatim, and then in pure code the earned fraction on the contract's own method, the unearned premium, the fee the rule allows, the refund, the payee and one CR-2026 verdict against the refund the dealer's system already printed.

And when it cannot

And what it does when it cannot. On the scored run 62 of 62 replies parsed, nothing stopped at the ceiling and there is no unparsed row to report. What it gets WRONG is published by name: on 3 packets of 62 it returned the dealer's own mileage on a packet where an ACCEPTED reading was never taken, on 3 it applied an event that was reported and did not happen, on 1 it missed the offsetting pair, and on 1 it lost EXPIRED. An arm that answers nothing reaches no rule at all — src/policy.py returns a null verdict rather than folding it into AGREED.

Where it fitsWhat did work

Every line below is a measured result from this kit's own runs, with the figure that supports it. The headline above is not softened by any of them.

  • Your evidence block's bad rows announce themselves — every transposition says "transposed", every duplicate says "duplicate" — the free rules floor
    it is 0 calls and $0.00, and on the wordings its keyword list carries it is right
  • Your odometer readings arrive in a mix of miles and kilometres with both figures recorded — free code
    it is arithmetic, and both arms are 5 of 5 on that family. Paying for it buys nothing measurable
  • Your packets carry events that are reported and later withdrawn, in prose — the paid call
    6 of 9 against the free rule's 2, and a wrong effective date moves the earned fraction AND the fee together
  • Your service notes talk about a correction to a DIFFERENT line on the same document — the paid call
    11 of 13 against 0. A keyword rule sets aside every one of them
  • You need the refund figure itself to be trustworthy rather than the reading — the station, whichever arm feeds it
    every arm on this page runs src/policy.py, and it moves the verdict from 10 to 54 on the paid arm alone

At a glanceHow the whole thing runs

84%all five correct pct
1,791 msp50, end to end
$0.00per 1,000 cancellation packet · google/gemini-3-flash

Run once, for real, on 2026-09-09. Every figure on these pages was captured from that run — nothing is written from intent.

14 steps, grouped by the question that sends you to them rather than by build order. Each tile carries the one figure that step is about, and opens the page behind it.

Should you use this?What you bring, where it stops, and when not to use it

Before you commit an afternoon to this, these are the answers that decide it. Each one is rendered from the record it lives in — and links the page that holds it in full.

What do I have to bring?Replace data/corpus/*.txt with your own packets in the same printed shape and data/contracts.json with your own product master, then rebuild the key by labelling the two readings and running tools/build_corpus.py. ⚠︎ WHAT STOPS BEING TRUE THE MOMENT YOU DO. Corpus lens →
When is this the wrong choice?Avoid: Paying per packet for a keyword list you could write in an afternoon. On the wordings its list carries, the free rule is right and the call adds nothing. That is the case against the best-fitting scenario (“Your evidence block's bad rows announce themselves — every transposition says "transposed", every duplicate says "duplicate"”). 5 scenarios scored in all, each with its own. Eval lens →
Where does it stop working?An evidence block that is not fixed-width columns. src/packet.py's row regex is the shape these packets print; a CSV, an administrator's API export or a scanned form needs a different parser and nothing above it changes. 6 recorded failure modes, each from a run rather than a guess. Corpus lens →
What was never verified?NO SECOND SCORED RUN. One was fired, so the run-to-run spread on this corpus is unknown and unclaimed. 8 items this kit says it could not check. Eval lens →
Can I run this on a model I control?Yes — any OpenAI-compatible endpoint, including one on your own hardware. The shipped adapter takes its host from BASE_URL and its model from MODEL, so nothing in src/ changes. The published figures come from 1 model on the fast tier, one provider, one key. Prompt lens →
And if it fits — what do I stand up?6 artifacts with a stated home and a stated egress, and 3 decisions each with what you provision past its ceiling — plus what was not measured. That is the next page, not this one. step 14 — Run it in your environment →

Not asked of this kit — 2 questions: clone (a fresh clone of this kit runs with nothing fetched); judge (nothing here is graded by a model).

Last verified 2026-09-09 — r001-cancel-refund. Every figure on these pages was captured from that run.

Run itHow this reaches your data

Every result on this page was produced by pure code over checked-in files, with no API key — which is why you can read the numbers before anyone spends anything.

Run this on your own data

  • The pipeline, its eval harness and the runs behind every numberdeployed inside your environment, on your own model endpoints, against your own documents.
  • The corpus above is the shape, not the limitit is a folder swap, and there is no database to migrate.

Talk to us →

Checked before this shipped — A clean checkout with no key configured renders the whole board, all three free floors, all six committed runs and every screenshot. python3 -m evals.check_labels and python3 tools/build_corpus.py --check both run on a machine with nothing installed — the kit is standard library only and requirements.txt is deliberately empty of packages.

A living map of modern AI — kept current every morning