Repository navigation
refactor(client): one VCS status refresh owner per checkout - #24975
Conversation
Thread transfer impact
This comment will update automatically after the next completed run. |
15db051 to
582db9c
Compare
ApprovabilityVerdict: Needs Human Approval Macroscope's review found this PR requires human approval — This refactor changes mobile VCS refresh ownership and timing by introducing a shared connection-aware atom, alongside new lifecycle tests. It also adds an Notes:
You can add or adjust custom eligibility rules. Learn more. |
582db9c to
ca1e467
Compare
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes.
|
| Layer / File(s) | Summary |
|---|---|
Shared VCS status revalidation packages/client-runtime/src/state/vcsStatusRevalidation.ts, packages/client-runtime/src/state/vcsStatusRevalidation.test.ts, packages/client-runtime/src/state/vcs.ts |
The new atom family shares refreshes by environment and working directory. It supports connection-triggered and wake-triggered refreshes, wake debouncing, and cleanup when subscribers unmount. Tests cover these behaviors, and vcs.ts re-exports the implementation. |
Selected-thread refresh integration apps/mobile/src/state/use-remote-environment-registry.ts, apps/mobile/src/state/use-selected-thread-git-actions.ts, apps/mobile/src/state/use-selected-thread-git-actions.test.ts, apps/mobile/src/state/use-selected-thread-git-actions.test.tsx, scripts/effect-budget.json |
Mobile adds an environment connection atom and mounts shared revalidation for a selected thread with a project and working directory. Refresh failures use a shared message helper. The test mocks change, the previous hook test is removed, and the effect-budget entry for the hook is deleted. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Refactor
Sequence Diagram(s)
sequenceDiagram
participant Hook as useSelectedThreadGitActions
participant Mount as useAtomMount
participant Revalidation as selectedThreadStatusRevalidation
participant Connection as environmentConnectedAtom
participant Refresh as vcsEnvironment.refreshStatus
participant ErrorState as setPendingConnectionError
Hook->>Mount: Mount revalidation for selected environment and cwd
Mount->>Revalidation: Subscribe to selected-thread status atom
Revalidation->>Connection: Read connection state
Connection-->>Revalidation: Connected state
Revalidation->>Refresh: Refresh status when connected or woken
Refresh-->>Revalidation: Return refresh result
Revalidation->>ErrorState: Clear or set pending connection error
Possibly related issues
- pingdotgg/t3code#20254 — The shared refresh owner may address overlapping refresh work, but this change does not address slow Git commands or event-loop stalls.
Possibly related PRs
- pingdotgg/t3code#24827 — Changes the same Mobile Git-status hook and shared revalidation implementation.
- pingdotgg/t3code#24814 — Adds the shared client-runtime state facilities used by this change.
- pingdotgg/t3code#24824 — Mounts the same shared revalidation pattern for web Git actions.
Suggested reviewers: macroscopeapp
Merge Risk: 
Minimal · up to ca1e4
-
No issue with the selected change is established; it is mergeable after normal checks.
Pre-merge checks: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passedFull details: Description check
-
Explanation
The description thoroughly covers the problem, change, behavior, and verification. It omits the required Scope and approval section, including issue or maintainer approval details or an exemption rationale.
Resolution
Add a Scope and approval section. Link the triaged issue or maintainer approval, or explain why this focused fix qualifies for an exemption.
-
Finishing Touches: 💡 1
-
🛠️ Fix failing CI checks: 💡
-
- 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
Comment @coderabbitai help to get the list of available commands.
Problem: mobile refreshed the selected thread's git status from a useEffect keyed on five primitives mirrored from the selection, with a useEffectEvent to dodge the stale-closure trap. Every consumer of useSelectedThreadGitActions ran its own copy, so two sheets showing the same checkout each fired a refresh. Fix: add createVcsStatusRevalidationAtomFamily to client-runtime. It yields one atom per environment and cwd that refreshes while mounted: on mount once connected, once per reconnect, and optionally on a debounced wake signal. Subscribers to the same checkout share it, and the wake subscription and timer end with the last subscriber. Mobile mounts it with useAtomMount, reading the connection flag from a new environmentConnectedAtom family. Effects: removes the selection-refresh useEffect (and its useEffectEvent) from use-selected-thread-git-actions. None added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The doc comment said the atom refreshes on mount. It refreshes only when a `connected` atom is passed and reads true: on mount and once per reconnect. Without it, only the debounced wake signal refreshes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ca1e467 to
da58a63
Compare
## What's Changed * fix(server): attachments are no longer silently dropped from prompts by @maria-rcks in pingdotgg/t3code#21356 * fix(mobile): images picked with choose files reach the agent as images by @maria-rcks in pingdotgg/t3code#21352 * feat(web): attach svg files to messages by @maria-rcks in pingdotgg/t3code#21355 * feat(server): send pdf attachments to claude and antigravity as documents by @maria-rcks in pingdotgg/t3code#21358 * feat(mobile): play audio inline and open shared files in the viewer by @maria-rcks in pingdotgg/t3code#21353 * feat(web): play audio inline and open any file the agent shares in the viewer by @maria-rcks in pingdotgg/t3code#21363 * perf(pull-requests): faster pull request timeline, code tab, summary and search by @maria-rcks in pingdotgg/t3code#22697 * perf(mobile): bound streaming markdown cache and pending work by @juliusmarminge in pingdotgg/t3code#24263 * fix(web): question options show a radio and an aligned shortcut key by @maria-rcks in pingdotgg/t3code#23255 * fix(server): notify parents after delegated background runs by @juliusmarminge in pingdotgg/t3code#23542 * fix(server): running delegates tell agents to end the turn by @t3dotgg in pingdotgg/t3code#24887 * perf(ci): cache server tests individually across shards by @juliusmarminge in pingdotgg/t3code#24966 * feat(mobile): one cached native-client install command for agents and machines by @juliusmarminge in pingdotgg/t3code#24965 * perf(source-control): pull request previews read only the pull request by @maria-rcks in pingdotgg/t3code#22583 * feat(infra): native client cache panels on the task cache dashboard by @juliusmarminge in pingdotgg/t3code#24994 * feat(web): UI design overhaul, with the new look behind a Beta toggle by @t3dotgg in pingdotgg/t3code#24865 * test(server): tests no longer depend on the host machine by @t3dotgg in pingdotgg/t3code#25001 * perf: opening a thread loads 85% less data by @t3dotgg in pingdotgg/t3code#24899 * fix(server): orchestrator records command count and duration again by @yordis in pingdotgg/t3code#23966 * fix(observability): every app reports its own environment and release channel by @yordis in pingdotgg/t3code#24340 * chore(lint): React effects can't grow past each file's budget by @juliusmarminge in pingdotgg/t3code#24964 * fix(observability): every exported resource names its service instance by @yordis in pingdotgg/t3code#23973 * fix(install): stable installs no longer fail behind a page of nightlies by @t3dotgg in pingdotgg/t3code#22907 * fix(server): Grok refresh no longer restores removed models from cache by @t3dotgg in pingdotgg/t3code#22849 * fix(server): resume fallback keeps the latest turn when an older handoff is pending by @t3dotgg in pingdotgg/t3code#22902 * fix(client-runtime): "Codex (hub)" badges read CH instead of C( by @t3dotgg in pingdotgg/t3code#22918 * fix(mobile): retain requested PR environment while connecting by @juliusmarminge in pingdotgg/t3code#24262 * fix(web): usage-limit banner reset time follows the Time format setting by @t3dotgg in pingdotgg/t3code#22915 * fix(web): terminal drag selection scrolls faster the farther past the edge by @t3dotgg in pingdotgg/t3code#22872 * fix(observability): resources add OTel semconv runtime and browser keys by @yordis in pingdotgg/t3code#23972 * chore(lint): main passes lint again with effects merged after the budget by @t3dotgg in pingdotgg/t3code#25055 * fix(server): ended roots no longer leave provider turns running by @t3dotgg in pingdotgg/t3code#22846 * feat(mobile): open threads and devices in a resizable iOS inspector by @juliusmarminge in pingdotgg/t3code#24985 * chore(web): measure renders before/after with render scenarios by @juliusmarminge in pingdotgg/t3code#24980 * fix(web): asset URLs re-mint per revision and before expiry without effects by @juliusmarminge in pingdotgg/t3code#24970 * fix(web): queued-message edits survive navigation and settle once by @juliusmarminge in pingdotgg/t3code#24972 * refactor(mobile): thread visits are tracked outside React by @juliusmarminge in pingdotgg/t3code#24973 * refactor(client): cloud account transitions run in one service by @juliusmarminge in pingdotgg/t3code#24974 * refactor(client): one VCS status refresh owner per checkout by @juliusmarminge in pingdotgg/t3code#24975 * refactor(web): document appearance and title are applied from app start by @juliusmarminge in pingdotgg/t3code#25004 * refactor(web): app shell shortcuts register once through one command dispatcher by @juliusmarminge in pingdotgg/t3code#25016 * refactor(web): shared intersection and document-activity stores by @juliusmarminge in pingdotgg/t3code#25020 * refactor(web): the theme store and diff worker pool stop syncing the theme through effects by @juliusmarminge in pingdotgg/t3code#25007 * fix(server): keep history discovery off the writer and pace compaction by @JustYannicc in pingdotgg/t3code#20877 * perf(mobile): compact persisted thread snapshots by @juliusmarminge in pingdotgg/t3code#24265 **Full Changelog**: pingdotgg/t3code@v0.1.3-nightly.20261025.3682...v0.1.3-nightly.20261025.3692 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.1.3-nightly.20261025.3692
Problem
Mobile refreshed the selected thread's git status from a
useEffectkeyed on five primitives copied out of the selection, plus auseEffectEventto avoid stale closures. Each consumer ofuseSelectedThreadGitActionsran its own copy, so two sheets showing the same checkout each fired a refresh.Change
packages/client-runtime: newcreateVcsStatusRevalidationAtomFamily. It makes one atom per environment and cwd that refreshes while mounted, but only through theconnectedatom it is given: once when that atom is true on mount, once per reconnect, and on an optional debounced wake signal. Every subscriber to the same checkout shares it, and the wake subscription and timer end with the last subscriber.useSelectedThreadGitActionsmounts the atom withuseAtomMount. A newenvironmentConnectedAtomfamily supplies the connection flag, andsetPendingConnectionErrorstill reports refresh failures..test.tsxrender-loop harness that tested the old effect. The new family test covers the behavior instead.Behavior change: opening a git sheet no longer refreshes on its own
Before, every
useSelectedThreadGitActionsmount fired a quiet refresh, so opening the Git overview, branches, commit, confirm, or review sheet triggered one. Those sheets are native-stack screens aboveThreadRouteScreen, which stays mounted and holds the same (environment, cwd) atom. The atom is already live, so opening a sheet adds a subscriber and does not refresh again. This is fine. The route already refreshed on selection and on every reconnect. The sheets read the livesubscribeVcsStatusstream, not a one-time snapshot. Every git mutation refreshes afterward, and the overview sheet keeps pull-to-refresh and its Refresh button. A sheet opened without the route underneath (a deep link) mounts the atom itself and refreshes as before.What this improves
useEffectEvent.refreshes once per connection, shared by every subscriber, and again on reconnect).vcsStatusRevalidation.test.ts: waiting for the connection before the first refresh, debounced wake with teardown, and the idle target.Effects
apps/mobile/src/state/use-selected-thread-git-actions.ts: 1 -> 0. Other touched files: 0 -> 0.Effect report
From
scripts/effect-report.ts --compare github/main(harness in #24980).apps/mobile/src/state/use-selected-thread-git-actions.tsRender scenarios: Mobile has no scenario harness yet; this change is mobile-only.
Verification
vp test run packages/client-runtime/src/state/vcsStatusRevalidation.test.ts apps/mobile/src/state/use-selected-thread-git-actions.test.ts: 15 passedvp run check:changed: format, lint, knip, and typecheck (34 packages) pass. Tests: 2701/2702. The one failure wasPullRequestCodeTab.browser.test.tsx("Cannot verify that chromium request interception is active after 5s"), an environment flake in a file this PR does not touch. It passes on rerun.Model: Claude Opus 5.5, Claude Code harness.
🤖 Generated with Claude Code