Expand description
Daemon-side full-duplex conversation-floor state machine (ADR-068 D1).
DuplexChannel is the actuator of ECC floor verdicts, never a decider of
the floor. ECC (the TalkModeLoop) decides;
this machine executes. Every ad-hoc hold that lived inline in the old
half-duplex TalkModeController (mic-drain, prebuffer, barge grace, ack-cache)
becomes one transition rule here, in the daemon (ADR-068 §Context thesis 1).
§The actuator/decider boundary, enforced by construction (ADR-068 D1, plan §2)
The two mutators are split by the type they consume:
observetakes aStreamObservation— something the daemon derived from the media stream (VAD onset, endpoint, ERL, drain, heartbeat) — and returns(Option<DuplexImpulse>, Vec<EdgeCommand>). It may perform daemon-local transitions and emit floor-pressure impulses, but its input and output types contain noFloorVerdict, and aDuplexImpulsecan only carry anImpulseType(TurnClaim/EndOfUtterance) — there is noGrantvariant. Soobserveis structurally incapable of granting the floor.applyis the only method that takes aFloorVerdict(the type that carriesGrantUser/GrantAgent), so it is the only path that can perform an ECC-authoritative floor transition.
A reviewer verifies “the daemon never decides the floor from the stream” by reading the two signatures — not by auditing the bodies.
§Resolved D1-table ambiguity: Overlap entry vs. the ERL gate
The D1 row Speaking | onset above gate, ERL confirms → Overlap reads, in
isolation, as if a high ERL were required to enter Overlap. That is not
how this machine implements it, because the required self-cancel regression test
(plan §3) drives a low-ERL onset during Speaking and asserts Overlap is
entered and then reverted by an ECC DenyBarge. The two are reconciled per the
ADR’s own stated mechanism (ADR-068 D1 “The self-cancel fix”): barge-in “becomes
a runtime floor decision”, i.e. the ERL discriminates genuine barge from AEC
self-echo at the floor read, not at Overlap entry. So:
- Any VAD onset during
Speaking(the energy gate is upstream, in the edge/daemon VAD) enters the provisionalOverlapand emits aTurnClaimcarrying the last measured ERL confidence. - ECC weights a low-ERL
TurnClaimdown (compute_urgency, ADR-062 D4, Phase 1) and returnsFloorVerdict::DenyBarge→ back toSpeaking; a genuine high-ERL onset yieldsFloorVerdict::GrantUser→Listening+ cancel.
This keeps the decider in ECC and makes the self-cancel fix expressible without
the machine judging the barge itself. Overlap is explicitly provisional
(ADR-068 D1 states table).
Structs§
- Duplex
Channel - The daemon-side floor state machine (ADR-068 D1). One per conversation. Pure
logic — no I/O, no async: it turns stream observations and ECC verdicts into a
state trajectory plus concrete
EdgeCommands. - Duplex
Impulse - A floor-pressure impulse the daemon derives from the media stream and lifts onto
the ECC
ImpulseQueuethat feeds theTalkModeLoop. Named to avoid colliding withcrate::impulse::Impulse; the plan (§Phase 0) calls this the derived “Impulse”. It can only carry anImpulseType(no grant variant exists), which is what makesobserveunable to decide the floor.
Enums§
- Duplex
State - The seven floor states (ADR-068 D1 states table). The edge holds only the
reflex shadow of these (see
clawft-channelsedge_reflex); the authoritative machine is daemon-side. - Edge
Command - A concrete command down the control lane to the edge (ADR-068 D3). Floor
verdicts reach the edge only as these — the edge never sees a
FloorVerdictor an impulse code. - Floor
Verdict - The ECC-authoritative floor verdict from the
TalkModeLoop. This is the only type carrying a floor grant, andapplyis the only method that consumes it — the structural core of the actuator/decider boundary (ADR-068 D1, plan §2). - Media
Payload - A media-lane payload carried by
EdgeCommand::Play(ADR-068 D3): PCM when the edge offers no local codec, rendered text when it advertises local TTS. - Payload
Kind - Which payload rides the media lane — the axis that decides which transitions are reachable (ADR-068 D2/D6). The machine is identical; text simply cannot reach the audio-only states.
- Stream
Observation - Something the daemon derived from the media stream (ADR-068 D3: “the daemon derives impulses from the media stream itself”). None of these can grant the floor — they are observations, not verdicts.