Skip to content

Fix iOS CI false-green: align manual-pairing tests with encrypted-route restriction, catch Swift Testing failures in success override - #5906

Merged
lawrencecchen merged 2 commits into
mainfrom
fix-ios-ci-false-green
Jun 11, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
fix-ios-ci-false-green

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Two test/CI-only fixes extracted from #5726 so every open iOS branch can rebase onto them instead of re-fixing divergently.

The bug: d7ee59384 (June 4, restrict Stack token to encrypted routes — correct security fix) updated the policy unit tests but left four cmuxFeatureTests auth-contract tests asserting the OLD contract (Stack token sent over plain-TCP LAN/.local manual routes). They have failed deterministically since, throwing insecureManualRoute before any request is sent. Nobody noticed because the ios-simulator workflow's success-override grep only recognizes XCTest failure formats, not Swift Testing's ✘ output — so the jobs false-greened (verified: the green iphone job on the merged #5876 run shows the identical 4 failures, 12 issues, exit 65, TEST FAILED in its log).

Fixes:

  1. Rewrite the three LAN/.local tests as rejection-contract tests (pairing fails before any RPC leaves the device, actionable error surfaced, zero requests recorded) and repoint the probe-fallback test at a Tailscale host so the method_not_found → synthetic-ticket path keeps coverage.
  2. Add Swift Testing's ✘ Test/✘ Suite markers to the negative grep in test-ios.yml so the success override can never mask Swift Testing failures again.

Residual: the loophole existed since June 4; other iOS test failures may have slipped onto main in that window. Audit queued separately.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
Test and CI workflow changes only; no production pairing or auth logic is modified in this PR.

Overview
Fixes iOS CI false greens and brings manual-host pairing feature tests in line with the existing encrypted-route Stack-auth policy (no app behavior changes in this diff).

The test-ios.yml success-override helper now treats Swift Testing failures (✘ Test / ✘ Suite) like XCTest failures, so exit 65 with real test failures cannot be misread as simulator cleanup and pass the job.

In cmuxFeatureTests, three LAN / .local manual-pairing cases are rewritten to assert rejection before any RPC (pairing phase, actionable error, zero recorded requests). The attach-ticket probe fallback test now uses a Tailscale IP host instead of a private LAN address so method_not_found → Stack-auth fallback coverage remains on a trusted route.

Reviewed by Cursor Bugbot for commit c5c7860. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fix iOS CI false greens and align manual pairing tests with the encrypted-route-only policy. Failing simulator runs now fail, and tests no longer expect Stack auth over plain LAN/.local routes.

  • Bug Fixes
    • CI: include Swift Testing "✘ Test"/"✘ Suite" markers in the negative grep in test-ios.yml so failures aren’t masked by the success override.
    • Tests: convert LAN/.local manual-pairing cases to rejection (no RPCs, no Stack token, clear error) and repoint the probe→fallback test to a Tailscale host to keep coverage.

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

Review in cubic

Summary by CodeRabbit

  • Tests

    • Updated iOS feature tests to enforce stricter encryption requirements for untrusted targets.
  • Chores

    • Enhanced iOS test workflow to improve failure detection in the CI pipeline.

lawrencecchen and others added 2 commits June 11, 2026 14:44
…tion

d7ee593 restricted routeAllowsStackAuth to encrypted/loopback routes
(Tailscale, iroh, loopback) as a security fix but only updated the
policy unit tests. Four cmuxFeatureTests auth-contract tests still
asserted the old contract (Stack token sent over plain-TCP LAN/.local
manual routes) and have failed on every ios-simulator run since, masked
by the workflow's success-override grep not recognizing Swift Testing
failure output.

Rewrite the three LAN/.local tests as rejection-contract tests: pairing
fails before any RPC (and the Stack bearer token) leaves the device,
and the actionable route-not-allowed error is surfaced. Repoint the
probe-then-fallback test at a Tailscale host so the method_not_found to
synthetic-ticket fallback path keeps its coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
selected_tests_passed_despite_xcodebuild_status only knew XCTest
failure formats. Swift Testing prints failures as '✘ Test ... failed'
and the final 'Failing tests:' section is not always flushed into the
tee'd log, so a run with genuinely failing Swift Testing tests (exit
65) could be misclassified as a runner cleanup failure and turn the job
green. That false green let the stale manual-pairing tests merge red on
#5876 and earlier. Add the
Swift Testing failure markers to the negative grep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 11, 2026 9:55pm
cmux-staging Building Building Preview, Comment Jun 11, 2026 9:55pm

@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ce7817d8-ca53-49f2-872a-de879c28abfa

📥 Commits

Reviewing files that changed from the base of the PR and between 578a333 and c5c7860.

📒 Files selected for processing (2)
  • .github/workflows/test-ios.yml
  • ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift

📝 Walkthrough

Walkthrough

The PR updates iOS pairing tests to enforce encrypted-routes-only behavior, rejecting untrusted plain-TCP targets with a "pairing route is not allowed" error. It also improves CI test failure detection by teaching the workflow to recognize Swift Testing failure markers (✘ Test/✘ Suite) alongside existing XCTest patterns.

Changes

CI Workflow Test Failure Detection

Layer / File(s) Summary
Swift Testing marker detection in xcodebuild log classification
.github/workflows/test-ios.yml
The selected_tests_passed_despite_xcodebuild_status helper extends its failure-marker grep to recognize "✘ Test" and "✘ Suite" strings as Swift Testing failures, preventing false-positive pass results when xcodebuild exits green despite test failures. Inline comments document the failure-output flushing behavior.

Manual Host Pairing Security Enforcement Tests

Layer / File(s) Summary
Private-LAN and Bonjour pairing rejection tests
ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
Private-LAN and .local/Bonjour manual host pairing tests are replaced with rejection tests asserting pairing-phase failure: disconnected state, nil active ticket/route, "pairing route is not allowed" error, and zero RPC requests sent.
Tailscale IP probing test with updated connection expectations
ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
The "probe Tailscale host for attach ticket before Stack-auth fallback" test shifts to use a Tailscale IP host value and asserts successful workspace connection, connected state, and correct connectedHostName.
Default-port LAN host pairing rejection test
ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift
The default-port LAN host pairing test is replaced with a rejection test enforcing the same pairing-phase failure mode, with assertions for disconnected state, nil active ticket/route, the specific error message, and zero sent requests.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#5713: The pairing test updates enforce specific rejection behavior and error messages for untrusted plain-TCP targets, aligning with the introduction of MobilePairingFailureCategory classification and offline reachability short-circuiting in that PR.

Poem

A Swift-test sleuth hops through the logs so bright,
Catching ✘ marks that hide from plain sight.
The pairing bouncer stands firm at the gate,
Turning plain-TCP guests away—encrypted fate! 🐰✨

🚥 Pre-merge checks | ✅ 20 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: fixing iOS CI false greens and aligning manual-pairing tests with encrypted-route restrictions.
Description check ✅ Passed The description provides context, root cause analysis, and details of fixes, but lacks some template sections like Testing procedures and explicit checklist completion.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR #5906 changes only CI workflow (.github/workflows/test-ios.yml) and a test file (ios/.../Tests/cmuxFeatureTests.swift); no production Swift actor-isolation code is modified per rule .github/revi...
Cmux Swift Blocking Runtime ✅ Passed PR changes only iOS test workflow grep and cmuxFeatureTests; no production Swift blocking/timing sync primitives (semaphores, main.sync, locks for runtime) introduced.
Cmux Expensive Synchronous Load ✅ Passed PR #5906 changes only .github/workflows/test-ios.yml and ios/cmuxPackage/Tests/cmuxFeatureTests.swift; modified files don’t reference RestorableAgentSessionIndex.load/SharedLiveAgentIndex.shared, s...
Cmux Cache Substitution Correctness ✅ Passed PR #5906 only modifies the iOS test workflow and cmuxFeatureTests (no production Swift/TS/JS persistence/history/undo/snapshot logic), so no cache-substitution risk is introduced per the rule.
Cmux No Hacky Sleeps ✅ Passed Checked .github/workflows/test-ios.yml and runtime-no-hacky-sleeps.md: no sleep/setTimeout/setInterval/polling/wait-for delays added; workflow YAML is out-of-scope, and Swift sleeps are covered els...
Cmux Algorithmic Complexity ✅ Passed Workflow change only adds fixed grep patterns + 2-attempt loop; Swift test changes are small scripted fixtures with no scalable loops/sorts/filters added, so algorithmic-complexity rules aren’t vio...
Cmux Swift Concurrency ✅ Passed Swift diff only updates XCTest/Swift Testing grep and @Test async/await host-pairing assertions; no new DispatchQueue/Combine/completion-handler or fire-and-forget Task patterns found vs swift-conc...
Cmux Swift @Concurrent ✅ Passed PR only changes .github/workflows/test-ios.yml and cmuxFeatureTests.swift; Swift diff contains no @concurrent or nonisolated async additions, so it doesn’t violate swift-concurrent-annotation rules.
Cmux Swift File And Package Boundaries ✅ Passed PR changes only .github/workflows/test-ios.yml and ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift (test code); no production Swift file boundary/oversize violations.
Cmux Swift Logging ✅ Passed PR only updates iOS CI workflow shell grep and cmuxFeatureTests; PR diff view shows no print/debugPrint/dump/NSLog or Logger additions, so swift-logging rules aren’t violated.
Cmux User-Facing Error Privacy ✅ Passed Inspected .github/workflows/test-ios.yml and cmuxFeatureTests.swift: CI helper only tweaks grep for ✘ markers; updated tests assert pairing-route errors with no new production user-facing error cop...
Cmux Full Internationalization ✅ Passed PR #5906 changes only .github/workflows/test-ios.yml and ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift; no production i18n catalogs or user-facing strings are modified.
Cmux Swiftui State Layout ✅ Passed PR #5906 only updates test-ios.yml and cmuxFeatureTests.swift; no SwiftUI/ObservableObject/@Published/GeometryReader/@Observable/LazyVStack additions found, so swiftui-state-layout rules aren’t vio...
Cmux Architecture Rethink ✅ Passed Diff only updates iOS CI grep and cmuxFeatureTests rejection/expectation assertions; added Swift lines contain no sleeps/delayed dispatch/polling/locks/observers/duplicate entrypoints per swift-arc...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR changes only .github/workflows/test-ios.yml and ios/.../cmuxFeatureTests.swift test cases; no Swift code adds/modifies NSWindow/NSPanel/WindowGroup/SwiftUI Window or cmux.* auxiliary window iden...
Cmux Source Artifacts ✅ Passed PR only modifies .github/workflows/test-ios.yml and ios/.../cmuxFeatureTests.swift; diff shows CI grep/test assertion code, not added generated artifacts/DerivedData/caches/log files or broad scrat...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-ios-ci-false-green

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 and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two CI correctness issues: (1) four cmuxFeatureTests auth-contract tests that regressed when the June 4 commit restricted Stack token use to encrypted routes only, and (2) a Swift Testing blind spot in the test-ios.yml success-override grep that allowed those failures to produce false-green CI results.

  • Test rewrites: Three LAN/.local tests are converted from connection-success assertions to early-rejection contract tests (zero requests sent, specific insecureManualRoute error surfaced, store stays in .pairing/.disconnected state); the fourth (probe-fallback) is repointed from a private LAN IP to a Tailscale CGNAT IP (100.71.210.41) so it exercises the encrypted-route path it always intended to cover.
  • CI fix: Adds ✘ Test|✘ Suite to the negative grep inside selected_tests_passed_despite_xcodebuild_status, so Swift Testing inline failure markers are caught before the override exits 0.

Confidence Score: 4/5

Safe to merge — both files are test/CI-only with no production Swift changes.

The Swift test rewrites correctly track the new encrypted-routes-only policy and the zero-requests assertion is a solid contract check. The CI grep fix closes the confirmed false-green loophole. The remaining gap is whether ✘ Test and ✘ Suite are exhaustive for all Swift Testing failure modes — a test that terminates via an uncaught throw or fatal error may not emit those markers, leaving the success override potentially able to green-light it.

test-ios.yml — the success-override grep is the most sensitive piece; confirm the ✘ Test / ✘ Suite tokens against an actual Swift Testing crash log before treating the CI guard as complete.

Important Files Changed

Filename Overview
.github/workflows/test-ios.yml Adds `✘ Test
ios/cmuxPackage/Tests/cmuxFeatureTests/cmuxFeatureTests.swift Four test functions rewritten to match the current encrypted-routes-only Stack-token policy; rejection tests correctly assert zero sent requests and the exact rejection error string; probe-fallback test repointed at a Tailscale CGNAT IP to restore intended coverage.

Sequence Diagram

sequenceDiagram
    participant App as iOS App
    participant Store as CMUXMobileShellStore
    participant Policy as routeAllowsStackAuth
    participant Transport as ScriptedTransport

    Note over App,Transport: LAN/Bonjour route (rejected by policy)
    App->>Store: connectManualHost("192.168.1.77" / "devbox.local")
    Store->>Policy: routeAllowsStackAuth(route)
    Policy-->>Store: false (unencrypted TCP)
    Store-->>App: "connectionError = "This pairing route is not allowed...""
    Note right of Store: phase=.pairing, state=.disconnected

    Note over App,Transport: Tailscale route (accepted by policy)
    App->>Store: connectManualHost("100.71.210.41")
    Store->>Policy: routeAllowsStackAuth(route)
    Policy-->>Store: true (Tailscale encrypted)
    Store->>Transport: mobile.attach_ticket.create (+ Stack token)
    Transport-->>Store: method_not_found
    Store->>Transport: workspace.list (synthetic ticket fallback)
    Transport-->>Store: workspace list
    Store-->>App: "phase=.workspaces, state=.connected"
Loading

Comments Outside Diff (1)

  1. .github/workflows/test-ios.yml, line 257 (link)

    P2 The echo message still says "XCTest reported the selected tests passed" even though selected_tests_passed_despite_xcodebuild_status now also checks for Swift Testing failures. A developer reading CI logs after this PR would see the old message on a legitimately clean run and might not realize Swift Testing output was also checked, complicating future debugging.

    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!

Reviews (1): Last reviewed commit: "Catch Swift Testing failures in the ios-..." | Re-trigger Greptile

grep -Eq "Test Suite 'Selected tests' passed|Test Suite 'cmuxUITests' passed" "$log_path" &&
grep -Eq "Executed [1-9][0-9]* tests, with 0 failures \\(0 unexpected\\)" "$log_path" &&
! grep -Eq "Test Suite '.*' failed|Test Case '.*' failed|Assertion Failure|Failing tests:|with [1-9][0-9]* failures|with [0-9]+ failures \\([1-9][0-9]* unexpected\\)" "$log_path"
! grep -Eq "Test Suite '.*' failed|Test Case '.*' failed|Assertion Failure|Failing tests:|with [1-9][0-9]* failures|with [0-9]+ failures \\([1-9][0-9]* unexpected\\)|✘ Test|✘ Suite" "$log_path"

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 Swift Testing crash / fatal-error failures may still slip through

✘ Test and ✘ Suite cover assertion failures and normal test failures, but Swift Testing can also terminate a test runner process via an uncaught thrown error or a fatal error, which in xcodebuild's log may surface as Fatal error:, error: ..., or just a non-zero exit without any ✘ marker. The existing Assertion Failure pattern catches one XCTest form; there is no equivalent for Swift Testing's Issue.record path or for a fatal thrown error outside a #expect. If any test in the suite crashes rather than fails gracefully, the negative grep could still pass and false-green the job. Worth adding a broader safety net (e.g. Issue recorded) once the exact log tokens are confirmed against a real crash log.

@lawrencecchen
lawrencecchen merged commit cf8e144 into main Jun 11, 2026
28 checks passed
lawrencecchen added a commit that referenced this pull request Jun 12, 2026
Re-applies origin/feat-ios-dog-unified (771f532, dog 11b) onto current
origin/main (f2627b9). Main's reviewed forms win for landed features
(composer #5876, QR #5872, presence gate #5912, CI fix #5906): seven
pairing/RPC/transport files take main's private-extension structure,
persistPairedMacFromTicket keeps main's serialized-write-chain lookup,
QR pairing tests keep main's durable-write polls. Dog-carried unlanded
work is preserved: MobileHostService+Capabilities.swift superset
(notification.dismiss.v1, terminal.paste.v1, workspace.groups.v1, DEBUG
dogfood verbs) replaces main's inline subset var, dismiss-sync observer
and capability flag resets kept, dogfood pane model kept.
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jun 12, 2026
…te restriction, catch Swift Testing failures in success override (manaflow-ai#5906)

* Align manual-pairing auth-contract tests with encrypted-route restriction

d7ee593 restricted routeAllowsStackAuth to encrypted/loopback routes
(Tailscale, iroh, loopback) as a security fix but only updated the
policy unit tests. Four cmuxFeatureTests auth-contract tests still
asserted the old contract (Stack token sent over plain-TCP LAN/.local
manual routes) and have failed on every ios-simulator run since, masked
by the workflow's success-override grep not recognizing Swift Testing
failure output.

Rewrite the three LAN/.local tests as rejection-contract tests: pairing
fails before any RPC (and the Stack bearer token) leaves the device,
and the actionable route-not-allowed error is surfaced. Repoint the
probe-then-fallback test at a Tailscale host so the method_not_found to
synthetic-ticket fallback path keeps its coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Catch Swift Testing failures in the ios-simulator success override

selected_tests_passed_despite_xcodebuild_status only knew XCTest
failure formats. Swift Testing prints failures as '✘ Test ... failed'
and the final 'Failing tests:' section is not always flushed into the
tee'd log, so a run with genuinely failing Swift Testing tests (exit
65) could be misclassified as a runner cleanup failure and turn the job
green. That false green let the stale manual-pairing tests merge red on
manaflow-ai#5876 and earlier. Add the
Swift Testing failure markers to the negative grep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
austinywang added a commit that referenced this pull request Jun 17, 2026
…eforeReplay (#5911)

The terminal-output collector tests waited for stream chunks with a fixed
200ms polling budget (200×1ms). On the slower iPad simulator leg the live
"current" replay chunk lands after that window, so the assertion saw only the
"old" snapshot and the test failed — a flake previously masked by the test-ios
false-green override (#5906).

Replace the time-budget poll with a deterministic continuation-based
synchronization point on TerminalOutputCollector: waitForLines(_:) parks a
CheckedContinuation and resumes the instant the stream delivers the target
chunk count, with no per-platform timing budget to overrun. unmount() releases
any parked waiter so a pending wait never hangs. Applied to all three sibling
tests that shared the fragile poll pattern.

Closes #5911

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview – cmux — c5c78606 Deployed Jun 11, 2026 by vercel[bot]
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.

1 participant