⟂

The agent reads  ·  The policy decides  ·  The database enforces

Remit-To is an autonomous accounts-payable agent for freight brokers. It matches invoices to proof, catches duplicates and payment redirects, and holds anything it cannot prove.

live · read-only · spacetimedb subscription
Claude·SpacetimeDB·TypeScript·Node·No funds move· Claude·SpacetimeDB·TypeScript·Node·No funds move·

The model never decides who gets paid.

Claude reads and explains. rules.ts decides. SpacetimeDB enforces who may approve. When a document disagrees with a verified record, the document loses.

How it works

One policy. Two call sites. Zero trust in prose.

The model produces typed fields and an explanation, nothing more. The same rules.ts runs in the agent to explain, and inside the reducer transaction to decide.

01

Read

Claude reads the rendered invoice, rate confirmation, POD, NOA and emails. It returns typed fields with a confidence score and a plain-language explanation. It cannot touch assignments, bank records or approvals.

02

Decide

Eight deterministic rule families (thirteen checks) in rules.ts select the amount from the rate con and the payee from verified assignments. Any failed rule is a hold with the rule id, reason and evidence ids attached.

03

Enforce

SpacetimeDB reducers re-run the policy on trusted rows, reserve the load and invoice key atomically, and derive the actor from the connection identity. The agent identity cannot approve. Refusals commit to the audit log.

Three stories

Proven on freight, where payment-redirect fraud is worst.

Three seeded loads run end to end through the same path: documents in, decision out, approval by a different human, payment status that never lies.

L-2041 · Ridgeline■ HOLD R2
policy payeeF-01 Meridian
requestedcarrier C-301
NOA effective2026-03-02
email "updated banking"unverified
escalationdrafted → factor
Redirect■ Hold

June invoice vs. March NOA

A June invoice and an "updated banking" email ask us to pay the carrier directly. A verified March assignment says the factor. An unverified email cannot replace a verified payment instruction.

Open L-2041 on the board →
L-2037 · Blue Mesa · re-bill■ HOLD R3
existing obligationINV-5521 · reserved
new invoiceINV-5521-R
concurrent requests2 → 1 reserved · 1 refused
refusalcommitted to decision_log
Duplicate■ Hold

Same load, new invoice number

Two requests race for the same load. Unique indexes inside one transaction leave exactly one reservation. The loser's refusal is a committed audit row, not an exception.

Open the re-bill on the board →
L-2037 · Blue Mesa□ APPROVED
rate con$1,850.00
invoice$1,850.00 · INV-5521
PODsigned · Jun 9
requested byagent
approved bycontroller · not requester
Clean□ Approved

Match, request, approve, submit

Invoice matches rate con and POD. The agent requests; a different human approves; the runner submits with a persisted idempotency key. Status moves to submitted, then simulated_settled, live.

Open L-2037 on the board →
Payment lifecycle

Submitted is not settled.

pending ──▶ approved ──▶ submitting ──▶ submitted ──▶ simulated_settled
   │            │              │              │
rejected     pending*       unknown         failed
                               │
                      submitted / failed

pending*  prior approval invalidated when the snapshot changes
unknown   obligation stays reserved; reconcile the stored key
Unknown is not failed.
Unknown is not permission to pay again.A timeout after a possible send never creates a new payment identity. The runner restarts, finds its stored idempotency key, and reconciles to one transfer.
Evidence

Measured on what we actually tested.

36 synthetic development cases scored against an explicit answer key the agent never reads. Not a blind benchmark. Not a production catch rate.

0/36
exact decision · payee · amount · rule
0/24
false holds on clean cases
0/12
unsafe requests on exception cases

Real

  • Deterministic policy, 8 rules (13 checks), 22 tests
  • SpacetimeDB tables, reducers, live subscriptions
  • Sender-derived identity and separation of duties
  • Atomic load + invoice reservation
  • Claude extraction and explanation
  • Idempotency key persisted before dispatch
  • Crash-after-accept recovery test

Simulated

  • Payment provider (in-process mock, labeled)
  • simulated_settled status
  • Carrier authority status (seeded)
  • Bank-change callback verification
  • Demo personas (first-claim, still sender-bound)

Not built

  • Real bank rails or TriumphPay
  • Accessorials, detention, lumper
  • Release processing, conflicting assignments
  • General ledger
  • Live authority lookup
  • Policy learning

No money moved. No real dollars saved are claimed. Extraction errors are reported separately from policy errors.