How Does the Codex Review Gate Work Internally
The Codex review gate is an optional safety mechanism that forces a Codex review before a Claude session can be stopped, blocking the stop if the review detects issues.
The review gate in the openai/codex-plugin-cc repository provides a configurable stop-time checkpoint that prevents session termination until a dedicated Codex task validates the previous turn's work. This article explains the complete internal workflow—from configuration persistence to decision emission—based on the actual source code implementation.
Configuration and State Management
The review gate is controlled by a boolean flag stored in persistent state. In codex-companion.mjs, the handleSetup function processes the --enable-review-gate and --disable-review-gate flags (lines 21‑23) and persists the configuration via setConfig (lines 29‑35).
The setConfig function in lib/state.mjs writes the stopReviewGate key to state.json (lines 53‑59). When this flag is true, the plugin treats subsequent stop requests as gated reviews.
# Enable the review gate
codex:setup --enable-review-gate
# Disable the review gate
codex:setup --disable-review-gate
The Stop Hook Mechanism
When Claude issues a stop command, the Stop hook defined in hooks.json executes stop-review-gate-hook.mjs. The hook begins by reading its JSON input through readHookInput (lines 21‑27) and resolving the workspace root (line 45).
It then loads the current configuration using getConfig (line 46) and checks config.stopReviewGate (lines 54‑57). If the gate is disabled, the hook logs any running task note and returns immediately, allowing the stop to proceed without interruption.
Running the Stop-Gate Review Task
If the gate is enabled, the hook launches a special Codex task containing the marker defined in STOP_REVIEW_TASK_MARKER (line 19): "Run a stop‑gate review of the previous Claude turn."
The runStopReview function spawns a child process executing codex-companion.mjs task … with the marker as the prompt (lines 99‑107). This child process runs through the normal task pipeline via executeTaskRun in codex-companion.mjs. Because the prompt contains the marker, buildTaskRunMetadata (lines 40‑46) creates a job explicitly titled "Codex Stop Gate Review" (lines 42‑45).
The child process returns a JSON payload where the rawOutput field contains the final Codex response.
Decision Handling and Blocking
After the review task completes, parseStopReviewOutput examines the first line of rawOutput (lines 79‑88):
- If the line starts with
ALLOW:→ returns{ ok: true } - If the line starts with
BLOCK:→ returns{ ok: false, reason: … }
If the task fails, times out, or returns malformed JSON, the hook constructs a generic failure reason (lines 70‑78).
The hook then emits the final decision:
- Block: If
review.okis false,emitDecision(line 30) sends a payload{ decision: "block", reason: … }(lines 66‑71), preventing the stop and surfacing the reason to the user. - Allow: If the review passes, the hook logs any running-task note (line 75) and permits the stop to continue.
// Example blocked stop response
{
"decision": "block",
"reason": "Codex stop-time review found issues that still need fixes before ending the session: BLOCK: missing import statements"
}
Summary
- Configuration: The
stopReviewGateboolean flag persists instate.jsonviahandleSetupandsetConfigincodex-companion.mjsandlib/state.mjs. - Hook Activation:
stop-review-gate-hook.mjsruns on every stop event, checking the config flag before proceeding. - Task Execution: The
runStopReviewfunction spawns a Codex task with a special marker, creating a "Codex Stop Gate Review" job viabuildTaskRunMetadata. - Result Parsing: The
parseStopReviewOutputfunction inspects the first line forALLOW:orBLOCK:prefixes to determine the review outcome. - Decision Emission: The
emitDecisionfunction blocks the stop if the review fails, otherwise allows the session to terminate normally.
Frequently Asked Questions
How do I enable or disable the Codex review gate?
Use the setup command with the appropriate flag: codex:setup --enable-review-gate sets stopReviewGate: true in the state file, while codex:setup --disable-review-gate disables it. These commands invoke handleSetup in codex-companion.mjs, which updates the persistent configuration via lib/state.mjs.
What happens if the review task fails or times out?
If the child process fails, times out, or returns malformed JSON, stop-review-gate-hook.mjs constructs a generic failure reason (lines 70‑78) and treats the result as a blocking condition. This ensures that network issues or process errors default to a safe state rather than allowing an unreviewed stop.
How does the hook distinguish between review task output and regular Codex responses?
The parseStopReviewOutput function specifically checks the first line of the rawOutput field for the prefixes ALLOW: or BLOCK: (lines 79‑88). This simple protocol allows the hook to parse the review decision without complex JSON parsing, making the gate reliable and easy to debug.
Can I customize the review criteria for the stop gate?
While the gate mechanism itself uses a fixed marker (STOP_REVIEW_TASK_MARKER), the actual review logic follows the standard Codex task pipeline. You can modify the prompt template in plugins/codex/prompts/stop-review-gate.md to adjust what the review checks for, but the ALLOW:/BLOCK: parsing logic in parseStopReviewOutput remains constant.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →