Skip to content

improve: make native chat scrolling reader-managed#98258

Merged
steipete merged 15 commits into
openclaw:mainfrom
christopheraaronhogg:codex/native-chat-reader-scroll
Jul 3, 2026
Merged

improve: make native chat scrolling reader-managed#98258
steipete merged 15 commits into
openclaw:mainfrom
christopheraaronhogg:codex/native-chat-reader-scroll

Conversation

@christopheraaronhogg

@christopheraaronhogg christopheraaronhogg commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Native chat views currently move the transcript as messages, tool rows, and streaming text arrive. That can pull readers away from content they are reviewing in long or actively streaming conversations.

Fixes #98255.

Why This Change Was Made

This gives iOS and Android an explicit reader-managed scrolling model:

  • restore an opened conversation near the latest user turn instead of the absolute bottom
  • preserve that user-turn anchor while assistant and tool content streams below it
  • stop following when the reader scrolls away
  • show Jump to latest when newer content is offscreen
  • resume live-edge following only after the reader taps the control or sends a new message
  • keep restored Android reader state scoped to the owning session

The repair removes the contributor's preview/debug proof harnesses. Production state transitions and the normal app UI test are the only paths retained.

User Impact

  • Reopening a session keeps the newest prompt visible for context.
  • Sending a message intentionally brings the new turn into view.
  • Manual reading position survives streaming and tool updates.
  • Jump to latest exposes and then clears the paused-reader state predictably.

Evidence

iOS simulator end-to-end

The standard OpenClawUITests live-gateway scenario exercises the shipping app from fresh isolated state against a real local gateway using openai/gpt-5.5 via GitHub Copilot. It completes four exact live round trips, verifies prompt anchoring and Jump to latest, taps the control, manually swipes away, verifies the control reappears, taps it again, then relaunches through Control and verifies Overview.

PASS OpenClawSnapshotUITests.testLiveGatewayChatRoundTripAndControlOverview
92.125 seconds, 0 failures

Result bundle: /tmp/pr98258-ios-fresh18-multiturn-pass26.xcresult (local maintainer artifact). Fresh baseline, prompt-anchor, post-jump, manual-departure, Control, and Overview screenshots were inspected. They were not uploaded without an approved authenticated destination.

Android behavior and emulator proof

Focused controller and timeline tests cover restore, manual-scroll preservation, regenerated message IDs, newer-user transitions, tool/stream updates, jump-to-latest, and rejection of saved state from a different session. The repaired screen was also exercised in the Android emulator; fresh current-main baseline, prompt-anchor, and post-jump screenshots were inspected.

PASS ANDROID_HOME="$HOME/Library/Android/sdk" ./gradlew \
  :app:testPlayDebugUnitTest \
  --tests ai.openclaw.app.ui.chat.ChatReaderScrollControllerTest \
  --tests ai.openclaw.app.ui.chat.ChatTimelineTest

The broader local Android ktlint lane reports two unrelated current-main violations in apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt; this PR does not touch that file. Exact-head hosted Android checks remain the merge gate.

Additional validation

  • swift test --package-path apps/shared/OpenClawKit --filter ChatReaderScrollStateTests: 5 tests passed.
  • pnpm native:i18n:sync and pnpm native:i18n:check: passed.
  • git diff --check: passed.
  • Fresh local autoreview: no findings, patch correct, confidence 0.90.
  • Fresh exact-head branch autoreview against 84d9d6eef4d3cd6c74c4a94a99106f59ccced214: no findings, patch correct, confidence 0.88.

Review

  • Current main still lacks the behavior; the repaired head is rebased onto current origin/main.
  • Two earlier review findings were fixed and their threads were resolved.
  • Native review artifacts validate with zero findings at the exact head.
  • Exact-head CI is the final merge gate.

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 3, 2026, 9:52 AM ET / 13:52 UTC.

Summary
This PR adds reader-managed iOS and Android native chat scrolling with Jump to latest controls, per-session reader state, timeline revision handling, native i18n updates, and focused native tests.

PR surface: Other +1165. Total +1165 across 14 files.

Reproducibility: not applicable. as a bug reproduction: this is a feature PR. Source inspection confirms current main lacks the requested native reader-managed scroll model, and the PR body/comments provide after-fix simulator/emulator proof.

Review metrics: 1 noteworthy metric.

  • Reader-state clients: 2 native clients changed. The same transcript-follow invariant now exists in Android Compose and iOS SwiftUI, so maintainers should review the UX as one cross-client behavior.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98255
Summary: This PR is the candidate implementation for the open native reader-managed chat scrolling feature issue.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Get explicit maintainer acceptance of the native reader-managed scroll semantics before merge.

Mantis proof suggestion
Native mobile scroll behavior is gesture-sensitive and a short visible simulator or device recording would materially help final maintainer review. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify iOS and Android native chat preserves manual reader position, shows Jump to latest, and returns to live content when tapped.

Risk before merge

  • [P1] The PR changes per-session native transcript restore, follow-target, and newer-content state, so existing users may see different chat positioning after upgrade; maintainers should intentionally accept that UX before merge.

Maintainer options:

  1. Accept Native Reader Scroll State (recommended)
    If maintainers want this UX, merge after required checks finish green and the per-session restore/follow semantics are explicitly accepted.
  2. Pause For The Feature Issue
    If the reader-managed model is still unsettled, keep the linked feature issue as the product discussion and do not land this branch yet.

Next step before merge

  • [P2] Human maintainer review should decide whether to accept the new native per-session reader-managed UX; no narrow automated repair remains from this code review.

Security
Cleared: The diff is limited to native UI Kotlin/Swift, native tests, and i18n inventory; no dependency, CI, secret, package, or supply-chain surface changed.

Review details

Best possible solution:

Land the current native implementation only after maintainer acceptance of the iOS/Android scroll semantics and completion of required checks.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction: this is a feature PR. Source inspection confirms current main lacks the requested native reader-managed scroll model, and the PR body/comments provide after-fix simulator/emulator proof.

Is this the best way to solve the issue?

Yes, with maintainer acceptance: the implementation keeps the behavior in native UI/controller code and avoids gateway protocol or persisted message changes. Gateway/core changes or a browser-only reuse path would be the wrong layer for this native gesture behavior.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 8c915f068525.

Label changes

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit. Replaced prior rating: 🦐 gold shrimp.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P3: This is a native chat readability and scroll-ergonomics feature with limited blast radius, not a crash, security issue, data loss, or urgent runtime regression.
  • merge-risk: 🚨 session-state: The PR changes per-session transcript restore, follow target, newer-content, and Jump-to-latest state across native chat surfaces.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit. Replaced prior rating: 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (live_output): The PR body and comments provide after-fix iOS simulator and Android emulator proof through live command output, UI-tree output, described screenshots, and linked recordings; contributors should keep redacting private details in future proof artifacts.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body and comments provide after-fix iOS simulator and Android emulator proof through live command output, UI-tree output, described screenshots, and linked recordings; contributors should keep redacting private details in future proof artifacts.
Evidence reviewed

PR surface:

Other +1165. Total +1165 across 14 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 14 1325 160 +1165
Total 14 1325 160 +1165

What I checked:

Likely related people:

  • steipete: The live PR is assigned to steipete, the latest branch includes his repair commits, and CODEOWNERS protects ownership rules through his handle. (role: current branch adopter and CODEOWNERS owner; confidence: high; commits: 75f6eabcdda0, 007d8c8f7612, bd9878993bbb; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift, apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatReaderScrollController.kt, .github/CODEOWNERS)
  • obviyus: CONTRIBUTING.md and apps/android/README.md identify obviyus with Android app ownership, and history shows Android chat path work under Ayaan Zaidi. (role: Android app owner and adjacent contributor; confidence: medium; commits: 5568b393a86d; files: apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatMessageListCard.kt, apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatSheetContent.kt, apps/android/README.md)
  • mbelinky: CONTRIBUTING.md identifies mbelinky with the iOS app, and recent history touched the shared SwiftUI chat surface involved here. (role: iOS app owner and recent iOS chat contributor; confidence: medium; commits: 9476dda9f617; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMessageViews.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 30, 2026
@christopheraaronhogg

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the proof blocker:

Runtime proof notes:

  • iOS snapshot_ui before Jump: Jump was tappable while visible text stayed on restored context; live/tool rows were not visible.
  • iOS snapshot_ui after Jump: Jump gone; visible text included Running tools..., scroll.proof, and the live assistant reply.
  • Android uiautomator dump before Jump: Jump was clickable while the visible hierarchy did not include This live reply or scroll.proof.

Re-run checks:

  • PASS swift build --package-path apps/shared/OpenClawKit --target OpenClawChatUI
  • PASS ANDROID_HOME=$HOME/Library/Android/sdk ANDROID_SDK_ROOT=$HOME/Library/Android/sdk ./gradlew :app:testPlayDebugUnitTest --tests ai.openclaw.app.ui.chat.ChatTimelineTest
  • PASS ANDROID_HOME=$HOME/Library/Android/sdk ANDROID_SDK_ROOT=$HOME/Library/Android/sdk ./gradlew :app:installPlayDebug
  • PASS git diff --check

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Jun 30, 2026
@christopheraaronhogg

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Fixed the Android Jump-to-latest target called out in the prior review.

Implementation update:

  • Commit: 32188240d782a0ec2530fd8d958f07a9f99e2740
  • ChatTimeline now separates the reader anchor from the newest-content target:
    • scrollTargetIndex remains the active user-prompt/read anchor.
    • latestContentIndex points at the newest live/tool/stream row.
  • ChatReaderScrollController now tracks whether it is following ReadAnchor or LatestContent.
  • jumpToLatest now scrolls to latestContentIndex, clears the newer-content affordance, and resumes following the real latest content instead of returning to the prompt anchor on the next stream update.

Android proof, captured on emulator emulator-5554 / AVD adn_test in screenshot mode scene chat-proof:

Before tapping Jump to latest, after delayed stream/tool rows arrived:
- visible UI tree contains Jump to latest: 1 node
- visible UI tree contains live/tool nodes: 0 nodes
- visible context remains restored reader context, including:
  - "Restored near the latest user turn with previous context still visible..."
  - "Keep this turn near the top of the viewport when the thread reopens."

Tap source:
- Tap target was derived from the UI tree node bounds for "Jump to latest".
- Bounds center tapped: 579 2039

After tapping Jump to latest:
- visible UI tree contains Jump to latest: 0 nodes
- visible UI tree contains live/tool nodes: 3 nodes
- visible nodes include:
  - "OpenClaw · Live"
  - "This live reply is streaming below the reader's current viewport..."
  - "Running tools..."
  - "scroll.proof"

Re-run checks:

  • PASS ANDROID_HOME=$HOME/Library/Android/sdk ANDROID_SDK_ROOT=$HOME/Library/Android/sdk ./gradlew :app:testPlayDebugUnitTest --tests ai.openclaw.app.ui.chat.ChatTimelineTest
  • PASS ANDROID_HOME=$HOME/Library/Android/sdk ANDROID_SDK_ROOT=$HOME/Library/Android/sdk ./gradlew :app:installPlayDebug
  • PASS git diff --check
  • BLOCKED unrelated existing lint: ANDROID_HOME=$HOME/Library/Android/sdk ANDROID_SDK_ROOT=$HOME/Library/Android/sdk ./gradlew :app:ktlintCheck still fails on pre-existing files outside this change:
    • apps/android/app/src/main/java/ai/openclaw/app/GatewayExecApprovals.kt:100
    • apps/android/app/src/main/java/ai/openclaw/app/ui/design/ClawSurfaces.kt:3

Screenshot files were captured locally from the emulator as 1080x2400 PNGs and visually inspected. External binary upload was unavailable from this environment, so the re-review evidence above uses the captured uiautomator tree plus the focused screenshots checked locally.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 1, 2026
@steipete steipete self-assigned this Jul 3, 2026
@steipete
steipete force-pushed the codex/native-chat-reader-scroll branch from 3218824 to a81c635 Compare July 3, 2026 11:27
@openclaw-barnacle openclaw-barnacle Bot added app: ios App: ios size: XL and removed size: L labels Jul 3, 2026
@steipete
steipete marked this pull request as ready for review July 3, 2026 11:28
@steipete
steipete force-pushed the codex/native-chat-reader-scroll branch from a81c635 to 6c504b8 Compare July 3, 2026 11:31

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a81c63584f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Jul 3, 2026
@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 3, 2026
@steipete
steipete force-pushed the codex/native-chat-reader-scroll branch from 6c504b8 to bd98789 Compare July 3, 2026 13:42
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Maintainer repair and exact-head verification complete for 84d9d6eef4d3cd6c74c4a94a99106f59ccced214.

What changed:

  • replaced transcript-wide scroll fingerprints with bounded Android/iOS reader state
  • preserved prompt anchoring while content arrives below it
  • stopped position theft after manual reader departure
  • made Jump to latest resume live-edge following explicitly
  • scoped Android restoration to the owning session and added regenerated-ID/session regressions
  • made iOS reader-position requests one-shot, preventing repeated SwiftUI layout transactions
  • synced native i18n inventory

Proof:

  • Android ChatReaderScrollControllerTest + ChatTimelineTest: passed
  • Swift ChatReaderScrollStateTests: 5 passed
  • native i18n sync/check and git diff --check: passed
  • fresh iOS shipping-app simulator E2E from empty isolated gateway state: 4 exact openai/gpt-5.5 live turns, prompt anchor, first jump, manual swipe-away, second jump, Control relaunch, Overview; passed in 92.125 seconds with 0 failures
  • fresh local autoreview: no findings, patch correct, confidence 0.90
  • fresh exact-head branch autoreview: no findings, patch correct, confidence 0.88
  • exact-head CI: run 28664569968, all required jobs green
  • native review artifacts: validated, zero findings

Before/after screenshots for Android and fresh-state iOS were visually inspected locally, including iOS prompt anchor, jump dismissal, manual departure, Control, and Overview. They were not uploaded without an approved authenticated destination.

Known local-only noise: the broader Android ktlint lane finds two unrelated violations already on current main in NodeRuntime.kt; this PR does not touch that file, and hosted exact-head check-lint plus Android build/test gates are green.

@steipete
steipete merged commit ab2761f into openclaw:main Jul 3, 2026
80 checks passed
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 4, 2026
* improve native chat scroll behavior

* add native chat scroll proof harnesses

* fix(android): jump native chat to live content

* fix(mobile): harden reader-managed chat scrolling

* fix(android): preserve prompt anchor through completion

* fix(android): gate follow changes on user scroll

* fix(ios): handle optimistic chat rollback

* fix(mobile): harden reader boundary transitions

* fix(mobile): expose newer content after restore

* fix(android): preserve restored reader identity

* test(ios): prove reader pause and resume

* fix(android): scope restored reader state to session

* chore(i18n): sync native chat inventory

* fix(mobile): harden reader scroll recovery

* chore(i18n): sync native inventory after rebase

---------

Co-authored-by: Chris Hogg <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android app: ios App: ios merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Make native chat scrolling reader-managed

2 participants