anomalyco / anomalyco/opencode

service: make subprocess PATH deterministic across reconnect election

Open
#38,897 1 comment 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Jul 25, 2026.

2.0 bug core
Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Summary

The V2 managed background service inherits the environment of whichever reconnecting client wins service election. When several clients reconnect after a restart, crash, or version replacement, they race to spawn detached contenders with potentially different PATH values.

The winning environment becomes the process-global environment used by shell calls. As a result, the same command can work before a service replacement and fail afterward with command not found (observed with curl).

Reproduction

A bounded harness exercised the current Service.ensure path with two simultaneous reconnecting clients:

  1. Client A had a PATH containing a fixture curl executable.
  2. Client B had a PATH without curl.
  3. Both called Service.ensure concurrently after the registration disappeared.
  4. The elected service ran /bin/sh -c curl using its inherited environment.
  5. The election was repeated 20 times.

Result:

{"found":10,"missing":10}

The command outcome depended only on which client's contender won election.

Cause

  • packages/client/src/effect/service.ts spawns each contender with inherited environment via spawn(command, args, { detached: true, stdio: "ignore" }).
  • A restart or service loss causes every attached client reconnect loop to call Service.ensure, so contenders may originate from different terminal, IDE, or launcher environments.
  • packages/core/src/shell.ts builds every shell environment from the elected server's process.env.
  • Repository search found no production path that deletes, replaces, or mutates PATH; this is environment replacement across server processes, not mutation within one server.

This is adjacent to the reconnect herd documented in #36285, but the user-visible failure here is nondeterministic process environment rather than contender resource overhead.

Expected behavior

Shell command resolution should remain deterministic across managed-service replacement and should not depend on which attached client wins a reconnect race.

Design options

  1. Canonical service environment: derive the service environment from the user's login shell on startup, independent of the spawning contender. Desktop already has a bounded login-shell environment probe that may be reusable after extraction.
  2. Persist a canonical local environment: choose and retain one non-secret environment contract across service replacements. This needs careful treatment of changing toolchains and secret values.
  3. Explicit client/session environment: pass the relevant execution environment through an owned client, session, or location boundary rather than relying on server-global process.env. This is more precise but requires conflict semantics for multiple clients.

Questions

  • Which boundary should own local command environment in the shared-service architecture?
  • Should activated environments such as mise, direnv, Nix, or virtualenv follow the client/session, or should the service always use a canonical login-shell environment?
  • What environment data is safe to persist or send over the local API?
  • Should shell, PTY, MCP, LSP, formatter, and other subprocess producers share one environment policy?

Source discussion: https://slack.com/archives/C0BE69AHCQP/p1785009702750149

Requested by: @rekram1-node (Aiden Cline via Slack)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.