
Codex Exec
- 23 installs
- 416 repo stars
- Updated August 5, 2026
- boshu2/agentops
codex-exec is a skill for running Codex workers or validators non-interactively through codex exec on the ChatGPT Pro subscription with evidence.
About
codex-exec is a skill for running Codex workers and validators non-interactively through codex exec on the ChatGPT Pro (OAuth) subscription. A developer uses it to spawn headless factory or loop workers and validators as a second-vendor lane. It enforces subscription (not per-token API) billing, deliberate sandbox selection, and safe stdin handling.
- Drive headless Codex workers and validators via codex exec on the Pro subscription
- Enforces subscription billing, never per-token API billing
- Sandbox selection (read-only, workspace-write, danger-full-access) sets the blast radius
Codex Exec by the numbers
- 23 all-time installs (skills.sh)
- Ranked #9,994 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
codex-exec capabilities & compatibility
Runs on a flat-rate ChatGPT Pro/Plus subscription via OAuth; API keys are explicitly banned for workers.
- Capabilities
- codex approval · casr · cc worktree isolation
- Use cases
- orchestration · testing · code review
- Pricing
- Paid
What codex-exec says it does
Use when running Codex workers or validators non-interactively through codex exec with evidence.
The one inviolable rule: **subscription billing, never per-token API billing.**
Never API-bill a worker.** Do NOT set `OPENAI_API_KEY` in a worker's env
npx skills add https://github.com/boshu2/agentops --skill codex-execAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 23 |
|---|---|
| repo stars | ★ 416 |
| Last updated | August 5, 2026 |
| Repository | boshu2/agentops ↗ |
What it does
Run headless Codex workers and validators non-interactively via codex exec.
When should I use this skill?
You run Codex workers or validators non-interactively through codex exec, or drive Codex from a script.
What you get
Codex workers run headless on the flat-rate Pro subscription under a deliberate sandbox with the final message captured.
- codex run output
By the numbers
- 3 sandbox modes (read-only, workspace-write, danger-full-access)
- verified against codex-cli 0.139.0
Files
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>" - 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. - Pipe the prompt (or close stdin) in any non-TTY lane — else codex HANGS. A positional-arg
codex exec "<prompt>"run with non-TTY stdin (background,&, ATM/NTM pane, cron, piped, inherited-pipe) still reads stdin — it printsReading additional input from stdin...and blocks forever when that stdin never reaches EOF (the classic idle open pipe). Why: codex appends piped stdin as a<stdin>block even when a positional prompt is present, so an open idle stdin is an unterminated read. For unattended/background/factory lanes the safe DEFAULT is to pipe the prompt —printf '%s' "$P" | codex exec … -(orcat prompt.txt | codex exec … -) — or close stdin —codex exec "<prompt>" </dev/null. The bare positional form is fine only for an interactive TTY. - Pick the sandbox deliberately.
-s read-onlyfor offline 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. - Network-touching validators are the exception — use `-s danger-full-access`. A validator that must
git fetch, clone a repo, or hit any network endpoint will FALSE-FAIL under-s read-only, because the sandbox blocksconnectsyscalls — the failure is an infrastructure artifact, not a real verdict. On an already-sandboxed host, give network validators-s danger-full-access. Offline validators stay-s read-only. - `--dangerously-bypass-approvals-and-sandbox` is 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 to
resume. 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.139.0 (codex exec --help, codex exec resume --help). Re-verify the flag surface on the next codex-cli bump — the flag tables (Phase 2 key flags, the Phase 3 resume currency note) are version-pinned; a CLI upgrade can add/rename/move flags, so re-run the two --help commands and reconcile before trusting this skill against a newer codex.
Folded triggers (ag-s43tg wave 1): codex-goals + codex-mcp-plugins + codex-sandbox-evidence route here
- `codex-goals` → this skill. Use when asked to define an objective once and let Codex iterate
until done (Codex Goals): express the objective as the codex exec prompt with an explicit done-condition ("Run tests. Report PASS/FAIL."), then drive iterations through codex exec resume --last (Phase 3) until the verdict lands — same sub-billing and sandbox rules as any worker lane.
- `codex-mcp-plugins` → this skill. Use when wiring MCP servers or plugins into Codex CLI
or the AgentOps Codex skill bundle: MCP/plugin config lives in $CODEX_HOME/config.toml (layer per-lane variants via -p/--profile <name>.config.toml); verify the wired server is visible to a worker with a cheap codex exec -s read-only probe before dispatching real work.
- `codex-sandbox-evidence` → this skill. Use when running
codex execin a
least-privilege sandbox with machine-checkable proof: pick the narrowest -s policy (Phase 2), then capture the proof surface — --json JSONL event stream, -o final-message file, and --output-schema for a schema-constrained verdict — so the sandbox posture and the result are both auditable artifacts.
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 nothingCheckpoint: 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 (offline / no network): 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."
# Validator that must reach the network (git fetch, clone, API): danger-full-access
# on an already-sandboxed host — -s read-only would FALSE-FAIL on blocked connect syscalls.
codex exec -C /path/to/repo -s danger-full-access \
-o /tmp/verdict.txt \
"Fetch origin/main, validate the change against it. Output VERDICT: PASS|FAIL + reasons."
# Stdin prompt (orchestrator piping the task in) — the SAFE DEFAULT for any
# unattended/background/ATM-pane/cron lane. The trailing `-` reads the prompt
# from the pipe and gives codex an immediate EOF, so it can't stall on
# "Reading additional input from stdin..." (see Critical Constraints).
printf '%s' "$TASK_PROMPT" | codex exec -C "$REPO" -s workspace-write -
# If you must pass the prompt positionally in a non-TTY lane, close stdin:
codex exec -C "$REPO" -s workspace-write "<task>" </dev/null
# 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.jsonlKey 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.jsonlresume takes a UUID session id or thread name; --last picks the newest in the cwd; --all disables cwd filtering. Currency note (codex-cli 0.139.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. resume cannot change the sandbox via -s, but --dangerously-bypass-approvals-and-sandbox (and --dangerously-bypass-hook-trust) IS available on resume — the resume-side lever for an externally-sandboxed host that needs a network-touching follow-up. 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: offline validator →
-s read-only; worker that edits →-s workspace-write; network-touching validator (git fetch/clone/API) →-s danger-full-access(read-only false-FAILs on blockedconnect) - [ ] 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."Validator dispatch rules (learned 2026-06-10, cp-4jac/cp-801l; extended cp-hhd7 cards 6–10)
- Network-touching validators need `-s danger-full-access`. A codex VALIDATOR that must read a Dolt-mode bd ledger, run
git fetch, or reach any network MUST be dispatched with-s danger-full-access.-s workspace-writeblocks network (connect: operation not permitted) and blocks FETCH_HEAD writes. A fail-closed FAIL caused purely by sandbox denial is an infrastructure artifact, not a verdict: fix the dispatch and re-run the judge. NEVER hand-verify the missing item yourself and upgrade the verdict — that breaks judge independence (author ≠ judge). - Set TMPDIR inside the workspace for any run that commits. The sandbox blocks git temp-object writes to
/var/folders; exportTMPDIRto a path inside the workspace (e.g.TMPDIR="$REPO/.tmp") before any codex run that needsgit committo succeed. - Verdict file contract. Bare
VERDICT: PASS|FAILas the first line, then a blank line, then a bareCOMMANDS RUN:line, then the commands + output verbatim. No##headings or parentheticals on those lines — the gate parses them anchored. Fail closed on anything unverifiable. - Judge prompt pattern — publish the output contract from the prompt (card 10, cp-b2by). A stated verdict spec drifts; the output shape must be derived from the prompt the judge reads. Minimal judge prompt:
You are an INDEPENDENT VALIDATOR. Author != judge.
BEAD: <id> — <title>
ACCEPTANCE: <verbatim acceptance text>
Re-run the cited commands on the actual artifacts. Do not read the evidence and agree.
Attest identity: include "judge_source: codex-<model>" inside COMMANDS RUN.
Return EXACTLY (no ## headings on these lines):
VERDICT: PASS
(blank line)
COMMANDS RUN:
judge_source: codex-<model>
$ <cmd>
<output snippet>
REASONS:
- bullet citing a COMMANDS RUN line- Output-contract validation before acting on a verdict. Programmatically confirm:
VERDICT:is on its own line,COMMANDS RUN:follows,judge_source:is present. A verdict missing any element is unverified — discard and re-dispatch. A judge that ran nothing is a reader, not a verifier (the counterfeit-judge shape, card 8). Use--output-schema FILEto enforce shape at the harness level when verdict feeds automation.
Codex runtime work rules (learned 2026-06-12, ag-codex-runtime-enhancement post-review)
Compact rules for any work on the Codex worker/receipt path (dispatch, packets, receipts, image-health). Full packet: `docs/learnings/2026-06-12-codex-runtime-review-auth-and-scope.md`.
1. Make the first acceptance test adversarial. Before any planner artifact: test packet-injected OPENAI_API_KEY, disabled/missing auth guards, command or sandbox mismatch, missing final verdict, missing required command evidence, and path-escape attempts. The 2026-06-12 review found a packet-provided env could re-inject OPENAI_API_KEY after the ambient-env guard passed — ceremony missed it; one adversarial test would not have. 2. Contracts need executable validators. If a JSON Schema is the contract, the dispatch path must validate against it (or generated validation from it). Hand-written partial checks + fixture inspection are documentary, not enforcement. 3. Receipt means evidence, not presence. A receipt proving "Codex ran" is not a receipt proving "acceptance commands ran and passed." Run and record evidence.required_commands results, or rename the field so it stops implying acceptance evidence. 4. Keep the critical path small. Worker-path MVP = packet validation, dispatch, receipt on success/failure, timeout/stdin/auth tests, one fixture-backed smoke. Image health, gate explainability, skill authoring, and doc migrations are follow-up beads unless explicitly requested. 5. Time-box discovery for implementation slices. ~15 minutes discovery, ~90 minutes vertical slice, then decide. New work becomes follow-up beads, not scope absorbed into the active bead (risk-class routing: `discovery`). 6. Approval evidence needs a durable proof surface. If Fable/ATM approval gates implementation, mirror the council artifact or a compact proof packet to a tracked durable path before the gating bead/epic closes (closeout rule: `codex-approval`).
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 |
Hangs on Reading additional input from stdin... | Non-TTY stdin (background/&/ATM pane/cron) with no EOF — codex reads stdin even with a positional prompt | Pipe the prompt (`printf '%s' "$P" \ |
| 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)using-atm— driving codex as an ATM TUI pane (keystroke /--codex-goalflow,atm codexreadiness gates) vs this skill's headlesscodex exec(stdin/positional). Different dispatch mechanics, same auth/sub rules — conflating them is how "positional arg → background hang" and "send → wedged TUI pane" co-occur.account-rotation— host-routed account switching; on Codex/Gemini and Linux/WSL lanes the swap tool iscaam(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
#!/usr/bin/env bash
# validate.sh — self-check for the codex-exec skill.
# Verifies: frontmatter completeness, exact line-start Triggers marker, the
# required section spine + ordering, and the Form-A line budget (<=250).
# Exit 0 on PASS, 1 on any FAIL. Run: bash scripts/validate.sh
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SKILL="$SKILL_DIR/SKILL.md"
PASS=0; FAIL=0
check() { # name, test-expr
if bash -c "$2" >/dev/null 2>&1; then
echo "PASS: $1"; PASS=$((PASS + 1))
else
echo "FAIL: $1"; FAIL=$((FAIL + 1))
fi
}
# --- existence + frontmatter ---
check "SKILL.md exists" "[ -f '$SKILL' ]"
check "starts with YAML frontmatter" "head -1 '$SKILL' | grep -q '^---$'"
check "frontmatter closes" "[ \$(grep -c '^---$' '$SKILL') -ge 2 ]"
check "has name: field" "grep -qE '^name: codex-exec$' '$SKILL'"
check "has description: field" "grep -qE '^description:' '$SKILL'"
check "has skill_api_version: 1" "grep -qE '^skill_api_version: 1$' '$SKILL'"
check "has metadata.tier" "grep -qE '^ tier:' '$SKILL'"
check "has output_contract" "grep -qE '^output_contract:' '$SKILL'"
# --- triggers (FAIL-severity per AUTHORING-STANDARD §0/§8.2) ---
check "line-start Triggers: marker present" "grep -qE '^[[:space:]]*Triggers:' '$SKILL'"
check "description carries a Triggers clause" "awk '/^description:/{f=1} /^[A-Za-z_]/&&!/^description:/{if(f)exit} f' '$SKILL' | grep -q 'Triggers:'"
# --- section spine (required order per §5) ---
check "has Critical Constraints section" "grep -qE '^## .*Critical Constraints' '$SKILL'"
check "has Why This Exists section" "grep -qE '^## Why This Exists' '$SKILL'"
check "has Quick Start section" "grep -qE '^## Quick Start' '$SKILL'"
check "has Workflow/Methodology section" "grep -qE '^## Workflow' '$SKILL'"
check "has Output Specification section" "grep -qE '^## Output Specification' '$SKILL'"
check "has Quality Rubric section" "grep -qE '^## Quality Rubric' '$SKILL'"
check "has Troubleshooting section" "grep -qE '^## Troubleshooting' '$SKILL'"
check "has See Also section" "grep -qE '^## See Also' '$SKILL'"
# spine ordering: Critical Constraints must precede Why This Exists
cc=$(grep -nE '^## .*Critical Constraints' "$SKILL" | head -1 | cut -d: -f1)
wy=$(grep -nE '^## Why This Exists' "$SKILL" | head -1 | cut -d: -f1)
check "Critical Constraints precedes Why This Exists" "[ -n '$cc' ] && [ -n '$wy' ] && [ '$cc' -lt '$wy' ]"
# --- line budget (Form A hard ceiling 250) ---
LINES=$(wc -l < "$SKILL" | tr -d ' ')
check "Form-A line budget (<=250, is $LINES)" "[ '$LINES' -le 250 ]"
echo ""
echo "Results: $PASS passed, $FAIL failed (SKILL.md = $LINES lines)"
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
{
"name": "codex-exec",
"form": "A",
"skill_api_version": 1,
"summary": "Drive worker/validator agents with `codex exec` on the ChatGPT Pro subscription (OAuth) — never an API-billed path.",
"sections": [
{ "id": "critical-constraints", "title": "Critical Constraints", "type": "constraints", "priority": 1 },
{ "id": "why-this-exists", "title": "Why This Exists", "type": "rationale", "priority": 2 },
{ "id": "quick-start", "title": "Quick Start", "type": "quickstart", "priority": 2 },
{ "id": "workflow", "title": "Workflow / Methodology", "type": "procedure", "priority": 1 },
{ "id": "output-specification", "title": "Output Specification", "type": "output-contract", "priority": 2 },
{ "id": "exit-codes", "title": "Exit Codes", "type": "reference-table", "priority": 2 },
{ "id": "quality-rubric", "title": "Quality Rubric", "type": "rubric", "priority": 2 },
{ "id": "examples", "title": "Examples", "type": "examples", "priority": 3 },
{ "id": "troubleshooting", "title": "Troubleshooting", "type": "troubleshooting", "priority": 3 },
{ "id": "see-also", "title": "See Also / References", "type": "routing", "priority": 3 }
],
"references": [],
"scripts": [
{ "path": "scripts/validate.sh", "intent": "execute", "exit0_on_pass": true, "checks": ["frontmatter", "exact-line-start-triggers", "section-spine", "spine-order", "line-budget"] }
],
"metadata": {
"tier": "orchestration",
"stability": "stable",
"user_invocable": false,
"hexagonal_role": "driving-adapter",
"dependencies": ["ntm", "caam"],
"consumes": [],
"produces": ["codex-run-output"],
"context_rel": [{ "kind": "supplier-to", "with": "codex-sandbox-evidence" }],
"triggers": [
"codex exec",
"spawn a codex worker",
"run codex non-interactively",
"codex validator",
"headless codex",
"codex resume",
"drive codex from a script",
"factory worker on Codex"
],
"token_estimate": {
"minimal": 120,
"overview": 380,
"standard": 1100,
"full": 1700
}
},
"evidence": {
"verified_against": "codex-cli 0.139.0",
"sources": [
{ "kind": "cli-help", "cmd": "codex exec --help", "verified": "2026-06-15" },
{ "kind": "cli-help", "cmd": "codex exec resume --help", "verified": "2026-06-15" },
{ "kind": "cli-status", "cmd": "codex login status", "result": "Logged in using ChatGPT", "verified": "2026-06-15" }
]
},
"quality_score": 0.95
}
Related skills
FAQ
Why never set OPENAI_API_KEY on a worker?
It flips Codex from flat-rate subscription billing to per-token API billing, the Codex twin of the banned claude -p.
How do you avoid codex exec hanging in a non-TTY lane?
Pipe the prompt (printf ... | codex exec ... -) or close stdin (codex exec "<prompt>" </dev/null); the bare positional form is fine only for a TTY.