Cross-agent review: Claude implements, Codex (GPT) reviews. Codex works in the same directory and can inspect the code on its own.
Location of scripts
The scripts are located in scripts/ next to this SKILL.md. Determine the full path:
- This file: the path from which you read SKILL.md
- Scripts: replace
SKILL.mdwithscripts/codex-review.sh(иscripts/codex-state.sh)
All commands below use the relative scripts/ — substitute the full path when calling.
CRITICAL: Sandbox
Codex CLI uses macOS system API (SCDynamicStore), which are blocked by the Claude Code sandbox. All calls to codex-review.sh and codex-state.sh MUST be executed with dangerouslyDisableSandbox: true in the Bash tool. Without this, codex crashes with a Rust panic.
Workflow
1. Session initialization
Create a Codex session with a description of the task.
bash scripts/codex-review.sh init "Implement JWT authentication for API"
The session can also be set manually in .codex-review/config.env: CODEX_SESSION_ID=sess_...
If there is no session (exit 3 — NO_SESSION), ask the user:
- Is there already a live session with Codex? → have them enter the id into config.env
- Or create a new one via
init?
2. Plan review
Pass the path to the plan file via --plan-file. Do NOT insert the plan contents into the command-line argument — the script reads the file itself and sends the contents to Codex on stdin, so the plan size is not limited in any way.
С plan mode
If you use plan mode — send the plan for review before ExitPlanMode:
- Wrote a plan → CC saves it to
~/.claude/plans/<slug>.md(automatically) - Pass this path to
--plan-file:bash scripts/codex-review.sh plan --plan-file ~/.claude/plans/<slug>.md CHANGES_REQUESTED→ adjust the plan in the file, send again (see "Accept or Argue")APPROVED→ process## Non-blockingи## Pre-existing(see "Sections of the reviewer's response"), then callExitPlanModefor user approval
Thus the plan goes through two reviews: technical (Codex) and business-priority (user).
Without plan mode
If the plan is written to a separate file inside the project:
bash scripts/codex-review.sh plan --plan-file docs/plan.md
Plan template (recommended file structure)
What: [problem being solved]
Approach: [chosen approach and why]
Alternatives considered: [what was rejected and why]
Files to change: [list]
Addressed concerns: [if resubmit — point-by-point from previous review]
3. Implementation
Before starting implementation, update the phase:
bash scripts/codex-state.sh set phase implementing
Implement according to the approved plan.
4. Code review
Describe WHAT you did, WHAT decisions you made. Do NOT pass the git diff — Codex will look at it itself.
Code description template
What changed: [summary of changes]
Key decisions: [non-obvious decisions made during implementation]
Files modified: [list with brief description per file]
Tests: [what tests were added/run, results]
Addressed concerns: [if resubmit — point-by-point from previous review]
bash scripts/codex-review.sh code "What changed: JWT auth middleware + refresh endpoint. Key decisions: RS256 over HS256 for key rotation. Files: auth/jwt.py (middleware), api/auth.py (refresh endpoint). Tests: 3 new tests (expired/invalid/valid tokens), all pass."
Description from file
If the description contains backticks (a common thing — that is how function and field names are written), $ or $(...) — pass the text as a file, not as an argument: the shell executes such fragments inside double quotes, and Codex receives distorted text or the call fails with command not found.
bash scripts/codex-review.sh code --description-file /path/to/description.md
The option works for init, plan и code; on plan it names the plan file — the same as --plan-file. Simultaneously with --plan-file or with a description in the argument — an error. The file is read to the last byte: empty lines at the end and the absence of a trailing newline are preserved.
The sent text is saved next to the attempt log byte for byte: codex-<phase>-<N>.request.md for review, codex-init.request.md for the session.
Task name for init
state.json stores one line — the task name that is visible in STATUS.md and in the archive summary. You provide the name yourself:
bash scripts/codex-review.sh init --description-file task.md --task-label "JWT auth: middleware + refresh endpoint"
Rules: one line, no double quotes, backslashes or control characters, no leading or trailing spaces, up to 200 characters. The name is saved exactly as passed: the script does not rewrite or clean up anything, but rejects it with an explanation. An empty value in --task-label — an error. If the task description takes more than one line and --task-label is not passed — the run fails, nothing is created: name the task yourself, and do not expect the script to guess. With a one-line description, the name is taken from it.
The full task text does not go into state.json — it goes entirely to Codex and is stored in codex-init.request.md.
5. State management
bash scripts/codex-state.sh show # Текущее состояние
bash scripts/codex-state.sh dir # Путь к state-каталогу текущей ветки
bash scripts/codex-state.sh reset # Сброс итераций (session сохраняется)
bash scripts/codex-state.sh reset --full # Полный сброс
bash scripts/codex-state.sh get session_id # Получить поле
bash scripts/codex-state.sh set session_id <val> # Установить вручную
bash scripts/codex-state.sh set phase implementing # Обновить фазу
bash scripts/codex-state.sh set iteration 2 # Откатить счётчик кругов
get returns the field value as it is recorded: an empty string field — an empty
string, a counter — a number. An unknown field name — an error with return code 1.
Special names: session_id (priority for config.env) и verdict (from
verdict.txt).
set accepts fields session_id, phase, iteration, max_iterations,
reviews_completed, last_review_status, last_review_timestamp,
task_description. Unknown field name, non-integer counter value, and a value
with a quote, backslash, newline, or tab — error with return
code 1, state is not changed.
To read review files (notes, STATUS.md, etc.), use codex-state.sh dir — it will return the absolute path to the current branch directory.
Handling exit codes
| Exit | Status | Action |
|---|---|---|
| 0 | APPROVED | Handle ## Non-blocking и ## Pre-existing (see "Reviewer response sections"), then continue working |
| 0 | CHANGES_REQUESTED | Adjust and send again (see "Accept or Argue") |
| 1 | ERROR | Report the error, suggest checking session_id |
| 2 | ESCALATE | Notify the user, output a brief summary, suggest options (see "Handling ESCALATE") |
| 3 | NO_SESSION | Ask: create a session via init? |
If ERROR the round did not take place: the iteration is not consumed, the same request
can be sent again. The iteration is consumed by the run that returned a verdict,
— even if the run itself ended with an error after that.
Handling ESCALATE (exit 2)
When the iteration limit is exhausted:
- Get the path:
STATE_DIR=$(bash scripts/codex-state.sh dir). Read the review notes from$STATE_DIR/notes/(files{phase}-review-{N}.md) - Output a brief summary to the user:
- Which stage (plan/code), how many iterations have passed
- Key comments and statuses for each iteration (1-2 lines per iteration)
- Use
AskUserQuestionwith three options:- One more iteration — extend the limit by 1 for one time
- Remove the limit — remove the restriction for this session
- Stop the review — output the final summary and stop (The "Custom option" option is added automatically)
Handling the response:
- "One more iteration" → repeat the call
codex-review.sh {phase} "..." --max-iter $((текущий_лимит + 1)) - "Remove the limit" → repeat the call
codex-review.sh {phase} "..." --max-iter 999 - "Stop the review" → output the final summary and end the review process
- Custom option → follow the user's instruction
STATUS.md
File STATUS.md in the branch state directory (path: codex-state.sh dir) is created and updated automatically by scripts. Do not edit it manually.
- File appears at
initand is updated on everyplan/codeиcodex-state.sh set - File is deleted on final APPROVED at stage
codeand atreset --full - File present = active review, absent = review not in progress
Verdict
Codex writes its verdict to verdict.txt inside the branch's state directory (one word: APPROVED or CHANGES_REQUESTED). To read the verdict, use bash scripts/codex-state.sh get verdict — the helper returns APPROVED, CHANGES_REQUESTED or an empty string (absent/invalid). The file is cleared before every review request, so the word in it was written by the current round. The verdict is taken only from here: the text of the response to the decision has no effect. If after the run the file does not contain APPROVED or CHANGES_REQUESTED — the round did not take place: the script returns ERROR (exit 1), the iteration is not consumed, the reviewer's response is saved next to the attempt log as codex-<фаза>-<N>.reply.md. Next to the verdict lies verdict.phase — one word, plan or code:
the phase that received it. Both files are deleted together.
The plugin hook ExitPlanMode links the verdict to the current Claude session via
current_session.txt in the same directory — a verdict that came from another session
is deleted. The hook skips ExitPlanMode only at APPROVED with the mark
plan. A verdict marked code or without a mark is deleted by the hook, and it responds
with a refusal indicating to run the plan review.
Completed cycle
APPROVED at stage code closes the cycle: STATUS.md is deleted, and the next
plan or code finishes with exit 1 and changes nothing. Next — one of
two paths:
- New task —
codex-review.sh init "<задача>": the previous cycle goes to the archive, a new Codex session opens. - Rework of the same task —
codex-state.sh reset, then the usual round: the round count starts from one, the task name and session_id are preserved.
Each round prints to stderr which task it relates to: Task: <название>
in the first round and Continuing task: <название> (previous round <время>), if there have already been rounds
on this branch. If the name does not match the current task — the loop
belongs to another job: choose one of the two paths above before sending the round.
Reviewer response sections
The reviewer response is divided into three sections. What to do with each:
| Section | Action |
|---|---|
## Blocking |
fix or dispute each item — see “Accept or Argue” |
## Non-blocking |
do not fix on your own initiative; present the list to the user via AskUserQuestion (take into work / as a separate task / do not do) |
## Pre-existing |
present each item of level critical to the user via AskUserQuestion (fix now / as a separate task / do not fix) before you declare the review finished; the remaining items — together with ## Non-blocking |
With APPROVED with a non-empty ## Non-blocking the work is considered accepted: there is no need to send a new review round because of these items.
Rules
- NEVER call
codex execdirectly — only via the scriptscodex-review.shиcodex-state.sh. The scripts know the model, config and session_id themselves - Describe WHAT you did and WHY, what decisions you made — use the description templates
- Do NOT pass git diff — Codex will look itself, it is in the same directory
- APPROVED → handle
## Non-blockingи## Pre-existing, then continue working - Before implementation, call
codex-state.sh set phase implementing - There is a customer (the user) — clarify ambiguous questions with them
- Option
--max-iter Nallows you to change the iteration limit
Worktree & Branch Isolation
Review state is isolated per branch. The scripts automatically determine the main repository and the current branch. Parallel work on several branches/worktrees is safe. config.env — shared (in the root of .codex-review/). To get the path to the state directory of the current branch, use codex-state.sh dir.
One run per branch
init, plan, code, codex-state.sh set и codex-state.sh reset occupy
the branch for the entire duration of work: the directory .lock inside the state directory. A second run
on the same branch exits with exit 1 without changing anything, and prints which
command holds the branch, its pid, machine and start time.
If the error text says the process is running, wait for it to finish and repeat the command.
If it says the process is not running or that this cannot be determined (a lock from
another machine, from another system user, with no owner record), make
sure nothing is running on this branch, delete the directory named in the error .lock and repeat the command. Only the run that took the lock, or a human, can release it.
Reading the state does not take the lock: show, get и dir always respond.
Auto-Workflow (AUTO_REVIEW=true)
When AUTO_REVIEW=true in .codex-review/config.env, the entire review cycle runs automatically. A plugin hook blocks ExitPlanMode until Codex approves the plan.
Plan phase
- Write the plan in plan mode as usual
- Before calling
ExitPlanMode, run review:
IMPORTANT: Always runbash scripts/codex-review.sh init "task description" # ALWAYS init for a new plan — archives previous session bash scripts/codex-review.sh plan --plan-file ~/.claude/plans/<slug>.mdinitbefore the firstplanreview in a conversation. Even ifcodex-state.sh showreports an existing session, it may be stale (from a previous conversation). Theinitcommand safely archives the old session and creates a fresh one. Only skipinitwhen re-submitting afterCHANGES_REQUESTEDwithin the same review cycle. - Formal verdict check — run
bash scripts/codex-state.sh get verdict. Proceed ONLY if it outputs the exact stringAPPROVED. Do NOT interpret review text — only the helper output matters. CHANGES_REQUESTED→ fix the plan, resubmit (follow «Accept or Argue» rules). Iterate automatically up to the iteration limit.APPROVED→ handle## Non-blockingand## Pre-existingfirst (see «Reviewer response sections»), then callExitPlanMode(the hook auto-approves it)
Implementation phase
- Implement as usual. Set phase:
bash scripts/codex-state.sh set phase implementing
Code phase
- After implementation, run code review:
bash scripts/codex-review.sh code "code description" # or, when the description contains backticks / `$` / `$(...)`: bash scripts/codex-review.sh code --description-file /path/to/description.md - Formal verdict check — same as step 3: run
bash scripts/codex-state.sh get verdictand check for exact stringAPPROVED. CHANGES_REQUESTED→ fix code, resubmit automatically.APPROVED→ handle## Non-blockingand## Pre-existing(see «Reviewer response sections»), then report to user.
ESCALATE handling in auto mode
Same as standard ESCALATE handling — present summary and ask user via AskUserQuestion.
Accept or Argue
When receiving CHANGES_REQUESTED:
- Read the previous review note from
$(bash scripts/codex-state.sh dir)/notes/{phase}-review-{N}.md - Critically evaluate each item in the section
## Blocking. In the description for the resubmission, address each of them point by point:- Fixed: [what exactly was fixed and how]
- Disagree: [counterargument with justification — Codex sees the history and can accept or insist]
- Deferred: [reason — only with the user's consent via AskUserQuestion]
- Record the user's decision on the items in the same description
## Non-blockingи## Pre-existing - If the same item
## Blockingrepeats 2+ times without new content (Codex insists, you have already argued) — escalate to the user via AskUserQuestion: show the comment, your arguments, and ask for a decision - If for two rounds in a row the entire
## Blockingconsists of defects in code that were produced by the fixes of previous rounds — stop the rounds and ask the user via AskUserQuestion: another round of fixes or roll back the mechanism to the state before the review - When the iteration limit is exhausted — follow the «Handling ESCALATE» procedure