Problem
Two concurrent /codex:* commands share one broker (per-workspace, via broker.json). If broker is busy with one long-running task (adversarial-review 5+ min), the second task (rescue) hangs forever in phase 'starting' — stuck in ensureBrokerSession waiting for the busy broker.
Turn-level timeout (PR #26/#28) doesn't help: the hang is BEFORE captureTurn, in CodexAppServerClient.connect → ensureBrokerSession.
Root cause
ensureBrokerSession checks 'is the endpoint alive?' but NOT 'is the broker free?'. A busy broker returns broker/busy RPC error, but the code has no timeout on waiting for it to free up.
Upstream
openai#509 — exact issue, no closing PR. +1 posted.
openai#343 (@60ke) — closest candidate PR (stale broker cleanup + lock).
Plan
TDD: write a test that reproduces broker-busy hang (fake-codex fixture with busy broker), then fix — health-check broker on connect, timeout or spawn-new if busy.
Refs openai#509, openai#343.
Problem
Two concurrent /codex:* commands share one broker (per-workspace, via broker.json). If broker is busy with one long-running task (adversarial-review 5+ min), the second task (rescue) hangs forever in phase 'starting' — stuck in ensureBrokerSession waiting for the busy broker.
Turn-level timeout (PR #26/#28) doesn't help: the hang is BEFORE captureTurn, in CodexAppServerClient.connect → ensureBrokerSession.
Root cause
ensureBrokerSession checks 'is the endpoint alive?' but NOT 'is the broker free?'. A busy broker returns broker/busy RPC error, but the code has no timeout on waiting for it to free up.
Upstream
openai#509 — exact issue, no closing PR. +1 posted.
openai#343 (@60ke) — closest candidate PR (stale broker cleanup + lock).
Plan
TDD: write a test that reproduces broker-busy hang (fake-codex fixture with busy broker), then fix — health-check broker on connect, timeout or spawn-new if busy.
Refs openai#509, openai#343.