Codex CLI Tutorial: One Bounded Repo Task, End to End

Run one complete Codex CLI task — inspect, plan, change, review the diff, verify with a test, then resume or stop — without widening scope.

October 24, 2026
codex-clitutorialworkflowverificationcode-review

The task

Add email validation to a single API handler. The change is small enough to finish in one session and real enough to exercise every step: the agent must find the handler, understand the existing error shape, edit one file, add one test, and prove the test fails before the fix and passes after it.

Assumed shape — adjust paths to your repo:

src/
  api/
    handlers/
      create-user.ts
      create-user.test.ts

The rule for the session: one handler, one test file. Anything else the agent wants to touch is a separate task.

Before you start

git status --short
git switch -c codex/email-validation

Start from a clean tree. If there are unrelated changes, stash them; a mixed diff makes the review step meaningless. Codex edits the working tree, and git diff is your audit surface.

Inspect

Start an interactive session in the repository root:

codex
Read src/api/handlers/create-user.ts and its test file. Report: the validation that already exists, the error response shape, the test runner command, and any shared validation helper I should reuse instead of writing a new one. Do not edit files.

The last sentence matters. Inspection prompts drift into edits when the agent finds something obviously broken. Keep the first turn read-only so you can agree on the target before anything changes.

If the answer cites a helper, open it. The most common wasted run is an agent reimplementing a validator that already exists two directories away.

Read the inspection output

The first answer is a map, not a verdict. Check three things before you accept it:

  • Does the named test command exist? Run it once yourself. If it fails on a clean checkout, the agent will spend the session chasing a broken baseline.
  • Does the error shape match production? Validation changes often keep the status code but change the body. Read the handler's existing branches rather than trusting the summary.
  • Is there a helper already? If a validator exists in src/lib, the correct change is usually one import, not a new function.

If any answer is wrong, correct it in the same session before planning. A plan built on a wrong map produces a diff that looks plausible and fails review.

Plan

Plan the change. Return exactly: the file you will edit, the lines you will add, the test case you will add, the command that runs that test, and what should happen for input "user@example". Do not edit yet.

A usable plan names the file and the test command. A plan that says "add validation and tests" is not a plan; it is a restatement of the request.

Check one thing before approving: does the plan preserve the existing error shape? A validation change that returns a different status code or body shape is an API change, not a bug fix.

Change

Implement the plan. Only edit src/api/handlers/create-user.ts and src/api/handlers/create-user.test.ts. Run only the test file for create-user. Stop after the test result and show me the diff.

Three constraints in one prompt — file scope, test scope, stop condition. Without the stop condition, the agent will often continue into adjacent cleanup.

The documented permission boundary for this kind of work is the Auto preset: workspace-write with on-request approvals. Codex can edit inside the workspace and run the test; it must ask before leaving the workspace or using the network.

Review the diff

git diff
TODO: capture from real run — paste the `git diff` for the handler and the test from the completed session, trimmed to the two changed files.

Read the test before the implementation. A test that asserts the mock was called, rather than the response status and body, does not verify the validation. The test should include at least one rejected input and one accepted input.

Verify

npm test -- create-user.test.ts
TODO: capture from real run — paste the full test-run output, including the command and the pass/fail summary.

If the test passes on the first try, prove it can fail: temporarily loosen the validation, rerun, and confirm the test catches it. A test that has never failed is an untested assertion. Revert the loosening before you continue.

For a machine-checkable record of what the agent ran, the same task can be executed non-interactively:

codex exec --sandbox workspace-write --json \
  "Implement the plan and run npm test -- create-user.test.ts. Stop after the test result." \
  | tee codex-run.jsonl

--json streams one JSON object per state change, including command executions and the final message. That file is the evidence trail; the summary is not.

Resume or stop

Stop when the diff contains only the planned change and the test fails without it. Commit:

git add src/api/handlers/create-user.ts src/api/handlers/create-user.test.ts
git commit -m "Validate email in create-user handler"

Resume when the session ended mid-task, not when the task failed conceptually. Continuing a session whose plan was wrong just carries the wrong plan forward.

codex resume

For non-interactive runs, resume the last session in the current directory:

codex exec resume --last "Finish the remaining test case and rerun the file."

If the remaining work is a different task — a second handler, a refactor of the validator — start a new session. One session, one change.

Session hygiene

Three habits keep a small task small:

  • One session per change. A resumed session keeps the earlier plan in context. If the plan was wrong, starting fresh is cheaper than arguing with the old context.
  • Interrupt early. When the first tool call is outside the agreed file, stop the turn instead of waiting for the summary. Correcting at the first step costs one message; correcting after ten edits costs a revert.
  • Keep the evidence. Save codex-run.jsonl from non-interactive runs and keep the test output with the commit or pull request. The transcript is the only record of what the agent actually executed.

For repo-wide conventions that should apply to every session, put them in AGENTS.md rather than repeating them in prompts. The prompt engineering page covers the discovery order; the configuration page covers the sandbox and approval settings this tutorial assumes.

What goes wrong

Three failures account for most first attempts. Record which one you hit and what corrected it, rather than smoothing it out of the write-up.

  • Scope creep. The agent edits a shared validator or a sibling handler. Correction: stop the run, revert the extra files, and restate the file fence. If the shared helper genuinely needs the change, that is a separate task with its own test.
  • A test that cannot fail. The assertion checks that the handler was called, not what it returned. Correction: assert status and body for one rejected and one accepted input, then prove the test fails against the unpatched handler.
  • Sandbox denial. The test command needs a package fetch and the run stops on network. Correction: enable [sandbox_workspace_write] network_access = true for that session, or pre-install dependencies before the run. Do not reach for danger-full-access to avoid one approval.

The write-up of a failure is more useful than a clean narrative. Paste the actual error, the actual diff, and the correction — not a paraphrase.

Running the same task under another harness is a fair comparison: Claude Code and OpenCode have equivalent guides, and the tool comparison covers the differences that matter for a single bounded change.

Sources