Skip to content

feat(android): restore in-flight runs after reconnect#100384

Merged
steipete merged 1 commit into
mainfrom
feat/android-restore-inflight-runs
Jul 5, 2026
Merged

feat(android): restore in-flight runs after reconnect#100384
steipete merged 1 commit into
mainfrom
feat/android-restore-inflight-runs

Conversation

@steipete

@steipete steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Part of #100197. Android twin of #100277: consumes the existing gateway reconnect snapshot contract — no protocol changes.

What Problem This Solves

When the Android app's gateway connection drops while an agent run is streaming, ChatController.onDisconnected clears all transient run state (pending runs, streaming text, tool placeholders). After reconnect, the app never rediscovered the run: the chat looked idle while the gateway was still streaming, and the reply only appeared after a manual refresh or the next event. Missed-event gaps (seqGap) had the same blind spot and only surfaced a "try refreshing" error.

Why This Change Was Made

The gateway already ships a canonical reconnect snapshot: chat.history carries inFlightRun { runId, text } (the run still streaming for the session plus its buffered assistant text; see src/gateway/chat-abort.ts resolveInFlightRunSnapshot) and sessionInfo.hasActiveRun. The TUI (src/tui/tui-session-actions.ts) and, since #100277, iOS/macOS consume this contract. Android now does the same instead of inventing a parallel mechanism; the earlier protocol-cursor approach was rejected in favor of this existing contract.

User Impact

  • After a reconnect (or app cold start, or a seqGap event-stream gap) while a run is active, the Android chat immediately shows the run as pending again with the buffered assistant text, and keeps consuming its live deltas and terminal event.
  • Reconnect with no active run stays clean — no phantom pending state, no duplicate transcript rows.
  • seqGap now recovers automatically by refetching history instead of only showing "Event stream interrupted; try refreshing."

Implementation notes:

  • parseHistory now decodes inFlightRun; bootstrap adopts it via adoptInFlightRun (adds the run to pendingRuns, arms the existing 120s pending-run timeout, restores buffered streaming text). Snapshot absence never clears local state — live terminal events and the timeout own completion — and a snapshot that reports a different run than a locally-owned pending send is ignored as stale.
  • Reconnect refresh is armed by onDisconnected and fired on the next health event via refreshHistoryForRecovery, which reuses the existing bootstrap/reconciliation path without the session-switch state reset (so healthOk does not flicker and sends are not blocked).
  • Sessions-list surfacing of hasActiveRun is not wired: the Android sessions UI currently has no active-run indicator to feed.

Evidence

  • New ChatControllerReconnectRestoreTest (ScriptedGateway replay harness): reconnect adopts the in-flight run and keeps consuming live deltas/terminal; reconnect without a run stays clean (single extra history fetch, no state); repeated reconnects do not duplicate the run or transcript rows; a stale recovery snapshot does not replace a locally-owned send; seqGap refetches and restores.
  • cd apps/android && ./gradlew :app:testPlayDebugUnitTest — full unit suite green (all chat tests: 47 passed).
  • Structured autoreview (Codex, gpt-5.5) on the diff: clean, no actionable findings.

@openclaw-barnacle openclaw-barnacle Bot added app: android App: android size: M maintainer Maintainer-authored PR labels Jul 5, 2026
@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 1:06 PM ET / 17:06 UTC.

Summary
The PR makes Android ChatController refetch history after reconnect or seqGap, parse inFlightRun, re-adopt pending run state, and adds replay-harness tests.

PR surface: Other +252. Total +252 across 4 files.

Reproducibility: yes. by source inspection: current main clears Android pending run state on disconnect and seqGap does not re-adopt chat.history.inFlightRun. I did not run a live Android reconnect repro in this read-only review.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #100197
Summary: This PR is the Android snapshot-based candidate for the broader mobile reconnect recovery issue; the older Android and Gateway afterSeq branches are closed unmerged and superseded by this direction.

Members:

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

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof is added.

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

Rank-up moves:

  • Attach redacted exact-head Android emulator or device proof showing active-run restoration after Gateway reconnect and seqGap.
  • Include visible pending/send-lock state and final transcript output so reviewers can check that output is not duplicated.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body lists replay-harness tests and a Gradle unit suite, but no real Android device/emulator or live Gateway reconnect proof is posted yet. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A visible native Android reconnect proof would materially reduce the remaining session-state and message-delivery risk. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify Android chat restores an active in-flight response after Gateway reconnect and seqGap, with pending/send-lock state and no duplicate transcript rows.

Risk before merge

  • [P1] Merging without exact-head Android emulator or device proof could miss lifecycle races around reconnect health events, stale-run adoption, send locking, or terminal event recovery that replay tests cannot fully exercise.
  • [P1] The diff changes Android pending-run/session-state and streamed-message recovery after missed Gateway events, so duplicate transcript rows or missed terminal updates remain user-visible risks until proven in a real setup.

Maintainer options:

  1. Prove Android reconnect before merge (recommended)
    Attach exact-head Android emulator or device proof for reconnect and seqGap recovery, then re-review the same head before maintainer approval.
  2. Merge with maintainer-owned lifecycle risk
    Maintainers can accept the source-and-unit-test evidence without native proof, but that intentionally accepts Android session-state and streamed-message recovery risk.
  3. Hold behind the reconnect workstream
    Pause this PR until Resume chat after reconnect via sequence cursor (gateway + mobile clients) #100197 and sibling platform work settle the final proof and recovery contract.

Next step before merge

  • [P1] Protected maintainer handling and missing real Android behavior proof make this a maintainer/contributor proof follow-up item, not a ClawSweeper repair job.

Maintainer decision needed

  • Question: What exact Android reconnect proof is sufficient before landing this maintainer-labeled active-run restoration PR?
  • Rationale: The source path looks correct, but the change is native Android session/message recovery and the PR currently has replay/unit evidence only, so automation cannot decide the maintainer proof threshold.
  • Likely owner: steipete — steipete is the strongest routing candidate from current Android and gateway/session history and is coordinating the linked reconnect workstream.
  • Options:
    • Require Android reconnect proof (recommended): Require a redacted exact-head emulator or device artifact showing active-run restoration after Gateway reconnect and seqGap, including pending/send-lock state and no duplicate transcript rows.
    • Accept unit proof only: Maintainers may intentionally merge based on source review and replay tests, but would own the unproven native lifecycle risk.
    • Hold for workstream review: Pause this PR behind the broader mobile reconnect issue until Android and Apple proof expectations are aligned.

Security
Cleared: The diff changes Android Kotlin chat state and tests only; I found no dependency, workflow, secret, permission, or supply-chain change.

Review details

Best possible solution:

Land the snapshot-based Android recovery path after exact-head Android emulator or device proof shows reconnect and seqGap restore the active run without duplicate output or stale ownership.

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

Yes by source inspection: current main clears Android pending run state on disconnect and seqGap does not re-adopt chat.history.inFlightRun. I did not run a live Android reconnect repro in this read-only review.

Is this the best way to solve the issue?

Yes, with a proof gap. Consuming the existing Gateway inFlightRun snapshot is the narrowest maintainable fix and avoids reviving the superseded afterSeq protocol design, but it still needs real Android reconnect proof before merge.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 444d54c5389a.

Label changes

Label justifications:

  • P1: The PR targets an Android reconnect workflow where an active chat response can appear idle and require manual refresh or later events to recover.
  • merge-risk: 🚨 session-state: The diff changes Android pending-run adoption, reconnect history refresh, and seqGap state rebuilding for active chat sessions.
  • merge-risk: 🚨 message-delivery: The diff changes how streamed assistant text and terminal history refreshes are recovered after missed Gateway events.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body lists replay-harness tests and a Gradle unit suite, but no real Android device/emulator or live Gateway reconnect proof is posted yet. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Other +252. Total +252 across 4 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 4 253 1 +252
Total 4 253 1 +252

What I checked:

Likely related people:

  • steipete: Current main history shows Peter Steinberger on the Android ChatController import/provenance and recent gateway/session lifecycle work adjacent to this reconnect workstream. (role: recent area contributor; confidence: high; commits: f535b2a3ecde, 1b8d83767445, 6df0fb818d67; files: apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt, src/gateway/server-methods/chat.ts, src/gateway/chat-abort.ts)
  • williamliu-ai: Merged commit 8eeb930 introduced the Gateway/TUI chat.history.inFlightRun snapshot path that this Android PR consumes. (role: introduced snapshot contract; confidence: high; commits: 8eeb9300df40; files: src/gateway/chat-abort.ts, src/gateway/server-methods/chat.ts, src/tui/tui-session-actions.ts)
  • joshavant: Recent Android chat history work preserved sent messages through history reconciliation, adjacent to this PR's reconnect/history path. (role: adjacent Android chat history contributor; confidence: medium; commits: 485446af8c95; files: apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt, apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt)
  • IWhatsskill: Recent current-main history added Android chat command controls in the same controller/model area. (role: recent Android chat contributor; confidence: medium; commits: c680312ef6f3; files: apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt, apps/android/app/src/main/java/ai/openclaw/app/chat/ChatModels.kt)
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.
Review history (1 earlier review cycle)
  • reviewed 2026-07-05T17:00:02.024Z sha 2439ad9 :: needs real behavior proof before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jul 5, 2026
Consume the gateway's canonical reconnect snapshot (chat.history inFlightRun)
to re-adopt still-streaming runs after reconnect, cold start, and seq-gap
recovery in the Android ChatController. Mirrors the iOS contract from #100277.
Part of #100197.
@steipete
steipete force-pushed the feat/android-restore-inflight-runs branch from 2439ad9 to 9bdb1de Compare July 5, 2026 19:23

@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: 9bdb1de3dc

ℹ️ 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 on lines +430 to +431
restoreRunStateOnReconnect = false
refreshHistoryForRecovery()

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.

P2 Badge Keep reconnect restore armed until history succeeds

When the first post-reconnect health event arrives while chat.history is still startup-gated or otherwise transiently unavailable, this clears restoreRunStateOnReconnect before refreshHistoryForRecovery() has succeeded. The gateway explicitly returns retryable startup-unavailable errors for chat.history (src/gateway/server-methods.ts:695, src/gateway/methods/core-descriptors.ts:210), and the TUI retries that path (src/tui/gateway-chat.ts:254); Android will instead consume the one-shot restore, surface the failed history request, and never retry on later health events, leaving the in-flight run idle until manual refresh or another event.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit f164157 into main Jul 5, 2026
44 checks passed
@steipete
steipete deleted the feat/android-restore-inflight-runs branch July 5, 2026 19:30
@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
Consume the gateway's canonical reconnect snapshot (chat.history inFlightRun)
to re-adopt still-streaming runs after reconnect, cold start, and seq-gap
recovery in the Android ChatController. Mirrors the iOS contract from openclaw#100277.
Part of openclaw#100197.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android maintainer Maintainer-authored PR merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant