Home › Use Cases › Reconcile a rental invoice line against its on-hire and off-hire records
Use caseUC0460
🧪 Use-case kit · runnable

Reconcile a rental invoice line against its on-hire and off-hire records

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

An oilfield operator hires pumps, tanks, generators and vacuum trucks by the day, and every month a vendor bills for them. Before the rentals desk can pass a line it has to agree with three other records: the hire tickets that say when each unit went on and off hire, the daily field reports that say what the unit was doing, and the day-rate schedule that says what each day costs. The records disagree constantly and for ordinary reasons — a collection ticket raised against the wrong unit, a lift booked and never worked, a delivery refused at the gate and accepted two days later, an amendment countersigned after the days it prices. Each of those lives in a SENTENCE on the pack, not in a column, and the desk's own three-check status line disagrees with the procedure on 30 of these 64 packs. Opening one rental invoice pack, matching every line to its hire ticket, reading every event against the AS AT date, reading each note to see whether a movement was voided, refused, worked or entered against another unit, then recomputing the allowed days and the rate in force line by line.

Audience

The rentals desk and the cost controller working a period's equipment invoices before the payment run, and the engineer deciding whether a model call is worth buying for this shape of work at all. On this kit the honest answer to the second question is a qualified no, and the page leads with it. Every number on these pages came from one real run of this code, not from a vendor page.

The inputThe actual rental invoice packs

The corpus is 64 rental invoice packs, 0.28 MB (txt 64). It is generated because it has to be. A real equipment rental book is an operator's own commercial record: named vendors, day rates under a negotiated agreement, unit serials, field locations and a desk's own notes about who was at fault. None of that can be published, and a redacted version would delete the sentences the whole kit turns on. Generating it buys the one thing a public measurement needs — a key derived from the generator's own structure rather than typed by a person — and costs the thing section 3 of data/SOURCES.md spends a page on: the packs are too regular, and a regex written from the generator's own 21 template sentences scores 62 of 64.

The corpus

  • The 64 rental invoice packsgenerated from a fixed seed, so no real record, person or institution appears in it.
  • Where each came fromdata/SOURCES.md states where every byte came from AND what the generator costs the measurement. Every vendor, unit, location and agreement is invented; no real operator, field, vendor or regulator is named anywhere, and RHR-2026 itself is an invented in-house procedure written so this kit has one stated, ordered rulebook to apply. Section 2 carries the free-floor table measured BEFORE the first call was bought, section 3 lists what is still too regular for a forker to trust, and section 4 names the seven cases the model was expected to fail — written down in advance so the result could contradict them, and one of them did.

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

One rental invoice pack, as the model receives itRHI-0001.txt · 1 of 64
================================================================================
EQUIPMENT RENTAL INVOICE RECONCILIATION -- ONE INVOICE, ONE AS-AT DATE
================================================================================
FILE              RHI-0001
RENTAL VENDOR     V-6107  Pennerton Hire Group
HIRE AGREEMENT    HA-8300
LOCATION          PAD-4200  an aggregate quarry bench
INVOICE           RI-26100
INVOICE DATE      2026-07-01
BILLING PERIOD    2026-06-01 to 2026-06-30
AS AT             2026-07-04
PACK COMPILED     2026-07-09
PAYMENT TERMS     net 60 days from receipt
CURRENCY          USD

-- CONTRACTED DAY-RATE SCHEDULE (schedule C and every amendment printed on it) ----
ROW         UNIT CLASS              CLASS      RATE/DAY  EFFECTIVE   SOURCE
RS-8300-1   LIGHT TOWER             OPERATING     210.00  2026-01-01  HA-8300 schedule C
RS-8300-2   LIGHT TOWER             STANDBY       105.00  2026-01-01  HA-8300 schedule C
RS-8300-3   VACUUM TRUCK            OPERATING     760.00  2026-01-01  HA-8300 schedule C
RS-8300-4   VACUUM TRUCK            STANDBY       380.00  2026-01-01  HA-8300 schedule C
RS-8300-5   GENERATOR SET           OPERATING     520.00  2026-01-01  HA-8300 schedule C
RS-8300-6   GENERATOR SET           STANDBY       260.00  2026-01-01  HA-8300 schedule C

-- HIRE TICKET EVENTS (as exported 2026-07-02 from the hire register) --------
EVENT     TICKET    UNIT      UNIT CLASS              TYPE          DATE        RECORDED
EV-0021   HT-4400   EQ-3100   LIGHT TOWER             DELIVERED     2026-06-03  2026-06-04
EV-0023   HT-4401   EQ-3101   VACUUM TRUCK            DELIVERED     2026-06-03  2026-06-05
EV-0024   HT-4401   EQ-3101   VACUUM TRUCK            COLLECTED     2026-06-28  2026-06-28

Abridged — the file continues.

The outcomeWhat a good result looks like

One invoice pack in, one row out: which hire-ticket events are in force at the AS AT date, which billed lines were standby days, which schedule rows were executed — and then, derived in code from those three sets, every line's allowed days, the rate in force, the supported amount, the exception set and one RHR-2026 verdict from DOUBLE-HIRE, OFF-HIRE-OVERRUN, PRE-DELIVERY, RATE-MISAPPLIED, OPEN-HIRE or RECONCILED, each naming the ticket behind it.

And when it cannot

And what it does when it cannot. On the scored run 64 of 64 replies parsed, 0 stopped at the 1,000-token ceiling, 0 were closed by the reply stop and no call failed. The 42 packs it got something wrong on are named in the taxonomy below, and the largest class was predicted in writing before any call was bought. Two packs are unanswerable by construction — the corpus prints two schedule rows under one id — and every arm, paid and free, is capped at 62 of 64 by them.

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 hire-ticket system records voids, refusals, worked lifts and wrong-unit entries as a STATUS column, and your schedule prints only executed amendments — free code, and do not buy a call at all
    On this corpus the domain-written floor already beats the paid call 23 to 22, and the columns-read-literally arm reaches 14 with no rules at all. Every figure — allowed days, the rate in force, the supported amount, the exceptions and the verdict — is integer arithmetic you already own.
  • Your voids, refusals and wrong-unit entries arrive as SENTENCES — a yard note, a field report pasted into the pack — the paid call, with the free floor beside it and both published
    This is the only place the call is ahead: the events reading is 38 of 64 against the floor's 31 and the columns arm's 20, and the two families nothing in the columns can decide are the two it wins — a collection entered against another unit (2 of 4 against 0) and a pack where the desk's own status line is wrong (14 of 30 against 13).
  • You need the standby classification right above all — a pad that shuts in often — free code, unambiguously
    Every free floor scores 64 of 64 on the standby reading and the paid arm 48. The 16 misses are all one shape: a note whose window covers part of a line, reclassed as though it covered the whole.
  • You want an exception raised rather than missed — a query is cheap, a silent overrun is not — the paid call, and read the two directions apart
    The paid arm reconciles where the key raises an exception on 2 packs of 64; the floor of record does it on 4. It errs the other way instead — 4 exceptions where the key reconciles, against the floor's 1.
  • You are deciding whether this SHAPE of work is worth a model call at all — read the floor table before the headline
    A fully specified rulebook applied to text drawn from a fixed sentence library is regex-solvable by anybody holding the library: the tuned arm reaches 62 of 64 for $0.00. That is a property of a generated corpus, not of this problem — and it is why the honest comparison is 22 against 23 and not 22 against 0.

At a glanceHow the whole thing runs

34%all correct pct
1,723 msp50, end to end
$2.92per 1,000 rental invoice packs · the fast tier

Run once, for real, on 2026-09-14. 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 rental invoice packs in the same six-block shape and data/hires.json with your own register of invoice identities, AS AT dates and declared idle grace, then run python3 -m evals.run --run-id b000-<yours>-modal --floor modal for a free reading and python3 -m evals.check_labels to re-derive the key independently. ⚠︎ WHAT STOPS BEING TRUE THE MOMENT YOU DO. Corpus lens →
When is this the wrong choice?Avoid: Paying per invoice for date arithmetic. That is the case against the best-fitting scenario (“Your hire-ticket system records voids, refusals, worked lifts and wrong-unit entries as a STATUS column, and your schedule prints only executed amendments”). 5 scenarios scored in all, each with its own. Eval lens →
Where does it stop working?An invoice line that cites no hire ticket. The whole reduction is the line-to-ticket-to-event join; a line citing nothing — a mobilisation charge, a lump sum, a damage recharge — has no window to test and this kit does not model it. 7 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 no confidence interval is claimed anywhere on this page. 9 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?5 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-14 — r001-rental-onhire. 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 four free floors and every committed run: Ask the model is disabled and says why, while the recorded arms replay from results/. requirements.txt installs nothing — the kit is standard library only. Measured during the screenshot shoot, on a second board started with the key blanked in its own environment: nine frames, 0 console errors, no call made by any route and nothing spent.

A living map of modern AI — kept current every morning