Summary
Driving a detached codex session headlessly (run --detach → session send → wait-idle) does not deliver the injected prompt to a fresh turn. The agent instead replays a stale prior conversation, does no work, and wait-idle times out. The full session/score surface works for the lifecycle; the codex adapter just doesn't consume the fresh session send correctly. (claude/opencode are "full" fidelity; codex is "lifecycle-only" per docs/agent-io-contract.md — this is the gap biting.)
Repro (Docker backend)
mkdir /tmp/pb-codex-smoke && cd /tmp/pb-codex-smoke
pillbox new --agent codex --name pb-codex-smoke
SID=$(pillbox run --agent codex --detach --json --workspace /tmp/pb-codex-smoke --label smoke --ttl 1h | jq -r .session.id)
pillbox session send "$SID" $'Create a file named hello.txt whose contents are exactly the text PILLBOX_OK (no trailing newline). Then stop.\n'
pillbox session wait-idle "$SID" --timeout 280
cat /tmp/pb-codex-smoke/hello.txt # expected: PILLBOX_OK
Expected
Codex receives the injected prompt, creates hello.txt = PILLBOX_OK, goes idle; wait-idle exits 0; session score --cmd 'test "$(cat hello.txt)" = PILLBOX_OK' → passed: true.
Actual
wait-idle timed out (280s, never idle); no hello.txt; score → passed:false.
- The durable §0 log shows the injected prompt was logged as input, but the agent processed a different, stale user message and answered that:
Its <environment_context> reported cwd=/workspace/cereal — a mount name from an unrelated prior session, not this run's pb-codex-smoke workspace. So the codex turn is running against a resumed/stale rollout, not the fresh detached session.
Hypothesis
The codex agent harness, on a detached run, resumes a previous codex rollout (or a codex exec/proto session) instead of starting fresh — so the session send text either lands in a session that's mid-replay or is dropped by a PTY-readiness race at startup, and an older buffered/persisted conversation ("what is websocket", /workspace/cereal) executes instead. Worth checking how src/agents/harness launches codex for a detached session (fresh vs --resume/persisted session id) and whether the first session send races the codex TUI/proto becoming ready to accept input.
Impact
Blocks headless/orchestrated codex runs (the send → wait-idle → score drive loop): any orchestrator (incl. a parallel multi-workspace "ship an issue with codex" flow) gets stale/empty results. claude is unaffected and is the working drive path today.
Environment
- pillbox: local debug build (
target/debug/pillbox, built 2026-06-13), version/runner ghcr.io/vu1n/pillbox-runner:latest
- codex:
codex-cli 0.139.0, ChatGPT auth (verified via pillbox auth list)
- backend: Docker 29.4.0;
pillbox doctor all green
Summary
Driving a detached codex session headlessly (
run --detach→session send→wait-idle) does not deliver the injected prompt to a fresh turn. The agent instead replays a stale prior conversation, does no work, andwait-idletimes out. The full session/score surface works for the lifecycle; the codex adapter just doesn't consume the freshsession sendcorrectly. (claude/opencodeare "full" fidelity; codex is "lifecycle-only" perdocs/agent-io-contract.md— this is the gap biting.)Repro (Docker backend)
Expected
Codex receives the injected prompt, creates
hello.txt=PILLBOX_OK, goes idle;wait-idleexits 0;session score --cmd 'test "$(cat hello.txt)" = PILLBOX_OK'→passed: true.Actual
wait-idletimed out (280s, never idle); nohello.txt;score→passed:false.Its
<environment_context>reportedcwd=/workspace/cereal— a mount name from an unrelated prior session, not this run'spb-codex-smokeworkspace. So the codex turn is running against a resumed/stale rollout, not the fresh detached session.Hypothesis
The codex agent harness, on a detached run, resumes a previous codex rollout (or a
codex exec/protosession) instead of starting fresh — so thesession sendtext either lands in a session that's mid-replay or is dropped by a PTY-readiness race at startup, and an older buffered/persisted conversation ("what is websocket",/workspace/cereal) executes instead. Worth checking howsrc/agents/harnesslaunches codex for a detached session (fresh vs--resume/persisted session id) and whether the firstsession sendraces the codex TUI/proto becoming ready to accept input.Impact
Blocks headless/orchestrated codex runs (the
send → wait-idle → scoredrive loop): any orchestrator (incl. a parallel multi-workspace "ship an issue with codex" flow) gets stale/empty results.claudeis unaffected and is the working drive path today.Environment
target/debug/pillbox, built 2026-06-13),version/runnerghcr.io/vu1n/pillbox-runner:latestcodex-cli 0.139.0, ChatGPT auth (verified viapillbox auth list)pillbox doctorall green