The contradiction
docs/cell-types.md describes the voice cell's interim path as backpressure by design: nothing is dropped, a slow listener delays the sender. The code says something narrower. VoiceIoShared::send_to hands each frame to the client task through a bounded DISPATCH_QUEUE (64) with try_send, and when that queue is full it gives the connection up — counter-based, deliberate, without a clock. So the colony-side lane really is backpressure (the router waits on a full mailbox), but the socket side is not: a client that cannot drain 64 frames loses its session and every frame after that.
Measured (2026-09-06, GH #600)
voice_t5_behaviour::backpressure_loses_nothing on a loaded host (loadavg 9–14 on 8 cores, three colonies live): 147, 159 and 204 of 500 interims reached the listener; the rest were never sent because the client task lost the race against the emitter and the session was discarded.
- The old test never measured backpressure at all: the listener mailbox ran at the default 1000 and swallowed 500 messages without the router ever waiting. The rewritten test (counter-based, mailbox 4, 50 interims, 5 green runs at 0.47–0.75 s under loadavg 14) proves the colony-side promise and stays below the queue that breaks the socket-side one.
The decision
Which contract is the voice cell's?
- "A client that falls behind is dropped, loudly, by count." Then
docs/cell-types.md and templates/voice/README.md say so, name the queue depth as the number that decides, and the drop becomes an error lane emission with error_code instead of a silent discard.
- "Backpressure all the way to the socket." Then
send_to awaits the queue (or the client task applies the same bounded wait the router does), a stalled client stalls the emitter, and the watchdog/timeout story for a stuck socket has to be written.
Either way documentation and code must say the same thing afterwards. Not to be built tonight; Marcus decides.
The contradiction
docs/cell-types.mddescribes thevoicecell's interim path as backpressure by design: nothing is dropped, a slow listener delays the sender. The code says something narrower.VoiceIoShared::send_tohands each frame to the client task through a boundedDISPATCH_QUEUE(64) withtry_send, and when that queue is full it gives the connection up — counter-based, deliberate, without a clock. So the colony-side lane really is backpressure (the router waits on a full mailbox), but the socket side is not: a client that cannot drain 64 frames loses its session and every frame after that.Measured (2026-09-06, GH #600)
voice_t5_behaviour::backpressure_loses_nothingon a loaded host (loadavg 9–14 on 8 cores, three colonies live): 147, 159 and 204 of 500 interims reached the listener; the rest were never sent because the client task lost the race against the emitter and the session was discarded.The decision
Which contract is the
voicecell's?docs/cell-types.mdandtemplates/voice/README.mdsay so, name the queue depth as the number that decides, and the drop becomes anerrorlane emission witherror_codeinstead of a silent discard.send_toawaits the queue (or the client task applies the same bounded wait the router does), a stalled client stalls the emitter, and the watchdog/timeout story for a stuck socket has to be written.Either way documentation and code must say the same thing afterwards. Not to be built tonight; Marcus decides.