Skip to content

streaming: render chat from MessageStore#32454

Merged
TirmanSidhu merged 1 commit into
TirmanSidhu/streaming-message-architecturefrom
run-plan/streaming-message-architecture/pr-4
May 28, 2026
Merged

streaming: render chat from MessageStore#32454
TirmanSidhu merged 1 commit into
TirmanSidhu/streaming-message-architecturefrom
run-plan/streaming-message-architecture/pr-4

Conversation

@TirmanSidhu
Copy link
Copy Markdown
Contributor

Summary

  • Repoints chat view to render from MessageStore (PR 3) instead of the legacy messages array via a new ChatViewModel.renderedMessages bridge that materializes MessageStore snapshots into ChatMessage values and drops the legacy lazy-bubble-creation rows that the streaming helpers would otherwise duplicate.
  • Persists lastAppliedSeq per conversation in a new LastAppliedSeqStore (UserDefaults), updated by every event the reducer applies, and threads it through EventStreamClientGatewayHTTPClient.stream(lastEventId:) as the Last-Event-Id header on SSE (re)connect (PR 2 wired the server side; the daemon honors it on per-conversation subscriptions).
  • The structural duplication symptom should now be impossible for assistant bubbles: every assistant message has exactly one identity (the daemon's stable messageId), and the reducer's idempotent apply is the sole writer for what the renderer sees.

Scope note

This PR removes the legacy currentAssistantMessageId field and clearCurrentTurnTracking() helper from the renderer source-of-truth — they no longer influence which rows the view shows. Physical deletion of those symbols is deferred to PR 6 (the "retire legacy vocabulary" cleanup), which is where the legacy messages array goes away entirely. Removing them in PR 4 would touch ~136 call sites across MessageSendCoordinator, ChatActionHandler, and ChatViewModel+SurfaceHandling — work that belongs in the same PR that retires the array those helpers mutate.

Part of plan: streaming-message-architecture.md (PR 4 of 6)

@TirmanSidhu TirmanSidhu merged commit 633993d into TirmanSidhu/streaming-message-architecture May 28, 2026
@TirmanSidhu TirmanSidhu deleted the run-plan/streaming-message-architecture/pr-4 branch May 28, 2026 19:21
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de45b04186

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +280 to +282
for snapshot in messageStore.orderedMessages {
guard !consumedDaemonIds.contains(snapshot.id) else { continue }
result.append(MessageStore.chatMessage(from: snapshot))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Filter MessageStore snapshots by conversation

Because each ChatViewModel starts a MessageStreamReducer that calls eventStreamClient.subscribe() without the existing conversation filter, every cached chat view model receives streaming events for all conversations. Now that rendering appends every unconsumed snapshot here, opening or returning to another conversation can show assistant messages streamed in a different conversation. This needs to scope the reducer/store by conversationId (or subscribe with broadcastFilter) before using these snapshots for rendering.

Useful? React with 👍 / 👎.

Comment on lines +263 to +270
} else {
// Streaming-bubble lazy-created by the legacy code path.
// Dropped: the corresponding MessageStore snapshot (if
// any) is the authoritative source and is appended
// below. This is what closes the duplication failure
// mode — the legacy "anonymous delta -> new bubble"
// branch no longer reaches the renderer.
continue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve assistant rows without daemon IDs

When any MessageStore snapshot exists, this branch drops every legacy assistant row that lacks daemonMessageId. That is broader than just the old lazy streaming bubble: inline surface rows, attachment warning rows, and inline conversation-error rows are also assistant messages created without a daemon ID, and the reducer ignores those event types, so they disappear from the rendered transcript as soon as a streamed snapshot is present.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant