openai / openai/codex-plugin-cc

Stop-review gate: hung jobs pile up into livelock; review --wait can exit 0 without a verdict

Open
#611 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
33.3k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Observed on plugin version 1.0.6 (macOS, Claude Code, Codex CLI via npm) during a long multi-agent session with the stop-review gate enabled. Three related defects in the review machinery, plus a skill-surface papercut. The review content was excellent throughout — one stop-gate run returned a correct, specific BLOCK on a real cross-tenant bug — so this is purely about the plumbing around the reviewer.

1. review --wait can exit 0 with no verdict (fails open)

Intermittently, codex-companion.mjs review --wait prints only the startup progress lines and exits 0:

[codex] Starting Codex review thread.
[codex] Thread ready (…).
[codex] Reviewer started: current changes
EXIT=0

No findings section, no # Codex Review block — while the underlying job stayed running for 11+ minutes with result: null until manually cancelled. Re-running the identical command worked (6m06s, full verdict). An agent taking the short output at face value reports a clean review that never happened.

Two contributing causes visible in the source:

  • handleReviewCommand (scripts/codex-companion.mjs, ~line 713) parses --wait/--background but never uses either — review always takes the same code path.
  • In scripts/lib/codex.mjs, review/start runs against a source thread while the actual review executes on a spawned reviewThreadId. The turn/completed handler (~line 541) treats completions for non-state.threadId threads as subagent turns and calls scheduleInferredCompletion; if the source thread's own turn completes immediately, completeTurn fires with finalTurn.status === "completed"buildResultStatus returns 0 with reviewText never captured. Whether a verdict is captured is a race between the two threads' notifications, which matches the observed intermittency.

Suggested fix: exit non-zero (or a distinguishable status) whenever a review run produces no reviewText, regardless of turn status.

2. Jobs hang indefinitely; no idle timeout anywhere

Four stop-gate/review jobs hung with their progress logs frozen at ~2 tool calls in (typically a git show + one rg), and never advanced. There is no idle timeout, heartbeat, or watchdog in scripts/lib/app-server.mjs or the broker — a stalled turn sits running forever. The Stop hook's own 15-minute spawnSync timeout (stop-review-gate-hook.mjs:16) kills only the CLI child; the broker-side turn and job record stay running.

Also, when a job produces no result, parseStopReviewOutput / the JSON-parse catch (stop-review-gate-hook.mjs:69-139) reports it as "returned invalid JSON" — misleading: the job returned nothing. "No result / job hung" and "unparseable result" need different messages, because they need different operator responses (inspect the stuck job vs. re-run the review).

Suggested fix: an idle/no-progress timeout on turns that fails closed with a distinguishable status, and a hook message that separates the two failure modes.

3. Every blocked stop spawns a fresh gate job; they pile up into livelock

stop-review-gate-hook.mjs (~line 148) finds the already-running job for the session — and uses it only to compose a courtesy note, then unconditionally spawns a new task (~line 166). A session blocked N times accumulates N concurrent reviews of the same diff. Combined with #2: hung job → block → new job → also hangs → the session cannot end without manual /codex:cancel of each job. One session ended with five stop-gate jobs plus two manual reviews for ~three turns of actual work.

Suggested fix: reuse or supersede an in-flight gate job for the same session/diff instead of spawning per stop attempt.

4. Papercut: focus text is rejected by review, accepted by a variant the listing hides

/codex:review <focus text> fails at runtime (exit 1 after launching) because the native reviewer rejects focus text; the variant that accepts it (adversarial-review) is not surfaced in the skill listing, so agents discover this only by failing. The review skill also mandates an interactive wait-vs-background question, which autonomous (non-interactive) runs cannot answer.

Suggested fix: surface the focus-accepting variant in the listing, and make the wait/background choice defaultable for non-interactive use.


Happy to provide the full job records / progress logs for the hung runs if useful.

Contributor guide

No contributing guide indexed for this repository

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.

Research direction

Start with handleReviewCommand in scripts/codex-companion.mjs, the turn/completed handling in scripts/lib/codex.mjs, and timeout/job behavior in scripts/lib/app-server.mjs and stop-review-gate-hook.mjs. Trace review --wait, stalled turns, and repeated stop-hook invocations; done means missing verdicts fail closed, hung jobs have a distinct outcome, in-flight gate jobs are reused or superseded, and the supported focus variant is surfaced.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
cli, devtools, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.