Goal
Session timeline scrolling should have one clear owner and one user-facing rule:
- If the user is following the latest turn and submits a message, the timeline must stay with the latest turn.
- Browser layout resets, markdown height changes, composer dock resize, rendered-window updates, tool output expansion, and internal scroll events must not leave the viewport near the top.
- If the user is deliberately reading older history, the app must preserve that position and avoid pulling them down.
This issue exists because the current fixes have become path-specific. Recent scroll-to-top bugs were fixed through separate guardrails, but a new 2026-05-10 session export still showed a submit-time jump near the top after the timeline first reached bottom.
Scope
In scope:
- Redesign the session timeline scroll ownership model before more tactical patches.
- Define a small state machine for whether the timeline is following latest, reading history, navigating to a message, or recovering from a layout reset.
- Make one session-level controller own the decision to follow bottom, pause following, restore bottom, or preserve reading position.
- Separate deliberate user intent from browser/programmatic layout movement in both code and diagnostics.
- Add diagnostics that explain why following was paused or restored, not only that a jump happened.
- Add deterministic E2E coverage for submit-time top resets, including the case where the reset is currently marked as
user_scrolled=true.
Out of scope:
Relevant files or context
Recent related issues:
Recent evidence from a 2026-05-10 session export:
- The route, visible session, and timeline session stayed the same.
- The timeline first reached bottom after submit.
- Less than one second later it jumped near the top and emitted
incident.session_scroll_jump_to_top.
- The jump sample had
user_scrolled=true, which suggests the submit-time bottom-follow guard can be cancelled by a gesture/user-scroll path even when the visible effect is a layout/browser reset.
- The viewport returned to bottom afterward, so the failure is not data loss or a permanent remount. It is a broken ownership/intent decision during the submit window.
Likely code paths:
packages/app/src/pages/session/use-session-timeline-interaction.ts
packages/app/src/pages/session/use-session-scroll-dock.ts
packages/app/src/pages/session/message-timeline.tsx
packages/app/src/pages/session/use-session-history-window.ts
packages/app/src/pages/session/use-session-hash-scroll.ts
packages/app/src/context/renderer-diagnostics.ts
packages/ui/src/hooks/create-auto-scroll.tsx
packages/ui/src/components/scroll-view.tsx
packages/ui/src/components/message-part.tsx
Verification
Design verification:
- Write a short design document before implementation.
- The design must state the user-facing scroll rules in plain language.
- The design must list the allowed sources that can change follow state and the sources that cannot.
- The design must explain how submit, streaming, composer resize, history reading, hash/message navigation, tool output resize, and browser layout resets interact.
Code verification for the eventual PR:
- Targeted unit tests for the scroll state machine or controller.
- Targeted tests for diagnostics fields that explain why follow state changed.
- E2E repro for submit from bottom followed by a forced top reset.
- E2E repro for submit from bottom followed by a forced top reset that is also marked as a recent scroll gesture.
- E2E check that real user reading history is still respected.
- Manual Electron check of the changed session path before claiming completion.
Execution mode
Human review required before code changes.
Goal
Session timeline scrolling should have one clear owner and one user-facing rule:
This issue exists because the current fixes have become path-specific. Recent scroll-to-top bugs were fixed through separate guardrails, but a new 2026-05-10 session export still showed a submit-time jump near the top after the timeline first reached bottom.
Scope
In scope:
user_scrolled=true.Out of scope:
packages/opencode/.Relevant files or context
Recent related issues:
Recent evidence from a 2026-05-10 session export:
incident.session_scroll_jump_to_top.user_scrolled=true, which suggests the submit-time bottom-follow guard can be cancelled by a gesture/user-scroll path even when the visible effect is a layout/browser reset.Likely code paths:
packages/app/src/pages/session/use-session-timeline-interaction.tspackages/app/src/pages/session/use-session-scroll-dock.tspackages/app/src/pages/session/message-timeline.tsxpackages/app/src/pages/session/use-session-history-window.tspackages/app/src/pages/session/use-session-hash-scroll.tspackages/app/src/context/renderer-diagnostics.tspackages/ui/src/hooks/create-auto-scroll.tsxpackages/ui/src/components/scroll-view.tsxpackages/ui/src/components/message-part.tsxVerification
Design verification:
Code verification for the eventual PR:
Execution mode
Human review required before code changes.