Skip to content

Fail app-host unit-test shards on hidden assertion failures - #9513

Closed
austinywang wants to merge 4 commits into
mainfrom
issue-7471-ci-app-host-unit-test-job-swallows-real
Closed

austinywang wants to merge 4 commits into
mainfrom
issue-7471-ci-app-host-unit-test-job-swallows-real

Conversation

@austinywang

@austinywang austinywang commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • inspect every app-host xcodebuild attempt log for XCTest failure counts and Swift Testing issue markers before retrying or accepting success
  • stop retries after a real assertion failure while retaining bounded retries for pure test-runner infrastructure failures
  • remove the (0 unexpected) pass fallback and propagate the real unit-test exit status

Testing

  • Red (994a62023a): gh workflow run ci.yml --ref issue-7471-ci-app-host-unit-test-job-swallows-real — workflow-guard-tests failed at the app-host retry regression.
  • Green (5b01f3c9cb): the same workflow dispatch — workflow-guard-tests passed, including the app-host retry regression.
  • The executable harness covers a failed attempt followed by a clean retry, a clean final XCTest summary after an earlier XCTest failure, a Swift Testing issue after a clean XCTest summary, and an ordinary assertion failure reported as (0 unexpected) by the actual workflow step.
  • Review repair (1f7639c964): hosted workflow guards passed, including exact exit-status propagation, communication-only retry, and sanitized diagnostics coverage.
  • Tagged dev build: ./scripts/reload-cloud.sh --tag sym7471 succeeded remotely on blacksmith-6vcpu-macos-26 (run 30882874657); no local build fallback was used. This is CI-only behavior with no applicable debug-socket runtime path. The app was not launched, and cleanup confirmed no sym7471 process remained.

Fixes #7471


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

Make app-host unit-test shards fail on real assertion failures by scanning every xcodebuild attempt log and stopping retries, while still retrying transient infra errors. Removes the "(0 unexpected)" pass fallback, surfaces a counted failure-marker diagnostic, and preserves the real exit code through the workflow (fixes #7471).

  • Bug Fixes
    • Detect XCTest and Swift Testing failures in each attempt log; abort immediately with a clear message and sanitized marker count.
    • Preserve and propagate the actual xcodebuild exit code in CI; drop summary-based tolerance and fail on any non-zero status.
    • Keep bounded retries only for infrastructure issues (e.g., test-runner comms, SwiftPM dependency resolution); skip retry if assertion markers are present, with tests covering assertion vs infra paths, summary-loss cases, and exit-code propagation.

Written for commit 1f7639c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved automated test failure reporting across supported test frameworks.
    • Prevented app-host test failures from being retried or incorrectly reported as successful.
    • Ensured assertion failures remain visible even when later test summaries appear clean.
    • Preserved retries for transient infrastructure issues while accurately surfacing genuine regressions.
  • Tests

    • Expanded coverage for failure detection, retry behavior, exit codes, and complete workflow results.

@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The app-host test wrapper now detects assertion failures in every xcodebuild attempt. CI no longer retries those failures as SwiftPM errors or treats nonzero app-host exits with clean summaries as successful. Regression tests cover XCTest, Swift Testing, retries, and workflow execution.

Changes

App-host failure accounting

Layer / File(s) Summary
Assertion detection and retry boundary
scripts/ci/run-app-host-xcodebuild.sh
The wrapper detects XCTest and Swift Testing assertion failures in complete attempt logs and exits with a nonzero status before retry or success handling.
CI retry and exit-code accounting
.github/workflows/ci.yml
SwiftPM retries exclude app-host assertion failures. Nonzero app-host test exits are returned directly. Regression-gate comments describe process isolation from unrelated app-host crashes.
Regression and end-to-end validation
tests/test_ci_app_host_xcodebuild_retry.sh
Tests cover assertion failures, clean-summary masking, retry counts, workflow execution, and aggregate failure reporting.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CIWorkflow
  participant AppHostWrapper
  participant Xcodebuild
  participant RegressionTests
  CIWorkflow->>AppHostWrapper: run app-host tests
  AppHostWrapper->>Xcodebuild: execute test attempt
  Xcodebuild-->>AppHostWrapper: return log and exit status
  AppHostWrapper->>AppHostWrapper: detect assertion failures
  AppHostWrapper-->>CIWorkflow: return failure without retry
  RegressionTests->>CIWorkflow: verify failure and retry behavior
Loading

Possibly related PRs

Suggested reviewers: lawrencecchen


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Cmux Swiftui State Layout ❌ Error New MobileHostPickerView stores @Bindable store and its List/ForEach rows call macRow, which reads and mutates store; this violates the snapshot/action-closure row rule. Pass immutable row snapshots and action closures into macRow. Keep the @Bindable store above the List and out of the row subtree.
✅ Passed checks (24 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #7471 by detecting failures across attempts, preventing invalid retries, removing the pass fallback, and propagating failure status.
Out of Scope Changes check ✅ Passed All modified workflow, script, and regression-test changes directly support the linked CI failure-handling objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed The available PR diff changes only a CI shell script and its test harness; it adds no production Swift files or actor-isolation declarations.
Cmux Swift Blocking Runtime ✅ Passed The PR diff changes only CI shell and test-harness files. It introduces no production Swift changes or blocking runtime primitives.
Cmux Browser Automation Off-Main ✅ Passed The PR changes only CI workflow and app-host test harness files; it does not modify browser commands, worker routing, WebKit/AppKit hops, or browser policy tests.
Cmux Expensive Synchronous Load ✅ Passed The PR changes only CI YAML, shell, and test-harness files; no Swift files or production agent-history loads are added or moved.
Cmux Cache Substitution Correctness ✅ Passed The full diff against main changes only CI workflow, shell script, and shell/Python tests; it contains no production Swift, TypeScript, or JavaScript persistence or snapshot changes.
Cmux No Hacky Sleeps ✅ Passed The PR adds no sleep, timer, polling, or fixed delay. The existing sleep 10 remains deterministic test scaffolding, and workflow YAML is out of scope.
Cmux Algorithmic Complexity ✅ Passed The PR adds bounded per-attempt linear log checks and exit handling only; it adds no nested scalable-collection scans, repeated sorting/filtering, joins, or slower record algorithm.
Cmux Swift Concurrency ✅ Passed The aggregate PR diff changes only one YAML file and two shell files; it contains no Swift paths or added Swift concurrency-pattern lines.
Cmux Swift @Concurrent ✅ Passed The full PR delta changes only CI YAML and shell test scripts; it adds or modifies no Swift files or Swift concurrency annotations.
Cmux Swift Package Boundaries ✅ Passed The PR changes only CI YAML, shell, and test-harness files; the comparison with origin/main contains no Swift changes, so Swift package boundaries are not implicated.
Cmux Swiftpm Lockfiles ✅ Passed The PR changes only CI workflow and test scripts; it changes no Package.swift, Package.resolved, Xcode project, or .gitignore files, and adds no dependency pin or package-reference change.
Cmux Swift Logging ✅ Passed The PR changes only CI YAML and shell/test harness files; it adds no production Swift logging or Swift files, and its output is CI/CLI diagnostics or test fixture output.
Cmux User-Facing Error Privacy ✅ Passed The changes add CI-only diagnostics and tests; failure output contains only a status and marker count, while tests reject raw xcodebuild lines. No prohibited user-facing data is added.
Cmux Full Internationalization ✅ Passed The PR changes only CI workflow logic, CI shell code, and regression tests; it adds no Swift UI, catalog, plist, web locale, or user-facing data changes.
Cmux Architecture Rethink ✅ Passed The PR changes only CI YAML and shell/test harnesses; it adds no Swift files or Swift lifecycle constructs, and its bounded retry/log parsing is a local CI correctness fix.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR diff changes only CI shell scripts and tests; it adds no Swift, NSWindow, NSPanel, SwiftUI Window, or WindowGroup code.
Cmux Source Artifacts ✅ Passed The PR changes only a CI workflow, a hand-written shell runner, and a regression test; logs and temp paths are runtime fixtures, not checked-in artifacts.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR changes only CI YAML and shell test harness files; it adds no Swift diff under a production Sources path, so this check is not applicable.
Cmux No Ambient Global State ✅ Passed The PR changes only .github/workflows/ci.yml and two shell test scripts; it contains no production Swift changes, so the ambient global state rule is not applicable.
Title check ✅ Passed The title clearly summarizes the main change: failing app-host unit-test shards on hidden assertion failures.
Description check ✅ Passed The description includes a clear summary, detailed testing evidence, the linked issue, and explains why the CI-only change does not need a demo video.
✨ 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 issue-7471-ci-app-host-unit-test-job-swallows-real

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.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/ci/run-app-host-xcodebuild.sh`:
- Around line 91-94: Update the assertion-failure branch around
log_contains_test_assertion_failure to remove the raw grep output from CI logs.
Instead, calculate and print only a sanitized count of matching test assertion
failures, while retaining raw diagnostics exclusively through the existing
restricted artifact or internal logging mechanism if available; preserve the
exit 1 behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 16e2ae23-ba97-43ff-b443-c8edd83794f8

📥 Commits

Reviewing files that changed from the base of the PR and between 87edd70 and 5b01f3c.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • scripts/ci/run-app-host-xcodebuild.sh
  • tests/test_ci_app_host_xcodebuild_retry.sh

Comment thread scripts/ci/run-app-host-xcodebuild.sh
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
scripts/ci/run-app-host-xcodebuild.sh (1)

86-97: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve and test the app-host exit-code contract.

The assertion path currently normalizes every marker hit to 1. The regression checks only require a nonzero result, so this status loss is not detected.

  • scripts/ci/run-app-host-xcodebuild.sh#L86-L97: preserve the captured nonzero status; use 1 only when markers prove failure with status=0.
  • tests/test_ci_app_host_xcodebuild_retry.sh#L76-L90: assert the expected status for the fixture that exits 65.
  • tests/test_ci_app_host_xcodebuild_retry.sh#L228-L234: assert the expected propagated status for the workflow fixture that exits 65.

Based on the PR objective to propagate the actual unit-test exit status, preserve and verify the exact status contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/run-app-host-xcodebuild.sh` around lines 86 - 97, Preserve the
captured nonzero exit status in the assertion-failure path of
scripts/ci/run-app-host-xcodebuild.sh lines 86-97, using status 1 only when
failure markers are found with status 0. Update
tests/test_ci_app_host-xcodebuild_retry.sh lines 76-90 and 228-234 to assert
that fixtures exiting 65 propagate status 65 through both direct and workflow
paths.
tests/test_ci_app_host_xcodebuild_retry.sh (2)

76-90: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Pin the exit-code contract in the regression checks.

The first fixture exits 65 at Line [64]. The workflow fixture exits 65 at Line [206]. Both checks only reject 0, so a regression that changes the propagated status to another nonzero value still passes.

Assert the documented propagated status at both layers. If the contract intentionally maps assertion failures to 1, assert 1 and document that mapping.

Based on the PR objective to propagate the actual unit-test exit status, test the exact contract instead of only testing failure.

Also applies to: 228-234

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_ci_app_host_xcodebuild_retry.sh` around lines 76 - 90, Update both
regression checks around the assertion-retry fixture and workflow fixture to
require the documented propagated exit status, rather than merely rejecting
zero. Assert the exact status produced by the first fixture and workflow path
(or explicitly use and document 1 if assertion failures are intentionally
mapped), while preserving the existing failure reporting and regression count
behavior.

54-73: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a communication-failure-only retry case.

This fixture emits both an XCTest failure and Failed to establish communication with the test runner at Lines [61-63]. It proves that an assertion stops a retry. It does not prove that a communication-only failure still retries.

Add a first attempt with 0 failures (0 unexpected) plus the communication marker. Add a clean second attempt. Assert two invocations and a successful result.

Based on the PR objective to retain bounded retries for infrastructure failures, add this regression case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_ci_app_host_xcodebuild_retry.sh` around lines 54 - 73, Add a
regression case in tests/test_ci_app_host_xcodebuild_retry.sh covering a
communication-only failure: make the first xcodebuild attempt output 0 failures
and 0 unexpected alongside the communication marker, then make the second
attempt clean. Assert that the command is invoked twice and ultimately succeeds,
while preserving the existing assertion-failure case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_ci_app_host_xcodebuild_retry.sh`:
- Around line 92-96: Add a negative assertion in the test block around the
sanitized marker-count check to ensure representative raw xcodebuild diagnostic
lines are absent from assertion-retry-output.log before accepting the test. Keep
the existing marker-count assertion and failure accounting, and use the
fixture’s known raw diagnostic lines as the rejection criteria.

---

Outside diff comments:
In `@scripts/ci/run-app-host-xcodebuild.sh`:
- Around line 86-97: Preserve the captured nonzero exit status in the
assertion-failure path of scripts/ci/run-app-host-xcodebuild.sh lines 86-97,
using status 1 only when failure markers are found with status 0. Update
tests/test_ci_app_host-xcodebuild_retry.sh lines 76-90 and 228-234 to assert
that fixtures exiting 65 propagate status 65 through both direct and workflow
paths.

In `@tests/test_ci_app_host_xcodebuild_retry.sh`:
- Around line 76-90: Update both regression checks around the assertion-retry
fixture and workflow fixture to require the documented propagated exit status,
rather than merely rejecting zero. Assert the exact status produced by the first
fixture and workflow path (or explicitly use and document 1 if assertion
failures are intentionally mapped), while preserving the existing failure
reporting and regression count behavior.
- Around line 54-73: Add a regression case in
tests/test_ci_app_host_xcodebuild_retry.sh covering a communication-only
failure: make the first xcodebuild attempt output 0 failures and 0 unexpected
alongside the communication marker, then make the second attempt clean. Assert
that the command is invoked twice and ultimately succeeds, while preserving the
existing assertion-failure case.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1f2146c8-f434-4e1f-9d8c-1b80ba8119f6

📥 Commits

Reviewing files that changed from the base of the PR and between 5b01f3c and 69d3226.

📒 Files selected for processing (2)
  • scripts/ci/run-app-host-xcodebuild.sh
  • tests/test_ci_app_host_xcodebuild_retry.sh

Comment thread tests/test_ci_app_host_xcodebuild_retry.sh Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

Review audit:

  • CodeRabbit review 4850818569: all three outside-diff findings are addressed in 1f7639c. Nonzero app-host status is preserved, direct and workflow fixtures pin status 65, and a communication-only failure is proven to retry once and recover.
  • CodeRabbit inline discussion #discussion_r3709610168: addressed in 1f7639c and replied in-thread. The harness separates streamed xcodebuild stdout from wrapper stderr, then rejects either representative raw failure line in the wrapper diagnostics.
  • CodeRabbit walkthrough/pre-merge summary: its MobileHostPickerView SwiftUI error is not applicable to this PR. git diff --name-only origin/main...HEAD contains only .github/workflows/ci.yml, scripts/ci/run-app-host-xcodebuild.sh, and tests/test_ci_app_host_xcodebuild_retry.sh; no Swift or MobileHostPickerView code is changed.
  • The two Cursor Bugbot spend-limit notices are service/quota notices with no code finding to address.
  • The remaining CodeRabbit walkthrough, related-PR links, and finishing-touch suggestions are informational and require no code change.

Focused hosted verification passed on HEAD 1f7639c: https://github.com/manaflow-ai/cmux/actions/runs/30883702668/job/91910231046

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.

CI: app-host unit test job swallows real assertion failures (crash-restart summary loss + "(0 unexpected)" fallback)

3 participants