- ≈800lines in the deterministic scrubber
- 500character ceiling on any clause, set by the synthesiser's own limits
A reasoning model writes for a screen. It writes "H₂O", "8.832", "abcdef.md", "U.S." and "1000", and every one of those is a trap for a synthesiser that speaks phonemes. In real use the formula for water came out as a sneeze, a thousand was spoken as ten thousand, a filename was read as a word, and the abbreviation for the country became "u dot s". Each is small. Together they are the difference between an assistant that sounds like a colleague and one that sounds like a machine reading a document.
Two layers
The first layer is a speech contract in the prompt. The model follows it most of the time, and most of the time is not enough for speech.
The second layer is a deterministic scrubber that runs on every clause after the splitter and before synthesis. What a person reads is what the model wrote, and what they hear is what a person would say.
I pair a contract the model is asked to follow with a deterministic layer that does not rely on it following.
Asides are never spoken
When a person interrupts and the agent's answer is cut off, the engine keeps the answer as the room heard it, so the model treats the interrupted question as addressed and answers only the new words. An early version marked the cut with a bracketed note, and the model started imitating the bracket aloud in later turns. The scrubber's bracket rule fixed that and a class of similar leaks at once.
The counting bug
Asked to count from one thousand upward, the voice skipped and doubled numbers while the correct text reached the synthesiser. The cause was an alignment slip inside the model when many numbers arrived in one clause. The fix is in the contract, one item per sentence, which keeps each number in its own clause, and the clause splitter cuts only on sentence-ending punctuation with decimals and abbreviations protected, under a single forced ceiling that the synthesiser's own limits dictate.
What this demonstrates
I close the gap between text that reads well and text that speaks well, and I do not trust a model to close it alone.
The say-and-show split and the contract were mine; AI coding agents, a mix of frontier and open-source models, built the scrubber and its tests under my governance system.