Inspiration

Private equity "buy-and-build" firms grow by acquiring add-on companies, but every new deal is gated by the lender. The credit agreement caps leverage (debt / EBITDA - Earnings before interest, taxes, depreciation, and amortization), and the lender tests that cap on the combined books. Today a finance team merges those books by hand. They read the credit agreement, map every account into the platform's chart of accounts in a spreadsheet, adjust EBITDA, and build the compliance pack. One vendor playbook puts a full integration at roughly 90 days per company, and a single bad mapping silently flows into every number after it.

We wanted to build a real corner of The Hyphenated Enterprise: autonomous agents that do the repeatable finance work, with code doing the math and a human controller keeping the final say.

What it does

Stochastic reads the lender's credit agreement, merges an acquired company into the platform's books, flags only the lines that are genuinely unclear, and checks combined leverage against the lender's limit.

  • Reads the contract: drafts the covenant brief (limit, debt definition, exclusions, EBITDA add-backs and caps) from the credit agreement, with a verbatim quote and section for every field
  • Verifies every quote in code: checks that each quote appears word for word in the agreement and in the section it cites, and that each number appears in its quote, before the controller approves the brief
  • Maps every target line to the platform's chart, with a confidence score and a short reason
  • Flags only the unclear lines for a human, showing each option and its computed effect on the verdict
  • Combines platform and target in code, with a tie-out check that the parts add to the total
  • Tests leverage using the covenant's own definitions of debt and EBITDA, not the naive ones
  • Reviews the work with a separate agent, then a human controller approves (maker-checker)
  • Logs every action in a hash-chained audit log, where each row is linked to the one before it
  • Learns: the controller's answer becomes a policy, so the next run applies it instead of asking again

Kewaunee Scientific acquired Nu Aire in November 2024 for $55M, and we rebuilt it from public SEC filings. Kewaunee's "Financing liability" line mixes a sale-leaseback, a new PNC term loan and revolver, and 8% seller notes. Read as filed, the combined company looks over its 2.50x limit. Read the way the PNC credit agreement actually defines Senior Funded Bank Debt, it's 0.76x, comfortably under. That one flagged line flips the verdict, and Stochastic puts it in front of the controller with every option already computed.

How we built it

Four stateful agents, zero model-made numbers

  • Brief Reader (credit analyst): reads the credit agreement and drafts the covenant brief, pointing at a verbatim quote for every field. It never gets the whole agreement in one prompt. Instead it works from a section and definitions index and asks for more through read-only get_section and get_definition tools.
  • Mapper (staff accountant): maps lines, tags each as debt / not debt / unclear, and raises flags
  • Preparer (senior accountant): builds the combined table and leverage entirely through tool calls
  • Reviewer: returns pass or needs-attention with reasons, and can never edit the draft

The rule for the Brief Reader is the model points, code decides. A code verifier (not a model) runs every check field by field and reports it in the open: 22 checks on the demo deal, covering 17 quoted fields and 5 amounts. Exact-substring matching only, with no fuzzy matches, because fuzzy matching can accept "4.50" for "4.25" or drop a "not." Anything the schema can't express becomes a question for the controller, not a guess. The brief is approved and locked before mapping is allowed to start, and an approved brief is keyed to the agreement's hash so later add-ons under the same loan reuse it.

Agents never talk to each other. A code-based job runner moves work items between them, and a gateway is the only thing that can write. It refuses any number that no tool run produced, any quote that isn't in the agreement, any borrowed run id, and any agent trying to approve its own work.

Factory AI for our agents

Our agents run on Factory’s Droid Python SDK, with one resumed Droid session per role, a strict tool allowlist, and typed output. The Mapper, for example, returns typed JSON: a covenant flag with filing evidence and three readings, plus any mapping flags. Everything goes through a single run_agent(role, item) function with swappable backends (Droid, Anthropic API, scripted rules, and recorded replay), so the console always shows which backend produced each result. We default to Claude Haiku 5.5 through Droid with per-role model overrides. A live Sonnet 5.5 run on Droid read the full PNC credit agreement and passed every verifier check. A scripted fallback, held to the same gateway checks, keeps the stage demo safe if the network isn’t.

Rewinding an agent’s memory. Stateful agents have a catch: if the gateway refuses an answer, that wrong answer stays in the session and can shape every later turn. We use Droid’s Session.rewind to cut it out. Every turn records the id of the message that started it. When the gateway refuses the Mapper’s result, the job runner retries once. It reopens the role’s session, rewinds it to just before the refused turn, and runs the retry on the new branch with the gateway’s reason attached. The agent forgets its wrong answer but still learns why it was refused. Rewind forks the session into a successor with a new session id, and the store saves that id, so the next work item continues from the clean history. We tested this with a probe script: we rewound a session to before its second turn, and the successor recalled the first turn’s word (PELICAN) but not the second’s (HERON). Droid’s own session record had no trace of HERON. If a rewind fails, the retry still runs and the log says why.

Tiger Data: the record, the audit trail, and the agents' memory

Everything the gateway writes lives in Postgres on Tiger Cloud (PostgreSQL 18.6 with TimescaleDB 2.31). The schema is one hypertable, audit_log, plus three plain Postgres tables: record, id_counter, and ledger. Restart the server and everything loads back, including what the agents learned.

  • Audit log as a hypertable: audit_log is a TimescaleDB hypertable partitioned by time in 1-day chunks, and a trigger makes it append-only. Each row is one agent or human action (actor, action, result, JSONB evidence, prev_hash, hash), so the hash chain can be verified in plain SQL. Chunks older than a day turn into columnstore.
  • What-ifs in zero-copy forks: when a covenant flag opens, app/db/forks.py calls tiger service fork to spin up a copy-on-write fork of the ledger and computes each reading there. Ours was ready in 33 seconds with all three what-ifs, and the fork is deleted when the controller answers. The main ledger is never touched by a hypothetical.
  • Memory you can query: ledger_runs gives one row per run. With agent memory cleared, the Mapper asks 2 questions. With memory kept, it asks none and applies 6 learned policies, and that's visible in a single SQL query.
  • Search and analytics in the database: pg_textsearch BM25 ranks every line, mapping reason, flag, and policy, so searching for the seller notes surfaces the line first and the flag that asked about it second. A continuous aggregate (audit_activity) counts audit rows per minute by ledger, actor, and action in real time, and TimescaleDB Toolkit's stats_agg and percentile_agg summarize run times across posted runs, including the time the controller took to answer.
  • Visible in the product: the console's status bar reads /health, so the screen shows the record is in Postgres on Tiger Cloud and every write is saved.

Figma: idea to demo

Figma Video and workflow below (Try it out links). Figma wasn't just where we drew screens. It was the source of truth the code was generated from. We ran a loop we call GRID:

  • G · Gather: Claude researched the prompt and wrote an HTML render, and the Figma agent turned it into gray-box frames in Figma Design (real copy, no color). We settled problem, product, and proof before any screen.
  • R · Rough out: FigJam mapped the flow between gray-box wireframes, so we wireframed strictly before styling anything.
  • I · Iterate: Figma Make and the Figma agent built the hi-fi v3 mockup with working code on variables and text styles, reviewed against real UI studies. The 3D "Agent House" on the Agents screen came from three.js code layers and Blender models. We tried 38 house code layers and kept one.
  • D · Deliver: subagents fanned the build out across layers. Claude Code read each v3 frame through the Figma MCP, copied tokens by name into our Tailwind @theme block, and wrote one route per v3 screen. Dev Mode and accessibility plugins checked every screen. When a frame changed, we re-read it instead of patching by hand.

Every dead end stays in the Figma file as a sibling frame, including the original all-in-one platform, the "paper and ink" screens, and three discarded visual directions. The file also has a Technologies Used page with screenshots and captions for every Tiger Data and Factory feature we touched, so the design file documents the whole build, not just the UI.

Stack

Next.js, React, TypeScript, and Tailwind for the console. Python and FastAPI for the gateway, tools, and verifier. Postgres + TimescaleDB on Tiger Cloud for the record, audit log, forks, and agent memory. A Playwright end-to-end suite covers the whole demo path.

Challenges we ran into

  • Scope. We started as "one platform for every corporate-finance special situation" and cut it down to one workflow done properly.
  • Contracts are long; models skim. A credit agreement runs 240K–690K characters, but the relevant clauses are only about 8K–30K. Indexing definitions and sections, and only handing the reader what it asks for, made the reads accurate and checkable. Our first live reads were refused by the gateway before we tightened the quoting rules, which was the gateway doing its job.
  • Hypotheticals polluting the ledger. Computing three readings of one line in the real ledger would have mixed hypotheticals into the record. Zero-copy forks on Tiger Cloud fixed that cleanly.
  • Design-to-code drift. Radius 12 in Figma became 16 in code. Naming every token and reading frames through the MCP is how we stopped values from drifting.
  • Real filings are messy. Public filings give statement lines, not trial balances, and the two companies' fiscal years differ, so we were explicit about what's verified and what's inferred.

Accomplishments that we're proud of

  • The deal runs end to end on real filing figures, from credit agreement to approved verdict, backed by 300+ passing backend tests
  • The Brief Reader cites the contract, and code verifies every citation before a human ever sees it
  • The flagged line genuinely changes the lender verdict, and every option's effect is computed by tools rather than guessed by a model
  • An audit log that refuses edits, chains every row by hash, and survives a restart
  • Run it a second time and the Mapper asks nothing, because the controller's answer became policy, and you can prove it with one SQL query
  • The Figma file is a working design system, not a mood board: the shipped console is read straight from it

What we learned

LLMs are great at judgment calls like "which clause defines debt?" or "which account does this line belong to?", and dangerous at arithmetic. Splitting those jobs (agents point, code verifies and computes, a human approves) is what makes an autonomous finance system trustworthy. Putting the record, audit trail, and memory in one database made "trust" queryable instead of a claim. On the design side, treating Figma as the source the code reads from, rather than a picture the code imitates, saved us hours of hand-patching.

What's next for Stochastic

  • Covenants as data: multiple covenants per agreement, step-down schedules, pro forma acquired EBITDA, and percent-of-EBITDA caps, so the Brief Reader handles sponsor-style agreements, not just simple bank loans
  • An answer key of hand-written briefs across more real agreements to score the reader field by field
  • Purchase accounting and intercompany eliminations
  • A second deal, so agent memory carries across companies and not just across re-runs

Built With

Share this project:

Updates

Submission history