Skip to content
C.W.K.
Lesson 05 of 05 · published

Detected, Never Silently; the Stranger's Voice Stays Private

~13 min · interpreter, invariants, privacy, audio

Level 0First Word
0 XP0/47 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

Two rules this lane bends

Spark carries two invariants about speech, and both come from how Dad actually uses it. The interpreter bends both, by Dad's ruling, and only inside this lane. How an exception is fenced matters as much as the exception itself, so this lesson reads the fences.

Invariant 7: chosen, never silently detected

Everywhere else in Spark, a speech language is chosen. Dad's dictation follows his Korean or English pick, the family rule. When he holds out the reply mic for someone's answer, it listens in the scene's language unless he switches it. The reply route will not even accept a recording without a language: a test sends one with none and expects HTTP 422. A detector that guesses wrong without saying so produces confident nonsense, and nobody at the table can tell.

The interpreter cannot work that way. Anyone at the table may speak, in any order, so nobody can pick the language in advance. Dad's ruling allows detection here and only here: Scribe names the language, and the direction that implies is shown before any translation and can be flipped with one tap. The architecture sums it up in three words: detected, never silently.

Each part of "never silently" lives in code:

  • Shown first. The engine publishes exchange.direction before the draft starts, so the chip is on the card before any translated word.
  • Readable by both people. The chip names each language in its own script, through the browser's Intl.DisplayNames: Español → 한국어.
  • Accountable. The direction records what decided it: Scribe, the script, or Dad.
  • Reversible. One tap posts a flip, and the same words are read the other way.
  • Fenced. Spark's transcription binding reserves a missing language for this lane in its contract, the reply route cannot send one, and any code it is given must look like a language code.

Invariant 8: Pippa's voice only on Dad's tap

Normally Pippa's voice plays for a card only when Dad taps it. He prepares cards before calling anyone over, and a voice that starts by itself in a quiet shop speaks to nobody. In a live exchange the person is already there, waiting. So Dad ruled that in the interpreter, a card made from his words plays at once, with a mute toggle.

The kit's speak method, in the first excerpt, shows the fences. It returns at once if Dad muted the card. It plays only when the exchange's speaker was Dad and the card has a recorded voice, so a relay meant for Dad's eyes is never read aloud. And while Pippa speaks, the microphone is deaf, and stays deaf for 400 ms after she stops. Without that tail, the room's echo of her voice would arrive at Scribe as the other person speaking, and the card would start interpreting itself. Dad's mute also survives his Clear: wiping the card does not unmute it.

Principle: Scope an exception to the condition that justifies it, and write the condition beside it. "The person is already there" licenses auto-play in one lane; it licenses nothing anywhere else.

Where a stranger's voice lives

A recording of the waiter is a stranger's voice, captured without a release form. Here is exactly what happens to it.

  • It is kept, privately. The card uploads each utterance's recording beside it, as recording-dad when Scribe heard Korean and recording-other otherwise. It becomes media of the sitting in Spark's media store, next to the veiled log, in Dad's private record.
  • Only two roles pass the door. cwkPippa's recording route accepts those two roles and nothing else, so an interpreter recording cannot be filed as a photo or an attachment.
  • Nothing outside Spark reads it. At design time, Beacon, Recall and the mobile video pipeline were checked and have no Spark media reader.
  • No voice is embedded. The speaker ear, cwkPippa's check of whether a voice is Dad's, never hears an interpreter recording.
  • The audio takes no detour. On a working socket, sound goes from the device straight to ElevenLabs, as voice mode does, through no relay of the family's.
  • A private chat gets no Talk. A card opened from a private conversation has no interpreter at all, because a sitting is kept in Spark and a private chat leaves nothing anywhere.

So the stranger's voice is kept, and kept in one place only. The reason to keep it at all is practical. When the phone's socket drops mid-sentence, the card goes on recording, and the engine transcribes the kept file once the link returns, with the batch model naming the language. In that path the recording goes up first and is filed as recording-other, because nobody knows who spoke until Scribe has heard it.

Voice Mode Quest's Whose Voice track teaches the speaker ear itself: how voice mode learned to tell Dad's voice from everyone else's.

Code

cwkSpark, frontend/src/kit/kitInterpreter.ts (vendored from cwkSiblingKit): the auto-play exception and its fences (verbatim, lines 505-517)·typescript
  /** Pippa's voice plays at once on a card made from Dad's words (Dad's ruling
   * 2026-10-23), unless he muted it. The mic is deaf while she speaks. */
  private speak(cardId: string): void {
    if (this.state.muted || !cardId) return;
    const exchange = this.state.exchanges.find(item => item.refinedId === cardId);
    if (!exchange?.voice?.length || exchange.speaker !== 'dad') return;
    this.deafUntil = Number.POSITIVE_INFINITY;
    this.set({ playing: true });
    void this.transport.playVoice(exchange.voice).catch(() => {}).finally(() => {
      this.deafUntil = Date.now() + ECHO_TAIL_MS;
      this.set({ playing: false });
    });
  }
cwkPippa, backend/routes/interpret.py: a recording is accepted in two roles only, and forwarded to Spark (verbatim, lines 187-206)·python
@router.put("/sessions/{moment_id}/recordings/{media_id}")
async def recording(
    moment_id: str,
    media_id: str,
    request: Request,
    role: str = Query(pattern="^(recording-dad|recording-other)$"),
    filename: str = Query(default="utterance.wav", max_length=200),
) -> dict[str, Any]:
    """The utterance's recording, kept with the sitting in Dad's Spark record
    only (a stranger's voice is never used for anything public)."""
    payload = await request.body()
    if not payload:
        raise HTTPException(status_code=400, detail="empty recording")
    return await _forward(
        "PUT",
        f"/api/moments/{_moment_id(moment_id)}/media/{_moment_id(media_id)}",
        params={"role": role, "filename": filename},
        content=payload,
        headers={"Content-Type": request.headers.get("content-type") or "audio/wav"},
    )

External links

Exercise

Find one value your software detects instead of asking for: a language, a time zone, a currency, a user's intent. For it, write down where the detected value is shown before anything acts on it, how a person corrects it in one step, and the exact scope in which detection is allowed. If any of the three is missing, add it. Then pick one piece of personal data your app stores about someone who is not your user, and list every reader of it.
Hint
Start the reader list from the storage, not from the features: grep for the table, the directory or the media role, and follow every path that opens it. A reader you cannot find is still a reader if it exists, so write down which repositories you searched and when.

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.