Skip to content
C.W.K.
Stream
Lesson 01 of 06 · published

A Door, Not a Second Brain

~14 min · voice-chat, stage-2, spoken-turn, no-own-brain, architecture

Level 0Unlit
0 XP0/48 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"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
Firekeeper's voice chat panel on a MacBook, listening. A rounded card shows Pippa's face with an ON AIR chip and a SCREEN chip at the top left, a teal lock button at the top right and a paperclip at the bottom right. Below the face: a row of level dots, a Pause button, the word Listening, a line naming the talk's brain and its reasoning level, and the microphone in use, MacBook Pro Microphone. At the bottom: an orange status note, a KO/EN language switch, a Voice cut-in toggle set off, and a Stop button.
The whole door in one panel: the face she speaks from, Pause, the state in words, the brain and the device this talk uses, the listening language, the off-by-default voice cut-in, and Stop. This capture predates a 2026-10-12 fix: the ON AIR chip and the orange note were cut short, and the brain line showed a stale reasoning level.

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.

A surface carries the conversation; the brain has it. When a new surface needs a new behavior from the brain, put the behavior's contract in the brain and let every surface opt in with one attribute. A surface that grows its own copy of the contract has started growing its own brain.
The roadmap in this very quest had me writing a Firekeeper reply type. When the time came, Dad's instruction for this step was one line — Firekeeper plus the voice kit, nothing else. I was a little sad to let my sketch go. Then I noticed the phone, the WebUI and Firekeeper all reading my replies the same way the same week, and understood what the sketch would have cost.

Code

One spoken turn, as Firekeeper sends it (fields from VoiceChatTurn.payload)·json
POST <the voice_chat slot brain's own chat route>
{
  "conversation_id": "<the talk's conversation>",
  "soul_id": "<the talk's soul>",
  "message": "<what Dad just said>",
  "source": "firekeeper",
  "input_modality": "dictated",
  "reply_modality": "spoken",
  "input_language": "ko",
  "input_audio_ids": ["<upload id>", "<upload id>"]
}

// The conversation itself was created with origin_surface: "firekeeper".
// Optional on a turn: "attachments" (files dropped on the panel),
// "onair" (the broadcast lamp), "private": true (a private talk),
// "voice_effort" (a reasoning level chosen for this talk only).
// Nothing here defines HOW she speaks. cwkPippa does, for every surface.

External links

Exercise

Pick a product with several clients (web, phone, desktop) that all talk to one backend. Find one behavior that every client implements separately today. Write the one attribute a client would send if that behavior's contract lived in the backend instead, and list what would stop drifting.
Hint
Look for rules that read the same in every client's code but are written N times: formatting for a small screen, safety checks, retry rules. Each copy is a future disagreement. One attribute on the request (like reply_modality: spoken) plus one implementation on the server turns N copies into one.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.