Repository navigation
fix(web): queued runs, relationships and changed files no longer re-render on every streamed token - #24055
fix(web): queued runs, relationships and changed files no longer re-render on every streamed token#24055Batuhan1999 wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused performance fix that makes existing queue, Git, relationship, and changed-files consumers ignore irrelevant streaming-only updates while preserving structural state and final-state updates. It adds targeted browser coverage without changing product defaults, APIs, schemas, or sensitive code. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
juliusmarminge
left a comment
There was a problem hiding this comment.
Thanks for tackling these streaming renders! The Git actions subscription is already covered by #27418 (aaee7743f8). Could you cut the Git actions changes and keep the remaining queue, relationships, and changed-files fixes?
…ender on every streamed token pingdotgg#23117 moved ChatView onto useQuietThreadProjection so streamed reply text stops re-rendering it. QueuedRunsControl, ThreadRelationshipsPanel and the changed-files section still subscribed to the live projection or shell, so they re-rendered on every token although they only read thread structure. Move them to the quiet hooks, and let deriveThreadRelationshipGraph and resolveMergeBackTargetThreadId take a ThreadStructure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8c51994 to
c29c437
Compare
|
Thanks! Rebased onto main and cut the Git actions changes (component and test), since #27418 covers them. The queue, relationships and changed-files fixes remain. Main's newer QueuedRunsControl tests now run on the real atoms, and the ThreadRelationshipsControl test mock picks up the renamed hook. |
Problem
While a reply streams, the server replaces the assistant turn item many times a second. #23117 added
useQuietThreadProjection, a thread projection that keeps its identity while only streamed text changes, and moved ChatView onto it. Some components that stay mounted during streaming still read the live projection or shell, so they re-render on every token even though they never read streamed text:QueuedRunsControlis always mounted under a server thread's composer. Each render runsderiveThreadQueueWorkflowStatetwice and builds a Set of every message id.ThreadRelationshipsPanel(thread details panel) reads the full projection for the thread and its parent, and re-runsderiveThreadRelationshipGraphand the subagent and workflow grouping each time.AssistantChangedFilesSectionInner, rendered for every turn that has changed files, reads the live shell but only usesprojectId,environmentIdandworktreePath.GitActionsControlhad the same problem; #27418 fixed it on main, so this PR no longer touches it.Change
These components now use the quiet hooks #23117 introduced:
QueuedRunsControlandThreadRelationshipsPaneluseuseQuietThreadProjection.deriveThreadQueueWorkflowState,deriveProviderSubagentStatus,resolveLatestMergeBackRunandcanDetachThreadProviderSessionalready acceptThreadStructure.deriveThreadRelationshipGraphandresolveMergeBackTargetThreadIdinpackages/client-runtime/src/state/threadRelationships.tsnow takeThreadStructuretoo. They only readthread,subagentsandcontextTransfers, and a full projection still fits that type, so other callers are unaffected.AssistantChangedFilesSectionInnerusesuseQuietThreadShell. The quiet shell ignores only streaming timestamps, item counts and the latest message, so the three fields it reads stay current.None of these components read streamed text,
updatedAtorlastVisitedAt. TheThreadStructuretype omits those fields, so typecheck would catch a read of any of them.Scope and approval
No prior issue or discussion. This finishes #23117's move of streaming-time readers onto the quiet projection: it only swaps hooks and narrows two parameter types to fields the functions already read. No new behavior, settings or UI; rendered output is unchanged.
Verification
Rebased onto current main (c29c437), Git actions changes dropped per review.
Added a browser test in
apps/web/src/components/chat/QueuedRunsControl.browser.test.tsx. It runs the real thread detail atoms (createEnvironmentThreadDetailAtoms) on a state atom the test writes and rendersQueuedRunsControlunder a ReactProfilerwith one queued follow-up. It pushes two streamed-text deltas for the running assistant item, keeping every other item's identity as the server does, and asserts the deltas cause no commits. As a positive control, it then completes the assistant item and asserts the control re-renders. Main's two newer tests in that file (sender labels, scroller containment) now run on the same real atoms instead of a mockeduseThreadProjection.useThreadProjection):AssertionError: expected 4 to be 2, one extra render per streamed token.ThreadRelationshipsControl.browser.test.tsx: its entities mock was renamed touseQuietThreadProjection; 10 tests pass.node scripts/check-changed.ts: format, lint, knip, typecheck (7 packages), tests beside the changes and react compiler all pass.Not checked:
ThreadRelationshipsPanelandAssistantChangedFilesSectionInnerswaps are covered by typecheck and by perf(web): streamed reply text no longer re-renders ChatView #23117'squietThreadAtomtests, not by render-count tests of their own.Implemented with Claude Opus 5.5 via Claude Code (running in T3 Code).
🤖 Generated with Claude Code