Devin Huber
Résumé
Python · FastAPI · React · personal build

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.

Built for
Credit analysts, accounts payable, treasury, FP&A (personal build)
Model
Rules first; an optional small local model maps labels and never sees a number
Data
SQLite · encrypted uploads · hash-chained audit log
Output
Spreads, reconciliations, eligible invoices, clean master data, scan reports, each approved by a named person
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
1.5%silent error rate on 30 held-out SEC 10-Ks
0 of 37problem invoices reached approval without a person
1,200+tests, plus a release gate in CI

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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

Statement review workspace: an income statement spread with a marker on every value, and a review queue on the right listing two blocking failed checks.
Statement review. Every value is marked by how it was produced (extracted, calculated, mapped, assumed) and opens its source. Two failed footing checks block approval until a person resolves them with a note.
Reconciliation run: 43 matched by rules, 12 proposed, 25 open exceptions, each explaining why no pairing was accepted automatically.
Reconciliation. 43 lines matched by measured rules; where two pairings are equally good, none is accepted and the best one is only proposed, with the reason written out.
Overview page: what needs a person today across statements, reconciliation, invoices, data quality and spreadsheets.
One queue across five engines. The home screen is what needs a person today. Screens are from a local run on synthetic and public data.

04 · Results Five engines, one set of controls

EngineWhat it doesMeasured onResult
StatementsStatements in, a traceable spread out30 SEC 10-Ks held back from tuning1.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
ReconciliationBank against ledger, layered matchingSynthetic sets, 10 seeds each0 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
InvoicesExtraction, arithmetic, three-way match, fraud controls8 synthetic sets, including unseen layouts0 of 37 problem invoices reached “eligible” without a person; unseen layouts fail closed
Data qualityVendor and order exports checked before loadingA set written blind by separate agents, first scoring60 of 63 vendor faults caught, 0 of 44 valid rows held; 34 of 35 order-line faults, 1 of 42 held
SpreadsheetsFormula errors and risky changes between versionsTwo blind-written sets, first scoring91.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.

Get in touch