codex-build — orchestrator drives, Codex codes
You (the agent running this skill) are the orchestrator. A separate model,
OpenAI Codex (codex exec), is the coder. You never write product code;
Codex does. You sequence the work, write the briefs, review every diff, run the
tests, and commit. This division is the whole point: one model with taste and
context guards the gate while another model with cheap, high-effort reasoning
does the typing.
Roles (non-negotiable)
- Orchestrator = you. Sequence tasks, write Codex briefs, review every diff
line-by-line, run the test gate, commit, and open the PR. You do not write
product code. The only exception is a trivial fix-up (≤5 lines, e.g. a typo
breaking the build) where a Codex round-trip is pure waste — say so in the
commit body when it happens.
- Codex = coder. All feature code, tests, and refactors are written by
codex exec. Model and effort are pinned per invocation (see Config) — never
rely on config defaults silently, or a codex upgrade will change your
builds out from under you.
Config (resolve once, at the top of the run)
| Variable |
How to resolve |
Default |
MODEL |
--model arg, else $CODEX_BUILD_MODEL, else your Codex config default |
pin one you have access to (see below) |
EFFORT |
second positional arg, else $CODEX_BUILD_EFFORT |
high (opt into xhigh for hard/architectural work) |
TRACKER |
detect: bd on PATH → beads, else markdown |
markdown |
Pin a model. Run codex exec --help / check ~/.codex/config.toml for what
your account exposes (e.g. gpt-5-codex, gpt-5.6-codex). Announce the resolved
MODEL/EFFORT to the user before starting so the run is reproducible. If the
CLI rejects xhigh, fall back to high and tell the user.
Arguments
codex-build <plan-file> [effort] [--model <name>]
<plan-file> — a Markdown plan with an ordered task list (T1..Tn), each
task naming its files, constraints, and test commands. See
references/plan-example.md for the shape. If the
plan has prose but no task list, extract one and show it to the user before
coding.
[effort] — high (default) | xhigh.
Step 0 — Preflight (once)
codex --version. If Codex isn't authed, codex exec "reply OK" -s read-only
fails — stop and tell the user to run codex login.
- Confirm the pinned model + effort are accepted (cheap, no side effects):
codex exec "Reply with the single word READY" \
-m "$MODEL" -c model_reasoning_effort="$EFFORT" -s read-only --ephemeral < /dev/null
If xhigh is rejected, fall back to high and tell the user.
Always redirect < /dev/null on every codex exec (see Rails) — without
it, a non-interactive run hangs forever on "Reading additional input from
stdin…" instead of doing the work.
- Read the plan end-to-end. Extract: task list, dependency order, per-task test
commands, guardrails (things the plan bans), branch/PR target.
- Resolve the current checkout and look for durable run state before applying
a clean-worktree rule:
REPO_ROOT = git rev-parse --show-toplevel.
GIT_DIR = git rev-parse --absolute-git-dir (this is worktree-specific).
SKILL_DIR = the directory containing this SKILL.md.
- State root =
$GIT_DIR/codex-build/. This keeps orchestration state off the
product diff while making it survive context compaction and process restarts.
- If
current points to an active run for this branch, read its run.md,
tasks.md, interfaces.md, and active allowlist before doing anything else.
Confirm its last recorded commit still matches HEAD; stop on a mismatch
instead of guessing. An in-flight task may legitimately have a dirty
worktree: immediately run its saved scope check before any new Codex call.
If current describes another branch or an inconsistent run, stop rather
than overwriting it.
- For a new run only, inspect
git status --porcelain. The task loop starts
clean so its scope check can attribute every changed path to the active task.
If the checkout is dirty, preserve it and create an isolated worktree from
the intended base (or stop and ask); never discard or absorb pre-existing
work.
- For a new run, create/checkout the working branch per the plan. Never work
on the default branch. If an isolated worktree changed the checkout,
recompute
REPO_ROOT and GIT_DIR.
- For a new run, create
runs/<run-id>/, write the run id to current, and
create run.md, tasks.md, interfaces.md, and allowlists/. run.md
records the plan path and content hash, branch/base, model, effort, tracker,
and run status. Set STATE_DIR to that run directory. Never put secrets in
run state. For a resumed run, set STATE_DIR from current instead.
- Initialize or reload the tracker (see below) and show the user the ordered
task list before starting — one short message, no approval gate unless the
order is ambiguous.
The durable layout is:
$GIT_DIR/codex-build/
current
runs/<run-id>/
run.md
tasks.md
interfaces.md
allowlists/T1.txt
Tracker (unit of work = one task)
The loop is identical regardless of tracker; only the bookkeeping commands differ.
markdown (default, zero install): keep the - [ ] / - [x] checklist in
the run's durable tasks.md. "Claim" = note the task in-flight; "close" =
check the box with the commit hash and test evidence.
beads (optional, if bd is on PATH): richer dependency tracking.
bd prime for context; one bead per task (bd create, title = task title,
description = plan excerpt + file paths, dependencies per the plan); claim with
bd update <id> --claim; close with bd close <id>. Mirror task status and
commit/test evidence to durable tasks.md so a resumed run has one local
recovery record. Install:
https://github.com/steveyegge/beads.
Step 1 — Materialize tasks
Turn the plan's task list into tracker units in dependency order. Each unit
records: goal, the plan excerpt (verbatim constraints, file paths, schema/copy
blocks), and its declared file scope (the files it is allowed to touch). The
file scope is enforced by an executable check — everything else is out of bounds.
For every task, write its scope to allowlists/<task-id>.txt: one exact,
repo-relative file path per non-empty line. Use / separators; no absolute
paths, directories, ./.., comments, or globs. A rename declares both old and
new paths. If the plan genuinely needs another file, amend the allowlist before
Codex touches it and record the reason in tasks.md; never expand scope merely
because an unexpected path appeared in the diff.
Initialize interfaces.md as the canonical cross-task contract. It starts with
"greenfield — no prior interfaces" and is updated only from committed source in
step 6 below.
Step 2 — Per-task loop (the heart; repeat until the queue is empty)
For each task, in dependency order:
Claim it in the tracker.
For a newly claimed task, confirm the product worktree is clean before
invoking Codex. Durable state is under the Git directory, so it does not make
the worktree dirty. When resuming an already in-flight task, the worktree may
contain its changes; run the saved allowlist check immediately instead.
Brief Codex. Compose a self-contained prompt — Codex sees only what you
give it. Use references/codex-brief.md as the
skeleton. It must carry:
- the task's goal and the verbatim plan excerpt (constraints, file paths,
schema/copy);
- the interfaces ledger (see step 6) — the signatures, types, endpoints,
and file paths that earlier tasks created and this one builds on. Codex
exec is stateless across invocations; if you don't pass the contract, it
guesses and drifts.
- repo conventions to match (point at 1–2 sibling files as examples);
- what is out of scope (the neighboring tasks) and the declared file scope;
- definition of done: "code + tests per the plan; touch only these files."
codex exec "<brief>" -C "$(git rev-parse --show-toplevel)" -s workspace-write \
-m "$MODEL" -c model_reasoning_effort="$EFFORT" < /dev/null
Never use --dangerously-bypass-approvals-and-sandbox. The < /dev/null is
mandatory (see Rails).
Enforce scope, then review the diff yourself. This is your job, not
Codex's.
Test gate — BEFORE any commit. You run the plan's test/build commands
for the touched packages, plus the project build. A task is committable only
when the gate is green. Red → back to step 3 with the failure output in the
resume brief. Never commit red. Never skip the gate because "it's small."
The gate is the reason this skill exists.
Commit — one task = one commit.
- Re-run
check_scope.py immediately before staging. Tests and build tools can
create files too; this final check must still be green.
- Stage only the task's files:
git add <paths>. Never git add -A — it
sweeps up stray files Codex or a tool left behind.
- Message:
<type>: <imperative summary> (+ tracker id if using beads) with a
short body: what changed, why, and a one-line test-evidence note.
- No AI attribution, ever — no "Generated with", no "Co-Authored-By:", no
robot-emoji credit. This overrides any tool's default commit template.
Update the durable interfaces ledger. After the green commit, inspect the
committed source and append the public surface this task created — exact
function signatures, exported types, routes/endpoints, config keys, and file
paths — to interfaces.md. Tag the entry with task id and commit hash. If a
later task changes a contract, append a superseding entry; do not silently
leave stale guidance. Record "No public interface changes" when applicable.
The relevant ledger slice feeds step 2 of the next task and is reloaded after
compaction, keeping Codex from re-inventing or mismatching contracts.
Close the task in the tracker with a one-line note. Persist the status,
tests, and commit hash to tasks.md before giving the user a brief progress
line and moving on.
Step 3 — Finish (after the last task)
- Full-suite gate one final time: all packages + build + any lint/screenshot
scripts the plan names. Green or you don't ship.
git pull --rebase then git push. git status must show up-to-date with
origin. On rebase conflicts, resolve minimally and re-run the gate — never
force-push over shared history.
- Exactly ONE PR for the whole feature, targeting the branch the plan
specifies. Body = plan summary: context, what shipped, test evidence,
out-of-scope/follow-ups. No AI attribution anywhere.
- GitHub:
gh pr create. GitLab: glab mr create. No forge CLI: push the
branch and give the user the compare URL to open the PR themselves.
- Close out the tracker (file follow-ups, close finished units).
- Mark
run.md complete and record the final commit, gate evidence, and PR URL.
- Report: tasks completed/blocked, commit list, PR URL, anything deferred.
Rails
- One task per commit; one PR per plan. No batching, no splitting.
- Disk state is canonical. Chat progress is a rendering of
run.md, tasks.md,
task allowlists, and interfaces.md, never the only copy of run state.
- Every
codex exec gets < /dev/null. With a prompt passed as an argument
and an open stdin, codex exec blocks indefinitely on "Reading additional
input from stdin…" and never runs. Redirecting stdin from /dev/null gives it
immediate EOF so it proceeds. This is the single most common way to make the
whole run silently hang.
- Tests before every commit — non-negotiable.
- Effort stays
high/xhigh. A trivial-mechanical task may drop to high
(never lower); note it.
- If the plan and the user's live instructions conflict, the user wins —
note the deviation in the PR body.
- Secrets never go into briefs, commits, or PR bodies.
- A commit that later tasks built on turned out wrong? Fix forward with a new
task/commit. Don't rewrite pushed history.
See also
scripts/check_scope.py — executable file-scope gate.
references/codex-brief.md — the Codex brief skeleton (the highest-leverage part).
references/plan-example.md — the shape of an input plan.
references/walkthrough.md — a full trace of one task, start to finish.
1---2name: codex-build3description: Orchestrator drives, Codex codes. Execute an approved plan one task at a time: your agent (Claude Code, etc.) sequences the work, briefs OpenAI Codex to write ALL product code, reviews every diff, runs the test gate BEFORE each commit, commits one task per commit, and opens exactly ONE PR at the end. Use when the user says 'codex-build', 'have codex code it', 'you orchestrate, codex codes', or hands over a plan for step-by-step implementation.4license: MIT5---67# codex-build — orchestrator drives, Codex codes89You (the agent running this skill) are the **orchestrator**. A separate model,10**OpenAI Codex** (`codex exec`), is the **coder**. You never write product code;11Codex does. You sequence the work, write the briefs, review every diff, run the12tests, and commit. This division is the whole point: one model with taste and13context guards the gate while another model with cheap, high-effort reasoning14does the typing.1516## Roles (non-negotiable)1718- **Orchestrator = you.** Sequence tasks, write Codex briefs, review every diff19 line-by-line, run the test gate, commit, and open the PR. You do **not** write20 product code. The only exception is a trivial fix-up (≤5 lines, e.g. a typo21 breaking the build) where a Codex round-trip is pure waste — say so in the22 commit body when it happens.23- **Codex = coder.** All feature code, tests, and refactors are written by24 `codex exec`. Model and effort are pinned per invocation (see Config) — never25 rely on config defaults silently, or a `codex` upgrade will change your26 builds out from under you.2728## Config (resolve once, at the top of the run)2930| Variable | How to resolve | Default |31| --- | --- | --- |32| `MODEL` | `--model` arg, else `$CODEX_BUILD_MODEL`, else your Codex config default | pin one you have access to (see below) |33| `EFFORT` | second positional arg, else `$CODEX_BUILD_EFFORT` | `high` (opt into `xhigh` for hard/architectural work) |34| `TRACKER` | detect: `bd` on PATH → `beads`, else `markdown` | `markdown` |3536**Pin a model.** Run `codex exec --help` / check `~/.codex/config.toml` for what37your account exposes (e.g. `gpt-5-codex`, `gpt-5.6-codex`). Announce the resolved38`MODEL`/`EFFORT` to the user before starting so the run is reproducible. If the39CLI rejects `xhigh`, fall back to `high` and tell the user.4041## Arguments4243`codex-build <plan-file> [effort] [--model <name>]`4445- `<plan-file>` — a Markdown plan with an **ordered task list** (T1..Tn), each46 task naming its files, constraints, and test commands. See47 [`references/plan-example.md`](references/plan-example.md) for the shape. If the48 plan has prose but no task list, extract one and show it to the user before49 coding.50- `[effort]` — `high` (default) | `xhigh`.5152## Step 0 — Preflight (once)53541. `codex --version`. If Codex isn't authed, `codex exec "reply OK" -s read-only`55 fails — stop and tell the user to run `codex login`.562. Confirm the pinned model + effort are accepted (cheap, no side effects):57 ```bash58 codex exec "Reply with the single word READY" \59 -m "$MODEL" -c model_reasoning_effort="$EFFORT" -s read-only --ephemeral < /dev/null60 ```61 If `xhigh` is rejected, fall back to `high` and tell the user.62 **Always redirect `< /dev/null`** on every `codex exec` (see Rails) — without63 it, a non-interactive run hangs forever on "Reading additional input from64 stdin…" instead of doing the work.653. Read the plan end-to-end. Extract: task list, dependency order, per-task test66 commands, guardrails (things the plan bans), branch/PR target.674. Resolve the current checkout and look for durable run state *before* applying68 a clean-worktree rule:69 - `REPO_ROOT` = `git rev-parse --show-toplevel`.70 - `GIT_DIR` = `git rev-parse --absolute-git-dir` (this is worktree-specific).71 - `SKILL_DIR` = the directory containing this `SKILL.md`.72 - State root = `$GIT_DIR/codex-build/`. This keeps orchestration state off the73 product diff while making it survive context compaction and process restarts.74 - If `current` points to an active run for this branch, read its `run.md`,75 `tasks.md`, `interfaces.md`, and active allowlist before doing anything else.76 Confirm its last recorded commit still matches `HEAD`; stop on a mismatch77 instead of guessing. An in-flight task may legitimately have a dirty78 worktree: immediately run its saved scope check before any new Codex call.79 If `current` describes another branch or an inconsistent run, stop rather80 than overwriting it.815. For a new run only, inspect `git status --porcelain`. The task loop starts82 clean so its scope check can attribute every changed path to the active task.83 If the checkout is dirty, preserve it and create an isolated worktree from84 the intended base (or stop and ask); never discard or absorb pre-existing85 work.866. For a new run, create/checkout the working branch per the plan. **Never work87 on the default branch.** If an isolated worktree changed the checkout,88 recompute `REPO_ROOT` and `GIT_DIR`.897. For a new run, create `runs/<run-id>/`, write the run id to `current`, and90 create `run.md`, `tasks.md`, `interfaces.md`, and `allowlists/`. `run.md`91 records the plan path and content hash, branch/base, model, effort, tracker,92 and run status. Set `STATE_DIR` to that run directory. Never put secrets in93 run state. For a resumed run, set `STATE_DIR` from `current` instead.948. Initialize or reload the tracker (see below) and show the user the ordered95 task list before starting — one short message, no approval gate unless the96 order is ambiguous.9798The durable layout is:99100```text101$GIT_DIR/codex-build/102 current103 runs/<run-id>/104 run.md105 tasks.md106 interfaces.md107 allowlists/T1.txt108```109110## Tracker (unit of work = one task)111112The loop is identical regardless of tracker; only the bookkeeping commands differ.113114- **`markdown` (default, zero install):** keep the `- [ ]` / `- [x]` checklist in115 the run's durable `tasks.md`. "Claim" = note the task in-flight; "close" =116 check the box with the commit hash and test evidence.117- **`beads` (optional, if `bd` is on PATH):** richer dependency tracking.118 `bd prime` for context; one bead per task (`bd create`, title = task title,119 description = plan excerpt + file paths, dependencies per the plan); claim with120 `bd update <id> --claim`; close with `bd close <id>`. Mirror task status and121 commit/test evidence to durable `tasks.md` so a resumed run has one local122 recovery record. Install:123 <https://github.com/steveyegge/beads>.124125## Step 1 — Materialize tasks126127Turn the plan's task list into tracker units in dependency order. Each unit128records: goal, the plan excerpt (verbatim constraints, file paths, schema/copy129blocks), and its **declared file scope** (the files it is allowed to touch). The130file scope is enforced by an executable check — everything else is out of bounds.131132For every task, write its scope to `allowlists/<task-id>.txt`: one exact,133repo-relative file path per non-empty line. Use `/` separators; no absolute134paths, directories, `.`/`..`, comments, or globs. A rename declares both old and135new paths. If the plan genuinely needs another file, amend the allowlist *before*136Codex touches it and record the reason in `tasks.md`; never expand scope merely137because an unexpected path appeared in the diff.138139Initialize `interfaces.md` as the canonical cross-task contract. It starts with140"greenfield — no prior interfaces" and is updated only from committed source in141step 6 below.142143## Step 2 — Per-task loop (the heart; repeat until the queue is empty)144145For each task, in dependency order:1461471. **Claim** it in the tracker.148149 For a newly claimed task, confirm the product worktree is clean before150 invoking Codex. Durable state is under the Git directory, so it does not make151 the worktree dirty. When resuming an already in-flight task, the worktree may152 contain its changes; run the saved allowlist check immediately instead.1531542. **Brief Codex.** Compose a *self-contained* prompt — Codex sees only what you155 give it. Use [`references/codex-brief.md`](references/codex-brief.md) as the156 skeleton. It must carry:157 - the task's goal and the verbatim plan excerpt (constraints, file paths,158 schema/copy);159 - the **interfaces ledger** (see step 6) — the signatures, types, endpoints,160 and file paths that earlier tasks created and this one builds on. Codex161 `exec` is stateless across invocations; if you don't pass the contract, it162 guesses and drifts.163 - repo conventions to match (point at 1–2 sibling files as examples);164 - what is **out of scope** (the neighboring tasks) and the declared file scope;165 - definition of done: "code + tests per the plan; touch only these files."166 ```bash167 codex exec "<brief>" -C "$(git rev-parse --show-toplevel)" -s workspace-write \168 -m "$MODEL" -c model_reasoning_effort="$EFFORT" < /dev/null169 ```170 Never use `--dangerously-bypass-approvals-and-sandbox`. The `< /dev/null` is171 mandatory (see Rails).1721733. **Enforce scope, then review the diff yourself.** This is your job, not174 Codex's.175 - **Scope check first:** after every Codex invocation or correction, run:176 ```bash177 python3 "$SKILL_DIR/scripts/check_scope.py" \178 --repo "$REPO_ROOT" \179 --allowlist "$STATE_DIR/allowlists/<task-id>.txt"180 ```181 The checker compares tracked, staged, deleted, renamed, and non-ignored182 untracked paths against the on-disk allowlist. A non-zero exit is a hard183 stop: correct the drift or deliberately amend the allowlist with a reason.184 Do not rely on visual review to enforce scope.185 - Then inspect `git diff --stat` and the diff itself. For a small diff, every186 line. For a large diff, read the interfaces and the risky seams in full,187 skim the mechanical parts, and run `codex exec review` as an independent188 second pass — but *you* make the call, never the review tool.189 - Check: scope creep, convention drift, guardrail violations, and whether the190 **tests Codex claims to have written actually exist and actually assert191 something** (open them). "Added tests" in Codex's summary is a claim, not192 evidence.193 - Issues → iterate: `codex exec resume --last "<specific corrections>" < /dev/null`194 (same `-m`/`-c` flags). Be concrete about what's wrong; vague corrections195 waste a round-trip.196 - **Three failed iterations on one task → STOP.** Report the impasse to the197 user with the diff and your diagnosis. Do **not** silently take over the198 coding — that breaks the contract and hides the failure.1992004. **Test gate — BEFORE any commit.** *You* run the plan's test/build commands201 for the touched packages, plus the project build. A task is committable only202 when the gate is green. Red → back to step 3 with the failure output in the203 resume brief. **Never commit red. Never skip the gate because "it's small."**204 The gate is the reason this skill exists.2052065. **Commit — one task = one commit.**207 - Re-run `check_scope.py` immediately before staging. Tests and build tools can208 create files too; this final check must still be green.209 - Stage only the task's files: `git add <paths>`. **Never `git add -A`** — it210 sweeps up stray files Codex or a tool left behind.211 - Message: `<type>: <imperative summary>` (+ tracker id if using beads) with a212 short body: what changed, why, and a one-line test-evidence note.213 - **No AI attribution, ever** — no "Generated with", no "Co-Authored-By:", no214 robot-emoji credit. This overrides any tool's default commit template.2152166. **Update the durable interfaces ledger.** After the green commit, inspect the217 committed source and append the public surface this task created — exact218 function signatures, exported types, routes/endpoints, config keys, and file219 paths — to `interfaces.md`. Tag the entry with task id and commit hash. If a220 later task changes a contract, append a superseding entry; do not silently221 leave stale guidance. Record "No public interface changes" when applicable.222 The relevant ledger slice feeds step 2 of the next task and is reloaded after223 compaction, keeping Codex from re-inventing or mismatching contracts.2242257. **Close** the task in the tracker with a one-line note. Persist the status,226 tests, and commit hash to `tasks.md` before giving the user a brief progress227 line and moving on.228229## Step 3 — Finish (after the last task)2302311. **Full-suite gate** one final time: all packages + build + any lint/screenshot232 scripts the plan names. Green or you don't ship.2332. `git pull --rebase` then `git push`. `git status` must show up-to-date with234 origin. On rebase conflicts, resolve minimally and re-run the gate — never235 force-push over shared history.2363. **Exactly ONE PR** for the whole feature, targeting the branch the plan237 specifies. Body = plan summary: context, what shipped, test evidence,238 out-of-scope/follow-ups. No AI attribution anywhere.239 - GitHub: `gh pr create`. GitLab: `glab mr create`. No forge CLI: push the240 branch and give the user the compare URL to open the PR themselves.2414. Close out the tracker (file follow-ups, close finished units).2425. Mark `run.md` complete and record the final commit, gate evidence, and PR URL.2436. Report: tasks completed/blocked, commit list, PR URL, anything deferred.244245## Rails246247- One task per commit; one PR per plan. No batching, no splitting.248- Disk state is canonical. Chat progress is a rendering of `run.md`, `tasks.md`,249 task allowlists, and `interfaces.md`, never the only copy of run state.250- **Every `codex exec` gets `< /dev/null`.** With a prompt passed as an argument251 and an open stdin, `codex exec` blocks indefinitely on "Reading additional252 input from stdin…" and never runs. Redirecting stdin from `/dev/null` gives it253 immediate EOF so it proceeds. This is the single most common way to make the254 whole run silently hang.255- Tests before every commit — non-negotiable.256- Effort stays `high`/`xhigh`. A trivial-mechanical task may drop to `high`257 (never lower); note it.258- If the plan and the user's live instructions conflict, the **user wins** —259 note the deviation in the PR body.260- Secrets never go into briefs, commits, or PR bodies.261- A commit that later tasks built on turned out wrong? Fix forward with a new262 task/commit. Don't rewrite pushed history.263264## See also265266- [`scripts/check_scope.py`](scripts/check_scope.py) — executable file-scope gate.267- [`references/codex-brief.md`](references/codex-brief.md) — the Codex brief skeleton (the highest-leverage part).268- [`references/plan-example.md`](references/plan-example.md) — the shape of an input plan.269- [`references/walkthrough.md`](references/walkthrough.md) — a full trace of one task, start to finish.