feat(android): restore in-flight runs after reconnect#100384
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 1:06 PM ET / 17:06 UTC. Summary PR surface: Other +252. Total +252 across 4 files. Reproducibility: yes. by source inspection: current main clears Android pending run state on disconnect and Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Land the snapshot-based Android recovery path after exact-head Android emulator or device proof shows reconnect and 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 Is this the best way to solve the issue? Yes, with a proof gap. Consuming the existing Gateway AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 444d54c5389a. Label changesLabel justifications:
Evidence reviewedPR surface: Other +252. Total +252 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
Review history (1 earlier review cycle)
|
2439ad9 to
9bdb1de
Compare
There was a problem hiding this comment.
💡 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".
| restoreRunStateOnReconnect = false | ||
| refreshHistoryForRecovery() |
There was a problem hiding this comment.
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 👍 / 👎.
|
Merged via squash.
|
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.
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.onDisconnectedclears 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.historycarriesinFlightRun { runId, text }(the run still streaming for the session plus its buffered assistant text; seesrc/gateway/chat-abort.tsresolveInFlightRunSnapshot) andsessionInfo.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
seqGapevent-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.seqGapnow recovers automatically by refetching history instead of only showing "Event stream interrupted; try refreshing."Implementation notes:
parseHistorynow decodesinFlightRun; bootstrap adopts it viaadoptInFlightRun(adds the run topendingRuns, 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.onDisconnectedand fired on the nexthealthevent viarefreshHistoryForRecovery, which reuses the existing bootstrap/reconciliation path without the session-switch state reset (sohealthOkdoes not flicker and sends are not blocked).hasActiveRunis not wired: the Android sessions UI currently has no active-run indicator to feed.Evidence
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;seqGaprefetches and restores.cd apps/android && ./gradlew :app:testPlayDebugUnitTest— full unit suite green (all chat tests: 47 passed).