Home / Learn / The Orchestrator Course / Hire the hands: Codex

Module 3 of 711 min readUpdated 2026-09-10

Hire the hands: Codex

Install Codex CLI, write the lane profile as its own toml file, symlink AGENTS.md to CLAUDE.md, and run one lane with a strict JSON report you can trust.

  • Free, no signup

Why Codex is worth the setup

Codex runs on the ChatGPT plan I already pay for. Codex gpt-5.6-sol and Codex gpt-5.6-terra are calibrated on my repo for scoped builds: three of three tickets green, zero P1 findings, once the pack named the repo rules. It is the cheapest strong hand I have.

The setup has three sharp edges. Each one cost me time. They are below.

Install

npm i -g @openai/codex
codex --version

Check the vendor docs for the current command. Mine is build 0.153.4, and every flag on this page is verified against that build.

Edge one: a profile is a separate file

A [profiles.lane] table inside ~/.codex/config.toml is rejected as legacy config. The profile has to be its own file, selected by name.

Write ~/.codex/lane.config.toml:

model = "gpt-5.6-terra"
model_reasoning_effort = "medium"
model_verbosity = "low"
approval_policy = "never"
sandbox_mode = "workspace-write"
hide_agent_reasoning = true
project_doc_fallback_filenames = ["CLAUDE.md"]

[sandbox_workspace_write]
writable_roots = [
  "/Users/you/Library/Developer/Xcode/DerivedData",
  "/Users/you/.cache/clang",
  "/Users/you/Library/Caches/org.swift.swiftpm",
]
network_access = false

The key ordering is load bearing. Every top level key must come before the [sandbox_workspace_write] table, or TOML swallows it as a table key and the setting silently does nothing. The whole file is the profile, so the table header carries no prefix.

Select it with --profile lane.

For the hard lanes I keep a second file, ~/.codex/astra.config.toml, with model = "gpt-6-astra" and model_reasoning_effort = "high". My runner picks it up from CODEX_PROFILE=astra.

Edge two: Codex reads fixed filenames only

Codex loads AGENTS.md. It never follows a prose pointer. My old AGENTS.md was fourteen lines saying "read CLAUDE.md", and it transferred exactly zero rules.

The fix is one line, committed to the repo:

ln -s CLAUDE.md AGENTS.md
git add AGENTS.md && git commit -m "AGENTS.md: symlink to CLAUDE.md so Codex loads the repo rules"

After that, a Codex lane quoted my no em dash rule back to me verbatim, with zero file reads. Do not replace the symlink with a copy: the two would drift, and Codex only reads one of them.

Edge three: the sandbox cannot run your test suite

In workspace-write mode the writable set is the working directory plus the temp dirs. My test script writes to Xcode's DerivedData and the clang module cache, which sit outside that. So Codex cannot run XCTest at all: the XPC call is refused.

I do not lower the sandbox. Instead the lane pack says so in plain words, and the runner owns the tests. A compile only build works in the sandbox, and that is enough for the agent to catch its own syntax errors.

Run one lane

codex exec \
  --profile lane \
  -C "$WORKTREE" \
  --sandbox workspace-write \
  --json \
  --output-schema lane-report.schema.json \
  -o last-message.json \
  < lane-pack.md > events.jsonl 2> stderr.log

With no positional prompt, Codex reads the pack from stdin. --output-schema forces the final message to be one strict JSON object: status, files changed, the test command it ran, the test result, one boolean per acceptance criterion, and its unknowns. Every object level in that schema needs additionalProperties set to false, or the run dies before any work happens.

The exit code is a turn level signal. A run can exit 0 with a failed step inside it, so parse the events file, not just the code.

One more rule: a kickback is always a fresh codex exec with a new pack and the new numbers. Never codex exec resume. A resumed lane argues with its own earlier reasoning.

Set it up

  1. Install Codex and confirm the version.
  2. Write ~/.codex/lane.config.toml with the top level keys above the table, and swap the writable roots for the cache paths your own test command needs.
  3. Symlink AGENTS.md to CLAUDE.md in the repo and commit it.
  4. Prove discovery: run a one line lane that asks Codex to quote back one specific rule from your CLAUDE.md. If it cannot, the symlink is wrong.
  5. Create a git worktree on a throwaway branch and run the codex exec line above against a tiny pack.
  6. Read last-message.json. Confirm it is one JSON object and that the test result is honest about what the sandbox could not do.

What you can do now

You can run one scoped Codex lane in a worktree and get back a machine readable report you can gate on.

Next module

Module 4 adds the second set of hands: OpenCode Go, a flat monthly plan with several models behind one command.

Talk to Claude instead of typing to it.

Ducktate turns what you say into clean text, on your Mac, and pastes it anywhere.

Get early access