Finance Operations Platform
A local, $0 platform for the keying and ticking finance teams do by hand. It spreads financial statements, reconciles bank against ledger, matches invoices to orders and receipts, gates vendor master data and scans spreadsheets for formula risks. Every engine checks its own work under one set of controls, and sends a person only what it is unsure of.
For a businessFinance staff stop re-keying and re-ticking: the routine work is done and checked, and their time goes to the items it flags, each with the evidence to resolve it.
- Status
- Personal build · tested prototype
- Result
- 1.5% of the statement values it accepted without review were wrong on 30 held-out SEC 10-Ks; 0 of 37 problem invoices reached approval without a person
- How it was measured
- Answer keys from the filings’ own XBRL tags, held-out filings, blind-written test sets and a release gate in CI
- Biggest limit
- No company, users or real documents yet; four of the five engines are measured on synthetic data
01 · Context The problem
Finance teams still key statements into spreadsheets, tick bank lines against the ledger and check invoices against orders by hand. Each job is mostly routine and occasionally critical: a footing error, a duplicate invoice or a changed bank account is exactly what the routine hides.
Handing it to a model alone is the wrong fix: a model that reads numbers can misread them silently. The real question is which values can be trusted without a person, and how you would know.
02 · Pipeline How it works
- Read with an address
Readers for HTML, PDF and Excel (and optional OCR) turn a document into positioned text, and every token keeps its source: an xpath, a page and box, or a sheet cell.
- Structure by arithmetic
Statements, periods and units are detected from the document itself. Totals are found by checking which lines add up, not by trusting labels, and amounts are exact decimals throughout.
- Map with a cascade
Each line is mapped to a standard template by memory, company memory, aliases, section and structure, and only then by an optional small local model, which picks from a closed list and never sees the numbers.
- Check like an accountant
Accounting tie-outs run on every spread (19 for a commercial company), with 18 credit ratios and any covenant limits a lender sets. A failed check is a blocker, and approval is refused while one is open.
- Trust only what was measured
One policy across all five engines: a rule acts on its own only if the lower bound of its measured accuracy clears a bar (98% for reconciliation, 80–90% for statement lines). A rule never measured goes to a person.
- A person decides the rest
Everything uncertain lands in a review queue for a signed-in person with the right role. Their decisions go into a hash-chained audit log and into the calibration that decides what to trust next time.
03 · The product What a reviewer sees



04 · Results Five engines, one set of controls
| Engine | What it does | Measured on | Result |
|---|---|---|---|
| Statements | Statements in, a traceable spread out | 30 SEC 10-Ks held back from tuning | 1.5% of the values it accepted without review were wrong (32 of 2,154); 81.6% of all values right; 37% of values go to review |
| Reconciliation | Bank against ledger, layered matching | Synthetic sets, 10 seeds each | 0 false automatic matches on clean sets (0 of 2,130), 1 of 2,114 on a round-amount stress set, 2 of 2,317 on a messy set; a greedy matcher made 3 of 233 on stress |
| Invoices | Extraction, arithmetic, three-way match, fraud controls | 8 synthetic sets, including unseen layouts | 0 of 37 problem invoices reached “eligible” without a person; unseen layouts fail closed |
| Data quality | Vendor and order exports checked before loading | A set written blind by separate agents, first scoring | 60 of 63 vendor faults caught, 0 of 44 valid rows held; 34 of 35 order-line faults, 1 of 42 held |
| Spreadsheets | Formula errors and risky changes between versions | Two blind-written sets, first scoring | 91.3% and 84.0% of its claims right, finding 42 of 43 and 39 of 39 planted issues; a naive checker made 685 and 818 claims |
Silent error: a wrong value or decision that would go out with no person looking. Only the statement engine is measured on real (public) filings; the other four on synthetic data.
05 · Choices Design decisions
The model never sees a number
The optional local model only chooses which template line a label means, from a closed list. Every amount is read, scaled and checked by code.
Refuse rather than guess
An ambiguous number, unit or pairing goes to a person. A sum found without a shared reference is only ever proposed.
Confidence is measured, not stated
Auto-acceptance uses the lower bound of a rule’s measured accuracy against a bar, so a rule that was right once out of once does not qualify.
Corrections are events
Nothing is edited in place: corrections, approvals and resolutions are appended to a hash-chained log, and a record can be erased without breaking the chain.
Eligibility is the ceiling
The invoice engine can mark an invoice eligible for approval but never approves or pays it. A changed bank account is a blocker that needs a call-back note from a different person.
A gate on every release
Versioned, hashed evaluation sets run in CI. A release fails on any rise above the confidence bound, or on any single item that turns from right to wrong.
06 · Evidence How I tested it
Statement spreading is scored against answer keys built from each filing’s own XBRL tags, independently of the pipeline: 33 SEC 10-Ks to tune on and 30 held back. Every change is logged with its measured effect. The first run on the tuning set was 60.8% right with 7.1% silent errors; it is now 81.6% and 2.3%, and two changes were reverted because silent errors rose.
The other four engines are measured on synthetic data I generated, so the data-quality and spreadsheet sets were also written blind, by separate agents that never saw the code, and my evaluation harness re-scores the results independently.
More than 1,200 automated tests: about 570 on the engines, the API and security, the other 700 checking the synthetic data generator. Red-team rounds by separate agents found about 30 problems, each fixed with a regression test. CI runs the tests, the evaluation manifests, the release gate and a 14-step smoke check on every push.
Built with Claude Code, with every design decision written down with its reason and its cost: 53 so far.
07 · Outcomes What it has done so far
- Runs locally at $0, with a web app for each engine, sign-in with roles, encrypted uploads, retention and legal holds, backups, and a learning-period mode in which the local model gives a hidden second opinion for a senior to rule on.
08 · Security Untrusted input and what the model can touch
- Every API route needs a named permission; a route without one is refused, and cross-site writes are blocked.
- Seven roles, with separation of duties available as a setting; an administrator can never approve.
- Uploads are encrypted at rest; bank details are compared as keyed hashes and only ever shown masked.
- Sign-in is OIDC with PKCE, and the development sign-in refuses to run anywhere but a local machine.
09 · Honest notes Known limits
- A tested prototype, not a production system: one machine, sign-in tested only against a local test identity provider, and no company, users or real documents yet.
- Only the statement engine is measured on real (public) filings; the other four are measured on synthetic data.
- The low silent error rate is paid for in review: about 37% of statement values, a median of 24 items per filing.
- Banks and insurers are far weaker (59–69% right), IFRS is only smoke-tested, and OCR has not been measured on a real scan.
- The held-back filings were used to confirm two decisions, so a fresh set of filings would be a cleaner test.
- Time saved has not been measured: there are no timed analyst sessions yet.
Want to see this one run?
I'll screen-share the workflow, the test set and the results, including what didn't work.