[Bug]: Codex 0.157 shared app-server daemon runs hooks with the first worker's env — concurrent workers are misattributed, and releasing one kills the others
Operating system
macOS (coordinator). Workers run on an SSH host: a Linux arm64 Docker Sandbox, reached through the Orca relay.
Orca version
1.4.211
Details
Summary
Codex 0.157.0 turned on daemon_auto_start by default (openai/codex#47179). The TUI now attaches to a shared codex app-server --managed-daemon, and lifecycle hooks run inside that daemon. The daemon is spawned as a child of whichever codex TUI started first, so it carries that pane's environment (ORCA_PANE_KEY, ORCA_AGENT_LAUNCH_TOKEN, hook endpoint). With two or more concurrent supervised codex workers on the same host:
- The second worker's hook events are sent under the first worker's identity. Orca then attributes them to the wrong Dispatch.
- Releasing the first worker closes its terminal, the daemon exits with it, and the second worker's session dies mid-task. It never sends
worker_done.
The readiness timeout in #22825 has to be worked around first ([tui] fullscreen_transcript = false); otherwise no codex 0.157 worker gets far enough to show this.
Environment
- Orca 1.4.211,
orca orchestration worker-start --agent codexonto an SSH host - codex-cli 0.157.0 with default
daemon_auto_start(true), plus[tui] fullscreen_transcript = false
Reproduction
- Start worker A with a short read-only task. Start worker B, in the same Run, while A is still live, with a task that runs
sleep 90first. - Record the ancestors of the hook processes on the worker host. We added a temporary line to the codex hook script that logs the parent chain's command lines, and nothing else.
- Compare
orca orchestration worker-read --dispatch <A|B> --jsonfor the two workers. - After A sends
worker_done, runworker-releasefor A, then read B's screen withorca terminal read --screen.
Observed
- Hooks for both A and B run under the same process tree:
bash (A's Orca pane) → node codex → codex TUI (A) → codex app-server --managed-daemon. - B:
provider: null,fallbackReason: session_not_reported, livenessunverifiable/missing_status, even though B is working. - A, already settled:
stage.activity: working, and its livenessobservedAtequals B'sPreToolUsetime. B's events land on A. - After releasing A, the daemon process is gone. B shows:B never sends
■ Connection lost. Attempting to reconnect… ■ Automatic reconnect could not restore this session. … ■ app-server session could not be restored • Reconnect failed — check the endpoint, then relaunchworker_done. We had toworker-stopit. - Strictly sequential use hides the problem: releasing a worker kills its daemon, and the next worker starts a fresh one under its own pane.
Expected
Each supervised worker's hook events are attributed to its own Dispatch, and releasing one worker does not affect other live workers.
Workaround (verified)
On the worker host, in ~/.codex/config.toml:
[features]
daemon_auto_start = falseWith this set and no daemon running, two concurrent workers each ran hooks under their own TUI process. Both showed provider: codex and liveness live. Releasing B did not affect A, which completed and sent worker_done.
Caveat, from reading codex source and not verified: a TUI may still attach to an already-running daemon socket even with the feature off. So a daemon started by hand (codex app-server daemon start, codex agents) would bring the problem back.
Possible fixes
- Launch supervised codex workers with
--no-daemon. Codex's own help says this runs "without the background server". - Or stop relying on the hook process's inherited environment for pane attribution when codex runs through the shared daemon.
Source: stablyai/orca