How the Stop-Time Review Gate Works in the Claude-Codex Workflow
The stop-time review gate is a safety checkpoint that automatically runs when a Claude session ends, using Codex to review the previous turn's code changes and returning either ALLOW or BLOCK to determine if the session may safely terminate.
The openai/codex-plugin-cc repository implements this gate as a hook script that intercepts the session stop event. When enabled, it ensures that no Claude session concludes without a final automated verification of the most recent code modifications. Understanding this mechanism helps developers configure reliable AI-assisted development workflows.
Where the Gate Lives
The stop-time review gate consists of two primary components registered within the plugin architecture.
The hook script at plugins/codex/scripts/stop-review-gate-hook.mjs contains the orchestration logic that Claude invokes automatically when the stop command is issued. This script manages input parsing, prompt construction, and decision emission.
The prompt template at plugins/codex/prompts/stop-review-gate.md defines the instructions fed to the Codex model. It establishes the review criteria and mandates a deterministic output format that the hook can parse reliably.
Registration occurs in .claude-plugin/plugin.json, which maps the stop event to the hook script path, ensuring Claude calls the gate automatically during session termination.
Execution Flow
The gate operates through a sequential seven-step process designed to fail safely.
Input Processing and Early Exits
The hook begins by calling readHookInput() (lines 21‑27) to parse JSON session context from stdin. This payload includes the current working directory, session ID, and the previous assistant message containing the code changes.
Before invoking Codex, the script performs two validation checks (lines 54‑57):
- Configuration check: If
config.stopReviewGateis false, the hook exits immediately with an ALLOW decision. - Availability check: If Codex is not configured (
getCodexAvailability), the hook logs a setup note viabuildSetupNote()(lines 59‑66) and skips the review.
Prompt Construction and Execution
When validation passes, buildStopReviewPrompt() (lines 48‑57) loads the template using loadPromptTemplate and interpolates the previous Claude response into the {{CLAUDE_RESPONSE_BLOCK}} placeholder.
The hook then executes runStopReview() (lines 98‑130), which spawns codex-companion.mjs via spawnSync with a 15‑minute timeout. This subprocess runs the Codex model against the constructed prompt in an isolated environment.
Decision Parsing and Session Control
After execution, parseStopReviewOutput() (lines 69‑95) extracts the rawOutput field from the returned JSON and inspects the first line for the mandatory prefix:
ALLOW:→ The hook permits session termination.BLOCK:→ The hook callsemitDecision()(lines 29‑31) to write a JSON block to stdout, instructing Claude to prevent session closure and display the provided reason.
The system also reports any still-running Codex jobs through logNote() (lines 33‑38), ensuring users are aware of background tasks before the session ends.
Prompt Logic and Response Contract
The stop-review-gate.md prompt template enforces strict constraints on Codex behavior to guarantee deterministic outcomes.
The template instructs Codex to only review work from the immediately preceding Claude turn. It mandates an immediate ALLOW if that turn produced no direct code edits, optimizing for the common case of conversational or planning sessions.
The output contract (lines 14‑20) requires a single-line response beginning with either ALLOW: or BLOCK: followed by an explanation. This structured format enables reliable machine parsing without ambiguity.
Default policy rules (lines 22‑26) establish that Codex should ALLOW when no edits are detected and reserve BLOCK for genuine issues or unfinished work, preventing unnecessary session interruptions.
Configuration and Integration
The gate integrates with the broader Claude-Codex ecosystem through configuration files and shared libraries.
The config.stopReviewGate setting, typically defined in .codexrc or .codex configuration files, acts as the master switch. Disabling this boolean bypasses the gate entirely, allowing immediate session termination.
The hook leverages utilities from plugins/codex/scripts/lib/state.mjs for configuration retrieval and job tracking. It queries the workspace job store via listJobs and filterJobsForCurrentSession to detect concurrent Codex tasks, warning users if background processes remain active.
All failure paths implement fail-safe semantics. If the Codex task exceeds the 15‑minute timeout (lines 12‑18), returns a non-zero exit code (lines 20‑27), or emits invalid JSON (lines 33‑38), the hook automatically BLOCKS the session rather than risking premature termination with unreviewed changes.
Debugging the Stop-Time Review Gate
You can manually invoke the gate for testing or debugging purposes without running a full Claude session.
Simulate a stop-time review by piping JSON context directly to the hook script:
cat <<EOF | node plugins/codex/scripts/stop-review-gate-hook.mjs
{
"cwd": "/path/to/project",
"session_id": "abc123",
"last_assistant_message": "Edited src/utils.ts to fix bug #42"
}
EOF
The script outputs a JSON decision to stdout:
{"decision":"block","reason":"Codex stop-time review found issues that still need fixes before ending the session: ..."}
When properly registered in .claude-plugin/plugin.json, Claude automatically triggers the hook when users issue /codex:stop or end their session:
{
"stop": "plugins/codex/scripts/stop-review-gate-hook.mjs"
}
To check for running jobs before manual invocation, inspect the job state using the internal utilities:
const runningTaskNote = runningJob
? `Codex task ${runningJob.id} is still running. Check /codex:status and use /codex:cancel ${runningJob.id} if you want to stop it before ending the session.`
: null;
Summary
- The stop-time review gate runs automatically when Claude sessions end, providing a final safety check on recent code changes.
- The implementation resides in
plugins/codex/scripts/stop-review-gate-hook.mjswith logic split acrossreadHookInput(),runStopReview(), andparseStopReviewOutput(). - A 15‑minute timeout and strict
ALLOW:/BLOCK:response format ensure reliable, deterministic behavior. - The gate respects the
config.stopReviewGateconfiguration setting and can be bypassed by disabling this flag. - All failure modes—including timeouts, non-zero exits, and invalid JSON—default to BLOCK to prevent unsafe session termination.
Frequently Asked Questions
What triggers the stop-time review gate?
The gate triggers automatically when a user ends a Claude session or issues the /codex:stop command. Claude invokes the registered hook script from .claude-plugin/plugin.json, passing the session context and previous assistant message as JSON via stdin.
How does the gate decide to ALLOW or BLOCK a session?
The gate spawns codex-companion.mjs with the stop-review-gate.md prompt, which instructs Codex to review only the previous turn's changes. Codex must return a single line starting with ALLOW: if the changes are acceptable or BLOCK: if issues remain. The hook parses this output in parseStopReviewOutput() (lines 69‑95) and emits the corresponding decision.
Can I disable the stop-time review gate?
Yes. Set stopReviewGate: false in your .codexrc or .codex configuration file. When config.stopReviewGate evaluates to false, the hook exits early (lines 54‑57) without invoking Codex, allowing immediate session termination.
What happens if the Codex review times out?
If the review exceeds 15 minutes, the hook catches the timeout in runStopReview() (lines 12‑18) and automatically emits a BLOCK decision. This fail-safe design ensures that sessions cannot end while the safety review is incomplete or unresponsive.
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 →