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

One Call, One Turn

~17 min · arbitration, floor-control, multi-device, concurrency, python

Level 0Idle Phone
0 XP0/45 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

Three bodies, one call

With three bodies in the house, one "코델리아 피파" can be heard three times. Each body hears the phrase on its own device and sends its own turn to Oriel, and none of them knows the others exist. That is deliberate: a body is a part, chosen by its UDID in Oriel's registry, and nothing in the app names another phone. So the judgement has to live where every turn arrives. At 01:19 on 2026-11-03 Dad set the rule: several bodies in earshot of one call answer once. The first block is how Oriel writes that rule down, each constant beside the night that set it.

The Arbiter

The second block is Arbiter.contest. Wake turns heard within 2.5 s of each other are one call. When another body in the same place has its ears open (wake switch on, phase listening, hearing or sending), a turn waits 1.5 s so the others can arrive; with nobody else listening it waits for nothing. Then the earliest wins, and among wakes within 0.4 s of the earliest the loudest wins, by level: the loudest buffer that body heard while the turn was being heard, which travels with the turn. Equal loudness goes to the earlier wake. The decision is kept for 60 s, so a late turn of the same call gets the same answer instead of a new contest. The losers are never sent to Pippa.

The body supplies two of the inputs. The time it heard the turn and the turn's length give Oriel a capture window, and level comes from the ear: when a listening begins, the body records the ear's loudness and keeps the highest value it sees while the turn is heard, so the body nearest Dad's mouth usually takes a near tie. Every turn that goes through to Pippa's door is also written down with its window for two minutes (Arbiter.heard), which is what the overlap rule below reads. The winner's later turns inside its conversation carry no phrase of their own, so they reach Pippa's door as whatever opened the conversation, a wake or a tap.

A tap is never contested. It was meant for the body that was tapped, so it takes the floor outright and closes a conversation open on any other body in the room.

The floor

The Arbiter settles one call. It cannot settle two calls a minute apart. At 02:03 one phone was in a conversation with Dad, he said the phrase in the middle of a sentence, a second phone woke on it, and both answered. Dad: "답은 왜 둘 다 하는 거지?" (why are both of them answering?).

So Oriel holds a conversation floor for each place. The body whose turn went through holds it while the turn is at Pippa's door, and for its own silence window (session_s) after my answer. Meanwhile any other body's wake or conversation turn yields. A turn whose capture overlaps another body's taken turn (within a 0.3 s margin) yields too: one utterance, one turn. The hold ends early on the holder's own word: its session item with open: false, its cancel, or a status fresher than 25 s that shows no conversation. After an Oriel restart, with no record, the bodies' statuses say who holds it. The listening grant obeys the same floor: a body outside it gets "Another device took it" instead of a socket, so its audio never leaves it.

The floor then caught its own makers. At 17:10 three bodies heard one call, and each opened its conversation at once, as the body does, before Oriel decided who answers. Each then saw the others' fresh conversations as a held floor, and all three yielded. Nobody answered. Hence SAME_CALL_S: a conversation opened by a wake within 4 s of this call is the same call, not a floor. Three minutes later a sibling bug, two bodies yielding to one call said inside a conversation, made the floor ask each body for its own last wake.

Every rule holds within one place. At 02:12 Dad drew the line: a phone carried abroad is never silenced by a conversation at home, and two places' captures are never one utterance. Each body's registry record names its room (office, music-room, living-room), so bodies in different rooms never yield to each other.

Losing well, and losing to the Pippa app

The third block is the losing body's side. A note marked yielded makes it drop what it holds, clear the "Sending to …" step Oriel named before the contest (that step was never this body's), close its own conversation, and say "Another device took it" on the face. It does not argue and it does not retry. The note is a fact.

The house has one more ear. During a broadcast the Pippa app on Dad's iPhone hears everything he says and asks cwkPippa's addressing judge about each line (Coop 1104-25). A body's tap or wake turn claims its words at cwkPippa's door; when the iPhone already took the same words, the door answers 409 "Another device took it (…)", and Oriel records the body as yielded, exactly as if another body had won. Oriel holds no judge and keeps no words: the judgment and its log are cwkPippa's.

One honest line from the architecture doc: on the build that first ran voice mode's root, the wake contest between two bodies was not yet field-checked. The fixes above came from field bugs, not from a planned check.

Code

cwkOriel, backend/ears.py: the rules of one call and one conversation, each with the night that set it (verbatim, lines 92-116)·python
# Several bodies in earshot of one call answer once (Dad, 2026-11-03 01:19):
# wakes this close together are one call; a turn waits this long for the
# others; wakes closer than TIE_S are a tie the louder body wins.
ARBITRATE_WINDOW_S = 2.5
ARBITRATE_WAIT_S = 1.5
TIE_S = 0.4
ANOTHER_TOOK_IT = "Another device took it"
# One conversation at a time across every body (Dad, 2026-11-03 02:03: "답은
# 왜 둘 다 하는 거지?"): the body that holds it keeps the floor while a turn
# is with Pippa and for its own silence window after her answer; no other
# body's wake or turn is taken meanwhile. A body's own word (its status, or
# its "session" event) ends the hold early.
SESSION_DEFAULT_S = 60.0
# All of this holds among the bodies of one place (Dad, 2026-11-03 02:12: a
# phone he carries abroad is never silenced by a conversation at home, and two
# places' captures are never one utterance). A body's place is its registry
# record's ``place``; one with none is home.
STATUS_FRESH_S = 25.0
# Every body that hears the phrase opens its own conversation at once, before
# Oriel decides who answers; a conversation opened by a wake this close to the
# call is that same call, not a conversation already holding the floor (the
# 2026-11-03 17:10 field bug: three bodies heard one call and all yielded).
SAME_CALL_S = 4.0
# Two bodies' captures of one utterance overlap by at least this much.
OVERLAP_MARGIN_S = 0.3
cwkOriel, backend/ears.py: the contest, earliest first and the loudest among a tie (verbatim, lines 477-502)·python
    def contest(self, turn_id: str, body: str, heard_at: float, level: float, others_listening: bool,
                place: str = DEFAULT_PLACE) -> str | None:
        """None when this turn answers; else the body that took the call.
        Only wakes heard in the same place contest one another."""
        with self._lock:
            now = self.clock()
            self._candidates = {k: c for k, c in self._candidates.items() if now - c["at"] < 60}
            self._decided = [d for d in self._decided if now - d["at"] < 60]
            decided = self._decision(turn_id, heard_at, place)
            if decided is not None:
                return None if decided["id"] == turn_id else decided["body"]
            self._candidates[turn_id] = {"id": turn_id, "body": body, "heard_at": heard_at, "level": level, "at": now,
                                         "place": place}
        if others_listening:
            self.sleep(ARBITRATE_WAIT_S)
        with self._lock:
            decided = self._decision(turn_id, heard_at, place)
            if decided is None:
                group = [c for c in self._candidates.values()
                         if c.get("place", DEFAULT_PLACE) == place and abs(c["heard_at"] - heard_at) <= ARBITRATE_WINDOW_S]
                earliest = min(c["heard_at"] for c in group)
                contenders = [c for c in group if c["heard_at"] - earliest <= TIE_S]
                winner = max(contenders, key=lambda c: (c["level"], -c["heard_at"]))
                decided = {**winner, "at": self.clock()}
                self._decided.append(decided)
            return None if decided["id"] == turn_id else decided["body"]
cwkCordelia, clients/apple/CordeliaMobile/Sources/Ears/EarsModel.swift: the losing body lets go (verbatim, lines 432-452)·swift
  /// Oriel says what became of a turn.
  func took(_ note: TurnNote) {
    if note.yielded {
      // Another body has the conversation: this one drops what it holds,
      // closes its own and waits for the phrase again.
      stopListening()
      waitingSince = nil
      answerID = nil
      // Oriel named its "Sending to …" step before the contest: that step
      // was never this body's, so it does not stay in the status.
      stage = nil
      stageUnread = nil
      if phase != .listening { phase = .closed }
      if session != nil {
        endSession(note.line ?? "Another device took it")
      } else {
        self.note = note
        Task { await reconcile() }
      }
      return
    }

External links

Exercise

Imagine two of your devices both respond to the same voice command. Write a server-side arbiter: requests heard within a window are one event, a request waits briefly only when another device could have heard it, the earliest wins and the loudest breaks a near tie, and the decision is remembered so a late arrival gets the same answer. Then add a floor that keeps the winner in charge for a while, and a test where three devices hear one command and exactly one answers.
Hint
Write the three-devices test first, with every device opening its own session the instant it hears the command. If your floor treats those fresh sessions as someone else's conversation, all three will yield, which is the bug Oriel shipped and fixed with a 4 s same-call window.

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.