Working effectively with two agents
ContextRelay runs Claude Code and Codex as one team in a single working session. The tooling is only half the story. Two strong agents can still produce slow, repetitive, or unsafe work if they talk past each other - and they will, unless you build a few habits into how they collaborate.
This page is not a reference. It is the set of working habits - distilled from the collaboration rules that ContextRelay writes into every project's CLAUDE.md / AGENTS.md block, and from real sessions - that reliably separate a productive pair from a noisy one. The single idea underneath all of them:
Agents cannot see each other's hidden reasoning. They share only what is written into the messages and the shared ledger. If a fact, decision, or plan lives in one agent's head, the other agent does not have it.
Everything below is a consequence of that constraint.
1. Write context into the ledger - always
Because the peer can only see what is recorded, the most important habit is to write durable context at every transition, not just at the end. At each meaningful step, record:
- Goal - what you are trying to accomplish.
- Current plan - the approach, not just the action.
- Files touched - concrete paths.
- Blockers - what is unresolved.
- Decisions - what was settled, and why.
- Next step - what should happen, and who does it.
The shared, append-only ledger lives locally under .contextrelay/state/. Agents read it with read_context and add to it with append_note. Use a note whenever you reach a decision or a fork that the other agent (or a human reviewing later) would need to understand the work.
append_note: "Decided to keep the retry in the adapter, not the caller -
caller already has a circuit breaker. Implemented in src/codex-adapter.ts.
Next: Codex adds a unit test for the backoff ceiling."
A good test: if your session crashed right now and the other agent had to resume from the ledger alone, could it? If not, you have not written enough down.
The ledger is also your audit trail and your recovery story. ctxrelay recover and ctxrelay status read it back. Treat notes as durable infrastructure, not chat.
2. Make handoffs explicit
A bare "over to you" stalls the pair - the receiving agent has to guess the reason, reconstruct the context, and decide whether it even owns the work. ContextRelay gives handoffs a deliberate structure for exactly this reason. When you pass control, always state four things:
- The reason - why you're handing off (blocked, better-suited peer, need review).
- The concrete ask - the specific next task, not a vague direction.
- The relevant files / context refs - paths and ledger entries the peer needs.
- Who speaks next - make the baton explicit.
Use the handoff tool (Claude) or handoff_to_claude (Codex) when you are transferring ownership of a discrete task. It records the handoff in the ledger and delivers it live, so the structure survives even if the live channel hiccups.
handoff
reason: "You own implementation under the current coordinator policy."
ask: "Implement the migrate-merge in src/cli/upgrade.ts so new default
keys are added but existing user values are preserved."
refs: "Spec in the ledger note above; mirror the shape in src/config-service.ts."
next: "Codex implements; I'll review the diff before we finalize."
3. Use the right channel for the job
ContextRelay gives you three distinct ways to talk to the peer. Picking the wrong one is a common source of friction.
| Channel | Tool (Claude / Codex) | Use it for |
|---|---|---|
| Reply | reply / send_to_claude | Back-and-forth discussion, status, answering a question. |
| Handoff | handoff / handoff_to_claude | Transferring ownership of a discrete task. |
| Deliberate | deliberate_with_codex / deliberate_with_claude | A bounded decision pass: pose a tradeoff, get the peer's view, synthesize. |
Deliberation is for convergence on a decision - "should we do A or B?" - not for running a long review.
A live deliberate_* call waits for the peer to respond in-band, and that wait is bounded - long, open-ended reviews can hit the wait limit and time out. For anything substantial (a full diff review, an architecture pass), use a plain reply to pose the question and let the peer pick it up from the ledger with read_context, then continue asynchronously. Reserve live deliberation for short, focused decisions.
4. Expect - and give - independent judgment
Two agents are only worth the coordination cost if they actually challenge each other. Passive agreement is the failure mode: if the reviewer just says "looks good," you have paid for a second agent and gotten a rubber stamp.
ContextRelay's collaboration language exists to force real positions. Use these phrases explicitly:
- "My independent view is:"
- "I agree on:"
- "I disagree on:"
- "Current consensus:"
When you are the reviewer, lead with severity-ordered findings of the actual diff - the most important problem first, grounded in the real code - not a summary and not agreement. A risk-review handoff answered with a bare acknowledgement is worse than useless: it signals "nothing to see here" and the coordinator will route the critical path right past the risk.
After a deliberation, the agent that asked should synthesize: state the current consensus, the remaining disagreement, the decision, and the next action. That synthesis is what gets written to the ledger and drives the next step.
5. Respect the coordinator and git boundary
One agent - the coordinator - owns git writes (branch, commit, merge, push, PR). This is not a suggestion; it prevents two agents from racing on the same history and producing conflicting or duplicated commits.
- The coordinator is configured in
.contextrelay/config.json(collaboration.coordinator) and mirrored into the managed instruction block, so both agents can read who holds the pen. - Non-coordinator agents use read-only git only (
git status,git diff,git log) and hand off any git-sensitive work to the coordinator or the human. - Before attempting any git write, the non-coordinator should confirm the current coordinator via
read_context- and if it's not them, hand off instead.
Change the coordinator deliberately, not implicitly:
ctxrelay coordinator status # who owns git right now
ctxrelay coordinator codex # hand the git pen to Codex
Changing the coordinator or the git-write policy is itself a human-authority decision (see habit 7). Don't reassign the pen mid-task to route around a boundary.
For the full model, see Coordinator and git-write policy.
6. Don't loop indefinitely
A pair that keeps "checking in" with each other burns tokens and goes nowhere. The discipline is simple: after the peer responds, summarize what changed, decide the next step, and either continue or finalize. If you find yourselves trading agreement without new information, that is the signal to converge - make the call, write it down, and move.
When the work genuinely looks done, don't keep talking - record it. Use propose_final (Claude) / propose_final (Codex) to mark the work complete and move it toward sign-off rather than re-opening the discussion.
7. Escalate to the human only for human-authority items
The pair should resolve most uncertainty between themselves first. When autonomy is enabled and you're unsure about a plan, tradeoff, design choice, risk, or next step, run a bounded deliberation with the peer before interrupting the human.
Reserve human escalation for decisions that genuinely require human authority:
- Credentials or anything requiring secrets the agents shouldn't hold.
- Spending beyond the configured opt-in budget.
- Destructive or outward actions - publishing, releasing, deleting, pushing to shared history.
- External business judgment the agents can't responsibly make.
- Changing the coordinator or the git-write policy itself.
Everything else - "which implementation is cleaner?", "is this test sufficient?", "what's the risk of this refactor?" - is a peer deliberation, not a human prompt. After the peer pass, synthesize and proceed.
This escalation discipline rests on a safe baseline. ContextRelay is read-only by default: backup-agent autonomy is off until you enable it, and autonomous edits (act:write) are off behind explicit, layered gates. The agents can reason and propose freely; turning any of those into action that touches your machine or your spend is a deliberate, gated choice. See Read-only by default.
8. Keep the loop quiet - leave the defaults on
Two agents narrating to each other in real time will flood both contexts and bury the signal. ContextRelay ships several coordination defaults specifically to keep the pair quiet, and you should leave them on unless you have a concrete reason not to:
- Turn digest (
turnDigest) - buffers Codex's transcript and status chatter and emits one summary when the turn completes, instead of a stream of partials. - Quiet turn pings (
quietTurnPings) - keeps routine "working…" / "done" lifecycle pings out of Claude's live context while preserving status and metrics. - Usage presets (
ctxrelay usage) - tune both the Claude hook and shared-context tool output with one operator-facing control. - Hook compaction (
hookCompaction) - compacts the pending-message context theUserPromptSubmithook injects, so repeated context doesn't pile up. - Usage control (
usageControl) - clampsread_contextandtask_statetool output inleanorstrictmode, regardless of the limit an agent asks for.
Genuinely important messages still come through live: anything tagged [IMPORTANT], handoffs, and MCP relay commands bypass the digest. Use the unified usage command for normal tuning:
ctxrelay usage status # show preset, hook, context, and ledger dry-run state
ctxrelay usage lean # practical daily lower-usage mode
ctxrelay usage strict # aggressive mode for expensive/parallel sessions
For very long sessions, compact the ledger deterministically. Dry-run prints the exact byte/entry impact; apply archives the original JSONL first, then replaces older bulky entries with hash-backed summaries:
ctxrelay usage ledger compact --dry-run
ctxrelay usage ledger compact --apply
The goal is a pair that surfaces decisions and handoffs loudly and routine chatter quietly. The defaults already do that - changing them is a tuning decision, not a starting point.
Putting it together
A healthy ContextRelay session looks like this: every fork and decision lands in the ledger; handoffs name the reason, the ask, the files, and who's next; reviews lead with the real findings; the coordinator owns git and nobody races it; the pair converges instead of looping; and the human is pulled in only for the few decisions that truly need them. None of it is heavy - it's a handful of habits that turn two capable agents into one effective team.
Next steps
- Handoffs, replies, and deliberation - the mechanics behind the three channels.
- Coordinator and git-write policy - who owns git, and how to change it safely.
- Read-only by default: safety and containment - the safe baseline these habits assume.
- Autonomy, idle scanner, and safe automation - when and how to let the pair do more on its own.