Skip to content

fix(web): reopen a thread that was following its stream at the end - #14916

Closed
santiago-ramos-02 wants to merge 1 commit into
pingdotgg:mainfrom
santiago-ramos-02:fix/timeline-restore-live-edge-v2
Closed

santiago-ramos-02 wants to merge 1 commit into
pingdotgg:mainfrom
santiago-ramos-02:fix/timeline-restore-live-edge-v2

Conversation

@santiago-ramos-02

Copy link
Copy Markdown

Problem

A thread that is following a streaming reply can be remembered as a reading position when the user switches away. While a turn streams, new output can land below the viewport before the follow scroll catches up, and a scroll event in that window saves atEnd: false. Coming back after the reply finishes restores that old offset, far above the latest message, with "Scroll to end" showing and live follow off.

Rebuilt on V2 as asked in #13602. The V2 timeline keeps up with plain streamed text, so it happens less often than before: in a sampled plain-text reply the saved position never left the end. Replies with tool calls still trigger it: in one sampled tool-heavy reply, 28 of 1,396 samples saved atEnd: false while the thread was following.

Change

MessagesTimeline remembers a thread as at the end while live follow is active: atEnd: isAtEnd || (liveFollowEnabled && isLiveFollowLatched() && !anchoredEndSpace). ChatView passes its follow latch as isLiveFollowLatched, because a scroll gesture releases the latch before liveFollowEnabled re-renders. A user scroll, the minimap, or other navigation releases the latch, so a real reading position is still saved. A new turn held near the top by anchored end space is not following the end, so its position is kept too.

ChatView's onIsAtEndChange already ignores isAtEnd === false while live follow is active. The remembered position now uses the same rule.

Scope and approval

Fixes #13601, triaged as accepted. Replaces #13602, closed for the V2 rewrite. This does not touch #5903 or #12372: when the remembered position is at the end, a restore can still land short of it after late row measurement, with the pill hidden. That is a separate problem and is not changed here.

Verification

  • vp test run src/components/chat/MessagesTimeline.test.tsx fails on current main (expected false to be true) and passes with the change. The test scrolls with the list 500 px short of the end and checks the remembered position with the latch held, the latch released while liveFollowEnabled is still true, follow off, and an anchored first send. MessagesTimeline.test.tsx and timelineScrollAnchoring.test.tsx: 92 passed. Typecheck, lint, and format are clean for apps/web.
  • In the browser (dev server on V2, synthetic data, GPT-6-Astra Medium): a thread follows a reply with tool calls, I switch to another thread while the follow scroll trails new output, wait for the reply to finish, and switch back. I read the remembered position right after switching away.
    • Before: switched at 596 px of follow lag, remembered atEnd: false, reopened 793 px above the end with "Scroll to end" showing.
    • After: switched at 616 px of follow lag, remembered atEnd: true, reopened at the end.
  • Not checked: mobile, which has its own timeline and is not changed here.

Before:

before.mp4

After:

after.mp4

Model: Claude Opus 5.5 (1M context), running in Claude Code inside T3 Code.

While a turn streams, the follow scroll trails new output, so scroll events report the thread as not at the end. The per-thread position cache saved that as a reading position, and returning to the thread restored it far above the latest message. Remember the thread at the end while live follow is latched and no anchored end space holds a first send near the top.
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Oct 2, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 2, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 6a9f043

Macroscope's review found this PR approvable — This is a focused, self-contained web bug fix that corrects per-thread scroll restoration during transient live-follow lag while preserving user navigation and anchored-turn behavior. The changed production path is small and accompanied by regression coverage, with no defaults, schema, infrastructure, security, billing, or static-analysis changes.

Notes:

  • Diff unchanged. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 2, 2026 — with ChatGPT Codex Connector
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 2, 2026 22:32

Dismissing prior approval to re-evaluate 6a9f043

@coderabbitai

coderabbitai Bot commented Oct 2, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e385a9ad-8dd7-429a-9bff-722c112412ba
📥 Commits

Reviewing files that changed from the base of the PR and between ca7df39 and 6a9f043.

📒 Files selected for processing (3)
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/MessagesTimeline.test.tsx
  • apps/web/src/components/chat/MessagesTimeline.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

When a thread is following new output, MessagesTimeline can now save its position as at-end even if the list reports that it has not reached the end. ChatView supplies the live-follow latch used for this decision.

Changes

Live-follow timeline position

Layer / File(s) Summary
Provide the live-follow latch
apps/web/src/components/ChatView.tsx, apps/web/src/components/chat/MessagesTimeline.tsx
ChatView compares live-follow and anchor user-scroll generations and passes the latch callback to MessagesTimeline.
Save position during live follow
apps/web/src/components/chat/MessagesTimeline.tsx, apps/web/src/components/chat/MessagesTimeline.test.tsx
When live follow is enabled and latched, and there is no anchored end space, MessagesTimeline saves the position as at-end. Tests cover released or disabled live follow and an active first-send anchor.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: maria-rcks

Fixed issue severity: <fixed_issue_severity>Low</fixed_issue_severity>

Merge Risk: ⚪ Minimal · up to 6a9f0

The change appears ready to merge after normal checks: it restores live-edge position during active follow without treating handled user navigation as follow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: restoring a thread at the end when it was following a streamed reply.
Description check ✅ Passed The description covers the problem, implementation, scope and approval, focused verification, browser results, screenshots, limitations, and agent attribution.
Linked Issues check ✅ Passed The PR addresses #13601. MessagesTimeline now saves the position as at the live end when live follow is enabled and latched, unless anchored end space contains a new turn near the top. ChatView su…
Out of Scope Changes check ✅ Passed The changed source files and tests support #13601. The changes only add the live-follow latch to ChatView, apply it to timeline position saving, and test the related cases. No unrelated product beha…
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@juliusmarminge

Copy link
Copy Markdown
Member

Note

Grok responding on behalf of Julius.

Closing as superseded by #17348, which just merged and fixes #13601. It covers the cases this PR handles (lagging follow scroll on thread switch, a gesture releasing follow before the re-render, and the anchored first send) plus the held thread-switch snapshot. Thanks for the work on this, it shaped the fix that landed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Returning to a thread that streamed in the background lands far above the latest message

2 participants