Companion to #331. That ticket explains why the microphone was still open; this one explains what
the silence turned into.
What happened
Coder Lead, 2026-08-06. Two user messages, both carrying an audioKey, both content "Coder
Lead" — 23:51:51 and 23:52:10. The user did not say it either time.
Immediately before, "Stop-stop." had failed to stop anything (#331), so the user had disengaged
while the mic was still live. Hands-free recorded silence and auto-submitted it.
Cause: the agent's own name is in the transcription bias list
store/console/src/pages/InstanceDetail.tsx:218:
transcribePrompt: buildTranscribePrompt(surfaces, instance?.name ? [instance.name] : []),
buildTranscribePrompt appends extra to the vocabulary list handed to Whisper. So for this
instance the prompt literally contains "Coder Lead" — and on silence, the decoder returns a
term from the list it was given. It is not imagining the user; it is echoing our own word back.
This is the unfinished half of an earlier fix
365475c fixed the fluent version of this: the prompt used to open "The speaker is talking to an
AI assistant about their work. Expect terms like: …", and silence came back as whole sentences
built from the term list — "I just need to refactor this function before I commit the changes to
the repo." Making it a bare comma-separated list removed the grammar that produced prose.
It did not remove the words. A bare list still supplies candidates, and a proper noun is the
most likely thing to surface — it is the only distinctive token in a list otherwise full of
generic vocabulary, and Whisper prefers it precisely because the prompt marked it as expected.
So the failure got quieter, not fixed: "I just need to refactor this function" became "Coder Lead".
Why not simply drop the bias
Vocabulary bias earns its place — it is what stops a developer's "bugs" being transcribed as
"bars", which is the reason buildTranscribePrompt exists. Removing it trades one wrong
transcription for another.
Better options, roughly in order of preference:
- Gate the bias on the speech gate.
hadSpeech(peakLevel) already exists and already refuses
to upload a recording with no speech. If that gate is doing its job, silence never reaches
Whisper and the bias is harmless. Worth checking why these two clips got through — a whole-room
silence should have been discarded before upload, and if it was not, that is the more valuable
bug.
- Keep proper nouns out of the list. Agent names, repo names and usernames are exactly the
tokens that come back verbatim from noise, and they are the least useful for disambiguating
ordinary speech. Generic domain vocabulary ("refactor", "commit") is where the value is.
- Reject a transcript that is exactly a bias term and nothing else. Cheap, targeted, and
isNoiseTranscript is already the place for it — it currently matches a fixed list of classic
hallucinations ("you", "thank you") and would extend naturally.
(1) is the real fix; (2) and (3) are defence in depth for when it leaks.
Note
#319 (persist the dictation) would have made this self-evident: the bubble would have shown nothing
captured, then "Coder Lead" appearing from the final pass. Right now the two are indistinguishable
from the outside, which is why it reads as the agent imagining things.
Verification
- A silent recording in hands-free produces no message at all.
- A transcript identical to a single bias term is discarded.
- "bugs" is still transcribed as "bugs", not "bars" — the bias still works for real speech.
Companion to #331. That ticket explains why the microphone was still open; this one explains what
the silence turned into.
What happened
Coder Lead, 2026-08-06. Two user messages, both carrying an
audioKey, both content "CoderLead" — 23:51:51 and 23:52:10. The user did not say it either time.
Immediately before, "Stop-stop." had failed to stop anything (#331), so the user had disengaged
while the mic was still live. Hands-free recorded silence and auto-submitted it.
Cause: the agent's own name is in the transcription bias list
store/console/src/pages/InstanceDetail.tsx:218:buildTranscribePromptappendsextrato the vocabulary list handed to Whisper. So for thisinstance the prompt literally contains "Coder Lead" — and on silence, the decoder returns a
term from the list it was given. It is not imagining the user; it is echoing our own word back.
This is the unfinished half of an earlier fix
365475cfixed the fluent version of this: the prompt used to open "The speaker is talking to anAI assistant about their work. Expect terms like: …", and silence came back as whole sentences
built from the term list — "I just need to refactor this function before I commit the changes to
the repo." Making it a bare comma-separated list removed the grammar that produced prose.
It did not remove the words. A bare list still supplies candidates, and a proper noun is the
most likely thing to surface — it is the only distinctive token in a list otherwise full of
generic vocabulary, and Whisper prefers it precisely because the prompt marked it as expected.
So the failure got quieter, not fixed: "I just need to refactor this function" became "Coder Lead".
Why not simply drop the bias
Vocabulary bias earns its place — it is what stops a developer's "bugs" being transcribed as
"bars", which is the reason
buildTranscribePromptexists. Removing it trades one wrongtranscription for another.
Better options, roughly in order of preference:
hadSpeech(peakLevel)already exists and already refusesto upload a recording with no speech. If that gate is doing its job, silence never reaches
Whisper and the bias is harmless. Worth checking why these two clips got through — a whole-room
silence should have been discarded before upload, and if it was not, that is the more valuable
bug.
tokens that come back verbatim from noise, and they are the least useful for disambiguating
ordinary speech. Generic domain vocabulary ("refactor", "commit") is where the value is.
isNoiseTranscriptis already the place for it — it currently matches a fixed list of classichallucinations ("you", "thank you") and would extend naturally.
(1) is the real fix; (2) and (3) are defence in depth for when it leaks.
Note
#319 (persist the dictation) would have made this self-evident: the bubble would have shown nothing
captured, then "Coder Lead" appearing from the final pass. Right now the two are indistinguishable
from the outside, which is why it reads as the agent imagining things.
Verification