Skip to content

Message cannot be scrolled automatically. #155

Closed

Description

@zFitness

When the message ends, you cannot scroll to.

Activity

  1. omgoshjosh commented on Aug 16, 2026

    @omgoshjosh

    Fixed in #182.

    Root cause: the transcript is an inverted FlatList, so "scroll to the newest message" means scrolling to offset 0, not to the end — and nothing was observing content growth, so nothing ever scrolled at all.

    The fix follows new content via a signature over (message count, last message id, last part count, last part text length). That changes both when a new message arrives and when the last one is edited in place, so a reply that streams in also follows, not just a completed one.

    It deliberately does not follow unconditionally: shouldAutoScroll only scrolls when the view is already within 200px of the newest message, so scrolling up to read history isn't yanked away mid-sentence. The scroll-to-bottom button uses the same predicate rather than its own copy, so the two can't disagree about where "bottom" is.

  2. Qiiks commented on Aug 21, 2026

    @Qiiks

    Fixed by the merge of #182 into the Qiiks/opencode-mobile maintenance branch (maint/v0.4.16) — shipping in release v0.4.16-maint.1 (APK will be attached there). Upstream review of #182 is still recommended; this merge keeps the fork usable while it waits.

  3. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Claimed: Ember — dev-workflow lead for the #155 auto-scroll fix. Branch: fix/auto-scroll-155.

    Scope: transcript follows new content in the inverted FlatList (follow signature + shouldAutoScroll guard so a user scrolled up reading history is never yanked).

    Noted: PR #182 claims this fix but is unmergeable as a whole (49 files, spans the keyboard, SSE, voice-alert and other work) — this task ships a scoped fix against current main, reusing #182's verified approach where it applies. The SSE lead's claim on #182 covers #189/#186 only, so no overlap on the auto-scroll part. Investigating first; fix lands via PR with typecheck + full test suite before merge.

  4. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Context pack — #155 auto-scroll (maintained by the lead; artifacts win over this pack)

    Stage: 1 done, 3 next (implementation). Lead: Ember — dev-workflow lead, branch fix/auto-scroll-155, worktree ~/workspace/opencode-mobile-autoscroll-155 at HEAD 2667ad5c. Claim: comment 6856121059. Work Log: https://app.notion.com/p/417ac25eb49f812a9aeccd169d621db8 (In progress). No Notion design/PRD exists for this; the spec is this issue + #182's approach + the acceptance criteria below.

    Stage 1 verdict (verified against main @ 2667ad5)

    Sweep (defect class: inverted FlatList without content-growth follow)

    • Command: grep -rn "inverted" --include="*.tsx" --include="*.ts" app src on main @ 2667ad5.
    • Hit list: exactly ONE inverted FlatList in the app — app/session/[id].tsx:760 (the transcript). All other FlatLists are non-inverted lists/pickers where follow-newest does not apply. No siblings to fix; no pattern sweep beyond this site.

    Design gap (bug lane, recorded before the fix starts)

    • No design ever specified the transcript scroll behavior. The inverted FlatList was built with a manual scroll button only and nothing observing content growth, so a streaming reply never followed. The gap is the missing follow rule itself: follow new content when the view is already near the newest message, never yank a user reading history. The fix records that rule in code (shouldAutoScroll + shared 200px threshold) and in tests.
    • Lane: simple fix (root cause known, one component, no contract change). No Notion design stage. Stage 4 applies in full.

    Acceptance criteria

    1. New message arriving, and a reply streaming in, scrolls to offset 0 when the view is within 200px of the newest message.
    2. A user scrolled up reading history (>200px away) is never yanked back down.
    3. Scroll-to-bottom button visibility uses the same predicate/threshold as auto-follow (no second copy of 200).
    4. maintainVisibleContentPosition stays; loading older pages via onEndReached must not jump the view.
    5. Typecheck clean + full test suite green, including the ported auto-scroll tests.
    6. Verified on device (Work Log: verify on device) — emulator/physical proof, not just unit tests.
    7. Issue Message cannot be scrolled automatically. #155 closed by the scoped PR, not by fix: keyboard/composer (#156), auto-scroll (#155), dead SSE (#186), blocked SSE startup (#189), stale transcripts, uncopyable text, axis-perfect table scrolling, 50-session list cap, transcript flash, dark-mode contrast #182.

    Decisions

    Open residuals

    • None. Device proof is part of the acceptance criteria (criterion 6), owed before merge per the Work Log.

    Next action

    Stage 3 verdict (PR #238, head 8c7747a, read back via API)

    Next action

    • Done — task complete. No release from this task (release is the product owner's separate act).

    Stage 4 verdict: PASS (comment 6856376173 on PR #238, read back via API)

    • Checked against head 8c7747a. Both passes run by the reviewer in sequence (no subagents at that depth). Diff read via gh api. Gates re-run in the real worktree: typecheck exit 0, full suite 405 pass / 0 fail.
    • Test-count delta independently verified: baseline re-run at 2667ad5 = 395 pass / 0 fail; PR head = 405 pass / 0 fail; 50 to 51 test files. AC-mapped exception covers the +10 (all map to ACs 1-3). Pure-helper lib-test convention confirmed on main (40 src/lib/*.test.ts files, node:test, no mocks).
    • Compliance: ACs 1-5 PASS, AC 6 device proof owed (release residual, not a merge block), AC 7 PASS on merge. Bug hunt: no validated findings. Two filed notes (not fails): scrollEventThrottle 100ms leaves scrollOffsetRef up to ~100ms stale mid-fling (narrow yank window, pre-existing); signature counts only newest-message text length, so non-text-part growth alone would not trigger a follow.
    • CI at review time: typecheck/test/secret-scan green; build, iOS Simulator build, activation-e2e still pending. Merge waits on CI green.

    Next action

    • Stage 5: merge on CI green + verdict read back (done), then Stage 5.5 sync.

    Stage 5 update (branch brought up to date with main)

    Stage 5 verdict: MERGED (read back via API)

    Stage 5.5 outcome: PRD updated, TDD checked

    Open residuals

    Next action

    • Done — task complete. No release from this task (release is the product owner's separate act).
  5. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Stage 3: scoped fix shipped as PR #238 (head 8c7747a, branch fix/auto-scroll-155).

    Signature decision: kept #182's 2-field form (count:length) — the only case the 4-field form would catch is a same-length swap of the newest message, negligible for a streaming transcript.

    Gates (worktree, base 2667ad5): npm run check:versions clean (0.4.17/44); tsc --noEmit clean; full suite 405 pass / 0 fail (395 baseline + 10 ported auto-scroll tests). Device proof (criterion 6) still owed before merge.

  6. added a commit that references this issue on Nov 13, 2026
  7. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Merged: PR #238, merge commit cad292a (main HEAD, read back). Stage 4 verdict PASS (comment 6856376173) on the reviewed head 8c7747a; branch updated to a2146cf by a clean merge of main (#235 only); CI green on a2146cf.

    Shipped: scoped auto-scroll fix — transcript follows new content via a signature over (message count, newest-message text length) with a shouldAutoScroll guard (200px threshold, shared with the scroll-to-bottom button), so a user scrolled up reading history is never yanked. 3 files +181/-2: new src/lib/auto-scroll.ts + test (10 cases, ported from #182), wiring in app/session/[id].tsx reusing scrollToBottom. Full suite 405 pass / 0 fail.

    Design/PRD sync: PRD section 3.4 updated with the follow behavior (https://app.notion.com/p/40bac25eb49f8112afebd66e0e8475b6); TDD checked — no transcript scroll behavior described there, no update needed.

    Residuals: device proof owed on the v0.4.18 build before it ships (owner phone check). PR #182 stays open — its other parts belong to their own tasks; this issue is closed by the scoped PR, not by #182.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions