Skip to content

fix(packaged): isolate headless MCP runtime namespace - #7494

Open
VaiYav wants to merge 3 commits into
nexu-io:mainfrom
VaiYav:fix/packaged-runtime-namespace-isolation-pr
Open

fix(packaged): isolate headless MCP runtime namespace#7494
VaiYav wants to merge 3 commits into
nexu-io:mainfrom
VaiYav:fix/packaged-runtime-namespace-isolation-pr

Conversation

@VaiYav

@VaiYav VaiYav commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #7154

Why

Agent/MCP bootstrap can launch packaged Open Design headlessly while the desktop runtime is already active. Both modes currently resolve the same release-stable IPC namespace, so the second daemon fails with EADDRINUSE and the desktop enters a restart loop.

This was reproduced locally on macOS arm64 with Open Design 0.20.2 outer bundle and 0.21.0 payload. Open MCP sessions must not require closing the desktop or each other.

What users will see

Open Design desktop and MCP/headless sessions can run at the same time. Headless bootstrap uses a separate runtime namespace/socket while continuing to use the desktop namespace data root, so existing projects remain visible.

Surface area

  • UI
  • CLI / env var
  • API / contract
  • Extension point
  • i18n keys
  • New top-level dependency
  • Default behavior change
  • None

Screenshots

None — lifecycle/runtime fix.

Bug fix verification

  • Test path: apps/packaged/tests/headless-runtime.test.ts, paths.test.ts, and sidecars.test.ts.
  • Targeted regression suite is green: 86 tests passed on the updated branch.
  • The regression asserts distinct headless runtime namespace and IPC paths, shared data root, and propagation through MCP bootstrap environment.

Validation

  • pnpm --filter @open-design/packaged exec vitest run tests/headless-runtime.test.ts tests/paths.test.ts tests/sidecars.test.ts — 86 passed
  • pnpm --filter @open-design/packaged typecheck — passed
  • pnpm --filter @open-design/packaged build — passed
  • Full packaged suite: 289 tests passed; 2 Electron-specific suites were blocked in this temporary worktree because Electron postinstall binary setup was unavailable.
  • git diff --check — passed

Implementation notes

OD_PACKAGED_NAMESPACE remains backward compatible. OD_PACKAGED_RUNTIME_NAMESPACE is an optional override; headless defaults to <data-namespace>-headless. The existing sidecar stamp contract is unchanged. Desktop keeps its existing namespace and behavior.

Prevent MCP bootstrap from starting a second packaged daemon on the desktop IPC socket while preserving the shared project data root.
@VaiYav
VaiYav requested a review from a team as a code owner August 27, 2026 08:19
@lefarcen

Copy link
Copy Markdown
Contributor

Thanks @VaiYav — isolating the packaged runtime namespace between desktop and headless MCP while keeping the shared data root is a sensible angle for #7154. I’ll queue this for pool review and keep the linked issue state in sync here.

@lefarcen
lefarcen requested a review from mrcfps August 27, 2026 08:26
@lefarcen lefarcen added size/S PR changes 20-100 lines risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps type/bugfix Bug fix needs-validation Runtime change detected; needs human or /explore agent validation. labels Aug 27, 2026
@lefarcen

Copy link
Copy Markdown
Contributor

Heads-up: PR #7388 is also open against #7154. Both PRs touch apps/packaged/src/index.ts and aim to stop the packaged/headless path from colliding with the desktop runtime, so it’s worth comparing approaches while we decide which path lands.

@lefarcen

Copy link
Copy Markdown
Contributor

🧪 Queued for QA validation — this PR has changes that need a manual QA pass before it's merged. Nothing needed from you; we'll update here once it's validated. Thanks for the contribution! 🙏

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

@VaiYav Thanks for the focused namespace-isolation patch and the clear regression coverage around runtime/data path separation. I traced the full desktop → daemon → installed MCP bootstrap lifecycle and found one main-path discovery mismatch that prevents the newly isolated daemon from being observed after it launches. The concrete fix is described inline; once the bootstrap target follows the new socket, this approach should be much safer.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

const activeConfig = launcherRuntime.config;
const paths = launcherRuntime.paths;
const stamp = createHeadlessStamp(config.namespace);
const stamp = createHeadlessStamp(runtimeNamespace);

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.

Blocking — switch post-bootstrap discovery to the isolated daemon socket.

This changed stamp moves the headless daemon from the data namespace socket (for example, release-stable/daemon) to the new runtime namespace socket (release-stable-headless/daemon). However, the installed MCP environment is still built in apps/daemon/src/mcp-routes.ts::computeInstallPayload from the current desktop daemon's SIDECAR_ENV.IPC_PATH, and apps/daemon/src/mcp-bootstrap.ts::ensureMcpDaemonUrl uses that same unchanged environment both before spawning and throughout its post-spawn polling loop. Therefore, when the desktop socket is unavailable, the packaged headless process can start successfully here while the invoking MCP process keeps polling the old desktop socket until the 60-second timeout. That breaks the primary headless-bootstrap flow this PR is intended to fix.

Please carry a distinct normalized bootstrap daemon IPC target through the install payload and switch discovery to it after spawning (while retaining the desktop socket for the initial fast-path probe), or otherwise update the polling environment to the socket derived from runtimeNamespace. Add a lifecycle fixture covering unavailable desktop IPC → isolated headless spawn → discovery of the headless daemon, including the explicit namespace override.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

@lefarcen

Copy link
Copy Markdown
Contributor

@VaiYav The current blocker is the one @mrcfps called out: after the headless daemon moves onto the isolated runtime namespace, the MCP bootstrap still keeps polling the old desktop socket after spawn. Once that post-spawn discovery follows the isolated namespace and the lifecycle coverage is in place, this path should be in much better shape.

Also, the PR template parser is still reading Surface area as missing, so please re-check those boxes in the body when you update the PR.

💡 To drive this PR to merge hands-free, paste this to your AI coding agent (Claude Code / Codex / opencode / Cursor …):
Take over nexu-io/open-design#7494 until it merges — read https://raw.githubusercontent.com/nexu-io/looper/main/skills/pr-takeover/SKILL.md and follow it.

Carry the packaged headless IPC target through MCP install metadata so post-bootstrap discovery follows the new runtime namespace.
@lefarcen lefarcen added size/M PR changes 100-300 lines and removed size/S PR changes 20-100 lines labels Aug 27, 2026
@lefarcen
lefarcen requested a review from mrcfps August 27, 2026 09:06
Pass the isolated runtime namespace to spawned headless clients so the bootstrap process binds the socket that post-spawn discovery polls.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-validation Runtime change detected; needs human or /explore agent validation. risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps size/M PR changes 100-300 lines type/bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Agent tool calls to packaged CLI (\$env:OD_BIN\ tools live-artifacts / export) trigger desktop single-instance 'headless-owner' restart

3 participants