fix(web): thread switches show only selected thread messages#21560
Switching to a thread whose history is still loading could display the previous thread’s messages beneath the newly selected title. The timeline now uses the selected thread’s cached messages, or stays empty/loading until that thread’s data arrives.
Removed only the foreign-thread fallback. The bounded selected-thread cache and current find, handoff, and paint-only guards remain intact. Rebuilt from the original contribution by @artile and @tao-io in #17142. The original fork advertises maintainer edits, but GitHub rejected the push with HTTP 403, so this is a one-layer replacement on origin. Compared the original diff against this rebuild: selected-thread caching is retained; removal of newer find, handoff, and paint-only guards is intentionally omitted.
Verification:
Two independent full-diff review rounds with Claude Opus 5.5 and Codex GPT-6-Sol: no code blockers. Optional removal of now-inert paint guards remains outside this fix.
Consolidated the thread-switch coverage from 138 to 137 cases in the existing
ChatView.logic.test.ts; the cold-switch regression fails on main.Focused
ChatView.logicandMessagesTimeline.logicsuites: 304 passed / 1 failed before the fix → 305 passed after it.Four focused files including the affected browser fixtures: 311 passed. A fresh-cache, single-CPU run of the browser pair passed all 6 tests. The initial CI run exposed two existing lazy-module/highlighter fixture races; the tests now await those resources inside React act, with assertions, timeouts, and test counts unchanged.
Web typecheck and scoped formatting pass. Scoped lint passes with four unchanged unused-import warnings in the test file.
Linux web: selected B while its actual bounded-history request was held; main displayed A’s messages under B, while this revision displayed B’s loading state. Releasing the request displayed B’s transcript. Returning to cached A and B showed each selected thread’s own messages.
Matching 1280×800 captures use isolated fixture threads and a local font substitute; no transcript contents were altered in the browser. Before production matches main 1451e7c; after production is identical to final head 2c97895; the final follow-up only fixes the two browser fixtures.
Before — B selected, A’s transcript remains
Before: Thread A messages beneath the selected Thread B title
After — B selected, only B’s loading state
After: Thread B loading without Thread A messages
Before recording · After recording
Not verified: macOS, Windows, iOS, Android, Electron desktop, remote/relay/tunnel connections, or live provider handoffs. The change is in the web timeline selection path; it changes no provider or wire contracts.
Implementation: Codex GPT-6.1-Sol; tests: Codex GPT-6-Astra; independent reviews: Claude Opus 5.5 and Codex GPT-6-Sol, through T3 Code.
Checks
Added 1 commit
- fix(web): thread switches show only selected thread messages1d5341f
Approvability
Verdict: Approved at 2c97895
Macroscope's review found this PR approvable — The production change is a small, well-scoped fix that prevents the previously viewed thread from being shown while the selected thread loads, while preserving the selected thread’s own cached timeline behavior. The remaining changes are test-only resource waits and updated coverage.
Notes:
- No code objects were reviewed. Approvability was decided on eligibility alone.
No code changes detected at 63124cc. Prior analysis still applies.
You can add or adjust custom eligibility rules. Learn more.
Thread transfer impact
⚠️ The latest CI run did not produce a thread transfer result for 1d5341f.
This comment will update automatically after the next completed run.
No actionable comments were generated in the recent review. 🎉
ℹ️ Recent review info
⚙️ Run configuration
- Configuration used: Path: .coderabbit.config.ts
- Review profile: CHILL
- Plan: Team
- Run ID:
aebc356f-aca3-4c72-8b49-615c67e0e6aa
📥 Commits
Reviewing files that changed from the base of the PR and between 2c9789522b1da66cbc31f2d47c337821889a2cc8 and 63124cc7038a13f0a1fc6a71d79364004813e507.
📒 Files selected for processing: 2
apps/web/src/components/ChatView.logic.tsapps/web/src/components/chat/WorkflowOpeningBlock.browser.test.tsx
💤 Files with no reviewable changes: 1
- apps/web/src/components/ChatView.logic.ts
Limit details: You’ve used all 10 included reviews currently available.
📝 Walkthrough
An uncached active thread now displays its own empty timeline while history loads. Browser tests wait for asynchronous rendering dependencies before asserting highlighted code or renewed-URL content.
Changes: Thread timeline loading
Layer / File(s) Summary Destination-thread loading timeline
apps/web/src/components/ChatView.logic.ts,apps/web/src/components/ChatView.logic.test.tsThe resolver no longer displays another thread’s cached timeline while the active thread loads. The test expects empty entries under the destination thread’s display key and removes the remount-retention assertion. Browser test readiness
Layer / File(s) Summary Wait for asynchronous rendering dependencies
apps/web/src/components/chat/WorkflowOpeningBlock.browser.test.tsx,apps/web/src/components/files/AttachmentFilePreview.browser.test.tsxThe workflow test waits for the JavaScript syntax highlighter before checking highlighted code. The attachment preview test awaits the ReadOnlySourcePreviewimport insideactbefore checking the renewed URL fetch and rendered HTML.Priority: ⬇️ Low
Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix
Possibly related PRs
- pingdotgg/t3code#21626: Changes the same thread-switch timeline resolver behavior and its tests, using per-thread remembered entries.
Suggested reviewers:
shivamhwp
Merge Risk: ⚪ Minimal · up to 63124
Switching threads no longer shows the previous thread's messages while the selected thread loads. The test changes are harmless. No merge-blocking risk was found.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title clearly and concisely describes the main web timeline fix: thread switches now show only messages from the selected thread. Description check Passed The description clearly explains the problem, implementation, scope, verification results, screenshots, recordings, and unverified environments. It provides sufficient detail for review, although it d… Linked Issues check Passed Check skipped because no linked issues were found for this pull request. Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request. Full details: Description check
Explanation
The description clearly explains the problem, implementation, scope, verification results, screenshots, recordings, and unverified environments. It provides sufficient detail for review, although it does not use the template headings explicitly.
✨ Finishing Touches
📝 Generate docstrings
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
- Commit to this branch
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Usage-based review receipt
- Mode: Continue automatically
- Reviewed files: 1
- Charged: $0.25
- View usage details
Note
This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.
Comment @coderabbitai help to get the list of available commands.
Added 1 commit
- test(web): await preview and syntax highlighting resources2c97895
Evidence for the WorkflowOpeningBlock highlighter wait in this PR, since the race doesn't reproduce on a fast machine:
- Impact on main: all 8 of main's 8 failed CI runs among the latest 20 fail on
WorkflowOpeningBlock.browser.test.tsx:103, and so do 42 of the 47 failures since 2026-10-18 20:06Z. Several open PRs are blocked on it, including #20086. - Timing in CI: in a failing run's log, the test started rendering at 16.58 s, and React's "A suspended resource finished loading inside a test, but the event was not wrapped in act(...)" warning arrived at 17.52 s. The cold
javascripthighlighter took about 0.94 s, insidevi.waitFor's 1 s window, before the block could reveal. - Reproduction: delaying
getSharedHighlighterfrom@pierre/diffsby 940 ms in a copy of this test fails the unchanged test 3 of 3 runs with CI'sexpected null not to be null. AwaitinggetSyntaxHighlighterPromise("javascript")first passes 3 of 3 with the same delay.
The full web suite (529 files) passes on 2 pinned cores on main without the delay, which is why it only shows up on CI.
Added 1 commit
- Merge remote-tracking branch 'origin/main' into HEAD63124cc