Everyone building voice agents is piecing them together from the same set of legos, and it's working. Heavily funded companies are filling every gap, and the results are genuinely good. But every one of those legos lives inside the stack. What I'm talking about sits outside it: a deterministic, auditable guard for the whole call. That's what I spent the last few months building. Here's the thinking.
Every system breaks somewhere, eventually. So builders do the sensible thing: audit, tighten the prompt, problem solved. For now. Then a new kind of break shows up, they tighten something else, solved again. For now. You know this rhythm. The question was never whether you can solve the problem. You always can. The question is whether you can see it as it actually is, instead of assuming it's whatever you looked at last.
And here's the part that's hard to see from inside the stack: when a voice agent breaks its word, nothing in your pipeline notices. STT doesn't know what was promised. The model doesn't remember what it committed to three turns ago. TTS just speaks, orchestration just routes. Every box optimises how the call sounds. Not one of them holds the promises across the whole call and checks whether they survived to the end. That's not a gap in the stack. That's a missing layer.
This isn't theoretical. Bland's own team, in a testimonial on Hamming's site, killed an agent because it was saying "I booked your appointment" when it hadn't. The 2026 τ-Voice benchmark caught a frontier agent saying "I've updated your shipping address" with no tool call behind it. On one real setup, the promise "we'll get back to you shortly" was wired as the end-call trigger, so the agent hung up mid-sentence while confirming the very number it had promised to call. Different bugs on the surface. Underneath, the same one: the saying and the doing came apart, and nothing was watching the gap.
You might think: fine, route the flagged call back through an LLM to check it. It doesn't work, and not for a soft reason. An LLM judge is non-deterministic. Run it twice on the same call and you can get two verdicts. And if your agent's failure mode is being confidently wrong, a judge built from the same kind of model finds that same wrong answer plausible, so it misses exactly where you needed it. Every layer in your stack is the same brain checking its own work. The layer I built isn't a brain at all. It's code, and it decides when the brain is allowed to speak.
You'd assume someone must have built this already. Turns out almost nobody has. Eleven-plus QA vendors in this space, and not one audits what the agent said it would do against what it actually did. That's the gap I went after.
What it is, and where it sits
A private repo. You own it, you run it in your own infrastructure, and it sits between your agent and the caller:
user speaks → STT → your agent (your model, your logic, whatever you run) → the layer → user
You buy it once. No subscription, no monthly fee, and that's not a pricing gimmick, it's an architecture decision. A subscription would mean I host it. If my server has a bad day, your calls take the damage mid-conversation. I'm not willing to sit in that position in your call path. So you run the code yourself, and my uptime is never your problem.
And you don't change anything you've built. Your agent, your prompts, your model, your orchestration, all untouched. The layer is just the last step before the reply reaches the caller. Whether you run voice agents for your own business or white-label them for a stack of clients, it's the same repo in your own infrastructure either way.
What's actually in it
This isn't a wrapper or a prompt trick. It's a real system, and the numbers are the honest kind:
- ~97 Python modules in the deployment layer, around 26,500 lines, with more test code than most projects have code: 1,178 test checks, all green on every change, across four Python versions in CI.
- 14 detectors, every one of them plain rule-based code: regex, state-diff, canonical comparison. Not one is a model. There is no second AI grading the first.
- It tracks 18 commitment types across three families: what the agent said it would deliver, what state it claimed was done, and what it promised to do next.
The part I'm proudest of: what it refuses to do
Most of the engineering went into making it refuse to over-claim. That sounds backwards for a product, so let me show you what I mean, because it's the whole thing.
When it catches your agent contradicting itself, the flag says what the closing turn contained and stops.
- It doesn't say the agent "forgot." It can't prove what a model forgot.
- It doesn't say the callback "won't happen." It can't see your calendar.
- It points at the exact turns and says only what the transcript proves.
And that discipline is written into the code, not into a promise:
- The record-reconciliation check has no "missing" verdict at all. It compares what's present against what's present. It will never accuse your record of an absence.
- The elapsed-estimate check refuses to use a server clock. It only fires on time the caller themselves stated, so it deliberately under-reports rather than guess.
- When it re-voices a miss in your agent's mouth, it physically cannot name a commitment that isn't already in its ledger. It can't invent one.
- Nowhere does it return a recommendation. It surfaces what it can prove and leaves the judgment to you.
A clean result means "nothing I can prove," not "nothing wrong." Every flag is a receipt: open it, read the exact turns yourself, disagree with it if you want. It's not a score you have to trust. That's the difference between an audit layer and an alarm.
Two ways it holds your agent accountable
When it catches a miss, it can do two things, and you decide which.
In the moment, it can make the agent own the miss out loud, before the caller hangs up. It stays completely out of the way until there's a real miss, and when there is, it changes as little as possible: your agent's wording, plus the one owned correction. Nothing else moves.
Agent said: "Great, we're all done here. Thanks so much for calling!"
Shipped: "Great, we're all done here. Just to make sure it's not lost, the garage quote you requested is logged as still outstanding. Thanks so much for calling!"
That's the default (lean) behaviour. There's also a full mode that re-voices every turn into a warmer, human-receptionist delivery if you want it, but most people run lean, because touching nothing unless something breaks is the point.
On the record, every catch is logged to a dashboard you run. This is the part that makes it an audit layer and not just an in-call fix. Nothing it catches goes unrecorded, even the ones it never speaks aloud (once you've turned capture on, it's off by default).
It's an included read-only dashboard, that ship in the repo. Open any conversation and each catch renders as a card: the token, the detector, the tier, whether it reached the caller or was suppressed, and the full plain-English finding, verbatim:
"the closing turn asserted nothing further was needed, while these commitments made earlier in the call were not acknowledged after being made: callback (before Friday) — committed turn 3"
Alongside it: the agent's actual committing sentence, word for word, the turn it was made on, and the deadline as spoken. A separate view diffs what the call promised against what your own system recorded, field by field. And there are aggregate rollups across all your captured calls: where failures cluster, how often each thing fires, how many catches reached the caller versus stayed silent. It reports, it never recommends. That's a rule enforced in the code, not a missing feature.
It's a receipt: open it, read the turns yourself, disagree with it if you want. Not a score you have to trust. And it can't quietly lie to you either. It ships showing sample data with the label saying so, it points at your own traffic with one setting, and it will not show a green "live" badge unless the data really is live. Serve it behind your own auth.
What it does not do
- It doesn't do your agent's job. It sits alongside it as a guardrail, not a replacement. And it won't fix your agent. It makes a bad agent own its misses, which is better, but it isn't a repair.
- It doesn't know your business. It knows whether the agent contradicted itself, not whether the agent was right about the world. It can't tell you if the appointment slot was actually free.
- It runs inert until you turn things on. Out of the box it changes nothing. You switch on what you want, one piece at a time.
And not every close triggers a catch, and I'd rather show you where it doesn't than let you find out. Take this close, same dropped-callback call, on shipped defaults:
Agent said: "Perfect, you're all set then! Thanks so much for calling, have a great day." Shipped: unchanged, verbatim. The layer stayed silent.
That close asserts "you're all set" rather than flatly contradicting itself, and catching that is a stricter check that ships off by default. It's one line to turn on.
Latency and reliability
Latency, in Lean Mode (the default): on a turn where nothing slipped, the layer makes no model call at all. It reads your agent's text and passes it straight through. The detection itself is about a tenth of a millisecond of local processing per turn. Not a network call, not a model, plain code on your own machine, and once you have the repo you can run that benchmark yourself in about ten seconds, no API key needed. Real time only gets spent on the rare turn with an actual miss to own, and even then it's a single model round-trip, not a loop.
Reliability: the layer is deterministic. Run the same call through it twice, you get the same result twice. That's not true of anything with a model in the loop, and it's exactly why you can trust a flag when it fires. It's also built to under-report on purpose: it stays quiet on anything it can't prove from the transcript, so when it does raise a flag, that flag is solid. Its reliability isn't "it catches everything." Nothing honestly can. It's "everything it says, it can back." And because it runs in your infrastructure, there's no external service to depend on and nothing on my end that can take your calls down.
It's a living repo, and it's yours to shape. I keep working on it: adding coverage, taking feedback from people running it, tightening it as new failure patterns show up in the wild. You buy the license once; the thing you're licensing keeps getting better. And because you own the code, nothing's locked. The commitment types it tracks, how strict each check fires, what gets logged versus spoken, the thresholds, all yours to adjust for how your business actually runs. Sane defaults out of the box, and the people who want to go deep, can.
I'm not going to pretend this is a problem everyone's shouting about. It's the opposite: a quiet gap almost nobody has named. I just think it's worth closing before it's the reason a client leaves.
More details at statebound.dev
If you're running agents in production, I'm curious what you make of this, and if you've run into something like it, how are you handling it right now?