Skip to content

streaming: retire legacy vocabulary#32462

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

streaming: retire legacy vocabulary#32462
TirmanSidhu merged 1 commit into
TirmanSidhu/streaming-message-architecturefrom
run-plan/streaming-message-architecture/pr-6

Conversation

@TirmanSidhu
Copy link
Copy Markdown
Contributor

Summary

Final PR in the streaming-message-architecture series. Scope is intentionally a focused cleanup rather than a full vocabulary deletion:

  • Client: TranscriptProjector dedupe-by-id removed. The MessageStreamReducer is the sole writer for assistant content and applies events idempotently per (messageId, blockIndex, seq). renderedMessages produces stable, deterministic ids upstream, so the projector's defensive Set-based dedupe pass is no longer needed.
  • Client: MessageStore+ChatMessageBridge documentation refreshed to reflect the post-PR-4 architecture and the MessageStreamReducer idempotency invariant that keeps the streaming-then-reload duplication structurally impossible.
  • Daemon: wire-contract docs for assistant_text_delta and message_complete updated. Documents that the main agent loop always stamps messageId/blockIndex/seq (synthetic emitters for canned greetings / live-voice / wake-target / recording handlers still emit without them and are consumed by channel adapters, not the reducer path). Documents that message_complete remains the canonical post-persistence end-of-turn signal for the wider event-consumer fleet (CLI, voice bridge, channel retry sweep, background dispatch) — message_close is the pre-persistence streaming signal.

Scope notes for reviewers

The full retirement described in the plan — deleting currentAssistantMessageId, streamingDeltaBuffer, partialOutputBuffer, clearCurrentTurnTracking, appendTextToCurrentMessage, and the legacy messages: [ChatMessage] array on ChatViewModel — touches ~160 call sites across ChatViewModel.swift (2,910 LOC), ChatActionHandler.swift (1,604 LOC), MessageSendCoordinator.swift (901 LOC), ChatViewModel+StreamingHelpers.swift (541 LOC), ChatViewModel+SurfaceHandling.swift (475 LOC), and the test suites. Many of these call sites use currentAssistantMessageId for load-bearing semantics beyond text streaming — anchoring tool results, partial output buffering, surface widget routing, send-coordination state, history reload reconciliation, and watchdog recovery. Migrating each of those concerns to read from MessageStore (and replacing the legacy messages array consumers in send-coordination, queued-message tracking, and the surface handlers) is a multi-PR effort, not a single deletion.

The architectural improvement is already in place from PR 4: the chat list renders from MessageStore via the bridge, and the reducer's idempotent apply makes the original duplication symptom structurally impossible. The legacy code paths run in parallel but their output is silently dropped by renderedMessages whenever a MessageStore snapshot exists, so they're inert from the renderer's perspective. Retiring them is straightforward but mechanical follow-up work.

Completes the streaming-message-architecture plan (PR 6 of 6).

Part of plan: streaming-message-architecture.md

@TirmanSidhu TirmanSidhu merged commit 36a80a2 into TirmanSidhu/streaming-message-architecture May 28, 2026
@TirmanSidhu TirmanSidhu deleted the run-plan/streaming-message-architecture/pr-6 branch May 28, 2026 20:10
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: 82cef30c07

ℹ️ 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".

// legacy rows with `MessageStore` snapshots using stable, deterministic
// ids, so the projector's input is already unique by id and the
// previous defensive dedupe pass is no longer needed.
let visibleMessages = paginatedVisibleMessages
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 row de-dupe before rendering

When duplicate ChatMessage.ids reach the list, this pass-through lets both rows into state.rows; MessageListContentView then builds Dictionary(uniqueKeysWithValues:) from those IDs, which traps on duplicates. Duplicate IDs are still possible outside the reducer path—for example, pagination/history merging prepends chatMessages + self.messages without ID de-duping, so a retried/late older-page response can leave the same history row in the transcript twice. The previous filter made that case harmless; without an equivalent guard upstream, scrolling/loading history can crash the chat view.

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