Two workers that correct each other

Open one worker with codex and another with cursor-agent on the same task, and have each review the other's diff.

Prompt#

Open an Orchemax worker with codex and another with cursor-agent on this task and have them correct each other.

What the Chair does#

Checks it is not a worker (denyWorkerFanout), resolves the project, then spawns one worker per preset and relays each diff to the other.

MCP calls#

dispatch_worker({
  "agent":  "codex",
  "prompt": "Rewrite client/internal/gateway/rotate.go so a 429 advances the ring. Report with verify_report.",
  "title":  "rotate ring on 429 — codex pass",
  "name":   "codex-a",
  "timeout_sec": 900
})

dispatch_worker({
  "agent":  "cursor",
  "prompt": "Review codex-a's diff on orch/<session>. Say what is wrong, do not rewrite it.",
  "title":  "rotate ring on 429 — cursor review",
  "name":   "cursor-b"
})

Or, when you want the acceptance criteria on the board:

task_add({ "title": "rotate ring on 429", "agent": "codex",
           "context": "gateway owns the ring; keys live in machine-home keys.env",
           "acceptance_criteria": ["go test ./internal/gateway/... -race passes",
                                   "no key material in logs"] })
task_run({ "id": "<task id>", "timeout_sec": 900 })

Relay between the two, then close:

msg_send({ "from_session": "<chair session>", "to_worker": "cursor-b",
           "kind": "handoff", "body": "codex-a diff on orch/<session>; review, do not rewrite" })
worktree_promote({ "session": "<the session you keep>" })

Expected response#

dispatch_worker and task_run return as soon as the worker is running: {task?, session_id, worker_id, name, worktree, branch, log, status: "running", note}. branch is orch/<session> — the branch you paste into the reviewer's prompt, available while the first worker is still writing. Pass wait: true to block until the worker exits instead; that reply adds exit, worktree_kept and verification (the worker's own half — tests[], symbols[], criteria[], summary from verify_report — plus Orchemax's observed half — guardrails, files[]). msg_send returns {message, delivery}.

Gotchas#

  • The preset id is cursor, not cursor-agent. cursor-agent is the binary behind it; Orchemax's preset key is cursor, and it runs as a plain subprocess — ACP transport ships only for gemini and opencode.
  • codex and cursor have headless defaults too. agents.cmds ships copilot, agy, claude, opencode, grok, kimi, commandcode, codex and cursor out of the box. mimo and zcode still exist only in agents.interactive. Dispatch a recognized CLI preset that has no agents.cmds.<preset> entry and Orchemax fails loudly to the Chair (preset "<preset>" has no headless command: set agents.cmds.<preset>) instead of silently running the stub agent.
  • dispatch_worker returns as soon as the worker is running (status: running, session_id, worker_id); the two workers run at the same time and the Chair gets a handoff message on the bus when each one exits. Pass wait: true to block until the exit code and verification are in. A worker cannot spawn the other half itself.
  • The reviewer still finds the diff after the writer exits. With the default keep_worktree: false a finished worker's worktree and orch/<session> branch are kept while another worker on the same project is still running (the reply says worktree_kept: true), and swept when the last one finishes. Before that, the first worker to finish deleted the branch its sibling had been told to review and the cross-review never happened.
  • Workers talk sideways with to_role: "sibling" or to_worker, never by opening each other's CLI.
  • An agent id Orchemax does not know is refused, with the list of presets that do exist — it no longer falls back to agents.default_preset and runs a different CLI that reports done. Free-form roles go through task_assign + task_run.
  • Ledger gate. With clarify_before_spawn on, the spawn is denied until orch ledger check passes; force: true (or ORCH_LEDGER_FORCE=1) overrides it.
  • Size timeout_sec to whether the task writes, not to how small it sounds. A real write — even "add one function" — means the agent reads the codebase, edits, then verifies (build/vet) before it exits: measured median 236s for cursor-agent on a one-line helper (R5-E2E item 103), so ≥480s (2×) is the floor for any write task. Reserve ≤180–300s for a genuinely read-only ask. The 900 above is for the real multi-file rewrite in this example. There is no default ceiling, so a Chair left to guess picks something much larger than the task needs.