Backpressure
Backpressure is what the protocol does when one side produces faster than the other side consumes. The server uses bounded queues at three points — transport ingress, request processing, and per-connection outbound writes — and the contract at each is explicit. Your client has one duty on each side: back off when the server says it is overloaded, and catch up by cursor when the server says it dropped push deliveries.
Ingress: reject, don’t buffer
Section titled “Ingress: reject, don’t buffer”When the server’s ingress or processing queue is full, new requests are
answered immediately with the typed
overloaded error rather
than being buffered. That error is retryable: retry with exponential backoff
and jitter. Client notifications —
initialized is the
only one today — are never the cause of ingress saturation.
The command plane has its own admission queue on top of this, which reports
a distinct typed
backpressured error
carrying the queue capacity in its data. Treat the two the same way
operationally — back off, then retry — but they are different kinds and
different codes; branch on data.kind.
Egress: gap, don’t drop silently
Section titled “Egress: gap, don’t drop silently”Each connection has a bounded outbound notification queue. When your client
reads too slowly and that queue fills, the server does not stall the session
and does not silently discard events. Instead it collapses the queued backlog
per subscription into a single gap notification — view/gap for session-view
subscriptions.
A view/gap means exactly this: events after your last delivered cursor were not
pushed to you, and you must catch up with a cursor read
(view/page) before treating
your local state as current. Three properties make gaps safe to build on:
- Nothing is ever dropped without a gap marking the hole.
- The gap is a push-delivery statement only — the underlying durable log is untouched, so a cursor read recovers everything.
- The gap notification itself is protected (below), so it cannot be lost to the same congestion it reports.
After a view/gap, splice-fill the missing range or re-anchor before rendering the
fold as current; that duty is a client MUST.
The protected set
Section titled “The protected set”Some frames are never dropped, never gapped, and cannot be opted out; they ride a separate small reserved queue:
- responses and error responses;
- server-initiated requests and their settlement notifications —
approval/requestanduserInput/request, the JSON-RPC request frames that present an approval or a user-input prompt, together with theapproval/resolvedanduserInput/settlednotifications that settle them. Theapproval/requestedanduserInput/requestedview events are ordinary notifications — gap-able, recoverable by cursor — not members of this set; view/gapand its raw-log analogue, and the deprecation notice.
The design consequence for your client: correctness-critical frames arrive or the connection is dead — you never need a timeout-based guess about whether an approval resolution was dropped in transit.
This pairs with the opt-out mechanism from the handshake: a client may suppress named notification methods for the connection (for example the high-volume streaming delta events), but the protected set can never be opted out.
Slow-consumer disconnects
Section titled “Slow-consumer disconnects”The disconnect policy is per transport:
- stdio — the transport you build against in v1 — the server never unilaterally closes the pipe; if even a protected write would block indefinitely, the server keeps the write blocked, because the single client owns its own liveness. A wedged client therefore wedges itself, not the session.
- unix socket / websocket (deferred transports): a connection whose protected queue stays full past a write timeout is closed; ordinary slowness never causes disconnect — only inability to deliver protected frames does. The disconnected client reconnects, re-initializes, and resumes by cursor.
What to build
Section titled “What to build”- Drain stdout continuously on a dedicated reader; never block your frame reader on your own UI or disk work, because the stdio server will not sever a wedged pipe for you.
- Handle
overloadedwith jittered exponential backoff on the request path. - Treat
view/gapas a state machine input, not a warning: mark the fold stale, page the missing range by cursor, and only then resume rendering as current. - Never rely on receiving an opted-out or unprotected notification for correctness; anything you cannot afford to miss is either protected or recoverable by cursor read.