I would ask Codex to review a design, copy the answer into Claude, wait for Claude's critique, copy that back into Codex, and repeat. It worked, but it felt less like an engineering team and more like manually forwarding email between two very clever people who were not allowed to speak to each other. The models did the reasoning. I did the routing. Every hand-off depended on me being present, preserving context, deciding what to copy, and not accidentally losing the most important sentence somewhere between two desktop apps.
Then I tried a different experiment: could Codex and Claude work together directly, on the same real codebase, with the rhythm of a human engineering team? Could I create a mechanism that allowed for Claude & Codex to collaborate without needing to install other 3rd-party agent harnesses?
I wanted dialogue before code. I wanted disagreement, evidence, review, correction and explicit agreement. I wanted one engineer to implement and another to challenge the implementation. I wanted them to involve me only when the remaining question was genuinely mine to answer. I wanted to mimic real-world human engineering interaction.
The experiment used a material-cost service in my business intelligence platform as its test case. The domain was useful because it was not a toy: financial data, SAP-derived prices, historical fallbacks, product catalogues, Bills of Material, budget snapshots, an external CRM integration, two API runtime generations, and a production platform in a soak period. A bad abstraction could silently change margins. A bad deployment decision could weaken the rollback path. This was exactly the kind of work where human engineers would normally spend serious time aligning before touching code.
Bottom line up front: it worked. Codex acted as the primary engineer. Claude Opus 4.8, at high effort, acted first as peer co-architect and then as a read-only expert reviewer. They held a direct, persistent conversation through Claude CLI, recorded the important exchanges in a shared engineering ledger, closed decisions explicitly, reviewed the implementation slice by slice, and produced a substantial feature branch without me copying a single message between them.
This post is not about the Material Cost Service itself. That deserves its own technical write-up. This is a how-to about the collaboration system: how I got two frontier coding agents to mimic the useful parts of a real engineering team, what failed in the original idea, what the protocol looked like, what Claude actually caught, and what I would recommend if you want to reproduce it.
Act 1: The original idea — put two AI engineers in one room
My first proposal was deliberately human. I wanted a shared Markdown file under /docs/plans where Codex and Claude could address one another as [Codex] and [Claude]. The file would be the living record: questions, arguments, decisions, points of contention, and anything that required escalation to me as the owner.
I initially imagined that each agent could run a listener or cron job, notice when the collaboration file changed, read the other agent's latest turn, and add a response. In other words: asynchronous engineers watching the same design document.
The intent was right. The transport was not.
File watchers would have introduced exactly the kind of coordination bugs I was trying to avoid: both agents writing at once, duplicated turns, missed updates, an unclear "next speaker", background jobs continuing after the work had changed direction, and no obvious live terminal showing what either agent was doing. A Markdown file is excellent as a durable record. It is a poor message queue.
Codex proposed a better division:
- Codex would orchestrate the conversation directly, calling Claude through the local CLI rather than waiting for a file event.
- One persistent Claude session would retain the shared context across turns.
- The ledger would record the curated conversation, exact prompts, evidence, decisions and review verdicts.
- Codex would remain the only code writer; Claude would inspect the same repository read-only.
- I would remain the decision authority, but only for real business, policy or risk choices that code could not answer.
This was an important distinction. The agents did not "chat through Markdown". Codex conducted explicit turns in a persistent Claude CLI session, and Markdown became the engineering notebook.
Act 2: Why Claude CLI was the unlock
I use both the Codex desktop app and the Claude desktop app. My first instinct was therefore to connect the two desktop experiences. But desktop UI automation would have recreated the fragility of my copy-and-paste workflow, only with a robot moving the clipboard instead of me.
Claude CLI gave us something more useful: a direct programmable interface to the same model, running on the same machine, against the same checked-out repository. Codex could launch a terminal-visible Claude session, supply a bounded prompt, let Claude inspect the actual files, capture the response, and then continue the discussion without me relaying anything.
For this experiment I chose:
| Role | Configuration |
|---|---|
| Primary engineer and orchestrator | Codex desktop app |
| Peer architect and independent reviewer | Claude CLI, Opus 4.8, high effort |
| Claude authority | Plan/read-only; no repository writes, commits, pushes, deployments or external mutations |
| Shared context | Same Git branch, repository contracts, architecture documents and collaboration ledger |
| Human role | Owner, scope authority and final business decision-maker — not message courier |
The terminal mattered. I wanted the experiment to be observable, not two invisible agents claiming they had consulted one another. The Claude CLI output could be surfaced while the curated conclusions were written into the ledger. We deliberately did not commit raw model event logs: they are noisy, may contain local paths and machine metadata, and are not the useful record. The useful record is the prompt, the evidence, the conclusion and the decision.
Act 3: The collaboration contract
Two models exchanging long opinions is not automatically collaboration. Without rules, they can politely agree with one another, repeat the same assumptions, or generate an impressive volume of text without closing a single decision.
So we wrote a small collaboration contract before the technical debate began:
Each discussion has a stable ID: D-01, D-02, ...
Each response addresses the other agent directly.
Ask no more than three focused questions per turn.
Every response must:
1. restate the strongest part of the other agent's position;
2. answer with repository or read-only runtime evidence;
3. state whether the evidence changed its position;
4. identify remaining risk or disagreement;
5. propose a synthesis and the next question.
A decision closes only when both agents write AGREE
against the same bounded decision statement.
Before escalating, complete at least two evidence-bearing exchanges
and explain the concrete consequence of each option.
That first requirement — restate the strongest part of the other position — turned out to be surprisingly powerful. It prevented the agents from arguing against a weak paraphrase. It also made position changes visible. At one point Claude explicitly wrote that Codex's latest turn had corrected a genuine misread and that the repository evidence backed Codex, not Claude's earlier position. That is exactly what I want from a senior engineer: not stubborn consistency, but evidence-led correction.
The three-question limit also mattered. It kept each turn answerable. "Review the architecture" is not a useful engineering request. "Does this boundary preserve the existing write path? Is the fallback provenance honest? What would falsify the first slice?" produces a much better response.
Act 4: The ledger — memory, minutes and decision register
The core artefact was Claude-Codex-colab-material-cost-solution.md. It was not a transcript dump. It looked more like the combination of architecture-decision records, design-review minutes and a pull-request review thread.
# Codex-Claude Collaboration: <workstream>
Status: <design / plan review / implementation / complete>
Owner and escalation authority: <human owner>
Implementation branch: <branch>
Baseline commit: <sha>
## Purpose
## Governing constraints
## Collaboration protocol
## Decision register
| ID | Decision | Status | Evidence |
| D-01 | Minimum viable domain boundary | Agreed | ... |
| D-02 | Evidence precedence and fallback | Agreed | ... |
## Dialogue
### D-01
#### D-01.1 — Codex to Claude
**[Codex]**
#### D-01.2 — Claude to Codex
**[Claude]**
## Implementation review record
## Escalations
## Deferred work
## Experiment observations
## Appendix — Exact prompts
There are three reasons this worked better than relying on either model's chat history.
- The state was inspectable. I could open one file and see what had been agreed, what was still open, and why.
- The state travelled with the code. Claude could re-read the ledger after a long implementation pass or a resumed CLI session. The branch, not somebody's memory, carried the decisions.
- The experiment became auditable. We preserved the original bootstrap prompt and twenty later review prompts in appendices. For a future blog post, retrospective, or process improvement, I did not have to reconstruct what the agents had been told.
The ledger did not request private chain-of-thought. We recorded claims, evidence, trade-offs, questions, corrections and conclusions. That is also closer to good human engineering practice: I do not need every private thought in an engineer's head. I need a reviewable argument and enough evidence to test it.
Act 5: A real dialogue, not alternating monologues
A typical design exchange followed this shape:
#### D-02.3 — Codex to Claude
**[Codex]**
Strongest part of Claude's position:
<the best version of Claude's argument>
Repository evidence:
- <file / function / observed data>
- <test or runtime behaviour>
My position changed here:
<what Codex now accepts or rejects>
Remaining disagreement:
<one bounded issue>
Proposed synthesis:
<decision statement>
Questions:
1. <focused question>
2. <focused question>
Next actor: Claude
Claude's response had to follow the same discipline. It could agree, challenge the evidence, or change position. But it could not close with "looks good". Agreement was a statement with a scope.
For example, the models initially spent time considering whether purchase-order and goods-received costs should become a selected canonical price. This was technically interesting and commercially plausible. It was also drifting beyond the practical business rule I wanted.
I clarified the rule: the BI platform should follow SAP's own price-control categories — Standard Price or Moving Average Price — and retain the prior governed Product Master cost when the newer SAP fields did not exist historically. PO, GRN and Stock costs could remain visible as additional metadata, not silently replace the SAP-controlled value.
That was not a failure of the collaboration. It was the collaboration behaving like a real team. The engineers explored a design space. The owner narrowed the product rule. Codex translated the clarification into an exact architecture statement. Claude independently audited that interpretation against the code and documents, then recorded:
[Claude]: ACKNOWLEDGE OWNER DECISION
The crucial point is that I did not have to carry Claude's message to Codex or Codex's interpretation back to Claude. I made the decision once, in the main conversation. The agents did the reconciliation themselves.
Act 6: From co-architecture to independent review
Once the architecture was jointly closed, the roles changed.
Codex became the implementing engineer. Claude stopped co-authoring the solution and became a read-only reviewer. This one-writer rule was important. Letting both models edit the same branch would have made authorship, regressions and rollback much harder to reason about. Human teams use ownership boundaries for the same reason.
The implementation plan was divided into reviewable slices. Before each Claude review, Codex recorded:
- the exact baseline and diff;
- the intended files;
- the observable acceptance checklist;
- both sides of every changed interface;
- the tests and their outputs;
- the deployment-package boundary;
- the rollback boundary; and
- anything not yet tested.
Claude had three possible verdicts:
[Claude]: APPROVE SLICE N
[Claude]: REQUEST CHANGES SLICE N
[Claude]: BLOCK SLICE N — OWNER DECISION REQUIRED
Codex did not automatically obey every review comment. It validated each finding against the code or read-only runtime evidence, accepted or rebutted it, made corrections, reran the relevant tests, and asked Claude to re-review material changes. This is another important human-team behaviour: reviewers are not infallible, and authors are not passive.
Act 7: The moment I knew the reviewer was real
The first implementation-plan review did not pass.
Claude found two concrete defects. The high-severity one concerned the real production ETL orchestration path: the plan had not proven where a last-known price projection would be computed and written before the production router advertised fresh data. A design could work perfectly in a standalone script and still be skipped by the actual production subprocess path. The medium-severity finding identified three tracked Sales-sync trees, two active and one legacy, while the plan named only two. That omission created a realistic wrong-tree editing hazard.
Claude's verdict was unambiguous:
[Claude]: REQUEST CHANGES — IMPLEMENTATION PLAN
Codex then inspected the finding and discovered something even more useful: Claude's concern was valid, but one premise was wrong. There was no existing child-deferral mechanism to reuse. The safe design required an explicit new --defer-manifest boundary. Codex corrected the plan, marked the legacy tree out of scope, and sent it back.
Claude re-read the actual production path, accepted Codex's correction of its premise, verified that both findings were closed, and then approved the plan.
This is the result I was looking for. Claude did not rubber-stamp Codex. Codex did not blindly follow Claude. The review exposed a real production-path gap. The author improved the reviewer’s proposed correction. The reviewer changed position when the evidence warranted it. Then both agents closed the decision.
The later reviews continued to produce practical value:
| Review point | What the second model noticed | What changed before checkpoint |
|---|---|---|
| Foundation resolver | A controlled valuation could be accepted with missing observed UoM; top-level freshness could incorrectly come from a newer comparator rather than the selected canonical value. | Missing UoM now refuses completion, and freshness follows the selected observation. Claude re-reviewed the correction. |
| BOM material roll-up | Aggregate freshness should expose the oldest contributing component; an invalid quantity could still look priced under defensive input. | Freshness became conservative, invalid quantities became explicit warnings, and priced coverage requires an actual extended cost. |
| Frontend compatibility boundary | The real runtime check showed the cross-boundary cost reference needed exact server-projector provenance, and an incomplete result had to preserve a legitimate legacy zero. | A cross-boundary test imported the real server projector and proved parity; legacy state remained stable when the new result was refused. |
| Operational publication layer | Admin-role visibility, activation boundaries, retained-byte delivery and the difference between a flaky suite timeout and a feature defect all needed explicit treatment. | The final record separated non-blocking operational observations from feature correctness, then repeated the complete test gate. |
None of these findings required Claude to write code. Its value was in forcing the implementing agent to inspect a boundary it might otherwise have declared complete.
Act 8: Did it actually work? The numbers
The experiment ran against a genuine end-to-end feature branch, not a synthetic coding benchmark. The final checkpoint was deliberately not activated in production because the platform's new backend was still soaking and the external integration contract was still under review. That restraint is part of the success: implementation completion, merge, deployment and activation remained separate decisions.
| Measure | Observed result |
|---|---|
| Human copy-and-paste relay turns after bootstrap | 0 |
| Persistent reviewer sessions | 1 primary Claude CLI session, with model/version/permission metadata recorded |
| Numbered collaboration turns | 68 |
| Recorded decision/review threads | 18 |
| Exact prompts preserved in the implementation-plan appendices | 20 (Appendix A through T) |
| Explicit Claude implementation/evidence approvals in the core ledger | 18, plus the separately recorded implementation-plan approval |
| Initial plan-review outcome | Rejected once with one high- and one medium-severity finding; approved after correction and re-review |
| Scoped implementation commits | 14 |
| Reviewed branch delta | 103 files, 10,779 insertions, 355 deletions |
| First implementation checkpoint to final reviewed checkpoint | Under 12 wall-clock hours, across an evening/overnight autonomous build |
| Final full regression gate | 405 test files, 2,791 tests passed; two explicitly integration-gated tests skipped |
| Production activation caused by the experiment | 0 — activation remained an explicit later owner decision |
The raw volume is not the main result. Sixty-eight turns would be waste if they were sixty-eight rounds of mutual praise. The useful signal is that the process produced:
- a plan rejection before unsafe implementation;
- evidence-led position changes by both agents;
- multiple concrete corrections at data, runtime and UI boundaries;
- explicit scope control when the agents began inventing policy;
- independent review of the actual diff rather than a prose summary;
- a fully auditable record of prompts and decisions; and
- a green full regression gate without collapsing deployment and activation into "done".
Would a strong human reviewer have found the same issues? Possibly. That is precisely the point. The experiment was not trying to invent a new species of engineering. It was trying to reproduce the behaviours that make a strong human engineering team effective.
Act 9: The how-to recipe
If you want to reproduce this with Codex and Claude, here is the smallest version I would recommend.
Step 1: Give the agents asymmetric roles
Pick one implementer and one reviewer. Do not start with two writers. My default is Codex as primary engineer and Claude as read-only peer/reviewer, but the brands are less important than the ownership boundary.
Step 2: Start from a safe Git boundary
Record the current branch, baseline commit and dirty worktree. Use a feature branch for substantive work. Make commit, push, deployment and production mutation separate permissions.
Step 3: Create the ledger before the debate
Capture purpose, governing constraints, decision register, roles, escalation authority and non-goals. Preserve exact prompts. Keep the ledger curated; do not dump raw model telemetry into Git.
Step 4: Bootstrap one persistent read-only Claude session
Tell Claude which governing files to read, what decision it is reviewing, what it may not mutate, and exactly how to structure its response. Record the CLI version, model, effort, permission mode and session identifier. Verify the installed CLI's real flags rather than assuming an old syntax.
Step 5: Enforce evidence-bearing turns
Stable decision IDs. At most three questions. Steelman the other position. Cite files, functions, tests or read-only runtime evidence. State position changes. End with the next actor.
Step 6: Require bilateral closure
Both agents must write AGREE against the same decision statement. Record deferrals separately. "No objection" is not an architecture decision.
Step 7: Escalate only irreducible human choices
Before asking the owner, make the agents exhaust code and read-only evidence. When escalation is necessary, present the shared facts, options, consequences and one exact question. The human should decide risk, business policy or authority — not locate a function in the repository.
Step 8: Review one implementation slice at a time
Send Claude the exact diff, acceptance checklist, interface boundaries, tests, rollback and gaps. Require findings first and one explicit verdict. If Codex changes the reviewed diff materially, re-review it.
Step 9: Validate the reviewer
Codex must verify Claude's findings rather than accepting them by prestige. The reviewer can be wrong. In our case Claude found a real problem but inferred a deferral mechanism that did not exist; Codex's correction produced the safer design.
Step 10: Finish with an honest ledger
Separate code-complete, committed, pushed, deployed, configured and activated. List parked work. State which exact invariants the tests exercised. Leave the next owner and action unmistakable.
Act 10: What not to do
| Temptation | Why it fails | Better approach |
|---|---|---|
| Use a shared file as a live message queue | Race conditions, duplicate turns, invisible failure and unclear ownership | Direct CLI orchestration; use the file as the durable record |
| Let both agents edit simultaneously | Blurred authorship, difficult rollback and review contamination | One writer, one independent reviewer |
| Ask "review everything" | Unbounded context produces generic comments | Exact decision, diff, interfaces, acceptance checklist and risk focus |
| Treat model agreement as truth | Two models can share the same wrong assumption | Require repository and runtime evidence plus falsification tests |
| Let the agents invent missing business rules | Technically elegant behaviour can still violate owner intent | Stop, inspect reality, then ask the owner one bounded question |
| Accept "tests pass" as review evidence | Tests may prove an intermediate while the consumer or write path remains wrong | Name the observable invariant and exercise the real boundary |
| Store every raw model event | Noise, local paths, duplicated content and potential secret exposure | Preserve exact prompts and curated evidence-bearing conclusions |
| Call implementation "shipped" | Commit, push, deploy, configuration and activation are different risk events | Report each state separately |
Act 11: What surprised me
The first surprise was that the collaboration became more useful when it became less agentic in the fashionable sense. We did not install a swarm framework. We did not create a message broker. We did not let agents recursively spawn agents. We used one orchestrator, one reviewer, one persistent session, one ledger and Git.
The second surprise was that disagreement needed structure more than intelligence. Both models were capable of producing excellent architecture alone. The extra value came from forcing each to expose evidence, recognise the other position, and close a bounded statement. The protocol turned model capability into engineering behaviour.
The third surprise was how naturally the human role improved. I was no longer the network. I became the owner. I could watch the reasoning, intervene when the product rule was wrong, and leave code-discoverable facts to the agents. That is a much higher-leverage use of my time.
The fourth was that a read-only reviewer was enough. Claude did not need write access to create value. In fact, keeping it read-only protected the independence of the review. It could challenge the implementation without becoming invested in its own patch.
Finally, the ledger became more than experiment theatre. By the end it was the clearest record of why the system had its boundaries, what had been tested, which decisions were human, which were evidence-derived, and what remained inactive. A future engineer — human or AI — can resume from that record without replaying the entire history.
The takeaway
Cross-LLM engineering collaboration does not require a human copying messages between browser tabs. It also does not require an elaborate autonomous-agent platform.
It requires a reliable transport, persistent context, explicit roles, a shared evidence record, bounded questions, a decision protocol, one-writer ownership, independent review and a clean escalation path to a human who owns the business outcome.
The experiment worked because we did not ask the models to imitate people superficially. We gave them the mechanisms that make good human engineering teams work: design documents, decision registers, code ownership, review gates, evidence, the freedom to disagree, the obligation to change position, and a manager who only steps in when the decision is genuinely managerial.
The most important lesson: autonomy is not the absence of human authority. It is the removal of unnecessary human routing. I did not disappear from the engineering process. I stopped being the clipboard.
I have since packaged the protocol as a personal Codex skill called claude-colab. The skill is not the interesting part. The interesting part is that the method is simple enough to repeat: one branch, one ledger, one persistent reviewer, evidence before agreement, and the human reserved for the decisions only a human should make.
Two AIs. One engineering problem. A real dialogue. No human clipboard.
Onwards.
Experiment note: the case study ran on a Windows development machine using the Codex desktop app and a persistent Claude CLI session configured for Claude Opus 4.8 at high effort and read-only/plan permissions. Codex owned repository edits and testing. Claude inspected the shared repository, plans, diffs and evidence. The human owner retained scope, source-control, deployment and business-policy authority throughout.
Measured branch result: 14 scoped commits between the first implementation checkpoint and the final reviewed checkpoint; 103 files changed; 10,779 insertions and 355 deletions across implementation, tests and documentation; final full gate of 405 test files and 2,791 passing tests, with two deliberately integration-gated tests skipped. The feature remained on its branch and external/production activation stayed separate. Cups of coffee saved by not being a human message bus: finally measurable in spirit, if not yet in Prometheus.


No comments:
Post a Comment