Splitting an AI team into roles — Director, Engineer, Tester, Designer, Document Owner — cleans up the conversation immediately. Then it creates a new problem, which is that the number of handoffs goes up.
Who was given what. From which state. What is explicitly not in scope. What has to be true before it comes back to the Director. When those go vague, an AI team does not get more useful; the conversation just spreads out.
Originally published in Japanese on June 4, 2026. This English version was written in September 2026, with the July 2026 addendum folded in and the source claims re-checked.
The conclusion first: the thing that makes an AI team work is not adding more agents. It is not breaking the handoff.
Below is a record of trying to support those handoffs with Codex’s thread machinery. Read the sourcing carefully, because this article deliberately keeps three things apart: what OpenAI’s documentation states, what surfaced in my environment and nowhere I can point you to, and what I have not verified at all.
Roles fix drift; they do not fix handoffs
Splitting roles works. Asking the same model to look at something as a Director and as an Engineer produces genuinely different work. As Tester, I want expected values reconciled against actual ones. As Document Owner, I want a judgment about what belongs in human-facing documentation.
“Handle this” becomes “in this role, within this scope, done when this is true.” That alone changes how the work goes.
What shows up after a few weeks is the cost of the seams. Director hands to Engineer. Engineer returns an implementation. Tester verifies. Document Owner decides whether the docs move. Director decides whether it closes.
Copy-pasting that by hand degrades in specific, repeatable ways: stale premises get forwarded, the “not doing” list gets dropped, completion criteria go missing, the Tester’s report never makes it back to the Director, and you lose track of which thread was asked for what.
A thread is a unit of work
Codex treats threads as first-class objects, not as chat scrollback. The app server documentation defines the hierarchy directly: a thread is the conversation, a turn is one request and the work it causes, and an item is what that work produced — a message, a command execution, a file change, a tool call. A thread is a place where work accumulates, and it has an ID you can come back to.
Treating it that way changes what the log is for:
- Director thread — direction, scope, in and out, the close decision
- Engineer thread — implementation, build, verification, the shape of the diff
- Tester thread — expected values, the procedure, actual behavior, reproduction conditions on failure
- Document Owner thread — decisions and documentation judgments a human will read later
This is not free. For a small fix, one thread start to finish is faster, and over-splitting adds weight rather than removing it. The split earns its cost when implementation, verification, documentation, and a publication decision are all in play at once.
What the documentation actually gives you
Before the interesting part, the boring and reliable part. Threads are a first-class concept across the whole Codex surface, and all of the following is documented.
One terminology note that will save you a search: the user interface says “chat” where the APIs say “thread.” The keyboard shortcuts are New chat, Archive chat, Pin or unpin chat, Rename chat, Search chats, Find in chat. /status reports a chat ID. “Thread” is the technical object in the app-server and SDK APIs — the glossary defines it as the object holding turns and stored conversation history. Same thing, two vocabularies.
| Surface | What is documented |
|---|---|
| CLI | codex resume continues a previous session by ID or resumes the most recent one; codex fork forks a previous session into a new chat while preserving the original transcript |
| App server | Thread, Turn, and Item as core primitives; thread/resume reopens a stored thread by ID; thread/fork copies stored history into a new thread ID, optionally truncating at a given turn or forking in-memory only |
| SDK | Start a thread, continue in the same thread, resume by thread ID |
| Diagnostics | codex doctor reports on thread inventory alongside install, config, auth, runtime, Git, and terminal checks |
| App server (cont.) | thread/list supports pagination plus archived, isPinned, cwd, and searchTerm filters; thread/archive moves a thread’s log file to the archived directory; thread/unarchive restores an archived log to the active sessions directory; thread/metadata/update patches stored metadata including isPinned |
| Deep links | codex://threads/<thread-id> opens a specific chat; codex://threads/new and codex://new?<query> open a new one, with optional prompt, path, and originUrl parameters |
| Subagents | Specialized agents run in parallel, each in its own agent thread, orchestrated by Codex; subagents inherit the parent’s sandbox policy, and /agent switches between active agent threads |
That is enough to build role-thread operations on, without any of what follows. Threads persist, resume by ID, and fork. Pinning, archiving, unarchiving, and search all exist as documented app-server methods. If you want a Tester thread that is still a Tester thread next week, the documented primitives already cover it.
The deep links deserve a specific mention, because they are the cheapest thing on this list to adopt. A per-role link that opens a new chat in the right workspace with the right opening prompt already in the composer removes most of the repetitive part of a handoff. Note that the prompt is placed, not sent — you still see it before anything runs, which for a handoff is the behavior you want.
What surfaced in my environment, and why that is a weaker claim
Now the part I cannot point you to a doc for.
Not every tool in an environment is visible up front; you search for what is available. (Tool search as a mechanism is documented on the OpenAI API side, where the model can look up and load tools into context on demand. Codex mentions it in passing — the 0.143.0 release notes say MCP tools use tool search by default, and it appears in the config reference — but no invocable tool_search tool is documented for Codex, which is itself part of the point here.)
Looking for thread-management tools, my first search returned nothing relevant — GitHub PR review thread tools and subagent tools, but nothing that touched Codex app threads. On a later turn, with a query in the same family, a codex_app namespace of thread-management tools was exposed.
These tool names do not appear in OpenAI’s published documentation. I checked again while writing this English version, in September 2026, across the whole published documentation set, and they are still absent. What I can honestly say is narrow: in my environment, on that day, a tool search exposed these. What I cannot say is that Codex has them, that you have them, or that they behave the same way for you.
There is one piece of outside corroboration, and it happens to make the variability point better than I can. An open issue on the Codex repository, filed on June 7, 2026, reports that a thread started remotely from one Mac against another does not receive the codex_app thread-management tools, while a thread started directly on the same machine does — listing create_thread, fork_thread, handoff_thread, list_threads, read_thread, send_message_to_thread, set_thread_pinned, set_thread_archived, and set_thread_title among them.
So the surface is real, and it varies by something as incidental as where the thread was started from — on top of environment, plugins, connectors, app state, rollout, and the search terms used. Build a workflow that depends on it and you have built a workflow that can quietly stop having the tools it needs.
| Tool | What it did | How I exercised it |
|---|---|---|
create_thread | Create a new Codex thread | Created one throwaway thread for verification |
list_threads | List recent threads, or find them by query | Searched for the verification thread only |
read_thread | Read status and turn summaries for a thread | Read the verification thread by explicit ID |
send_message_to_thread | Send a follow-up prompt into an existing thread | Sent one short prompt to the verification thread |
set_thread_title | Rename a thread | Renamed the verification thread |
set_thread_pinned | Pin and unpin | Both directions worked |
set_thread_archived | Archive and unarchive | Archive worked. Unarchive did not — see below |
The test protocol was deliberately small: one purpose-built thread, no reads of existing work threads, no sends into existing work threads. The opening prompt told the thread to do no real work — no file edits, no commands, acknowledge receipt only.
Reading it immediately after creation showed the first turn as inProgress; a moment later it was completed, with the agent message readable. Sending a follow-up added a new turn, and the reply was readable in the same way. The prompt was recorded not as loose text but as a delegation, with the source thread and the sent input held together — which is the property that makes this interesting for handoffs. State moves between threads in a structured form rather than as pasted prose.
The wrapper archived. Restoring through it failed.
The one finding I would carry to any similar tool: in the undocumented set_thread_archived wrapper exposed in my environment, archive succeeded, the thread disappeared from search results, and reading it by explicit ID still worked. Unarchiving through that wrapper failed, with an error to the effect that an unloaded thread needs a host ID. The wrapper schema appeared to take only a thread ID and an archived flag; the internal state said otherwise. This does not contradict the documented app-server thread/unarchive method; they are separate interfaces.
“It works” and “it is reversible” are different claims, and only one of them was true here. Archive is not a tool to poke at casually while writing an article, which is exactly what I was doing. Before using anything like it on real work, confirm the way back, not just the way forward.
A delivered notification is not the contract
An addendum from July 20, 2026, and the most operationally useful thing here.
Cross-thread notification is convenient. In my environment it also has a failure mode worth designing around: a send can time out and the message can still arrive later. Once that is possible, a notification cannot be the authoritative record of what was agreed, because you cannot tell a lost request from a slow one.
So the record moved off the wire. Before sending anything, I persist the work contract as metadata only:
- The target
- The scope of change
- What must be preserved
- How it will be verified
- How it rolls back
Each request also carries a delivery ID, so a duplicate arrival is recognized as the same request rather than executed twice. This is my own operational practice, not a published OpenAI specification. Its whole purpose is to separate “the notification failed” from “the contract was lost” — which are the same event if the message is the only place the contract exists.
If you take one implementation detail from this article, take that one. It survives whatever the tool surface turns out to be.
What goes in a handoff
Tools do not rescue a vague handoff. They make vague instructions travel faster.
What I put in one:
- Purpose
- Scope
- What to do
- What not to do
- Files and screens that may be touched
- Files and screens that may not
- Current state
- Completion criteria
- How to verify
- Report format
- Conditions for returning to the Director
Development work usually needs repository, branch, worktree, and the HEAD commit it assumes. None of those belong in a public article, so treat that as a category, not an example.
The contents differ by role, and that is the point. The Engineer gets implementation scope and completion criteria. The Tester gets expected values, the procedure, the URL or screen to check, and the report format for a failure. The Document Owner gets what should reach the docs and what should not.
Subagents are a different tool for a different job
Codex documents a subagent workflow: ask for it explicitly and specialized agents run in parallel, each in its own agent thread, with results brought back together. It suits read-heavy exploration, tests, triage, and summarization, and it is a supported, documented feature — which the tool surface above is not.
It is also not the same thing as role threads. A subagent is a worker spawned for a piece of work. A role thread is a continuing seat — Director, Engineer, Tester, Document Owner — that persists across many pieces of work.
Neither is superior. Parallelize exploration or review with subagents. Keep a running record of decisions per role in threads. I use both: judgment in a main Director thread, exploration handed to subagents, and the role continuity that has to outlive a task kept as separate threads.
Check the destination before you send
Anything that can send into an existing thread is worth being careful with. The failure is mundane and expensive: content meant for the Engineer lands in a different work thread, or a handoff containing internal detail lands in a thread whose output is public.
Before sending, I check the thread, the title, the role, and whether the payload contains a thread ID, a real repository name, a worktree path, a branch name, a HEAD SHA, a client name, an admin URL, a token, or a credential.
The tempting summary is that faster handoffs make an AI team faster. The accurate one is that the easier a tool makes it to hand work off, the more damage a careless handoff does. Correct beats fast at this particular seam.
The design matters more than the tool
“AI team” sounds grand. The actual work is not: decide the role, write the handoff, find the thread, confirm the destination, return the result, decide whether it belongs in the docs, check after publication.
All of it is unglamorous, and without it the work simply drains away. I am not trying to have AI automate everything. I am trying to keep my own judgment in the loop while running more work than one person can hold.
Which leaves a practical position rather than a tidy one. Split threads by role. Check what your own environment actually exposes rather than assuming a feature exists. Where tooling supports the handoff, use it — after confirming you can undo it. Where it does not, a written handoff plus codex resume does the job.
Still one person, thinking, building, and checking alone. But the work now has a Director in it, an Engineer, a Tester, a Document Owner, each handed the context it needs and returning something the next decision can be made from.
What makes an AI team work is not adding more AI. It is not breaking the handoff.
References
Everything in the “what the documentation gives you” section comes from these. The codex_app tool names do not appear in any of them, and I have not found them documented anywhere else.
- Command line options | Codex CLI reference —
codex resume,codex fork,codex doctor - Codex app server — Thread, Turn, and Item primitives,
thread/resume,thread/fork,thread/list,thread/archive,thread/unarchive,thread/metadata/update - Commands and shortcuts | Codex — the chat shortcuts and the
codex://deep links with their query parameters - Codex glossary — “thread” as an app-server and SDK object, and thread forking
- Codex SDK — starting, continuing, and resuming a thread by ID
- Subagents | Codex documentation — parallel specialized agents, each in its own agent thread
- openai/codex issue #26907 — a third-party report of the
codex_appthread-management tools, and of their appearing or not depending on how the thread was started - Tool search | OpenAI API — the documented mechanism, on the API side rather than in Codex
- Routing work across GPT-5.6 Sol, Terra, and Luna — the companion question of which model a given role thread should run on