Skip to content

[Task] Consolidate session timeline scroll owners into single state machine #595

@Astro-Han

Description

@Astro-Han

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:
    1. createAutoScroll — ResizeObserver auto-snap-bottom with internal userScrolled bookkeeping
    2. use-session-scroll-dock.tsbottomFollowLock with 3s expiry plus followBottom() scheduling
    3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priorityappApplication behavior and product flowstaskMaintainer or agent execution tasktech-debtInternal cleanup and maintainability debtuiDesign system and user interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions