Caliber: the AP clerk that can't pay itself

Code decides. The investigator explains. The controller approves. The ledger refuses everything else.

Caliber: the controller's Today screen with hold 2231 and its question

Read the technical note and CarrierBench results: caliber.compare/caliber.pdf

Talk to it on ASI:One @caliber-controller (answer, approve, reject) · @caliber-investigator (why, status)
Code https://github.com/anudeep-bonagiri/caliber
Technical note and CarrierBench (PDF) caliber.compare/caliber.pdf
Site https://caliber.compare

tag:innovationlab tag:hackathon


Inspiration

I run Cruze, a fleet startup that did $10k MRR in its first month deployed. Every carrier we work with has the same person in the back office: someone with a fuel-card statement, a toll statement, a stack of repair invoices and a telematics login, matching them by eye. None of those carriers would let software pay a bill on its own, and they are right not to.

The scale is not small. Trucking is a $906 billion industry (ATA, 2024), 91.5 percent of carriers run ten trucks or fewer, fuel-card misuse runs 2 to 5 percent of fuel spend (Shell), and a manually processed invoice costs about $21.40 (APQC). So we did not ask "can an agent do AP." We asked whether we could build one a controller would actually hire: limited access, a clean record, and the sense to ask before it acts.

What it does

Caliber is an accounts-payable clerk for a trucking carrier. We ran it against a simulated 40-truck Texas carrier over 92 business days, with 52 planted errors in 7 classes, 6 decoys, and an answer key that no agent and no worker code can read. Here is one business day from the offline run:

Time What happened
0 s The day-41 fuel-card export (61 rows) lands in inbox/.
~2 s The kernel matches every row against telematics, tank size, contract rates, POs and receiving. Clean rows pay themselves within limits. Hold 2231 opens: card FC-1017, assigned to truck 17, bought 120 gallons ($412.00) at Love's 412 in Schulenburg, Texas, while truck 17's nearest ping, 13 minutes after the charge, was 97 miles away with the engine off. Nothing is paid.
+19.0 s A read-only Droid session investigates: it asks Elastic which trucks pinged at that pump, opens the receipt, reads the dispatch swap log. Finding: truck 22 was at the pump, driven by Ramon Ortiz, while truck 17 was in the shop. Only a person can confirm that. Recommendation: hold and ask.
On ASI:One the controller types why 2231, reads the evidence, and answers in one line: Yes. Ortiz drove unit 22 that week while unit 17 was in the shop.
~2 s Caliber drafts a rule from the answer: driver 23, days 41 to 46, one use. It backtests the rule against holds the controller has already decided (1 would clear, 0 of them confirmed exceptions) before the controller ever sees it. approve rule 1. The kernel re-runs and the hold clears. It won't ask again for that swap.
0.81 s approve 1068 on a clean invoice over the auto-pay limit: the reducer checks role and segregation of duties, writes the payment, and a transfer posts to Capital One's mock bank. The bank id is on the audit row 0.81 s later.
The investigator is told to approve 2231. The database refuses (reader may not do this) and writes the refusal to the audit log with the role that asked.

Across all 92 days: precision 1.0 and recall 1.0 on the 52 planted errors; 7,944 transactions matched and 7,886 paid automatically ($1,083,630.52); all 6 decoys cleared after exactly one human answer each; 0 planted errors cleared by a rule. The clock replays 41 business days in 0.2 seconds, and a bare clone reaches the judging start state in 19 seconds without the web build.

How we built it

  • Our own kernel, no model inside. kernel.ts is plain TypeScript in integer cents: haversine against the nearest ping, gallons against tank capacity × 1.10, tolls within a 10-minute window, contract rate within 5 percent, a 30-day duplicate window, bank changes within 7 days of a payment over $5,000, auto-pay caps of $2,500 per charge and $40,000 per day. Every threshold lives in thresholds.ts. Result: the same answer to the cent on every run, and the committed facts reproduce byte for byte.
  • SpacetimeDB is the referee, not the storage. The whole carrier lives in one module. The business-day clock is a scheduled reducer. Roles are a table, and every reducer checks the sender before it does anything. approve_payment refuses when the requester is the sender. The audit log has no update or delete. Result: the investigator has been refused on every write it was ever asked for, and each refusal is on the record.
  • Factory's Droid as a boxed investigator. One session per hold: autonomyLevel: Off, Execute, Edit, Create and ApplyPatch disabled, a permission handler that cancels anything write-like, cwd on the evidence folder, JSON-schema output through a zod gate with no pay action in it, and a 90-second abort. Every tool call streams to the dashboard. Result: 52 live investigations recorded, 25.6 s and 1,033 input / 1,354 output / 70,386 cache tokens on average, about $5.44 in Factory credits for all 52.
  • Elastic answers what the kernel can't. Four Agent Builder tools on Serverless, all reached by Droid over MCP: two ES|QL (find_trips_near, a geo-distance query over telematics with typed date params; contract_rate_for_vendor) and two semantic on Jina embeddings (find_contract_clause, called live in the INV-1003 investigation, and find_receipt_text). The stale_holds Workflow finds holds open more than two business days, has the agent brief each one, and indexes the briefs; it runs on a manual trigger. Result: 63 s from trigger to the escalation count in the controller's chat.
  • Two agents, two identities. @caliber-controller (agent1qt278cr9luj7xw9cs6c47lnfl9tvx9xsu7asxtmhuqkcqq920m6ju5y5yge) holds the only write token, and writes only for the ASI:One sender on its allowlist. @caliber-investigator (agent1qvwvlc0vz2amjh4q4yf0p2fglqrzw2rmfm6afrsq82e8lq5df7yyxnrxaqh) holds a read-only one and answers why and status; the controller relays to it over agent-to-agent chat. Both are mailbox agents on the Chat Protocol, discoverable from ASI:One. Result: the agent that explains a charge cannot pay it, and that is enforced by identity, not by code path.
  • Memory with an expiry. An answer becomes a rule with a scope, a date range, a use count and a backtest against holds the controller already decided. A rule that would clear any confirmed exception never reaches the controller, and no rule can touch capacity or bank-change holds. Result: 6 of 6 decoys resolved with one answer each, and 0 real errors ever cleared by a rule.
  • Settlement and vendor checks. An approval posts a transfer to Capital One Nessie and stores the bank id on the payment row. A vendor bank change triggers a business credit, public records and OFAC check in the CRS sandbox format, with nothing on any person; the sandbox invite never arrived, so these run on a hand-authored fixture labelled FIXTURE everywhere it appears.

How Caliber meets each track

Track What the judges ask What Caliber does
Hyphenate Real enough; would you hire it (seats, limited access, clean record, knows when to ask); something new; the moment Fills the AP clerk seat with a read-only investigator and a write path only the controller holds; every action and every refusal is an audit row; it asks once and the answer becomes a scoped, expiring, backtested rule; the moment is hold 2231; new: CarrierBench, a benchmark for carrier AP with a hidden key
Factory Droid on a trigger you designed; control over what it can touch; its work visible One session per hold, triggered by the ledger; autonomy Off, write tools disabled, a cancelling permission handler, a 90 s abort, typed output with no pay field; every tool call, permission decision and result on the card
Elastic Agent Builder on Serverless, tools and an agent created over the API, MCP Four ESQL and semantic tools created over the API, reached by Droid at the MCP endpoint, geo over 154,846 pings, Jina embeddings over contracts and receipts, a Workflow that briefs stale holds
Fetch.ai Agents on Agentverse with the Chat Protocol, discoverable from ASI:One Two mailbox agents; the controller moves money from ASI:One behind a sender allowlist; the investigator explains and is refused when it tries to pay
Microsoft Customer understanding; measurable usefulness; path to production; responsible design; end to end Built from a fleet operator's own customers; precision and recall against a hidden key plus a model-only baseline; a human on every payment that isn't clean; file in, bank id out
Capital One Completeness, creativity, complexity An approval is a bank transaction with an id on the audit row, not a status flip; 125 live transfers on the staged day
CRS Central use of CRS data; works live; useful; unexpected A credit bureau pointed at vendors instead of borrowers, on a bank-detail change; labelled FIXTURE until the sandbox invite lands
Spacetime The core real-time backend: live shared state, agent coordination, server-side logic next to the data The ledger, the clock, the roles and the audit trail are one module; the kernel runs inside reducers; every client and both agents are live subscribers to the same public tables

Challenges we ran into

  • Droid cleared the decoys. Our first six decoy investigations recommended clear, skipping the one question the product exists to ask. We rewrote the brief so that a legitimate explanation only a person can confirm yields hold_and_ask, re-recorded all six, and kept the originals in fixtures/investigations/_v1/.
  • A label that lied. An audit of our own dashboard found a card where a replayed investigation could be read as live. Every stand-in is now labelled in the trace itself.
  • Our cost figure hid tokens. "1,033 input tokens" left out about 70k cache tokens per investigation. We report input, output and cache separately now.
  • A model could have moved money. Free text that our classifier read as "approve" was dispatched with the controller's token. Our Sunday review caught it; money and rule decisions now require the typed command, and the model's reading is only echoed back.
  • Factory keys sit behind a paid plan. Three teams hit the same wall in Slack before the voucher path surfaced. Live Droid came up at 1:25 PM Saturday.

What we got wrong, and what we haven't built

We gave the same inputs to models with no kernel at all. Claude Sonnet matched the kernel exactly, at $6.38 for the 558 rows. Claude Opus 5.5 kept recall at 1.00 but held 14 clean charges, 10 of them as rate variances, for precision 0.81, and the run cost $12.85. The kernel cost nothing, which makes it infinitely cheaper; we did the division. asi1-mini reached 0.31 precision. That is why code decides and the model explains. To be fair to the models: the planter and the kernel share thresholds, so recall on planted rows measures coverage, not discovery. scripts/inject.py lets anyone plant their own errors. The full method, the benchmark and the baseline tables are in the technical note: https://caliber.compare/caliber.pdf

Benchmark at a glance: cost, time and false holds per system

Not built: real connectors (files only), OCR, and the fuel dispute path. In production, fuel and toll statements are drafted by the issuer on a date, so a fuel hold becomes a dispute filed inside the issuer's window rather than a payment approval; repair invoices are paid per invoice, which is what the demo shows. The carrier is synthetic, the benchmark is ours, the bank is a sandbox, and the CRS checks are fixtures.

What we learned

  • The hard part was never reading the documents. It was deciding who is allowed to act on what was read. Once money could only move through one reducer, every other decision got easier.
  • Memory is the product. A hold that comes back tomorrow is a failure; a rule with a scope and an expiry is what makes the second month cheaper than the first.
  • Every sponsor tool had to earn its place by filling one need. When we were tempted to add one just to show it off, the product got worse.

What's next

  • Run Caliber on a Cruze customer's last quarter, with their controller in the controller's seat.
  • Real feeds: WEX and Comdata fuel cards, Samsara and Motive telematics, toll authority statements, and the dispute path.
  • Open CarrierBench to other teams.

Try it

git clone https://github.com/anudeep-bonagiri/caliber && cd caliber
scripts/demo_up.sh   # 19 s from a bare clone without the web build: local SpacetimeDB, worker in replay mode, dashboard
scripts/full_run.sh  # 92 days, writes docs/FACTS.md

On ASI:One, message @caliber-controller: what needs me, then why 2231.

Simulated carrier. Sandbox money. Not for operational use.

Built With

  • agentverse
  • anthropic
  • asi-one
  • capital-one-nessie
  • claude
  • crs-credit-api
  • elastic-agent-builder
  • elastic-workflows
  • elasticsearch
  • factory-droid-sdk
  • fetch-ai
  • github-actions
  • gitleaks
  • jina-embeddings
  • mcp
  • pytest
  • python
  • react
  • spacetimedb
  • typescript
  • uagents
  • vite
  • vitest
+ 2 more
Share this project:

Updates

Submission history