Repository navigation
[Bug]: Desktop keeps rebuilding the local environment's shell state, causing hard rerenders (nightly 20261005.2667) #16018
Description
Activity
- changed the title
[-][Bug]: Desktop UI keeps rebuilding environment shell state and kicks the open thread to the new-thread screen (nightly 20261005.2667)[/-][+][Bug]: Desktop keeps rebuilding the local environment's shell state, causing hard rerenders (nightly 20261005.2667)[/+]on Oct 5, 2026 Note
Grok responding on behalf of Julius.
Triage
Thanks for the really thorough trace, @itsdaymi! Your span timings lined up closely with what's in the code, and the redirect path is still present on current
main(cf3e714b0f).What I found
The redirect.
ThreadRouteViewtreats any shell snapshot as a finished bootstrap:// apps/web/src/components/ThreadRouteView.tsx:75 const bootstrapComplete = shell.data?.snapshot._tag === "Some";
resolveThreadRouteRenderStatethen returns"missing"when that snapshot has no row for the open thread, and the effect at lines 144–158 navigates to/whenever the environment still has other threads. A rebuiltEnvironmentShellStateseeds fromcache.loadShell, andsetSynchronizingkeeps that snapshot while flippingstatusto"synchronizing", so the first value the route sees is the cached snapshot. The authoritativeGET /api/orchestration/shelllands about 24ms later. Since shell cache writes are delayed (500ms, then every 10s inrunCachePersistence), a recently created thread is often missing from the cache, which is why recent threads are the ones that get dropped. Elsewhere,createAllEnvironmentProjectSnapshotsReadyAtomalready requiresstatus === "live"before trusting a snapshot.The repeated rebuilds. These look like restarts of the shell stream rather than a new supervisor.
shellStateChangesand the server-config stream both go throughfollowStream, whichswitchMaps to a new effect when the environment's catalog entry isn'tEqual. That matches your persist,acquireSupervisor, make, then shell fetch sequence.installEntryLockedwithretainEquivalentRuntimereturns before writing the catalog, so the 3s platform poll alone doesn't seem to be the trigger, andlearnRoutesreturns early for a platform primary route. The shell atom also isn'tAtom.keepAlive, so a gap with no subscribers would rebuild it too. The writer that changes the entry between makes hasn't been pinned down yet; tracing whetherentrieschanges, and whetherlearnRoutes,setEnabled, orsetCompatibilityruns at those moments, would be the next step.Related: #14689 covers the same cached-snapshot redirect for thread links, and open PR #14697 ("fix(web): keep thread links open until the shell is live") addresses that redirect half but not the rebuild loop.
Likely fix area
- The redirect in
ThreadRouteView/resolveThreadRouteRenderState: one option is treating"cached"and"synchronizing"as"loading"rather than"missing", matching the"live"bar used elsewhere (overlapping with fix(web): keep thread links open until the shell is live #14697). - The rebuild loop in
followStreamand whichever catalog writer is changing the environment entry, or the shell atom's lifetime.
A maintainer will decide on the fix direction.
- The redirect in
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 5, 2026
Before submitting
Area
apps/web
Steps to reproduce
0.0.46-nightly.20261005.2667with only the local environment, a few projects and several threads running at once.I don't have a deterministic repro. In my session it happened every 5–40 s while the window was visible and never while it was hidden.
Expected behavior
Shell state for the local environment is built once per app session, then kept up to date over the existing subscription.
Actual behavior
EnvironmentShellState.makekeeps running for the same local environment: 21 times in about 7 minutes. Each run reloads the IndexedDB cache, re-subscribes and fetchesGET /api/orchestration/shell(~1.5 MB). The UI visibly hard-rerenders each time. The desktop sees the app's readiness (appActivation) flip false/true at the same timestamps (DesktopAppActivation.setRendererReadypairs at 12:25:33, 12:29:48, 12:33:48 local).What the traces rule out:
installEntryLockedtakes ~0 ms on every 3 sreconcilePlatform, so it returns at theretainEquivalentRuntimecheck.EnvironmentRpc.getInitialServerConfigdoesn't repeat.followStreamrestart from anentrieswrite. None of the writers ofentriesshow up during the rebuilds: nosetCompatibility,setEnabled,learnRoutes,replaceRoutesLockedorremovePlatformEnvironmentspans.EnvironmentSupervisor.signal/rpcSession.probefired only 3 times and not at the rebuild timestamps.EnvironmentServerConfigState.persist, preview automationfocusHostcalls, or the 3 s registrations poll.That leaves the shell
stateAtom(environmentId)atom inpackages/client-runtime/src/state/shell.tsitself. It isn't keep-alive, so if every component subscribed to it unmounts at once, it is disposed and rebuilt on the next mount. I couldn't confirm what unmounts its subscribers without a debugger on the packaged renderer.Impact
Major degradation or frequent failure
Version or commit
T3 Code (Nightly) 0.0.46-nightly.20261005.2667 (37de6cb), desktop.
This looks like a regression in this build:
setRendererReadyflaps went from a few per day up to the 11:37 auto-update from 20261004.2657 to every few minutes afterwards. I looked at the two client-runtime connection changes in that range, #15467 and #15468, and the traces don't implicate them for the local platform environment (see above). None of the 6 commits onmainafter 37de6cb touchshell.ts,registry.ts,runtime.tsorThreadRouteView.tsx.Environment
macOS (Apple silicon), Electron 44.4.2 / Chrome 152, desktop app with only the local environment, background activity profile
performance. Several Claude Agent and Codex threads running at the same time, one using preview automation.Logs or stack traces
Workaround
None reliable. #14697 would stop the redirect half. Rolling back to
0.0.46-nightly.20261004.2657might help if this is a regression in today's build.Investigated with Claude Code (Claude Opus 5.5) from local trace logs, the shipped bundle and the source at 37de6cb.