"The quest said Stage 2 would need its own reply contract. It got one — in the brain's house, not in Firekeeper's."
What the Roadmap Predicted
Track 1 drew a hard line between dictation (text to insert) and voice chat (words to hear), and sketched the second as a Firekeeper-owned type: a VoiceChatReply with textForTTS and shouldContinue. The line was right. The sketch was not what shipped. On 2026-09-26 Stage 2 landed as something narrower and stronger: a door. Firekeeper opens a hotkey door onto cwkPippa's own spoken turn and owns nothing of the conversation except the loop that carries it.
The Contract Lives Where the Brain Lives
Every turn Firekeeper sends is an ordinary cwkPippa chat turn marked reply_modality: "spoken" — the same attribute the WebUI's voice mode, the phone, the watch and the sidekick send. The rules that make a reply sound right aloud (short, interruptible, no headings or tables, a line before a tool) are enforced once, inside cwkPippa, for every surface at the same time. If Firekeeper had grown its own voice_chat_reply contract, the family would now keep two definitions of "how Pippa speaks" and they would drift. Putting the contract in the brain's house is what kept one Pippa.
hotkey (⌃⌥⌘V)
-> listen cwkPippa /api/stt/realtime-session (lesson 2)
-> one turn the brain's chat path, reply_modality: "spoken"
-> speak /api/tts/prepare + /api/tts/stream/{ticket} (cwkBellows)
-> report /messages/{id}/heard how far Dad actually heard
-> listen again ... a quiet minute ends the talk
What Firekeeper Does Own
The door is not thin. Firekeeper owns the parts only a Mac app can own: the global hotkey and the menu items (Talk with Pippa, Continue Voice Chat), a small non-activating panel that floats over whatever app is in front, the microphone and speaker this Mac uses, the keys that pause and cut in, and the loop's timing — when an utterance is over, when to listen again, when a quiet minute ends the talk. The listening, speaking and reading machinery comes from the family's voice kit (CwkKitVoice and CwkKitMedia: RealtimeListener, VoiceAudioOwner, HeardPosition, ReplyReading), the same parts the phone was proven on. The loop itself is VoiceChatController, nearly two thousand lines, and every one of them is about carrying a conversation, none about having one.
A Talk Is a Conversation Like Any Other
A talk is a normal cwkPippa conversation with origin_surface: firekeeper. That one decision pays off twice. Dad can start a talk at his desk and continue it on the phone or in the WebUI, because there is nothing Firekeeper-specific to migrate. And the talk's brain is chosen the way every other surface's is: Firekeeper resolves its own central slot, voice_chat, from the family's Brain Control Plane when the talk starts. No assignment means no talk — the panel says why instead of guessing a brain. Before that slot existed, Settings showed one brain while the talk quietly ran another; a visible, central assignment is how that stopped.
The Offline Half Is Honest About Being Missing
The plan also promised a Local Mini voice chat (Ollama plus an offline TTS). It is not built. When no Pippa Full connection is selected, the talk refuses with "Voice chat needs cwkPippa" rather than sliding into a weaker local brain. That is the offline track's rule applied to a feature that does not have an offline path yet: say so, never fake it.