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 --versionCheck 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 = falseThe 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.logWith 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
- Install Codex and confirm the version.
- Write
~/.codex/lane.config.tomlwith the top level keys above the table, and swap the writable roots for the cache paths your own test command needs. - Symlink
AGENTS.mdtoCLAUDE.mdin the repo and commit it. - 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.
- Create a git worktree on a throwaway branch and run the
codex execline above against a tiny pack. - 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