Skip to content

fix(web): queued runs, relationships and changed files no longer re-render on every streamed token - #24055

Open
Batuhan1999 wants to merge 1 commit into
pingdotgg:mainfrom
Batuhan1999:fix/quiet-projection-rerenders
Open

Batuhan1999 wants to merge 1 commit into
pingdotgg:mainfrom
Batuhan1999:fix/quiet-projection-rerenders

Conversation

@Batuhan1999

@Batuhan1999 Batuhan1999 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

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:

  • QueuedRunsControl is always mounted under a server thread's composer. Each render runs deriveThreadQueueWorkflowState twice and builds a Set of every message id.
  • ThreadRelationshipsPanel (thread details panel) reads the full projection for the thread and its parent, and re-runs deriveThreadRelationshipGraph and the subagent and workflow grouping each time.
  • AssistantChangedFilesSectionInner, rendered for every turn that has changed files, reads the live shell but only uses projectId, environmentId and worktreePath.

GitActionsControl had 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:

  • QueuedRunsControl and ThreadRelationshipsPanel use useQuietThreadProjection. deriveThreadQueueWorkflowState, deriveProviderSubagentStatus, resolveLatestMergeBackRun and canDetachThreadProviderSession already accept ThreadStructure. deriveThreadRelationshipGraph and resolveMergeBackTargetThreadId in packages/client-runtime/src/state/threadRelationships.ts now take ThreadStructure too. They only read thread, subagents and contextTransfers, and a full projection still fits that type, so other callers are unaffected.
  • AssistantChangedFilesSectionInner uses useQuietThreadShell. 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, updatedAt or lastVisitedAt. The ThreadStructure type 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 renders QueuedRunsControl under a React Profiler with 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 mocked useThreadProjection.

  • Before the fix (on useThreadProjection): AssertionError: expected 4 to be 2, one extra render per streamed token.
  • After the fix: all 4 QueuedRunsControl tests pass; the two streamed tokens cause 0 renders and the structural change still re-renders.
  • ThreadRelationshipsControl.browser.test.tsx: its entities mock was renamed to useQuietThreadProjection; 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:

  • No manual check in a running client and no profiler capture in a real app. The render-count test covers QueuedRunsControl only.
  • The ThreadRelationshipsPanel and AssistantChangedFilesSectionInner swaps are covered by typecheck and by perf(web): streamed reply text no longer re-renders ChatView #23117's quietThreadAtom tests, 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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Oct 23, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 8c51994

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.

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Web components now use quiet thread hooks for projection or shell reads. Thread relationship utilities accept ThreadStructure projections. Browser tests provide thread state atoms and check QueuedRunsControl render counts during streaming updates and completion.

Changes: Thread state reads

Layer / File(s) Summary
Relationship projection reads
packages/client-runtime/src/state/threadRelationships.ts, apps/web/src/components/chat/ThreadRelationshipsControl.tsx, apps/web/src/components/chat/ThreadRelationshipsControl.browser.test.tsx
Thread relationship utilities now accept ThreadStructure projections. ThreadRelationshipsPanel and its browser test use useQuietThreadProjection.
Quiet component reads and render validation
apps/web/src/components/chat/QueuedRunsControl.tsx, apps/web/src/components/chat/TimelineAssistantRows.tsx, apps/web/src/components/chat/QueuedRunsControl.browser.test.tsx
QueuedRunsControl reads its projection through useQuietThreadProjection, and TimelineAssistantRows reads its shell through useQuietThreadShell. Browser tests use thread-state atoms and check render counts during streaming text updates and completion.

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge, mnigos

Merge Risk: Minimal · up to c29c4

This change reduces unnecessary re-renders during streamed output without altering the rendered output. No actionable merge-blocking risk was identified.

Pre-merge checks: Passed 4

Check name Status Explanation
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.
Title check Passed The title clearly and concisely describes the main change: preventing queued runs, relationships, and changed-files components from re-rendering on every streamed token.
Description check Passed The description includes all required sections. It explains the problem, the implementation, scope rationale, focused verification, observed results, limitations, and agent usage.
Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 8c51994 and c29c437.

Files selected for processing: 5

  • apps/web/src/components/chat/QueuedRunsControl.browser.test.tsx
  • apps/web/src/components/chat/QueuedRunsControl.tsx
  • apps/web/src/components/chat/ThreadRelationshipsControl.browser.test.tsx
  • apps/web/src/components/chat/ThreadRelationshipsControl.tsx
  • apps/web/src/components/chat/TimelineAssistantRows.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@juliusmarminge juliusmarminge left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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>
@Batuhan1999
Batuhan1999 force-pushed the fix/quiet-projection-rerenders branch from 8c51994 to c29c437 Compare November 11, 2026 08:57
@Batuhan1999 Batuhan1999 changed the title fix(web): queued runs, git actions and relationships no longer re-render on every streamed token fix(web): queued runs, relationships and changed files no longer re-render on every streamed token Nov 11, 2026
@Batuhan1999

Copy link
Copy Markdown
Contributor Author

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. check:changed passes. Head: c29c437.

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants