Before submitting
Related: #5903 (this is the stale-pill half split out per discussion there).
Area
apps/web
Steps to reproduce
- Open a thread with enough history to overflow the viewport.
- Send a prompt, then switch to a different thread while the turn runs.
- Return to the first thread after its response has landed.
- Observe the viewport: it can be stranded above the newest message, often farther up than where it was left.
- Observe the "Scroll to end" pill: it does not appear. Nudge the scroll up/down a little and the pill appears.
Expected behavior
If the viewport is not at the live edge, the "Scroll to end" pill is visible. The end-state tracking reflects the actual viewport position after a thread switch settles, including content whose height changes after rendering.
Actual behavior
The pill stays hidden on the stranded viewport, as if the state believes it is already scrolled to the end when it is not. Only a manual scroll nudge (which fires real scroll events) makes the pill appear.
Impact
Major degradation or frequent failure
The stranded viewport looks settled, and the missing pill removes the visible recovery path; END key is the only recourse.
Version or commit
Local checkout @ dc49767
Environment
T3 Code web/desktop client (Linux). Provider-independent; affects the shared web timeline.
Logs or stack traces
No associated exception or console error.
Screenshots, recordings, or supporting files
No response
Workaround
Nudge the scroll wheel up/down until the pill appears, or press END to jump to the live edge.
Suspected mechanism (from code reading, not runtime-verified)
- The position cached for the background thread was saved with
atEnd: true (timelineScrollAnchoring.ts), so the thread-switch effect in ChatView.tsx takes the scrollToEnd branch and hides the pill.
- Transient
isAtEnd=false scroll events during settle are suppressed by the post-open live-follow guard (onIsAtEndChange early return when the scroll generation still matches) plus the 150ms show-debounce.
- If row heights settle after that (streamed markdown/tool output measuring late), the viewport strands with
isAtEndRef stale-true and no further scroll event corrects it.
- A manual wheel gesture bumps the generation via
cancelTimelineLiveFollowForUserNavigation, letting the next isAtEnd=false through so the pill appears.
A post-settle reconciliation (re-resolve isAtEnd from the list state a frame or two after position restore completes and sync the pill) would cover this class of stranding regardless of which layout pass caused it.
Before submitting
Related: #5903 (this is the stale-pill half split out per discussion there).
Area
apps/web
Steps to reproduce
Expected behavior
If the viewport is not at the live edge, the "Scroll to end" pill is visible. The end-state tracking reflects the actual viewport position after a thread switch settles, including content whose height changes after rendering.
Actual behavior
The pill stays hidden on the stranded viewport, as if the state believes it is already scrolled to the end when it is not. Only a manual scroll nudge (which fires real scroll events) makes the pill appear.
Impact
Major degradation or frequent failure
The stranded viewport looks settled, and the missing pill removes the visible recovery path; END key is the only recourse.
Version or commit
Local checkout @ dc49767
Environment
T3 Code web/desktop client (Linux). Provider-independent; affects the shared web timeline.
Logs or stack traces
No associated exception or console error.
Screenshots, recordings, or supporting files
No response
Workaround
Nudge the scroll wheel up/down until the pill appears, or press END to jump to the live edge.
Suspected mechanism (from code reading, not runtime-verified)
atEnd: true(timelineScrollAnchoring.ts), so the thread-switch effect inChatView.tsxtakes thescrollToEndbranch and hides the pill.isAtEnd=falsescroll events during settle are suppressed by the post-open live-follow guard (onIsAtEndChangeearly return when the scroll generation still matches) plus the 150ms show-debounce.isAtEndRefstale-trueand no further scroll event corrects it.cancelTimelineLiveFollowForUserNavigation, letting the nextisAtEnd=falsethrough so the pill appears.A post-settle reconciliation (re-resolve
isAtEndfrom the list state a frame or two after position restore completes and sync the pill) would cover this class of stranding regardless of which layout pass caused it.