Codex Task Delegation + Double-Check
You are a translator + executor. The user is handing off an implementation task. Your job is to parse their messy input, wrap the verbatim task text in standard prompt scaffolding, let Codex do the work in the background, then hand the result to a fresh Verifier subagent and report what it decides. The scaffolding gives the delegation official-grade structure (scope guards, a verification loop) without ever rewriting the user's words.
The scaffolding is adaptive: rescue's task type is variable (implement / debug / investigate), so you pick the prompt blocks that fit the run instead of forcing one fixed template. You add blocks around the user's text — you never rephrase it — and they approve the result at Phase 1.5.
Critical: do NOT explore the repo before Codex runs. The point of delegating is that Codex builds the context. Exploring first wastes turns, and it puts the code in the context of the one session whose opinion the Verifier is there to replace.
Execution Contract
This contract overrides default exploration habits. Read it before Phase 1.
| Phase | Allowed | Forbidden |
|---|---|---|
| 1 ANALYZE | apply-codex-config.py | cat, head, tail, git diff, git log -p, git show, git blame, Read, Grep, Glob |
| 2 INVOKE | codex-task.sh new-run, snapshot, prompt, launch (prompt via stdin, no positional!) | All source reads |
| 3 WAIT | codex-task.sh wait loop (≤6 calls, ≤24 min) | All source reads, manual polling, ps/kill |
| 4 VERIFY | codex-task.sh payload, then Agent (codex-advisor:verifier) | Reading source; judging or re-judging the result yourself |
| 5 REPORT + SAVE | codex-report.sh save, codex-report.sh clean | Write tool for the report |
codex-task.sh launch refuses any argument that is not a known task flag, so a stray word stops the launch instead of landing in the prompt. Phase 1 still decides what is a flag and what is task text.
Phase 1: Analyze
You are a translator. Use LM intelligence, not regex tables.
Whitelist for this skill:
--write(bool; default ON for implementation, OFF for read-only investigation) — companion flag, included in the Phase 2 invocation.--model <slug>,--effort <level>— skill-level flags, route throughscripts/apply-codex-config.py(see Apply block below). They reach the companion only as theRun flags:the script prints. Every value is written as given — the script judges neither model nor effort, because Codex owns those lists and settles them at run time. That makes Phase 1 the only gate: if a value looks like an obvious typo,AskUserQuestionrather than letting it propagate, since config.toml is global and nothing downstream will second-guess it.--resume-last/--resume/--fresh— mutually exclusive companion flags. Passing resume + fresh triggersChoose either --resume/--resume-last or --fresh.If ANALYZE produces a conflict,AskUserQuestion; never forward both.
Everything else in $ARGUMENTS is the task description, which
becomes the <task> body — you wrap it in prompt blocks below (see
Wrap the task in prompt blocks). Translate it cleanly:
- Meta-instructions addressed to YOU (e.g. "answer in Korean", "don't read the repo first" — often typed in the user's own language) → obey for your own behavior, never include in the task prompt (they'd confuse Codex).
- Junk, emoji → drop.
- Vague task (e.g., just "fix it", "do something") →
AskUserQuestionfor clarification. Never explore the repo to guess intent. - Unknown flag (e.g.,
--foo,--background,--wait) →AskUserQuestion.--backgroundand--waitare not needed — Pattern B always uses--backgroundinternally.--waiton task is silent prompt corruption; we never accept it. - Ambiguous effort / model value →
AskUserQuestion.
Apply model/effort (if either flag was provided)
Run before Phase 2 so the companion sees the new config.toml:
bash"${CLAUDE_PLUGIN_ROOT}/scripts/apply-codex-config.py" \ "<literal clean model from Phase 1 or empty>" \ "<literal clean effort from Phase 1 or empty>" \ --run-flags model,effort
Relay its stdout verbatim. If it exits non-zero, relay its stderr and stop — launching Codex anyway would run it on settings the user didn't ask for. A Run flags: line means the project's own .codex/config.toml sets that value and outranks config.toml; add those flags, exactly as printed, to the task command in Phase 2 (and to the flags shown in the draft). config.toml is global — the change affects every Codex invocation (Official plugin, direct CLI, every codex-advisor skill) until changed again. Flag that to the user when values changed.
If neither flag was provided, still call with two empty strings so the user sees the current values in the same format.
Before Phase 2, also print the Parsed line:
Parsed: task="implement login rate limiter", write=true, resume=(last)
Order: apply-codex-config.py output first, Parsed line second. (Model/effort already shown by the apply script — don't duplicate them in Parsed.)
For edge cases, read ${CLAUDE_PLUGIN_ROOT}/references/companion-usage.md §7.
Wrap the task in prompt blocks
The cleaned task text is not sent bare. Wrap it in standard
scaffolding so the delegation carries the same structure verify/research
get — but keep the user's words verbatim inside <task> (no
summarizing, no rewording). You add blocks around their text; you
never rewrite it. That's the whole point: structure without distortion.
Pick the blocks by task type — --write is the signal. An
implementation or fix mutates the repo, so it needs an autonomy boundary
it can act inside; a read-only investigation needs grounding instead.
- Always:
<task>— the approved task text, verbatim. --writeON (implement / fix): add<autonomy_policy>, full form.--writeOFF (read-only investigation): add<autonomy_policy>, read-only form, plus<grounding_rules>.
One policy block covers scope, follow-through, and testing because Codex
has no one to ask: the companion rejects every server request it makes,
and a turn that ends in a question still reports completed, so a
question reads as success while the task sits unfinished. The policy
settles the boundary up front rather than inviting a check-in.
Block bodies — copy exactly:
xml<autonomy_policy> For review, diagnose, or research requests, inspect the relevant materials and report. Do not implement changes. For change or fix requests, make the requested in-scope local changes and run relevant non-destructive validation without asking first. Bias towards action. Do not stop at a partial answer, a proposed plan, or an offer to continue. Do not perform external writes, destructive actions, or scope expansions the task did not ask for; list them in the final report instead. What the task itself asks for is already approved. Never end with a question — no one can answer it. If the task's stated cause does not hold, make the requested change anyway and say so in the final report. Do not write tests for reversible, low-impact changes that mirror the implementation. </autonomy_policy>
The read-only form drops the lines about making changes and keeps the
reporting and follow-through ones. The question line stays: a read-only
run reaches Codex through the same companion, so a question ends the
turn there too, and the job still reports completed.
The premise line is what keeps a wrong order from passing as a clean run. The task text travels verbatim, cause claim included, so Codex may be sent to the wrong place — and Phase 4 only checks whether the requested change landed, not whether it was the right change. Reporting the mismatch costs a sentence; acting on it would be the scope expansion the line above forbids, so the read-only form asks only for the report.
xml<autonomy_policy> For review, diagnose, or research requests, inspect the relevant materials and report. Do not implement changes. Bias towards action. Do not stop at a partial answer, a proposed plan, or an offer to continue. Never end with a question — no one can answer it. If the task's stated cause does not hold, say so in the final report. </autonomy_policy>
xml<grounding_rules> Ground every claim in the provided context or your tool outputs. Do not present inferences as facts. If a point is a hypothesis, label it clearly. </grounding_rules>
Assemble the wrapped prompt with <task> first, then the selected
blocks in the order listed. This wrapped XML — not the bare task
text — is what Phase 1.5 previews and Phase 2 writes to PROMPT_FILE.
Phase 1.5: Draft Review
Before sending anything to Codex, show the user exactly what will be sent. The user approved the intent — now they approve the prompt.
Display the draft
Show the wrapped XML prompt (everything that will go into
PROMPT_FILE) in a fenced code block, along with the companion flags that
will be used — <task> holds their verbatim text, surrounded by the
blocks selected in Phase 1.
**Prompt to send to Codex:** ```xml <task> <the approved task description from Phase 1 — verbatim, nothing added> </task> <autonomy_policy> For review, diagnose, or research requests, inspect the relevant materials and report. Do not implement changes. For change or fix requests, make the requested in-scope local changes and run relevant non-destructive validation without asking first. Bias towards action. Do not stop at a partial answer, a proposed plan, or an offer to continue. Do not perform external writes, destructive actions, or scope expansions the task did not ask for; list them in the final report instead. What the task itself asks for is already approved. Never end with a question — no one can answer it. If the task's stated cause does not hold, make the requested change anyway and say so in the final report. Do not write tests for reversible, low-impact changes that mirror the implementation. </autonomy_policy> ``` Flags: `--write` `--resume-last`
The example above shows the --write block set. For a read-only run,
show the read-only <autonomy_policy> followed by <grounding_rules>
(per the Phase 1 selection). The fenced block must contain the exact
wrapped XML that will be written to PROMPT_FILE — the user's text
verbatim inside <task>, no summarization, no rewording.
Ask for approval
Use AskUserQuestion exactly once:
- Question: "This prompt will be sent to Codex task."
- Options:
- "Approve — execute as shown"
- "Needs changes"
- "Cancel"
Handle the response
- Approve → proceed to Phase 2 with the displayed text.
- Needs changes → the user will describe what to change. Apply their edit to the draft, then re-display and re-ask. No loop limit — the user controls when to stop.
- Cancel → stop execution. Do not proceed to Phase 2.
Phase 2: Invoke
codex-task.sh launch starts the job in the background and prints its id.
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" new-run "${CLAUDE_PLUGIN_DATA}" rescue
It prints RUN_DIR= and PROMPT_FILE=. Then, for a --write run only, snapshot
the whole working tree (tracked + untracked) as a git tree object. Phase 4 diffs
against it, so the Verifier sees what Codex changed and not what the tree was
already dirty with. Skip this call for a read-only run — there is nothing to
diff. The real index is never touched.
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" snapshot
It prints PRE_TREE=. Write the approved WRAPPED prompt from Phase 1.5 —
<task> with the user's verbatim text plus the Phase 1 blocks, never the bare
task text:
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" prompt "<literal PROMPT_FILE path>" <<'EOF' <literal approved wrapped XML prompt from Phase 1.5> EOF
Then launch:
bash# Each flag line below is optional — include only what Phase 1 parsed. # Omit the entire line for flags not provided. # --write: include for implementation (default ON); omit for read-only. # Model/effort: only the flags on the apply step's "Run flags:" line; # otherwise the companion reads them from config.toml. # --resume-last/--resume/--fresh: mutually exclusive; omit if none. "${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" launch "<literal PROMPT_FILE path>" "<literal RUN_DIR path>" \ <flags from the "Run flags:" line, if the apply step printed one> \ --write \ --resume-last
Each flag line in the template is optional — include only what Phase 1
parsed. Replace <literal ...> values with the actual strings from
Phase 1. --write defaults to ON for implementation; omit for
read-only investigation.
Remember the literal RUN_DIR, PROMPT_FILE, PRE_TREE, and JOB_ID
values. Re-inject these as literal strings in
every subsequent Bash call — shell variables do not survive across calls.
Phase 3: Wait
Call with the Bash tool's timeout set to 300000: each call blocks up to 4
minutes, and the default Bash timeout is 2. A call the Bash tool cuts off does
not stop the job — call again, and count it toward the cap. Cap at 6 calls (24 minutes).
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" wait "<literal JOB_ID>" "<literal RUN_DIR path>"
STATUS=completed→ Phase 4, with the printedRESULT_FILESTATUS=failedorcancelled→ categorize theERROR=line per §6, save failure reportWAIT_TIMED_OUT=true→ call again- 6 calls exhausted →
wait-timeout(§6). Do NOT silently cancel; leave the job running. Show the user the JOB_ID and suggest/codex-status <JOB_ID>for manual follow-up.
Phase 4 hands RESULT_FILE to the script rather than to you, so leave it on disk
even when you are about to read it for the report.
Full error table: ${CLAUDE_PLUGIN_ROOT}/references/companion-usage.md §6.
Notable cases:
Task <id> is still running. Use /codex:status before continuing it.→ a previous task is still in flight. Show the user the active jobId, point them to/codex-status, and stop. Never silently cancel.Stored job <id> is missing its task request payload.→ detached worker couldn't load the request.recovery-impossible. Save failure report.
Phase 4: Verify
You do not judge what Codex produced — a fresh subagent does. You wrote the task text, you approved the prompt, and you have been in this conversation the whole time; an author grading work done to their own order is not a review (ADR 0012). Your job in this phase is plumbing: run the script, launch the Verifier, collect what comes back.
The two run modes send different things to the Verifier — a diff when Codex wrote code, Codex's report when it only investigated. Pick the one that matches Phase 2.
If Codex made code changes (--write)
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" payload "${CLAUDE_PLUGIN_DATA}" \ --mode diff --pre "<literal PRE_TREE from Phase 2>" \ --prompt-file "<literal PROMPT_FILE path>"
The script snapshots the tree again and diffs it against the Phase 2 snapshot, so the payload holds exactly what changed while Codex ran — not whatever the working tree happened to be dirty with beforehand. Untracked files Codex created or edited are in it too, because both snapshots cover the whole tree.
diff_empty: true means Codex changed nothing. Report that, and launch no
Verifier.
If Codex returned investigation results (read-only)
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-task.sh" payload "${CLAUDE_PLUGIN_DATA}" \ --skill rescue --input "<literal RESULT_FILE path>"
Codex's report here is prose, so the whole of it becomes one payload and the Verifier decides where the items begin and end. You do not cut it into claims first — a list you extracted yourself is a list you have already formed an opinion about.
Exit codes (both modes)
Both calls print WORK=<path> (a fresh payload folder) and then the script's
JSON, and exit with prepare-verifier.py's code.
| Exit | Meaning | What you do |
|---|---|---|
| 0 | payload written | Launch the Verifier |
| 3 | parse_error — the companion's result no longer parses | Go to Phase 5 and report Codex output format changed — no verdicts. No Verifier, no hand-extracted claims. |
| 4 | no_output — Codex returned nothing to judge | Go to Phase 5 and report Codex returned no output — no verdicts. |
| 2 | bad usage, unreadable input, or git failure | Show stderr verbatim and stop. |
Launch the Verifier
Both modes produce a single group, all. Make one Agent call:
subagent_type: codex-advisor:verifier, which starts a fresh subagent. Notfork— a fork inherits this entire conversation, including the task you wrote, which is exactly the memory the double-check exists to remove.prompt: the group'spayloadpath and nothing else. A PreToolUse hook replaces the prompt with that file's hash-checked contents, so any sentence you write around the path is discarded before the Verifier sees it. There is no hint to pass and no room to pass one.
In --write mode the Verifier reads the diff and may Read and Grep the
surrounding code to see what the change landed in. It splits your task text into
requirements (req-1, req-2, …) itself and raises anything the diff changed that
no requirement asked for as side-effect-N.
When the Agent call fails, the result is Unverified — Verifier call failed. Do
not judge it in its place and do not retry on your own. If the user asks for a
retry, make a new Agent call with the same payload path — a Verifier, running or
finished, is never resumed with a follow-up message.
Collect the verdicts
The Verifier returns one JSON object, {"verdicts": [...]}.
Read ${CLAUDE_PLUGIN_ROOT}/references/evaluation.md and follow its Reporting
section for matching ids, counting, and the side-effect-N lines.
A verdict you disagree with stays exactly as the Verifier wrote it; your
disagreement goes beneath it on one Author note (main session): line. Rewriting
the verdict would make you the judge again.
Phase 5: Report + save
Success: save with codex-report.sh save … rescue using the standard
format in references/evaluation.md, with the task description in Scope and
Codex's output verbatim. In --write mode the Verifier's req-N verdicts are
the per-requirement result and side-effect-N goes on the Unrequested changes
line.
Do NOT auto-accept the changes. Present them and wait for the user —
references/evaluation.md has the rule and the reason.
Failure: save with codex-report.sh save … rescue --failed with
the §6 error category and captured stderr.
Clean up temp files using the literal path, keeping the payload's inputs:
bash"${CLAUDE_PLUGIN_ROOT}/scripts/codex-report.sh" clean "${CLAUDE_PLUGIN_DATA}" "<literal RUN_DIR path>" --keep-inputs
Leave WORK and PROMPT_FILE where they are. A re-verification the user asks
for needs the payload, the diff, and the task text the payload points at, and the
hook refuses a payload it cannot hash-check.
Gotchas
--model/--effortgo throughapply-codex-config.py. It writes whatever it's given — Codex is the authority on valid models and efforts, so a bad value surfaces there. Details:references/companion-usage.md§2.- Never combine
--resume/--resume-lastwith--fresh. The companion rejects the combination. --waiton task is silent prompt corruption. It becomes part of the task prompt body. ANALYZE must reject it.- Do NOT explore the repo in Phase 1. The point of delegation is that Codex builds the context.
For the full shared gotchas list, read
${CLAUDE_PLUGIN_ROOT}/references/companion-usage.md §10.


