Uh oh!
There was an error while loading. Please reload this page.
perf(clients): lease sidebar status by visibility - #9052
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Both findings are about the new "keep the last status we rendered" behavior that backs the lease gating: the legacy row retains a snapshot that is not keyed to the PR it describes, and the V2 rows retain nothing at all, so the two sidebars now behave differently for the same lease transition.
Posted via Macroscope — UI Consistency
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This change alters production subscription lifecycles and gates VCS/pull-request status work on sidebar visibility across multiple clients, with retained snapshots masking unloaded state. An unresolved concrete identity-handling concern in the legacy sidebar also warrants human review. You can add or adjust custom eligibility rules. Learn more. |
Web rows repeated the same identity-keyed retention block three times. Move it into one useRetainedValue hook in Sidebar.logic. Mobile rows read the shared snapshot map with a per-thread selector so a write for one row does not re-render every other row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
277a888 to
4faaad4Comparet3dotgg
commented
Sep 2, 2026
Rebased onto main and pushed one commit on top of yours (4faaad4).
I verified the lease path: one IntersectionObserver per row, scoped to the scroll-area viewport with a 160 px rootMargin, callbacks only on intersection changes, disconnected on unmount. Active rows skip the observer. When the atom idles out, the registry's TTL sweep unmounts it, the RPC client sends Interrupt, and the server's |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4faaad4. Configure here.
| thread.linkedPullRequest === null | ||
| ? null | ||
| : JSON.stringify([thread.environmentId, thread.linkedPullRequest]), | ||
| linkedPullRequestStatus, |
There was a problem hiding this comment.
Legacy sidebar uses strict null check for optional field
Low Severity
The retention key for visibleLinkedPullRequestStatus uses thread.linkedPullRequest === null (strict equality), but the display path at line 479 uses thread.linkedPullRequest == null (loose equality). Since linkedPullRequest is Schema.optional(Schema.NullOr(ThreadLinkedPullRequest)), it can be undefined. When undefined, the strict check is false, producing a non-null retention key (JSON.stringify([envId, undefined]) → '["envId",null]') for a thread that the display logic correctly treats as having no linked PR. This inconsistency breaks the identity contract — the key computation diverges from the display branch.
Reviewed by Cursor Bugbot for commit 4faaad4. Configure here.
There was a problem hiding this comment.
Agent comment posted on behalf of @t3dotgg by Claude Fable 5.1.
Checked against source. No behavior difference. When linkedPullRequest is undefined, useLinkedThreadPullRequest returns null, so nothing is ever stored under the ["env",null] key, and the display branch on the next line takes the == null path and never reads visibleLinkedPullRequestStatus. A relink from a PR to undefined changes the key, so the old PR's badge and URL cannot leak. Leaving as is.
Uh oh!
There was an error while loading. Please reload this page.
## What's Changed * perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145 * perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052 * fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138 * fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147 * fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302 * fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941 * fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119 * fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113 * fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120 * fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154 * perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058 * feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554 * test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157 * fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142 * fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064 * fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968 * feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140 * feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143 * fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160 * fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910 ## New Contributors * @404khai made their first contribution in pingdotgg/t3code#8910 **Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Adopts main's round-19 features into the v2 stack: payload-budgeted orchestration replay (#8992), sidebar row subscription leases (#9052), tool group virtualization and scroll anchoring (#9106), repeated-command and browser-group presentation, inline assistant citations (#9146), per-cwd provider skills discovery (#8778), Claude composer skill dispatch (#9128), grok health probe and model negotiation (#9154), and the failed-tool thinking fallback (#9165). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopts main's round-19 features into the v2 stack: payload-budgeted orchestration replay (#8992), sidebar row subscription leases (#9052), tool group virtualization and scroll anchoring (#9106), repeated-command and browser-group presentation, inline assistant citations (#9146), per-cwd provider skills discovery (#8778), Claude composer skill dispatch (#9128), grok health probe and model negotiation (#9154), and the failed-tool thinking fallback (#9165). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>


Mounted sidebar rows kept VCS streams and linked pull request refreshes alive for five minutes, even after the rows left the viewport. With many unique worktrees, idle rows continued doing Git and provider work. This is item 07 of the performance opportunity audit.
Web and desktop rows now lease live status only while active or within 160 px of the sidebar viewport. Active and highlighted search rows always stay live. Mobile keeps its existing list virtualization and now preserves the last pull request badge separately while the shared live atom expires. VCS streams get a 10-second scroll grace and linked pull request detail queries get a 5-second idle TTL.
Measurements
Measured with the same isolated 50-row, 50-worktree fixture on the pre-change commit (
b883fc066) and this commit. Scope counts come from the server's background-policy snapshot after the browser settled. The after run scrolled from row 01 to row 50, waited past the grace period, and selected row 50.In this fixture, the change releases 39 offscreen VCS streams. That removes unnecessary Git status work and server/client subscription bookkeeping, while offscreen linked PR polling can stop after 5 seconds instead of remaining eligible for 5 minutes. The 78% figure is a measured live-scope reduction, not a CPU reduction claim.
Proof
The native-resolution sidebar GIF drives the real web client through the 50-row fixture, scrolls the full list, and opens row 50.
Verification
pnpm exec vp test run packages/client-runtime/src/state/vcs.test.ts packages/client-runtime/src/state/pullRequests.test.ts apps/web/src/components/Sidebar.logic.test.ts apps/web/src/components/ThreadStatusIndicators.test.ts apps/mobile/src/features/threads/threadListV2.test.ts(192 tests)pnpm --filter @t3tools/client-runtime typecheckpnpm --filter @t3tools/web typecheckpnpm --filter @t3tools/mobile typecheckChanges and PR text by GPT-5.6 Sol in T3 Code with the Codex harness.
Note
Low Risk
Performance-focused subscription gating with retained UI snapshots; no auth or data-model changes, though off-screen rows may show slightly stale PR/git badges until scrolled back into range.
Overview
Sidebar thread rows no longer keep live VCS status and linked PR detail subscriptions for every mounted row. A new visibility lease (
useSidebarRowSubscriptionLease) turns those queries on only when the row is active or within 160px of the scroll viewport;useRetainedValuekeeps the last git/PR values so PR badges and branch metadata do not flash empty when the lease drops.Client-runtime shortens idle teardown for off-lease consumers: VCS status streams use a 10s idle TTL (was the default 5 min), and linked PR detail queries use 5s.
Mobile adds a bounded (500 entry) thread PR snapshot atom so virtualized list rows can show the last PR presentation while shared live atoms expire, without one atom per thread forever.
Reviewed by Cursor Bugbot for commit 4faaad4. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Lease sidebar VCS and linked-PR status queries by viewport visibility
useSidebarRowSubscriptionLeaseto gate VCS status and linked-PR queries on active or near-viewport rows (160px overscan viaIntersectionObserver, with a fallback that keeps leases alive)useRetainedValueso rows continue rendering the last non-null status keyed by environment and checkout identity while their live query is disabledthreadPrSnapshotsAtom, a 500-entry bounded cache on mobile that persists thread PR presentations across unmount/remount, with eviction of the oldest entriescreateVcsEnvironmentAtoms) and 5s for linked-PR detail (createLinkedPullRequestDetailAtomFamily)useThreadPron mobile clears cached entries on definitive null resultsMacroscope summarized 4faaad4.