Skip to content

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

@Astro-Han

Description

@Astro-Han

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 change 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, then implement only after the plan is approved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priorityappApplication behavior and product flowsenhancementNew feature or requesttech-debtSupplemental cleanup, maintainability, architecture, test, or quality debt contextuiDesign system and user interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions