The SDK's inbound ordering does not automatically extend to an application's UI
executor. A notification handler may have finished enqueueing an update while
the application has not yet applied it. Awaiting a request with block_task()
on another task does not drain that application queue.
Use one FIFO queue with one sequential consumer for:
- Notifications, enqueued by typed connection handlers.
- Response results, enqueued by
PreparedRequest::on_receiving_result. - Connection closure, enqueued by
Builder::on_close.
Apply updates in the consumer and expose a response result only when the consumer reaches that event. For v2 resume, this makes preceding replay visible to application code before it observes the resume result. Do not spawn a separate application task for each event unless that executor also preserves their application order.
The compiled ordered application-dispatch cookbook recipe
demonstrates this with an application callback that need not be Send.
Connection handlers retain only event senders, not the application view or an
owning connection. This is an integration pattern, not a new session state
machine or coordinator.
Use prepare_request(...).on_receiving_result(...) to install ordered handling
before publishing the request, even from another task. Eager
send_request(...).on_receiving_result(...) only holds dispatch if registration
wins the race with response routing. The barrier is not retroactive:
already-routed responses and later routing through a retained ResponseRouter
do not hold subsequent wire traffic. See Sending Requests
for the additive API and publication timing.
The callback should enqueue the result and return. Do not await another
inbound response or notification on that connection inside the callback.
That traffic cannot be dispatched until the callback finishes, causing a
deadlock even on a multithreaded runtime. Perform work that needs later traffic
in the application consumer, the connect_with foreground, or a task started
with ConnectionTo::spawn.
If another application task needs a projection-drained acknowledgement, the consumer can send it after applying the response marker. The SDK callback should not wait for that acknowledgement.
See the SDK ordering contract for the full callback and response-routing semantics. Queue capacity is an application decision: the recipe uses an unbounded queue, while a bounded queue requires a backpressure policy that cannot deadlock inbound dispatch.
Clean incoming EOF follows already-received notifications and timely ordered
wire responses. Putting on_close on the same queue lets the consumer apply
those events before marking the connection closed.
EOF fails pending SDK requests before running close callbacks, but their
synthetic error callbacks are not ordered wire-response barriers and can
enqueue results after the closure event. The application should settle its
outstanding operations at Closed and tolerate late completions, rather than
waiting for every callback before accepting closure. Transport and handler
errors still propagate from the connection future; this pattern does not turn
all failures into clean EOF.
The queue preserves dispatch order, not a relationship the protocol does not
provide. A v2 prompt response acknowledges acceptance. Session running and
idle updates describe foreground state, not a specific prompt's completion,
and background updates may continue while idle. Keep these as separate
application events; do not assign the next Idle to a prompt without an
additional protocol guarantee. See high-level v2 sessions.