openai / openai/codex-plugin-cc
`codex-rescue` can generate duplicate `pgrep -f "codex-companion.mjs"` wait loops that keep each other alive on macOS (stuck background tasks)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
Environment
- Plugin:
codex@1.0.6(marketplaceopenai/codex-plugin-cc, commitdb52e28) - Claude Code CLI 2.1.238 on macOS Darwin 25.5.0
- Rescue subagent model: sonnet
- Note: the session ran with user-level config (CLAUDE.md, custom hooks). I have not yet reproduced end-to-end in a clean profile, so this report presents an observed occurrence plus an isolated reproduction of the persistence mechanism — not a deterministic repro.
Symptom
After several /codex:rescue --background ... delegations, the session accumulates background tasks that never finish. Their process shape is always:
zsh -c while pgrep -f "codex-companion.mjs" > /dev/null 2>&1; do sleep 5; done; echo "===PROCESS_DONE==="; cat "<claude tasks dir>/<id>.output"
They stay alive after the Codex job (and the companion process) has completed, each showing as a stuck "background task running" entry in the session UI. Five instances observed over two days of normal use.
Observed occurrence (timestamped, from the subagent transcript, 2026-08-25)
| Time (UTC) | Event |
|---|---|
| 11:09:57 | subagent runs codex-companion.mjs task ... (foreground Bash) |
| 11:20:00 | harness: "Command did not complete within its 600s timeout and was moved to the background (ID: bankuk08v)" |
| 11:20:49 | subagent issues wait loop #1 (while pgrep -f "codex-companion.mjs" ...; cat .../bankuk08v.output) |
| 12:07:08 | (later round) wait loop targeting .../bcjiv3vzi.output issued |
| 12:16:59 | harness: that wait loop itself "moved to the background (ID: bajyfm7mc)" — still running at this moment |
| 12:17:01 | subagent issues a second identical wait loop for the same output file — two seconds later, i.e. within a single sleep 5 iteration of the first |
| 12:26:52 | harness: second loop also "moved to the background (ID: b5wz9tl0t)" |
Six such auto-backgrounded wait loops accumulated in this one session (IDs bajyfm7mc, b5wz9tl0t, bb662rqqw, by7r8ttif, b322f0nm6, byx7cqzqh), and the stuck-task symptom above matches sustained overlap.
Where the loop comes from
The plugin source at db52e28 contains no pgrep, so the command is model-generated. agents/codex-rescue.md requires the subagent to use exactly one Bash call, return the companion stdout as-is, and forbids polling status / fetching result. Once the companion call is auto-backgrounded at the Bash timeout, there is no compliant way left to "return the stdout as-is", and the model recovers by writing a pgrep wait loop — noncompliant with the letter of the contract, but a predictable failure mode of it: the prompt defines no fallback for the timeout/auto-background case, and the same improvisation recurred across independent runs (8+ wait loops in the transcript above).
Why the loops never exit (isolated mechanism reproduction)
The pgrep pattern codex-companion.mjs appears verbatim inside each wait loop's own zsh -c command line. Verified experimentally on the tested Darwin 25.5 setup:
- A single loop did not match its own ancestor shell and exited once no companion process remained. This matches the documented BSD pgrep default: "the current pgrep or pkill process and all of its ancestors are excluded" (macOS
man pgrep,-aflag description). - Two sibling loops matched each other and stayed alive indefinitely after every codex-related process was gone (observed over several minutes; each loop's command line satisfies the other's pgrep).
pgrep -fwas confirmed to match the fullzsh -cargv at least to offset 2000, so the loop body is always visible to a sibling.
This establishes the persistence mechanism whenever two waiters overlap — and the timeline above shows the plugin flow producing exactly that overlap. It does not claim process-name waiting is generally safe in the single-loop case.
Untested expectation (not required for this report): on procps-based Linux, pgrep excludes only itself, so even a single loop is expected to match its own parent shell and never exit.
Potential fixes
- Define an explicit timeout/background fallback in the agent contract (
agents/codex-rescue.md,skills/codex-cli-runtime/SKILL.md): if the Bash call times out or is moved to the background, return the job handle / harness message immediately; never issue another Bash call and never construct a waiter. - Provide a job-specific blocking await and let the subagent use it (e.g.
codex-companion.mjs status <job-id> --wait --timeout-ms ...). Today the "do not callstatus" rule closes off the safe path while the missing stdout creates the pressure. - Supervise completion inside the companion:
task --backgroundself-daemonizes and writes its result to a known file, so no shell-side waiting is needed.
(The bracket pattern pgrep -f "[c]odex-companion.mjs" prevents waiter self/mutual matching, but it is globally scoped — it can wait on unrelated concurrent companion jobs — so it is only a narrow mitigation, not a fix.)
Related
- #515 (item 4) reports the absence of a blocking result-await primitive and hand-written poller loops as a consequence. That gap plausibly creates the same design pressure, but a shared causal path has not been established; this report concerns the distinct generated-waiter failure observed on macOS.
- #542 concerns the same 10-minute Bash cap in the foreground dispatch path (there the chain is killed; here the harness moved the call to the background and the recovery behavior is the problem).
Happy to provide fuller transcript excerpts (with timestamps and task IDs) or run additional experiments if that helps.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with agents/codex-rescue.md and skills/codex-cli-runtime/SKILL.md, then inspect the timeout and background-task behavior around codex-companion.mjs. Compare the contract with the observed macOS pgrep overlap and choose a fallback or await design; done means a timed-out or auto-backgrounded call cannot create duplicate waiter loops that remain alive.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, shell
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100