"Dictation keeps the rule. The conversation crossed it — and wrote down why."
The Line This Quest Drew
Three tracks of this quest lean on one rejection: no cloud transcription. Dictation still honors it to the letter. WhisperKit or Apple's SpeechAnalyzer hears you on the Mac, and dictation audio never leaves as a file and is discarded once transcribed, apart from the bounded failure rescue track 8 describes. Voice chat does not honor it. When Dad opens a talk, the microphone streams his voice to ElevenLabs' Scribe v2 Realtime, a cloud model. That was his choice, made with the tradeoff in view, and the quest should say so plainly instead of letting "your voice never leaves your Mac" stand as if it covered every surface.
How the Audio Travels
Nothing is relayed. cwkBellows, the family's voice engine, holds the ElevenLabs account key and mints a single-use token. cwkPippa builds the complete socket address around that token: the language, the end-of-speech window, and keyterms — the names of the souls this soul may see, in Korean for a Korean session. Firekeeper's listener (RealtimeListener in the voice kit) then connects straight to ElevenLabs. The key never touches the Mac, and the audio passes through no server of ours on its way. The stream opens only after Dad opens a talk; between talks the microphone is closed.
Firekeeper --POST /api/stt/realtime-session--> cwkPippa
cwkPippa --POST /api/stt/realtime-token----> cwkBellows (holds the key)
cwkPippa <-- full socket URL: token + language + VAD window + keyterms
Firekeeper ==== mic audio, live ============> ElevenLabs Scribe v2 Realtime
<=== partial words, commits =====
Dictation never takes this path. Its ear stays on the Mac.
Why the Conversation Crossed the Line
The decision is recorded with its reasons. A talk needs partial words while Dad is still speaking, an end-of-speech window that cwkPippa's voice settings define (3 seconds by default, tunable per device), and keyterms so names come out right — 피파 was being transcribed as FIFA in 30 of 682 dictated turns before the Korean soul names rode the session. It also keeps dictation's local STT and cleanup lanes entirely out of the talk, so neither can slow or break the other. And the family runs one transcription vendor everywhere, so the WebUI, the phone and Firekeeper hear Dad the same way. Every one of those reasons is about the conversation. None of them applies to dictating into a text field, which is why dictation did not move.
A Commit Is Not the End of Speech
Track 3 taught how to fake streaming on top of a batch model. Here the model streams natively, and it has its own edges. Scribe commits a segment on its own after about 35.8 seconds of continuous talk, voice activity or not. The first loop took every commit as the end of the utterance, and Dad's demo went out mid-sentence. Now a commit is asked whether silence followed it: if a third or more of the window was loud, he is still talking, the piece is kept, and listening continues. After Scribe's quiet window, the loop also waits out the rest of Dad's window; if two chunks in a row come back loud (above three times the committed silence's floor and above 300 RMS), the turn stays open, and words that arrive within 2.5 seconds join it.
When the Realtime Model Drops Words
The second edge is worse because it is silent. On the WebUI, a 132-second count reached the text with 20 seconds of it missing: the realtime model had stalled inside a segment. The batch model wrote every word of the same recording in 3.3 seconds. So an utterance of 30 seconds or more is sent once more, as a file, to cwkPippa's batch transcription (/api/stt, never asked to keep the audio), and the batch text replaces the live words. If that call fails, the live words still go out. The direction of track 3 flips: there the batch model was the base and streaming was the illusion; here streaming is the base and batch is the ground truth you fall back on when it matters.