Use this for maintainer or agent execution work that is not a user-facing bug report or feature request.
Keep the issue concrete enough that an agent can understand the target, scope, and verification path.
Goal
Make the session timeline scroll controller the single source of truth for scroll decisions so that upward reading, bottom follow, jump-to-latest, and resize-driven restore behavior no longer depend on three parallel owners that can race each other.
Concretely, when this task is done:
- the controller owns the decision state,
- history reading preserves anchor position instead of snapping back to latest,
- bottom-follow behavior is still preserved when the user is actually pinned to latest,
- and the current workaround logic can be retired from the auxiliary scroll helpers.
Scope
In scope:
- Consolidate scroll ownership around
session-timeline-scroll-controller.ts.
- Move
createAutoScroll toward a pure measurement + imperative-scroll helper with no independent follow / userScrolled state.
- Remove
bottomFollowLock as a parallel decision owner from use-session-scroll-dock.ts.
- Preserve current timeline UX semantics while simplifying ownership.
- Add focused unit and E2E coverage for the consolidated behavior.
Out of scope:
Relevant files or context
Background:
- The session timeline currently has 3 parallel scroll owners that can race each other:
createAutoScroll — ResizeObserver auto-snap-bottom with internal userScrolled bookkeeping
use-session-scroll-dock.ts — bottomFollowLock with 3s expiry plus followBottom() scheduling
session-timeline-scroll-controller.ts — high-level scroll intent state machine (following_latest, reading_history, restore_latest, etc.)
Recent regressions that motivated this follow-up:
- First regression: scrolling to a history position, then receiving a new turn, no longer auto-snapped correctly. The minimum patch suppressed
createAutoScroll ResizeObserver behavior for 500ms after wheel-up.
- Second regression: scrolling to a position, then scrolling again, snapped the viewport back to bottom. The minimum patch changed controller observe behavior, widened gesture-based lock cancellation, and reordered
message-timeline.tsx onScroll handling.
Likely files:
packages/app/src/pages/session/session-timeline-scroll-controller.ts
packages/app/src/pages/session/use-session-scroll-dock.ts
packages/app/src/pages/session/use-session-timeline-interaction.ts
packages/app/src/pages/session/message-timeline.tsx
- corresponding unit + E2E tests
Related context:
Verification
- Unit tests cover:
- weak wheel or touch gesture transitions the controller into
reading_history
content_resize during reading_history restores anchor rather than latest
- submit / layout reset while returning to latest restores the latest position correctly
- E2E covers:
- scroll up into history, receive a new turn, viewport stays at the history position
- click Jump to Bottom, viewport returns to latest
- Manual verification confirms:
- pinned-bottom behavior still follows latest
- reading history no longer snaps back to bottom unexpectedly
- nested scroll containers do not hijack main timeline state
- No parallel
userScrolled state remains inside createAutoScroll
- No parallel
bottomFollowLock remains as an independent owner
Execution mode
Agent should investigate and propose a plan first
Use this for maintainer or agent execution work that is not a user-facing bug report or feature request.
Keep the issue concrete enough that an agent can understand the target, scope, and verification path.
Goal
Make the session timeline scroll controller the single source of truth for scroll decisions so that upward reading, bottom follow, jump-to-latest, and resize-driven restore behavior no longer depend on three parallel owners that can race each other.
Concretely, when this task is done:
Scope
In scope:
session-timeline-scroll-controller.ts.createAutoScrolltoward a pure measurement + imperative-scroll helper with no independent follow /userScrolledstate.bottomFollowLockas a parallel decision owner fromuse-session-scroll-dock.ts.Out of scope:
Relevant files or context
Background:
createAutoScroll— ResizeObserver auto-snap-bottom with internaluserScrolledbookkeepinguse-session-scroll-dock.ts—bottomFollowLockwith 3s expiry plusfollowBottom()schedulingsession-timeline-scroll-controller.ts— high-level scroll intent state machine (following_latest,reading_history,restore_latest, etc.)Recent regressions that motivated this follow-up:
createAutoScrollResizeObserver behavior for 500ms after wheel-up.message-timeline.tsxonScrollhandling.Likely files:
packages/app/src/pages/session/session-timeline-scroll-controller.tspackages/app/src/pages/session/use-session-scroll-dock.tspackages/app/src/pages/session/use-session-timeline-interaction.tspackages/app/src/pages/session/message-timeline.tsxRelated context:
Verification
reading_historycontent_resizeduringreading_historyrestores anchor rather than latestuserScrolledstate remains insidecreateAutoScrollbottomFollowLockremains as an independent ownerExecution mode
Agent should investigate and propose a plan first