coder / coder/ai-sdk

chatd ask: wire-level correlation between a queue entry and its materialized message

Open
#119 1 comment 0 reactions 0 assignees View on GitHub
accepted enhancement
Dominant language
TypeScript
Stars
3
Forks
0
Avg merge
29m
Merged PRs (30d)
127

Description

## 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:

1. **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;
2. **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_update`s 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`](https://github.com/coder/mux) • Model: `anthropic:claude-fable-5` • Thinking: `xhigh`_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.