Context
#114 shipped a client-side attribution gate for queued submissions: when POST /chats/{id}/messages returns queued: true, the SDK withholds attribution until the queue entry materializes as the turn's own user message. Because nothing on the wire links queued_message.id (queue-entry id space) to the chat_messages id it eventually becomes, the gate correlates by:
- structural content equality — the queue entry's
content and the materialized message's content are produced by the same server-side parse/strip (db2sdk.chatMessageParts), so a canonicalized comparison is exact today;
- FIFO confirmation — a matching user
message snapshot becomes the anchor candidate, confirmed when the next queue_update (which carries the full queue) no longer lists our entry; a candidate observed while our entry is still listed is discarded as an earlier identical-content entry's materialization.
Residual ambiguity (why a wire correlation is still wanted)
Two entries with byte-identical content from concurrent clients can still be mis-anchored in one window: a reconnect that spans both promotions replays both user-message snapshots but only the connect-time queue state — the per-promotion queue_updates that would have disambiguated them are collapsed into one. The gate then anchors on the newest matching snapshot, which may be the other client's twin. Content matching is also inherently coupled to the server's content encoding staying replay-stable.
Ask
Expose the correlation chatd already has in hand at promotion time (FinishTurn/FinishInterruption/PromoteQueuedMessage insert the message and delete the queue entry in one transaction):
- Preferred: stamp the materialized user message (or its
message stream event) with the queue-entry id it came from — e.g. queued_message_id on codersdk.ChatMessage or on the stream envelope; or
- allocate and return the future message id in
CreateChatMessageResponse.queued_message; or
- extend
queue_update with the promotion pairing (queued_message_id → message_id).
Any of these lets the SDK anchor exactly, delete the content-matching heuristic, and close the identical-twin window.
Downstream context
coder/tss runs many turns against long-lived chats where corrective follow-up turns are submitted precisely while the previous run is dying — the population where queued submissions and identical retried prompts are common.
Generated with mux • Model: anthropic:claude-fable-5 • Thinking: xhigh
Context
#114 shipped a client-side attribution gate for queued submissions: when
POST /chats/{id}/messagesreturnsqueued: true, the SDK withholds attribution until the queue entry materializes as the turn's own user message. Because nothing on the wire linksqueued_message.id(queue-entry id space) to thechat_messagesid it eventually becomes, the gate correlates by:contentand the materialized message'scontentare produced by the same server-side parse/strip (db2sdk.chatMessageParts), so a canonicalized comparison is exact today;messagesnapshot becomes the anchor candidate, confirmed when the nextqueue_update(which carries the full queue) no longer lists our entry; a candidate observed while our entry is still listed is discarded as an earlier identical-content entry's materialization.Residual ambiguity (why a wire correlation is still wanted)
Two entries with byte-identical content from concurrent clients can still be mis-anchored in one window: a reconnect that spans both promotions replays both user-message snapshots but only the connect-time queue state — the per-promotion
queue_updates that would have disambiguated them are collapsed into one. The gate then anchors on the newest matching snapshot, which may be the other client's twin. Content matching is also inherently coupled to the server's content encoding staying replay-stable.Ask
Expose the correlation chatd already has in hand at promotion time (
FinishTurn/FinishInterruption/PromoteQueuedMessageinsert the message and delete the queue entry in one transaction):messagestream event) with the queue-entry id it came from — e.g.queued_message_idoncodersdk.ChatMessageor on the stream envelope; orCreateChatMessageResponse.queued_message; orqueue_updatewith the promotion pairing (queued_message_id→message_id).Any of these lets the SDK anchor exactly, delete the content-matching heuristic, and close the identical-twin window.
Downstream context
coder/tss runs many turns against long-lived chats where corrective follow-up turns are submitted precisely while the previous run is dying — the population where queued submissions and identical retried prompts are common.
Generated with
mux• Model:anthropic:claude-fable-5• Thinking:xhigh