You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Diff panel keeps jumping back from "Uncommitted" to "Changes" while an agent turn is running #15352
I searched existing issues and did not find a duplicate.
I included enough detail to reproduce or investigate the problem.
Area
apps/web
Steps to reproduce
In a thread for a git repo, send a prompt so the agent starts working.
While the turn is running, open the Diff panel (it opens on "Changes").
Expand a few files.
Pick "Uncommitted" in the scope dropdown.
Wait a few seconds without touching anything.
Expected behavior
The panel stays on "Uncommitted" until I pick another scope, and files I expanded stay expanded.
Actual behavior
A few seconds later the dropdown switches back to "Changes" on its own (about 8s in the attached recording, with no input from me). This keeps happening for as long as the turn runs, so "Uncommitted" is basically unusable mid-turn. The files I expanded also collapse again every time.
constactiveThreadRef=useMemo(()=>(activeThread ? scopeThreadRef(activeThread.environmentId,activeThread.id) : null),[activeThread],);// ...useLayoutEffect(()=>{constexplicitThreadRef=explicitDiffOpenRef.current;explicitDiffOpenRef.current=null;// Generic openings always show Changes, including tab fallbacks and thread changes.if(diffOpen&&activeThreadRef&&explicitThreadRef!==activeThreadRef){useDiffPanelStore.getState().selectGitScope(activeThreadRef,"branch");}},[activeThreadRef,diffOpen]);
activeThreadRef is memoized on the whole activeThread object, and that object gets a new identity on every thread update while a turn is streaming. So each update creates a new ref, the layout effect runs again and calls selectGitScope(..., "branch"), even though the panel was already open and the thread didn't change.
The collapse reset is probably a side effect of the same thing: in DiffPanel.tsx, collapseScopeKey / codeViewMountKey are derived from the selected scope, so getting forced back to branch swaps the collapse state and remounts the code view.
Keying the memo/effect on environmentId + threadId (or activeThreadKey) instead of the thread object should make it fire only when the panel opens or the thread actually changes.
Impact
Major degradation or frequent failure
Version or commit
T3 Code (Nightly) 0.0.46-nightly.20261003.2638 (source checked at main @ a21702d)
Environment
macOS 27.2, desktop app
Logs or stack traces
Screenshots, recordings, or supporting files
Screen.Recording.2026-10-04.at.00.42.42.mov
Workaround
Wait for the turn to finish, then switch to "Uncommitted".
Thanks for the clear report and the recording, @fatalist! This reproduces on current main (8d84666), your read of the cause is right, and I didn't find a duplicate.
What happens
In ChatView (apps/web/src/components/ChatView.tsx, around line 2169), activeThreadRef is memoized on the whole thread shell object:
A running turn keeps committing events, and each coalesced shell delta replaces the shell object, so the memo creates a new { environmentId, threadId } even though the ids haven't changed. That new ref re-runs the layout effect that forces the Changes scope (around line 2222). Its guard compares references, and explicitDiffOpenRef is only set by a timeline click, so after you pick Uncommitted the next shell update calls selectGitScope(..., "branch"). The few seconds' delay in your recording is just the wait for the next delta.
The collapsed files come from the same reset. DiffPanel keys the code view on a collapseScopeKey that includes the scope, so switching back to Changes remounts it. The same effect also clears a selected turn (Latest turn or Turn N).
Merged #15005 didn't add the re-run itself. It changed the forced scope from unstaged to branch, which is why Uncommitted is now the choice that gets overwritten.
Likely fix area
One option is to key the activeThreadRef memo on environmentId and threadId, the way routeThreadRef and ChatHeader already do, so the effect runs only when the panel opens or the thread changes.
Another is to have the effect compare thread ids instead of object identity.
Before submitting
Area
apps/web
Steps to reproduce
Expected behavior
The panel stays on "Uncommitted" until I pick another scope, and files I expanded stay expanded.
Actual behavior
A few seconds later the dropdown switches back to "Changes" on its own (about 8s in the attached recording, with no input from me). This keeps happening for as long as the turn runs, so "Uncommitted" is basically unusable mid-turn. The files I expanded also collapse again every time.
Likely cause
This looks like it came in with d1034d6 (#15005). In
ChatView.tsx:activeThreadRefis memoized on the wholeactiveThreadobject, and that object gets a new identity on every thread update while a turn is streaming. So each update creates a new ref, the layout effect runs again and callsselectGitScope(..., "branch"), even though the panel was already open and the thread didn't change.The collapse reset is probably a side effect of the same thing: in
DiffPanel.tsx,collapseScopeKey/codeViewMountKeyare derived from the selected scope, so getting forced back tobranchswaps the collapse state and remounts the code view.Keying the memo/effect on
environmentId+threadId(oractiveThreadKey) instead of the thread object should make it fire only when the panel opens or the thread actually changes.Impact
Major degradation or frequent failure
Version or commit
T3 Code (Nightly) 0.0.46-nightly.20261003.2638 (source checked at main @ a21702d)
Environment
macOS 27.2, desktop app
Logs or stack traces
Screenshots, recordings, or supporting files
Screen.Recording.2026-10-04.at.00.42.42.mov
Workaround
Wait for the turn to finish, then switch to "Uncommitted".