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 orch 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, log, status: "running", note}. Pass wait: true to block until the worker exits instead; that reply adds exit and verification (the worker's own half — tests[], symbols[], criteria[], summary from verify_report — plus orch'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; orch'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 orch 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.
  • Workers talk sideways with to_role: "sibling" or to_worker, never by opening each other's CLI.
  • 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.