Writing

LLM proposes, structure verifies

There's a popular class of self-hosted tools right now: upload your documents, a model extracts the fields, the fields get saved. It feels like magic until you notice what never happened — nothing ever checked the extraction. We built a tax pipeline on the opposite architecture, and the difference shows up exactly where it hurts: the numbers you file.

The pattern everyone ships

The extract-and-trust pattern is easy to build and demos beautifully: the model reads a PDF, returns structured fields, and the fields go straight into your database. The extraction is the record. When the model reads a wage box correctly, you saved twenty minutes. When it hallucinates one digit, you saved twenty minutes and filed a wrong number — and nothing in the system can tell you which of those two things happened, because the output was trusted the moment it appeared.

That's not an LLM problem. It's an architecture problem: the proposer and the record-keeper are the same step. Any extractor — a model, a regex, a person having a bad day — will sometimes be wrong. The question is whether the system is built to notice.

The inversion: every proposal is untrusted

Our reference implementation is a household tax workspace — a real one, filing real returns. Its extraction seam is built onTraverse, and Traverse's contract is blunt: an extraction is a proposal, and a proposal must carry a verbatim excerpt of the source text it claims to come from. The pipeline checks that the excerpt actually occurs in the document. If it doesn't — because the extractor paraphrased, summarized, or invented — the proposal is dropped. Surviving proposals don't even get to keep their own source locations: the pipeline recomputes every locator from the verified match, so "where this fact came from" is never taken on the extractor's word.

Note what the contract doesn't care about: who proposed.A hallucination-prone model and a deterministic parser go through the same check, because the check costs the same either way and the failure it prevents is identical.

Four states, one direction

Past extraction, every fact moves through an explicit spine:extracted → resolved → verified — three separate stores, not a status column someone remembers to update. Extracted facts carry their excerpt, locator, confidence, and rationale. Resolution collapses competing candidates into one winner by a deterministic precedence order — a user's correction beats a corrected form, which beats the original document — and keeps every losing candidate on the record. Verification is the gate: a fact that's medium-confidence, or OCR-derived, or has more than one candidate, is held with a recorded reason until a person resolves it.

The part that makes it structural rather than aspirational: the tax math can only read verified facts. Withholding analysis, the return package, year-over-year diffs — all of it queries the verified store and nothing else. An unverified fact isn't flagged for caution; it's invisible to the engine. And the gate swings both ways: when a new document makes an old fact ambiguous, the old fact is demoted — pulled back out of the verified store, with both candidates preserved — rather than left to coast on a stale approval.

Two moments where it mattered

The corrected W-2. A W-2 comes in and its six wage fields extract cleanly — verbatim excerpts verified, single candidates, promoted. Later a corrected W-2 arrives from the same employer. In the extract-and-trust pattern the new numbers would overwrite the old, or worse, coexist silently. Here, both versions become candidates for the same fact keys, promotion is blocked, and the pipeline refuses to verify until a person picks — recording the choice, and keeping the losing original visible as asuperseded claim instead of deleting it.

The duplicate interest statement. Two interest statements from the same bank, different account labels, different amounts, land on the same fact key. Resolution holds one conflicting fact at the gate with both candidates preserved; the year's readiness flips to needs-review; and exactly zero of those dollars reach the tax engine until the conflict is explicitly resolved. The failure mode of the trusting pattern — two numbers, one silently wins — is structurally unrepresentable.

What we're not claiming

Honesty is the product, so: the reference implementation's production extractors are currently deterministic parsers, not LLMs — the seam is LLM-shaped and hallucination-proofed, and a model provider plugs into the same verified-excerpt contract, but today's facts come from regex that gets the same distrust. Only some document paths run the full Traverse machinery; the rest use direct parsers whose provenance is recorded but not excerpt-verified. High-confidence, single-candidate facts auto-promote as system-verified — the gate holds the ambiguous residue for humans, it doesn't make a person stare at every number. And the gate checks structure and provenance, not arithmetic: nothing in it recomputes your return against an oracle. One household, federal plus one state, sixteen document types. A reference implementation, not a product.

The receipts

Because every stage is Survey-shaped on the way in and Surface-shaped on the way out, the pipeline's whole trust state exports as one inspectable record: documents, extractions, resolutions, verified and superseded claims, each with provenance and status. The same architecture this site publishes for its own deliveries — recomputable receipts — applied to a domain where a wrong number has a filing deadline. The implementation is a private household repo (it holds real financial records), so this story describes its mechanics rather than linking its internals; the products it composes are public:Survey for extraction review,Traverse for verified extraction, Surface for the trust state itself.

If you're building on the extract-and-trust pattern, the upgrade path isn't a better model. It's a gate the model can't talk its way past.