Multi-Agent Operating Model (Claude Code + Codex)
Status: Active Last reviewed: 2026-06-30
TwoMore is worked by two agent systems — Claude Code (planning / orchestration / verification) and Codex (frontier implementation editor). This doc is the contract that keeps them reading the same rules and prevents the two instruction systems from drifting apart. If this conflicts with AGENTS.md, AGENTS.md wins.
Why two agents
- Claude Code — plans, designs, reviews, verifies, commits, and runs the OTA flow. Auto-loads
CLAUDE.md+AGENTS.mdevery turn. - Codex (
codexMCP server, registered user-scope; orcodex execvia shell) — a frontier implementation model used as the editor for hard, self-contained, logic-heavy work, and as a second-model reviewer for risky surfaces. Auto-loadsAGENTS.mdonly (capped byproject_doc_max_bytes).
Model diversity is the prize: a different model catches different things and shoulders implementation load when Claude is rate-limited. The risk is drift — the two systems silently diverging on the rules. The three layers below exist to prevent that.
The three instruction layers
| Layer | Bytes / budget | Claude reads | Codex reads | Holds |
|---|---|---|---|---|
AGENTS.md | ≤ 28,672 (lint-enforced) | referenced | yes, natively | Hard, mechanically-enforced constraints (the ID | Rule | enforcement tables) |
CLAUDE.md | thin (orchestration core) | yes, auto every turn | no | How-I-Work, workflow, delegation, terminology, compaction invariants, + a pointer index to canon |
docs/canon/* | on-demand | via Read / doc-router | via injected task spec | The soft canon (design + implementation conventions) — single source, loaded only when relevant |
The rule that prevents drift: every rule lives in exactly one layer.
- A mechanically-enforceable hard constraint → AGENTS.md (both agents auto-load it).
- A soft convention (design judgment, IA, naming, patterns) →
docs/canon/*(loaded on demand). CLAUDE.mdnever restates canon — it only points to it. If you find yourself copying a canon rule into CLAUDE.md, stop: leave a pointer instead.- Never duplicate a rule across layers. Duplication is the drift.
How canon reaches Codex
Codex has no @import/include directive (verified against the Codex config reference — it loads AGENTS.md up to project_doc_max_bytes, with project_doc_fallback_filenames as the only extension). So canon does not auto-reach Codex. Two sanctioned mechanisms:
- Orchestrator injection (default). When Claude hands Codex a task, Claude pastes the relevant
docs/canon/*file(s) into the task spec. The split-by-topic canon makes this precise — hand Codexstatus-and-recruitment.md, not all canon. - Codex reads the file as a task step. For larger tasks, the spec instructs Codex to
Read docs/canon/<topic>.mdbefore editing.
Do not try to solve this by importing all canon into AGENTS.md — it would blow the 28,672-byte lint budget and bloat every Codex call.
Codex-as-editor handoff protocol
Claude (orchestrator) → research → plan → write a scoped task spec
(inject the governing AGENTS.md IDs + relevant docs/canon/* slice)
Codex (editor) → implement in a workspace-write sandbox → self-run `yarn check`
Claude (orchestrator) → independently run `yarn check` + `yarn lint:strict`
→ review the diff against canon → commit → push → OTANon-negotiables:
- The verify + commit + OTA gates stay with Claude (AOR-7 / AOR-8). Claude never claims done on Codex's word.
- Codex review is read-only (
--sandbox read-only); the second-model reviewer is input, never an editor and never authority. - Codex-as-editor is scoped to hard, self-contained, logic-heavy work. Pattern-application and design-system-touching work stays with Claude
Agent(model: "sonnet")subagents, which inheritCLAUDE.mdautomatically (no canon-injection gap). - Reach for a second-model review on risky surfaces: auth, RLS / SECURITY DEFINER migrations, payments (Portone /
session_payments), PIPA / consent, concurrency, or 5+ file refactors.
Canon extraction recipe (proven by the pilot)
Use these exact steps to move a topic out of CLAUDE.md into docs/canon/ (validated on status-and-recruitment.md):
- Locate the topic's blocks in
CLAUDE.md(exact line numbers; note interleaved bullets that belong to a different topic and must stay). - Create
docs/canon/<topic>.md— dual-audience: a# Title+**Status:** Active/**Last reviewed:**header (required by the doc-inventory audit), a one-paragraph intro for humans,##/###sections for the VitePress outline, then the rules verbatim. Convert[text](packages/…)source links to code spans (preserving the path) — VitePress dead-link checking fails on links to non-page.tsfiles; this matchesscreen-blueprint.md. - Thin
CLAUDE.md— replace the moved blocks with a one-line pointer that keeps the single hardest invariant inline + links to the canon doc. - Register in
scripts/doc-state-config.mjs: add toCURRENT_ACTIVE_DOCS(tracked + stale-linted) and to the Conventions group inSITE_REFERENCE_GROUPS(VitePress sidebar). - Wire the sidebar — the
Conventionssection already exists indocs/.vitepress/site-sidebar.mjs; new canon docs only need theSITE_REFERENCE_GROUPSentry from step 4. - Verify:
yarn docs:generate→yarn docs:check(state + stale + rule-docs + vitepress build) →yarn lint:agents-md→yarn check. - Commit (docs-only → no OTA; the OTA-after-change rule is for shipped app behavior, not docs).
Drift-prevention checklist (run when adding or changing a rule)
- [ ] Is this a hard constraint? → AGENTS.md (with an
enforcementvalue). Soft? →docs/canon/*. - [ ] Did I avoid restating it in
CLAUDE.md? (pointer only) - [ ] Is it in exactly one layer? (no duplication)
- [ ] If it's a new canon doc:
Statusheader + registered indoc-state-config.mjs+docs:checkgreen? - [ ] If Codex will touch the affected area: is the canon doc in the handoff spec?
See also
- AGENTS.md — the hard-constraint contract both agents load.
- Conventions › Status & Recruitment — the first extracted canon doc (the pilot).
- Change Routing — what to read / update / verify by change type.
- CLAUDE.md — orchestration core + canon pointer index.