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.

Original synthetic consumer acceptance table; no stream or action executed
Observed sequence or raceExpected consumer stateEffect rule
Input deltas already form valid-looking JSON; block still openPREVIEWNo dispatch
Normal finish, expected tool request, valid authorized proposalVALIDATED_PROPOSALEligible for separate commit only
Block closes, then connection drops before message stopIncomplete attemptNo dispatch; do not infer completion
Final marker arrives with max_tokensHeld as truncated for this fixtureNo dispatch
Error, followed by a late completion callbackINVALID remains terminalLate callback cannot revive attempt
Cancellation accepted before commit; remaining deltas arriveABORTED remains terminalIgnore late proposal callbacks
Same trusted local delivery envelope handled twiceNo second state transitionDeduplicate envelope, not matching text
Effect commits, then display connection dropsCOMMITTED on server; client uncertainReconcile 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.

  1. Claude: Streaming messages
  2. MDN: Using server-sent events
  3. Claude: Stop reasons and fallback