Skip to content

fix: show promoted queued message in chat timeline immediately#23232

Merged
mafredri merged 1 commit intomainfrom
fix/promote-queued-message-missing-from-timeline
Mar 19, 2026
Merged

fix: show promoted queued message in chat timeline immediately#23232
mafredri merged 1 commit intomainfrom
fix/promote-queued-message-missing-from-timeline

Conversation

@mafredri
Copy link
Member

@mafredri mafredri commented Mar 18, 2026

Two issues caused the promoted message to never appear in the chat
timeline.

Problem 1: handlePromoteQueuedMessage discarded the ChatMessage
returned by the promote API, relying entirely on the WebSocket to
deliver it.

Problem 2 (root cause): even when the WebSocket delivered the
message (via upsertDurableMessage), the queue_update event in the
same batch called updateChatQueuedMessages, which mutated the React
Query cache. This gave chatMessagesList a new reference, triggering
the message sync effect. The effect found the promoted message in the
store but not in the (stale) REST-fetched data, classified it as a
"stale entry" (a code path designed for edit truncation), and called
replaceMessages, wiping the promoted message from the store before
it ever rendered.

Fix 1 (AgentDetail.tsx): capture the ChatMessage from the
promote response and upsert it into the store immediately, matching
the pattern in handleSend for non-queued messages.

Fix 2 (ChatContext.ts): track the fetched message array elements
across effect runs using element-level reference comparison. Only run
the hasStaleEntries / replaceMessages path when the message
objects actually changed (e.g. a refetch producing new objects from the
server), not when only an unrelated field like queued_messages caused
the query data reference to update. Element references work because
useMemo(flatMap) preserves object identity when only non-message
fields change in the page data. This avoids the ghost-on-edit-truncation
regression that a pure ID-fingerprint approach would have (IDs stay the
same after content edits, so the guard would never fire).

A regression test verifies that a WebSocket-delivered message survives
a queue_update in the same batch without being wiped by the sync
effect.

🤖 This PR was created with the help of Coder Agents, and has been reviewed by a human. 🏂🏻

@mafredri mafredri force-pushed the fix/promote-queued-message-missing-from-timeline branch 3 times, most recently from 7e3f351 to 63e3c75 Compare March 18, 2026 16:15
Two issues caused the promoted message to never appear:

1. handlePromoteQueuedMessage discarded the ChatMessage returned by
the promote API, relying on the WebSocket to deliver it.

2. Even when the WebSocket did deliver it (via upsertDurableMessage),
the queue_update event in the same batch called
updateChatQueuedMessages, which mutated the React Query cache. This
gave chatMessagesList a new reference, triggering the message sync
effect. The effect found the promoted message in the store but not in
the REST-fetched data, classified it as a stale entry (the path
designed for edit truncation), and called replaceMessages, wiping it.

Fix (1): capture the ChatMessage from the promote response and upsert
it into the store, matching handleSend for non-queued messages.

Fix (2): track the fetched message array elements across effect runs
using element-level reference comparison. Only run the
hasStaleEntries/replaceMessages path when the message objects actually
changed (e.g. a refetch producing new objects from the server), not
when only an unrelated field like queued_messages caused the query
data reference to update. Element references work because
useMemo(flatMap) preserves object identity when only non-message
fields change in the page data.
@mafredri mafredri force-pushed the fix/promote-queued-message-missing-from-timeline branch from 63e3c75 to 28591cf Compare March 18, 2026 17:40
@mafredri mafredri marked this pull request as ready for review March 19, 2026 13:23
@mafredri mafredri merged commit f31a827 into main Mar 19, 2026
36 of 38 checks passed
@mafredri mafredri deleted the fix/promote-queued-message-missing-from-timeline branch March 19, 2026 13:27
@github-actions github-actions bot locked and limited conversation to collaborators Mar 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants