
A language model is a wonderful reader and an unreliable accountant. So it never does the arithmetic here. It shapes the question — which rows, which grouping, which window — and ordinary, testable code computes the answer. Everything below is a mechanism you can point at, not a principle we hope to keep.
“How much did we invoice in March?”
Ask “how much did we invoice in March?” and the model doesn’t answer. It fills in a constrained query specification — a small, validated object naming a source, a measure, a grouping and a date window. Plain JavaScript then walks the real rows and adds them up.
That single choice removes two whole classes of failure at once. There is no SQL for anyone to inject, because no query string is ever assembled. And a total can’t be hallucinated, because no total was ever generated — it was computed, and it comes back with the exact rows it came from.
Bookkeeping isn’t a report we render from your invoices; it’s a real double-entry ledger underneath them. Four guarantees live in the database, not in a promise: an entry whose debits and credits disagree is rejected at commit; a line is debit or credit, never both and never negative; the same event booked twice does nothing; and a closed month can’t be inserted into, altered or deleted — not even by the service role.
Which posting follows from an invoice, an expense or a VAT code is a pure function with a test that walks every code against six amounts. Corrections are counter-entries, never edits: your history stays what it was.
The interesting part isn’t that it asks permission. It’s that it couldn’t skip asking if it wanted to.
Agents draft. Sending, publishing, invoicing, refunding — anything the outside world can see — waits for you. That isn’t enforced by hiding a button: the send path loads the recipient and the content from the database, not from whatever the browser posted, then re-checks the do-not-contact list, your sending window and your daily cap before it moves.
There is one narrow exception, and it’s allowlisted server-side: shop questions like “where is my order?” can auto-reply if you switch it on. Money, complaints, cancellations and address changes never can. Your settings can only make that stricter, never looser.

Not every step needs the strongest model. Mechanical work — mapping a column, classifying a ticket — routes to a small one; judgement and anything a customer will read has a floor it never drops below.
When a cheap attempt produces a specification, it is escalated only if it fails the actual validator. Self-reported certainty is the one signal that correlates with nothing.
Separation between accounts is row-level security in the database rather than a filter in the application, so another account can’t read your rows even if the app asks it to. Keys you connect are encrypted, decrypted server-side for the one action you approved, never logged, never sent to a model, and never returned to the browser.
There is also an environment-level lock that closes every outbound path at once — so a staging copy of real data cannot reach a real customer. It defaults to off, refuses loudly rather than silently, and a test reads the source to prove each path checks the lock before it ever unlocks a credential.
All of the above is what the machine guarantees on its own. How much an agent is allowed to do without asking is a separate thing entirely — and that one is yours to set, per kind of work, revocable at any time.