A stylist finishes an appointment, taps a button, and a few seconds later Patron has written down what happened: the formula and the toner, the thing the client said about her sister's wedding, the fact that she reacts to a particular product. That note becomes part of a person's permanent record. Next time she sits in the chair — at the salon, the nail bar, the lash studio — the pro already knows.
Which means the worst bug we can ship isn't a crash. It's a memory of something that never happened. A crash is loud and obvious. A fabricated memory is quiet, plausible, and it erodes the one thing the whole product is selling: that you can trust what Patron remembers.
This is the story of one of those bugs — how a transcription model invented an entire appointment out of an empty room, why it walked straight through three separate defenses we'd already built, and the surprisingly simple thing that finally caught it.
The empty room
We were running an end-to-end test through the real app: open it, start a recording, let it run for a few seconds, stop. Nothing was said. The phone was sitting on a quiet desk. We expected the result every test of silence had given us before — a session marked "No speech detected," with no notes.
Instead, the app came back with a complete, confident session. A summary describing a straight-razor setup. A specific tool setting, quoted as if the client had requested it. And — this is the part that made our stomachs drop — a new preference written to a real client's profile, the kind of durable fact the barber would be greeted with on the next visit.
None of it had happened. There was no client, no conversation, no haircut. There was a quiet room and a microphone.
Why models dream
Speech-to-text models hallucinate. This is well known, but it's worth being precise about when. Hand a modern transcription model a file of true, digital silence — a buffer of mathematical zeros — and it will correctly return nothing. We had tested that case exhaustively, and the pipeline handled it perfectly.
But a real microphone never gives you digital silence. A phone on a desk in a "quiet" room is picking up a faint floor of hiss, electrical noise, the building's HVAC, a far-off door. To a person, that's silence. To the model, it's ambiguous audio — and faced with ambiguity, these models don't shrug. They commit. They produce a short, confident burst of speech that isn't there, frequently in a language that has nothing to do with the input. In our case, a few words of Devanagari script — Hindi — conjured from the hiss of an empty room.
Three guards, and how it walked through all of them
Here's the humbling part. We had been burned by fabrication before, and we'd built defenses. By the time of this test there were three independent layers standing between a recording and a client's profile:
- A loudness gate: before transcription even runs, we measure how loud the audio actually is. Dead-quiet audio is routed straight to "no speech" without ever asking the model.
- A substance gate: if the transcript that comes back is too short to be a real conversation, we treat it as no speech and stop.
- A verifier: every claim the system extracts is checked back against the transcript before it's allowed to become a note. Unsupported claims are deleted.
The hallucination beat all three, and the way it beat each one is the actual lesson of this post.
It beat the loudness gate because that gate is tuned for silence, and this wasn't silence — it was noise. Room hiss is meaningfully louder than a buffer of zeros. The gate did its job; its job was just the wrong job for this input.
It beat the substance gate because that gate counts characters, and the hallucination was long enough to count as "real." How long? About two characters over the threshold. A guard measured in length was defeated by a forgery that happened to be the right length.
And it beat the verifier in the most insidious way of all. The verifier checks each claim against the transcript. But here, the transcript itself was the lie. The fabricated "straight-razor setup" was, indeed, faithfully supported by the fabricated transcript that mentioned a straight razor. Garbage in, garbage validated. A fact-checker is only as good as its source, and we had handed it a poisoned one.
Three guards. Every one of them measured the wrong thing. Two measured magnitude — how loud, how long. The third trusted a source it had no way to know was compromised.
The axis we weren't measuring
We spent a while staring at thresholds. Make the length gate stricter? A real "yeah, the usual fade" is also short — we'd start throwing away legitimate quick sessions. Tune the loudness gate? It would just move the line; noise is a continuum, and there's no loudness number that cleanly separates "noisy room with no speech" from "quiet room with speech."
Every dial we reached for was a dial on the wrong axis. We kept asking how loud and how long. The actual tell had been sitting in the transcript the whole time: it was the wrong language entirely.
The audio our customers record is overwhelmingly English — including accented English, and the various Latin-script languages a diverse clientele speaks. A burst of Devanagari, or Cyrillic, or CJK characters is, almost by definition, not a record of a session in the chair. It's a signature of the failure mode itself: these models, when they hallucinate on noise, reach for a random script far more often than they produce believable English.
So we added the axis we'd been missing. A transcript whose letters are overwhelmingly non-Latin is treated as no-speech — and we apply that test at every layer, including teaching the system to discard its own domain hint and re-listen the moment the output looks like a foreign-script burst.
The check is deliberately boring and deliberately conservative. It never fires on genuine English, including accented names and Latin-script languages — a Croatian client's name, a Dutch street, a Spanish-speaking regular all sail through untouched, because they're still Latin letters. It only catches the thing that is, unmistakably, not a session.
What we're still honest about
This fix closes the common door, not every door. The hardest remaining case is the one where the model hallucinates English-looking gibberish — real-script, real-shaped words describing nothing. A script check can't catch that; it needs a confidence or speech-presence signal, and we wrote that limitation down in our own engineering notes rather than pretend it was solved. We'd rather ship a guard we understand the edges of than a guard we believe is perfect.
Two lessons we took with us
Test the noise, not just the signal. We had run hundreds of clean audio files through this pipeline and it looked bulletproof. The bug was structurally invisible to that test, because clean test audio is the one thing a real microphone never produces. The failure only appeared when we stopped feeding the system perfect inputs and handed it the messy, ambient, real-world thing: a phone in a quiet room. The most valuable test we ran all month was the one where we recorded nothing at all.
Defense in depth only works if the layers are actually different. Three guards feel like three guards. But two of ours were measuring magnitude and the third was trusting the very artifact under suspicion. They weren't three independent checks — they were closer to one check, wearing three coats. Real redundancy means each layer can fail in a way the others structurally cannot. Adding the script axis didn't just add a fourth guard; it added the first one that asked a genuinely different question.
For most products, a transcription model inventing a few words from background noise is a curiosity. For a product whose entire promise is "we remember your clients accurately," it's existential. So we treat the empty room as one of our most important test cases — because the sound of nothing is exactly where a memory product is most tempted to make something up.
More from the team → Patron Engineering. Building the memory layer for service professionals? We'd love to talk.