Skip to content

Fix display harness manifest handoff in UI test - #1724

Merged
lawrencecchen merged 7 commits into
mainfrom
feat-display-harness-manifest-env
Mar 18, 2026
Merged

lawrencecchen merged 7 commits into
mainfrom
feat-display-harness-manifest-env

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the post-merge failure from #1598 where \ moved its external harness manifest out of \ but still only looked for the old fallback path when the direct env handoff was missing.\n\nThis passes the manifest path into the UI test runner explicitly and teaches the test to read it before falling back to the legacy default.


Summary by cubic

Fixes UI test failures by launching the virtual display helper inside the UI test sandbox and using stable /tmp prefixes for rendezvous files. The UI test reads a prebuilt helper path from CMUX_UI_TEST_DISPLAY_HELPER_BINARY_PATH or a manifest at /tmp/cmux-ui-test-display-harness.json (override with CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH), and CI now just compiles the helper and writes helperBinaryPath to that manifest.

  • Bug Fixes
    • Fixed test target build after the split: renamed/privatized helpers and added @MainActor where needed.
    • Moved drag configuration snapshot helpers into WindowAndDragTests behind compiler(>=6.2) to restore builds on older compilers.

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

Summary by CodeRabbit

  • Chores
    • Simplified UI display test harness to use a single, fixed helper binary path and a compact manifest (helperBinaryPath), reducing per-test artifact clutter.
    • Tests now accept a prebuilt helper or manifest-provided helper, and use consistent temp-based prefixes for per-test files.
    • Removed readiness polling and complex per-run path scaffolding, streamlining setup/teardown and improving reliability.

@vercel

vercel Bot commented Mar 18, 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 Mar 18, 2026 10:50am

@greptile-apps greptile-apps Bot left a comment

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.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces per-run HARNESS_DIR plumbing with a fixed helper binary at /tmp/create-virtual-display and a compact manifest containing helperBinaryPath. CI/workflows now place the helper at that path and pass it into xcodebuild; UITests prefer a prebuilt helper from env/manifest and fall back to legacy per-test temp paths when needed.

Changes

Cohort / File(s) Summary
Workflows
\.github/workflows/ci.yml, \.github/workflows/test-e2e.yml
Removed HARNESS_DIR and per-path artifact creation/cleanup. Use fixed HELPER_PATH="/tmp/create-virtual-display" and MANIFEST_PATH="/tmp/cmux-ui-test-display-harness.json". Manifest now contains only {"helperBinaryPath":"$HELPER_PATH"}; startup no longer polls readiness files.
UITests
cmuxUITests/DisplayResolutionRegressionUITests.swift
Renamed manifest path var, added tempPrefix-based per-test artifact fallback, added support for a prebuilt helper via env/manifest (helperBinaryPath), made ExternalDisplayHarness fields optional, and updated prepare/load logic to prefer and launch a helper binary when provided.

Sequence Diagram(s)

sequenceDiagram
    participant CI as CI Workflow
    participant FS as Filesystem (/tmp)
    participant Xcode as xcodebuild / Test Runner
    participant Test as DisplayResolutionRegressionUITests

    CI->>FS: build/link helper -> /tmp/create-virtual-display
    CI->>Xcode: invoke xcodebuild with CMUX_UI_TEST_DISPLAY_HELPER_BINARY_PATH=/tmp/create-virtual-display
    Xcode->>Test: launch tests (env includes helper path or manifest path)
    Test->>FS: check env CMUX_UI_TEST_DISPLAY_HELPER_BINARY_PATH
    alt helper present
        Test->>FS: execute/interact with helper at /tmp/create-virtual-display
    else helper absent
        Test->>FS: read manifest from CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH or default
        alt manifest provides helperBinaryPath
            Test->>FS: execute/interact with helper from manifest
        else fallback
            Test->>FS: use per-test tempPrefix artifact paths (ready/start/done/log) as before
        end
    end
    Test-->>Xcode: tests proceed with harness integrated
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Poem

🐰 I stitched a helper in /tmp tonight,
A tiny binary, tidy and bright.
CI set the path, tests gave a cheer,
The rabbit hummed — the harness is here! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

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.
Description check ⚠️ Warning The PR description is largely incomplete and does not follow the required template structure with separate Summary, Testing, and Demo Video sections. Add a clear Summary section explaining what changed and why, include a Testing section documenting how the changes were tested, and note if a demo video is needed. Complete the checklist items.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: addressing a display harness manifest handoff issue in the UI test.

✏️ 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 feat-display-harness-manifest-env
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@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.

No issues found across 3 files

@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 (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="cmuxUITests/DisplayResolutionRegressionUITests.swift">

<violation number="1" location="cmuxUITests/DisplayResolutionRegressionUITests.swift:125">
P1: The test now deletes externally provided helper binaries by storing the env path in `helperBinaryPath` and later removing it during teardown.</violation>
</file>

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

Comment thread cmuxUITests/DisplayResolutionRegressionUITests.swift

@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 (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=".github/workflows/test-e2e.yml">

<violation number="1" location=".github/workflows/test-e2e.yml:220">
P1: This heredoc terminator is indented, so Bash does not close the heredoc and the workflow step fails with a syntax error.</violation>
</file>

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

Comment on lines 220 to +222
cat >"$MANIFEST_PATH" <<EOF
{"readyPath":"$READY_PATH","displayIDPath":"$DISPLAY_ID_PATH","startPath":"$START_PATH","donePath":"$DONE_PATH","logPath":"$LOG_PATH"}
EOF
echo "Display harness manifest: $MANIFEST_PATH"
cat "$MANIFEST_PATH"

DISPLAY_ENV_PREFIX=(
CMUX_UI_TEST_DISPLAY_READY_PATH="$READY_PATH"
CMUX_UI_TEST_DISPLAY_ID_PATH="$DISPLAY_ID_PATH"
CMUX_UI_TEST_DISPLAY_START_PATH="$START_PATH"
CMUX_UI_TEST_DISPLAY_DONE_PATH="$DONE_PATH"
CMUX_UI_TEST_DISPLAY_LOG_PATH="$LOG_PATH"
)
{"helperBinaryPath":"$HELPER_PATH"}
EOF

@cubic-dev-ai cubic-dev-ai Bot Mar 18, 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.

P1: This heredoc terminator is indented, so Bash does not close the heredoc and the workflow step fails with a syntax error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/test-e2e.yml, line 220:

<comment>This heredoc terminator is indented, so Bash does not close the heredoc and the workflow step fails with a syntax error.</comment>

<file context>
@@ -209,13 +209,17 @@ jobs:
-            DISPLAY_ENV_PREFIX=(
-              CMUX_UI_TEST_DISPLAY_HELPER_BINARY_PATH="$HELPER_PATH"
-            )
+            cat >"$MANIFEST_PATH" <<EOF
+            {"helperBinaryPath":"$HELPER_PATH"}
+            EOF
</file context>
Suggested change
cat >"$MANIFEST_PATH" <<EOF
{"readyPath":"$READY_PATH","displayIDPath":"$DISPLAY_ID_PATH","startPath":"$START_PATH","donePath":"$DONE_PATH","logPath":"$LOG_PATH"}
EOF
echo "Display harness manifest: $MANIFEST_PATH"
cat "$MANIFEST_PATH"
DISPLAY_ENV_PREFIX=(
CMUX_UI_TEST_DISPLAY_READY_PATH="$READY_PATH"
CMUX_UI_TEST_DISPLAY_ID_PATH="$DISPLAY_ID_PATH"
CMUX_UI_TEST_DISPLAY_START_PATH="$START_PATH"
CMUX_UI_TEST_DISPLAY_DONE_PATH="$DONE_PATH"
CMUX_UI_TEST_DISPLAY_LOG_PATH="$LOG_PATH"
)
{"helperBinaryPath":"$HELPER_PATH"}
EOF
printf '{"helperBinaryPath":"%s"}\n' "$HELPER_PATH" >"$MANIFEST_PATH"
Fix with Cubic

@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

🧹 Nitpick comments (2)
.github/workflows/test-e2e.yml (1)

211-222: Pass the manifest path explicitly into xcodebuild to avoid default-path coupling.

The manifest is created at MANIFEST_PATH, but the test process still depends on the default path because DISPLAY_ENV_PREFIX is never populated with CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH.

Proposed patch
           if [ "$TEST_FILTER" = "DisplayResolutionRegressionUITests" ]; then
             HELPER_PATH="/tmp/create-virtual-display"
             MANIFEST_PATH="/tmp/cmux-ui-test-display-harness.json"

             rm -f "$MANIFEST_PATH"
             trap 'rm -f "$MANIFEST_PATH"' EXIT

             clang -framework Foundation -framework CoreGraphics \
               -o "$HELPER_PATH" scripts/create-virtual-display.m

             cat >"$MANIFEST_PATH" <<EOF
             {"helperBinaryPath":"$HELPER_PATH"}
             EOF
+
+            DISPLAY_ENV_PREFIX=("CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH=$MANIFEST_PATH")
           fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test-e2e.yml around lines 211 - 222, The test manifest is
written to MANIFEST_PATH but the manifest path is never passed into xcodebuild
because DISPLAY_ENV_PREFIX / CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH is not
set; update the workflow so when invoking xcodebuild you explicitly inject the
manifest path environment variable (CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH)
or set DISPLAY_ENV_PREFIX to include that mapping, referencing MANIFEST_PATH and
the xcodebuild invocation so the test process reads the correct manifest instead
of relying on a default path.
.github/workflows/ci.yml (1)

468-486: Export CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH in this step for deterministic handoff.

Right now the UI test depends on the in-test fallback path. Passing the manifest path explicitly removes that implicit dependency.

Proposed patch
           cat >"$MANIFEST_PATH" <<EOF
           {"helperBinaryPath":"$HELPER_PATH"}
           EOF

-          xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \
+          CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH="$MANIFEST_PATH" \
+          xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug \
             -clonedSourcePackagesDirPath "$SOURCE_PACKAGES_DIR" \
             -disableAutomaticPackageResolution \
             -destination "platform=macOS" \
             -only-testing:cmuxUITests/DisplayResolutionRegressionUITests \
             test
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 468 - 486, Export the manifest path so
the UI test receives it deterministically: set and export the environment
variable CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH to the MANIFEST_PATH used in
this step (where MANIFEST_PATH and HELPER_PATH are defined) before invoking
xcodebuild, ensuring the xcodebuild/test invocation (the xcodebuild command and
the only-testing cmuxUITests/DisplayResolutionRegressionUITests) picks up the
explicit manifest instead of relying on the in-test fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/DisplayResolutionRegressionUITests.swift`:
- Around line 124-133: The teardown currently always removes helperBinaryPath
even when it was provided externally; update the flow that sets helperBinaryPath
(in branches using loadExternalHarnessFromEnvironment or
loadExternalHarnessFromManifest and when helperBinaryPath is non-empty) to mark
ownership (e.g., set a Boolean flag like isHelperExternallyOwned or store an
ownership enum alongside helperBinaryPath), and change the teardown/cleanup
logic (the method that currently removes helperBinaryPath after
launchDisplayHelper) to skip deleting the file when the ownership flag indicates
an external/prebuilt helper; ensure symbols to update include helperBinaryPath,
loadExternalHarnessFromEnvironment, loadExternalHarnessFromManifest,
launchDisplayHelper, and the teardown/cleanup method.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 468-486: Export the manifest path so the UI test receives it
deterministically: set and export the environment variable
CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH to the MANIFEST_PATH used in this
step (where MANIFEST_PATH and HELPER_PATH are defined) before invoking
xcodebuild, ensuring the xcodebuild/test invocation (the xcodebuild command and
the only-testing cmuxUITests/DisplayResolutionRegressionUITests) picks up the
explicit manifest instead of relying on the in-test fallback.

In @.github/workflows/test-e2e.yml:
- Around line 211-222: The test manifest is written to MANIFEST_PATH but the
manifest path is never passed into xcodebuild because DISPLAY_ENV_PREFIX /
CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH is not set; update the workflow so
when invoking xcodebuild you explicitly inject the manifest path environment
variable (CMUX_UI_TEST_DISPLAY_HARNESS_MANIFEST_PATH) or set DISPLAY_ENV_PREFIX
to include that mapping, referencing MANIFEST_PATH and the xcodebuild invocation
so the test process reads the correct manifest instead of relying on a default
path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5766b1b6-0fe8-41cc-b94a-090590fd3520

📥 Commits

Reviewing files that changed from the base of the PR and between 7fb1f50 and fbe209c.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • .github/workflows/test-e2e.yml
  • cmuxUITests/DisplayResolutionRegressionUITests.swift

Comment on lines +124 to +133
if let helperBinaryPath = loadPrebuiltHelperBinaryPath(env) {
self.helperBinaryPath = helperBinaryPath
try launchDisplayHelper()
return
}
if let externalHarness = loadExternalHarnessFromEnvironment(env) ?? loadExternalHarnessFromManifest(env) {
if let helperBinaryPath = externalHarness.helperBinaryPath, !helperBinaryPath.isEmpty {
self.helperBinaryPath = helperBinaryPath
try launchDisplayHelper()
return

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

Do not delete externally provided helper binaries during teardown.

When helperBinaryPath comes from env/manifest, it is externally owned, but teardown still removes helperBinaryPath. This can break retries or subsequent tests that expect the same prebuilt helper path.

Proposed patch (track helper ownership before cleanup)
 final class DisplayResolutionRegressionUITests: XCTestCase {
     private let defaultDisplayHarnessManifestPath = "/tmp/cmux-ui-test-display-harness.json"
@@
     private var helperBinaryPath = ""
+    private var shouldCleanupHelperBinary = true
@@
     override func setUp() {
         super.setUp()
         continueAfterFailure = false
@@
         helperBinaryPath = "\(tempPrefix)-helper"
         helperLogPath = "\(tempPrefix)-helper.log"
+        shouldCleanupHelperBinary = true

         removeTestArtifacts()
     }
@@
     private func prepareDisplayHarnessIfNeeded() throws {
         let env = ProcessInfo.processInfo.environment
         if let helperBinaryPath = loadPrebuiltHelperBinaryPath(env) {
             self.helperBinaryPath = helperBinaryPath
+            shouldCleanupHelperBinary = false
             try launchDisplayHelper()
             return
         }
         if let externalHarness = loadExternalHarnessFromEnvironment(env) ?? loadExternalHarnessFromManifest(env) {
             if let helperBinaryPath = externalHarness.helperBinaryPath, !helperBinaryPath.isEmpty {
                 self.helperBinaryPath = helperBinaryPath
+                shouldCleanupHelperBinary = false
                 try launchDisplayHelper()
                 return
             }
@@
     private func removeTestArtifacts() {
-        for path in [
+        var paths = [
             diagnosticsPath,
             displayReadyPath,
             displayIDPath,
             displayStartPath,
             displayDonePath,
-            helperBinaryPath,
             helperLogPath,
-        ] {
+        ]
+        if shouldCleanupHelperBinary {
+            paths.append(helperBinaryPath)
+        }
+        for path in paths {
             guard !path.isEmpty else { continue }
             try? FileManager.default.removeItem(atPath: path)
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/DisplayResolutionRegressionUITests.swift` around lines 124 - 133,
The teardown currently always removes helperBinaryPath even when it was provided
externally; update the flow that sets helperBinaryPath (in branches using
loadExternalHarnessFromEnvironment or loadExternalHarnessFromManifest and when
helperBinaryPath is non-empty) to mark ownership (e.g., set a Boolean flag like
isHelperExternallyOwned or store an ownership enum alongside helperBinaryPath),
and change the teardown/cleanup logic (the method that currently removes
helperBinaryPath after launchDisplayHelper) to skip deleting the file when the
ownership flag indicates an external/prebuilt helper; ensure symbols to update
include helperBinaryPath, loadExternalHarnessFromEnvironment,
loadExternalHarnessFromManifest, launchDisplayHelper, and the teardown/cleanup
method.

@lawrencecchen
lawrencecchen merged commit 63e65a7 into main Mar 18, 2026
20 of 28 checks passed
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…ness-manifest-env

Fix display harness manifest handoff in UI test

This branch was successfully deployed

1 active deployment
Preview — e99bc402 Deployed Mar 18, 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