CoverageCheck

The build

How it is built

The whole design in one sentence: models read pages, code does the arithmetic, and every value carries the page it came from.

The shape

Six steps. The first three interpret and the last three execute, and the boundary between them is the load bearing wall of the build.

ingest → classify → extract → resolve → assemble → check

Ingest takes the PDFs. Classify names what each document is, from a closed list. Extract reads the declarations and schedules, and it is the only step that reads whole documents. Resolve turns documents into policies, because a policy arrives split across files or several to a file. Assemble stacks the policies into the tower. Check answers the configured questions against what was read.

Only classify and extract touch a model. Everything after them is ordinary code: deterministic, unit tested, and the same on every run over the same documents.

The division of labor

No model does arithmetic on limits anywhere in this codebase. A model is good at reading a scanned declarations page and bad at being audited. Code is the reverse. So the model produces values with sources, and the code computes the tower, finds the gaps, and answers the questions. When a number is wrong, the citation says where it was read from, and when a computation is wrong, a unit test can prove it.

The citation contract

Every extracted value carries three things: the document it came from, the page it was read on, and the verbatim text as printed. A value without a source cannot be constructed, because the schema will not admit it. Values the system computes are derived, not cited, and the distinction is kept because it decides what can be challenged and how.

What a policy turned out to be

A policy is a packet of declarations, coverage forms, and endorsements, and the packet does not respect file boundaries. One corpus file holds four policies for one association. Another program arrives as two files joined only by a policy number printed in a schedule. So documents resolve into policies before policies resolve into a program, and routing is by policy number only, never by name, because a wrong merge between two insureds is not recoverable.

The join key

Every excess and umbrella policy prints a schedule of underlying insurance naming what should sit beneath it: carrier, policy number, limits, term. That schedule is what joins separate files into one tower, and it is what turns a missing document from something nobody noticed into something the system can name. In the corpus, the Fox Run excess names a Scottsdale general liability policy that is not in the file, with its limits and term, on page 13.

The pause rule

When classification cannot place a document, the system re-reads once, narrowly. If the second reading also fails, the run pauses and asks a person a specific question rather than guessing. Both outcomes feed the record: a supplied value is logged against who supplied it, an exclusion is logged in the scope. Extraction never runs on a document whose identity is in doubt, because triage costs pennies and a full read costs dollars.

Validation lives in code

The reading is validated by the same schema whether it came from the live model or a test fixture. Grammar constrained decoding was tried first and could not hold the full cited schema, so the schema travels in the prompt and the response is validated on return. A reading that fails validation gets one correction pass with the specific error, then the run raises rather than looping. The full record is decision 0003.

The page budget

The API accepts one hundred pages per document. Corpus files run to three hundred and twenty nine. The reader is given a front slice of each document, and the run reports where the cut fell rather than hiding it. The cut is a real limitation of the current build and is stated as one.

The preflight

Every live failure this project has hit was a property of the request, not of any document, and every one was rejected by the API before a page was read. The preflight sends the smallest useful request per task, checks the response against the schema, and reports seconds and cost. It runs before any money is spent on a full reading.

The accuracy figure

Accuracy is scored against a manifest of human verified values, one entry per corpus document. Some labels are still pending a human read, and scores are computed against verified labels only. Until every label in a claim is verified, the accuracy figure stays redacted. Quoting a number scored against unverified ground truth would be the exact failure this system exists to prevent.

Known limitations

Form and endorsement interpretation is not built: the forms schedule is read for presence checks, but what a form does to coverage is not interpreted. Loss run ingestion is not built. Persistence is not built; reviews live in memory behind a store interface. Scanned documents without a text layer are in the corpus but not yet exercised. The five question set is configuration awaiting delivery, and the question engine refuses questions that need fields the system does not extract.

What production integration would take

The model boundary is one file, so swapping vendors or models is contained. The store interface is one file, so persistence is an implementation, not a refactor. The reading runs inside one request today, which holds for a demo and does not hold for a queue; production wants one reading per job with the review assembled from stored readings. The pause rule becomes a work queue with an audit trail. None of this requires reworking the engine, which was the point of the boundaries.

Verification for every claim on this page: the test suite, the typecheck, the lint, and the production build run clean, and the repository is the record.