$catMANUAL||~47 min

Codex CLI Subagents in the Wild: Three Real Workflows I Ran This Week

advertisement

I almost missed it. OpenAI pushed a chunky subagent feature into the Codex CLI on July 6, and I only noticed because codex features list spat out some new flags I'd never seen before. The blog post never came. The changelog was quiet. But the docs got rewritten, and once I started reading, I couldn't stop.

Subagents sound like buzzword territory. "AI agent teams" has been a phrase on every other pitch deck for months. But this one actually works, and the workflow is concrete enough that I want to show you exactly what happened when I ran it on three different jobs.

If you write Codex CLI tutorials for a living, this is the post to read. If you don't, this is the one that'll save you six hours next time you have to review a PR.

What Subagents Actually Are

In plain English: you tell Codex to do a thing, and it spins up specialized child agents to do parts of the thing in parallel. Each child has its own model, its own tool surface, its own context window. The parent waits for everyone, then hands you a consolidated answer.

This is a big deal because most of the AI coding work I do isn't one-thread. Look at what I actually got stuck on last week:

  • PR review. I want to know about security, code quality, test coverage, and API accuracy at the same time. Single-thread has to pick one.
  • UI debugging. I need someone to reproduce the bug in the browser, someone to trace the code path, and eventually someone to make the fix. Those three steps can start in parallel.
  • Bulk auditing. I have 32 components, each needs a risk review. One agent doing them in series takes 45 minutes.

The trade-off is real, though: subagent runs eat roughly 5-6x the tokens of a comparable single-agent run. That's not a bug. That's how you pay for context isolation between children. If you go in knowing that, the rest makes sense.

The Three Built-in Agents

Codex ships three out of the box, and you don't need to configure them:

  • default — general-purpose fallback, can do almost anything
  • worker — execution-focused, used for implementation and fixes
  • explorer — read-heavy codebase exploration

To trigger them, you have to be explicit. Subagents only spawn when you ask. In the interactive TUI, you'd say something like:

text
1
I would like to review the following points on the current PR (this branch vs main).
2
Spawn one agent per point, wait for all of them, and summarize the result for each point.
3
1. Security issue
4
2. Code quality
5
3. Bugs
6
4. Race
7
5. Test flakiness
8
6. Maintainability of the code

The magic phrase is "spawn one agent per point, wait for all of them." Drop that and Codex will just do it serially. Once the subagents are running, you can use /agent in the TUI to bounce between threads and see what each one is doing.

Writing Your Own Custom Agents

The built-ins are fine for a while, but you'll outgrow them fast. The first time I wanted an agent that "only checks documentation, never touches code," I had to write one.

File Locations

text
1
~/.codex/agents/    # personal agents, available across all projects
2
.codex/agents/      # project-scoped agents, only loaded in this repo

Each agent is a single .toml file. The minimum is three fields:

toml
1
name = "reviewer"
2
description = "PR reviewer focused on correctness, security, and missing tests."
3
developer_instructions = """
4
Review code like an owner.
5
Prioritize correctness, security, behavior regressions, and missing test coverage.
6
"""

Optional fields you'll actually use:

  • model — pick from gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex-spark
  • model_reasoning_effortlow / medium / high / extra high
  • sandbox_moderead-only / workspace-write / danger-full-access
  • mcp_servers — agent-specific MCP server config
  • skills.config — load specific skill files
  • nickname_candidates — list of display names; I always set this because three "reviewer" threads in the TUI are a nightmare

Global Limits

No matter how many agents you define, the hard caps live in config.toml under [agents]:

toml
1
[agents]
2
max_threads = 6           # concurrent open agent threads, default 6
3
max_depth = 1             # nesting depth, root session starts at 0, default 1
4
job_max_runtime_seconds = 1800  # default per-worker timeout for spawn_agents_on_csv

max_depth = 1 means the root can spawn children, but children can't spawn grandchildren. I bumped it to 3 once to test recursive delegation. The token bill was ugly. Don't do that unless you have a very specific reason.

Workflow 1: PR Review, Three Subagents in Parallel

This is the one the official docs lead with, and it works. I ran it against my own repo on Tuesday.

Project Config

.codex/config.toml:

toml
1
[agents]
2
max_threads = 6
3
max_depth = 1

.codex/agents/pr-explorer.toml — a cheap explorer to map the affected code:

toml
1
name = "pr_explorer"
2
description = "Read-only codebase explorer for gathering evidence before changes are proposed."
3
model = "gpt-5.3-codex-spark"
4
model_reasoning_effort = "medium"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Stay in exploration mode.
8
Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them.
9
Prefer fast search and targeted file reads over broad scans.
10
"""

The model choice here matters. gpt-5.3-codex-spark is the fast one; the explorer is just reading code and citing files. I don't need GPT-5.5 for that. The sandbox_mode = "read-only" is the load-bearing line — it physically cannot edit files, which is exactly what I want from a subagent whose job is "gather evidence."

.codex/agents/reviewer.toml — the strict reviewer:

toml
1
name = "reviewer"
2
description = "PR reviewer focused on correctness, security, and missing tests."
3
model = "gpt-5.4"
4
model_reasoning_effort = "high"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Review code like an owner.
8
Prioritize correctness, security, behavior regressions, and missing test coverage.
9
Lead with concrete findings, include reproduction steps when possible, and avoid style-only comments unless they hide a real bug.
10
"""

I'm spending gpt-5.4 with high reasoning here. The reviewer's job is to find real problems, and a cheaper model has a bad habit of waving through bugs because they look plausible. Worth the cost.

.codex/agents/docs-researcher.toml — a documentation lookup specialist:

toml
1
name = "docs_researcher"
2
description = "Documentation specialist that uses the docs MCP server to verify APIs and framework behavior."
3
model = "gpt-5.4-mini"
4
model_reasoning_effort = "medium"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Use the docs MCP server to confirm APIs, options, and version-specific behavior.
8
Return concise answers with links or exact references when available.
9
Do not make code changes.
10
"""
11
 
12
[mcp_servers.openaiDeveloperDocs]
13
url = "https://developers.openai.com/mcp"

This one is wired to the OpenAI developer docs MCP server. Cheap model, narrow job, and a hard rule: no code changes.

The Launch Prompt

In the TUI:

text
1
Review this branch against main.
2
Have pr_explorer map the affected code paths,
3
reviewer find real risks,
4
and docs_researcher verify the framework APIs that the patch relies on.

What Happened

I watched the TUI spin up three threads. The numbers:

  • pr_explorer came back in about 30 seconds with a list of affected files, key functions, and call sites
  • reviewer took 1-2 minutes with concrete findings, including one race condition I had missed
  • docs_researcher was fastest at around 20 seconds, returning API verification with reference links

The parent thread consolidated them into a single review report. If any child had hung, I would have used /agent to jump in and inspect.

What I Changed in My Version

The official demo uses an English prompt. I tried the same prompt in Chinese once and it sort of worked, but I noticed the agents drifting across boundaries — reviewer would occasionally go verify a doc reference that was clearly docs_researcher's job. The boundary got fuzzy when the prompt language didn't match the developer_instructions language.

The fix was to make the descriptions mutually exclusive. I added "Do not check documentation; rely on docs_researcher for that" to reviewer's instructions. That removed the overlap and the parent dispatcher stopped confusing them.

One more thing: if you want a child to be strictly read-only, lock it down in sandbox_mode, not in developer_instructions. Models forget instructions. Sandbox enforcement is a property of the system, not the prompt.

Workflow 2: Frontend UI Debugging, Browser + Code + Fixer

PR review is read-only. To actually fix a bug you need some children to be able to write files. The second official demo covers this.

.codex/agents/code-mapper.toml:

toml
1
name = "code_mapper"
2
description = "Read-only codebase explorer for locating the relevant frontend and backend code paths."
3
model = "gpt-5.4-mini"
4
model_reasoning_effort = "medium"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Map the code that owns the failing UI flow.
8
Identify entry points, state transitions, and likely files before the worker starts editing.
9
"""

.codex/agents/browser-debugger.toml:

toml
1
name = "browser_debugger"
2
description = "UI debugger that uses browser tooling to reproduce issues and capture evidence."
3
model = "gpt-5.4"
4
model_reasoning_effort = "high"
5
sandbox_mode = "workspace-write"
6
developer_instructions = """
7
Reproduce the issue in the browser, capture exact steps, and report what the UI actually does.
8
Use browser tooling for screenshots, console output, and network evidence.
9
Do not edit application code.
10
"""
11
 
12
[mcp_servers.chrome_devtools]
13
url = "http://localhost:3000/mcp"
14
startup_timeout_sec = 20

workspace-write lets it save screenshots and logs, but the developer_instructions explicitly forbid touching application code. This is a "give access but constrain intent" pattern. Trust the model a little, but not all the way.

.codex/agents/ui-fixer.toml:

toml
1
name = "ui_fixer"
2
description = "Implementation-focused agent for small, targeted fixes after the issue is understood."
3
model = "gpt-5.3-codex-spark"
4
model_reasoning_effort = "medium"
5
developer_instructions = """
6
Own the fix once the issue is reproduced.
7
Make the smallest defensible change, keep unrelated files untouched, and validate only the behavior you changed.
8
"""

ui_fixer doesn't override sandbox_mode, so it inherits the parent session's policy. The model is fast because "make a small fix" is not a hard reasoning task.

The Launch Prompt

text
1
Investigate why the settings modal fails to save.
2
Have browser_debugger reproduce it,
3
code_mapper trace the responsible code path,
4
and ui_fixer implement the smallest fix once the failure mode is clear.

What Happened

I ran this against a real Next.js project where the settings page "Save" button was silently broken.

  1. browser_debugger opened Chrome via the DevTools MCP, clicked Save, captured screenshots, and confirmed the bug
  2. code_mapper traced the call chain and pinpointed the onSave handler in app/settings/page.tsx
  3. The parent decided ui_fixer should take a swing
  4. ui_fixer made a 4-line change, and Codex ran lint + tests as verification

End to end: 3-4 minutes. If I'd asked a single agent to do the same work, I'd estimate 8+ minutes, with a much higher chance of the agent forgetting the reproduction step once it started editing.

Things That Went Wrong

Chrome DevTools MCP startup. I set startup_timeout_sec = 20 and it was way too tight. The MCP server has to wait for Vite to finish its first compile, and on a cold start that takes longer than 20 seconds in my project. I bumped it to 60 and the failures stopped. If your startup time is even longer, you'll need an actual health check.

Sandbox race conditions. When workspace-write and read-only agents read the same file at the same time, the read-only one can occasionally see a half-written version. I haven't seen data corruption, but I have seen warnings in the TUI.

MCP inheritance is fragile. The docs say child agents inherit the parent's MCP servers. Sometimes they do. I have a non-zero failure rate where they don't, and the error surfaces as a generic "tool not found." Workaround: declare every MCP server in every agent file that needs it. Verbose, but reliable.

Workflow 3: Bulk Auditing with spawn_agents_on_csv

The first two workflows are "few agents, big jobs." Codex also ships a dedicated tool for "many agents, small jobs": spawn_agents_on_csv.

You give it a CSV. It reads one row per item, spawns one worker per row, and dumps the consolidated results into another CSV. Each worker has to call report_agent_job_result exactly once, or the row gets marked as an error in the output.

The Parameters

  • csv_path — source CSV
  • instruction — worker prompt template, with {column_name} placeholders
  • id_column — which column provides stable item IDs
  • output_schema — required JSON shape per worker
  • output_csv_path — where the result CSV lands
  • max_concurrency — fan-out cap
  • max_runtime_seconds — per-worker timeout

Real Example

I had a CSV of 32 components I wanted risk-scored:

csv
1
path,owner
2
src/components/Settings.tsx,alice
3
src/components/Profile.tsx,bob
4
src/components/Dashboard.tsx,carol

The prompt I used:

text
1
Create /tmp/components.csv with columns path,owner and one row per frontend component.
2
 
3
Then call spawn_agents_on_csv with:
4
- csv_path: /tmp/components.csv
5
- id_column: path
6
- instruction: "Review {path} owned by {owner}. Return JSON with keys path, risk, summary, and follow_up via report_agent_job_result."
7
- output_csv_path: /tmp/components-review.csv
8
- output_schema: an object with required string fields path, risk, summary, and follow_up

Run with codex exec (non-interactive). The stderr shows single-line progress:

text
1
[12/50] reviewing src/components/Dashboard.tsx

The output CSV looks like this (truncated):

csv
1
path,owner,job_id,item_id,status,last_error,result_json
2
src/components/Settings.tsx,alice,job-abc,path,success,,"{""path"":""src/components/Settings.tsx"",""risk"":""low"",""summary"":""..."",""follow_up"":""...""}"

Notice result_json is a quoted string, not a real JSON column. You'll need to json.loads() it yourself if you're piping the CSV into another tool.

The Actual Numbers

I ran this against my 32-component project. Some honest measurements:

  • Single agent, serial: 45 minutes, ~310k tokens
  • spawn_agents_on_csv with max_concurrency=6: 9 minutes, ~1.8M tokens

So I traded about 5x the tokens for 5x the speed. That's the deal. For some jobs, it's a great deal. For others, it isn't.

Things I Wish I'd Known Earlier

Write output_schema strictly. If you say the schema requires path, risk, summary, and follow_up, and a worker omits one, that row is marked as an error. I'd rather have a slightly less rich but always-valid result than chase down why half my rows are red.

Don't set max_concurrency higher than max_threads. They'll fight. The CSV tool will queue workers beyond the cap, but you'll waste time waiting when you could have just lowered the concurrency.

Watch your CSV size. I tried this with 500 rows and Codex OOM'd during the planning step. 200 rows is comfortable on a 16GB machine. Above that, chunk it.

The result CSV's result_json column is escaped JSON-as-string. Don't trust your eyes when reading the CSV. Parse it.

Things I Haven't Figured Out Yet

I want to flag a few open questions for anyone going deeper than I have:

  1. MCP inheritance in child agents. The docs say children inherit parent MCP servers. I have a flaky success rate. Could be a bug, could be that I'm misconfiguring something.
  2. The real cost of max_depth > 1. Recursive delegation. I have not had the courage (or the budget) to test this on a non-trivial task.
  3. Session recovery after a child crash. codex resume works for single-agent sessions. What happens when three children crash and you want to resume? I haven't tested.
  4. GPT-5.3-Codex-Spark quality vs GPT-5.4-mini in subagent context. The marketing says "extra fast." In my runs they're close in speed and close in quality. I haven't been able to make a confident call.

If you've got answers to any of these, drop them in the comments. I check there.

Should You Actually Use Subagents?

Honest answer: probably not, for most of your work.

If your day-to-day is "edit one file" or "ask a quick question," single-thread is fine. Subagents are worth it when the task naturally has N independent branches and the wall-time savings beat the token cost.

My personal rule of thumb: if you could give the same task to three interns and have them work in parallel, subagents will probably help. If the task has hard dependencies between steps, single agent every time.

A more concrete decision tree:

  • Will the task finish in under 5 minutes? → Single agent. Don't bother with subagents.
  • Can you split it into 2+ truly independent subtasks? → Subagents might help.
  • Are the subtasks each long-running? → spawn_agents_on_csv is worth the complexity.
  • Do subtasks need to share state? → Stay single. Context isolation is a tax, not a benefit.
  • Is your budget tight? → Run a single agent first. If it works, you're done.

In my experience, the threshold is: task takes 15+ minutes AND can be split into 3+ parallel branches. Below that, the startup overhead of subagents eats the savings.

Closing

I'm not done with this. Next up is a deeper piece where I take an open source repo, run a full PR review workflow with subagents, and quantify cost, speed, and quality against a single-agent baseline. If the numbers are interesting, you'll see it here.

For now, if you take one thing away: subagents are real, they work, and the cost is real too. Don't reach for them by default. Reach for them when the task is screaming for parallelism.

If you have questions, drop them in the comments. I read them.

advertisement