The direct answer
Keep streamed fragments in a preview state that cannot trigger a business effect. Require protocol completion, an accepted stop reason, full proposal validation, and current authorization before a separate commit transition. Cancellation, errors, and disconnects must resolve against that transition’s authoritative state rather than the last text shown on screen.
Choose one stream contract and a narrow workload
Suppose a fictional application asks for a client-tool proposal to change an invented work item’s display label. The tool is a proposed local mock, not a connected service. The interface may show progress, but no streamed fragment may change the work item. Server-executed tools are outside this exercise.
This article uses only the Claude Messages streaming protocol: message start, indexed content-block events, message-level deltas, and final message_stop. Tool-input deltas contain partial JSON strings. A block’s stop event closes that block; it is not approval to run a tool. These event names do not describe every provider’s protocol.
Treat transport framing and application completion separately
MDN’s SSE guide describes text events separated by blank lines. A network read chunk need not be an application event: buffer and decode according to the selected protocol before processing its data. Apply byte, event-count, and duration bounds so an unfinished preview cannot grow indefinitely.
MDN also describes automatic reconnection for browser EventSource. That behavior does not establish reconnection, replay identifiers, or resumability for a POST request handled by a model SDK. Define recovery for the actual client you use. A closed socket alone is not the Messages completion marker, and HTTP success does not prevent an error event later in the stream.
Accumulate content by block index and event type. Keep preview rendering separate from the authoritative proposal buffer. Do not interpret a syntactically complete JSON prefix, a visible success sentence, or an SDK’s incremental parsed object as permission to act.
Name the states the consumer is allowed to enter
For this original design, PREVIEW means content is still being assembled. MESSAGE_COMPLETE requires a valid complete event sequence with all relevant blocks closed and the final marker received. VALIDATED_PROPOSAL adds full parsing, schema and business checks, and authorization for the current actor and work item.
Claude’s stop-reason documentation distinguishes natural completion, tool requests, and token-limit truncation. This client-tool fixture accepts only the expected tool_use reason and the permitted tool shape; even a well-formed object stopped by max_tokens is held. Receiving message_stop alone does not satisfy that application rule.
COMMITTING is a separate trusted transition after the proposal passes every gate. COMMITTED requires the responsible application’s authoritative effect record. ABORTED and INVALID are terminal for that attempt. An uncertain effect outcome needs reconciliation; it is not permission to submit the effect again.
Write event-sequence acceptance cases before implementation
In this worksheet, “normal finish” means the expected start, content-block lifecycle, final stop reason, and message stop. “Commit” refers to a hypothetical application transition after validation, never an event sent by the model. Every row is proposed and UNRUN.
| Observed sequence or race | Expected consumer state | Effect rule |
|---|---|---|
| Input deltas already form valid-looking JSON; block still open | PREVIEW | No dispatch |
| Normal finish, expected tool request, valid authorized proposal | VALIDATED_PROPOSAL | Eligible for separate commit only |
| Block closes, then connection drops before message stop | Incomplete attempt | No dispatch; do not infer completion |
| Final marker arrives with max_tokens | Held as truncated for this fixture | No dispatch |
| Error, followed by a late completion callback | INVALID remains terminal | Late callback cannot revive attempt |
| Cancellation accepted before commit; remaining deltas arrive | ABORTED remains terminal | Ignore late proposal callbacks |
| Same trusted local delivery envelope handled twice | No second state transition | Deduplicate envelope, not matching text |
| Effect commits, then display connection drops | COMMITTED on server; client uncertain | Reconcile recorded status; do not repeat effect |
A valid preview is intentionally included so a test that blocks everything cannot appear correct. The positive case must reach review-ready state without claiming the effect has happened.
Handle duplicates and cancellation at the right boundary
A content-block index identifies a block, not every delta within it. Identical text fragments may be legitimate repeated content; deleting repeated strings can corrupt the proposal. If an internal queue can redeliver callbacks, give its own trusted envelopes attempt and delivery identities. That is an application mechanism, not an assumed provider event-ID guarantee.
For a detected protocol violation or ambiguous assembly, hold the attempt instead of guessing which bytes to discard. Handle documented ignorable or future events gracefully, but never let an unknown event grant completion or action authority. Unsupported content that affects the proposal needs an explicit compatibility decision.
Serialize cancellation and commit against the same authoritative attempt state. If cancellation wins before commit, later events cannot revive that attempt. If commit already won, report the recorded outcome; a Stop button cannot retroactively undo it. If an external effect’s outcome is unknown, reconcile under that effect’s own recovery rules. Stopping generation is a different operation from reversing an application change.
Expose useful status without overstating the evidence
Use distinct interface messages: “Preview only,” “Proposal ready for review,” “Cancelled before commit,” and “Outcome being reconciled.” Avoid displaying “Done” merely because text stopped arriving. A reconnecting interface should retrieve the application’s recorded attempt status rather than reconstruct success from partial text.
A future local test could feed invented event records into a consumer with a recording adapter, assert each state transition, and schedule cancellation immediately before and after commit. Record expected transitions first. Keep sensitive payloads out of traces, and verify proposal validation separately from stream assembly.
All checks here are UNRUN: no provider request, model output, live stream, tool dispatch, or business action occurred. Boundary is a prelaunch commercial model router with planned request-path policies; this is not a shipped streaming consumer, transaction engine, or cancellation guarantee. The existing eight mock-policy tests do not evaluate this state machine.
Primary sources
Official references supporting the guidance. Examples in this guide are synthetic and are not reports of a production incident.