codex-exec
Drive headless Codex worker and validator agents with codex exec on the ChatGPT Pro subscription (OAuth) — the Codex side of the flywheel. The one inviolable rule: subscription billing, never per-token API billing.
⚠️ Critical Constraints
- Never API-bill a worker. Do NOT set
OPENAI_API_KEYin a worker's env, and do NOT usecodex login --with-api-key. Why: that flips Codex from flat-rate sub billing to per-token API billing — the Codex twin of the bannedclaude -p. A factory cycle on API keys silently burns real money. (Mirror of the "neverclaude -pfor workers" rule.)- WRONG:
OPENAI_API_KEY=sk-... codex exec -C "$REPO" "<task>" - CORRECT:
codex login status # Logged in using ChatGPTthencodex exec -C "$REPO" -s workspace-write "<task>"
- WRONG:
- Confirm the sub before dispatch. Run
codex login statusand requireLogged in using ChatGPT. Why: a worker that "runs fine" on a leaked API token bills per token; the check is the only thing standing between a green run and a surprise invoice. - Pick the sandbox deliberately.
-s read-onlyfor validators,-s workspace-writefor workers that must edit,-s danger-full-accessonly inside an already-sandboxed host. Why:codex execruns model-generated shell commands; the sandbox is the blast radius. --dangerously-bypass-approvals-and-sandboxis for externally-sandboxed hosts only. Why: it removes every guardrail in one flag; use it only when the OS/container is the sandbox.- Don't strand work in
--ephemeral. It skips session persistence, so there is nothing toresume. Why: a crashed ephemeral run cannot be recovered or continued. - Multi-account lanes go through
caam, not env-var juggling. Why:caam exec codex <profile> --keeps each Pro lane isolated; hand-settingCODEX_HOMEinvites cross-account token bleed.
Why This Exists
codex exec runs Codex non-interactively: it takes a prompt (argument or stdin), executes against a working directory under a sandbox policy, and prints the final agent message. It is the Codex analogue of an NTM Claude pane — the right tool for factory/loop workers and validators that need a second vendor lane.
Auth is the whole game. codex login status must read Logged in using ChatGPT (the Pro/Plus subscription via OAuth). That billing is flat-rate. The moment Codex is authed with an API key (OPENAI_API_KEY, codex login --with-api-key), every token is metered against the API account — the exact failure mode claude -p causes on the Claude side. This skill exists to keep Codex workers on the sub. Without it, a loop that dispatches Codex turns can quietly run on metered API billing and produce a surprise invoice.
Verified against codex-cli 0.137.0 (codex exec --help, codex exec resume --help).
Quick Start
codex login status # MUST read: Logged in using ChatGPT
test -z "${OPENAI_API_KEY:-}" || echo "ABORT: API key set" # MUST be empty
codex exec -C "$REPO" -s read-only "Validate the change. Output VERDICT: PASS|FAIL."
Workflow / Methodology
Phase 1: Verify the lane is on the subscription
codex login status # MUST print: Logged in using ChatGPT
echo "${OPENAI_API_KEY:+API_KEY_SET}" # MUST print nothing
Checkpoint: Status reads Logged in using ChatGPT AND no OPENAI_API_KEY is set. If either fails, STOP — fix auth before dispatching. If API-key auth is present, run codex login (browser OAuth) or --device-auth to re-auth on the sub.
Phase 2: Dispatch the worker / validator
# Worker: edit in a repo
codex exec -C /path/to/repo -s workspace-write \
"Implement bead ag-123: <task>. Run tests. Report PASS/FAIL."
# Validator: read-only, structured verdict
codex exec -C /path/to/repo -s read-only \
-o /tmp/verdict.txt \
"Independently validate the change on this branch. Output VERDICT: PASS|FAIL + reasons."
# Stdin prompt (orchestrator piping the task in)
printf '%s' "$TASK_PROMPT" | codex exec -C "$REPO" -s workspace-write -
# Named profile (a specific model/config lane)
codex exec -p worker-fast -C "$REPO" -s workspace-write "<task>"
# Machine-readable event stream for a loop to parse
codex exec --json -C "$REPO" -s read-only "<task>" > events.jsonl
Key flags: -C/--cd <DIR> working root · -s/--sandbox <read-only|workspace-write|danger-full-access> · -p/--profile <name> layers $CODEX_HOME/<name>.config.toml · -m/--model · -o/--output-last-message FILE · --json JSONL events · --output-schema FILE constrains final-response shape · --add-dir extra writable dir · --skip-git-repo-check for non-repo dirs · --ephemeral no persistence.
Checkpoint: Worker exited 0; the final message (stdout / -o file / last JSONL item) carries the expected verdict or artifact.
Phase 3: Resume to continue a session
cd "$REPO" && codex exec resume --last "Address the validator's findings, then re-run tests."
codex exec resume <SESSION_ID> "<follow-up>"
codex exec resume --last --json "<follow-up>" > events2.jsonl
resume takes a UUID session id or thread name; --last picks the newest in the cwd; --all disables cwd filtering. Currency note (codex-cli 0.137.0): resume accepts -m/--model, -o/--output-last-message, --json, --output-schema, --ephemeral, -i/--image, --skip-git-repo-check — but NOT -C/--cd and NOT -s/--sandbox. The resumed session inherits its original working root and sandbox policy, so cd into the repo first (the resume picks the newest session in the cwd) rather than passing -C/-s.
Checkpoint: The resumed session id matches the intended thread and the follow-up landed in the same working tree.
Output Specification
Format: plain text (final agent message) by default; JSONL with --json; raw last message to a file with -o/--output-last-message.
Filename: caller-chosen via -o <FILE> (e.g. /tmp/codex-verdict.txt) or redirected JSONL (events.jsonl). No fixed convention — the orchestrator names it.
Structure: non---json = the agent's final message to stdout. --json = one JSON event per line (item/agent/tool events); parse the terminal agent message item for the result. --output-schema FILE forces the final response to conform to a supplied JSON Schema (use for machine-checkable verdicts).
Exit Codes
A loop should branch on the process exit code, not on scraped text.
| Code | Meaning | Loop action |
|---|---|---|
0 |
Run completed; final message emitted | Parse the result (-o FILE / last --json item); proceed |
| non-zero | Codex error — auth failure, sandbox denial, bad flag, or model/tool error | Do NOT treat as a verdict; re-check codex login status, sandbox mode, and flags, then retry or escalate |
Quality Rubric
-
codex login statusshowedLogged in using ChatGPTBEFORE dispatch - No
OPENAI_API_KEYin the worker env and no--with-api-keyanywhere - Sandbox mode matches role (read-only validator / workspace-write worker)
- Working root set explicitly with
-C, not assumed from cwd - Result captured deterministically (
-o FILE,--json, or--output-schema) — not scraped from terminal noise - Long/multi-turn work uses
resume(not--ephemeral) so it can be recovered - Multi-account lanes dispatched via
caam exec codex <profile> --
Examples
# Factory validator lane (read-only, schema-constrained verdict)
codex exec -C "$REPO" -s read-only \
--output-schema /tmp/verdict.schema.json \
-o /tmp/verdict.json \
"Validate bead $BEAD independently. Emit {verdict, reasons}."
# caam-isolated Pro lane, then continue it
caam exec codex pro-2 -- exec -C "$REPO" -s workspace-write "Start ag-77."
cd "$REPO" && codex exec resume --last "Fix the failing test from the prior turn."
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| Tokens billing to API account | API-key auth or OPENAI_API_KEY set |
unset OPENAI_API_KEY; codex login (OAuth) or --device-auth; re-check codex login status |
Logged in with API key in status |
Wrong auth path | Re-auth on the sub via browser OAuth / --device-auth; never --with-api-key for workers |
not a git repository error |
-C points outside a repo |
Add --skip-git-repo-check (or point -C at a repo) |
| Worker can't write files | -s read-only |
Use -s workspace-write; add --add-dir for paths outside the root |
resume finds nothing |
Prior run used --ephemeral, or wrong cwd |
Don't use --ephemeral for resumable work; try resume --all to drop cwd filtering |
| Empty / truncated output in a loop | Parsing terminal text | Use -o FILE or --json and read the structured result |
| Rate-limited on the Pro lane | One account saturated | Switch lanes with caam exec codex <other-profile> -- |
See Also / References
ntm— Claude worker panes (the Claude-side lane; neverclaude -p)caam— isolated multi-account profiles for the 4-lane flywheel (Claude Max ×2 + Codex Pro + Gemini)dcg— destructive-command guard that can enforce the never-API-bill rule- Memory: "Never claude -p for workers 2026-06-06" — the Claude-side twin of this skill's core rule