Status: Accepted
Channel live delivery follows durable commit
For #141, the current Channel message log is the durable authority. A committed Channel message becomes visible to other clients only after its append succeeds. The Host then publishes a process-local notification to subscribers of that Channel. An Orchestrator channel_send call may also have a presentation-only draft while its arguments stream; the draft is never a Channel message, Source Event, Inbox input, or second live-message store.
The Client first reads a Channel snapshot through botharness/channelMessages, including a monotonically increasing Channel revision. While that Channel is selected, it keeps one authenticated SSE connection at /api/botharness/stream. Each channel/message frame contains the committed message and revision. The Host replays committed rows after the requested revision when the stream opens, including reconnects via Last-Event-ID. The Client ignores duplicate revisions and message IDs, and re-reads the authoritative snapshot on a revision gap or invalid cursor. Local send reconciliation is immediate and does not wait for Orchestrator or Assignment work.
For a live PersonaBot reply, the Host globally observes DSH’s process-local agent/assistant-stream for the active Orchestrator Session and uses DSH’s shared BlockAssembler to project only explicit channel_send tool-call arguments. A draft is scoped to an LLM attempt and Channel; the Host checks Channel access, tolerates incomplete JSON, and abandons an inbound preview before publishing it in a different explicit Channel. Assignment output and ordinary Orchestrator final text never enter this path. The Host broadcasts channel/draft, channel/draft-settled, and channel/draft-abandoned with an attempt ID and a process-local monotonic draft revision; a reconnect receives a full transient baseline. Draft revisions are separate from durable Channel message revisions and are never SSE replay IDs. The Client discards previews and re-reads committed history on a gap, and batches streaming Markdown updates to an animation frame. A Host restart intentionally cannot replay drafts from Channel history.
Draft consumers have three classes. Presentation may show or discard the preview freely. Speculative consumers may act early only when the action can be interrupted and fully rolled back; they must handle abandonment, retargeting, and a changed attempt. Durable or irreversible consumers—including TTS publication, Bot Inbox admission, notifications with external effects, and audit history—must wait for the committed Channel message. A draft never becomes a Channel message, Source Event, Inbox input, or second live-message store.
Provider spike (2026-09-22, #144): an isolated real-model DM run produced 16 increasing body previews from channel_send tool-call deltas, one settlement, zero abandonments, one contiguous draft revision sequence, and a 31-character final preview. Every observed body was a prefix of the next. The tracker also rejects a non-dense DSH chunk index, but prefix monotonicity is an observation of this provider run, not a cross-provider guarantee; presentation must accept replacement or abandonment. The same run committed one Bot reply and displayed it in the authenticated DSH Client.
The SSE route is registered through DSH Connection Fetch with the full /api/… pathname; it does not intercept the API gateway’s single /api RPC slot. This keeps unary commands and reads on the existing Typert bridge. Channel authorization remains the current authenticated local-profile boundary; per-actor Channel access controls follow the Messaging authority in later slices.
When #46 moves Channel facts to the operational database, the transaction commit replaces the NDJSON append as the publication boundary, and the revision/replay contract must follow the new authority without dual writes. Presence, activity, and unread projections are not channel/message frames.
Update (2026-09-22, #143): channelTimeline is now the Client’s initial and recovery read model, replacing the bounded channelMessages snapshot described above; the older endpoint remains for compatibility. ADR-0061 records the opaque cursor and visible-window policy. The commit-before-publish SSE boundary in this ADR is unchanged.