Skip to content

fix(ios): keep offline chat sends on their original route#100942

Merged
steipete merged 1 commit into
mainfrom
codex/ios-offline-outbox-rewrite
Jul 6, 2026
Merged

fix(ios): keep offline chat sends on their original route#100942
steipete merged 1 commit into
mainfrom
codex/ios-offline-outbox-rewrite

Conversation

@steipete

@steipete steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Related: #100331, #100196, #46664

What Problem This Solves

Fixes an issue where iOS users who queued chat text while offline could lose the draft, replay it to a newly selected gateway or agent, duplicate an ambiguously accepted send, or lose the queued turn after relaunch.

Why This Change Was Made

The follow-up replaces the landed single-view queue with one gateway-scoped SQLite outbox and one canonical delivery path. Enqueue captures the gateway, agent, canonical session key, thinking level, and routing contract; replay leases that exact route, fails closed on route changes or ambiguous delivery, and removes rows only after canonical history confirms them. iOS and macOS share the same lifecycle, while live sends remain compatible with older gateways and queued replay requires the additive routing capability.

User Impact

Offline text survives termination and relaunch, stays visible in the correct transcript, and flushes once after the original gateway reconnects. Agent or gateway changes cannot silently retarget queued text. Definitive rejections remain retryable; uncertain delivery waits for reconciliation or explicit user action.

Evidence

  • Live iOS Simulator scenario: stop gateway, queue text, terminate and relaunch offline, reconnect, observe one exact reply, and verify outbox_commands drained to zero.
  • swift test --package-path apps/shared/OpenClawKit --filter ChatTranscriptCacheStoreTests — 36 passed.
  • swift test --package-path apps/shared/OpenClawKit --filter ChatViewModelOutboxTests — 45 passed.
  • swift test --package-path apps/shared/OpenClawKit --filter ChatViewModelTests — 144 passed.
  • macOS focused suites — 2 + 7 + 5 passed.
  • iOS focused simulator slice (IOSGatewayChatTransportTests, RootTabsSourceGuardTests, NodeAppModelInvokeTests) — 181 passed.
  • node --import tsx scripts/native-app-i18n.ts check, SwiftFormat, oxfmt, and git diff --check passed.
  • Fresh whole-branch Codex autoreview: no accepted or actionable findings.

@steipete
steipete requested a review from a team as a code owner July 6, 2026 14:31
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: ios App: ios app: macos App: macos app: web-ui App: web-ui gateway Gateway runtime size: XL maintainer Maintainer-authored PR labels Jul 6, 2026
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready proof for head 2e61796771dc39dff6d393f9f6f24c03105a0b22:

  • Exact-head hosted CI passed: https://github.com/openclaw/openclaw/actions/runs/28799464832 (including iOS build, macOS Swift/Node, native i18n, protocol/contracts, type checks, lint, guards, security, and all selected Node shards).
  • OPENCLAW_TESTBOX=1 scripts/pr prepare-run 100942 passed in hosted_exact_or_recent_rebase mode and verified the remote PR tree matches the prepared head. No separate Testbox lease was allocated because the exact-head hosted workflow satisfied the gate.
  • Shared Swift proof: ChatTranscriptCacheStoreTests 36/36, ChatViewModelOutboxTests 45/45, ChatViewModelTests 144/144.
  • macOS proof: WebChatSwiftUISmokeTests 2/2, GatewayConnectionTests 7/7, GatewayConnectionControlTests 5/5.
  • iOS Simulator proof after the final rebase: IOSGatewayChatTransportTests, RootTabsSourceGuardTests, and NodeAppModelInvokeTests 181/181.
  • Live iOS outbox scenario on the rewritten branch: stop the gateway, queue text, terminate/relaunch while offline, reconnect, receive the exact reply once, and observe outbox_commands drain to zero.
  • Native i18n check, SwiftFormat, oxfmt, git diff --check, and fresh whole-branch autoreview passed.

Known gap: the full terminate/relaunch live scenario preceded the final main rebase; the final head was revalidated by the 181-test simulator slice and exact-head hosted CI.

@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 6, 2026, 10:58 AM ET / 14:58 UTC.

Summary
The PR rewrites Apple offline chat outbox routing so queued sends persist in SQLite, bind to the original gateway/session route, and wait for canonical history confirmation before draining.

PR surface: Source +79, Tests +99, Docs 0, Other +4709. Total +4887 across 42 files.

Reproducibility: yes. from source: current main's merged outbox lacks route-bound confirmation, while the PR binds replay to captured gateway/session routing and canonical-history confirmation. I did not run a live iOS simulator in this read-only review, so the reproduction is source-based plus contributor live proof.

Review metrics: 2 noteworthy metrics.

  • Protocol/API additions: 1 optional chat.send param, 1 hello capability added. The client and gateway must agree on the additive routing contract before queued replay can fail closed safely.
  • Outbox schema upgrade: v2-v4 migration paths changed, schema moves to v5. Existing Apple chat-cache databases with queued text are user state, so migration behavior matters before merge.

Stored data model
Persistent data-model change detected: persistent cache schema: apps/macos/Tests/OpenClawIPCTests/ChatTranscriptCacheIdentityTests.swift, persistent cache schema: docs/platforms/ios.md, serialized state: apps/ios/Tests/NodeAppModelInvokeTests.swift, serialized state: src/config/sessions/main-session.ts. Confirm migration or upgrade compatibility proof before merge.

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:

  • [P1] Maintainer should explicitly accept the fail-closed legacy outbox upgrade behavior before merge.

Risk before merge

  • [P1] The PR intentionally changes persisted outbox upgrade behavior and gateway replay safety: v2/v3 legacy rows are parked for explicit retry and queued replay requires a gateway advertising the new routing-contract capability.
  • [P1] The PR changes a user-message delivery path and session-state storage across iOS, macOS, shared Swift, and Gateway protocol surfaces; CI and targeted tests reduce but do not eliminate upgrade/device-path risk.

Maintainer options:

  1. Accept the fail-closed upgrade contract (recommended)
    Merge with the current migrations and routing capability gate, accepting that unsafe legacy queued rows require explicit retry rather than automatic replay.
  2. Require broader upgrade proof first
    Pause until maintainers have additional before/after upgrade evidence for v2, v3, and v4 outbox databases plus an older-gateway replay attempt.
  3. Narrow to iOS route binding
    Split or defer macOS/shared schema hardening if maintainers want a smaller follow-up after the already-landed iOS outbox.

Next step before merge

  • No automated repair is indicated; the remaining action is maintainer confirmation of the compatibility-sensitive upgrade contract and normal protected-label landing.

Maintainer decision needed

  • Question: Is the fail-closed legacy outbox upgrade contract acceptable for this PR, including parking unsafe queued rows for explicit retry and requiring the new routing-contract gateway capability for automatic replay?
  • Rationale: The patch appears correct, but it intentionally changes persisted user-message state and replay behavior during upgrade, so maintainer intent is needed before treating the compatibility risk as accepted.
  • Likely owner: steipete — steipete authored the preceding durable outbox and this compatibility-sensitive follow-up, so he has the strongest current routing signal for the upgrade contract.
  • Options:
    • Accept fail-closed upgrade (recommended): Merge with the current migrations and routing capability gate, accepting explicit retry over automatic replay for unsafe legacy rows.
    • Require more upgrade proof: Pause until additional before/after upgrade evidence covers v2, v3, v4 outbox databases and an older-gateway replay attempt.
    • Narrow the follow-up: Split or defer macOS/shared schema hardening if maintainers want a smaller iOS-only route-binding change after the already-landed outbox.

Security
Cleared: No concrete security or supply-chain issue found; the diff changes persisted local chat text and gateway routing but does not add third-party code execution, secrets exposure, lockfile changes, or broader permissions.

Review details

Best possible solution:

Land the route-bound outbox after maintainers accept the fail-closed legacy outbox upgrade behavior and the exact-head CI/live proof as sufficient for the Apple chat delivery path.

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

Yes from source: current main's merged outbox lacks route-bound confirmation, while the PR binds replay to captured gateway/session routing and canonical-history confirmation. I did not run a live iOS simulator in this read-only review, so the reproduction is source-based plus contributor live proof.

Is this the best way to solve the issue?

Yes, this appears to be the best owner-boundary fix: route capture lives in the Apple transports, the guard is additive in the Gateway protocol, and durable state stays in the existing SQLite chat cache/outbox rather than adding a parallel store. The main alternative, automatic replay without a routing contract, would preserve the misroute and duplicate ambiguity the PR is fixing.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 83f052772429.

Label changes

Label changes:

  • add P2: This is normal-priority Apple chat reliability work with real message-loss/misroute risk but no evidence of a shipped release outage.
  • add merge-risk: 🚨 compatibility: The patch changes the outbox SQLite schema and requires a new gateway routing-contract capability for automatic queued replay.
  • add merge-risk: 🚨 message-delivery: The patched flush path decides whether offline text is replayed, parked, confirmed, or retried after reconnect.
  • add merge-risk: 🚨 session-state: The patch changes session routing identity, transcript cache partitioning, and durable outbox confirmation state across app restarts.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body/comment include after-fix live iOS Simulator proof for stop-gateway, queue, terminate/relaunch offline, reconnect, one exact reply, and drained outbox, plus exact-head hosted CI; the final live scenario predates the final rebase but is backed by final-head simulator and CI slices.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body/comment include after-fix live iOS Simulator proof for stop-gateway, queue, terminate/relaunch offline, reconnect, one exact reply, and drained outbox, plus exact-head hosted CI; the final live scenario predates the final rebase but is backed by final-head simulator and CI slices.

Label justifications:

  • P2: This is normal-priority Apple chat reliability work with real message-loss/misroute risk but no evidence of a shipped release outage.
  • merge-risk: 🚨 compatibility: The patch changes the outbox SQLite schema and requires a new gateway routing-contract capability for automatic queued replay.
  • merge-risk: 🚨 message-delivery: The patched flush path decides whether offline text is replayed, parked, confirmed, or retried after reconnect.
  • merge-risk: 🚨 session-state: The patch changes session routing identity, transcript cache partitioning, and durable outbox confirmation state across app restarts.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body/comment include after-fix live iOS Simulator proof for stop-gateway, queue, terminate/relaunch offline, reconnect, one exact reply, and drained outbox, plus exact-head hosted CI; the final live scenario predates the final rebase but is backed by final-head simulator and CI slices.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body/comment include after-fix live iOS Simulator proof for stop-gateway, queue, terminate/relaunch offline, reconnect, one exact reply, and drained outbox, plus exact-head hosted CI; the final live scenario predates the final rebase but is backed by final-head simulator and CI slices.
Evidence reviewed

PR surface:

Source +79, Tests +99, Docs 0, Other +4709. Total +4887 across 42 files.

View PR surface stats
Area Files Added Removed Net
Source 7 81 2 +79
Tests 3 100 1 +99
Docs 1 1 1 0
Config 0 0 0 0
Generated 0 0 0 0
Other 31 5654 945 +4709
Total 42 5836 949 +4887

What I checked:

Likely related people:

  • steipete: Authored and merged the preceding durable iOS outbox PR and authored this route-hardening follow-up over the same Apple chat/outbox surface. (role: feature owner and recent area contributor; confidence: high; commits: 4b7661e9a0c4, 2e61796771dc; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel+Outbox.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatTranscriptCache.swift, apps/ios/Sources/Chat/IOSGatewayChatTransport.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 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. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. 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 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Security-sensitive guard cleared

This PR no longer has blocked security-sensitive file changes. A future security-sensitive change requires a fresh /allow-security-sensitive-change comment after the guard blocks that new head SHA.

  • Current SHA: b6c6b8ae978260f9758b295eb3091dccb258e5ce

@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord channel: telegram Channel integration: telegram app: android App: android cli CLI command changes scripts Repository scripts commands Command implementations agents Agent runtime and tooling channel: qqbot extensions: qa-lab and removed security-sensitive-changed labels Jul 6, 2026

@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: b6c6b8ae97

ℹ️ 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 +491 to +492
if self.transport.outboxRequiresSessionRoutingContract,
next.routingContract != routeLease.sessionRoutingContract

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 Don't park exact agent-session outbox rows on default changes

When a queued row already has an exact agent-scoped deliverySessionKey such as agent:ops:ios-..., its target is not affected by later changes to the gateway default agent or main-session key. This unconditional routing-contract comparison still marks the row as delivery_target_changed whenever that global contract changes, so offline sends from explicit agent sessions get parked and require manual retry even though replaying them cannot retarget to the new default. Consider applying the contract mismatch gate only to mutable aliases like main/global, or persisting whether the row actually depends on the routing contract.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit 06789ca into main Jul 6, 2026
111 of 117 checks passed
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

@steipete
steipete deleted the codex/ios-offline-outbox-rewrite branch July 6, 2026 16:54
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: ios App: ios app: macos App: macos app: web-ui App: web-ui docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. 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. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XL status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant