Skip to content

Add deterministic iOS connectivity soak workloads - #12735

Merged
lawrencecchen merged 63 commits into
mainfrom
feat-ios-connectivity-soak
Sep 20, 2026
Merged

lawrencecchen merged 63 commits into
mainfrom
feat-ios-connectivity-soak

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Adds deterministic ten-minute and one-hour connectivity workloads inside the real iOS app. Each cycle verifies authenticated Iroh, terminal input and returned output, workspace mutations, events and related mobile RPCs. The stress sequence also exercises navigation, output bursts, scratch workspace creation/closure and deliberate reconnects.

Unexpected connection replacement, slow cycles, missing actions and shortened observation windows fail the run. Results contain workload version, action counts and the last operation. The gate supports an isolated agent account and captures an iOS screenshot alongside its transport report. Contributor instructions and the PR template require maintaining this coverage when the relevant product behavior changes.

Validation: shell syntax, argument validation and whitespace checks passed. Tagged Mac and isolated Simulator builds are running on the fleet; full-duration validation is pending. Added behavior tests for final-transaction requirements, unexpected reconnects, insufficient coverage and failed terminal operations.

Simulator coverage does not prove physical-device radios, background suspension or pixel correctness. Runtime activation is Debug-only and requires an explicit soak profile.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Turns the iOS release gate from a one-shot connectivity probe into deterministic 10-minute basic or 60-minute stress soaks in the real iOS app. Soak runs now fail on unexpected connection replacement, invalid native path, stalled operations, incomplete coverage, or a shortened observation window.

Workloads

  • Basic cycles run every 10 seconds; stress cycles run every 5 seconds and add Unicode output, scratch workspace lifecycle, navigation, and forced reconnects through the shared retry action.
  • A forced reconnect must replace the native connection identity; reusing an already-healthy session fails the run.
  • Each cycle checks auth, RPC inventory, terminal round trips, workspace changes, events, notifications, chat sessions, and artifact scans; validation requires at least 50 basic or 300 stress cycles plus coverage minimums.
  • The soak drives the real workspace UI before each run, captures a simulator-composited screenshot, and keeps one terminal consumer mounted until reconnects or surface switches release it.
  • Terminal verification is time-bounded, terminal selection is re-checked after reconnect, and terminal latency is reported separately after reconnects.
  • Foreground workspace refresh no longer waits on secondary peer discovery.
  • Native identity and path come from the installed RPC transport, including deferred Iroh transports, rather than a settings snapshot.
  • Reports include plan version, elapsed time, cycle/operation counts, maximum cycle duration, last operation, failure reason, per-operation latencies, and UI readiness timings.

Gate support

  • Adds the iroh-soak.xctestplan harness and focused tests, including one asserting a stress reconnect replaces a healthy connection.
  • scripts/run-iroh-release-gate.sh gains --soak-profile, isolated staging --credentials-file, and --simulator-id, and can reuse a dedicated simulator across cold launches.
  • Relay-only runs constrain both Iroh endpoint generations to the relay and validate the selected native path.
  • Iroh attach tickets use the published v2 device identity instead of a local settings identity.
  • Soaks are Debug-only, run in an isolated Simulator, and do not prove physical-device radios, suspension recovery, cellular handoff, gestures, or pixel-perfect rendering.
  • The PR template and iOS guidance require updating or recording affected soak coverage for connectivity, auth, lifecycle, workspace, and terminal changes.

Written for commit cf2b850. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added configurable iOS connectivity soak testing for basic and stress workloads, including reconnect, terminal, workspace, latency, and connection-path checks.
    • Added release-gate evidence for UI presentation timing, terminal visibility, transport continuity, and operation performance.
    • Added support for dedicated simulator runs, credentials, relay-only validation, and automated report verification.
  • Bug Fixes

    • Foreground workspace refresh no longer waits for secondary discovery.
    • Pairing tickets now use the currently published installation identity for Iroh routes.
  • Documentation

    • Added iOS connectivity soak testing guidance and contributor requirements.

@coderabbitai

coderabbitai Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cd0fffce-ef16-4341-8416-720dd7bcecfe

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3574a and cf2b850.

📒 Files selected for processing (49)
  • .github/pull_request_template.md
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/CmxTransport.swift
  • Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileReleaseGateUIProbe.swift
  • Packages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileReleaseGateUIProbeTests.swift
  • Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohDeferredByteTransport.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohDeferredByteTransportTests.swift
  • Packages/Shared/CmuxIrxTransport/Sources/CmuxIrxTransport/IrxControlByteTransport.swift
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient+ReleaseGate.swift
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCSession+ReleaseGate.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateProbeFailure.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateProbeResult.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateTerminalClient.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateTerminalSession.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileShellComposite+IrohReleaseGate.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileShellComposite+IrohSoak.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/ForegroundRefreshIsolationTests.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/IrohConnectionRecoveryOwnerTests.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileIrohReleaseGateTerminalSessionTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/GhosttySurfaceRepresentable.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileReleaseGateUIProbeEnvironment.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileReleaseGateUISnapshot.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTable.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTableCoordinator.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Actions.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceShellView.swift
  • Resources/Localizable.xcstrings
  • Sources/Mobile/MobileAttachTicketStore.swift
  • Sources/Mobile/MobileHostService.swift
  • Sources/Mobile/Pairing/MobilePairingModel.swift
  • cmuxTests/CloudSurfaceDragFeedbackTests.swift
  • cmuxTests/MobileHostWorkspaceTicketAuthorizationTests.swift
  • docs/ios-connectivity-soak.md
  • ios/AGENTS.md
  • ios/cmux-ios.xcodeproj/xcshareddata/xcschemes/cmux-ios.xcscheme
  • ios/cmux/cmuxApp.swift
  • ios/cmux/iroh-soak.xctestplan
  • ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateHostView.swift
  • ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateRunner.swift
  • ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateScene.swift
  • ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohSoakRunner.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/MobileIrohSoakRunnerTests.swift
  • scripts/lib/iroh-soak.test.mjs
  • scripts/lib/mach-clock-ns.py
  • scripts/mobile-dev-launch.sh
  • scripts/run-iroh-release-gate.sh
  • tests/test_iroh_monitor_simulator_plan.py
 _______________________________________________________________________________________________________________________________________
< Test your estimates. Mathematical analysis of algorithms doesn't tell you everything. Try timing your code in its target environment. >
 ---------------------------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Commissioning update: 22 focused iOS tests passed on the fleet, plus 2 deferred-transport tests and 2 shell relay-policy tests. A 602-second basic run completed 58 terminal/RPC cycles but exposed missing route evidence in Settings. The soak now reads identity and path from the active native connection, including through the deferred transport wrapper. Stalled operations now produce a bounded failure. Matching tagged Mac/iOS pairs are being installed for another full run. The earlier Unicode burst stalled after the Mac printed its marker; this is retained as unclassified evidence pending the new run. Recurring service activation and Slack delivery are not yet complete.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Live validation on 3f8ceec: basic PASS, 59 complete cycles over 601.70 seconds, final transaction passed, native relay path and connection identity verified, maximum cycle 3.35 seconds. Stress FAIL at unicode_output_burst after 35.17 seconds with one completed cycle; the 30-second deadline wrote the report and the runner cleaned up. iOS replay logs show repeated waiting_for_baseline followed by replay_followup_cap_reached. The Mac printed the marker, but the probe did not observe completion. This remains an unclassified app/probe interaction, and the full one-hour sequence is not validated. Evidence is retained under the two run IDs 20260916T194028Z-basic-2c8aca and 20260916T194241Z-stress-00fc26 on cmux-mac-mini. Temporary app instances and simulators are stopped; recurring monitoring remains disabled.

@azooz2003-bit

Copy link
Copy Markdown
Collaborator Author

Full-window retry completed on cmux-mac-mini, source 39d7669.

  • Basic PASS: 602.839 seconds, 59 cycles, maximum cycle 3.089 seconds.
  • Stress PASS: 3602.568 seconds, 704 cycles, 176 Unicode bursts, 176 workspace create/use/close sequences, five verified disconnect/reconnect operations, maximum cycle 28.171 seconds.
  • Both used the native Iroh relay path and passed the final terminal transaction.

This retry exposed a script mistake: retrying a healthy connection keeps that connection, while the stress script demanded a changed connection ID. The corrected action disconnects while preserving the pairing, invokes normal retry, then requires a new connection and successful terminal use. A behavioral regression fails without the correction and passes with it. All 17 focused release-gate tests and 11 supervisor tests pass.

The earlier 35-second Unicode timeout did not reproduce; its underlying cause remains unproven. The tested source includes main snapshot be7d9fa fetched at retry start. Main advanced during rebuilding and verification; this is not a claim about the later main tip.

Slack delivery still returns missing_scope. Results remain queued, and recurring jobs are not installed.

* Record per-operation iOS soak latencies

* Fix soak latency test dictionary fixtures
* Measure real iOS UI readiness timings

* Only report presented terminal frames

* Measure workspace tap to detail latency

* Keep UI probe reset scoped to runner
@lawrencecchen

Copy link
Copy Markdown
Contributor

Verified macOS fleet artifact for aeb554a: pr-12735-aeb554ab. HQ restores/downloads this exact artifact on click.

Job 90a45420685174d868b83405. Active execution/cleanup: 799.9s; queue/setup: 3.4s. Free disk: 302.6 → 299.5 GiB. Workspace reset: True.

This proves a macOS app build and publication; it does not prove iOS, tests, or UI behavior. Fetch the durable receipt with cmux-ci wait 90a45420685174d868b83405 --receipt artifacts/fleet/90a45420685174d868b83405.json. Do not resubmit this completed build. If the head changes, rebuild the new exact SHA.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Mac fleet instructions for head aeb554ab0096d8e85e22c0e8d731316fea8d25d2. Planned tag: pr-12735-aeb554ab; this is not yet a published build.

JOB_JSON=$(~/.local/bin/cmux-ci submit --kind cmux --command 'CMUX_FLEET_BUILD_TAG=pr-12735-aeb554ab /Users/Shared/cmux-build-fleet/recipes/cmux.sh https://github.com/manaflow-ai/cmux.git aeb554ab0096d8e85e22c0e8d731316fea8d25d2' --artifact artifacts/cmux.app.zip --workspace https://github.com/manaflow-ai/cmux/pull/12735 --source-digest aeb554ab0096d8e85e22c0e8d731316fea8d25d2 --cache-key cmux:pr-12735 --min-free-bytes 268435456000 --label cmux --label ram48)
JOB_ID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$JOB_JSON")
~/.local/bin/cmux-ci wait "$JOB_ID" --receipt artifacts/fleet/$JOB_ID.json
~/.local/bin/cmux-ci publish-hq "$JOB_ID"

Use an existing campaign job ID if one is already posted; do not submit a duplicate. A wait timeout leaves the remote job running. Published results will include an exact-head artifact link and timing/disk receipt. This recipe validates the macOS app only, not iOS or tests. Never use maclease or put credentials in a PR comment.

Repair real iOS UI timing and soak terminal ownership
@lawrencecchen
lawrencecchen marked this pull request as ready for review September 20, 2026 11:37
@greptile-apps

greptile-apps Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR should not merge until the soak observes terminal output without displacing the mounted renderer and the fixed readiness sleep is replaced with a real readiness signal.

Findings

  1. P1 Soak Displaces Mounted Renderer ▶
  2. P2 Readiness Uses Fixed Delay ▶

Summary

This PR adds deterministic iOS Iroh connectivity soak profiles, native connection/path observations, UI evidence capture, sustained terminal/workspace workloads, and release-gate script support.

  • Adds ten-minute basic and one-hour stress workload orchestration and reporting.
  • Exercises RPCs, workspace mutations, terminal traffic, navigation, and deliberate reconnects.
  • Extends the simulator gate with isolated credentials, retained-device support, screenshots, and coverage validation.
  • The sustained terminal workload currently replaces rather than exercises the mounted renderer consumer.

Diagram

sequenceDiagram
    participant Runner as Soak runner
    participant Renderer as Ghostty renderer
    participant Store as MobileShellComposite
    participant Session as Soak terminal session
    Runner->>Renderer: Restore selected workspace/surface
    Renderer->>Store: terminalOutputStream(renderer owner)
    Runner->>Session: Start sustained terminal verification
    Session->>Store: terminalOutputStream(soak owner)
    Note over Store: Sole per-surface registration is overwritten
    Store-->>Renderer: Ownership check fails
    Renderer-->>Renderer: Exit output loop
    Store-->>Session: Deliver and acknowledge terminal chunks
    Session-->>Runner: Marker verified
Loading

Reviews (1) · Last reviewed commit: "Bound terminal markers and verify restor..."

break
}
let owner = UUID()
let stream = client.terminalOutputStream(surfaceID: surfaceID, ownerID: owner)

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.

P1 Soak displaces mounted renderer

The soak opens a second output stream for the foreground surface, but terminal output supports only one registration per surface. Registering this stream replaces the mounted Ghostty renderer’s continuation, token, owner, and delivery queue. The renderer then detects that it lost ownership and exits, while the soak consumer drains and acknowledges the output instead. As a result, the workload can pass even though the mounted renderer stopped receiving terminal output, so it does not provide the claimed sustained renderer-path coverage.

Comment on lines +209 to +211
settleReadiness: {
try await ContinuousClock().sleep(for: .milliseconds(500))
},

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 Readiness uses fixed delay

This uses a fixed 500 ms sleep as readiness synchronization before taking another state snapshot. The delay does not prove continuous readiness: a disconnect and recovery within that interval remains invisible. This violates the repository directive requiring an owning-subsystem signal or cancellation-aware readiness abstraction instead of fixed sleeps for synchronization, so the requirement must be satisfied before merging.

Rule Used: Flag new blocking or timing-based synchronization in production Swift: semaphores, DispatchGroup.wait, sleeps, Task.sleep, asyncAfter, timers or polling for synchronization, DispatchQueue.main.sync, or manual locks where actor isolation or a real sig... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@lawrencecchen
lawrencecchen merged commit 04ac7a4 into main Sep 20, 2026
55 of 57 checks passed
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 20, 2026
34ecef4 perf: coalesce concurrent process snapshots across diagnostics and restore (manaflow-ai#13014)
150d7fa Add app-host test failure census (manaflow-ai#13124)
04ac7a4 Merge pull request manaflow-ai#12735 from manaflow-ai/feat-ios-connectivity-soak
c022438 fix: update Ghostty environment lifetime fix (manaflow-ai#13191)
eb18207 Fix mobile devices dashboard WebSocket failures and naming (manaflow-ai#13156)
c7d961d perf: split BrowserPanelView's modifier chain so it type-checks quickly (manaflow-ai#13130)
e188035 refactor: move the Computer Use runtime out of the app module into a package (manaflow-ai#13132)
cf2b850 Bound terminal markers and verify restored selection
2d7fc1c Measure terminal latency separately after reconnect
44dcd1e Reconcile iOS monitor stack with main
70034bc Merge pull request manaflow-ai#13116 from manaflow-ai/feat-ios-monitor-e2e-repair
deafe6e Skip release gate text scans without a probe
a1be46b Release terminal ownership from reader teardown
96d6574 Restore transport target after UI evidence
31ff358 Schedule terminal owner cleanup from deinit
e82f6f6 Keep bounded terminal text evidence reliable
ccbc4b2 Bound frame evidence scans and handshake setup
301dbad Make terminal evidence capture causal
6b50e3a Finish bounded release gate cleanup
14f2cd9 Stop stale release gate probes and bound frame inspection
cd64e8c Bound pairing bootstrap loading
035fd37 Harden release gate evidence and readiness
b6ca6e9 Close release gate review races
389026e Make release gate readiness and dismissal causal
aa2bb02 Restore main translations for the pairing preparation error
46bbfc9 Restore the pairing preparation handling already present on main
1bcbf60 Give the soak one owned terminal reader across steady-state commands
9d700f9 Test soak terminal consumer lifetime across commands and reconnects
1dd9f69 Fix existing Cloud test imports and nested macro compilation
bd5fee0 Give launch-request samples a distinct statistics key
bc68fbf Measure UI readiness from the actual simulator launch request
e5772e8 Test launch request timing across app initialization
8006ba9 Clear prior UI evidence before each retained-simulator launch
d8e1b4c Reuse isolated monitor devices while cold-launching the app
0968fcf Test the dedicated monitor simulator plan boundary
e7da214 Wait for the published pairing identity and inject screenshot capture
0af38fe Avoid the Swift task-group isolation checker defect in refresh test
48a0eb9 Own UI measurements per launch and capture composited terminal evidence
7e28e4e Mint pairing tickets with the active v2 device identity
f498caf Test pairing tickets against the current transport identity
95f931d Correct the foreground suspension entrypoint in the test
f5d5b2b Use the public foreground lifecycle for regression-test cleanup
0eded5a End the UI exercise only after terminal consumer ownership is released
c0a61bb Keep UI state on its actor across the task-group boundary
30000a5 Drive and measure the real workspace UI before each soak; decouple background discovery
0d896f5 test: foreground refresh must finish while secondary discovery is blocked
4e739bb test: require real UI selection and stable first-frame measurements
aeb554a Measure real iOS UI readiness timings (manaflow-ai#12887)
6a2c896 Record per-operation iOS soak latencies (manaflow-ai#12883)
39d7669 test: advertise workspace actions in the soak reconnect fixture
7e9a676 fix: disconnect the soak session before testing reconnect
c51a096 test: require stress reconnect to replace a healthy connection
e1a2767 fix: import the workspace model from its owning module
e6a6ba4 fix: import mobile workspace preview module
3361141 Merge remote-tracking branch 'origin/main' into feat-ios-connectivity-soak
dbfebac Merge remote-tracking branch 'origin/main' into feat-ios-connectivity-soak
3f8ceec fix: forward connection snapshots through deferred Iroh transport
d8f30dc test: require deferred transports to forward native path snapshots
3dce84c fix: observe soak path and identity on the native RPC connection
38ee330 test: require native connection path evidence throughout soak
055880c test: cover final soak deadline and name failed usage actions
b27046c fix: bound stalled soak cycles with an independent deadline
7f1f748 test: require stalled soak operations to report promptly
e116e6b Use accepted boolean spelling for the Mac relay setting
aa3dc13 Exercise relay setup command arguments in both modes
db17d3d Constrain current Iroh endpoints to relays in app gates
245b55e Reproduce release gate missing current Iroh relay policy
c9289ed Add focused Iroh soak harness test plan
aec8291 Support an isolated agent account for unattended soaks
915c080 Add deterministic iOS Iroh connectivity soak workloads

# Conflicts:
#	.github/workflows/ci.yml
#	.github/workflows/iroh-v2.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants