Skip to content

fix(agent): stop phantom desktop app boots from agent subprocesses#3239

Open
gantoine wants to merge 6 commits into
mainfrom
posthog-code/fix-phantom-node-shim-boots
Open

fix(agent): stop phantom desktop app boots from agent subprocesses#3239
gantoine wants to merge 6 commits into
mainfrom
posthog-code/fix-phantom-node-shim-boots

Conversation

@gantoine

@gantoine gantoine commented Jul 7, 2026

Copy link
Copy Markdown
Member

Problem

Users sometimes see dozens of empty dark windows cascade onto the screen with dozens of hung "node" processes in Activity Monitor (hit again today — screenshots in the internal thread). Root cause was established in #3108: the agent host puts a node -> app binary shim on PATH that only behaves as node while ELECTRON_RUN_AS_NODE=1 survives the caller's environment. Claude's Bash tool replays commands under a login-shell snapshot of the user's dotfiles, so direnv/mise/flox or a stray unset strips the var, and every node invocation then boots a full desktop app instance: while the real app runs, phantoms lose the single-instance lock and exit invisibly; when the lock is free (quit, crash, update restart) each spawn opens an empty window and never exits.

#3108 and its follow-up #3113 were closed unmerged, so main is still exposed (#3114 remains open). This PR re-lands the #3108 scope, rebased onto current main.

Changes

Cherry-picked from feat/vendored-node-runtime (original work by @charlesvien):

  • The node shim is now a self-contained wrapper script that exports ELECTRON_RUN_AS_NODE=1 itself before exec'ing the app binary (POSIX; Windows keeps the symlink). Also heals stale shims pointing at a moved binary, which the old EEXIST-skip never did. (node-shim.ts)
  • Defense in depth: the workspace-server child env carries POSTHOG_CODE_INTERNAL_CHILD=1, and bootstrap.ts refuses to boot the packaged app when the marker is present without ELECTRON_RUN_AS_NODE, failing fast on stderr instead of racing the single-instance lock.
  • Integrated terminals stop inheriting ELECTRON_RUN_AS_NODE, so Electron-based CLIs launched from app terminals don't silently run as node. (shell.ts)

Adapted to current main (the old adapters/codex was replaced by codex-app-server in #2723):

  • The codex local-tools MCP helper runs command: process.execPath — the app binary in packaged installs — while codex's own env has the var deleted. Its server config now carries ELECTRON_RUN_AS_NODE=1 explicitly, porting the old-adapter fix. Inert on real node (cloud runs).

Not included: the #3113 vendored Node runtime (tracked in #3114).

How did you test this?

  • Targeted suites all green: node-shim.test.ts (7), agent.test.ts (25), shell.test.ts (5) in workspace-server; local-tools-mcp.test.ts (4, incl. new env assertion), spawn.test.ts (7), codex-app-server-agent.test.ts (57) in agent.
  • Full pnpm typecheck and biome on all changed files pass.
  • Unrelated local failures (db repository tests, claude settings, posthog-analytics) are byte-identical files to main and fail due to the local better-sqlite3 Electron/Node ABI flip-flop — fittingly, the dev machine's agent shell itself runs vitest under the app binary via the shim this PR hardens. CI runs them clean.

Closes #3108-scope; refs #3114.


Created with PostHog Code

charlesvien and others added 4 commits July 7, 2026 12:30
Generated-By: PostHog Code
Task-Id: c7c785f3-12d4-4e51-ba0f-8c39056461da
Generated-By: PostHog Code
Task-Id: c7c785f3-12d4-4e51-ba0f-8c39056461da
Generated-By: PostHog Code
Task-Id: c7c785f3-12d4-4e51-ba0f-8c39056461da
Codex spawns the local-tools MCP helper as process.execPath, which in
packaged desktop installs is the app binary, while codex's own env has
ELECTRON_RUN_AS_NODE stripped (spawn.ts). Without the var the helper
boots the full desktop app instead of running the script. Ports the
old-adapter fix from #3108 to the app-server adapter.

Generated-By: PostHog Code
Task-Id: c7c785f3-12d4-4e51-ba0f-8c39056461da
@trunk-io

trunk-io Bot commented Jul 7, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 9f098ff.

@gantoine gantoine marked this pull request as draft July 7, 2026 16:34
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(agent): carry ELECTRON_RUN_AS_NODE o..." | Re-trigger Greptile

Comment thread packages/workspace-server/src/services/shell/shell.ts Outdated
Comment thread packages/workspace-server/src/services/agent/node-shim.test.ts
@gantoine gantoine marked this pull request as ready for review July 7, 2026 18:26
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "fix(agent): carry ELECTRON_RUN_AS_NODE o..." | Re-trigger Greptile

@gantoine gantoine requested review from a team and removed request for charlesvien and jonathanlab July 7, 2026 18:32
@charlesvien

Copy link
Copy Markdown
Member

note to all: this needs careful review

gantoine and others added 2 commits July 7, 2026 17:13
The workspace-server inherits both ELECTRON_RUN_AS_NODE=1 and
POSTHOG_CODE_INTERNAL_CHILD=1 from apps/code (service.ts). We already
dropped the former from user terminals; the latter leaked through, so a
direct invocation of the packaged app binary from an integrated terminal
would hit the bootstrap guard (app.isPackaged && POSTHOG_CODE_INTERNAL_CHILD)
and exit(1) with a confusing error, even when the user meant to launch a
second instance. Drop both markers.

Generated-By: PostHog Code
Task-Id: c7c785f3-12d4-4e51-ba0f-8c39056461da
@gantoine gantoine added the build-installer Build a macOS installer for this PR label Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🖥️ macOS test build (Apple Silicon)

A signed macOS build for this PR is ready to download and run.

⬇️ Open this run and download the code-macos-arm64 artifact (Apple Silicon / M-series).

Unzip it, then open the .dmg (or the .app inside the -mac.zip).

ℹ️ The build is signed but not notarized, so macOS may warn on first launch.
Right-click the app → Open, or run xattr -dr com.apple.quarantine "PostHog Code.app".

Built from 787b983 · rebuilds on each push while the build-installer label is present. Downloading requires being signed in to GitHub with repo access.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build-installer Build a macOS installer for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants