Home › Use Cases › Refresh a patron KYC file from resubmitted documents and route exceptions to a person
Use caseUC0291
🧪 Use-case kit · runnable

Refresh a patron KYC file from resubmitted documents and route exceptions to a person

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

A casino's compliance team refreshes a patron's KYC file on a cycle. The patron sends in a bundle — an identification document, a utility statement, an employer letter, and usually a cover sheet on which they restate all their own particulars — and somebody reads all of it to fill six fields in the file. Today that is a person opening four documents side by side and deciding, field by field, which one actually says the thing. Reading four resubmitted documents side by side to fill six fields of a patron file, and deciding for each field which document actually states it — for every refresh, every cycle. It replaces the reading and the sourcing. It replaces no decision: nothing here verifies an identity, accepts or rejects a document, or approves a patron.

Audience

A KYC analyst working a refresh queue, and the BSA officer who owns the exceptions that reach them. The decision in front of them is not "is this person who they say" — nothing here answers that — it is "can this file be filled from what was sent, and if not, who has to look at it". Every number on these pages came from one real run of this code, not from a vendor page.

The inputThe actual patron KYC file refresh packets

The corpus is 60 patron KYC file refresh packets, 0.13 MB (txt 60). It is generated because it has to be. A patron KYC file is the single most sensitive record a casino holds, and a real refresh packet is somebody's passport, somebody's home address and somebody's employer. There was never a real file to reduce, redact or ask permission for — so the corpus is built from invented people, and the thing it is built to TEST is a structural property that does not depend on the people at all: which document in a bundle actually states a field, and what follows when the only one that does is the patron's own cover sheet.

The corpus

  • The 60 patron KYC file refresh packetsgenerated from a fixed seed, so no real record, person or institution appears in it.
  • Where each came fromNowhere — every one of the 60 packets, the patron register, KYCR-2026 and the whole answer key are generated in-process. tools/build_corpus.py --check rebuilds all of it in memory and diffs it against disk byte for byte, and it was run under two different PYTHONHASHSEEDs with 0 differences, which is what makes 'deterministic' a measurement rather than a claim.

Swap this folder for your own material and the kit is pointed at your patron KYC file refresh packets. That is the whole change — there is no database to migrate.

One patron KYC file refresh packet, as the model receives itPKR-0001.txt · 1 of 60
PATRON KYC FILE REFRESH PACKET  PKR-0001
Packet date: 2026-09-02
Procedure: KYCR-2026  (see data/policy.md -- INVENTED, cites nothing real)

This packet is the set of documents a patron resubmitted for a periodic file
refresh. No document below is marked as the source of any field, and no total,
finding or exception is struck anywhere on it.

------------------------------------------------------------------------------
D1  National identity card, issued copy
Document date: 2026-05-20
    Document reference: NI-621704
    Document class as printed: National identity card
    Name as printed: Dmitri Emrys Nethercott
    Date of birth as printed: 1977-08-01
    Issued by: Ardenholt Registry Office (copy taken at the cage desk)

------------------------------------------------------------------------------
D2  Utility account statement
Document date: 2026-02-08
    Account reference: UT-2991905
    Statement period: 2026-01-09 to 2026-02-08
    Account in the name of: Dmitri Emrys Nethercott
    Service address: 66 Inglewood Terrace, Fairbarrow FB3 6P
    Balance carried forward: settled in full

------------------------------------------------------------------------------
D3  Employer confirmation letter
Document date: 2025-11-27
    Letter reference: EC-652838
    Issued by: Kelburn Orthopaedic Partnership
    Employee date of birth: 1977-08-01
    Correspondence address we hold: 66 Inglewood Terrace, Fairbarrow FB3 6P
    Job title on the payroll record: Retired further education lecturer
    Category of the earnings paid: pension

------------------------------------------------------------------------------
D4  Patron declaration (completed by the patron)
Document date: 2026-09-02
    Completed and signed by the patron on 2026-09-02

Abridged — the file continues.

The outcomeWhat a good result looks like

One packet in, thirteen graded cells out: six file values, the document label each one came from, and the one verification exception that stops the file being filled cleanly — with a line copied verbatim out of the packet establishing it, and the human lane computed in code.

And when it cannot

And what it does when it cannot. On 7 of 60 packets something was wrong. Four of them are KY-7, the refresh window, which THE MODEL CANNOT ANSWER because the window length depends on the patron's tier and the tier is in the register, which is deliberately never sent — that is an asymmetry in this harness, not a model failure, and the free floor's 4 of 4 there is the tier and not skill. Two are a gap in KYCR-2026 itself, found by the model: where a field's only independent source states it UNREADABLY and the declaration restates it, the rulebook orders KY-1 before KY-3 and never says whether an unreadable statement still counts as "stated". One is a packet where it named the exception correctly, routed correctly, and then recorded a value as null rather than as the declaration's own text.

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 documents are fixed-layout machine exports with labelled fields — the free rules floor alone — evals/baseline.py
    355 of 360 field values and 355 of 360 source labels exact for $0.00, and 50 of 60 packets fully right. A label parser is the correct tool for a labelled document and this kit measured that rather than assuming it.
  • Your documents are LETTERS — a fact stated in a sentence rather than beside a label — the paid call
    5 of 5 on the prose family against the floor's 0. This is half of everything the money bought on this corpus, and it is the one place the gap is not arguable.
  • A value has more than one written form — a date, a name, an address — the paid call
    5 of 5 on the long-form-date family against the floor's 0, and 0 false conflicts against the floor's 5. A string comparison reads 7 June 1957 and 1957-06-07 as a date-of-birth conflict and raises a verification exception on a clean file.

And where nothing here is good enough:

  • The rule depends on a fact the packet does not contain — neither arm — change the harness
    KY-7 needs the patron's tier and the tier is in the register. The model gets 0 of 4 because it cannot see it; the floor gets 4 of 4 because it is handed it. That is not a comparison, and this kit publishes it as a defect rather than as a result.

At a glanceHow the whole thing runs

53%packet all correct
84,814 msp50, end to end
$37.85per 1,000 patron KYC file refresh packets · Gemini 3 Flash

Run once, for real, on 2026-09-02. 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 and data/patrons.json with your own register rows (id, tier, review_state, refresh_state, packet_date, documents, has_declaration), then write data/gold.jsonl in the same shape. ⚠︎ WHAT STOPS BEING TRUE THE MOMENT YOU DO. Corpus lens →
When is this the wrong choice?Avoid: Paying for parsing. It is most of the work in this job and, on labelled documents, none of the value. That is the case against the best-fitting scenario (“Your documents are fixed-layout machine exports with labelled fields”). 4 scenarios scored in all, each with its own. Eval lens →
Where does it stop working?A packet longer than about 2,500 bytes. The longest here is 2482 and nothing tests a forty-page scanned correspondence file. 8 recorded failure modes, each from a run rather than a guess. Corpus lens →
What was never verified?Run-to-run variance. One paid run, one corpus, one model, one prompt, one day. 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?8 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-02 — r001-patron-refresh. 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 on 127.0.0.1:9291 and scores both free floors and the stub end to end, because all four committed result files ship in the repo. The 60 packets, the register, the key with its character offsets, KYCR-2026 and every grader are in the tree; nothing has to be downloaded and nothing has to be paid for.

A living map of modern AI — kept current every morning