Fix iOS cmux-tui topology reconnect (#21001)
Summary
A transient cmux-tui control disconnect no longer permanently stops iOS topology observation. The provider now reconnects its existing session control with a single bounded backoff loop, keeps stable terminal ownership, and forces a fresh workspace snapshot after a generation gap so stale rows cannot win a race.
Testing
python3 scripts/verify-local.py --all— passed.python3 scripts/verify-local.py --only swift-syntax --swift-changed origin/main— passed (4 files).python3 scripts/verify-local.py --only test-wiring— passed../scripts/lint-ios-package-conventions.sh— passed with existing repository warnings only.python3 scripts/localization_defaults.py— passed (0 mismatches).- Hosted iOS package, simulator build, iPhone, iPad, and aggregate iOS tests passed for the latest head.
- Added provider-level stale-generation and bounded-churn tests; reconnect watcher handoff and post-open subscription backoff are covered.
python3 scripts/check-workspace-package-groups.py --check— passed.python3 scripts/lint-feature-flags.py— passed.- Focused
swift test --package-path Packages/iOS/CmuxMobileShell --filter MobileSSHCmuxTUITopologyReconnectTestswas not run: the prescribednx-remotehost session failed before job start becausecmux-lawrence-2DNS was unavailable. The PR iOS package lane is the required compile/test evidence.
Issues
Issue lifecycle
- Pickup comment: https://github.com/manaflow-ai/cmux/issues/21001#issuecomment-6205883222
- Resolution comment: pending merge.
Impact map
- Source of truth and owner:
MobileSSHCmuxTUIProviderowns one reconnect coordinator and one activeCmuxTUIControlper session;CmuxTUIControl.subscribe()remains the event source. - Direct callers and sibling paths inspected:
MobileSSHHostProviders,MobileSSHComputers, and the separate tmux topology/restart provider path; cmux-tui workspace and terminal resource IDs remain the ownership keys. - Cross-surface effects: reconnect stays within the cached provider/session, closes superseded controls before installing a replacement, and cancels observation/backoff from
close(); no UI strings, localization, persistence schema, or release metadata changed. - Tests and evidence: the new package tests simulate disconnect → automatic control replacement → later topology event, assert capped backoff, and prove stale generations cannot clear or admit a newer snapshot.
- Residual risk: live SSH transport behavior still needs the hosted iOS package lane and the affected iOS connectivity-soak workload; no local app build was run because Swift jobs must use the hosted route.
Proof
No visual proof applies; this is an iOS connectivity and topology lifecycle change. Hosted iPhone and iPad simulator lanes exercise the affected package and test target.
Checklist
- Reconnect ownership stays single-flight and closes superseded controls.
- Stale generations cannot admit events or clear newer snapshots.
- Recovery and generation-stability retries are bounded and cancellation-aware.
- Hosted iOS package, build, simulator, and aggregate test lanes are required before merge.
Changelog
Fixed: iOS cmux-tui topology observation now reconnects after a transient control transport disconnect and refreshes from a current workspace snapshot.
Mergeability
- Base SHA: 6dc229ca0c47ea12babc0c22ffceae59e81d5109
- Head SHA: e22f1c5dc3e8e3e8acf6d55ab283c8c2574c76bd
- Conflict-only gate: mergeable and clean; all required iOS lanes passed.
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes iOS cmux-tui topology observation so a transient control transport disconnect no longer permanently stops updates. The provider now reconnects its session control with a bounded backoff loop, shares in-flight reconnect attempts among concurrent callers, keeps stable terminal ownership, and forces a fresh workspace snapshot after a generation gap so stale rows cannot win a race.
- Reconnecting caps retry delay at 30 seconds and stops after three attempts so dead owners fall out of discovery; a control is replaced only after the old one is closed, and backoff sleeps are injectable for fast tests.
- Added an explicit handshake with a client name and 30-second timeout on session connect and 10-second on socket connect.
- Tracks control generations so events and list results from superseded controls are discarded, and
listWorkspacesretries up to three times when the generation changes mid-request before failing the snapshot. - Adds package tests covering disconnect → reconnection, bounded backoff, dead-owner recovery exhaustion, stale-generation gating, and snapshot retry bounds.
Closes #21001.
Summary by CodeRabbit
- Reliability
- The mobile SSH interface reconnects automatically after control-channel interruptions and resumes receiving workspace updates when the connection is restored.
- Reconnection attempts use increasing delays, up to 30 seconds, and stop after a limited number of tries.
- Failed operations trigger a reconnection attempt and are retried once.
- Workspace listings retry if the connection changes mid-request, helping ensure results reflect a consistent connection.
- Updates from replaced connections are ignored, and a fresh workspace snapshot is requested when needed.
- Connection setup uses a bounded handshake timeout.