Skip to content

fix(web): queued runs, git actions and relationships 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

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.
  • GitActionsControl subscribes to the full projection only to use projection.thread as a fallback when the quiet shell is missing.
  • 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.

Change

These components now use the quiet hooks #23117 introduced:

  • QueuedRunsControl, GitActionsControl 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.

All four changes fix the same problem: they finish #23117's move of streaming-time readers onto the quiet projection. Each one swaps a hook for the quiet hook that already exists.

Scope and approval

No prior issue or discussion. This is a very small, focused fix for an obvious bug. #23117 established the intended behavior (structural readers should not re-render on streamed text) and the mechanism, but some readers were left on the live subscriptions. The change only swaps hooks and narrows two parameter types to fields the functions already read. It adds no new behavior, settings or UI. The rendered output is unchanged. The only difference is that these components skip renders while text streams in.

Verification

Added a browser test in the existing 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 it renders QueuedRunsControl under a React Profiler with one queued follow-up. It then pushes two streamed-text deltas for the running assistant item, keeping every other item's identity as the server does. The test asserts that the deltas cause no commits. As a positive control, it then completes the assistant item and asserts that the control does re-render.

vp test run --project browser src/components/chat/QueuedRunsControl.browser.test.tsx src/components/GitActionsControl.browser.test.tsx   # from apps/web
  • Before the fix (QueuedRunsControl on useThreadProjection): fails with AssertionError: expected 4 to be 2. Each of the two streamed tokens caused one extra render, plus two deriveThreadQueueWorkflowState calls per render.
  • After the fix: both files pass (3 tests). The two streamed tokens cause 0 renders, and the structural change still re-renders.
  • GitActionsControl.browser.test.tsx only had its mock renamed from useThreadProjection to useQuietThreadProjection, and it still passes.
  • vp run check:changed upstream/main (7 changed files): format, lint, knip, typecheck (7 packages) and tests beside the changes (4 files, 25 tests) all pass.

Not checked:

  • No manual check in a running client and no profiler capture in a real app. The test measures re-renders of QueuedRunsControl only.
  • The GitActionsControl, ThreadRelationshipsPanel and AssistantChangedFilesSectionInner swaps are covered by typecheck, which confirms every field they read exists on the quiet type, and by perf(web): streamed reply text no longer re-renders ChatView #23117's quietThreadAtom tests. They have no render-count test of their own.

Implemented with Claude Opus 5.5 via Claude Code (running in T3 Code).

🤖 Generated with Claude Code

…der on every streamed token

pingdotgg#23117 moved ChatView onto useQuietThreadProjection so streamed reply text
stops re-rendering it. QueuedRunsControl, GitActionsControl,
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>
@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

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

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

📒 Files selected for processing: 7
  • apps/web/src/components/GitActionsControl.browser.test.tsx
  • apps/web/src/components/GitActionsControl.tsx
  • apps/web/src/components/chat/QueuedRunsControl.browser.test.tsx
  • apps/web/src/components/chat/QueuedRunsControl.tsx
  • apps/web/src/components/chat/ThreadRelationshipsControl.tsx
  • apps/web/src/components/chat/TimelineAssistantRows.tsx
  • packages/client-runtime/src/state/threadRelationships.ts

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

📝 Walkthrough

Several web components now use quiet thread hooks. Thread relationship utilities accept ThreadStructure projections. Browser tests update their thread-state mocks and check how QueuedRunsControl renders during assistant streaming.

Changes: Thread state read updates

Layer / File(s) Summary
Thread relationship input contract
packages/client-runtime/src/state/threadRelationships.ts
The relationship utility signatures now use ThreadStructure for projection inputs.
Quiet hook migration
apps/web/src/components/GitActionsControl.tsx, apps/web/src/components/chat/QueuedRunsControl.tsx, apps/web/src/components/chat/ThreadRelationshipsControl.tsx, apps/web/src/components/chat/TimelineAssistantRows.tsx, apps/web/src/components/GitActionsControl.browser.test.tsx
The components now read thread projection or shell data through quiet hooks. The GitActionsControl test mock now overrides useQuietThreadProjection.
QueuedRunsControl state tests
apps/web/src/components/chat/QueuedRunsControl.browser.test.tsx
The browser tests use writable thread-state atoms. They check that text deltas do not cause another render and that stream completion does.

Priority: ⬇️ Low

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

Change: Bug fix

Suggested labels: bugfix

Suggested reviewers: juliusmarminge, scottn-pv, mnigos

Merge Risk: ⚪ Minimal · up to 8c519

The changes appear mergeable after normal checks; no actionable issue remains identified.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely summarizes the main change: preventing unnecessary re-renders during streamed tokens in the affected web components.
Description check Passed The description includes all required sections and clearly explains the problem, implementation, scope justification, verification results, limitations, and agent details. It is sufficiently complete …
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 includes all required sections and clearly explains the problem, implementation, scope justification, verification results, limitations, and agent details. It is sufficiently complete for this focused UI performance fix.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

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.

1 participant