Skip to content

fix(desktop/perm-06): re-pass automation port on non-prod Quit & Reopen relaunch#9391

Merged
eulicesl merged 1 commit into
BasedHardware:mainfrom
eulicesl:fix/desktop-perm06-relaunch-port
Jul 10, 2026
Merged

fix(desktop/perm-06): re-pass automation port on non-prod Quit & Reopen relaunch#9391
eulicesl merged 1 commit into
BasedHardware:mainfrom
eulicesl:fix/desktop-perm06-relaunch-port

Conversation

@eulicesl

@eulicesl eulicesl commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What

The non-prod automation bridge exposes a quit_and_reopen action (PERM-06) that
drives the real permission-flow restart, AppState.restartApp(). That relaunch is a
bare /bin/sh -c "sleep 0.5 && open \"<bundle>\""no argv, no env. The reopened
app therefore resolves its automation port from whatever OMI_AUTOMATION_PORT the
launchd user session happens to carry (or the default 47777), not the port the
harness launched with (e.g. 47894 via --automation-port=). A harness polling the
original port sees no listener after Quit & Reopen, wait-ready times out, and
same-bundle signed-in / onboarded continuity can't be proven.

Fix

AppState.restartApp() now builds its relaunch command via a pure
relaunchCommand(appPath:isNonProduction:automationPort:). On non-prod builds it
re-passes the current automation port as an argv:

open "<bundle>" --args --automation-port=<port>

argv is the highest-precedence port source in the bridge's resolver, so the reopened
bundle rebinds the same port the harness used — overriding any launchd-inherited
OMI_AUTOMATION_PORT. The production relaunch is untouched: relaunchCommand
returns the byte-identical sleep 0.5 && open "<path>" for isNonProduction == false.

Why this shape (an earlier persist-based attempt was wrong)

I first tried persisting the last-bound port in UserDefaults and falling back to it in
the bridge's port resolver. Runtime testing disproved it: the launchd session here
carried OMI_AUTOMATION_PORT=47812 (set out-of-band — no script does launchctl setenv), and env outranks a persisted value, so the bare-open relaunch inherited
47812 and shadowed the persisted 47894. Only re-passing the port as argv — which
outranks the inherited env — reliably pins the reopened port. That approach was fully
reverted; this is the task's Option A.

Tests

  • RestartRelaunchCommandTests (new, 4): non-prod re-passes --automation-port=<port>;
    prod is byte-identical bare open (no automation args); the sleep precedes the
    open; the flag equals the bridge's own portPrefix (DRY, no drift). Pure helper —
    fails if the fix is reverted. (restartApp() itself can't be unit-run — it
    terminates the host — so the pure-command builder is the testability seam.)
  • Full Desktop suite: 1749/1750 pass; the sole failure is
    ActionItemsFTSRepairTests/testRepairToleratesMissingActionItemsFTSShadowTable,
    already in scripts/swift-test-skips.json (macOS-26 system SQLite rejects
    DELETE FROM sqlite_master) and skipped in CI — unrelated.

Verification (exercised, not just compiled)

Named bundle omi-perm06v, with the launchd session deliberately left carrying a
different port (OMI_AUTOMATION_PORT=47812) to prove the override:

  1. Launched via run.sh --yolo OMI_AUTOMATION_PORT=47894open --args --automation-port=47894; bridge on 47894, gate-0 /state signedIn+onboarded.
  2. quit_and_reopen{restarting:true, relaunch_path:/Applications/omi-perm06v.app}.
  3. Listener on 47894 changes pid (18506 → 24479); the reopened process's own argv is …/Omi Computer --automation-port=47894 — the re-pass, visible in ps.
  4. Env-override control: port 47812 (launchd env) has zero omi listeners afterward — the old bare-open code would have bound 47812.
  5. After /state (47894): same bundle, signedIn=true, onboarded=true, bridgePort=47894 — session intact on the same port.

Invariants

Touches desktop/macos/Desktop/Sources/**INV-UI-1 (no purple): adds zero purple
literals; brand ratchet unaffected.

Scope

Non-prod automation-bridge / dev-harness only; no user-facing behavior → no-changelog-needed.
Does not claim the PERM-06 acceptance row PASS — post-merge runtime re-verification is the Codex lane.

Review in cubic

…en relaunch

AppState.restartApp() relaunches via a bare `open <bundle>` (no argv, no env),
so after the permission-flow Quit & Reopen the reopened app resolved its
automation port from the launchd-inherited OMI_AUTOMATION_PORT (or default
47777) instead of the port the harness launched with. The harness, polling the
pre-quit port, timed out and could not prove signed-in/onboarded continuity
(PERM-06 FAIL, Codex Wave 18).

Re-pass --automation-port=<current port> as an argv on the NON-PROD relaunch;
argv is the highest-precedence port source, so it beats any inherited env. The
production relaunch stays a byte-identical bare `open`. Extracted a pure
relaunchCommand(appPath:isNonProduction:automationPort:) seam so the behavior is
unit-testable (restartApp() itself terminates the host and can't be unit-run).

An earlier persist-last-bound-port attempt was reverted: the launchd session
carried OMI_AUTOMATION_PORT=47812 and env outranks a persisted value, so the
bare-open relaunch shadowed the persisted port — disproven at runtime.

Tests: RestartRelaunchCommandTests (non-prod re-passes the port; prod bare open;
delay precedes open; flag == bridge portPrefix). Full Desktop suite green (the
sole failure is the already-CI-skipped ActionItemsFTSRepairTests, a macOS-26
system-SQLite issue).

Runtime: with launchd OMI_AUTOMATION_PORT=47812 set, launched on argv 47894 →
quit_and_reopen → reopened process argv shows --automation-port=47894, binds
47894, 47812 gets no listener, session intact.

INV-UI-1: adds no purple.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@eulicesl eulicesl added the no-changelog-needed Skip desktop changelog enforcement for internal-only changes label Jul 10, 2026
@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.

Copilot AI 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.

Pull request overview

This PR fixes a non-production desktop automation harness reliability issue in the “Quit & Reopen” permission flow by ensuring the app relaunch preserves the originally bound automation port. It introduces a testable relaunch-command builder and adds unit tests to prevent regressions, while documenting the correct e2e verification loop.

Changes:

  • Update AppState.restartApp() to build its relaunch shell command via a pure helper that (non-prod only) re-passes --automation-port=<port> as argv.
  • Add RestartRelaunchCommandTests to assert non-prod relaunch includes the port argv while prod remains byte-identical to the prior bare open form.
  • Update the e2e SKILL documentation to reflect same-port relaunch behavior and common pitfalls when polling after Quit & Reopen.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
desktop/macos/e2e/SKILL.md Updates PERM-06 recipe to keep polling the original automation port and avoid false pass/fail pitfalls after relaunch.
desktop/macos/Desktop/Tests/RestartRelaunchCommandTests.swift Adds focused unit tests for the relaunch command to lock in the non-prod argv re-pass and prod byte-identical behavior.
desktop/macos/Desktop/Sources/AppState/AppState+SystemActions.swift Implements relaunchCommand(...) helper and uses it in restartApp() to re-pass automation port on non-prod relaunch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@eulicesl eulicesl merged commit f3e73f8 into BasedHardware:main Jul 10, 2026
19 checks passed

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Desktop bug fix: relaunch re-passes automation port on non-prod builds; pure relaunchCommand + test, prod path byte-identical. Owner-override.

@eulicesl eulicesl deleted the fix/desktop-perm06-relaunch-port branch July 10, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog-needed Skip desktop changelog enforcement for internal-only changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants