Skip to content

Add latest macOS launch crash smoke test - #2765

Closed
lawrencecchen wants to merge 3 commits into
mainfrom
task-latest-macos-launch-crash-smoke
Closed

lawrencecchen wants to merge 3 commits into
mainfrom
task-latest-macos-launch-crash-smoke

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Apr 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add LatestMacOSLaunchSmokeUITests, a startup smoke test that launches cmux and verifies it stays alive through startup on latest macOS CI runners
  • seed an isolated HOME with managed ~/.config/cmux/settings.json (app.appIcon = automatic) before launch so the test exercises the same managed app-icon startup path reported in Instant crash on macOS 26.4.1 #2763
  • run the launch smoke class in .github/workflows/ci-macos-compat.yml on the macOS 26 lane (latest 26.x)

Testing

Issues

Summary by CodeRabbit

  • Tests

    • Added UI smoke tests to validate app launch stability on latest macOS versions.
    • Added automated startup crash detection probes to catch launch issues across multiple configurations.
  • Chores

    • Updated CI workflow configuration to run additional test suites on different macOS versions.

@vercel

vercel Bot commented Apr 10, 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 Apr 10, 2026 0:45am

@coderabbitai

coderabbitai Bot commented Apr 10, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a conditional macOS UI launch smoke test and a CI startup-crash probe: new XCUITest source and Xcode project entries, CI matrix flags and conditional xcodebuild steps, plus a bash script that repeatedly probes app startup from Xcode DerivedData.

Changes

Cohort / File(s) Summary
CI workflow
​.github/workflows/ci-macos-compat.yml
Added matrix flags launch_smoke_ui and direct_startup_probe with lane-specific values; added conditional steps to run the Xcode UI smoke test and to build+run the startup-crash-probe script when flags are true; propagate CMUX_SKIP_ZIG_BUILD.
Xcode project
GhosttyTabs.xcodeproj/project.pbxproj
Registered new UI test source LatestMacOSLaunchSmokeUITests.swift in the cmuxUITests target so it is compiled into the UI test bundle.
UI test
cmuxUITests/LatestMacOSLaunchSmokeUITests.swift
New LatestMacOSLaunchSmokeUITests that creates an isolated HOME, writes managed settings fixture, launches XCUIApplication with env (CMUX_TAG, HOME), wraps launch in non-strict XCTExpectFailure, polls for running state (20s) and stability (10s), then terminates and cleans up.
Startup probe script
scripts/startup-crash-probe-ci.sh
New script to locate built cmux DEV.app, run repeated startup probes across iterations and app-icon modes, create temp HOME with settings fixture, detect early exits, print logs/diagnostics on failure, and exit non-zero if any probe fails.

Sequence Diagram(s)

sequenceDiagram
  participant GH as GitHub Actions
  participant CI as CI Job
  participant XCB as xcodebuild
  participant SCRIPT as startup-crash-probe script
  participant XCT as XCTest Runner
  participant APP as App Process
  participant FS as Filesystem (isolated HOME)

  GH->>CI: start macOS compatibility job (matrix lane)
  CI-->GH: evaluate matrix flags
  alt launch_smoke_ui == true
    CI->>XCB: run xcodebuild test target (cmuxUITests/...LatestMacOSLaunchSmokeUITests)
    XCB->>XCT: start XCTest runner
    XCT->>FS: create isolated HOME + write settings.json
    XCT->>APP: launch XCUIApplication (CMUX_TAG, HOME)
    APP-->>XCT: report lifecycle (running/exit)
    XCT->>APP: poll for running state and stability, then terminate
    XCT->>FS: remove isolated HOME
  end
  alt direct_startup_probe == true
    CI->>XCB: build cmux scheme (DerivedData)
    CI->>SCRIPT: run startup-crash-probe (ITERATIONS, STABILITY_SECONDS)
    SCRIPT->>FS: create temp HOME + write settings.json
    SCRIPT->>APP: launch app binary (env CMUX_TAG, HOME, CMUX_SOCKET_MODE)
    APP-->>SCRIPT: exit or remain running
    SCRIPT->>FS: collect logs/diagnostics on failure, cleanup temp HOME
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 I hopped into CI, nose twitching bright,
Wrote a HOME, set env, and watched it light,
Tolerant of headless skies and flaky starts,
Polling the app’s heartbeat with patient arts,
A rabbit’s clap for launches passing night.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a launch crash smoke test for the latest macOS configuration.
Description check ✅ Passed The description covers the summary and testing sections required by the template, though the demo video and checklist items are missing.

✏️ 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 task-latest-macos-launch-crash-smoke

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 Apr 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds LatestMacOSLaunchSmokeUITests, a minimal UI smoke test that launches cmux and asserts it stays alive for 10 seconds, wired into a new launch_smoke_ui matrix lane that runs on warp-macos-26-arm64-6x with Zig skipped. The test is well-structured and complies with the project's test quality policy by exercising observable runtime behavior end-to-end.

Confidence Score: 5/5

This PR is safe to merge — it adds a well-structured smoke test and a targeted CI lane with no changes to production code.

All findings are P2 (style/hardening suggestions). The test logic correctly detects launch crashes via isRunning polling even when XCTExpectFailure swallows the app.launch() failure, the workflow step ordering is correct, and the project file changes are standard boilerplate.

cmuxUITests/LatestMacOSLaunchSmokeUITests.swift — minor hardening opportunities around isRunning state coverage and XCTExpectFailure matcher scope.

Important Files Changed

Filename Overview
cmuxUITests/LatestMacOSLaunchSmokeUITests.swift New UI smoke test for macOS 26 launch stability; logic is sound but isRunning omits .runningBackgroundSuspended and XCTExpectFailure swallows all launch failures without a targeted matcher, slightly weakening debuggability.
.github/workflows/ci-macos-compat.yml Adds launch_smoke_ui matrix flag and a new workflow step that runs the smoke test only on the macOS 26 lane; correctly placed after package resolution and before the virtual-display/smoke steps that only run on macOS 15.
GhosttyTabs.xcodeproj/project.pbxproj Adds LatestMacOSLaunchSmokeUITests.swift to the cmuxUITests target's file reference, group, and sources build phase — standard boilerplate, no issues.

Sequence Diagram

sequenceDiagram
    participant CI as GitHub Actions (macOS 26 lane)
    participant XB as xcodebuild test
    participant Host as XCTest Runner
    participant App as cmux.app

    CI->>XB: xcodebuild -only-testing:LatestMacOSLaunchSmokeUITests test
    XB->>Host: launch test host process
    Host->>App: app.launch() (wrapped in XCTExpectFailure)
    App-->>Host: state → runningForeground / runningBackground
    Host->>Host: waitForAppToStart (poll 0.2s, up to 20s)
    Host->>Host: waitForNoImmediateCrash (poll 0.2s for 10s)
    alt app stays running
        Host->>App: app.terminate()
        Host-->>CI: test passed
    else app crashes
        App-->>Host: state → notRunning
        Host-->>CI: XCTAssertTrue failure
    end
Loading

Reviews (1): Last reviewed commit: "Add latest macOS launch crash smoke UI t..." | Re-trigger Greptile

Comment on lines +66 to +68
private func isRunning(_ app: XCUIApplication) -> Bool {
app.state == .runningForeground || app.state == .runningBackground
}

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 runningBackgroundSuspended not covered

isRunning treats .runningBackgroundSuspended as "not running", so if macOS suspends the app during the 10-second stability window waitForNoImmediateCrash will return false and the test will report a false crash. While macOS rarely suspends apps, it is a valid XCUIApplicationState on the platform and including it keeps the test from flaking on heavily-loaded CI runners.

Suggested change
private func isRunning(_ app: XCUIApplication) -> Bool {
app.state == .runningForeground || app.state == .runningBackground
}
private func isRunning(_ app: XCUIApplication) -> Bool {
app.state == .runningForeground
|| app.state == .runningBackground
|| app.state == .runningBackgroundSuspended
}

Comment on lines +34 to +42
private func launchAllowingHeadlessBackgroundState(_ app: XCUIApplication) {
// Some CI runners launch in background-only mode, which can emit an
// activation failure even when the process is healthy.
let options = XCTExpectedFailure.Options()
options.isStrict = false
XCTExpectFailure("App activation may fail on headless CI runners", options: options) {
app.launch()
}
}

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 XCTExpectFailure swallows all launch failures without a targeted matcher

No issueMatcher is supplied, so every failure recorded inside the block — including codesign errors, bundle-not-found, or sandbox violations — is silently suppressed. When any of those infrastructure issues occur, the test continues and ultimately fails with "Expected cmux to start" instead of the underlying cause, making CI failures harder to diagnose. Consider adding an issue matcher scoped to activation/connection errors:

Suggested change
private func launchAllowingHeadlessBackgroundState(_ app: XCUIApplication) {
// Some CI runners launch in background-only mode, which can emit an
// activation failure even when the process is healthy.
let options = XCTExpectedFailure.Options()
options.isStrict = false
XCTExpectFailure("App activation may fail on headless CI runners", options: options) {
app.launch()
}
}
private func launchAllowingHeadlessBackgroundState(_ app: XCUIApplication) {
// Some CI runners launch in background-only mode, which can emit an
// activation failure even when the process is healthy.
let options = XCTExpectedFailure.Options()
options.isStrict = false
options.issueMatcher = { $0.compactDescription.contains("activation") || $0.compactDescription.contains("connection") }
XCTExpectFailure("App activation may fail on headless CI runners", options: options) {
app.launch()
}
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci-macos-compat.yml:
- Line 20: Add an actionlint config to allowlist the custom self-hosted runner
labels so actionlint stops reporting unknown runners like "warp-macos-*" used in
runs-on; create a file named .github/actionlint.yaml that defines the
"self-hosted-runner" key with a labels list including "warp-macos-*" and
"depot-macos-*", ensuring any workflows that use runs-on: warp-macos-26-arm64-6x
(and similar labels) pass actionlint validation.

In `@cmuxUITests/LatestMacOSLaunchSmokeUITests.swift`:
- Around line 12-31: The test currently calls app.terminate() directly and can
skip that call if an assertion fails; wrap cleanup in an addTeardownBlock to
guarantee termination: capture the XCUIApplication instance `app` created in
`testAppLaunchDoesNotCrashOnStartup()` and add an `addTeardownBlock` that checks
`isRunning(app)` and calls `app.terminate()` so the app is always terminated
even on early assertion failures (remove or keep the inline terminate only if
you still have the teardown to ensure cleanup).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ef9468f-513c-4659-8294-fdc7c851e955

📥 Commits

Reviewing files that changed from the base of the PR and between 9155d09 and 3ba23f0.

📒 Files selected for processing (3)
  • .github/workflows/ci-macos-compat.yml
  • GhosttyTabs.xcodeproj/project.pbxproj
  • cmuxUITests/LatestMacOSLaunchSmokeUITests.swift

smoke: true
skip_zig: false
launch_smoke_ui: false
- os: warp-macos-26-arm64-6x

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Workflow references =="
rg -n 'warp-macos-26-arm64-6x|warp-macos-15-arm64-6x|runs-on:' .github/workflows/ci-macos-compat.yml

echo
echo "== actionlint config files =="
fd -HI 'actionlint\.ya?ml$' || true

echo
echo "== actionlint label allowlist snippets =="
for f in $(fd -HI 'actionlint\.ya?ml$'); do
  echo "--- $f"
  rg -n 'label|labels|self-hosted|warp-macos|runner' "$f" || true
done

Repository: manaflow-ai/cmux

Length of output: 276


🏁 Script executed:

# Check for any actionlint references in the repo
echo "=== Searching for actionlint references ==="
rg -r 'actionlint' . --type yaml --type toml --type json --type yml 2>/dev/null | head -20

echo
echo "=== Checking for other workflow files ==="
fd -HI '\.ya?ml$' .github/workflows | head -20

echo
echo "=== Other workflows using warp runners ==="
for f in $(fd -HI '\.ya?ml$' .github/workflows); do
  if rg -q 'warp-macos' "$f" 2>/dev/null; then
    echo "Found in: $f"
    rg -n 'warp-macos' "$f"
  fi
done

echo
echo "=== Checking for actionlint in package.json or CI config ==="
fd -HI 'package.json|\.github' . | xargs -I {} sh -c 'test -f {} && grep -l actionlint {} 2>/dev/null || true'

Repository: manaflow-ai/cmux

Length of output: 1160


🏁 Script executed:

# Check if actionlint is in any config files (including hidden/dotfiles)
echo "=== All files in .github directory ==="
find .github -type f | head -30

echo
echo "=== Check for CI/linting setup ==="
ls -la .github/ 2>/dev/null || true

Repository: manaflow-ai/cmux

Length of output: 970


🌐 Web query:

warp-macos-26-arm64-6x GitHub Actions runner

💡 Result:

warp-macos-26-arm64-6x is a WarpBuild (third-party) GitHub Actions cloud runner label for macOS 26 on Apple Silicon (arm64) with 6 vCPU, 22 GB RAM, 120 GB SSD (priced at $0.08/min per WarpBuild’s docs). [1]

Use it in your workflow like:

jobs:
  build:
    runs-on: warp-macos-26-arm64-6x
    steps:
      - uses: actions/checkout@v4

If you meant GitHub-hosted runners instead, GitHub’s equivalent label is macos-26 (standard arm64) or macos-26-xlarge (larger arm64). [2]

Sources: WarpBuild runner tags/specs [1], GitHub macOS 26 runner labels [2], WarpBuild macOS runner overview [3].


🌐 Web query:

actionlint warp-macos runner validation

💡 Result:

actionlint validates runs-on against a built-in list of runner labels, so WarpBuild labels like warp-macos-latest-arm64-6x will be reported as “unknown” unless you allow them via config. WarpBuild’s macOS runner label example is warp-macos-latest-arm64-6x. [1]

Add an actionlint config at .github/actionlint.yaml and whitelist the Warp labels as self-hosted runner labels (glob patterns are supported):

self-hosted-runner:
  labels:
    - warp-macos-*

This tells actionlint that warp-macos-* is an allowed custom runner label and stops the runner-label validation error. [2][3]


🏁 Script executed:

# Check if there are any comments in workflows about these runners or depot
echo "=== Comments about warp/depot runners ==="
rg -n 'depot|self.hosted|self-hosted|warp' .github/workflows/ || true

echo
echo "=== Check the specific ci-macos-compat.yml file context ==="
cat -n .github/workflows/ci-macos-compat.yml | head -30

Repository: manaflow-ai/cmux

Length of output: 2424


Add actionlint config to allowlist WarpBuild runner labels and prevent CI validation failures.

actionlint validates runs-on against a built-in list of runner labels and will report warp-macos-* runners as unknown. Create .github/actionlint.yaml with:

self-hosted-runner:
  labels:
    - warp-macos-*
    - depot-macos-*

This affects multiple workflows using these runner labels across the repository.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 20-20: label "warp-macos-26-arm64-6x" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-intel", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci-macos-compat.yml at line 20, Add an actionlint config
to allowlist the custom self-hosted runner labels so actionlint stops reporting
unknown runners like "warp-macos-*" used in runs-on; create a file named
.github/actionlint.yaml that defines the "self-hosted-runner" key with a labels
list including "warp-macos-*" and "depot-macos-*", ensuring any workflows that
use runs-on: warp-macos-26-arm64-6x (and similar labels) pass actionlint
validation.

Comment on lines +12 to +31
func testAppLaunchDoesNotCrashOnStartup() {
let app = XCUIApplication()
app.launchEnvironment["CMUX_TAG"] = launchTag
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"

launchAllowingHeadlessBackgroundState(app)

XCTAssertTrue(
waitForAppToStart(app, timeout: 20.0),
"Expected cmux to start on latest macOS. state=\(app.state.rawValue)"
)

XCTAssertTrue(
waitForNoImmediateCrash(app, duration: 10.0),
"Expected cmux to remain running for startup stability window. state=\(app.state.rawValue)"
)

if isRunning(app) {
app.terminate()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Ensure cleanup always runs even on early assertion failure.

If either assertion fails, execution can stop before the explicit terminate call, leaving the app alive for subsequent tests in the same run. Move termination into addTeardownBlock.

💡 Proposed fix
 func testAppLaunchDoesNotCrashOnStartup() {
     let app = XCUIApplication()
     app.launchEnvironment["CMUX_TAG"] = launchTag
     app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
+    addTeardownBlock {
+        if app.state == .runningForeground || app.state == .runningBackground {
+            app.terminate()
+        }
+    }

     launchAllowingHeadlessBackgroundState(app)
@@
-    if isRunning(app) {
-        app.terminate()
-    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func testAppLaunchDoesNotCrashOnStartup() {
let app = XCUIApplication()
app.launchEnvironment["CMUX_TAG"] = launchTag
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
launchAllowingHeadlessBackgroundState(app)
XCTAssertTrue(
waitForAppToStart(app, timeout: 20.0),
"Expected cmux to start on latest macOS. state=\(app.state.rawValue)"
)
XCTAssertTrue(
waitForNoImmediateCrash(app, duration: 10.0),
"Expected cmux to remain running for startup stability window. state=\(app.state.rawValue)"
)
if isRunning(app) {
app.terminate()
}
func testAppLaunchDoesNotCrashOnStartup() {
let app = XCUIApplication()
app.launchEnvironment["CMUX_TAG"] = launchTag
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
addTeardownBlock {
if app.state == .runningForeground || app.state == .runningBackground {
app.terminate()
}
}
launchAllowingHeadlessBackgroundState(app)
XCTAssertTrue(
waitForAppToStart(app, timeout: 20.0),
"Expected cmux to start on latest macOS. state=\(app.state.rawValue)"
)
XCTAssertTrue(
waitForNoImmediateCrash(app, duration: 10.0),
"Expected cmux to remain running for startup stability window. state=\(app.state.rawValue)"
)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/LatestMacOSLaunchSmokeUITests.swift` around lines 12 - 31, The
test currently calls app.terminate() directly and can skip that call if an
assertion fails; wrap cleanup in an addTeardownBlock to guarantee termination:
capture the XCUIApplication instance `app` created in
`testAppLaunchDoesNotCrashOnStartup()` and add an `addTeardownBlock` that checks
`isRunning(app)` and calls `app.terminate()` so the app is always terminated
even on early assertion failures (remove or keep the inline terminate only if
you still have the teardown to ensure cleanup).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmuxUITests/LatestMacOSLaunchSmokeUITests.swift">

<violation number="1" location="cmuxUITests/LatestMacOSLaunchSmokeUITests.swift:39">
P2: `XCTExpectFailure` without an `issueMatcher` silently suppresses **all** failures inside the block — including codesign errors, bundle-not-found, and sandbox violations. When any of those occur, the test continues past `launch()` and ultimately fails at the "Expected cmux to start" assertion, hiding the real root cause. Scope the matcher to activation/connection errors only:

```swift
options.issueMatcher = { issue in
    issue.compactDescription.contains("activation")
        || issue.compactDescription.contains("connection")
}
```</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

// activation failure even when the process is healthy.
let options = XCTExpectedFailure.Options()
options.isStrict = false
XCTExpectFailure("App activation may fail on headless CI runners", options: options) {

@cubic-dev-ai cubic-dev-ai Bot Apr 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: XCTExpectFailure without an issueMatcher silently suppresses all failures inside the block — including codesign errors, bundle-not-found, and sandbox violations. When any of those occur, the test continues past launch() and ultimately fails at the "Expected cmux to start" assertion, hiding the real root cause. Scope the matcher to activation/connection errors only:

options.issueMatcher = { issue in
    issue.compactDescription.contains("activation")
        || issue.compactDescription.contains("connection")
}
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmuxUITests/LatestMacOSLaunchSmokeUITests.swift, line 39:

<comment>`XCTExpectFailure` without an `issueMatcher` silently suppresses **all** failures inside the block — including codesign errors, bundle-not-found, and sandbox violations. When any of those occur, the test continues past `launch()` and ultimately fails at the "Expected cmux to start" assertion, hiding the real root cause. Scope the matcher to activation/connection errors only:

```swift
options.issueMatcher = { issue in
    issue.compactDescription.contains("activation")
        || issue.compactDescription.contains("connection")
}
```</comment>

<file context>
@@ -0,0 +1,69 @@
+        // activation failure even when the process is healthy.
+        let options = XCTExpectedFailure.Options()
+        options.isStrict = false
+        XCTExpectFailure("App activation may fail on headless CI runners", options: options) {
+            app.launch()
+        }
</file context>
Fix with Cubic

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

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: 0e81c1df9e

ℹ️ 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".


let app = XCUIApplication()
app.launchEnvironment["CMUX_TAG"] = launchTag
app.launchEnvironment["HOME"] = launchHomeDirectory.path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Write launch fixture to cmux settings path

This smoke test creates settings.json under a temporary directory and relies on app.launchEnvironment["HOME"] to redirect cmux there, but cmux resolves its primary settings file from FileManager.default.homeDirectoryForCurrentUser (Sources/KeyboardShortcutSettingsFileStore.swift:99-101). Because that path is not derived from the test’s HOME launch environment, the fixture may be ignored and the test can pass without exercising the managed app.appIcon startup path it is meant to guard. Please write the fixture to the path cmux actually reads (or add/use an explicit settings-path override consumed by the app).

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

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: 1f80c54edb

ℹ️ 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".

log_file="$LOG_DIR/${iteration}-${mode}.log"

echo "--- Launch iteration=$iteration mode=$mode tag=$tag ---"
HOME="$probe_home" CMUX_TAG="$tag" CMUX_SOCKET_MODE=allowAll "$BINARY_PATH" >"$log_file" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Seed startup-probe settings at cmux's resolved settings path

The probe launches cmux with HOME="$probe_home", but cmux resolves managed settings from FileManager.default.homeDirectoryForCurrentUser / .applicationSupportDirectory (Sources/KeyboardShortcutSettingsFileStore.swift:99-114), not from this launch env override. That means the fixtures written under $probe_home/.config/... and $probe_home/Library/Application Support/... are frequently ignored, so this job can pass without exercising the intended app.appIcon startup paths (automatic/light/dark) and miss the regression it is meant to catch.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/startup-crash-probe-ci.sh`:
- Around line 104-107: The current shutdown sequence may hang because wait
"$app_pid" can block indefinitely if the process ignores TERM; update the block
that uses app_pid (the kill -0 check, kill "$app_pid", and wait "$app_pid") to
perform a bounded wait instead of an unbounded wait: either wrap the wait in a
timeout (e.g., use the timeout utility to limit wait to a few seconds) or
replace the single wait with a short polling loop that checks kill -0 "$app_pid"
with a sleep and a max-iteration/countdown to give up after a bounded interval,
then proceed (ensure you still attempt kill and consume exit status as before).
- Around line 11-15: The APP_PATH assignment uses a single find -print -quit
which can return an arbitrary/stale app; change it to collect all matching paths
and deterministically pick the most recent build (e.g., use find to list all
matches for "*/Build/Products/Debug/cmux DEV.app", then sort by modification
time and select the newest) before testing [[ -z "$APP_PATH" ]], updating the
APP_PATH variable assignment and keeping rest of the script intact (refer to the
APP_PATH variable and the existing find invocation).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 76516cb7-9a74-41d1-9f0a-bdd861920006

📥 Commits

Reviewing files that changed from the base of the PR and between 0e81c1d and 1f80c54.

📒 Files selected for processing (2)
  • .github/workflows/ci-macos-compat.yml
  • scripts/startup-crash-probe-ci.sh

Comment on lines +11 to +15
APP_PATH="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path "*/Build/Products/Debug/cmux DEV.app" -print -quit 2>/dev/null || true)"
if [[ -z "$APP_PATH" ]]; then
echo "ERROR: could not find built cmux DEV.app in DerivedData" >&2
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make app-bundle discovery deterministic.

Using find ... -print -quit can pick an arbitrary/stale DerivedData app when multiple builds exist, which can mask regressions in this probe.

Proposed fix
-APP_PATH="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path "*/Build/Products/Debug/cmux DEV.app" -print -quit 2>/dev/null || true)"
+APP_PATH="$(
+  find "$HOME/Library/Developer/Xcode/DerivedData" \
+    -type d -path "*/Build/Products/Debug/cmux DEV.app" -print0 2>/dev/null \
+  | xargs -0 stat -f '%m %N' 2>/dev/null \
+  | sort -nr \
+  | head -n 1 \
+  | cut -d' ' -f2- || true
+)"
 if [[ -z "$APP_PATH" ]]; then
   echo "ERROR: could not find built cmux DEV.app in DerivedData" >&2
   exit 1
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
APP_PATH="$(find "$HOME/Library/Developer/Xcode/DerivedData" -path "*/Build/Products/Debug/cmux DEV.app" -print -quit 2>/dev/null || true)"
if [[ -z "$APP_PATH" ]]; then
echo "ERROR: could not find built cmux DEV.app in DerivedData" >&2
exit 1
fi
APP_PATH="$(
find "$HOME/Library/Developer/Xcode/DerivedData" \
-type d -path "*/Build/Products/Debug/cmux DEV.app" -print0 2>/dev/null \
| xargs -0 stat -f '%m %N' 2>/dev/null \
| sort -nr \
| head -n 1 \
| cut -d' ' -f2- || true
)"
if [[ -z "$APP_PATH" ]]; then
echo "ERROR: could not find built cmux DEV.app in DerivedData" >&2
exit 1
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/startup-crash-probe-ci.sh` around lines 11 - 15, The APP_PATH
assignment uses a single find -print -quit which can return an arbitrary/stale
app; change it to collect all matching paths and deterministically pick the most
recent build (e.g., use find to list all matches for
"*/Build/Products/Debug/cmux DEV.app", then sort by modification time and select
the newest) before testing [[ -z "$APP_PATH" ]], updating the APP_PATH variable
assignment and keeping rest of the script intact (refer to the APP_PATH variable
and the existing find invocation).

Comment on lines +104 to +107
if kill -0 "$app_pid" 2>/dev/null; then
kill "$app_pid" 2>/dev/null || true
wait "$app_pid" 2>/dev/null || true
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bound shutdown wait to avoid CI hangs.

wait "$app_pid" can block indefinitely if TERM is ignored, stalling the probe/job until global timeout.

Proposed fix
 if kill -0 "$app_pid" 2>/dev/null; then
   kill "$app_pid" 2>/dev/null || true
-  wait "$app_pid" 2>/dev/null || true
+  for _ in {1..20}; do
+    if ! kill -0 "$app_pid" 2>/dev/null; then
+      break
+    fi
+    sleep 0.25
+  done
+  if kill -0 "$app_pid" 2>/dev/null; then
+    kill -9 "$app_pid" 2>/dev/null || true
+  fi
+  wait "$app_pid" 2>/dev/null || true
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/startup-crash-probe-ci.sh` around lines 104 - 107, The current
shutdown sequence may hang because wait "$app_pid" can block indefinitely if the
process ignores TERM; update the block that uses app_pid (the kill -0 check,
kill "$app_pid", and wait "$app_pid") to perform a bounded wait instead of an
unbounded wait: either wrap the wait in a timeout (e.g., use the timeout utility
to limit wait to a few seconds) or replace the single wait with a short polling
loop that checks kill -0 "$app_pid" with a sleep and a max-iteration/countdown
to give up after a bounded interval, then proceed (ensure you still attempt kill
and consume exit status as before).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/startup-crash-probe-ci.sh">

<violation number="1" location="scripts/startup-crash-probe-ci.sh:106">
P2: `wait "$app_pid"` will block indefinitely if the process ignores SIGTERM, stalling the CI job until the global workflow timeout. Add a bounded poll after `kill` and fall back to `kill -9` before calling `wait`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


if kill -0 "$app_pid" 2>/dev/null; then
kill "$app_pid" 2>/dev/null || true
wait "$app_pid" 2>/dev/null || true

@cubic-dev-ai cubic-dev-ai Bot Apr 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: wait "$app_pid" will block indefinitely if the process ignores SIGTERM, stalling the CI job until the global workflow timeout. Add a bounded poll after kill and fall back to kill -9 before calling wait.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/startup-crash-probe-ci.sh, line 106:

<comment>`wait "$app_pid"` will block indefinitely if the process ignores SIGTERM, stalling the CI job until the global workflow timeout. Add a bounded poll after `kill` and fall back to `kill -9` before calling `wait`.</comment>

<file context>
@@ -0,0 +1,113 @@
+
+    if kill -0 "$app_pid" 2>/dev/null; then
+      kill "$app_pid" 2>/dev/null || true
+      wait "$app_pid" 2>/dev/null || true
+    fi
+
</file context>
Fix with Cubic

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview — 1f80c54e Deployed Apr 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants