From 2f5930b41dcac69231491f8e6431923f4d7057e5 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 14 Oct 2026 22:35:06 -0700 Subject: [PATCH 01/30] fix(orchestration): refuse run-use by a worker on the Run it works for A worker holding an active Dispatch in a Run could bind that Run as its coordinator. That silently unbound the real coordinator, remembered the worker's address as a coordinator address (so the worker's own mail in that Run routed to the Run mailbox from then on), and when the worker later bound its own sub-Run, left the parent Run with no coordinator at all. bindRun now refuses a caller whose handle holds an active Dispatch in the Run, with worker_cannot_coordinate_own_run, before any effect. Creating its own Run for sub-workers is unchanged. --- .../runtime/orchestration/db/runs/run-binding.ts | 15 +++++++++++++++ .../orchestration-run-delivery-db.test.ts | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index b396b79f1ece..de857a69b031 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -40,6 +40,21 @@ export function bindRun( this.db.exec('ROLLBACK') return undefined } + const callerAddress = mailboxAddressOf(coordinator) + const workedDispatch = callerAddress + ? this.getActiveDispatchMailboxOwners(callerAddress).find( + (dispatch) => dispatch.run_id === params.runId + ) + : undefined + // Why: a worker binding the Run it works for unbinds its real coordinator, and the worker's + // own mail then routes to the Run mailbox for good. Lineage is fixed by the Dispatch. + if (workedDispatch) { + throw new OrchestrationError( + 'worker_cannot_coordinate_own_run', + `This caller works Dispatch ${workedDispatch.id} in Run ${params.runId}, so it cannot coordinate that Run. Report to its coordinator with orchestration send; to coordinate sub-workers, create your own Run with orchestration run-create. No effects were applied.`, + { effectsApplied: false } + ) + } const sameBinding = runBoundToCoordinator(run, coordinator) const adoption = this.getLegacyAdoption() const adoptedRun = adoption?.adopted_run_id === params.runId diff --git a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts index fdaa5e92a801..f78016297aed 100644 --- a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts +++ b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts @@ -223,6 +223,18 @@ describe('OrchestrationDb Run state', () => { }) describe('lightweight Run scope', () => { + it('refuses a worker binding the Run it works for, leaving its coordinator bound', () => { + const d = createDb() + const run = createBoundRun(d) + createRootDispatch(d, d.createTask({ spec: 'work', runId: run.id }).id, 'term_worker') + const bind = { runId: run.id, coordinatorHandle: 'term_worker', coordinatorPaneKey: null } + + expect(() => d.bindRun(bind)).toThrow( + expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' }) + ) + expect(d.getRun(run.id)?.coordinator_handle).toBe('term_coord') + }) + it('binds creation to one pane and fences that pane when it creates another Run', () => { const d = createDb() const first = d.createRun({ From 110fb8f7f293c829afd31baa333a28d8bb17560d Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:16:53 -0700 Subject: [PATCH 02/30] fix(orchestration): tell a worker naming its own Run where its mail is Workers that made a sub-Run ran `check --run `, were told "This coordinator terminal is bound to X, not Y", and ran run-use on the parent to "switch back", unbinding their coordinator. That refusal now says the Run belongs to their coordinator, that their own mail arrives through plain `check`, and that they report with `send`; it never mentions run-use. The run-use refusal now matches the caller the way check and send do (address, then pane, so a reminted terminal handle is still caught), also covers Runs above the caller's own through recorded creator_dispatch_id lineage, skips context-only self-dispatches so a coordinator can still rebind its own Run, and passes its code through the RPC wire. --- .../orchestration/db/dispatch-depth.ts | 2 +- .../orchestration/db/runs/run-binding.ts | 21 +++--- .../db/runs/run-worker-lineage.ts | 64 +++++++++++++++++++ .../orchestration-run-delivery-db.test.ts | 43 ++++++++++--- src/main/runtime/rpc/errors.ts | 1 + .../run-bound-mailbox-safety.test.ts | 4 +- .../methods/orchestration/runs/run-scope.ts | 13 ++++ .../orchestration-session-coordinator.test.ts | 25 ++++++++ 8 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 src/main/runtime/orchestration/db/runs/run-worker-lineage.ts diff --git a/src/main/runtime/orchestration/db/dispatch-depth.ts b/src/main/runtime/orchestration/db/dispatch-depth.ts index 32020aa3b3f9..e03a73eed21c 100644 --- a/src/main/runtime/orchestration/db/dispatch-depth.ts +++ b/src/main/runtime/orchestration/db/dispatch-depth.ts @@ -62,7 +62,7 @@ export function recordedCreatorIdentity(creator: DispatchCreator): { * terminal. Nothing was delegated, so it is not a nesting parent. Rows written before v37 record * no creator and keep counting, which is the pre-v37 answer and fails closed. */ -function isSelfCreatedDispatch(row: DispatchContextRow): boolean { +export function isSelfCreatedDispatch(row: DispatchContextRow): boolean { if (row.creator_pane_key && row.assignee_pane_key) { return isEquivalentPaneKey(row.creator_pane_key, row.assignee_pane_key) } diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index de857a69b031..bc720ab00019 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -4,6 +4,10 @@ import { LEGACY_CONTRACT_VERSION } from '../contract-constants' import { isEquivalentPaneKey } from '../pane-key-match' import type { OrchestrationDb } from '../orchestration-db' import type { OrcaSessionId } from '../../../../../shared/orca-session-address' +import { + findCallerDispatchWorkingForRun, + workerCannotCoordinateRunError +} from './run-worker-lineage' import { mailboxAddressOf, runBoundToCoordinator, @@ -40,20 +44,11 @@ export function bindRun( this.db.exec('ROLLBACK') return undefined } - const callerAddress = mailboxAddressOf(coordinator) - const workedDispatch = callerAddress - ? this.getActiveDispatchMailboxOwners(callerAddress).find( - (dispatch) => dispatch.run_id === params.runId - ) - : undefined - // Why: a worker binding the Run it works for unbinds its real coordinator, and the worker's - // own mail then routes to the Run mailbox for good. Lineage is fixed by the Dispatch. + // Why: a worker binding a Run it works for unbinds the coordinator it reports to, and its + // own mail then routes to that Run's mailbox for good. Lineage is fixed by the Dispatch rows. + const workedDispatch = findCallerDispatchWorkingForRun(this, coordinator, params.runId) if (workedDispatch) { - throw new OrchestrationError( - 'worker_cannot_coordinate_own_run', - `This caller works Dispatch ${workedDispatch.id} in Run ${params.runId}, so it cannot coordinate that Run. Report to its coordinator with orchestration send; to coordinate sub-workers, create your own Run with orchestration run-create. No effects were applied.`, - { effectsApplied: false } - ) + throw workerCannotCoordinateRunError(workedDispatch, params.runId, 'bind') } const sameBinding = runBoundToCoordinator(run, coordinator) const adoption = this.getLegacyAdoption() diff --git a/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts b/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts new file mode 100644 index 000000000000..0215c351d624 --- /dev/null +++ b/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts @@ -0,0 +1,64 @@ +import type { DispatchContextRow } from '../../types' +import { OrchestrationError } from '../../orchestration-error' +import { isSelfCreatedDispatch } from '../dispatch-depth' +import type { OrchestrationDb } from '../orchestration-db' +import { + mailboxAddressOf, + type OrchestrationCoordinatorKey +} from '../../orchestration-caller-identity' + +// Why: creator links only point at older rows, so this caps a corrupt chain, not real nesting. +const MAX_LINEAGE_STEPS = 32 + +/** + * The caller's live Dispatch that works for this Run: one in the Run itself, or one started + * from work in it (recorded by creator_dispatch_id). The caller is matched the way `check` and + * `send` find a worker's Dispatch: its address, then its pane. A context-only self-dispatch is + * a coordinator's own bookkeeping, not work for anyone, so it never counts. + */ +export function findCallerDispatchWorkingForRun( + db: OrchestrationDb, + caller: OrchestrationCoordinatorKey, + runId: string +): DispatchContextRow | undefined { + const address = mailboxAddressOf(caller) + if (address === null) { + return undefined + } + for (const own of db.getActiveDispatchMailboxOwners(address, caller.paneKey ?? undefined)) { + if (isSelfCreatedDispatch(own)) { + continue + } + let row: DispatchContextRow | undefined = own + for (let step = 0; row && step < MAX_LINEAGE_STEPS; step++) { + if (row.run_id === runId) { + return own + } + row = row.creator_dispatch_id ? db.getDispatchContextById(row.creator_dispatch_id) : undefined + } + } + return undefined +} + +/** Refuse a worker acting as the coordinator of a Run it works for; never points it at run-use. */ +export function workerCannotCoordinateRunError( + dispatch: DispatchContextRow, + runId: string, + attempted: 'bind' | 'act' +): OrchestrationError { + const role = + dispatch.run_id === runId + ? `You work Dispatch ${dispatch.id} in Run ${runId}, so that Run belongs to your coordinator and you cannot coordinate it.` + : `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id}, which was started from work in Run ${runId}, so you cannot coordinate Run ${runId}.` + const mail = + 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' + const bindOnly = + attempted === 'bind' + ? ' To coordinate sub-workers, create your own Run with `orchestration run-create`. If you must take this Run over, first finish your Dispatch with worker_done.' + : '' + return new OrchestrationError( + 'worker_cannot_coordinate_own_run', + `${role} ${mail}${bindOnly} No effects were applied.`, + { effectsApplied: false } + ) +} diff --git a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts index f78016297aed..327d1add38cb 100644 --- a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts +++ b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts @@ -4,6 +4,7 @@ import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { LEGACY_RUN_ID, OrchestrationDb } from './db' import { createRootDispatch } from './db/root-dispatch-test-fixture' +import type { DispatchCreator } from './db/dispatch-depth' describe('OrchestrationDb Run state', () => { let db: OrchestrationDb | undefined @@ -17,11 +18,13 @@ describe('OrchestrationDb Run state', () => { return db } + const COORD_PANE = 'tab_coord:11111111-1111-4111-8111-111111111111' + function createBoundRun(d: OrchestrationDb) { return d.createRun({ objective: 'Mailbox test', coordinatorHandle: 'term_coord', - coordinatorPaneKey: 'tab_coord:11111111-1111-4111-8111-111111111111' + coordinatorPaneKey: COORD_PANE }) } @@ -223,16 +226,38 @@ describe('OrchestrationDb Run state', () => { }) describe('lightweight Run scope', () => { - it('refuses a worker binding the Run it works for, leaving its coordinator bound', () => { + it('refuses a worker binding a Run above its own, but not a coordinator with a self-dispatch', () => { const d = createDb() - const run = createBoundRun(d) - createRootDispatch(d, d.createTask({ spec: 'work', runId: run.id }).id, 'term_worker') - const bind = { runId: run.id, coordinatorHandle: 'term_worker', coordinatorPaneKey: null } + const parent = createBoundRun(d) + const coordinator = { kind: 'terminal', handle: 'term_coord', paneKey: COORD_PANE } as const + const dispatchIn = (runId: string, handle: string, creator: DispatchCreator) => + d.createDispatchContext({ + taskId: d.createTask({ spec: 'work', runId }).id, + assigneeHandle: handle, + creator, + maxDepth: Number.MAX_SAFE_INTEGER + }) + // A context-only self-dispatch is bookkeeping, so its coordinator keeps its Run. + dispatchIn(parent.id, 'term_coord', coordinator) + expect( + d.bindRun({ + runId: parent.id, + coordinatorHandle: 'term_coord', + coordinatorPaneKey: COORD_PANE + }) + ).toBeDefined() + dispatchIn(parent.id, 'term_worker', coordinator) + const sub = d.createRun({ + objective: 'sub', + coordinatorHandle: 'term_worker', + coordinatorPaneKey: null + }) + dispatchIn(sub.id, 'term_grand', { kind: 'terminal', handle: 'term_worker' }) - expect(() => d.bindRun(bind)).toThrow( - expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' }) - ) - expect(d.getRun(run.id)?.coordinator_handle).toBe('term_coord') + expect(() => + d.bindRun({ runId: parent.id, coordinatorHandle: 'term_grand', coordinatorPaneKey: null }) + ).toThrow(expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' })) + expect(d.getRun(parent.id)?.coordinator_handle).toBe('term_coord') }) it('binds creation to one pane and fences that pane when it creates another Run', () => { diff --git a/src/main/runtime/rpc/errors.ts b/src/main/runtime/rpc/errors.ts index a78f186bbc24..574e9f5e80df 100644 --- a/src/main/runtime/rpc/errors.ts +++ b/src/main/runtime/rpc/errors.ts @@ -139,6 +139,7 @@ const STRUCTURED_RUNTIME_PASSTHROUGH_CODES: ReadonlySet = new Set([ 'worker_prompt_too_large', 'terminal_worktree_mismatch', 'terminal_is_coordinator', + 'worker_cannot_coordinate_own_run', 'request_mismatch', 'mutation_ledger_full', 'legacy_read_only', diff --git a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-safety.test.ts b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-safety.test.ts index c7c4edf23629..e100e5673687 100644 --- a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-safety.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-safety.test.ts @@ -510,7 +510,9 @@ describe('Run-bound lead mailbox boundaries', () => { runId: leadRun.id, messages: [{ subject: 'Run report' }] }) - await expect(check({ run: dispatch.run_id })).rejects.toMatchObject({ code: 'consumer_fenced' }) + await expect(check({ run: dispatch.run_id })).rejects.toMatchObject({ + code: 'worker_cannot_coordinate_own_run' + }) expect(state.db.getMessageById(pending.id)?.read).toBe(0) }) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts index 0496c92806ee..80eb68e6af27 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts @@ -12,6 +12,10 @@ import { type OrchestrationSessionCaller } from '../../../../orchestration/orchestration-caller-identity' import { resolveDeclaredCallerParty } from '../../../../orchestration/orchestration-party' +import { + findCallerDispatchWorkingForRun, + workerCannotCoordinateRunError +} from '../../../../orchestration/db/runs/run-worker-lineage' export type RunScopeParams = { runId?: string @@ -153,6 +157,15 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar ) } const current = db.getCurrentRunForCoordinator(caller) + // Why: "bound to X, not Y" reads as a nudge to rebind; a worker naming the Run it works for + // has to learn where its own mail is instead. + const workedDispatch = + explicit && current?.id !== explicit.id + ? findCallerDispatchWorkingForRun(db, caller, explicit.id) + : undefined + if (explicit && workedDispatch) { + throw workerCannotCoordinateRunError(workedDispatch, explicit.id, 'act') + } if (!current) { if (explicit) { throw new OrchestrationError( diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index a60c3a169168..8cd78e5d0c2b 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -599,6 +599,31 @@ describe('a structured worker that names itself by session id', () => { } }) + it('cannot take or read the Run it works for after making a sub-Run, and keeps its mail', async () => { + // The 10-14 incident: a worker made a sub-Run, `check --run ` said "bound to X, not Y", + // and it ran run-use on the parent, unbinding its coordinator. + const { runId } = dispatchToWorker() + const as = (method: string, params: Row) => + h.dispatch(orchestrationRequest(method, params, { sessionId: workerSession })) + resultOf(await as('orchestration.runCreate', { objective: 'sub' })) + + const read = await as('orchestration.check', { run: runId, peek: true }) + expect(read).toMatchObject({ ok: false, error: { code: 'worker_cannot_coordinate_own_run' } }) + expect(JSON.stringify(read)).not.toContain('run-use') + expect(resultOf(await as('orchestration.check', { peek: true }))).toMatchObject({ + messages: [{ subject: 'instructions' }] + }) + + expect(await as('orchestration.runUse', { id: runId })).toMatchObject({ + ok: false, + error: { code: 'worker_cannot_coordinate_own_run', data: { effectsApplied: false } } + }) + expect(h.db.getRunRaw(runId)).toMatchObject({ + coordinator_handle: 'term_coord', + consumer_generation: 1 + }) + }) + it.each([ ['its handle', handle], ['its session address', ADDRESS_Y] From f32d59106ec45641a53d52c245e9a1729421e24b Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:06:12 -0700 Subject: [PATCH 03/30] feat(orchestration): refuse run-use over a live coordinator without --takeover run-use silently took a Run from whoever coordinated it, even a chat or agent that was still running. It now refuses with run_coordinator_live when there is positive evidence the current coordinator is running (its chat is still reachable, or the agent hooks prove its terminal agent live) unless the caller passes --takeover. Anything unproven (closed chat, exited or unhooked terminal, another host) rebinds as before, so restart handoffs need no flag. The probe is async in the RPC handler; bindRun enforces it only if the Run is still bound to the probed party. A caller working a Dispatch can never take over, with or without the flag (worker_cannot_take_over_run). The help text now says run-use is for resuming a Run and that workers never use it. --- .../handlers/orchestration/run-handlers.ts | 1 + src/cli/root-help-text-primary.ts | 2 +- src/cli/specs/orchestration.ts | 7 +- ...e-adopt-terminal-orphans-from-inventory.ts | 7 ++ .../orchestration/db/runs/run-binding.ts | 43 +++++++- .../db/runs/run-worker-lineage.ts | 64 +++++++---- src/main/runtime/rpc/errors.ts | 2 + .../run-bound-mailbox-interleavings.test.ts | 3 +- .../runs/run-coordinator-liveness.ts | 43 ++++++++ .../runs/run-use-takeover.test.ts | 101 ++++++++++++++++++ .../rpc/methods/orchestration/runs/runs.ts | 16 ++- .../orchestration-session-coordinator.test.ts | 12 ++- src/shared/cli-argument-boundary.ts | 2 + .../rpc-contract/orchestration-runs-params.ts | 3 +- 14 files changed, 274 insertions(+), 32 deletions(-) create mode 100644 src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts create mode 100644 src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts diff --git a/src/cli/handlers/orchestration/run-handlers.ts b/src/cli/handlers/orchestration/run-handlers.ts index d8abe3533c2c..8da0f45f2399 100644 --- a/src/cli/handlers/orchestration/run-handlers.ts +++ b/src/cli/handlers/orchestration/run-handlers.ts @@ -28,6 +28,7 @@ export const ORCHESTRATION_RUN_HANDLERS: Record = { }>(client, flags, 'orchestration.runUse', { id: getRequiredStringFlag(flags, 'id'), from, + ...(flags.has('takeover') ? { takeover: true } : {}), ...(flags.has('takeover-legacy') ? { takeoverLegacy: true } : {}) }) printResult(result, json, (r) => `Using Run ${r.run.id}: ${r.run.objective}`) diff --git a/src/cli/root-help-text-primary.ts b/src/cli/root-help-text-primary.ts index 398db623fb77..0a249bb904e0 100644 --- a/src/cli/root-help-text-primary.ts +++ b/src/cli/root-help-text-primary.ts @@ -107,7 +107,7 @@ export const ROOT_HELP_TEXT_PRIMARY = [ '', 'Orchestration:', ' orchestration run-create Create and bind a lightweight orchestration Run', - ' orchestration run-use Bind this coordinator to an existing Run', + ' orchestration run-use Resume coordinating a Run (workers never use this)', " orchestration run-current Show this coordinator's bound Run", ' orchestration run-list List lightweight orchestration Runs', ' orchestration run-show Show one lightweight orchestration Run', diff --git a/src/cli/specs/orchestration.ts b/src/cli/specs/orchestration.ts index b879de5d5935..ecb1ec6cfd5b 100644 --- a/src/cli/specs/orchestration.ts +++ b/src/cli/specs/orchestration.ts @@ -16,12 +16,13 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [ }, { path: ['orchestration', 'run-use'], - summary: 'Bind this coordinator to an existing Run', + summary: 'Resume coordinating a Run (workers never use this)', usage: - 'orca orchestration run-use --id [--from ] [--takeover-legacy] [--retry-request ] [--json]', - allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover-legacy', 'retry-request'], + 'orca orchestration run-use --id [--from ] [--takeover] [--takeover-legacy] [--retry-request ] [--json]', + allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover', 'takeover-legacy', 'retry-request'], identityFlagRoles: { from: 'caller' }, notes: [ + 'Refused while another coordinator of the Run is still running; --takeover replaces it and fences its unread mail. A caller working a Dispatch can never take over.', '--takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' ] }, diff --git a/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts b/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts index 0361a3411bd2..4b42a358ef41 100644 --- a/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts +++ b/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts @@ -206,6 +206,13 @@ export abstract class OrcaRuntimeWithAdoptTerminalOrphansFromInventory extends O ) } + /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ + checkCoordinatorAgentPresence( + paneKey: string + ): Promise<'live' | 'unverifiable' | 'exited' | null> { + return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) + } + getLiveTerminalPaneKey(handle: string): string | null { const structured = resolveStructuredWorkerAuthority( handle, diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index bc720ab00019..4a04a774d5fc 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -6,12 +6,15 @@ import type { OrchestrationDb } from '../orchestration-db' import type { OrcaSessionId } from '../../../../../shared/orca-session-address' import { findCallerDispatchWorkingForRun, - workerCannotCoordinateRunError + findCallerWorkerDispatches, + workerCannotCoordinateRunError, + workerCannotTakeOverRunError } from './run-worker-lineage' import { mailboxAddressOf, runBoundToCoordinator, - runCoordinatorKey + runCoordinatorKey, + type OrchestrationCoordinatorKey } from '../../orchestration-caller-identity' export function bindRun( @@ -23,6 +26,10 @@ export function bindRun( /** The coordinator's bare Orca session id when it is a structured session; see orca-session-address. */ coordinatorOrcaSessionId?: OrcaSessionId | null takeoverLegacy?: boolean + /** The user asked to replace a coordinator that is still running. */ + takeover?: boolean + /** The coordinator the RPC layer proved live just before this call; enforced only if the Run is still bound to it. */ + provenLiveCoordinator?: OrchestrationCoordinatorKey | null legacyCoordinatorAuthority?: { runId: string principalId: string | null @@ -50,6 +57,13 @@ export function bindRun( if (workedDispatch) { throw workerCannotCoordinateRunError(workedDispatch, params.runId, 'bind') } + const explicitTakeover = params.takeover === true || params.takeoverLegacy === true + const takeoverByWorker = explicitTakeover + ? findCallerWorkerDispatches(this, coordinator)[0] + : undefined + if (takeoverByWorker) { + throw workerCannotTakeOverRunError(takeoverByWorker, params.runId) + } const sameBinding = runBoundToCoordinator(run, coordinator) const adoption = this.getLegacyAdoption() const adoptedRun = adoption?.adopted_run_id === params.runId @@ -131,6 +145,18 @@ export function bindRun( } ) } + if ( + params.provenLiveCoordinator && + !sameBinding && + !provenLegacyBinding && + !explicitTakeover && + runBoundToCoordinator(run, params.provenLiveCoordinator) + ) { + const worker = findCallerWorkerDispatches(this, coordinator)[0] + throw worker + ? workerCannotTakeOverRunError(worker, params.runId) + : runCoordinatorLiveError(params.runId, params.provenLiveCoordinator) + } this.unbindOtherRunsForCoordinator(coordinator, params.runId) // The mailbox address of the coordinator being replaced and of the one binding now. for (const address of new Set([ @@ -196,6 +222,19 @@ export function bindRun( return this.getRun(params.runId) } +// No recoveryCommand on purpose: agents run those mechanically, and the flag must carry user intent. +function runCoordinatorLiveError( + runId: string, + live: OrchestrationCoordinatorKey +): OrchestrationError { + const holder = mailboxAddressOf(live) ?? 'another coordinator' + return new OrchestrationError( + 'run_coordinator_live', + `Run ${runId} is coordinated by ${holder}, which is still running. Nothing changed. Take it over only if the user asked you to: orca orchestration run-use --id ${runId} --takeover. That fences ${holder}; its unacknowledged mail is replayed to you.`, + { effectsApplied: false, runId, coordinator: holder } + ) +} + export type RunBindingMethods = { bindRun: typeof bindRun } diff --git a/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts b/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts index 0215c351d624..846fc18b05b3 100644 --- a/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts +++ b/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts @@ -11,24 +11,33 @@ import { const MAX_LINEAGE_STEPS = 32 /** - * The caller's live Dispatch that works for this Run: one in the Run itself, or one started - * from work in it (recorded by creator_dispatch_id). The caller is matched the way `check` and - * `send` find a worker's Dispatch: its address, then its pane. A context-only self-dispatch is - * a coordinator's own bookkeeping, not work for anyone, so it never counts. + * The caller's live Dispatches, matched the way `check` and `send` find a worker's Dispatch: its + * address, then its pane. A context-only self-dispatch is a coordinator's own bookkeeping, not work + * for anyone, so it never makes the caller a worker. + */ +export function findCallerWorkerDispatches( + db: OrchestrationDb, + caller: OrchestrationCoordinatorKey +): DispatchContextRow[] { + const address = mailboxAddressOf(caller) + if (address === null) { + return [] + } + return db + .getActiveDispatchMailboxOwners(address, caller.paneKey ?? undefined) + .filter((dispatch) => !isSelfCreatedDispatch(dispatch)) +} + +/** + * The caller's live Dispatch that works for this Run: one in the Run itself, or one started from + * work in it (recorded by creator_dispatch_id). */ export function findCallerDispatchWorkingForRun( db: OrchestrationDb, caller: OrchestrationCoordinatorKey, runId: string ): DispatchContextRow | undefined { - const address = mailboxAddressOf(caller) - if (address === null) { - return undefined - } - for (const own of db.getActiveDispatchMailboxOwners(address, caller.paneKey ?? undefined)) { - if (isSelfCreatedDispatch(own)) { - continue - } + for (const own of findCallerWorkerDispatches(db, caller)) { let row: DispatchContextRow | undefined = own for (let step = 0; row && step < MAX_LINEAGE_STEPS; step++) { if (row.run_id === runId) { @@ -40,6 +49,11 @@ export function findCallerDispatchWorkingForRun( return undefined } +const WORKER_MAIL_HINT = + 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' +const SUB_RUN_HINT = + 'To coordinate sub-workers, create your own Run with `orchestration run-create`.' + /** Refuse a worker acting as the coordinator of a Run it works for; never points it at run-use. */ export function workerCannotCoordinateRunError( dispatch: DispatchContextRow, @@ -50,15 +64,27 @@ export function workerCannotCoordinateRunError( dispatch.run_id === runId ? `You work Dispatch ${dispatch.id} in Run ${runId}, so that Run belongs to your coordinator and you cannot coordinate it.` : `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id}, which was started from work in Run ${runId}, so you cannot coordinate Run ${runId}.` - const mail = - 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' - const bindOnly = - attempted === 'bind' - ? ' To coordinate sub-workers, create your own Run with `orchestration run-create`. If you must take this Run over, first finish your Dispatch with worker_done.' - : '' + const bindOnly = attempted === 'bind' ? ` ${SUB_RUN_HINT}` : '' return new OrchestrationError( 'worker_cannot_coordinate_own_run', - `${role} ${mail}${bindOnly} No effects were applied.`, + `${role} ${WORKER_MAIL_HINT}${bindOnly} No effects were applied.`, { effectsApplied: false } ) } + +/** A worker never takes over a Run, even with the flag: the flag carries a user's intent, not a worker's. */ +export function workerCannotTakeOverRunError( + dispatch: DispatchContextRow, + runId: string +): OrchestrationError { + return new OrchestrationError( + 'worker_cannot_take_over_run', + `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id}, so you cannot take over Run ${runId}. ${WORKER_MAIL_HINT} ${SUB_RUN_HINT} No effects were applied.`, + { effectsApplied: false } + ) +} + +/** What a worker with no Run of its own is told instead of "bind a Run first". */ +export function unboundWorkerMessage(dispatch: DispatchContextRow): string { + return `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id} and are not bound to a Run of your own. ${WORKER_MAIL_HINT} ${SUB_RUN_HINT} No effects were applied.` +} diff --git a/src/main/runtime/rpc/errors.ts b/src/main/runtime/rpc/errors.ts index 574e9f5e80df..9bd4af84bd43 100644 --- a/src/main/runtime/rpc/errors.ts +++ b/src/main/runtime/rpc/errors.ts @@ -140,6 +140,8 @@ const STRUCTURED_RUNTIME_PASSTHROUGH_CODES: ReadonlySet = new Set([ 'terminal_worktree_mismatch', 'terminal_is_coordinator', 'worker_cannot_coordinate_own_run', + 'worker_cannot_take_over_run', + 'run_coordinator_live', 'request_mismatch', 'mutation_ledger_full', 'legacy_read_only', diff --git a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts index 4e92d7c35c16..86694bccf76b 100644 --- a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts @@ -103,7 +103,8 @@ describe('Run binding during Dispatch checks', () => { runId: run.id, messages: [{ subject: 'after bind' }] }) - if (phase === 'recovering') { + // runUse awaits the target coordinator's liveness first, so its check has parked by then. + if (phase === 'recovering' && method === 'runCreate') { expect(wait).not.toHaveBeenCalled() } } diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts new file mode 100644 index 000000000000..ee9af51d3eb7 --- /dev/null +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -0,0 +1,43 @@ +import type { OrchestrationDb } from '../../../../orchestration/db' +import type { OrcaRuntimeService } from '../../../../orca-runtime' +import { + runCoordinatorKey, + type OrchestrationCoordinatorKey +} from '../../../../orchestration/orchestration-caller-identity' +import { readAgentSessionRecordStore } from '../../../../orchestration/structured-session-lineage' +import { + lookupOrcaAgentSession, + structuredSessionMailReach +} from '../../../../orchestration/structured-session-mail-address' + +/** + * The Run's current coordinator when there is positive evidence it is still running; else null. + * Only proof counts: a closed chat, an exited or unhooked terminal, another host, or anything that + * cannot be checked reads as not live, so a restart handoff never needs a flag. + */ +export async function provenLiveRunCoordinator( + runtime: OrcaRuntimeService, + db: OrchestrationDb, + runId: string +): Promise { + const run = db.getRunRaw(runId) + if (!run || run.legacy === 1) { + return null + } + const key = runCoordinatorKey(run) + if (key.orcaSessionId !== null) { + // Why: the record store opens lazily, as for a send naming a session. + await runtime.ensureStructuredAgentSessionHost().catch(() => undefined) + const store = readAgentSessionRecordStore() + const found = store ? lookupOrcaAgentSession(store, key.orcaSessionId) : null + return store && + found?.kind === 'found' && + structuredSessionMailReach(store, found.record, db).kind === 'reachable' + ? key + : null + } + if (key.paneKey !== null) { + return (await runtime.checkCoordinatorAgentPresence(key.paneKey)) === 'live' ? key : null + } + return null +} diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts new file mode 100644 index 000000000000..34b89d89adfd --- /dev/null +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -0,0 +1,101 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createRootDispatch } from '../../../../orchestration/db/root-dispatch-test-fixture' +import { + createSessionCallerHarness, + idOf, + orchestrationRequest, + resultOf, + SESSION_X, + SESSION_Y, + WORKER_HANDLE, + type SessionCallerHarness +} from '../../../orchestration-session-caller-test-fixture' + +const hostRef = vi.hoisted((): { current: unknown } => ({ current: null })) +vi.mock('../../../../../native-chat/agent-session-wire/structured-agent-session-registry', () => ({ + getStructuredAgentSessionHost: () => hostRef.current +})) + +const COORD_PANE = 'tab_coord:12121212-1212-4212-8212-121212121212' + +describe('run-use against a Run whose coordinator is still running', () => { + let h: SessionCallerHarness + + beforeEach(() => { + h = createSessionCallerHarness(hostRef) + }) + afterEach(() => { + h.close() + vi.restoreAllMocks() + }) + + const as = (sessionId: string | undefined, method: string, params: Record) => + h.dispatch(orchestrationRequest(method, params, { sessionId })) + async function chatRun(): Promise { + return idOf(resultOf(await as(SESSION_X, 'orchestration.runCreate', { objective: 'o' })).run) + } + + it('refuses a second chat until it passes --takeover, then fences the first', async () => { + const runId = await chatRun() + const before = h.db.getRunRaw(runId) + + expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ + ok: false, + error: { code: 'run_coordinator_live', data: { effectsApplied: false } } + }) + expect(h.db.getRunRaw(runId)).toEqual(before) + + resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true })) + expect(await as(SESSION_X, 'orchestration.check', { run: runId })).toMatchObject({ + ok: false, + error: { code: 'consumer_fenced' } + }) + }) + + it.each([ + ['its chat was closed', 'closed'], + ['its terminal agent exited', 'exited'], + ['no hook identifies its terminal agent', null] + ] as const)('hands the Run over without a flag when %s', async (_label, state) => { + let runId: string + if (state === 'closed') { + runId = await chatRun() + hostRef.current = { + deps: { + store: { + getRecord: (id: string) => h.records.get(id) ?? null, + listRecords: () => [...h.records.values()], + getVisibleSessionTabIndex: () => ({ present: true, sessionIds: [SESSION_Y] }) + } + } + } + } else { + runId = h.db.createRun({ + objective: 'o', + coordinatorHandle: 'term_coord', + coordinatorPaneKey: COORD_PANE + }).id + vi.spyOn(h.runtime, 'checkCoordinatorAgentPresence').mockResolvedValue(state) + } + + resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId })) + expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_Y) + }) + + it('never lets a worker take over, with or without the flag', async () => { + const runId = await chatRun() + const home = h.db.createRun({ + objective: 'home', + coordinatorHandle: null, + coordinatorPaneKey: null + }) + createRootDispatch(h.db, h.db.createTask({ spec: 'w', runId: home.id }).id, WORKER_HANDLE) + + for (const takeover of [false, true]) { + expect( + await as(undefined, 'orchestration.runUse', { id: runId, from: WORKER_HANDLE, takeover }) + ).toMatchObject({ ok: false, error: { code: 'worker_cannot_take_over_run' } }) + } + expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_X) + }) +}) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts index 6af5ac6a2edb..13231465641e 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts @@ -3,7 +3,11 @@ import { OrchestrationError } from '../../../../orchestration/orchestration-erro import { assertCallerHandleMatchesEvidence, resolveOrchestrationCaller } from './run-scope' import { exposeRun } from './run-receipt' import type { OrcaRuntimeService } from '../../../../orca-runtime' -import type { OrchestrationCallerIdentity } from '../../../../orchestration/orchestration-caller-identity' +import { + runBoundToCoordinator, + type OrchestrationCallerIdentity +} from '../../../../orchestration/orchestration-caller-identity' +import { provenLiveRunCoordinator } from './run-coordinator-liveness' import { currentDispatchAssigneeRun } from '../messaging/recipient-routing' import { RunCreateParams, @@ -57,7 +61,7 @@ export const ORCHESTRATION_RUN_METHODS = [ name: 'orchestration.runUse', permission: 'workspace', params: RunUseParams, - handler: ( + handler: async ( params, { runtime, @@ -89,12 +93,20 @@ export const ORCHESTRATION_RUN_METHODS = [ assertCallerHandleMatchesEvidence(runtime, params.from, orchestrationCompatibilityEvidence) const db = runtime.getOrchestrationDb() const priorRun = db.getCurrentRunForCoordinator(caller) + const target = db.getRunRaw(params.id) + // Why: liveness probes are async, so they run here; bindRun enforces the answer in its transaction. + const provenLiveCoordinator = + target && !runBoundToCoordinator(target, caller) + ? await provenLiveRunCoordinator(runtime, db, params.id) + : null const run = db.bindRun({ runId: params.id, coordinatorHandle: caller.terminalHandle, coordinatorPaneKey: caller.paneKey, coordinatorOrcaSessionId: caller.orcaSessionId, takeoverLegacy: params.takeoverLegacy, + takeover: params.takeover, + provenLiveCoordinator, legacyCoordinatorAuthority }) if (!run) { diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index 8cd78e5d0c2b..cb38305d6115 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -238,7 +238,9 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( run: runId }) - expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ + expect( + await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true }) + ).toMatchObject({ run: { id: runId } }) @@ -275,7 +277,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( const deliver = vi.spyOn(h.db, 'getOrCreateRunDelivery') const acknowledge = vi.spyOn(h.db, 'acknowledgeRunDelivery') - await as(SESSION_Y, 'orchestration.runUse', { id: runId }) + await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true }) const fenced = await waiting expect(fenced).toMatchObject({ ok: false, error: { code: 'consumer_fenced' } }) @@ -533,7 +535,11 @@ describe('a structured worker that names itself by session id', () => { // A chat takes the Run over; the worker's addresses stay remembered as a former coordinator's. resultOf( await h.dispatch( - orchestrationRequest('orchestration.runUse', { id: runId }, { sessionId: SESSION_X }) + orchestrationRequest( + 'orchestration.runUse', + { id: runId, takeover: true }, + { sessionId: SESSION_X } + ) ) ) expect(h.db.getRunMailboxOwnerIdsForHandle(ADDRESS_Y)).toEqual([runId]) diff --git a/src/shared/cli-argument-boundary.ts b/src/shared/cli-argument-boundary.ts index da732cab23f9..4b6fb17d6c61 100644 --- a/src/shared/cli-argument-boundary.ts +++ b/src/shared/cli-argument-boundary.ts @@ -50,6 +50,8 @@ export const CLI_BOOLEAN_FLAGS = new Set([ 'staged', 'supervise', 'tab', + 'takeover', + 'takeover-legacy', 'tasks', 'text-stdin', 'unread', diff --git a/src/shared/rpc-contract/orchestration-runs-params.ts b/src/shared/rpc-contract/orchestration-runs-params.ts index 30aad1e9eae6..e3737fe6cad1 100644 --- a/src/shared/rpc-contract/orchestration-runs-params.ts +++ b/src/shared/rpc-contract/orchestration-runs-params.ts @@ -10,7 +10,8 @@ export const RunCreateParams = z.object({ export const RunUseParams = z.object({ id: requiredString('Missing --id'), from: requiredString('Missing coordinator terminal'), - takeoverLegacy: OptionalBoolean + takeoverLegacy: OptionalBoolean, + takeover: OptionalBoolean }) export const RunCurrentParams = z.object({ from: requiredString('Missing coordinator terminal') }) From 0d918d4f090b6eb87356b260df374af554658b18 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:06:18 -0700 Subject: [PATCH 04/30] fix(orchestration): stop pointing workers at run-use Workers learned run-use from error text: "No Run is bound. Use run-create or run-use first", "Rebind with orchestration run-use", and "Rebind this terminal with orchestration run-use". Each now says what the caller can do instead. An unbound worker is told its mail arrives through plain check and it reports with send; a displaced coordinator is told to stop and take the Run back only if the user asks. The orchestration skill now says run-use resumes your own Run, --takeover needs the user's word, and workers never run it. --- skill-guides/orchestration.md | 4 ++ .../references/worker-contract.md | 5 +- src/cli/bundled-skill-guides.ts | 6 +-- .../orchestration/db/runs/run-delivery.ts | 2 +- .../gates/gate-run-authorization.test.ts | 2 +- .../orchestration/messaging/check-methods.ts | 2 +- .../methods/orchestration/runs/run-scope.ts | 46 +++++++++++++------ .../orchestration-session-coordinator.test.ts | 2 +- 8 files changed, 48 insertions(+), 21 deletions(-) diff --git a/skill-guides/orchestration.md b/skill-guides/orchestration.md index 728eeb473a0e..dfe8903665e1 100644 --- a/skill-guides/orchestration.md +++ b/skill-guides/orchestration.md @@ -108,6 +108,10 @@ ORCA orchestration worker-start --spec "" --worktree current --ag ORCA orchestration check --wait --types "worker_done,escalation,question" --timeout-ms 900000 --json ``` +After a restart, resume your own Run with `run-use --id `. It is refused +while another coordinator of that Run is still running; replace it with +`--takeover` only when the user asks. Workers never run `run-use`. + If `worker-start` exits non-zero, do not relaunch. Read the receipt's `failedStage` and `residualResources`, then load `references/recovery-and-cleanup.md`. diff --git a/skill-guides/orchestration/references/worker-contract.md b/skill-guides/orchestration/references/worker-contract.md index a2315b7ceeee..74b8c023bca6 100644 --- a/skill-guides/orchestration/references/worker-contract.md +++ b/skill-guides/orchestration/references/worker-contract.md @@ -46,7 +46,10 @@ run — and once more immediately before `worker_done`, so a redirect or a cancellation lands before the Task settles. `check` names its caller with `--terminal`, never `--from`. Stop checking after `worker_done`. -If `check` returns `consumer_fenced`, this process no longer owns its Dispatch: +Never run `run-use`. If you created your own Run, plain `check` still returns +your Dispatch's mail; report to your coordinator with `send`. + +If a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch: the Attempt was re-attached to another worker or settled without you. Stop, do not send `worker_done`, and do not retry the check. An empty `check` never means you were replaced; `consumer_fenced` is the only way you learn that. diff --git a/src/cli/bundled-skill-guides.ts b/src/cli/bundled-skill-guides.ts index 1546c40b6cf7..ae69a64f9609 100644 --- a/src/cli/bundled-skill-guides.ts +++ b/src/cli/bundled-skill-guides.ts @@ -72,10 +72,10 @@ const ORCA_PER_WORKSPACE_ENV_SSH_HOST_REFERENCE_MARKDOWN = "# SSH connection mod const ORCA_PER_WORKSPACE_ENV_WINDOWS_SCRIPTS_REFERENCE_MARKDOWN = "# Windows local-side scripts\n\nLoad this when the user's desktop is Windows and you are scaffolding the local-side scripts. A bare\n`.sh` will not execute there. Either require WSL or Git Bash and point `orca.yaml` at a launcher such\nas `bash ./scripts/orca-vm/.sh` through a `.cmd` file, or scaffold PowerShell equivalents.\n\nThe remote-side commands you run inside the Linux environment stay bash regardless of the desktop OS.\n\n```powershell\n#requires -Version 5\n$ErrorActionPreference = 'Stop'\n# resolve env→state→fallback; run the provider CLI / ssh the same way;\n# capture provider output; build the result object for the chosen mode and write ONE line of JSON to stdout.\n# Orca-server mode: @{ schemaVersion=1; pairingCode=$pairingCode; projectRoot=$projectRoot; userData=@{...} }\n# SSH mode: @{ schemaVersion=1; connection=@{ type=\"ssh\"; projectRoot=$projectRoot;\n# target=@{ label=$label; host=$host; port=$port; username=$user } } }\n($result | ConvertTo-Json -Compress -Depth 6)\n# progress/errors → Write-Error / the error stream, never stdout.\n```\n\nThe doctor's executable-bit check is a POSIX concept and is skipped on Windows, so a script that is\nunusable on the user's machine for a different reason still has to be caught by the `--provision`\nself-test.\n" // oxfmt-ignore -const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" +const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" // oxfmt-ignore -const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nIf `check` returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nNever run `run-use`. If you created your own Run, plain `check` still returns\nyour Dispatch's mail; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore const ORCHESTRATION_COORDINATOR_LOOP_REFERENCE_MARKDOWN = "# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n" @@ -96,7 +96,7 @@ const ORCHESTRATION_PLACEMENT_AND_REMOTE_REFERENCE_MARKDOWN = "# Placement and r const ORCHESTRATION_RECOVERY_AND_CLEANUP_REFERENCE_MARKDOWN = "# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n" // oxfmt-ignore -const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nIf `check` returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nNever run `run-use`. If you created your own Run, plain `check` still returns\nyour Dispatch's mail; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore export const BUNDLED_SKILL_GUIDES = [ diff --git a/src/main/runtime/orchestration/db/runs/run-delivery.ts b/src/main/runtime/orchestration/db/runs/run-delivery.ts index b941c8ae9511..5df81950c1a1 100644 --- a/src/main/runtime/orchestration/db/runs/run-delivery.ts +++ b/src/main/runtime/orchestration/db/runs/run-delivery.ts @@ -12,7 +12,7 @@ export function requireCurrentConsumer( if (!run || run.legacy === 1 || run.consumer_generation !== consumerGeneration) { throw new OrchestrationError( 'consumer_fenced', - 'This mailbox consumer has been replaced. Rebind with orchestration run-use.' + `Run ${runId}'s coordinator binding changed, so this mailbox reader was replaced. Stop reading it; take the Run back only if the user asks (\`orchestration run-use --id ${runId} --takeover\`).` ) } return run diff --git a/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts b/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts index 6093f3eba492..a9b9bf438432 100644 --- a/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts @@ -194,7 +194,7 @@ describe('orchestration gate Run authorization', () => { ok: false, error: { code: 'consumer_fenced', - message: `This coordinator terminal is bound to ${runA}, not ${strangerRun.id}.` + message: `This caller is bound to ${runA}, not ${strangerRun.id}.` } }) expect(harness.db.getTask(strangerTask.id)?.status).toBe('ready') diff --git a/src/main/runtime/rpc/methods/orchestration/messaging/check-methods.ts b/src/main/runtime/rpc/methods/orchestration/messaging/check-methods.ts index 5693320cbccd..6d4cb8c6cce1 100644 --- a/src/main/runtime/rpc/methods/orchestration/messaging/check-methods.ts +++ b/src/main/runtime/rpc/methods/orchestration/messaging/check-methods.ts @@ -107,7 +107,7 @@ export const ORCHESTRATION_CHECK_METHODS = [ if (!hasRunBindingKey(caller) && consumingCheck) { throw new OrchestrationError( 'stable_pane_required', - `Terminal ${handle} has no live pane bound to a Run, so this inbox can never receive Run mail. Rebind this terminal with orchestration run-use, or read the Run mailbox with --run .`, + `Terminal ${handle} has no live pane and no active Dispatch, so a consuming check here can never receive mail. Run check from the agent's own live terminal.`, orchestrationSkillRecoveryData() ) } diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts index 80eb68e6af27..83aa4391c5c7 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts @@ -8,14 +8,19 @@ import type { } from '../../../../orca-runtime' import { hasRunBindingKey, + mailboxAddressOf, + runCoordinatorKey, type OrchestrationCallerIdentity, type OrchestrationSessionCaller } from '../../../../orchestration/orchestration-caller-identity' import { resolveDeclaredCallerParty } from '../../../../orchestration/orchestration-party' import { findCallerDispatchWorkingForRun, + findCallerWorkerDispatches, + unboundWorkerMessage, workerCannotCoordinateRunError } from '../../../../orchestration/db/runs/run-worker-lineage' +import type { OrchestrationDb } from '../../../../orchestration/db' export type RunScopeParams = { runId?: string @@ -120,6 +125,31 @@ export function resolveOrchestrationCaller( return caller } +const COORDINATOR_RUN_REQUIRED = + 'No Run is bound. A coordinator creates one with `orchestration run-create`, or resumes its own with `orchestration run-use --id `.' + +// Why: a worker is told where its own mail is, never to bind a Run. +function unboundCallerError( + db: OrchestrationDb, + caller: OrchestrationCallerIdentity, + explicit: RunRow | undefined +): OrchestrationError { + if (explicit) { + // Why: consumer_fenced, since a reader that lost this Run mid-check must stop, worker or not. + const holder = mailboxAddressOf(runCoordinatorKey(explicit)) + return new OrchestrationError( + 'consumer_fenced', + `This caller is not Run ${explicit.id}'s coordinator (${holder ? `its coordinator is ${holder}` : 'it has no coordinator'}). Nothing changed.` + ) + } + const worked = findCallerWorkerDispatches(db, caller)[0] + return new OrchestrationError( + 'run_required', + worked ? unboundWorkerMessage(worked) : `${COORDINATOR_RUN_REQUIRED} No effects were applied.`, + orchestrationSkillRecoveryData() + ) +} + // Why: task and gate mutations must share one Run-binding rule. export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopeParams): RunRow { const db = runtime.getOrchestrationDb() @@ -134,7 +164,7 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar if (!params.callerTerminalHandle) { throw new OrchestrationError( 'run_required', - 'No Run is bound. Use orchestration run-create or run-use first. No effects were applied.', + `${COORDINATOR_RUN_REQUIRED} A worker never binds a Run: its mail arrives through plain \`orchestration check\`, and it reports with \`orchestration send\`. No effects were applied.`, orchestrationSkillRecoveryData() ) } @@ -167,22 +197,12 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar throw workerCannotCoordinateRunError(workedDispatch, explicit.id, 'act') } if (!current) { - if (explicit) { - throw new OrchestrationError( - 'consumer_fenced', - `This coordinator terminal is no longer bound to Run ${explicit.id}.` - ) - } - throw new OrchestrationError( - 'run_required', - 'No Run is bound. Use orchestration run-create or run-use first. No effects were applied.', - orchestrationSkillRecoveryData() - ) + throw unboundCallerError(db, caller, explicit) } if (explicit && current.id !== explicit.id) { throw new OrchestrationError( 'consumer_fenced', - `This coordinator terminal is bound to ${current.id}, not ${explicit.id}.` + `This caller is bound to ${current.id}, not ${explicit.id}.` ) } return current diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index cb38305d6115..e26fdeb27734 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -251,7 +251,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( ok: false, error: { code: 'consumer_fenced', - message: `This coordinator terminal is no longer bound to Run ${runId}.` + message: `This caller is not Run ${runId}'s coordinator (its coordinator is ${ADDRESS_Y}). Nothing changed.` } }) expect(await as(SESSION_X, 'orchestration.runCurrent', {})).toMatchObject({ run: null }) From 51899d805d5eeb7d6046110ae58b429e12f568d0 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:06:25 -0700 Subject: [PATCH 05/30] fix(orchestration): stop routing mail to addresses that grabbed a Run's seat Every address that ever bound a Run is remembered as one of its coordinator addresses, so mail to it routes to the Run mailbox. When a worker bound the Run it was working for, its own address joined that list for good, and its direct mail in that Run kept landing in the coordinator's inbox. The live database has 45 such rows since 2026-08-20. All four readers of the remembered addresses now skip one that was first bound while it worked an unsettled, delegated Dispatch in the same Run. It is derived from existing rows, so no migration or delete is needed and rows written by older builds stop mattering. The active-Dispatch check also matches a worker's orca_session_id spelling, not only its handle. --- ...n-mailbox-notification-consistency.test.ts | 3 +- .../foreign-direct-mailbox-routing.ts | 2 + .../db/runs/run-coordinator-mail-routing.ts | 44 ++++++++++++++++--- .../orchestration/db/runs/run-lookup.ts | 3 +- ...ispatch-creator-identity-migration.test.ts | 2 + .../run-coordinator-handle-migration.test.ts | 33 ++++++++++++++ 6 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/main/runtime/orchestration-mailbox-notification-consistency.test.ts b/src/main/runtime/orchestration-mailbox-notification-consistency.test.ts index ee03a1494104..c650a130a543 100644 --- a/src/main/runtime/orchestration-mailbox-notification-consistency.test.ts +++ b/src/main/runtime/orchestration-mailbox-notification-consistency.test.ts @@ -279,7 +279,8 @@ describe('orchestration notification mailbox consistency', () => { .map(([sql]) => sql) .find((sql) => sql.includes('FROM run_coordinator_handles AS coordinator')) expect(routingSql).toContain('INDEXED BY idx_messages_undelivered_direct_run') - expect(routingSql).not.toContain('NOT EXISTS') + // An anti-join per coordinator row is fine; one per candidate message would scan the backlog. + expect(routingSql).not.toMatch(/NOT EXISTS \([^)]*candidate\./) expect(db.getMessageById(owned.id)?.to_handle).toBe(`run:${ownedRun.id}`) expect(db.getUnreadDirectMessageTypes(detachedHandle)).toEqual(['status']) db.close() diff --git a/src/main/runtime/orchestration/db/messages/foreign-direct-mailbox-routing.ts b/src/main/runtime/orchestration/db/messages/foreign-direct-mailbox-routing.ts index f8ebfb4762cc..2414381a113a 100644 --- a/src/main/runtime/orchestration/db/messages/foreign-direct-mailbox-routing.ts +++ b/src/main/runtime/orchestration/db/messages/foreign-direct-mailbox-routing.ts @@ -3,6 +3,7 @@ import type { DispatchContextRow, MessageType } from '../../types' import { DISPATCH_PANE_KEY_MATCH_SUFFIX_SQL, paneKeyMatchSuffix } from '../pane-key-match' import type { OrchestrationDb } from '../orchestration-db' import { DISPATCH_CONTEXT_COLUMN_LIST } from '../row-column-lists' +import { workerSeatGrabSql } from '../runs/run-coordinator-mail-routing' import { ORCHESTRATION_DELIVERY_BATCH_LIMIT, type ForeignDirectMailboxRoutingPage @@ -58,6 +59,7 @@ export function routeForeignDirectMessagesToOwnedMailboxes( ON candidate.run_id = coordinator.run_id AND candidate.to_handle = coordinator.terminal_handle WHERE coordinator.terminal_handle = ?${runExclusion} + AND NOT ${workerSeatGrabSql('coordinator')} AND candidate.read = 0 AND candidate.delivered_at IS NULL AND candidate.delivery_contract = 'current_delivery'`, `SELECT candidate.id, candidate.run_id, candidate.type, candidate.sequence diff --git a/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts b/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts index c1f80a592036..20459c03e2a7 100644 --- a/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts +++ b/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts @@ -1,17 +1,47 @@ import type Database from '../../../../sqlite/sync-database' import type { OrchestrationDb } from '../orchestration-db' import { currentRunCoordinatorSessionAddressSql } from './run-coordinator-orca-session' +import { orcaSessionAddressSql } from '../orca-session-address-sql' /** Mail to an active Dispatch assignee's address in the same Run is that worker's, not coordinator mail. */ export function activeDispatchOwnsAddressSql(runIdSql: string, addressSql: string): string { return `EXISTS ( SELECT 1 FROM dispatch_contexts WHERE dispatch_contexts.run_id = ${runIdSql} - AND dispatch_contexts.assignee_handle = ${addressSql} + AND (dispatch_contexts.assignee_handle = ${addressSql} + OR ${orcaSessionAddressSql('dispatch_contexts.assignee_orca_session_id')} = ${addressSql}) AND dispatch_contexts.status IN ('pending', 'dispatched') )` } +/** + * A remembered address first bound while it worked an unsettled, delegated Dispatch in the same Run + * was a worker grabbing the seat, not a spelling of the coordinator, so it never routes as one. + * Derived from the rows, so addresses older builds remembered stop mattering without editing them. + * julianday() because completed_at is stored in two text formats. + */ +export function workerSeatGrabSql(aliasRow: string): string { + return `EXISTS ( + SELECT 1 FROM dispatch_contexts AS grab + WHERE grab.run_id = ${aliasRow}.run_id + AND (grab.assignee_handle = ${aliasRow}.terminal_handle + OR ${orcaSessionAddressSql('grab.assignee_orca_session_id')} = ${aliasRow}.terminal_handle) + AND NOT (grab.creator_handle IS NOT NULL AND grab.creator_handle = grab.assignee_handle) + AND julianday(grab.created_at) <= julianday(${aliasRow}.first_bound_at) + AND (grab.status IN ('pending', 'dispatched') + OR julianday(grab.completed_at) >= julianday(${aliasRow}.first_bound_at)) + )` +} + +/** Whether `addressSql` is a coordinator address of the Run `runIdSql`, seat grabs excluded. */ +function coordinatorAliasSql(runIdSql: string, addressSql: string): string { + return `EXISTS ( + SELECT 1 FROM run_coordinator_handles AS alias + WHERE alias.run_id = ${runIdSql} AND alias.terminal_handle = ${addressSql} + AND NOT ${workerSeatGrabSql('alias')} + )` +} + export function rememberRunCoordinatorHandle( this: OrchestrationDb, runId: string, @@ -93,10 +123,7 @@ export function createCoordinatorMailRoutingTrigger(this: OrchestrationDb): void SELECT 1 FROM runs WHERE runs.id = NEW.run_id AND runs.legacy = 0 ) - AND EXISTS ( - SELECT 1 FROM run_coordinator_handles - WHERE run_id = NEW.run_id AND terminal_handle = NEW.to_handle - ) + AND ${coordinatorAliasSql('NEW.run_id', 'NEW.to_handle')} AND NOT ${activeDispatchOwnsAddressSql('NEW.run_id', 'NEW.to_handle')} BEGIN UPDATE messages SET to_handle = 'run:' || NEW.run_id WHERE sequence = NEW.sequence; @@ -119,7 +146,12 @@ export function routeAllUnreadDirectMessagesToRunMailbox( `UPDATE messages SET to_handle = ? WHERE run_id = ? AND to_handle = ? AND read = 0 AND delivery_contract = 'current_delivery' - AND NOT ${activeDispatchOwnsAddressSql('messages.run_id', 'messages.to_handle')}` + AND NOT ${activeDispatchOwnsAddressSql('messages.run_id', 'messages.to_handle')} + AND NOT EXISTS ( + SELECT 1 FROM run_coordinator_handles AS alias + WHERE alias.run_id = messages.run_id AND alias.terminal_handle = messages.to_handle + AND ${workerSeatGrabSql('alias')} + )` ) .run(`run:${runId}`, runId, directHandle) } diff --git a/src/main/runtime/orchestration/db/runs/run-lookup.ts b/src/main/runtime/orchestration/db/runs/run-lookup.ts index 94a57c345a38..ff17585567a4 100644 --- a/src/main/runtime/orchestration/db/runs/run-lookup.ts +++ b/src/main/runtime/orchestration/db/runs/run-lookup.ts @@ -16,6 +16,7 @@ import { encodeRunListCursor, decodeRunListCursor } from '../run-list-cursor' import type { RunListPage } from '../run-list-page' import type { OrchestrationDb } from '../orchestration-db' import { RUN_COLUMN_LIST } from '../row-column-lists' +import { workerSeatGrabSql } from './run-coordinator-mail-routing' export type LegacyAdoptedMailboxOwner = { runId: string @@ -63,7 +64,7 @@ export function getRunMailboxOwnerIdsForHandle( `SELECT coordinator.run_id FROM run_coordinator_handles AS coordinator JOIN runs ON runs.id = coordinator.run_id AND runs.legacy = 0 - WHERE coordinator.terminal_handle = ? + WHERE coordinator.terminal_handle = ? AND NOT ${workerSeatGrabSql('coordinator')} ORDER BY coordinator.run_id` ) .all(terminalHandle) as { run_id: string }[] diff --git a/src/main/runtime/orchestration/dispatch-creator-identity-migration.test.ts b/src/main/runtime/orchestration/dispatch-creator-identity-migration.test.ts index a84a862abb05..7259f6c073d6 100644 --- a/src/main/runtime/orchestration/dispatch-creator-identity-migration.test.ts +++ b/src/main/runtime/orchestration/dispatch-creator-identity-migration.test.ts @@ -40,6 +40,8 @@ describe('OrchestrationDb v36 to v37 migration', () => { seed.close() const raw = new Database(dbPath) + // The current mail-routing trigger reads creator_handle; the next open recreates it. + raw.exec('DROP TRIGGER trg_messages_route_coordinator_mail') for (const column of CREATOR_COLUMNS) { raw.exec(`ALTER TABLE dispatch_contexts DROP COLUMN ${column}`) } diff --git a/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts b/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts index f15ad3c316a6..8369e6d775b1 100644 --- a/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts +++ b/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts @@ -5,6 +5,7 @@ import { afterEach, describe, expect, it } from 'vitest' import Database from '../../sqlite/sync-database' import { OrchestrationDb } from './db' import { SCHEMA_VERSION } from './db/contract-constants' +import { createRootDispatch } from './db/root-dispatch-test-fixture' describe('Run coordinator handle history migration', () => { let db: OrchestrationDb | undefined @@ -165,4 +166,36 @@ describe('Run coordinator handle history migration', () => { expect(db.getMessageById(message.id)?.to_handle).toBe(`run:${run.id}`) }) + + it('stops routing an address that grabbed the seat while it worked for the Run', () => { + db = new OrchestrationDb(':memory:') + const run = db.createRun({ + objective: 'grabbed', + coordinatorHandle: 'term_coord', + coordinatorPaneKey: 'tab:leaf' + }) + const dispatch = createRootDispatch( + db, + db.createTask({ spec: 'w', runId: run.id }).id, + 'term_worker' + ) + // An older build remembered the worker as a coordinator address when it bound the Run. + db.rememberRunCoordinatorHandle(run.id, 'term_worker') + db.db + .prepare( + `UPDATE dispatch_contexts SET status = 'completed', completed_at = datetime('now') WHERE id = ?` + ) + .run(dispatch.id) + + const mail = db.insertMessage({ + runId: run.id, + from: 'term_coord', + to: 'term_worker', + subject: 'direct' + }) + + expect(db.getMessageById(mail.id)?.to_handle).toBe('term_worker') + expect(db.getRunMailboxOwnerIdsForHandle('term_worker')).toEqual([]) + expect(db.routeForeignDirectMessagesToOwnedMailboxes('term_worker').routedCount).toBe(0) + }) }) From 7e731377d8f13021a4cd379442c54ca9cf283635 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:17:13 -0700 Subject: [PATCH 06/30] fix(cli): keep the orchestration spec file within its line limit --- src/cli/specs/orchestration.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cli/specs/orchestration.ts b/src/cli/specs/orchestration.ts index ecb1ec6cfd5b..1bf6124e867a 100644 --- a/src/cli/specs/orchestration.ts +++ b/src/cli/specs/orchestration.ts @@ -22,8 +22,7 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [ allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover', 'takeover-legacy', 'retry-request'], identityFlagRoles: { from: 'caller' }, notes: [ - 'Refused while another coordinator of the Run is still running; --takeover replaces it and fences its unread mail. A caller working a Dispatch can never take over.', - '--takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' + 'Refused while another coordinator of the Run is still running; --takeover replaces it and fences its unread mail. A caller working a Dispatch can never take over. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' ] }, { From e00c287ea3b3efa096cee9054d716e1f11ceef43 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:24:53 -0700 Subject: [PATCH 07/30] fix(orchestration): read remembered coordinator Run ids without a type assertion --- .../orchestration/db/runs/run-lookup.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/runtime/orchestration/db/runs/run-lookup.ts b/src/main/runtime/orchestration/db/runs/run-lookup.ts index ff17585567a4..85e0290bfce5 100644 --- a/src/main/runtime/orchestration/db/runs/run-lookup.ts +++ b/src/main/runtime/orchestration/db/runs/run-lookup.ts @@ -58,17 +58,20 @@ export function getRunMailboxOwnerIdsForHandle( terminalHandle: string, legacyAdoptedMailboxOwner?: LegacyAdoptedMailboxOwner | null ): string[] { - const runIds = ( - this.db - .prepare( - `SELECT coordinator.run_id - FROM run_coordinator_handles AS coordinator - JOIN runs ON runs.id = coordinator.run_id AND runs.legacy = 0 - WHERE coordinator.terminal_handle = ? AND NOT ${workerSeatGrabSql('coordinator')} - ORDER BY coordinator.run_id` - ) - .all(terminalHandle) as { run_id: string }[] - ).map((row) => row.run_id) + const runIds = this.db + .prepare( + `SELECT coordinator.run_id + FROM run_coordinator_handles AS coordinator + JOIN runs ON runs.id = coordinator.run_id AND runs.legacy = 0 + WHERE coordinator.terminal_handle = ? AND NOT ${workerSeatGrabSql('coordinator')} + ORDER BY coordinator.run_id` + ) + .all(terminalHandle) + .flatMap((row) => + typeof row === 'object' && row !== null && 'run_id' in row && typeof row.run_id === 'string' + ? [row.run_id] + : [] + ) const adoptedOwner = legacyAdoptedMailboxOwner === undefined ? this.getLegacyAdoptedRunMailboxOwner() From a9a9dbbe290ac39e7869638c014e2277a214162b Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:23:24 -0700 Subject: [PATCH 08/30] fix(orchestration): compare seat-grab times at whole seconds first_bound_at has no milliseconds, so a worker bound in the same second its Dispatch settled was counted as grabbing the seat. Compare completed_at at whole seconds with a strict >. --- .../orchestration/db/runs/run-coordinator-mail-routing.ts | 5 +++-- .../orchestration/run-coordinator-handle-migration.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts b/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts index 20459c03e2a7..22d76d646b04 100644 --- a/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts +++ b/src/main/runtime/orchestration/db/runs/run-coordinator-mail-routing.ts @@ -18,7 +18,8 @@ export function activeDispatchOwnsAddressSql(runIdSql: string, addressSql: strin * A remembered address first bound while it worked an unsettled, delegated Dispatch in the same Run * was a worker grabbing the seat, not a spelling of the coordinator, so it never routes as one. * Derived from the rows, so addresses older builds remembered stop mattering without editing them. - * julianday() because completed_at is stored in two text formats. + * julianday() because completed_at is stored in two text formats; whole seconds and a strict > + * because first_bound_at has no milliseconds, so a bind in the second a Dispatch settled is not a grab. */ export function workerSeatGrabSql(aliasRow: string): string { return `EXISTS ( @@ -29,7 +30,7 @@ export function workerSeatGrabSql(aliasRow: string): string { AND NOT (grab.creator_handle IS NOT NULL AND grab.creator_handle = grab.assignee_handle) AND julianday(grab.created_at) <= julianday(${aliasRow}.first_bound_at) AND (grab.status IN ('pending', 'dispatched') - OR julianday(grab.completed_at) >= julianday(${aliasRow}.first_bound_at)) + OR julianday(datetime(grab.completed_at)) > julianday(${aliasRow}.first_bound_at)) )` } diff --git a/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts b/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts index 8369e6d775b1..6a7318b6b794 100644 --- a/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts +++ b/src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts @@ -183,7 +183,7 @@ describe('Run coordinator handle history migration', () => { db.rememberRunCoordinatorHandle(run.id, 'term_worker') db.db .prepare( - `UPDATE dispatch_contexts SET status = 'completed', completed_at = datetime('now') WHERE id = ?` + `UPDATE dispatch_contexts SET status = 'completed', completed_at = datetime('now', '+1 second') WHERE id = ?` ) .run(dispatch.id) From 0ee076e3786f066d7e825e7071fc05201742ab21 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:23:35 -0700 Subject: [PATCH 09/30] fix(orchestration): a worker coordinates only Runs it created The worker refusals depended on proving the target's coordinator alive, so a worker could still take any Run whose coordinator was closed, unhooked, remote or headless. Replace them with one rule derived from existing rows: a caller working a Dispatch may bind only a Run it legitimately coordinated before (a remembered, non-seat-grab address of that Run, or a Run it dispatched work into), never the Run it works for, and never with --takeover. A same-binding no-op is never refused, and workers are classified before the legacy gate so they never see its --takeover-legacy hint. The refusal names the Dispatch and how to end it with worker-abandon if it is stale. The ancestor walk is gone. run-use now records which coordinator it probed. If the binding moved before the transaction, it probes again (three tries) and then refuses with a retryable run_binding_changed instead of binding over a party nobody probed. run_coordinator_live names the chat by its title and worktree, or the terminal by its title and worktree, says it is still open, and that closing it clears the refusal. --- .../orchestration/db/runs/run-binding.ts | 86 ++++++------ .../db/runs/run-worker-lineage.ts | 90 ------------ .../db/runs/worker-run-binding.ts | 128 ++++++++++++++++++ .../orchestration-run-delivery-db.test.ts | 4 +- src/main/runtime/rpc/errors.ts | 1 + .../run-bound-mailbox-interleavings.test.ts | 25 ++-- .../runs/run-coordinator-liveness.ts | 98 +++++++++++--- .../methods/orchestration/runs/run-scope.ts | 19 +-- .../runs/run-use-takeover.test.ts | 65 ++++++++- .../rpc/methods/orchestration/runs/runs.ts | 18 +-- .../orchestration-session-coordinator.test.ts | 2 +- .../orchestration-session-run-binding.test.ts | 23 ++-- 12 files changed, 359 insertions(+), 200 deletions(-) delete mode 100644 src/main/runtime/orchestration/db/runs/run-worker-lineage.ts create mode 100644 src/main/runtime/orchestration/db/runs/worker-run-binding.ts diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index 4a04a774d5fc..1cb86dd4552a 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -4,12 +4,7 @@ import { LEGACY_CONTRACT_VERSION } from '../contract-constants' import { isEquivalentPaneKey } from '../pane-key-match' import type { OrchestrationDb } from '../orchestration-db' import type { OrcaSessionId } from '../../../../../shared/orca-session-address' -import { - findCallerDispatchWorkingForRun, - findCallerWorkerDispatches, - workerCannotCoordinateRunError, - workerCannotTakeOverRunError -} from './run-worker-lineage' +import { findCallerWorkerDispatches, workerRunBindingRefusal } from './worker-run-binding' import { mailboxAddressOf, runBoundToCoordinator, @@ -26,10 +21,13 @@ export function bindRun( /** The coordinator's bare Orca session id when it is a structured session; see orca-session-address. */ coordinatorOrcaSessionId?: OrcaSessionId | null takeoverLegacy?: boolean - /** The user asked to replace a coordinator that is still running. */ + /** The user asked to replace a coordinator that is still open. */ takeover?: boolean - /** The coordinator the RPC layer proved live just before this call; enforced only if the Run is still bound to it. */ - provenLiveCoordinator?: OrchestrationCoordinatorKey | null + /** + * What the RPC layer saw just before this call: the Run's coordinator then, and how a user + * recognizes it when it was proven open. A binding that moved since is refused, never bound blindly. + */ + probed?: { coordinator: OrchestrationCoordinatorKey; liveLabel: string | null } legacyCoordinatorAuthority?: { runId: string principalId: string | null @@ -51,20 +49,18 @@ export function bindRun( this.db.exec('ROLLBACK') return undefined } - // Why: a worker binding a Run it works for unbinds the coordinator it reports to, and its - // own mail then routes to that Run's mailbox for good. Lineage is fixed by the Dispatch rows. - const workedDispatch = findCallerDispatchWorkingForRun(this, coordinator, params.runId) - if (workedDispatch) { - throw workerCannotCoordinateRunError(workedDispatch, params.runId, 'bind') - } + const sameBinding = runBoundToCoordinator(run, coordinator) const explicitTakeover = params.takeover === true || params.takeoverLegacy === true - const takeoverByWorker = explicitTakeover - ? findCallerWorkerDispatches(this, coordinator)[0] - : undefined - if (takeoverByWorker) { - throw workerCannotTakeOverRunError(takeoverByWorker, params.runId) + // Why: before the legacy gate, so a worker never sees its --takeover-legacy recovery command. + const workerRefusal = sameBinding + ? null + : workerRunBindingRefusal(this, coordinator, params.runId, { + takeover: explicitTakeover, + bind: true + }) + if (workerRefusal) { + throw workerRefusal } - const sameBinding = runBoundToCoordinator(run, coordinator) const adoption = this.getLegacyAdoption() const adoptedRun = adoption?.adopted_run_id === params.runId const legacyAuthority = params.legacyCoordinatorAuthority @@ -145,17 +141,22 @@ export function bindRun( } ) } - if ( - params.provenLiveCoordinator && - !sameBinding && - !provenLegacyBinding && - !explicitTakeover && - runBoundToCoordinator(run, params.provenLiveCoordinator) - ) { - const worker = findCallerWorkerDispatches(this, coordinator)[0] - throw worker - ? workerCannotTakeOverRunError(worker, params.runId) - : runCoordinatorLiveError(params.runId, params.provenLiveCoordinator) + const probed = params.probed + if (probed && !sameBinding && !provenLegacyBinding && !explicitTakeover) { + if (!sameCoordinatorKey(runCoordinatorKey(run), probed.coordinator)) { + throw new OrchestrationError( + 'run_binding_changed', + `Run ${params.runId}'s coordinator changed while this command ran. Nothing changed; run the same command again.`, + { effectsApplied: false, retryable: true } + ) + } + if (probed.liveLabel !== null) { + // A worker passed the worker rule, so this is its own sub-Run that someone else now holds. + const worker = findCallerWorkerDispatches(this, coordinator)[0] + throw worker + ? workerRunBindingRefusal(this, coordinator, params.runId, { takeover: true, bind: true }) + : runCoordinatorLiveError(params.runId, probed.liveLabel) + } } this.unbindOtherRunsForCoordinator(coordinator, params.runId) // The mailbox address of the coordinator being replaced and of the one binding now. @@ -222,16 +223,23 @@ export function bindRun( return this.getRun(params.runId) } +function sameCoordinatorKey( + a: OrchestrationCoordinatorKey, + b: OrchestrationCoordinatorKey +): boolean { + return ( + a.terminalHandle === b.terminalHandle && + a.paneKey === b.paneKey && + a.orcaSessionId === b.orcaSessionId + ) +} + // No recoveryCommand on purpose: agents run those mechanically, and the flag must carry user intent. -function runCoordinatorLiveError( - runId: string, - live: OrchestrationCoordinatorKey -): OrchestrationError { - const holder = mailboxAddressOf(live) ?? 'another coordinator' +function runCoordinatorLiveError(runId: string, label: string): OrchestrationError { return new OrchestrationError( 'run_coordinator_live', - `Run ${runId} is coordinated by ${holder}, which is still running. Nothing changed. Take it over only if the user asked you to: orca orchestration run-use --id ${runId} --takeover. That fences ${holder}; its unacknowledged mail is replayed to you.`, - { effectsApplied: false, runId, coordinator: holder } + `Run ${runId} is coordinated by ${label}, which is still open. Nothing changed. Closing it clears this. Take it over only if the user asks: orca orchestration run-use --id ${runId} --takeover replaces it, and its unacknowledged mail is replayed to you.`, + { effectsApplied: false, runId, coordinator: label } ) } diff --git a/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts b/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts deleted file mode 100644 index 846fc18b05b3..000000000000 --- a/src/main/runtime/orchestration/db/runs/run-worker-lineage.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { DispatchContextRow } from '../../types' -import { OrchestrationError } from '../../orchestration-error' -import { isSelfCreatedDispatch } from '../dispatch-depth' -import type { OrchestrationDb } from '../orchestration-db' -import { - mailboxAddressOf, - type OrchestrationCoordinatorKey -} from '../../orchestration-caller-identity' - -// Why: creator links only point at older rows, so this caps a corrupt chain, not real nesting. -const MAX_LINEAGE_STEPS = 32 - -/** - * The caller's live Dispatches, matched the way `check` and `send` find a worker's Dispatch: its - * address, then its pane. A context-only self-dispatch is a coordinator's own bookkeeping, not work - * for anyone, so it never makes the caller a worker. - */ -export function findCallerWorkerDispatches( - db: OrchestrationDb, - caller: OrchestrationCoordinatorKey -): DispatchContextRow[] { - const address = mailboxAddressOf(caller) - if (address === null) { - return [] - } - return db - .getActiveDispatchMailboxOwners(address, caller.paneKey ?? undefined) - .filter((dispatch) => !isSelfCreatedDispatch(dispatch)) -} - -/** - * The caller's live Dispatch that works for this Run: one in the Run itself, or one started from - * work in it (recorded by creator_dispatch_id). - */ -export function findCallerDispatchWorkingForRun( - db: OrchestrationDb, - caller: OrchestrationCoordinatorKey, - runId: string -): DispatchContextRow | undefined { - for (const own of findCallerWorkerDispatches(db, caller)) { - let row: DispatchContextRow | undefined = own - for (let step = 0; row && step < MAX_LINEAGE_STEPS; step++) { - if (row.run_id === runId) { - return own - } - row = row.creator_dispatch_id ? db.getDispatchContextById(row.creator_dispatch_id) : undefined - } - } - return undefined -} - -const WORKER_MAIL_HINT = - 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' -const SUB_RUN_HINT = - 'To coordinate sub-workers, create your own Run with `orchestration run-create`.' - -/** Refuse a worker acting as the coordinator of a Run it works for; never points it at run-use. */ -export function workerCannotCoordinateRunError( - dispatch: DispatchContextRow, - runId: string, - attempted: 'bind' | 'act' -): OrchestrationError { - const role = - dispatch.run_id === runId - ? `You work Dispatch ${dispatch.id} in Run ${runId}, so that Run belongs to your coordinator and you cannot coordinate it.` - : `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id}, which was started from work in Run ${runId}, so you cannot coordinate Run ${runId}.` - const bindOnly = attempted === 'bind' ? ` ${SUB_RUN_HINT}` : '' - return new OrchestrationError( - 'worker_cannot_coordinate_own_run', - `${role} ${WORKER_MAIL_HINT}${bindOnly} No effects were applied.`, - { effectsApplied: false } - ) -} - -/** A worker never takes over a Run, even with the flag: the flag carries a user's intent, not a worker's. */ -export function workerCannotTakeOverRunError( - dispatch: DispatchContextRow, - runId: string -): OrchestrationError { - return new OrchestrationError( - 'worker_cannot_take_over_run', - `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id}, so you cannot take over Run ${runId}. ${WORKER_MAIL_HINT} ${SUB_RUN_HINT} No effects were applied.`, - { effectsApplied: false } - ) -} - -/** What a worker with no Run of its own is told instead of "bind a Run first". */ -export function unboundWorkerMessage(dispatch: DispatchContextRow): string { - return `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id} and are not bound to a Run of your own. ${WORKER_MAIL_HINT} ${SUB_RUN_HINT} No effects were applied.` -} diff --git a/src/main/runtime/orchestration/db/runs/worker-run-binding.ts b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts new file mode 100644 index 000000000000..d913036164d4 --- /dev/null +++ b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts @@ -0,0 +1,128 @@ +import type { DispatchContextRow } from '../../types' +import { OrchestrationError } from '../../orchestration-error' +import { isSelfCreatedDispatch } from '../dispatch-depth' +import type { OrchestrationDb } from '../orchestration-db' +import { + mailboxAddressOf, + type OrchestrationCoordinatorKey +} from '../../orchestration-caller-identity' +import { workerSeatGrabSql } from './run-coordinator-mail-routing' + +/** + * The caller's live Dispatches, matched the way `check` and `send` find a worker's Dispatch: its + * address, then its pane. A context-only self-dispatch is a coordinator's own bookkeeping, not work + * for anyone, so it never makes the caller a worker. + */ +export function findCallerWorkerDispatches( + db: OrchestrationDb, + caller: OrchestrationCoordinatorKey +): DispatchContextRow[] { + const address = mailboxAddressOf(caller) + if (address === null) { + return [] + } + return db + .getActiveDispatchMailboxOwners(address, caller.paneKey ?? undefined) + .filter((dispatch) => !isSelfCreatedDispatch(dispatch)) +} + +/** + * A Run the worker legitimately coordinated: its address is one of the Run's remembered coordinator + * addresses and was not a seat grab, or it dispatched work into the Run while working one of its + * Dispatches (which survives a reissued terminal handle). + */ +function workerCoordinatedRun( + db: OrchestrationDb, + address: string, + dispatches: readonly DispatchContextRow[], + runId: string +): boolean { + const ids = dispatches.map((dispatch) => dispatch.id) + return Boolean( + db.db + .prepare( + `SELECT 1 WHERE EXISTS ( + SELECT 1 FROM run_coordinator_handles AS alias + WHERE alias.run_id = ? AND alias.terminal_handle = ? AND NOT ${workerSeatGrabSql('alias')} + ) OR EXISTS ( + SELECT 1 FROM dispatch_contexts + WHERE run_id = ? AND creator_dispatch_id IN (${ids.map(() => '?').join(', ')}) + )` + ) + .get(runId, address, runId, ...ids) + ) +} + +/** + * Why a worker may not coordinate this Run, or null when it may or the caller is not a worker. A + * worker coordinates only Runs it created; it never coordinates the Run it works for, and never + * takes a Run over. Derived from the Dispatch and coordinator-address rows, with no liveness probe. + */ +export function workerRunBindingRefusal( + db: OrchestrationDb, + caller: OrchestrationCoordinatorKey, + runId: string, + attempted: { takeover: boolean; bind: boolean } +): OrchestrationError | null { + const dispatches = findCallerWorkerDispatches(db, caller) + const address = mailboxAddressOf(caller) + const first = dispatches[0] + if (!first || address === null) { + return null + } + const own = dispatches.find((dispatch) => dispatch.run_id === runId) + if (own) { + return workerRefusal( + 'worker_cannot_coordinate_own_run', + own, + `Run ${runId} is the Run you work for (Dispatch ${own.id}); it belongs to your coordinator.`, + attempted.bind + ) + } + if (attempted.takeover) { + return workerRefusal( + 'worker_cannot_take_over_run', + first, + `You work Dispatch ${first.id} in Run ${first.run_id}, and a worker never takes a Run over.`, + attempted.bind + ) + } + if (workerCoordinatedRun(db, address, dispatches, runId)) { + return null + } + return workerRefusal( + 'worker_cannot_take_over_run', + first, + `You work Dispatch ${first.id} in Run ${first.run_id}, and workers coordinate only Runs they created; Run ${runId} is not one of yours.`, + attempted.bind + ) +} + +function workerRefusal( + code: string, + dispatch: DispatchContextRow, + role: string, + bind: boolean +): OrchestrationError { + const subRun = bind + ? ' To coordinate sub-workers, create your own Run with `orchestration run-create`.' + : '' + return new OrchestrationError( + code, + `${role} ${WORKER_MAIL_HINT}${subRun} ${staleDispatchHint(dispatch)} No effects were applied.`, + { effectsApplied: false, dispatchId: dispatch.id } + ) +} + +const WORKER_MAIL_HINT = + 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' + +// Why worker-abandon, not worker-stop: stop closes the Dispatch's terminal, which may be the caller's own. +function staleDispatchHint(dispatch: DispatchContextRow): string { + return `If nothing is working Dispatch ${dispatch.id} any more, its Run's coordinator or the user can end it with \`orca orchestration worker-abandon --dispatch ${dispatch.id}\`; this then works.` +} + +/** What a worker with no Run of its own is told instead of "bind a Run first". */ +export function unboundWorkerMessage(dispatch: DispatchContextRow): string { + return `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id} and are not bound to a Run of your own. ${WORKER_MAIL_HINT} To coordinate sub-workers, create your own Run with \`orchestration run-create\`. No effects were applied.` +} diff --git a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts index 327d1add38cb..9add5c067abe 100644 --- a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts +++ b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts @@ -226,7 +226,7 @@ describe('OrchestrationDb Run state', () => { }) describe('lightweight Run scope', () => { - it('refuses a worker binding a Run above its own, but not a coordinator with a self-dispatch', () => { + it('refuses a worker binding a Run it did not create, but not a coordinator with a self-dispatch', () => { const d = createDb() const parent = createBoundRun(d) const coordinator = { kind: 'terminal', handle: 'term_coord', paneKey: COORD_PANE } as const @@ -256,7 +256,7 @@ describe('OrchestrationDb Run state', () => { expect(() => d.bindRun({ runId: parent.id, coordinatorHandle: 'term_grand', coordinatorPaneKey: null }) - ).toThrow(expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' })) + ).toThrow(expect.objectContaining({ code: 'worker_cannot_take_over_run' })) expect(d.getRun(parent.id)?.coordinator_handle).toBe('term_coord') }) diff --git a/src/main/runtime/rpc/errors.ts b/src/main/runtime/rpc/errors.ts index 9bd4af84bd43..3bd8d99864d5 100644 --- a/src/main/runtime/rpc/errors.ts +++ b/src/main/runtime/rpc/errors.ts @@ -142,6 +142,7 @@ const STRUCTURED_RUNTIME_PASSTHROUGH_CODES: ReadonlySet = new Set([ 'worker_cannot_coordinate_own_run', 'worker_cannot_take_over_run', 'run_coordinator_live', + 'run_binding_changed', 'request_mismatch', 'mutation_ledger_full', 'legacy_read_only', diff --git a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts index 86694bccf76b..5e0327c1f8a8 100644 --- a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts @@ -52,17 +52,22 @@ describe('Run binding during Dispatch checks', () => { subject }) } + // A Run this lead created earlier and that lost its binding: the one kind a worker may resume. + function orphanedOwnRun(): string { + const id = state.db.createRun({ + objective: 'resume', + coordinatorHandle: 'term_lead', + coordinatorPaneKey: LEAD + }).id + state.db.db + .prepare( + 'UPDATE runs SET coordinator_handle = NULL, coordinator_pane_key = NULL WHERE id = ?' + ) + .run(id) + return id + } async function bind(method: 'runCreate' | 'runUse') { - const params = - method === 'runCreate' - ? { objective: 'child' } - : { - id: state.db.createRun({ - objective: 'adopt', - coordinatorHandle: 'term_other', - coordinatorPaneKey: OTHER - }).id - } + const params = method === 'runCreate' ? { objective: 'child' } : { id: orphanedOwnRun() } await h.call(`orchestration.${method}`, { from: 'term_lead', ...params }, state.ctx) const run = state.db.getCurrentRunForPane(LEAD) if (!run) { diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts index ee9af51d3eb7..94e778317616 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -1,6 +1,10 @@ import type { OrchestrationDb } from '../../../../orchestration/db' +import { OrchestrationError } from '../../../../orchestration/orchestration-error' import type { OrcaRuntimeService } from '../../../../orca-runtime' +import type { RunRow } from '../../../../orchestration/types' import { + mailboxAddressOf, + runBoundToCoordinator, runCoordinatorKey, type OrchestrationCoordinatorKey } from '../../../../orchestration/orchestration-caller-identity' @@ -9,35 +13,93 @@ import { lookupOrcaAgentSession, structuredSessionMailReach } from '../../../../orchestration/structured-session-mail-address' +import { defaultAgentChatLabel } from '../../../../../../shared/agent-session-chat-label' +import { splitWorktreeId } from '../../../../../../shared/worktree/id' + +// Why: a binding that keeps moving under a probe is contention, not something to win by retrying forever. +const PROBE_ATTEMPTS = 3 + +type BindParams = Parameters[0] /** - * The Run's current coordinator when there is positive evidence it is still running; else null. - * Only proof counts: a closed chat, an exited or unhooked terminal, another host, or anything that - * cannot be checked reads as not live, so a restart handoff never needs a flag. + * Probe the Run's coordinator, then bind with what was probed so bindRun can refuse a live one. + * When the binding moved between probe and transaction, probe again; after a few tries the refusal + * stands and says to retry, so a bind never displaces a coordinator nobody probed. */ -export async function provenLiveRunCoordinator( +export async function bindAfterProbe( runtime: OrcaRuntimeService, db: OrchestrationDb, - runId: string -): Promise { - const run = db.getRunRaw(runId) - if (!run || run.legacy === 1) { - return null + params: () => Omit +): Promise { + for (let attempt = 1; ; attempt++) { + const bind = params() + const target = db.getRunRaw(bind.runId) + const caller = { + terminalHandle: bind.coordinatorHandle, + paneKey: bind.coordinatorPaneKey, + orcaSessionId: bind.coordinatorOrcaSessionId ?? null + } + const coordinator = target ? runCoordinatorKey(target) : null + const liveLabel = + coordinator && target && !runBoundToCoordinator(target, caller) + ? await liveCoordinatorLabel(runtime, db, coordinator) + : null + try { + return db.bindRun({ ...bind, ...(coordinator ? { probed: { coordinator, liveLabel } } : {}) }) + } catch (error) { + if ( + attempt >= PROBE_ATTEMPTS || + !(error instanceof OrchestrationError) || + error.code !== 'run_binding_changed' + ) { + throw error + } + } } - const key = runCoordinatorKey(run) +} + +/** + * How a user recognizes the coordinator, when there is positive evidence it is still open; else null. + * Only proof counts: a closed chat, an exited or unhooked terminal agent, another host, or anything + * that cannot be checked reads as not open, so a restart handoff never needs a flag. + */ +async function liveCoordinatorLabel( + runtime: OrcaRuntimeService, + db: OrchestrationDb, + key: OrchestrationCoordinatorKey +): Promise { + const address = mailboxAddressOf(key) if (key.orcaSessionId !== null) { // Why: the record store opens lazily, as for a send naming a session. await runtime.ensureStructuredAgentSessionHost().catch(() => undefined) const store = readAgentSessionRecordStore() const found = store ? lookupOrcaAgentSession(store, key.orcaSessionId) : null - return store && - found?.kind === 'found' && - structuredSessionMailReach(store, found.record, db).kind === 'reachable' - ? key - : null + if (!store || found?.kind !== 'found') { + return null + } + const reach = structuredSessionMailReach(store, found.record, db) + if (reach.kind !== 'reachable') { + return null + } + const record = reach.session + const title = record.conversationName ?? defaultAgentChatLabel(record.provider) + return `the "${title}" chat${inWorktree(record.location.workspaceId)} (${address})` } - if (key.paneKey !== null) { - return (await runtime.checkCoordinatorAgentPresence(key.paneKey)) === 'live' ? key : null + if ( + key.paneKey === null || + (await runtime.checkCoordinatorAgentPresence(key.paneKey)) !== 'live' + ) { + return null } - return null + const terminal = key.terminalHandle + ? await runtime.showTerminal(key.terminalHandle).catch(() => null) + : null + const title = terminal?.title ? ` "${terminal.title}"` : '' + return `the terminal${title}${inWorktree(terminal?.worktreeId)} (${address})` +} + +function inWorktree(worktreeId: string | undefined): string { + const path = worktreeId ? splitWorktreeId(worktreeId)?.worktreePath : undefined + const name = path?.split(/[\\/]/).filter(Boolean).at(-1) + return name ? ` in ${name}` : '' } diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts index 83aa4391c5c7..b062e366f1c0 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts @@ -15,11 +15,10 @@ import { } from '../../../../orchestration/orchestration-caller-identity' import { resolveDeclaredCallerParty } from '../../../../orchestration/orchestration-party' import { - findCallerDispatchWorkingForRun, findCallerWorkerDispatches, unboundWorkerMessage, - workerCannotCoordinateRunError -} from '../../../../orchestration/db/runs/run-worker-lineage' + workerRunBindingRefusal +} from '../../../../orchestration/db/runs/worker-run-binding' import type { OrchestrationDb } from '../../../../orchestration/db' export type RunScopeParams = { @@ -139,7 +138,9 @@ function unboundCallerError( const holder = mailboxAddressOf(runCoordinatorKey(explicit)) return new OrchestrationError( 'consumer_fenced', - `This caller is not Run ${explicit.id}'s coordinator (${holder ? `its coordinator is ${holder}` : 'it has no coordinator'}). Nothing changed.` + holder + ? `Stop coordinating Run ${explicit.id}; its coordinator is now ${holder}. Take it back only if the user asks. Nothing changed.` + : `This caller does not coordinate Run ${explicit.id}, which has no coordinator now. Nothing changed.` ) } const worked = findCallerWorkerDispatches(db, caller)[0] @@ -189,12 +190,12 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar const current = db.getCurrentRunForCoordinator(caller) // Why: "bound to X, not Y" reads as a nudge to rebind; a worker naming the Run it works for // has to learn where its own mail is instead. - const workedDispatch = + const workerRefusal = explicit && current?.id !== explicit.id - ? findCallerDispatchWorkingForRun(db, caller, explicit.id) - : undefined - if (explicit && workedDispatch) { - throw workerCannotCoordinateRunError(workedDispatch, explicit.id, 'act') + ? workerRunBindingRefusal(db, caller, explicit.id, { takeover: false, bind: false }) + : null + if (workerRefusal) { + throw workerRefusal } if (!current) { throw unboundCallerError(db, caller, explicit) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts index 34b89d89adfd..9285b3a99651 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -39,10 +39,14 @@ describe('run-use against a Run whose coordinator is still running', () => { const runId = await chatRun() const before = h.db.getRunRaw(runId) - expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ + const refused = await as(SESSION_Y, 'orchestration.runUse', { id: runId }) + expect(refused).toMatchObject({ ok: false, error: { code: 'run_coordinator_live', data: { effectsApplied: false } } }) + expect(JSON.stringify(refused)).toMatch( + /the \\"[^"\\]+\\" chat in tree-x \(orca_session_id:[^)]+\), which is still open/ + ) expect(h.db.getRunRaw(runId)).toEqual(before) resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true })) @@ -82,20 +86,67 @@ describe('run-use against a Run whose coordinator is still running', () => { expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_Y) }) - it('never lets a worker take over, with or without the flag', async () => { - const runId = await chatRun() + it('lets a worker coordinate only a Run it created, never by --takeover', async () => { + const stranger = h.db.createRun({ + objective: 'x', + coordinatorHandle: null, + coordinatorPaneKey: null + }) const home = h.db.createRun({ objective: 'home', coordinatorHandle: null, coordinatorPaneKey: null }) createRootDispatch(h.db, h.db.createTask({ spec: 'w', runId: home.id }).id, WORKER_HANDLE) + const runUse = (id: string, takeover: boolean) => + as(undefined, 'orchestration.runUse', { id, from: WORKER_HANDLE, takeover }) + const sub = idOf( + resultOf( + await as(undefined, 'orchestration.runCreate', { objective: 's', from: WORKER_HANDLE }) + ).run + ) + resultOf( + await as(undefined, 'orchestration.runCreate', { objective: 's2', from: WORKER_HANDLE }) + ) + // Nobody coordinates the stranger Run, so no liveness probe could have stopped this. for (const takeover of [false, true]) { - expect( - await as(undefined, 'orchestration.runUse', { id: runId, from: WORKER_HANDLE, takeover }) - ).toMatchObject({ ok: false, error: { code: 'worker_cannot_take_over_run' } }) + expect(await runUse(stranger.id, takeover)).toMatchObject({ + ok: false, + error: { code: 'worker_cannot_take_over_run' } + }) } - expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_X) + expect(await runUse(sub, true)).toMatchObject({ + error: { code: 'worker_cannot_take_over_run' } + }) + resultOf(await runUse(sub, false)) + resultOf(await runUse(sub, true)) + }) + + it('re-probes when the binding moves under it, then refuses rather than bind blindly', async () => { + const runId = h.db.createRun({ + objective: 'o', + coordinatorHandle: 'term_coord', + coordinatorPaneKey: COORD_PANE + }).id + let racers = 0 + const probe = vi + .spyOn(h.runtime, 'checkCoordinatorAgentPresence') + .mockImplementation(async () => { + racers += 1 + h.db.bindRun({ + runId, + coordinatorHandle: `term_racer${racers}`, + coordinatorPaneKey: `tab_r:0000000${racers}-0000-4000-8000-000000000000` + }) + return 'exited' + }) + + expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ + ok: false, + error: { code: 'run_binding_changed', data: { effectsApplied: false, retryable: true } } + }) + expect(probe).toHaveBeenCalledTimes(3) + expect(h.db.getRunRaw(runId)?.coordinator_handle).toBe('term_racer3') }) }) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts index 13231465641e..4f9564f61b74 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts @@ -3,11 +3,8 @@ import { OrchestrationError } from '../../../../orchestration/orchestration-erro import { assertCallerHandleMatchesEvidence, resolveOrchestrationCaller } from './run-scope' import { exposeRun } from './run-receipt' import type { OrcaRuntimeService } from '../../../../orca-runtime' -import { - runBoundToCoordinator, - type OrchestrationCallerIdentity -} from '../../../../orchestration/orchestration-caller-identity' -import { provenLiveRunCoordinator } from './run-coordinator-liveness' +import type { OrchestrationCallerIdentity } from '../../../../orchestration/orchestration-caller-identity' +import { bindAfterProbe } from './run-coordinator-liveness' import { currentDispatchAssigneeRun } from '../messaging/recipient-routing' import { RunCreateParams, @@ -93,22 +90,15 @@ export const ORCHESTRATION_RUN_METHODS = [ assertCallerHandleMatchesEvidence(runtime, params.from, orchestrationCompatibilityEvidence) const db = runtime.getOrchestrationDb() const priorRun = db.getCurrentRunForCoordinator(caller) - const target = db.getRunRaw(params.id) - // Why: liveness probes are async, so they run here; bindRun enforces the answer in its transaction. - const provenLiveCoordinator = - target && !runBoundToCoordinator(target, caller) - ? await provenLiveRunCoordinator(runtime, db, params.id) - : null - const run = db.bindRun({ + const run = await bindAfterProbe(runtime, db, () => ({ runId: params.id, coordinatorHandle: caller.terminalHandle, coordinatorPaneKey: caller.paneKey, coordinatorOrcaSessionId: caller.orcaSessionId, takeoverLegacy: params.takeoverLegacy, takeover: params.takeover, - provenLiveCoordinator, legacyCoordinatorAuthority - }) + })) if (!run) { throw new OrchestrationError( 'run_not_found', diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index e26fdeb27734..e612af755a4d 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -251,7 +251,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( ok: false, error: { code: 'consumer_fenced', - message: `This caller is not Run ${runId}'s coordinator (its coordinator is ${ADDRESS_Y}). Nothing changed.` + message: `Stop coordinating Run ${runId}; its coordinator is now ${ADDRESS_Y}. Take it back only if the user asks. Nothing changed.` } }) expect(await as(SESSION_X, 'orchestration.runCurrent', {})).toMatchObject({ run: null }) diff --git a/src/main/runtime/rpc/orchestration-session-run-binding.test.ts b/src/main/runtime/rpc/orchestration-session-run-binding.test.ts index 0f6b65d6571d..2e359f546714 100644 --- a/src/main/runtime/rpc/orchestration-session-run-binding.test.ts +++ b/src/main/runtime/rpc/orchestration-session-run-binding.test.ts @@ -30,6 +30,7 @@ function deliveryId(result: Record): string { describe('structured lead Run binding through the RPC dispatcher', () => { let h: SessionCallerHarness let dispatchId: string + let leadHandle: string function call(sessionId: string, method: string, params: Record = {}) { return h @@ -40,6 +41,7 @@ describe('structured lead Run binding through the RPC dispatcher', () => { beforeEach(async () => { h = createSessionCallerHarness(hostRef) const handle = mintStructuredWorkerHandle() + leadHandle = handle const paneKey = mintStructuredWorkerPaneKey(SESSION_Y) structuredWorkerIdentities.register({ handle, @@ -60,6 +62,16 @@ describe('structured lead Run binding through the RPC dispatcher', () => { maxDepth: Number.MAX_SAFE_INTEGER }).id }) + // A Run this lead created earlier and that lost its binding: the one kind a worker may resume. + function orphanedOwnRun(): string { + const id = h.db.createRun({ + objective: 'resume', + coordinatorHandle: leadHandle, + coordinatorPaneKey: null + }).id + h.db.db.prepare('UPDATE runs SET coordinator_handle = NULL WHERE id = ?').run(id) + return id + } afterEach(() => { h.runtime.cancelMessageWaiters(`dispatch:${dispatchId}`) h.close() @@ -69,16 +81,7 @@ describe('structured lead Run binding through the RPC dispatcher', () => { it.each(['runCreate', 'runUse'] as const)( 'cancels a parked session Dispatch check on %s', async (method) => { - const params = - method === 'runCreate' - ? { objective: 'child' } - : { - id: h.db.createRun({ - objective: 'unbound', - coordinatorHandle: null, - coordinatorPaneKey: null - }).id - } + const params = method === 'runCreate' ? { objective: 'child' } : { id: orphanedOwnRun() } const wait = vi.spyOn(h.runtime, 'waitForMessage') const waiting = call(SESSION_Y, 'check', { wait: true, timeoutMs: 500 }) await vi.waitFor(() => From da2e1960f898980b3ded088f8c8b34a758fae1b2 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:23:41 -0700 Subject: [PATCH 10/30] fix(orchestration): say what happens to mail on takeover and stop naming run-use to readers The help now says --takeover replaces the coordinator and replays its unacknowledged mail to you. A replaced mailbox reader is told only that another session coordinates the Run and to stop reading it. --- src/cli/specs/orchestration.ts | 2 +- src/main/runtime/orchestration/db/runs/run-delivery.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/specs/orchestration.ts b/src/cli/specs/orchestration.ts index 1bf6124e867a..ba49e95999de 100644 --- a/src/cli/specs/orchestration.ts +++ b/src/cli/specs/orchestration.ts @@ -22,7 +22,7 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [ allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover', 'takeover-legacy', 'retry-request'], identityFlagRoles: { from: 'caller' }, notes: [ - 'Refused while another coordinator of the Run is still running; --takeover replaces it and fences its unread mail. A caller working a Dispatch can never take over. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' + 'Refused while another coordinator of the Run is still open; --takeover replaces it, and its unacknowledged mail is replayed to you. A caller working a Dispatch can never take over. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' ] }, { diff --git a/src/main/runtime/orchestration/db/runs/run-delivery.ts b/src/main/runtime/orchestration/db/runs/run-delivery.ts index 5df81950c1a1..d30c47926ae1 100644 --- a/src/main/runtime/orchestration/db/runs/run-delivery.ts +++ b/src/main/runtime/orchestration/db/runs/run-delivery.ts @@ -12,7 +12,7 @@ export function requireCurrentConsumer( if (!run || run.legacy === 1 || run.consumer_generation !== consumerGeneration) { throw new OrchestrationError( 'consumer_fenced', - `Run ${runId}'s coordinator binding changed, so this mailbox reader was replaced. Stop reading it; take the Run back only if the user asks (\`orchestration run-use --id ${runId} --takeover\`).` + `Another session now coordinates Run ${runId}; stop reading this mailbox.` ) } return run From 2a68fbbee43167391f09ca5a6466ac2494b2d3d9 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:24:21 -0700 Subject: [PATCH 11/30] refactor(orchestration): throw one non-null refusal from the live gate --- .../runtime/orchestration/db/runs/run-binding.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index 1cb86dd4552a..5fafbcdbfdc5 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -4,7 +4,7 @@ import { LEGACY_CONTRACT_VERSION } from '../contract-constants' import { isEquivalentPaneKey } from '../pane-key-match' import type { OrchestrationDb } from '../orchestration-db' import type { OrcaSessionId } from '../../../../../shared/orca-session-address' -import { findCallerWorkerDispatches, workerRunBindingRefusal } from './worker-run-binding' +import { workerRunBindingRefusal } from './worker-run-binding' import { mailboxAddressOf, runBoundToCoordinator, @@ -151,11 +151,13 @@ export function bindRun( ) } if (probed.liveLabel !== null) { - // A worker passed the worker rule, so this is its own sub-Run that someone else now holds. - const worker = findCallerWorkerDispatches(this, coordinator)[0] - throw worker - ? workerRunBindingRefusal(this, coordinator, params.runId, { takeover: true, bind: true }) - : runCoordinatorLiveError(params.runId, probed.liveLabel) + // A worker that passed the worker rule is resuming its own sub-Run, which someone else now holds. + throw ( + workerRunBindingRefusal(this, coordinator, params.runId, { + takeover: true, + bind: true + }) ?? runCoordinatorLiveError(params.runId, probed.liveLabel) + ) } } this.unbindOtherRunsForCoordinator(coordinator, params.runId) From 2b3b6726971917b97419d40d8eeed5e2122b9707 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:27:08 -0700 Subject: [PATCH 12/30] refactor(runtime): host the coordinator presence probe beside the hook presence seam Main moved the orphan-adoption runtime file; keeping the one-line probe next to checkHookAgentPresenceFn leaves this PR off that file. --- .../orca-runtime-adopt-terminal-orphans-from-inventory.ts | 7 ------- src/main/runtime/orca-runtime-preserved-branch-cleanup.ts | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts b/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts index 4b42a358ef41..0361a3411bd2 100644 --- a/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts +++ b/src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts @@ -206,13 +206,6 @@ export abstract class OrcaRuntimeWithAdoptTerminalOrphansFromInventory extends O ) } - /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ - checkCoordinatorAgentPresence( - paneKey: string - ): Promise<'live' | 'unverifiable' | 'exited' | null> { - return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) - } - getLiveTerminalPaneKey(handle: string): string | null { const structured = resolveStructuredWorkerAuthority( handle, diff --git a/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts b/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts index 95e0dc289b22..0c6d806f9539 100644 --- a/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts +++ b/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts @@ -125,6 +125,13 @@ export abstract class OrcaRuntimeWithPreservedBranchCleanup extends OrcaRuntimeW return this.getAgentProviderSessionRowsForPaneFn?.(paneKey) ?? [] } + /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ + checkCoordinatorAgentPresence( + paneKey: string + ): Promise<'live' | 'unverifiable' | 'exited' | null> { + return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) + } + protected readonly attestAgentHookCompatibilityAuthorityFn: | ((candidate: { paneKey: string From dc1730ad0d67b1d2b8654789c36b62e485d7e22a Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 02:38:38 -0700 Subject: [PATCH 13/30] fix(orchestration): use findLast for the worktree name --- .../rpc/methods/orchestration/runs/run-coordinator-liveness.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts index 94e778317616..1be0d528c4cb 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -100,6 +100,6 @@ async function liveCoordinatorLabel( function inWorktree(worktreeId: string | undefined): string { const path = worktreeId ? splitWorktreeId(worktreeId)?.worktreePath : undefined - const name = path?.split(/[\\/]/).filter(Boolean).at(-1) + const name = path?.split(/[\\/]/).findLast(Boolean) return name ? ` in ${name}` : '' } From cf735988c0fe5f7ca076ad7307dd1217ef21d838 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 13:15:12 -0700 Subject: [PATCH 14/30] refactor(runtime): move the coordinator presence probe beside the pty presence recheck Main is moving the runtime construction files; the probe now sits with recheckHookAgentPresenceForPty, which reads the same seam, so this PR stays off the files being moved. --- .../orca-runtime-preserved-branch-cleanup.ts | 7 ------- .../runs/run-coordinator-liveness.ts | 5 +---- .../runs/run-use-takeover.test.ts | 20 +++++++++---------- .../terminal-agent-status-and-submission.ts | 5 +++++ 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts b/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts index 0c6d806f9539..95e0dc289b22 100644 --- a/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts +++ b/src/main/runtime/orca-runtime-preserved-branch-cleanup.ts @@ -125,13 +125,6 @@ export abstract class OrcaRuntimeWithPreservedBranchCleanup extends OrcaRuntimeW return this.getAgentProviderSessionRowsForPaneFn?.(paneKey) ?? [] } - /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ - checkCoordinatorAgentPresence( - paneKey: string - ): Promise<'live' | 'unverifiable' | 'exited' | null> { - return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) - } - protected readonly attestAgentHookCompatibilityAuthorityFn: | ((candidate: { paneKey: string diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts index 1be0d528c4cb..f7870696da6c 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -85,10 +85,7 @@ async function liveCoordinatorLabel( const title = record.conversationName ?? defaultAgentChatLabel(record.provider) return `the "${title}" chat${inWorktree(record.location.workspaceId)} (${address})` } - if ( - key.paneKey === null || - (await runtime.checkCoordinatorAgentPresence(key.paneKey)) !== 'live' - ) { + if (key.paneKey === null || (await runtime.coordinatorAgentPresence(key.paneKey)) !== 'live') { return null } const terminal = key.terminalHandle diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts index 9285b3a99651..6576893fc8c0 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -79,7 +79,7 @@ describe('run-use against a Run whose coordinator is still running', () => { coordinatorHandle: 'term_coord', coordinatorPaneKey: COORD_PANE }).id - vi.spyOn(h.runtime, 'checkCoordinatorAgentPresence').mockResolvedValue(state) + vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockResolvedValue(state) } resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId })) @@ -130,17 +130,15 @@ describe('run-use against a Run whose coordinator is still running', () => { coordinatorPaneKey: COORD_PANE }).id let racers = 0 - const probe = vi - .spyOn(h.runtime, 'checkCoordinatorAgentPresence') - .mockImplementation(async () => { - racers += 1 - h.db.bindRun({ - runId, - coordinatorHandle: `term_racer${racers}`, - coordinatorPaneKey: `tab_r:0000000${racers}-0000-4000-8000-000000000000` - }) - return 'exited' + const probe = vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockImplementation(async () => { + racers += 1 + h.db.bindRun({ + runId, + coordinatorHandle: `term_racer${racers}`, + coordinatorPaneKey: `tab_r:0000000${racers}-0000-4000-8000-000000000000` }) + return 'exited' + }) expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ ok: false, diff --git a/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts b/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts index f5ecc0ffe466..beee52bb7a3e 100644 --- a/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts +++ b/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts @@ -72,6 +72,11 @@ export abstract class OrcaRuntimeWithSerializeAgentPromptSubmission extends Orca return this.ptyForegroundAgent.read(ptyId, afterTitleObservation) } + /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ + coordinatorAgentPresence(paneKey: string): Promise<'live' | 'unverifiable' | 'exited' | null> { + return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) + } + protected async recheckHookAgentPresenceForPty( ptyId: string ): Promise<'live' | 'unverifiable' | 'exited' | null> { From 6c1eb61e9721be5e6317f9de4fa6b50338e4bde5 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 15:38:14 -0700 Subject: [PATCH 15/30] fix(orchestration): keep workers out of their own Run seat --- .../references/worker-contract.md | 6 +- src/cli/bundled-skill-guides.ts | 4 +- src/cli/specs/orchestration.ts | 4 +- .../orchestration/db/runs/run-binding.ts | 30 +++-- .../db/runs/worker-run-binding.ts | 119 ++++++------------ .../orchestration-adopted-run-binding.test.ts | 27 ++++ .../orchestration-run-delivery-db.test.ts | 4 +- .../runs/run-coordinator-liveness.ts | 8 +- .../methods/orchestration/runs/run-scope.ts | 7 +- .../runs/run-use-takeover.test.ts | 38 ++++-- .../worker/manual-dispatch-release.test.ts | 44 +++++++ .../orchestration/worker/worker-control.ts | 27 +++- .../orchestration-session-coordinator.test.ts | 2 +- 13 files changed, 195 insertions(+), 125 deletions(-) diff --git a/skill-guides/orchestration/references/worker-contract.md b/skill-guides/orchestration/references/worker-contract.md index 74b8c023bca6..68028f5e5fc2 100644 --- a/skill-guides/orchestration/references/worker-contract.md +++ b/skill-guides/orchestration/references/worker-contract.md @@ -46,8 +46,10 @@ run — and once more immediately before `worker_done`, so a redirect or a cancellation lands before the Task settles. `check` names its caller with `--terminal`, never `--from`. Stop checking after `worker_done`. -Never run `run-use`. If you created your own Run, plain `check` still returns -your Dispatch's mail; report to your coordinator with `send`. +Do not use `run-use` to check your Dispatch's mail or bind the Run you work for. +You may resume a separate Run when your coordinator directs a handoff, but you +cannot use either takeover flag. Plain `check` returns your Dispatch's mail +unless you are coordinating a Run; report to your coordinator with `send`. If a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch: the Attempt was re-attached to another worker or settled without you. Stop, do diff --git a/src/cli/bundled-skill-guides.ts b/src/cli/bundled-skill-guides.ts index ae69a64f9609..0bbb7c1df753 100644 --- a/src/cli/bundled-skill-guides.ts +++ b/src/cli/bundled-skill-guides.ts @@ -75,7 +75,7 @@ const ORCA_PER_WORKSPACE_ENV_WINDOWS_SCRIPTS_REFERENCE_MARKDOWN = "# Windows loc const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" // oxfmt-ignore -const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nNever run `run-use`. If you created your own Run, plain `check` still returns\nyour Dispatch's mail; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore const ORCHESTRATION_COORDINATOR_LOOP_REFERENCE_MARKDOWN = "# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n" @@ -96,7 +96,7 @@ const ORCHESTRATION_PLACEMENT_AND_REMOTE_REFERENCE_MARKDOWN = "# Placement and r const ORCHESTRATION_RECOVERY_AND_CLEANUP_REFERENCE_MARKDOWN = "# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n" // oxfmt-ignore -const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nNever run `run-use`. If you created your own Run, plain `check` still returns\nyour Dispatch's mail; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore export const BUNDLED_SKILL_GUIDES = [ diff --git a/src/cli/specs/orchestration.ts b/src/cli/specs/orchestration.ts index ba49e95999de..6b01f49f397e 100644 --- a/src/cli/specs/orchestration.ts +++ b/src/cli/specs/orchestration.ts @@ -16,13 +16,13 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [ }, { path: ['orchestration', 'run-use'], - summary: 'Resume coordinating a Run (workers never use this)', + summary: 'Resume coordinating a Run', usage: 'orca orchestration run-use --id [--from ] [--takeover] [--takeover-legacy] [--retry-request ] [--json]', allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover', 'takeover-legacy', 'retry-request'], identityFlagRoles: { from: 'caller' }, notes: [ - 'Refused while another coordinator of the Run is still open; --takeover replaces it, and its unacknowledged mail is replayed to you. A caller working a Dispatch can never take over. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' + 'Refused while another coordinator of the Run is still open; --takeover replaces it, and its unacknowledged mail is replayed to you. A worker cannot bind its own or ancestor Run or use either takeover flag. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' ] }, { diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index 5fafbcdbfdc5..499e021956ce 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -4,7 +4,7 @@ import { LEGACY_CONTRACT_VERSION } from '../contract-constants' import { isEquivalentPaneKey } from '../pane-key-match' import type { OrchestrationDb } from '../orchestration-db' import type { OrcaSessionId } from '../../../../../shared/orca-session-address' -import { workerRunBindingRefusal } from './worker-run-binding' +import { findCallerWorkerDispatches, workerRunBindingRefusal } from './worker-run-binding' import { mailboxAddressOf, runBoundToCoordinator, @@ -132,6 +132,13 @@ export function bindRun( // Why: only LIVE legacy work needs the flag — settled work has no competing authority left, and // fencing it would strand the recovered graph behind an attestation the caller may not have. if (activeLegacyAssignment && !sameBinding && !provenLegacyBinding && !params.takeoverLegacy) { + if (findCallerWorkerDispatches(this, coordinator).length > 0) { + throw new OrchestrationError( + 'consumer_fenced', + `Run ${params.runId} still has active legacy work and this caller cannot bind it. Nothing changed.`, + { effectsApplied: false } + ) + } throw new OrchestrationError( 'consumer_fenced', 'This adopted Run still has live legacy work. Its attested coordinator may rebind it, or a current coordinator may explicitly use run-use --takeover-legacy.', @@ -151,12 +158,10 @@ export function bindRun( ) } if (probed.liveLabel !== null) { - // A worker that passed the worker rule is resuming its own sub-Run, which someone else now holds. - throw ( - workerRunBindingRefusal(this, coordinator, params.runId, { - takeover: true, - bind: true - }) ?? runCoordinatorLiveError(params.runId, probed.liveLabel) + throw runCoordinatorLiveError( + params.runId, + probed.liveLabel, + findCallerWorkerDispatches(this, coordinator).length > 0 ) } } @@ -237,10 +242,17 @@ function sameCoordinatorKey( } // No recoveryCommand on purpose: agents run those mechanically, and the flag must carry user intent. -function runCoordinatorLiveError(runId: string, label: string): OrchestrationError { +function runCoordinatorLiveError( + runId: string, + label: string, + worker: boolean +): OrchestrationError { + const takeoverAdvice = worker + ? '' + : ` Take it over only if the user asks: orca orchestration run-use --id ${runId} --takeover replaces it, and its unacknowledged mail is replayed to you.` return new OrchestrationError( 'run_coordinator_live', - `Run ${runId} is coordinated by ${label}, which is still open. Nothing changed. Closing it clears this. Take it over only if the user asks: orca orchestration run-use --id ${runId} --takeover replaces it, and its unacknowledged mail is replayed to you.`, + `Run ${runId} is coordinated by ${label}, which is still open. Nothing changed. Closing it clears this.${takeoverAdvice}`, { effectsApplied: false, runId, coordinator: label } ) } diff --git a/src/main/runtime/orchestration/db/runs/worker-run-binding.ts b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts index d913036164d4..2c5d5933851a 100644 --- a/src/main/runtime/orchestration/db/runs/worker-run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts @@ -6,13 +6,11 @@ import { mailboxAddressOf, type OrchestrationCoordinatorKey } from '../../orchestration-caller-identity' -import { workerSeatGrabSql } from './run-coordinator-mail-routing' -/** - * The caller's live Dispatches, matched the way `check` and `send` find a worker's Dispatch: its - * address, then its pane. A context-only self-dispatch is a coordinator's own bookkeeping, not work - * for anyone, so it never makes the caller a worker. - */ +// Creator links point at older rows; this caps corrupt chains without limiting real nesting. +const MAX_LINEAGE_STEPS = 32 + +/** The caller's active delegated Dispatches, matched by address then pane as check and send do. */ export function findCallerWorkerDispatches( db: OrchestrationDb, caller: OrchestrationCoordinatorKey @@ -26,38 +24,25 @@ export function findCallerWorkerDispatches( .filter((dispatch) => !isSelfCreatedDispatch(dispatch)) } -/** - * A Run the worker legitimately coordinated: its address is one of the Run's remembered coordinator - * addresses and was not a seat grab, or it dispatched work into the Run while working one of its - * Dispatches (which survives a reissued terminal handle). - */ -function workerCoordinatedRun( - db: OrchestrationDb, - address: string, +/** A delegated Dispatch in this Run, or started from work in this Run. */ +function callerDispatchWorkingForRun( dispatches: readonly DispatchContextRow[], + db: OrchestrationDb, runId: string -): boolean { - const ids = dispatches.map((dispatch) => dispatch.id) - return Boolean( - db.db - .prepare( - `SELECT 1 WHERE EXISTS ( - SELECT 1 FROM run_coordinator_handles AS alias - WHERE alias.run_id = ? AND alias.terminal_handle = ? AND NOT ${workerSeatGrabSql('alias')} - ) OR EXISTS ( - SELECT 1 FROM dispatch_contexts - WHERE run_id = ? AND creator_dispatch_id IN (${ids.map(() => '?').join(', ')}) - )` - ) - .get(runId, address, runId, ...ids) - ) +): DispatchContextRow | undefined { + for (const own of dispatches) { + let row: DispatchContextRow | undefined = own + for (let step = 0; row && step < MAX_LINEAGE_STEPS; step++) { + if (row.run_id === runId) { + return own + } + row = row.creator_dispatch_id ? db.getDispatchContextById(row.creator_dispatch_id) : undefined + } + } + return undefined } -/** - * Why a worker may not coordinate this Run, or null when it may or the caller is not a worker. A - * worker coordinates only Runs it created; it never coordinates the Run it works for, and never - * takes a Run over. Derived from the Dispatch and coordinator-address rows, with no liveness probe. - */ +/** A worker cannot bind its own or ancestor Run, or use a takeover flag on another Run. */ export function workerRunBindingRefusal( db: OrchestrationDb, caller: OrchestrationCoordinatorKey, @@ -65,64 +50,30 @@ export function workerRunBindingRefusal( attempted: { takeover: boolean; bind: boolean } ): OrchestrationError | null { const dispatches = findCallerWorkerDispatches(db, caller) - const address = mailboxAddressOf(caller) - const first = dispatches[0] - if (!first || address === null) { - return null - } - const own = dispatches.find((dispatch) => dispatch.run_id === runId) + const own = callerDispatchWorkingForRun(dispatches, db, runId) if (own) { - return workerRefusal( + const role = + own.run_id === runId + ? `You work Dispatch ${own.id} in Run ${runId}, so you cannot coordinate that Run.` + : `You work Dispatch ${own.id} in Run ${own.run_id}, which was started from work in Run ${runId}, so you cannot coordinate Run ${runId}.` + return new OrchestrationError( 'worker_cannot_coordinate_own_run', - own, - `Run ${runId} is the Run you work for (Dispatch ${own.id}); it belongs to your coordinator.`, - attempted.bind + `${role} Your mail arrives through plain \`orchestration check\` (no --run); report with \`orchestration send\`. No effects were applied.`, + { effectsApplied: false, dispatchId: own.id } ) } - if (attempted.takeover) { - return workerRefusal( + const first = dispatches[0] + if (first && attempted.bind && attempted.takeover) { + return new OrchestrationError( 'worker_cannot_take_over_run', - first, - `You work Dispatch ${first.id} in Run ${first.run_id}, and a worker never takes a Run over.`, - attempted.bind + 'A caller working an active Dispatch cannot use a takeover flag. No effects were applied.', + { effectsApplied: false, dispatchId: first.id } ) } - if (workerCoordinatedRun(db, address, dispatches, runId)) { - return null - } - return workerRefusal( - 'worker_cannot_take_over_run', - first, - `You work Dispatch ${first.id} in Run ${first.run_id}, and workers coordinate only Runs they created; Run ${runId} is not one of yours.`, - attempted.bind - ) -} - -function workerRefusal( - code: string, - dispatch: DispatchContextRow, - role: string, - bind: boolean -): OrchestrationError { - const subRun = bind - ? ' To coordinate sub-workers, create your own Run with `orchestration run-create`.' - : '' - return new OrchestrationError( - code, - `${role} ${WORKER_MAIL_HINT}${subRun} ${staleDispatchHint(dispatch)} No effects were applied.`, - { effectsApplied: false, dispatchId: dispatch.id } - ) -} - -const WORKER_MAIL_HINT = - 'Your own mail arrives through plain `orchestration check` (no --run); report with `orchestration send`.' - -// Why worker-abandon, not worker-stop: stop closes the Dispatch's terminal, which may be the caller's own. -function staleDispatchHint(dispatch: DispatchContextRow): string { - return `If nothing is working Dispatch ${dispatch.id} any more, its Run's coordinator or the user can end it with \`orca orchestration worker-abandon --dispatch ${dispatch.id}\`; this then works.` + return null } -/** What a worker with no Run of its own is told instead of "bind a Run first". */ +/** What a worker with no Run binding is told instead of a Run binding command. */ export function unboundWorkerMessage(dispatch: DispatchContextRow): string { - return `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id} and are not bound to a Run of your own. ${WORKER_MAIL_HINT} To coordinate sub-workers, create your own Run with \`orchestration run-create\`. No effects were applied.` + return `You work Dispatch ${dispatch.id} in Run ${dispatch.run_id} and are not bound to a Run. Your mail arrives through plain \`orchestration check\` (no --run); report with \`orchestration send\`. No effects were applied.` } diff --git a/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts b/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts index d667267e0a6e..0fecbad18ede 100644 --- a/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts +++ b/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts @@ -126,6 +126,33 @@ describe('adopted Run binding without --takeover-legacy', () => { }) }) + it('does not offer a legacy takeover command to a worker from another Run', () => { + const { db, adoptedRunId } = createAdoptedFixture({ settleWork: false }) + const home = db.createRun({ + objective: 'worker home', + coordinatorHandle: CURRENT_COORDINATOR_HANDLE, + coordinatorPaneKey: CURRENT_COORDINATOR_PANE + }) + createRootDispatch( + db, + db.createTask({ runId: home.id, spec: 'worker' }).id, + 'term_foreign_worker', + 'tab_foreign:22222222-2222-4222-8222-222222222222' + ) + try { + db.bindRun({ + runId: adoptedRunId, + coordinatorHandle: 'term_foreign_worker', + coordinatorPaneKey: 'tab_foreign:22222222-2222-4222-8222-222222222222' + }) + throw new Error('Expected the legacy bind to be fenced') + } catch (error) { + expect(error).toMatchObject({ code: 'consumer_fenced', data: { effectsApplied: false } }) + expect(JSON.stringify(error)).not.toContain('run-use') + } + expect(db.getRun(adoptedRunId)?.coordinator_handle).toBeNull() + }) + it('claims the settled adopted Run and inherits its recovered task graph', () => { const { db, adoptedRunId } = createAdoptedFixture({ settleWork: true }) diff --git a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts index 9add5c067abe..c178ce2ab98b 100644 --- a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts +++ b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts @@ -226,7 +226,7 @@ describe('OrchestrationDb Run state', () => { }) describe('lightweight Run scope', () => { - it('refuses a worker binding a Run it did not create, but not a coordinator with a self-dispatch', () => { + it('refuses a worker binding an ancestor Run, but not a coordinator with a self-dispatch', () => { const d = createDb() const parent = createBoundRun(d) const coordinator = { kind: 'terminal', handle: 'term_coord', paneKey: COORD_PANE } as const @@ -256,7 +256,7 @@ describe('OrchestrationDb Run state', () => { expect(() => d.bindRun({ runId: parent.id, coordinatorHandle: 'term_grand', coordinatorPaneKey: null }) - ).toThrow(expect.objectContaining({ code: 'worker_cannot_take_over_run' })) + ).toThrow(expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' })) expect(d.getRun(parent.id)?.coordinator_handle).toBe('term_coord') }) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts index f7870696da6c..fb50d6557348 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -13,7 +13,6 @@ import { lookupOrcaAgentSession, structuredSessionMailReach } from '../../../../orchestration/structured-session-mail-address' -import { defaultAgentChatLabel } from '../../../../../../shared/agent-session-chat-label' import { splitWorktreeId } from '../../../../../../shared/worktree/id' // Why: a binding that keeps moving under a probe is contention, not something to win by retrying forever. @@ -81,9 +80,7 @@ async function liveCoordinatorLabel( if (reach.kind !== 'reachable') { return null } - const record = reach.session - const title = record.conversationName ?? defaultAgentChatLabel(record.provider) - return `the "${title}" chat${inWorktree(record.location.workspaceId)} (${address})` + return `the chat${inWorktree(reach.session.location.workspaceId)} (${address})` } if (key.paneKey === null || (await runtime.coordinatorAgentPresence(key.paneKey)) !== 'live') { return null @@ -91,8 +88,7 @@ async function liveCoordinatorLabel( const terminal = key.terminalHandle ? await runtime.showTerminal(key.terminalHandle).catch(() => null) : null - const title = terminal?.title ? ` "${terminal.title}"` : '' - return `the terminal${title}${inWorktree(terminal?.worktreeId)} (${address})` + return `the terminal${inWorktree(terminal?.worktreeId)} (${address})` } function inWorktree(worktreeId: string | undefined): string { diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts index b062e366f1c0..dbdf3eb1b90a 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-scope.ts @@ -139,7 +139,7 @@ function unboundCallerError( return new OrchestrationError( 'consumer_fenced', holder - ? `Stop coordinating Run ${explicit.id}; its coordinator is now ${holder}. Take it back only if the user asks. Nothing changed.` + ? `This caller is not bound to Run ${explicit.id}, which is bound to ${holder}. Nothing changed.` : `This caller does not coordinate Run ${explicit.id}, which has no coordinator now. Nothing changed.` ) } @@ -188,8 +188,7 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar ) } const current = db.getCurrentRunForCoordinator(caller) - // Why: "bound to X, not Y" reads as a nudge to rebind; a worker naming the Run it works for - // has to learn where its own mail is instead. + // A worker naming its own or ancestor Run needs its Dispatch mailbox, not a binding change. const workerRefusal = explicit && current?.id !== explicit.id ? workerRunBindingRefusal(db, caller, explicit.id, { takeover: false, bind: false }) @@ -203,7 +202,7 @@ export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopePar if (explicit && current.id !== explicit.id) { throw new OrchestrationError( 'consumer_fenced', - `This caller is bound to ${current.id}, not ${explicit.id}.` + `This caller is bound to Run ${current.id}, not Run ${explicit.id}. Nothing changed.` ) } return current diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts index 6576893fc8c0..5478b98356f0 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -45,7 +45,7 @@ describe('run-use against a Run whose coordinator is still running', () => { error: { code: 'run_coordinator_live', data: { effectsApplied: false } } }) expect(JSON.stringify(refused)).toMatch( - /the \\"[^"\\]+\\" chat in tree-x \(orca_session_id:[^)]+\), which is still open/ + /the chat in tree-x \(orca_session_id:[^)]+\), which is still open/ ) expect(h.db.getRunRaw(runId)).toEqual(before) @@ -86,12 +86,13 @@ describe('run-use against a Run whose coordinator is still running', () => { expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_Y) }) - it('lets a worker coordinate only a Run it created, never by --takeover', async () => { + it('lets a worker resume a separate Run with plain run-use, never by takeover', async () => { const stranger = h.db.createRun({ - objective: 'x', - coordinatorHandle: null, - coordinatorPaneKey: null + objective: 'predecessor sub-Run', + coordinatorHandle: 'term_previous', + coordinatorPaneKey: COORD_PANE }) + const presence = vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockResolvedValue('live') const home = h.db.createRun({ objective: 'home', coordinatorHandle: null, @@ -109,13 +110,26 @@ describe('run-use against a Run whose coordinator is still running', () => { await as(undefined, 'orchestration.runCreate', { objective: 's2', from: WORKER_HANDLE }) ) - // Nobody coordinates the stranger Run, so no liveness probe could have stopped this. - for (const takeover of [false, true]) { - expect(await runUse(stranger.id, takeover)).toMatchObject({ - ok: false, - error: { code: 'worker_cannot_take_over_run' } - }) - } + expect(await runUse(home.id, false)).toMatchObject({ + ok: false, + error: { code: 'worker_cannot_coordinate_own_run' } + }) + expect(await runUse(stranger.id, true)).toMatchObject({ + ok: false, + error: { code: 'worker_cannot_take_over_run' } + }) + const foreignRead = await as(undefined, 'orchestration.check', { + run: stranger.id, + terminal: WORKER_HANDLE, + peek: true + }) + expect(foreignRead).toMatchObject({ ok: false, error: { code: 'consumer_fenced' } }) + expect(JSON.stringify(foreignRead)).not.toContain('run-use') + const liveRefusal = await runUse(stranger.id, false) + expect(liveRefusal).toMatchObject({ ok: false, error: { code: 'run_coordinator_live' } }) + expect(JSON.stringify(liveRefusal)).not.toContain('run-use --id') + presence.mockResolvedValue('exited') + resultOf(await runUse(stranger.id, false)) expect(await runUse(sub, true)).toMatchObject({ error: { code: 'worker_cannot_take_over_run' } }) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-release.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-release.test.ts index dca1eddadf45..27a53d9b70fb 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-release.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-release.test.ts @@ -269,6 +269,50 @@ describe('orchestration.workerAbandon', () => { ) }) + it('refuses self-abandon by a terminal handle, reissued pane, or resolved session before effects', async () => { + const dispatchId = readyWorker() + const pane = 'tab_worker:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' + const orcaSessionId = parseOrcaSessionAddress('orca_session_id:worker_1')! + const session: OrchestrationSessionCaller = { + address: 'term_worker', + terminalHandle: 'term_worker', + paneKey: pane, + orcaSessionId, + sessionId: orcaSessionId, + workspaceId: 'wt' + } + const callers: Partial[] = [ + { orchestrationCompatibilityEvidence: { terminalHandle: 'term_worker', paneKey: pane } }, + { orchestrationCompatibilityEvidence: { terminalHandle: 'term_reminted', paneKey: pane } }, + { orchestrationCaller: session } + ] + for (const caller of callers) { + await expect(abandon(dispatchId, caller)).rejects.toMatchObject({ + code: 'invalid_argument', + data: { effectsApplied: false, dispatchId } + }) + } + expect(db.getDispatchContextById(dispatchId)?.status).toBe('dispatched') + expect(db.getWorkerDispatch(dispatchId)?.state).toBe('ready') + }) + + it('refuses self-abandon of a context-only Dispatch', async () => { + const task = db.createTask({ runId: 'run_legacy_local', spec: 'context-only worker' }) + const dispatch = db.createDispatchContext({ + taskId: task.id, + assigneeHandle: 'term_worker', + creator: { kind: 'system' }, + maxDepth: 9 + }) + await expect( + abandon(dispatch.id, { + orchestrationCompatibilityEvidence: { terminalHandle: 'term_worker' } + }) + ).rejects.toMatchObject({ code: 'invalid_argument', data: { effectsApplied: false } }) + expect(db.getDispatchContextById(dispatch.id)?.status).toBe('dispatched') + expect(db.getTask(task.id)?.status).toBe('dispatched') + }) + it('reports an already-settled worker as stale and changes nothing', async () => { const dispatchId = readyWorker() db.failDispatch(dispatchId, 'tab closed', { workerProcessExited: true }) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/worker-control.ts b/src/main/runtime/rpc/methods/orchestration/worker/worker-control.ts index 7482f573cccd..af3f5d1a63fa 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/worker-control.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/worker-control.ts @@ -203,8 +203,33 @@ export const ORCHESTRATION_WORKER_CONTROL_METHODS = [ name: 'orchestration.workerAbandon', permission: 'workspace', params: WorkerDispatchParams, - handler: (params, { runtime, orchestrationCaller }) => { + handler: (params, { runtime, orchestrationCaller, orchestrationCompatibilityEvidence }) => { const db = runtime.getOrchestrationDb() + // Session identity wins; a terminal-only caller is identified by its launch evidence and pane. + const caller = orchestrationCaller + ? { handle: orchestrationCaller.address, paneKey: orchestrationCaller.paneKey } + : orchestrationCompatibilityEvidence?.terminalHandle + ? { + handle: orchestrationCompatibilityEvidence.terminalHandle, + paneKey: + runtime.getTerminalPaneKey(orchestrationCompatibilityEvidence.terminalHandle) ?? + orchestrationCompatibilityEvidence.paneKey + } + : null + if ( + caller && + db.isDispatchMessageSender({ + dispatchId: params.dispatch, + handle: caller.handle, + paneKey: caller.paneKey + }) + ) { + throw new OrchestrationError( + 'invalid_argument', + `You cannot abandon Dispatch ${params.dispatch} while you are working it. Report to its coordinator instead. No effects were applied.`, + { effectsApplied: false, dispatchId: params.dispatch } + ) + } const abandoned = db.abandonWorkerDispatch( params.dispatch, runtime.getRuntimeId(), diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index e612af755a4d..d400849e3375 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -251,7 +251,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( ok: false, error: { code: 'consumer_fenced', - message: `Stop coordinating Run ${runId}; its coordinator is now ${ADDRESS_Y}. Take it back only if the user asks. Nothing changed.` + message: `This caller is not bound to Run ${runId}, which is bound to ${ADDRESS_Y}. Nothing changed.` } }) expect(await as(SESSION_X, 'orchestration.runCurrent', {})).toMatchObject({ run: null }) From a118df084fe0cbcf54db464d83aae774e4f5668d Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:03:47 -0700 Subject: [PATCH 16/30] fix(orchestration): clarify worker Run handoffs in skill --- skill-guides/orchestration.md | 6 +++++- src/cli/bundled-skill-guides.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/skill-guides/orchestration.md b/skill-guides/orchestration.md index dfe8903665e1..97b3959ce5b3 100644 --- a/skill-guides/orchestration.md +++ b/skill-guides/orchestration.md @@ -110,7 +110,11 @@ ORCA orchestration check --wait --types "worker_done,escalation,question" --time After a restart, resume your own Run with `run-use --id `. It is refused while another coordinator of that Run is still running; replace it with -`--takeover` only when the user asks. Workers never run `run-use`. +`--takeover` only when the user asks. A worker cannot bind the Run it works for +or an ancestor. When its coordinator directs a handoff, a replacement worker +may resume a separate Run with plain `run-use` after the prior coordinator is +no longer provably live. A worker cannot use either takeover flag to replace +another coordinator; an already-bound no-op remains allowed. If `worker-start` exits non-zero, do not relaunch. Read the receipt's `failedStage` and `residualResources`, then load diff --git a/src/cli/bundled-skill-guides.ts b/src/cli/bundled-skill-guides.ts index 0bbb7c1df753..ada298049f51 100644 --- a/src/cli/bundled-skill-guides.ts +++ b/src/cli/bundled-skill-guides.ts @@ -72,10 +72,10 @@ const ORCA_PER_WORKSPACE_ENV_SSH_HOST_REFERENCE_MARKDOWN = "# SSH connection mod const ORCA_PER_WORKSPACE_ENV_WINDOWS_SCRIPTS_REFERENCE_MARKDOWN = "# Windows local-side scripts\n\nLoad this when the user's desktop is Windows and you are scaffolding the local-side scripts. A bare\n`.sh` will not execute there. Either require WSL or Git Bash and point `orca.yaml` at a launcher such\nas `bash ./scripts/orca-vm/.sh` through a `.cmd` file, or scaffold PowerShell equivalents.\n\nThe remote-side commands you run inside the Linux environment stay bash regardless of the desktop OS.\n\n```powershell\n#requires -Version 5\n$ErrorActionPreference = 'Stop'\n# resolve env→state→fallback; run the provider CLI / ssh the same way;\n# capture provider output; build the result object for the chosen mode and write ONE line of JSON to stdout.\n# Orca-server mode: @{ schemaVersion=1; pairingCode=$pairingCode; projectRoot=$projectRoot; userData=@{...} }\n# SSH mode: @{ schemaVersion=1; connection=@{ type=\"ssh\"; projectRoot=$projectRoot;\n# target=@{ label=$label; host=$host; port=$port; username=$user } } }\n($result | ConvertTo-Json -Compress -Depth 6)\n# progress/errors → Write-Error / the error stream, never stdout.\n```\n\nThe doctor's executable-bit check is a POSIX concept and is skipped on Windows, so a script that is\nunusable on the user's machine for a different reason still has to be caught by the `--provision`\nself-test.\n" // oxfmt-ignore -const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" +const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. A worker cannot bind the Run it works for\nor an ancestor. When its coordinator directs a handoff, a replacement worker\nmay resume a separate Run with plain `run-use` after the prior coordinator is\nno longer provably live. A worker cannot use either takeover flag to replace\nanother coordinator; an already-bound no-op remains allowed.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" // oxfmt-ignore -const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. Workers never run `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. A worker cannot bind the Run it works for\nor an ancestor. When its coordinator directs a handoff, a replacement worker\nmay resume a separate Run with plain `run-use` after the prior coordinator is\nno longer provably live. A worker cannot use either takeover flag to replace\nanother coordinator; an already-bound no-op remains allowed.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore const ORCHESTRATION_COORDINATOR_LOOP_REFERENCE_MARKDOWN = "# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n" From 69818e21ac50e4ae33ac06ec5ca6f89e54efe6e3 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 17:12:16 -0700 Subject: [PATCH 17/30] test: align explicit Run gate refusal wording --- .../methods/orchestration/gates/gate-run-authorization.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts b/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts index a9b9bf438432..f0bdbc44df6c 100644 --- a/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts @@ -194,7 +194,7 @@ describe('orchestration gate Run authorization', () => { ok: false, error: { code: 'consumer_fenced', - message: `This caller is bound to ${runA}, not ${strangerRun.id}.` + message: `This caller is bound to Run ${runA}, not Run ${strangerRun.id}. Nothing changed.` } }) expect(harness.db.getTask(strangerTask.id)?.status).toBe('ready') From 7cef60ca266840a949261362d47779badf4284f3 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:51:57 -0700 Subject: [PATCH 18/30] Move coordinator presence beside terminal presence checks --- .../terminal/input/terminal-agent-status-and-submission.ts | 5 ----- src/main/runtime/terminal/input/terminal-input-dispatch.ts | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts b/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts index afe477fea40c..774fd248fb5e 100644 --- a/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts +++ b/src/main/runtime/terminal/input/terminal-agent-status-and-submission.ts @@ -72,11 +72,6 @@ export abstract class OrcaRuntimeWithSerializeAgentPromptSubmission extends Orca return this.ptyForegroundAgent.read(ptyId, afterTitleObservation) } - /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ - coordinatorAgentPresence(paneKey: string): Promise<'live' | 'unverifiable' | 'exited' | null> { - return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) - } - protected async recheckHookAgentPresenceForPty( ptyId: string, exitedSince?: number diff --git a/src/main/runtime/terminal/input/terminal-input-dispatch.ts b/src/main/runtime/terminal/input/terminal-input-dispatch.ts index c751e96e4c83..2150a72a84b7 100644 --- a/src/main/runtime/terminal/input/terminal-input-dispatch.ts +++ b/src/main/runtime/terminal/input/terminal-input-dispatch.ts @@ -66,6 +66,11 @@ export abstract class OrcaRuntimeWithControllerKnowsPtyIsLive extends OrcaRuntim ) } + /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ + coordinatorAgentPresence(paneKey: string): Promise<'live' | 'unverifiable' | 'exited' | null> { + return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) + } + protected controllerKnowsPtyIsLive(ptyId: string): boolean { try { return this.ptyController?.hasPty?.(ptyId) === true From 61ffe2df28b02ed94d5e15916388efece2a6a0c8 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 16 Oct 2026 00:58:31 -0700 Subject: [PATCH 19/30] Walk complete worker creator lineage when binding Runs --- .../runtime/orchestration/db/runs/worker-run-binding.ts | 9 ++++----- .../orchestration/orchestration-run-delivery-db.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/runtime/orchestration/db/runs/worker-run-binding.ts b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts index 2c5d5933851a..cbe9beec7316 100644 --- a/src/main/runtime/orchestration/db/runs/worker-run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/worker-run-binding.ts @@ -7,9 +7,6 @@ import { type OrchestrationCoordinatorKey } from '../../orchestration-caller-identity' -// Creator links point at older rows; this caps corrupt chains without limiting real nesting. -const MAX_LINEAGE_STEPS = 32 - /** The caller's active delegated Dispatches, matched by address then pane as check and send do. */ export function findCallerWorkerDispatches( db: OrchestrationDb, @@ -32,10 +29,12 @@ function callerDispatchWorkingForRun( ): DispatchContextRow | undefined { for (const own of dispatches) { let row: DispatchContextRow | undefined = own - for (let step = 0; row && step < MAX_LINEAGE_STEPS; step++) { - if (row.run_id === runId) { + const visited = new Set() + while (row) { + if (row.run_id === runId || visited.has(row.id)) { return own } + visited.add(row.id) row = row.creator_dispatch_id ? db.getDispatchContextById(row.creator_dispatch_id) : undefined } } diff --git a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts index c178ce2ab98b..8969b55e4868 100644 --- a/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts +++ b/src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts @@ -257,6 +257,15 @@ describe('OrchestrationDb Run state', () => { expect(() => d.bindRun({ runId: parent.id, coordinatorHandle: 'term_grand', coordinatorPaneKey: null }) ).toThrow(expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' })) + let creatorHandle = 'term_grand' + for (let depth = 0; depth < 33; depth++) { + const handle = `term_deep_${depth}` + dispatchIn(sub.id, handle, { kind: 'terminal', handle: creatorHandle }) + creatorHandle = handle + } + expect(() => + d.bindRun({ runId: parent.id, coordinatorHandle: creatorHandle, coordinatorPaneKey: null }) + ).toThrow(expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' })) expect(d.getRun(parent.id)?.coordinator_handle).toBe('term_coord') }) From ff924f66875d559843fd5cf79ddfd6a9782f4aa0 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:31:22 -0700 Subject: [PATCH 20/30] Restore main documentation after merge hook formatting --- docs/automated-setup-tab-design.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/automated-setup-tab-design.md b/docs/automated-setup-tab-design.md index ce65de893684..710d08c72553 100644 --- a/docs/automated-setup-tab-design.md +++ b/docs/automated-setup-tab-design.md @@ -10,22 +10,22 @@ This is PR 2. [PR 1 (#29810)](https://github.com/stablyai/orca/pull/29810) separ ## Evidence and severity -- Confirmed pre-change mechanism: `shouldSeedWorktreePrimaryTerminal` treated split setup as a reason to create a shell even when the caller supplies native chat. Renderer `queueSetupAndIssueCommands`, runtime `provisionWorktreeTerminals`, and `spawnLocalStartupAndSetupTerminals` in `worktree-remote.ts` each branched on setup location. This PR reuses their existing dedicated-tab arms. -- The supplied incident investigation observed two leaves bound to one PTY and an independent equal-count chat-publication failure. This worktree does not establish the original missing-binding cause or two setup executions. Removing placement complexity is the user-requested product policy; it is not proof of fixing every frozen terminal. -- No demonstrated P0. P1 acceptance risks are replaying setup after host provisioning, losing requested startup/setup sequencing, or changing primary selection/background workspace selection. The validation below exercises actual creation routes and the preserved one-shot startup mechanisms. -- P2 tradeoff: automatic side-by-side setup placement disappears; manual splitting remains. Older execution hosts can still return existing split layouts, which updated clients must accept without moving/replaying setup. -- The first-terminal delayed delivery coupling exists in `ensureWorktreeHasInitialTerminal` for runtime-owned fallback commands. Ordinary desktop structured creation passes no backend startup and returns setup through local IPC, then uses host authority `none`, so its null-primary Setup path is already independent. The host native-chat route normally consumes setup before returning. Keep delayed delivery unchanged: no actual request proves the alleged native-chat P1. The post-PR-1 semantic review found no reason to expand this scope. The conditional STA-9417 expected-failure fixture does not establish the original incident's cause. +* Confirmed pre-change mechanism: `shouldSeedWorktreePrimaryTerminal` treated split setup as a reason to create a shell even when the caller supplies native chat. Renderer `queueSetupAndIssueCommands`, runtime `provisionWorktreeTerminals`, and `spawnLocalStartupAndSetupTerminals` in `worktree-remote.ts` each branched on setup location. This PR reuses their existing dedicated-tab arms. +* The supplied incident investigation observed two leaves bound to one PTY and an independent equal-count chat-publication failure. This worktree does not establish the original missing-binding cause or two setup executions. Removing placement complexity is the user-requested product policy; it is not proof of fixing every frozen terminal. +* No demonstrated P0. P1 acceptance risks are replaying setup after host provisioning, losing requested startup/setup sequencing, or changing primary selection/background workspace selection. The validation below exercises actual creation routes and the preserved one-shot startup mechanisms. +* P2 tradeoff: automatic side-by-side setup placement disappears; manual splitting remains. Older execution hosts can still return existing split layouts, which updated clients must accept without moving/replaying setup. +* The first-terminal delayed delivery coupling exists in `ensureWorktreeHasInitialTerminal` for runtime-owned fallback commands. Ordinary desktop structured creation passes no backend startup and returns setup through local IPC, then uses host authority `none`, so its null-primary Setup path is already independent. The host native-chat route normally consumes setup before returning. Keep delayed delivery unchanged: no actual request proves the alleged native-chat P1. The post-PR-1 semantic review found no reason to expand this scope. The conditional STA-9417 expected-failure fixture does not establish the original incident's cause. ## Surfaces and selection -| Intent | Surfaces owed by this create with setup | -| --------------------- | -------------------------------------------------------------------- | -| Native chat | Requested chat plus Setup, without a split-anchor shell. | -| Terminal agent | Requested agent terminal plus Setup. | -| Plain terminal | Normal primary terminal plus Setup. | -| Setup only | Setup alone; preserve current suppression of default tabs. | +| Intent | Surfaces owed by this create with setup | +| --- | --- | +| Native chat | Requested chat plus Setup, without a split-anchor shell. | +| Terminal agent | Requested agent terminal plus Setup. | +| Plain terminal | Normal primary terminal plus Setup. | +| Setup only | Setup alone; preserve current suppression of default tabs. | | Explicit default tabs | Existing template allocation/commands/title/color policy plus Setup. | -| No setup | Existing behavior. | +| No setup | Existing behavior. | Issue automation retains its independent split from the normal primary pane, command/env, one-shot behavior, and focus policy. Setup stays unselected when another primary surface exists. Setup-only can select Setup when creation is still in view. Background creates/navigation away preserve workspace/tab selection and never reveal/focus a native window. @@ -59,6 +59,7 @@ Rendered checks require rebuilt hidden Electron plus Playwright CDP; inspect tab After implementation: Astra reviews actual final diff/tests and semantic overlap after PR 1 rebase. No merge until PR 1 confirmation, exact-head full `.github/workflows/pr.yml` successful aggregate `verify` / PR checks passed and all required checks, and parent approval through the mandatory blocking merge-gate ask. Use `--match-head-commit` and confirm the merge. + ## Implemented validation record Reviewed source head: `d96ae2ca6afdca919b4d7f8d85308363abe8b28b`, based on the exact PR 1 merge above. The follow-up documentation commit changes no source behavior. The independent review records no unresolved P0/P1 and explicit P2 tradeoffs. From e2e2ddf36b5ac5589237f84da588538e8dc785bf Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:16:12 -0700 Subject: [PATCH 21/30] perf(orchestration): check run bindings without terminal snapshots --- config/vitest.performance.config.ts | 2 + .../run-binding-performance.test.ts | 259 ++++++++++++++++++ .../runs/run-coordinator-liveness.ts | 6 +- .../runs/run-use-takeover.test.ts | 55 ++++ 4 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 src/main/runtime/orchestration/run-binding-performance.test.ts diff --git a/config/vitest.performance.config.ts b/config/vitest.performance.config.ts index 8c0b623d4c74..cfe112fbc23c 100644 --- a/config/vitest.performance.config.ts +++ b/config/vitest.performance.config.ts @@ -6,6 +6,8 @@ import { nodeRuntimePool } from './scripts/vitest-node-runtime-pool' const contracts = [ 'src/main/sqlite/sync-database.test.ts', + 'src/main/runtime/orchestration/run-binding-performance.test.ts', + 'src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts', 'src/main/runtime/orchestration/db/row-column-lists.test.ts', 'src/shared/native-chat-tool-attribution-allocation.test.ts', 'src/shared/native-chat-tool-pairing.performance.test.ts', diff --git a/src/main/runtime/orchestration/run-binding-performance.test.ts b/src/main/runtime/orchestration/run-binding-performance.test.ts new file mode 100644 index 000000000000..b552c346f9d9 --- /dev/null +++ b/src/main/runtime/orchestration/run-binding-performance.test.ts @@ -0,0 +1,259 @@ +import { DatabaseSync } from 'node:sqlite' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { OrchestrationDb } from './db' +import { createRootDispatch } from './db/root-dispatch-test-fixture' +import { formatOrcaSessionAddress } from '../../../shared/orca-session-address' +import { testOrcaSessionId } from '../../../shared/orca-session-address-test-fixture' + +const databases: OrchestrationDb[] = [] +afterEach(() => { + vi.restoreAllMocks() + for (const db of databases.splice(0)) { + db.close() + } +}) + +function openDatabase(): OrchestrationDb { + const db = new OrchestrationDb(':memory:') + databases.push(db) + return db +} + +// Count real SQLite date evaluations, forwarding to SQLite's unchanged implementation on a second connection. +function historyWork(db: OrchestrationDb) { + const native: unknown = Reflect.get(db.db, 'db') + if (!(native instanceof DatabaseSync)) { + throw new Error('Expected node:sqlite database') + } + const oracle = new DatabaseSync(':memory:') + const date = oracle.prepare('SELECT julianday(?) AS value') + let evaluations = 0 + native.function('julianday', { deterministic: true }, (value) => { + evaluations++ + return date.get(value)?.value ?? null + }) + return { + reset: () => { + evaluations = 0 + }, + count: () => evaluations, + close: () => oracle.close() + } +} + +describe('Run binding scaling contracts', () => { + it('walks to the root with one indexed ancestor read per link at N and 10N', () => { + for (const depth of [4, 40]) { + const db = openDatabase() + const root = db.createRun({ + objective: 'root', + coordinatorHandle: 'root', + coordinatorPaneKey: null + }) + const child = db.createRun({ + objective: 'child', + coordinatorHandle: null, + coordinatorPaneKey: null + }) + let parent = createRootDispatch( + db, + db.createTask({ spec: 'root work', runId: root.id }).id, + 'worker_0' + ) + for (let link = 1; link <= depth; link++) { + parent = db.createDispatchContext({ + taskId: db.createTask({ spec: 'child work', runId: child.id }).id, + assigneeHandle: `worker_${link}`, + creator: { kind: 'terminal', handle: `worker_${link - 1}` }, + maxDepth: Number.MAX_SAFE_INTEGER + }) + } + const separate = db.createRun({ + objective: 'handoff', + coordinatorHandle: null, + coordinatorPaneKey: null + }) + const before = db.getRunRaw(root.id) + const reads: string[] = [] + const observed = new WeakSet() + const originalPrepare = db.db.prepare.bind(db.db) + const prepare = vi.spyOn(db.db, 'prepare').mockImplementation((sql) => { + const statement = originalPrepare(sql) + if (/FROM dispatch_contexts WHERE id = \?/.test(sql) && !observed.has(statement)) { + observed.add(statement) + const get = statement.get.bind(statement) + vi.spyOn(statement, 'get').mockImplementation((...params) => { + reads.push(sql) + return get(...params) + }) + } + return statement + }) + const bind = (runId: string) => + db.bindRun({ + runId, + coordinatorHandle: parent.assignee_handle, + coordinatorPaneKey: null + }) + const assertWalk = () => { + expect(reads).toHaveLength(depth) + expect(prepare.mock.calls.length).toBeLessThanOrEqual(depth + 50) + const sql = reads[0] + expect(sql).toBeDefined() + expect(db.db.prepare(`EXPLAIN QUERY PLAN ${sql}`).all('absent')).toContainEqual( + expect.objectContaining({ + detail: expect.stringMatching(/SEARCH dispatch_contexts USING INDEX .*\(id=\?\)/) + }) + ) + } + expect(() => bind(root.id)).toThrow( + expect.objectContaining({ code: 'worker_cannot_coordinate_own_run' }) + ) + assertWalk() + expect(db.getRunRaw(root.id)).toEqual(before) + prepare.mockClear() + reads.length = 0 + expect(bind(separate.id)?.coordinator_handle).toBe(parent.assignee_handle) + assertWalk() + prepare.mockRestore() + } + }) + + it.each([ + [4, 2], + [40, 2], + [4, 20] + ])( + 'bounds four real alias consumers with %i historical Dispatches and %i unread messages per alias', + (history, messages) => { + const db = openDatabase() + const run = db.createRun({ + objective: 'historical aliases', + coordinatorHandle: 'coord', + coordinatorPaneKey: null + }) + const other = db.createRun({ + objective: 'unrelated history', + coordinatorHandle: null, + coordinatorPaneKey: null + }) + const task = db.createTask({ spec: 'retained work', runId: run.id }) + const otherTask = db.createTask({ spec: 'unrelated retained work', runId: other.id }) + const sessionId = testOrcaSessionId('11111111-1111-4111-8111-111111111111') + const aliases = ['old_coord', 'old_worker', formatOrcaSessionAddress(sessionId)] + const insert = db.db.prepare(`INSERT INTO dispatch_contexts + (id, task_id, run_id, assignee_handle, assignee_orca_session_id, creator_handle, status, created_at, completed_at) + VALUES (?, ?, ?, ?, ?, 'coord', 'completed', '2000-01-01', ?)`) + for (const [index, alias] of aliases.entries()) { + db.rememberRunCoordinatorHandle(run.id, alias) + for (let row = 0; row < history; row++) { + insert.run( + `history_${index}_${row}`, + task.id, + run.id, + index === 0 ? alias : 'old_worker', + index === 0 ? null : sessionId, + index > 0 && row === history - 1 ? '2000-01-04' : '2000-01-02' + ) + } + } + for (let row = 0; row < history * 10; row++) { + insert.run(`unrelated_${row}`, otherTask.id, other.id, 'old_coord', null, '2000-01-04') + } + db.db + .prepare('UPDATE run_coordinator_handles SET first_bound_at = ? WHERE run_id = ?') + .run('2000-01-03', run.id) + const work = historyWork(db) + const prepare = vi.spyOn(db.db, 'prepare') + const ids = new Map() + const assertWork = (messageMultiplier: number) => { + // Each alias history is visited, but no unrelated Run or quadratic history join is needed. + expect(work.count()).toBeGreaterThanOrEqual(history) + expect(work.count()).toBeLessThanOrEqual(24 * history * messageMultiplier) + expect(prepare.mock.calls.length).toBeLessThanOrEqual(12 * messageMultiplier + 12) + for (const [sql] of prepare.mock.calls.filter( + ([query]) => + query.includes('FROM dispatch_contexts AS grab') && !query.startsWith('EXPLAIN') + )) { + const plan = db.db + .prepare(`EXPLAIN QUERY PLAN ${sql}`) + .all(...Array(sql.match(/\?/g)?.length ?? 0).fill(null)) + expect(plan).toContainEqual( + expect.objectContaining({ + detail: expect.stringMatching(/SEARCH grab(?: EXISTS)? USING INDEX .*\(run_id=\?\)/) + }) + ) + expect(plan).not.toContainEqual( + expect.objectContaining({ detail: expect.stringMatching(/SCAN grab/) }) + ) + } + work.reset() + prepare.mockClear() + } + const restoreDirect = () => { + for (const [alias, messageIds] of ids) { + for (const id of messageIds) { + db.db.prepare('UPDATE messages SET to_handle = ? WHERE id = ?').run(alias, id) + } + } + work.reset() + prepare.mockClear() + } + try { + for (const alias of aliases) { + expect(db.getRunMailboxOwnerIdsForHandle(alias)).toEqual( + alias === 'old_coord' ? [run.id] : [] + ) + } + assertWork(1) + + // Production INSERT fires the actual routing trigger, including both address spellings. + for (const alias of aliases) { + const messageIds: string[] = [] + for (let message = 0; message < messages; message++) { + const inserted = db.insertMessage({ + from: 'coord', + to: alias, + runId: run.id, + subject: 'direct' + }) + expect(inserted.to_handle).toBe(alias === 'old_coord' ? `run:${run.id}` : alias) + messageIds.push(inserted.id) + } + ids.set(alias, messageIds) + } + assertWork(messages) + + // Restored pre-upgrade direct mail is swept by binding and foreign-mail reconciliation. + restoreDirect() + for (const alias of aliases) { + db.routeAllUnreadDirectMessagesToRunMailbox(run.id, alias) + } + assertWork(messages) + for (const [alias, messageIds] of ids) { + for (const id of messageIds) { + expect(db.getMessageById(id)?.to_handle).toBe( + alias === 'old_coord' ? `run:${run.id}` : alias + ) + } + } + restoreDirect() + for (const alias of aliases) { + expect(db.routeForeignDirectMessagesToOwnedMailboxes(alias).routedCount).toBe( + alias === 'old_coord' ? messages : 0 + ) + } + assertWork(messages) + for (const [alias, messageIds] of ids) { + for (const id of messageIds) { + expect(db.getMessageById(id)?.to_handle).toBe( + alias === 'old_coord' ? `run:${run.id}` : alias + ) + } + } + } finally { + work.close() + } + } + ) +}) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts index fb50d6557348..b1a11d662fdb 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts @@ -85,10 +85,10 @@ async function liveCoordinatorLabel( if (key.paneKey === null || (await runtime.coordinatorAgentPresence(key.paneKey)) !== 'live') { return null } - const terminal = key.terminalHandle - ? await runtime.showTerminal(key.terminalHandle).catch(() => null) + const worktreeId = key.terminalHandle + ? runtime.getTerminalWorktreeIdForPaneKey(key.paneKey) : null - return `the terminal${inWorktree(terminal?.worktreeId)} (${address})` + return `the terminal${inWorktree(worktreeId ?? undefined)} (${address})` } function inWorktree(worktreeId: string | undefined): string { diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts index 5478b98356f0..06c78df5803f 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -1,5 +1,16 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { createRootDispatch } from '../../../../orchestration/db/root-dispatch-test-fixture' +import { registerBoundPty } from '../../../../bind-first-pane.test-fixture' +import { + createRuntime, + PANE_KEY, + TAB_ID, + LEAF_ID, + TERMINAL_HANDLE, + WORKTREE_ID +} from '../../../../orchestration-mailbox-notification-test-harness' +import { RpcDispatcher } from '../../../dispatcher' +import { ORCHESTRATION_METHODS } from '..' import { createSessionCallerHarness, idOf, @@ -56,6 +67,50 @@ describe('run-use against a Run whose coordinator is still running', () => { }) }) + it.each([4, 40])( + 'labels a live terminal without inspecting output among %i workspaces', + async (count) => { + const { runtime } = createRuntime(h.db, { checkHookAgentPresence: async () => 'live' }) + for (let index = 0; index < count; index++) { + registerBoundPty(runtime, `pty_${index}`, `repo::/work/other_${index}`, null, { + tabId: `tab_${index}`, + leafId: `leaf_${index}` + }) + } + const getPane = runtime.getTerminalPaneKey.bind(runtime) + vi.spyOn(runtime, 'getTerminalPaneKey').mockImplementation((handle) => + handle === WORKER_HANDLE ? 'tab_caller:caller' : getPane(handle) + ) + const metadata = vi.spyOn(runtime, 'getTerminalWorktreeIdForPaneKey') + const show = vi.spyOn(runtime, 'showTerminal') + const run = h.db.createRun({ + objective: 'live', + coordinatorHandle: TERMINAL_HANDLE, + coordinatorPaneKey: PANE_KEY + }) + const before = h.db.getRunRaw(run.id) + const dispatcher = new RpcDispatcher({ runtime, methods: ORCHESTRATION_METHODS }) + expect(runtime.getTerminalWorktreeIdForPaneKey(`${TAB_ID}:${LEAF_ID}`)).toBe(WORKTREE_ID) + metadata.mockClear() + const refused = await dispatcher.dispatch( + orchestrationRequest('orchestration.runUse', { id: run.id, from: WORKER_HANDLE }) + ) + expect(refused).toMatchObject({ + ok: false, + error: { + code: 'run_coordinator_live', + message: expect.stringContaining( + `the terminal in mailbox (${TERMINAL_HANDLE}), which is still open.` + ), + data: { effectsApplied: false } + } + }) + expect(metadata).toHaveBeenCalledTimes(1) + expect(show).not.toHaveBeenCalled() + expect(h.db.getRunRaw(run.id)).toEqual(before) + } + ) + it.each([ ['its chat was closed', 'closed'], ['its terminal agent exited', 'exited'], From 3787d5737a95786f3dfb03cd0a43c55070e8285e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:21:49 -0700 Subject: [PATCH 22/30] test(orchestration): narrow SQLite instrumentation before access --- .../orchestration/run-binding-performance.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/runtime/orchestration/run-binding-performance.test.ts b/src/main/runtime/orchestration/run-binding-performance.test.ts index b552c346f9d9..f816fd402fc5 100644 --- a/src/main/runtime/orchestration/run-binding-performance.test.ts +++ b/src/main/runtime/orchestration/run-binding-performance.test.ts @@ -21,10 +21,16 @@ function openDatabase(): OrchestrationDb { // Count real SQLite date evaluations, forwarding to SQLite's unchanged implementation on a second connection. function historyWork(db: OrchestrationDb) { - const native: unknown = Reflect.get(db.db, 'db') - if (!(native instanceof DatabaseSync)) { + const adapter: unknown = db.db + if ( + typeof adapter !== 'object' || + adapter === null || + !('db' in adapter) || + !(adapter.db instanceof DatabaseSync) + ) { throw new Error('Expected node:sqlite database') } + const native = adapter.db const oracle = new DatabaseSync(':memory:') const date = oracle.prepare('SELECT julianday(?) AS value') let evaluations = 0 From f5eccb8e0cb53ba4892595fa5b30f1c03b1af0f6 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:27:41 -0700 Subject: [PATCH 23/30] test(orchestration): import the explicit RPC methods module --- .../rpc/methods/orchestration/runs/run-use-takeover.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts index 06c78df5803f..b915281449a8 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts @@ -10,7 +10,7 @@ import { WORKTREE_ID } from '../../../../orchestration-mailbox-notification-test-harness' import { RpcDispatcher } from '../../../dispatcher' -import { ORCHESTRATION_METHODS } from '..' +import { ORCHESTRATION_METHODS } from '../../orchestration' import { createSessionCallerHarness, idOf, From ab1c437f3a9e58d3e44a393c8e1f1a25ceda7e70 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:40:19 -0700 Subject: [PATCH 24/30] test: cover orchestration restart and continued Run handoff --- .../orchestration-cli-restart-handoff.test.ts | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 src/main/runtime/orchestration-cli-restart-handoff.test.ts diff --git a/src/main/runtime/orchestration-cli-restart-handoff.test.ts b/src/main/runtime/orchestration-cli-restart-handoff.test.ts new file mode 100644 index 000000000000..c795ab4e7560 --- /dev/null +++ b/src/main/runtime/orchestration-cli-restart-handoff.test.ts @@ -0,0 +1,258 @@ +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, expect, it, vi } from 'vitest' +import { z } from 'zod' + +const entity = z.object({ id: z.string() }) +const check = z.object({ + result: z.object({ + runId: z.string(), + deliveryId: z.string(), + messages: z.array(z.object({ id: z.string() }).passthrough()) + }) +}) + +function idOf(response: unknown, key: string): string { + const { result } = z.object({ result: z.record(z.string(), z.unknown()) }).parse(response) + return entity.parse(result[key]).id +} + +afterEach(() => { + vi.unstubAllEnvs() + vi.restoreAllMocks() +}) + +it('retains CLI work and mail across restart, refuses takeover, and completes a separate Run handoff', async () => { + const home = mkdtempSync(join(tmpdir(), 'orca-cli-restart-handoff-')) + for (const name of [ + 'HOME', + 'USERPROFILE', + 'XDG_CONFIG_HOME', + 'XDG_CACHE_HOME', + 'XDG_DATA_HOME', + 'XDG_STATE_HOME', + 'XDG_RUNTIME_DIR', + 'CLAUDE_CONFIG_DIR', + 'CODEX_HOME', + 'ORCA_E2E_HOME_DIR', + 'ORCA_E2E_USER_DATA_DIR', + 'ORCA_USER_DATA_PATH' + ]) { + vi.stubEnv(name, home) + } + vi.stubEnv('ORCA_BACKGROUND_LAUNCH', '1') + vi.stubEnv('ORCA_DISABLE_MACOS_LOGIN_SHELL', '1') + for (const name of [ + 'ORCA_ENVIRONMENT', + 'ORCA_PAIRING_CODE', + 'ORCA_REMOTE_PAIRING', + 'ORCA_AGENT_SESSION_ID', + 'ORCA_RUNTIME_SOURCE_ID', + 'ORCA_RUNTIME_SOURCE_INCARNATION', + 'ORCA_RUNTIME_SOURCE_PROFILE_PATH' + ]) { + vi.stubEnv(name, '') + } + const { main } = await import('../../cli/index') + const { OrchestrationDb } = await import('./orchestration/db') + const { OrcaRuntimeRpcServer } = await import('./runtime-rpc') + const fixture = await import('./orchestration-mailbox-notification-test-harness') + const { registerBoundPty } = await import('./bind-first-pane.test-fixture') + const coord = fixture.TERMINAL_HANDLE + const worker = fixture.SECOND_TERMINAL_HANDLE + const predecessor = 'term_predecessor' + const predecessorTab = '55555555-5555-4555-8555-555555555555' + const predecessorLeaf = '66666666-6666-4666-8666-666666666666' + const predecessorPane = `${predecessorTab}:${predecessorLeaf}` + let predecessorPresence: 'live' | 'exited' = 'live' + let db = new OrchestrationDb(join(home, 'orchestration.db')) + + function runtimeForDatabase() { + const { runtime } = fixture.createRuntime(db, { + checkHookAgentPresence: async (pane) => + pane === predecessorPane ? predecessorPresence : 'live' + }) + fixture.registerSecondPane(runtime) + registerBoundPty(runtime, 'pty-predecessor', fixture.WORKTREE_ID, null, { + tabId: predecessorTab, + leafId: predecessorLeaf, + incarnationId: 'predecessor-incarnation' + }) + runtime.registerPreAllocatedHandleForPty('pty-predecessor', predecessor) + vi.spyOn(runtime, 'getNestedWorkerMaxDepth').mockReturnValue(2) + return runtime + } + let runtime = runtimeForDatabase() + let server = new OrcaRuntimeRpcServer({ runtime, userDataPath: home }) + const output = vi.spyOn(console, 'log').mockImplementation(() => {}) + const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true) + const originalExitCode = process.exitCode + + async function cli(handle: string, args: string[], exitCode = 0): Promise { + vi.stubEnv('ORCA_TERMINAL_HANDLE', handle) + vi.stubEnv('ORCA_PANE_KEY', runtime.getTerminalPaneKey(handle) ?? '') + vi.stubEnv( + 'ORCA_AGENT_LAUNCH_TOKEN', + handle === coord ? fixture.LAUNCH_TOKEN : handle === worker ? fixture.SECOND_LAUNCH_TOKEN : '' + ) + output.mockClear() + stderr.mockClear() + process.exitCode = 0 + await main(['orchestration', ...args, '--json'], home) + expect( + process.exitCode, + JSON.stringify({ args, output: output.mock.calls, stderr: stderr.mock.calls }) + ).toBe(exitCode) + expect(output.mock.calls).toHaveLength(1) + const response: unknown = JSON.parse(String(output.mock.calls.at(-1)?.[0])) + expect(response).toMatchObject({ ok: exitCode === 0 }) + return response + } + + try { + await server.start() + const root = idOf( + await cli(coord, ['run-create', '--objective', 'Retained parent work']), + 'run' + ) + const task = idOf(await cli(coord, ['task-create', '--spec', 'Finish after restart']), 'task') + const dispatch = idOf( + await cli(coord, ['dispatch', '--task', task, '--to', worker]), + 'dispatch' + ) + const separate = idOf( + await cli(predecessor, ['run-create', '--objective', 'Predecessor work']), + 'run' + ) + const continuation = idOf( + await cli(predecessor, ['task-create', '--spec', 'Continue predecessor work']), + 'task' + ) + const retainedMessage = idOf( + await cli(worker, ['send', '--subject', 'Retain this report']), + 'message' + ) + const retainedBatch = check.parse(await cli(coord, ['check'])).result + expect(retainedBatch).toMatchObject({ runId: root, messages: [{ id: retainedMessage }] }) + + await server.stop() + db.close() + db = new OrchestrationDb(join(home, 'orchestration.db')) + runtime = runtimeForDatabase() + server = new OrcaRuntimeRpcServer({ runtime, userDataPath: home }) + await server.start() + expect(await cli(coord, ['run-current'])).toMatchObject({ result: { run: { id: root } } }) + expect(await cli(coord, ['task-list'])).toMatchObject({ + result: { tasks: [{ id: task, status: 'dispatched', dispatch_id: dispatch }] } + }) + expect(check.parse(await cli(coord, ['check'])).result).toEqual(retainedBatch) + + const bindingBefore = db.getRunRaw(root) + expect(await cli(worker, ['run-use', '--id', root], 1)).toMatchObject({ + error: { code: 'worker_cannot_coordinate_own_run', data: { effectsApplied: false } } + }) + expect(db.getRunRaw(root)).toEqual(bindingBefore) + expect(check.parse(await cli(coord, ['check'])).result).toEqual(retainedBatch) + expect(await cli(coord, ['check', '--ack', retainedBatch.deliveryId])).toMatchObject({ + result: { acknowledged: retainedBatch.deliveryId, count: 0 } + }) + expect(await cli(worker, ['run-use', '--id', separate], 1)).toMatchObject({ + error: { code: 'run_coordinator_live', data: { effectsApplied: false } } + }) + predecessorPresence = 'exited' + expect(await cli(worker, ['run-use', '--id', separate])).toMatchObject({ + result: { run: { id: separate } } + }) + + const question = idOf( + await cli(worker, [ + 'send', + '--to', + `run:${root}`, + '--type', + 'question', + '--subject', + 'Proceed with predecessor work?' + ]), + 'message' + ) + const questionBatch = check.parse(await cli(coord, ['check'])).result + expect(questionBatch).toMatchObject({ + runId: root, + messages: [{ id: question, type: 'question' }] + }) + const reply = idOf( + await cli(coord, ['reply', '--id', question, '--body', 'Proceed with the retained task']), + 'message' + ) + const replyBatch = check.parse(await cli(worker, ['check'])).result + expect(replyBatch).toMatchObject({ + runId: separate, + messages: [ + { + id: reply, + thread_id: question, + to_handle: `run:${separate}`, + run_id: separate, + body: 'Proceed with the retained task' + } + ] + }) + expect(db.getMessageById(reply)?.read).toBe(0) + expect(await cli(worker, ['check', '--ack', replyBatch.deliveryId])).toMatchObject({ + result: { runId: separate, acknowledged: replyBatch.deliveryId, count: 0 } + }) + expect(db.getMessageById(reply)?.read).toBe(1) + await cli(coord, ['check', '--ack', questionBatch.deliveryId]) + + const continuedDispatch = idOf( + await cli(worker, ['dispatch', '--task', continuation, '--to', predecessor]), + 'dispatch' + ) + expect( + await cli(predecessor, [ + 'send', + '--type', + 'worker_done', + '--subject', + 'Predecessor work complete', + '--payload', + JSON.stringify({ + taskId: continuation, + dispatchId: continuedDispatch, + outcome: 'succeeded' + }) + ]) + ).toMatchObject({ result: { lifecycle: { action: 'completed' } } }) + expect(await cli(worker, ['task-list'])).toMatchObject({ + result: { tasks: [{ id: continuation, status: 'completed' }] } + }) + expect(await cli(worker, ['dispatch-show', '--task', continuation])).toMatchObject({ + result: { dispatch: { id: continuedDispatch, status: 'completed' } } + }) + + expect( + await cli(worker, [ + 'send', + '--type', + 'worker_done', + '--subject', + 'Parent work complete', + '--payload', + JSON.stringify({ taskId: task, dispatchId: dispatch, outcome: 'succeeded' }) + ]) + ).toMatchObject({ result: { lifecycle: { action: 'completed' } } }) + expect(await cli(coord, ['task-list'])).toMatchObject({ + result: { tasks: [{ id: task, status: 'completed' }] } + }) + expect(await cli(coord, ['dispatch-show', '--task', task])).toMatchObject({ + result: { dispatch: { id: dispatch, status: 'completed' } } + }) + } finally { + process.exitCode = originalExitCode + await server.stop() + db.close() + rmSync(home, { recursive: true, force: true }) + } +}) From dbadf5e78cacd0800bb53c2286d121b232a9f64e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 23 Oct 2026 12:51:29 -0700 Subject: [PATCH 25/30] test: place orchestration CLI integration in CLI project --- .../orchestration-restart-handoff.test.ts} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename src/{main/runtime/orchestration-cli-restart-handoff.test.ts => cli/orchestration-restart-handoff.test.ts} (95%) diff --git a/src/main/runtime/orchestration-cli-restart-handoff.test.ts b/src/cli/orchestration-restart-handoff.test.ts similarity index 95% rename from src/main/runtime/orchestration-cli-restart-handoff.test.ts rename to src/cli/orchestration-restart-handoff.test.ts index c795ab4e7560..8ad6dfe6f76b 100644 --- a/src/main/runtime/orchestration-cli-restart-handoff.test.ts +++ b/src/cli/orchestration-restart-handoff.test.ts @@ -54,11 +54,11 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a ]) { vi.stubEnv(name, '') } - const { main } = await import('../../cli/index') - const { OrchestrationDb } = await import('./orchestration/db') - const { OrcaRuntimeRpcServer } = await import('./runtime-rpc') - const fixture = await import('./orchestration-mailbox-notification-test-harness') - const { registerBoundPty } = await import('./bind-first-pane.test-fixture') + const { main } = await import('./index') + const { OrchestrationDb } = await import('../main/runtime/orchestration/db') + const { OrcaRuntimeRpcServer } = await import('../main/runtime/runtime-rpc') + const fixture = await import('../main/runtime/orchestration-mailbox-notification-test-harness') + const { registerBoundPty } = await import('../main/runtime/bind-first-pane.test-fixture') const coord = fixture.TERMINAL_HANDLE const worker = fixture.SECOND_TERMINAL_HANDLE const predecessor = 'term_predecessor' From d3c6c99e91c4f3137ddeaba1f14ad79514352c73 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:43:17 -0700 Subject: [PATCH 26/30] test(cli): use explicit module extensions in restart handoff test --- src/cli/orchestration-restart-handoff.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli/orchestration-restart-handoff.test.ts b/src/cli/orchestration-restart-handoff.test.ts index 8ad6dfe6f76b..5fb39196e542 100644 --- a/src/cli/orchestration-restart-handoff.test.ts +++ b/src/cli/orchestration-restart-handoff.test.ts @@ -54,11 +54,11 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a ]) { vi.stubEnv(name, '') } - const { main } = await import('./index') - const { OrchestrationDb } = await import('../main/runtime/orchestration/db') - const { OrcaRuntimeRpcServer } = await import('../main/runtime/runtime-rpc') - const fixture = await import('../main/runtime/orchestration-mailbox-notification-test-harness') - const { registerBoundPty } = await import('../main/runtime/bind-first-pane.test-fixture') + const { main } = await import('./index.js') + const { OrchestrationDb } = await import('../main/runtime/orchestration/db.js') + const { OrcaRuntimeRpcServer } = await import('../main/runtime/runtime-rpc.js') + const fixture = await import('../main/runtime/orchestration-mailbox-notification-test-harness.js') + const { registerBoundPty } = await import('../main/runtime/bind-first-pane.test-fixture.js') const coord = fixture.TERMINAL_HANDLE const worker = fixture.SECOND_TERMINAL_HANDLE const predecessor = 'term_predecessor' From a865bf9f597edf53638dabdb17b7a07fb56a8778 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:55:20 -0700 Subject: [PATCH 27/30] test(orchestration): isolate source CLI integration in subprocesses --- ...orchestration-cli-restart-handoff.test.ts} | 81 +++++++++++++------ 1 file changed, 55 insertions(+), 26 deletions(-) rename src/{cli/orchestration-restart-handoff.test.ts => main/runtime/orchestration-cli-restart-handoff.test.ts} (81%) diff --git a/src/cli/orchestration-restart-handoff.test.ts b/src/main/runtime/orchestration-cli-restart-handoff.test.ts similarity index 81% rename from src/cli/orchestration-restart-handoff.test.ts rename to src/main/runtime/orchestration-cli-restart-handoff.test.ts index 5fb39196e542..0196747a6ed1 100644 --- a/src/cli/orchestration-restart-handoff.test.ts +++ b/src/main/runtime/orchestration-cli-restart-handoff.test.ts @@ -1,8 +1,15 @@ +import { spawn } from 'node:child_process' import { mkdtempSync, rmSync } from 'node:fs' +import { createRequire } from 'node:module' import { tmpdir } from 'node:os' import { join } from 'node:path' +import { pathToFileURL } from 'node:url' import { afterEach, expect, it, vi } from 'vitest' import { z } from 'zod' +import { OrchestrationDb } from './orchestration/db' + +const cliPath = join(process.cwd(), 'src/cli/index.ts') +const tsxLoader = pathToFileURL(createRequire(import.meta.url).resolve('tsx')).href const entity = z.object({ id: z.string() }) const check = z.object({ @@ -28,6 +35,8 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a for (const name of [ 'HOME', 'USERPROFILE', + 'APPDATA', + 'LOCALAPPDATA', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME', 'XDG_DATA_HOME', @@ -54,11 +63,9 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a ]) { vi.stubEnv(name, '') } - const { main } = await import('./index.js') - const { OrchestrationDb } = await import('../main/runtime/orchestration/db.js') - const { OrcaRuntimeRpcServer } = await import('../main/runtime/runtime-rpc.js') - const fixture = await import('../main/runtime/orchestration-mailbox-notification-test-harness.js') - const { registerBoundPty } = await import('../main/runtime/bind-first-pane.test-fixture.js') + const { OrcaRuntimeRpcServer } = await import('./runtime-rpc') + const fixture = await import('./orchestration-mailbox-notification-test-harness') + const { registerBoundPty } = await import('./bind-first-pane.test-fixture') const coord = fixture.TERMINAL_HANDLE const worker = fixture.SECOND_TERMINAL_HANDLE const predecessor = 'term_predecessor' @@ -85,27 +92,50 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a } let runtime = runtimeForDatabase() let server = new OrcaRuntimeRpcServer({ runtime, userDataPath: home }) - const output = vi.spyOn(console, 'log').mockImplementation(() => {}) - const stderr = vi.spyOn(process.stderr, 'write').mockImplementation(() => true) - const originalExitCode = process.exitCode - async function cli(handle: string, args: string[], exitCode = 0): Promise { - vi.stubEnv('ORCA_TERMINAL_HANDLE', handle) - vi.stubEnv('ORCA_PANE_KEY', runtime.getTerminalPaneKey(handle) ?? '') - vi.stubEnv( - 'ORCA_AGENT_LAUNCH_TOKEN', - handle === coord ? fixture.LAUNCH_TOKEN : handle === worker ? fixture.SECOND_LAUNCH_TOKEN : '' + const child = spawn( + process.execPath, + [ + '--import', + tsxLoader, + '--conditions=orca-source', + cliPath, + 'orchestration', + ...args, + '--json' + ], + { + cwd: home, + env: { + ...process.env, + ORCA_TERMINAL_HANDLE: handle, + ORCA_PANE_KEY: runtime.getTerminalPaneKey(handle) ?? '', + ORCA_AGENT_LAUNCH_TOKEN: + handle === coord + ? fixture.LAUNCH_TOKEN + : handle === worker + ? fixture.SECOND_LAUNCH_TOKEN + : '' + }, + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + timeout: 15_000 + } ) - output.mockClear() - stderr.mockClear() - process.exitCode = 0 - await main(['orchestration', ...args, '--json'], home) - expect( - process.exitCode, - JSON.stringify({ args, output: output.mock.calls, stderr: stderr.mock.calls }) - ).toBe(exitCode) - expect(output.mock.calls).toHaveLength(1) - const response: unknown = JSON.parse(String(output.mock.calls.at(-1)?.[0])) + let stdout = '' + let stderr = '' + child.stdout.setEncoding('utf8').on('data', (chunk: string) => { + stdout += chunk + }) + child.stderr.setEncoding('utf8').on('data', (chunk: string) => { + stderr += chunk + }) + const code = await new Promise((resolveExit, rejectExit) => { + child.once('close', resolveExit) + child.once('error', rejectExit) + }) + expect(code, JSON.stringify({ args, stdout, stderr })).toBe(exitCode) + const response: unknown = JSON.parse(stdout) expect(response).toMatchObject({ ok: exitCode === 0 }) return response } @@ -250,9 +280,8 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a result: { dispatch: { id: dispatch, status: 'completed' } } }) } finally { - process.exitCode = originalExitCode await server.stop() db.close() rmSync(home, { recursive: true, force: true }) } -}) +}, 120_000) From b2fbefe9a3c3ccc30a695778c935be4a36ebed90 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 25 Oct 2026 16:53:39 -0700 Subject: [PATCH 28/30] refactor(orchestration): leave the live-coordinator takeover rule to #29292 #29008 is the worker fix: a worker cannot bind the Run it works for or an ancestor, cannot abandon itself to get around that, and addresses it held while grabbing a seat stop redirecting its mail. The separate rule that refuses any second coordinator while the first is still open (the presence probe, the --takeover flag and the run_coordinator_live / run_binding_changed refusals) moves to #29292, which models Run ownership and hand-over and already builds on it. run-use binds synchronously again, as on main. --- config/vitest.performance.config.ts | 1 - skill-guides/orchestration.md | 12 +- .../references/worker-contract.md | 2 +- src/cli/bundled-skill-guides.ts | 6 +- .../handlers/orchestration/run-handlers.ts | 1 - src/cli/specs/orchestration.ts | 6 +- .../orchestration-cli-restart-handoff.test.ts | 13 +- .../orchestration/db/runs/run-binding.ts | 56 +---- src/main/runtime/rpc/errors.ts | 2 - .../run-bound-mailbox-interleavings.test.ts | 3 +- .../runs/run-coordinator-liveness.ts | 98 -------- .../runs/run-use-takeover.test.ts | 219 ------------------ .../rpc/methods/orchestration/runs/runs.ts | 8 +- .../orchestration-session-coordinator.test.ts | 12 +- .../terminal/input/terminal-input-dispatch.ts | 5 - src/shared/cli-argument-boundary.ts | 1 - .../rpc-contract/orchestration-runs-params.ts | 3 +- 17 files changed, 24 insertions(+), 424 deletions(-) delete mode 100644 src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts delete mode 100644 src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts diff --git a/config/vitest.performance.config.ts b/config/vitest.performance.config.ts index cfe112fbc23c..1986bb2cc6d2 100644 --- a/config/vitest.performance.config.ts +++ b/config/vitest.performance.config.ts @@ -7,7 +7,6 @@ import { nodeRuntimePool } from './scripts/vitest-node-runtime-pool' const contracts = [ 'src/main/sqlite/sync-database.test.ts', 'src/main/runtime/orchestration/run-binding-performance.test.ts', - 'src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts', 'src/main/runtime/orchestration/db/row-column-lists.test.ts', 'src/shared/native-chat-tool-attribution-allocation.test.ts', 'src/shared/native-chat-tool-pairing.performance.test.ts', diff --git a/skill-guides/orchestration.md b/skill-guides/orchestration.md index f67402354538..a5928f65913a 100644 --- a/skill-guides/orchestration.md +++ b/skill-guides/orchestration.md @@ -108,13 +108,11 @@ ORCA orchestration worker-start --spec "" --worktree current --ag ORCA orchestration check --wait --types "worker_done,escalation,question" --timeout-ms 900000 --json ``` -After a restart, resume your own Run with `run-use --id `. It is refused -while another coordinator of that Run is still running; replace it with -`--takeover` only when the user asks. A worker cannot bind the Run it works for -or an ancestor. When its coordinator directs a handoff, a replacement worker -may resume a separate Run with plain `run-use` after the prior coordinator is -no longer provably live. A worker cannot use either takeover flag to replace -another coordinator; an already-bound no-op remains allowed. +After a restart, resume your own Run with `run-use --id `. A worker +cannot bind the Run it works for or an ancestor, and cannot use +`--takeover-legacy`; an already-bound no-op remains allowed. When its +coordinator directs a handoff, a replacement worker may resume a separate Run +with plain `run-use`. If `worker-start` exits non-zero, do not relaunch. Read the receipt's `failedStage` and `residualResources`, then load diff --git a/skill-guides/orchestration/references/worker-contract.md b/skill-guides/orchestration/references/worker-contract.md index e494a61fb3c4..a7ad813a7785 100644 --- a/skill-guides/orchestration/references/worker-contract.md +++ b/skill-guides/orchestration/references/worker-contract.md @@ -53,7 +53,7 @@ cancellation lands before the Task settles. `check` names its caller with Do not use `run-use` to check your Dispatch's mail or bind the Run you work for. You may resume a separate Run when your coordinator directs a handoff, but you -cannot use either takeover flag. Plain `check` returns your Dispatch's mail +cannot use `--takeover-legacy`. Plain `check` returns your Dispatch's mail unless you are coordinating a Run; report to your coordinator with `send`. If a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch: diff --git a/src/cli/bundled-skill-guides.ts b/src/cli/bundled-skill-guides.ts index d7b8b063c11c..d2db951170bf 100644 --- a/src/cli/bundled-skill-guides.ts +++ b/src/cli/bundled-skill-guides.ts @@ -72,10 +72,10 @@ const ORCA_PER_WORKSPACE_ENV_SSH_HOST_REFERENCE_MARKDOWN = "# SSH connection mod const ORCA_PER_WORKSPACE_ENV_WINDOWS_SCRIPTS_REFERENCE_MARKDOWN = "# Windows local-side scripts\n\nLoad this when the user's desktop is Windows and you are scaffolding the local-side scripts. A bare\n`.sh` will not execute there. Either require WSL or Git Bash and point `orca.yaml` at a launcher such\nas `bash ./scripts/orca-vm/.sh` through a `.cmd` file, or scaffold PowerShell equivalents.\n\nThe remote-side commands you run inside the Linux environment stay bash regardless of the desktop OS.\n\n```powershell\n#requires -Version 5\n$ErrorActionPreference = 'Stop'\n# resolve env→state→fallback; run the provider CLI / ssh the same way;\n# capture provider output; build the result object for the chosen mode and write ONE line of JSON to stdout.\n# Orca-server mode: @{ schemaVersion=1; pairingCode=$pairingCode; projectRoot=$projectRoot; userData=@{...} }\n# SSH mode: @{ schemaVersion=1; connection=@{ type=\"ssh\"; projectRoot=$projectRoot;\n# target=@{ label=$label; host=$host; port=$port; username=$user } } }\n($result | ConvertTo-Json -Compress -Depth 6)\n# progress/errors → Write-Error / the error stream, never stdout.\n```\n\nThe doctor's executable-bit check is a POSIX concept and is skipped on Windows, so a script that is\nunusable on the user's machine for a different reason still has to be caught by the `--provision`\nself-test.\n" // oxfmt-ignore -const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. A worker cannot bind the Run it works for\nor an ancestor. When its coordinator directs a handoff, a replacement worker\nmay resume a separate Run with plain `run-use` after the prior coordinator is\nno longer provably live. A worker cannot use either takeover flag to replace\nanother coordinator; an already-bound no-op remains allowed.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\nA dispatched worker starts its own helpers with `worker-start --spec` on the Run\nit works for, without `run-create`; the helpers report to that worker.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" +const ORCHESTRATION_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. A worker\ncannot bind the Run it works for or an ancestor, and cannot use\n`--takeover-legacy`; an already-bound no-op remains allowed. When its\ncoordinator directs a handoff, a replacement worker may resume a separate Run\nwith plain `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\nA dispatched worker starts its own helpers with `worker-start --spec` on the Run\nit works for, without `run-create`; the helpers report to that worker.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n" // oxfmt-ignore -const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. It is refused\nwhile another coordinator of that Run is still running; replace it with\n`--takeover` only when the user asks. A worker cannot bind the Run it works for\nor an ancestor. When its coordinator directs a handoff, a replacement worker\nmay resume a separate Run with plain `run-use` after the prior coordinator is\nno longer provably live. A worker cannot use either takeover flag to replace\nanother coordinator; an already-bound no-op remains allowed.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\nA dispatched worker starts its own helpers with `worker-start --spec` on the Run\nit works for, without `run-create`; the helpers report to that worker.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\nHelpers a worker starts appear in this Run's `worker-list` with their\n`creatorDispatchId`, and report to that worker rather than to you.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`;\na helper's question goes to the worker that started it, which answers it the\nsame way.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nEach wait defaults to 600000 ms (10 minutes). `--timeout-ms` accepts a positive\ninteger up to 1800000 ms (30 minutes); larger values are rejected before creating\nor resuming a question. For decisions that take longer, resume the same question\nafter each timeout.\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Helper workers\n\nWhen the preamble has a SUB-DISPATCH section, start helpers on your own\nDispatch's Run. Do not `run-create` or `run-use` for them.\n\n```text\nORCA orchestration worker-start --from --spec \"\" --worktree current --agent --json\nORCA orchestration reply --from --id --body \"\"\n```\n\nTheir questions, escalations, and `worker_done` arrive in your own `check`, next\nto your coordinator's mail. Answer their questions with `reply`, and wait for\nevery helper's `worker_done` before sending yours. After you settle, a helper\nstill running reports to your coordinator's Run.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_FULL_MARKDOWN = "---\nname: orchestration\ndescription: >-\n Coordinate supervised Orca workers: threaded messages, blocking ask/reply,\n task dispatch, worker_done/escalation waits, task DAGs, decision gates,\n coordinator loops, and decomposing work across agents. Use `orca-cli` for full\n ownership handoffs — \"hand off\", \"handoff\", \"handover\", \"give this to another\n agent\", \"another worktree\" — unless asked to supervise, monitor, or coordinate\n a DAG, and for terminal control, lightweight terminal prompts, shell commands,\n Orca worktree management, and reading or waiting on terminals.\n---\n\n# Orca orchestration\n\nOrchestration is Orca's structured coordination layer. It records who owns work,\nwhich attempt is authoritative, and when supervised work has settled.\n\n## Outcome\n\n**Result:** every in-scope Task has one explicit outcome and every settled worker\nterminal has a next owner or cleanup decision. **Next consumer:** the user who\nrequested supervision. **Done:** all expected Dispatches have settled, every\ndelivered message was processed before acknowledgment, each settled worker was\nreused, explicitly retained, or released, and the turn ends only when the report\nto that user names, per Task, its outcome, the evidence behind it, and any\nunresolved blocker.\n\n**Safe failure:** preserve work and authority and report the state as unknown or\n`unverifiable`. Only positive proof of exit authorizes stop, abandon, or retry,\nand only an accepted settlement authorizes release. Every other observation,\nabsence included, is a checkpoint.\n\n## Classify the role\n\n| Current context | Role | Route |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------ |\n| The user explicitly asks to supervise, monitor, wait for results, track completion, coordinate a DAG, use a decision gate, or manage ask/reply | Coordinator | Use the supervised loop below |\n| The current prompt contains a live injected preamble with Task and Dispatch IDs | Dispatched worker | Follow the preamble and the worker obligations below |\n| The user asks to hand off ownership or start another agent/worktree without supervision | Handoff owner | Use `orca-cli`; create no Run, Task, or Dispatch and do not monitor completion |\n| A message carries a legacy authority label | Compatibility operator | Load the legacy contract reference before any lifecycle mutation |\n| No live preamble and no explicit supervision | Ordinary terminal agent | Do not emit lifecycle messages; use `orca-cli` for terminal/worktree work |\n\nModel or effort selection does not make a handoff supervised: `orca-cli`'s\n`worktree create --agent` and `agent launch` take `--model` and `--effort`, and\nopen a chat or a terminal per the user's Chat UI setting, as `worker-start` does.\nNever substitute a non-Orca subagent tool when Orca orchestration provenance was\nrequested.\n\n## Authority and safety floor\n\n- A Run is a durable namespace and coordinator inbox; it does not schedule or\n place workers. A Task is work. A Dispatch is one authoritative Task attempt.\n- Lifecycle authority comes from the active Dispatch, not a terminal title,\n copied ID, old database row, provider transcript, or visible pane.\n- Workers use the exact executable, handle, Task ID, and Dispatch ID in the live\n preamble, plus any other flag it carries. Never reconstruct, translate, or\n broaden those arguments.\n- After remote start, address the worker by Dispatch ID. The execution host owns\n process, filesystem, transcript, stop, and cleanup facts. Preserve the verdicts\n `live` / `unverifiable` / `exited`; contact loss is not process death.\n- Liveness is layered: `worker-list`'s `projection.liveness` is the fleet verdict\n for the agent; `worker-show`'s `observation.status` is PTY liveness only. A live\n terminal can still hold a dead or stuck agent.\n- Folder workspaces are valid; never require Git or assume a worktree.\n- Clients and remote servers update independently. Treat unknown optional fields\n as absent. A new stream operation requires advertised capability because old\n decoders may silently drop unknown opcodes. Never fall back to local execution\n when remote authority or capability is unproven.\n- Use the executable you used to run `skills get` for the entire run. In the\n examples below, replace `ORCA` with it; do not create a shell variable or run\n `ORCA` literally. If it fails, report that exact error instead of switching.\n- `ORCA status --json` shows your Orca session ID as `caller.orcaSessionId` when you have one.\n- A successful `orchestration send` proves durable enqueue; its wake or nudge is\n best-effort attention only and does not prove the recipient read or accepted it.\n\n## Worker obligations\n\nThe injected preamble is authoritative. A dispatched worker must:\n\n1. Do only the current Task and use the preamble's `ask` command for a blocking\n coordinator question. Never open a local question TUI the coordinator cannot\n answer. Resume the same message ID after an ask timeout.\n2. Send heartbeats only at the cadence in the preamble. A heartbeat proves\n liveness, not completion.\n3. Read coordinator follow-ups at each natural checkpoint — before starting a\n new file, after a test run — and once more immediately before `worker_done`:\n `ORCA orchestration check --terminal --json`.\n4. Send `worker_done` exactly once, from the dispatched terminal, with a\n three-sentence executive summary, both lifecycle IDs, and explicit\n `--outcome succeeded` or `--outcome failed`. Never encode failure only in prose.\n5. Append `--files-modified` and `--report-path` only with real values when\n applicable. After `worker_done`, end the dispatched turn and idle; do not poll\n or start new work.\n\nA direct user instruction after completion starts new user-owned work and takes\nprecedence over the idle rule. Do not reuse the settled lifecycle IDs.\n\n## Canonical supervised loop\n\nConfirm the runtime, bind one Run, and start the full independent wave before\nwaiting. `worker-start --spec` creates the Task and its attempt in one call:\n\n```text\nORCA status --json\nORCA orchestration run-create --objective \"\" --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent codex --json\nORCA orchestration worker-start --spec \"\" --worktree current --agent claude --json\nORCA orchestration check --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nAfter a restart, resume your own Run with `run-use --id `. A worker\ncannot bind the Run it works for or an ancestor, and cannot use\n`--takeover-legacy`; an already-bound no-op remains allowed. When its\ncoordinator directs a handoff, a replacement worker may resume a separate Run\nwith plain `run-use`.\n\nIf `worker-start` exits non-zero, do not relaunch. Read the receipt's\n`failedStage` and `residualResources`, then load\n`references/recovery-and-cleanup.md`.\n\nUse `task-create` plus `worker-start --task ` for planned fan-out with\ndependencies or a retry of a known Task. Use dependencies only for real ordering\nand prefer parallel waves over chains deeper than three or four steps; nested\nworkers obey the depth limit, and a new Run does not reset the caller's depth.\nA dispatched worker starts its own helpers with `worker-start --spec` on the Run\nit works for, without `run-create`; the helpers report to that worker.\n\nA consuming `check` names its caller with `--terminal `, never `--from`;\nomit it inside the coordinator's own Orca terminal. It returns the bound Run's\noldest FIFO Delivery and replays that batch until acknowledged. Process every\nmessage: reply to questions, validate each `worker_done` against the expected\nactive Dispatch, and decide each settled terminal's next owner before the ack:\n\n```text\nORCA orchestration reply --id --body \"\" --json\nORCA orchestration worker-release --dispatch --json\nORCA orchestration check --ack --wait --types \"worker_done,escalation,question\" --timeout-ms 900000 --json\n```\n\nKeep waiting until every expected Dispatch settles. A timeout or empty result is\na checkpoint, not a failure. Do not stop, retry, release, or launch a duplicate\neditor without the positive proof `## Outcome` requires.\n\nAfter three consecutive empty waits, stop waiting blindly and enumerate with\n`ORCA orchestration worker-list --include-remote --json` (defaults to the bound\nRun; `--run ` overrides; the receipt's `scope` names which), acting on\neach row's `projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv.\nRows come newest first and page at 100: while `page.hasMore`, follow `page.nextCursor` with `--cursor `.\nA `none` `nextAction` has no argv to run: read `liveness.reason` and keep waiting\nwith `check --wait`. Absence never earns an argv; settlement and pending work still do.\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Then load `references/recovery-and-cleanup.md` and choose\n`worker-stop` or `worker-abandon` explicitly. `unverifiable` is absence,\nincluding when `worker-show` reports `agentWait` null. Absence never authorizes\nstop, abandon, retry, or release; keep waiting or inspect.\n\n`worker-start` is the normal path, composing placement, terminal readiness,\nprompt injection, and supervised resource ownership. `dispatch --inject` leaves\nan operator-created process unsupervised and is only for an expressiveness gap.\n\n## Task-spec contract\n\nEvery Task spec must be self-contained and name:\n\n- **Target:** the files, component, or environment in scope.\n- **Change:** the concrete result to produce.\n- **Constraints:** invariants, compatibility rules, and do-not-touch boundaries.\n- **Ownership:** what this worker may edit and any coordination boundary.\n- **Observable acceptance:** the test, output, or evidence that proves completion.\n\n## Completion accounting\n\nAfter an accepted success or failure report, immediately do exactly one:\n\n1. Reuse the same proven agent terminal for an immediate follow-up Dispatch.\n2. Record user-requested retention with `worker-retain`.\n3. Run `worker-release`.\n\nRelease is post-settlement cleanup, not cancellation. Only an accepted\nsettlement authorizes it; no other observation does. If release is uncertain,\nfollow its exact recovery receipt and never substitute `terminal close`.\n\nA valid `worker_done` settles the Task and Dispatch automatically; do not follow\nit with `task-update --status completed`. Enumerate the terminals still owing a\ndecision with `worker-list --run --terminal-state reclaimable --json`,\nand do not end the coordinator turn until it returns none.\n\n## Conditional references\n\nThis compact guide is sufficient for the normal local loop. At an action gate\nbelow, run `ORCA skills get orchestration --reference references/.md` and\nread only that document; `--references` lists the names. If the CLI rejects\n`--reference`, run `ORCA skills get orchestration --full` once instead: it\nreturns this exact kernel and every reference, so read only the named one. If an\nolder CLI rejects `--full`, keep this kernel's safety floor, use that command's\n`--help`, and never guess newer flags.\n\n| Action gate | Bundled reference |\n| ------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |\n| Expanded DAG waves, launch model/effort, same-terminal reuse, or review ownership | `references/coordinator-loop.md` |\n| You are a dispatched worker and the live preamble does not answer your question, or `check` returned an error | `references/worker-contract.md` |\n| New worktree, exact workspace, SSH, WSL, or connected-server placement | `references/placement-and-remote.md` |\n| Inbox replay, follow-up messages, group addresses, or decision gates | `references/messaging-and-gates.md` |\n| Failed/stopped/unknown attempts, retry, stop, abandon, retain, or uncertain release | `references/recovery-and-cleanup.md` |\n| Custom argv or terminal topology that `worker-start` cannot express | `references/low-level-topology.md` |\n| Any legacy label, adopted Run, compatibility receipt, or takeover | `references/legacy-contract-migration.md` |\n\nRetired scheduler commands are not aliases for Run creation. Recovery commands\nmust provide their exact next action; follow it with the same selected executable.\n\n---\n\n# Bundled references\n\nThese references belong to the version-matched guide above. Read only the documents named by its action gates.\n\n\n\n# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\nHelpers a worker starts appear in this Run's `worker-list` with their\n`creatorDispatchId`, and report to that worker rather than to you.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n\n\n\n# Legacy contract migration\n\nLoad this reference only for an authority label, adopted Run, compatibility or\nrecovery receipt, or explicit legacy takeover. A newly created attempt always\nuses the current grammar.\n\n## Authority labels\n\n- `[LEGACY COMPATIBILITY]` is live and attested. Run only the exact supported\n command printed with the message, using the same selected executable and\n arguments supplied by the original prompt.\n- `[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]` is one bounded,\n at-least-once cutover replay. Process it idempotently and acknowledge only\n through the exact displayed guidance.\n- `[LEGACY READ-ONLY]` is inspection-only. It has no reply, acknowledgment, or\n lifecycle mutation.\n- An unlabeled current message uses the current guide and grammar.\n\nAn explicitly selected current Run, attested current binding, current Dispatch,\nor federated attachment takes precedence over legacy fallback. A retained\nadoption record alone does not grant mutation authority. If liveness, principal\nownership, capability, or the exact legacy contract is unproven, degrade to\nread-only inspection and never fall back to local execution.\n\nAdoption preserves the live agent process, PTY/session, terminal handle,\ntab/pane, worktree or folder workspace, Task, and Dispatch. It never restarts or\nreplaces the worker and never revives the retired scheduler. Loss of lifecycle\nauthority does not invalidate the existing process, assignment, or filesystem\nwork. Exact recovery may restore the same PTY once in its original inactive\nbackground tab; it must not spawn, write, signal, stop, switch, focus, split, or\ninject a terminal.\n\n## Compatibility recovery\n\nWhen a compatibility response returns structured next-step arguments, execute\nthose exact arguments with the same selected CLI executable. Do not translate\nfrom memory, broaden the recipient, or retry as a current mutation unless the\nreceipt explicitly authorizes it.\n\nA pending ask, reply, final Dispatch settlement, and consuming check have\ndurable recovery identities. Heartbeat and escalation remain at-least-once\nacross a manual contract-boundary retry. If an ask may already have been\nanswered, run the exact non-consuming recovery check printed by Orca before\ncreating any new question. Never guess among identical question threads.\n\nOn packaged Windows, a legacy ask uses a two-step commit/resume protocol. The\ninitial command commits the question, prints its exact\n`ask --resume ` command, and exits with launcher status `75`. Run\nthat exact resume after the launcher or update boundary. For an attested WSL\nlaunch, preserve the printed `orca-ide` executable and distro route. Older WSL\nworkers without launch proof remain lifecycle read-only even while their\nterminal and filesystem work continue.\n\n## Read-only inspection and takeover\n\nRead-only inspection does not consume mail:\n\n```text\nORCA orchestration run-list --json\nORCA orchestration run-show --id run_legacy_local --json\nORCA orchestration run-show --id --json\nORCA orchestration task-list --run --json\nORCA orchestration inbox --full --json\nORCA orchestration check --terminal --peek --format --json\nORCA terminal read --terminal --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\n```\n\n`run_legacy_local` is an empty audit tombstone after adoption. Find the ordinary\nRun whose objective is `Recovered orchestration work from a contract update`.\n\nOnly when the original coordinator is unavailable or cannot prove retained\nauthority may a new live coordinator take over from its own terminal:\n\n```text\nORCA orchestration run-use --id --takeover-legacy --json\nORCA orchestration check --run --json\n```\n\nTakeover binds the authenticated invoking terminal; `--from` cannot nominate\nanother coordinator. It fences only the old coordinator and moves pending mail\ninto current Run delivery. It preserves live workers, Tasks, Dispatches, processes, and files.\nNever take over while the original coordinator is actively coordinating.\n\nDo not launch a replacement editor merely because Orca updated or authority is\nunclear. Keep the original worker as the only editor until a stable handoff\npoint, then use a fresh current Dispatch in a conflict-free placement.\n\n\n\n# Low-level topology\n\nLoad this reference only when `worker-start` cannot express required custom argv\nor terminal topology. It is not the normal supervised loop and is never a full\nhandoff recipe. A model or effort is not custom argv: `worker-start --model\n--effort` carries them and keeps the user's Chat UI setting.\n\n```text\nORCA terminal create --worktree active --title --command \"\" --json\nORCA terminal wait --terminal --for tui-idle --timeout-ms 60000 --json\nORCA orchestration dispatch --task --to --inject --json\n```\n\nWait for readiness only when startup could lose injected input. Prefer\nagent-first `worker-start` whenever its argv and topology are sufficient.\n\n`dispatch --inject` creates authoritative Task/Dispatch context but deliberately\nkeeps an operator-created process unsupervised: it creates no supervised worker\nresource row. `worker-show`, `worker-read`, and `worker-list` report the lane as\n`unsupervised`; `worker-stop` and `worker-abandon` do not close that process, and\nsettled retain/release take no process action.\n\nUse `worker-start --terminal ` when lifecycle ownership of an existing\nagent terminal is required. Never imply that low-level dispatch retroactively\nowns a process, never use it to route around the nested-depth limit, and never\nuse it for an ownership handoff.\n\n\n\n# Messaging and gates\n\nLoad this reference for inbox replay, attempt-specific guidance, group\naddresses, blocking questions, or coordinator-managed DAG decisions.\n\nA successful `send` proves durable enqueue. Wake and nudge are best-effort\nattention only: neither proves the recipient read the message, began a turn, or\naccepted steering.\n\n## Coordinator delivery loop\n\n`check` names its caller with `--terminal ` and is the only verb that\nrejects `--from`. Omit `--terminal` inside an Orca terminal, where Orca resolves\nthe caller; pass it explicitly from anywhere else, including a dispatched\nworker reading coordinator follow-ups.\n\nA consuming coordinator `check` returns the bound Run's oldest FIFO Delivery,\nup to 50 messages, and replays that exact batch until acknowledged. Process\nevery row and required terminal ownership decision before `--ack`. Type filters\ndecide when a waiter wakes; they do not authorize skipping older actionable\nmail. A Delivery therefore always carries the whole FIFO batch whatever its\ntypes, and a `check` without `--wait` hands that batch over unfiltered.\n`--peek` and `--all` are read-only inspection, not progress through the\ncoordinator inbox.\n\nAn empty wait or timeout is a checkpoint. Continue rolling waits until every\nexpected Dispatch settles. Heartbeat or visible activity means alive, not done.\n\n## Addresses\n\nUse a stable Dispatch address for attempt-specific coordinator guidance:\n\n```text\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\n```\n\nDo not substitute a remote terminal handle. Omit `--from` for ordinary\ncoordinator calls; a dispatched worker instead copies the exact `--from` and\nother arguments in its preamble. `check` is the exception: it identifies\nits caller with `--terminal`, never `--from`.\n\nGroup addresses include `@all`, `@idle`, `@claude`, `@codex`, `@opencode`,\n`@gemini`, `@droid`, `@grok`, `@cursor`, and `@worktree:`. Every group but\n`@worktree:` means the live Dispatches of the sender's own Run. Mail goes\nto each `dispatch:` mailbox, except a worker coordinating a child Run\nreceives it in that `run:` mailbox. A sender bound to no Run is refused;\n`--run` must match the group audience and never grants membership.\nA Run group excludes its owning coordinator; a worker raising a blocker sends\nto `run:`. A worker that created its own Run addresses that Run's workers,\nnot its siblings. `@worktree:` reaches matching workspace terminals,\nincluding coordinators. Use groups only for intentional fan-out status or\nquestions. `worker_done`, heartbeat, and other\nDispatch lifecycle messages never target groups.\n\n## Questions and gates\n\nA worker uses `ask`; its timeout leaves one durable question pending, which the\nworker resumes by message ID. The coordinator answers that message with `reply`;\na helper's question goes to the worker that started it, which answers it the\nsame way.\n\nUse a gate only for a coordinator-owned Task-DAG decision:\n\n```text\nORCA orchestration gate-create --task --question \"\" --options --json\nORCA orchestration gate-resolve --id --resolution \"\" --json\nORCA orchestration gate-list --task --json\n```\n\nPass `json_array` using the quoting rules of the active shell; do not copy POSIX\nsingle-quote syntax into PowerShell or `cmd.exe`.\n\nDo not create a gate merely to answer a worker's `ask`.\n\n\n\n# Placement and remote execution\n\nLoad this reference before creating a new worktree or placing work through SSH,\nWSL, or another connected Orca server.\n\n## Placement choices\n\nA fresh worker means a fresh agent terminal, not a new Git worktree. Use the\ncurrent or an exact existing workspace by default. Create a worktree only when\nthe user requested one or a concrete checkout or filesystem conflict makes\nsharing unsafe.\n\n```text\n# Current workspace; setup is not rerun.\nORCA orchestration worker-start --task --worktree current --agent codex --json\n\n# Stacked child worktree.\nORCA orchestration worker-start --task --worktree new-child --name --agent codex --setup run --json\n\n# Independent top-level worktree.\nORCA orchestration worker-start --task --worktree new-top-level --name --agent codex --setup run --json\n```\n\nCurrent and exact existing workspaces create a fresh terminal unless\n`--terminal` is explicit. Folder workspaces are first-class; do not invoke Git\nor require worktree lineage when the selected workspace is a folder.\n\nRegister a folder workspace through project setup. `repo add --path `\nrequires a valid Git repository and rejects a plain directory:\n\n```text\nORCA project setup-existing-folder --project --host --path --kind folder --json\n```\n\nThen place work on the returned workspace with an exact selector. A worktree\nselector needs the full `::` value Orca returned, passed as\n`id:`; a bare repo id is not a worktree id. `new-child` and\n`new-top-level` are worktree creation and do not apply to a folder.\n\nNew worktrees use agent-first creation and run setup by default. Preserve the\nrepository's startup policy: `start-immediately` can report setup as `running`,\nwhile `wait-for-setup` gates prompt delivery on success. Orca lineage, Git base,\nfilesystem isolation, coordination parentage, UI grouping, and execution host\nare separate decisions.\n\n## Connected servers\n\nThe Run and Tasks remain authoritative on the current server. `--on` selects\nonly the worker's execution server and appears only on `worker-start`:\n\n```text\nORCA orchestration worker-start --task --on --worktree new-top-level --repo --name --agent codex --setup run --json\n```\n\nRemote `current` and `new-child` are invalid because they are ambiguous across\nservers. Use an exact discovered remote workspace, or `new-top-level` with an\nexact remote repository selector. After start, route every follow-up, read,\nstop, and cleanup by Dispatch ID; never repeat `--on` or substitute a remote\nterminal handle.\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\nORCA orchestration send --to dispatch: --subject \"Follow-up\" --body \"\" --json\nORCA orchestration worker-list --run --include-remote --json\n```\n\n`worker-list` reads local fleet state only; enumerate remote workers with\n`--include-remote` or every one of them reads `unverifiable`. Scope every list\nwith `--run `: unscoped, it reports every Dispatch this runtime has\nrecorded, and the workers you are waiting on are lost in that history.\n\n## Execution-host and mixed-version floor\n\nThe execution host owns process, filesystem, transcript, stop, and cleanup\nfacts. Render only `live`, `unverifiable`, or `exited`. Connection loss, relay\nabsence, missing client inventory, or timeout yields `unverifiable`, never\nsynthetic exit and never a client-local substitute action.\n\nClients and servers update independently. Optional response fields may be\nabsent. Forward model/effort, transcript reads, cleanup, or another new remote\noperation only when the peer advertises the relevant capability; unknown stream\nopcodes can be silently dropped. A narrow unsupported response may degrade to a\ndocumented older path, but must not broaden the target or cross the execution\nboundary. Changing host-published content reaches old clients even without a\nwire-shape change, so preserve established semantics or negotiate the behavior.\n\nFor WSL, use the exact executable and arguments returned by Orca so the distro\nand packaged launcher remain bound. Do not translate a printed `orca-ide`\nrecovery command into a PATH-resolved local command.\n\n\n\n# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n\n\n\n# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nEach wait defaults to 600000 ms (10 minutes). `--timeout-ms` accepts a positive\ninteger up to 1800000 ms (30 minutes); larger values are rejected before creating\nor resuming a question. For decisions that take longer, resume the same question\nafter each timeout.\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use `--takeover-legacy`. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Helper workers\n\nWhen the preamble has a SUB-DISPATCH section, start helpers on your own\nDispatch's Run. Do not `run-create` or `run-use` for them.\n\n```text\nORCA orchestration worker-start --from --spec \"\" --worktree current --agent --json\nORCA orchestration reply --from --id --body \"\"\n```\n\nTheir questions, escalations, and `worker_done` arrive in your own `check`, next\nto your coordinator's mail. Answer their questions with `reply`, and wait for\nevery helper's `worker_done` before sending yours. After you settle, a helper\nstill running reports to your coordinator's Run.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore const ORCHESTRATION_COORDINATOR_LOOP_REFERENCE_MARKDOWN = "# Coordinator loop\n\nLoad this reference for expanded DAG waves, per-invocation launch preferences,\nsame-terminal reuse, or review ownership. The compact guide remains the source\nof truth for the loop order and completion boundary.\n\n## Ready waves\n\nCreate independent Tasks before the first wait. Encode only real dependencies,\nthen use the ready view as external memory:\n\n```text\nORCA orchestration task-create --spec \"\" --deps --json\nORCA orchestration task-list --ready --brief --json\n```\n\n`--brief` collapses whitespace and caps echoed specs at 160 characters;\n`spec_truncated` identifies shortened rows. Omit it when full specs are needed or\nwhen an older CLI rejects the flag. A nested worker must respect\n`nested_worker_depth_exceeded`; creating another Run does not reset depth.\nHelpers a worker starts appear in this Run's `worker-list` with their\n`creatorDispatchId`, and report to that worker rather than to you.\n\n## Launch preferences\n\nFor a fresh Claude, Codex, Cursor, Antigravity, or Muse terminal, `--model`\naccepts an opaque provider model ID. Pass it only when the user named a model;\notherwise omit it so the worker inherits the user's configured agent default.\nAdd `--effort` only when that model supports it:\n\n```text\nORCA orchestration worker-start --task --worktree current --agent claude --model opus --effort high --json\nORCA orchestration worker-start --task --worktree current --agent muse --model muse-spark-1.3 --json\n```\n\nOpenCode also accepts `--model` in an existing worktree when the execution host\nverifies its CLI version and model availability. Creating a new worktree with an\nOpenCode model override is unsupported; use `--worktree current` or an existing\nworktree selector, or omit `--model`. OpenCode effort is unsupported. Omit the\nmodel to inherit its configuration; unsupported or unknown hosts refuse the\noverride explicitly.\n\n`--effort` requires `--model`; neither option combines with `--terminal`. A\nconnected worker server must advertise launch-preference support before Orca\nforwards either field. Compare `launch.requested` with `launch.effective`; never\nclaim a model or effort from requested arguments alone.\n\n## Reuse after settlement\n\nChoose the terminal's next owner before acknowledging the Delivery. When the\nsame exact agent has immediate follow-up work, recover the proven handle and\ntransfer cleanup ownership to the new Dispatch:\n\n```text\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-start --task --terminal --json\n```\n\nOtherwise explicitly retain or release the settled worker. Do not leave it live\nonly to inspect output; archived output remains available through `worker-read`.\n\n## Review ownership\n\nA review-only `worker_done` authorizes synthesis of findings, not coordinator\nfile edits. Dispatch or hand off fixes unless the user explicitly assigned them\nto the coordinator. If the user's plan names a next owner, post-review fixes and\nPR preparation remain with that owner; the coordinator routes and synthesizes.\n" @@ -96,7 +96,7 @@ const ORCHESTRATION_PLACEMENT_AND_REMOTE_REFERENCE_MARKDOWN = "# Placement and r const ORCHESTRATION_RECOVERY_AND_CLEANUP_REFERENCE_MARKDOWN = "# Recovery and cleanup\n\nLoad this reference only after a failed/stopped/unknown attempt, explicit retry\ndecision, stop/abandon request, retention request, or uncertain release.\n\n| Proven state | Safe action |\n| ----------------------- | ------------------------------------------------------------------ |\n| `ready` or active | Keep waiting; optionally read bounded output |\n| `failed` or `stopped` | Start a replacement with `--retry-of`; repeat placement explicitly |\n| `outcome_unknown` | Inspect, then choose `worker-stop` or explicit `worker-abandon` |\n| Accepted `worker_done` | Reuse, retain, or release |\n| Remote contact lost | Preserve `unverifiable`; do not stop or retry from absence alone |\n| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |\n| Proven `exited` agent | Enumerate with `worker-list`; follow its `nextAction` |\n\n## Inspect before acting\n\n```text\nORCA orchestration worker-list --run --json\nORCA orchestration worker-list --run --include-remote --json\nORCA orchestration worker-show --dispatch --json\nORCA orchestration worker-read --dispatch --limit 50 --json\n```\n\n`worker-list` is the enumerating command and the authority on agent liveness:\neach row carries `projection.liveness`, `projection.attention.categories`,\n`projection.attention.requiresAction`, and a literal `projection.nextAction`\nargv to run. Always scope it with `--run `; an unscoped list reports\nevery Dispatch this runtime has ever recorded and buries the live ones.\n`worker-show`'s `observation.status` is PTY liveness only, so a `live` terminal\nwhose agent died at a trust prompt still reads `live` there.\n\nWhen the two disagree, the fleet verdict decides — unless the fleet row is\n`unverifiable` for a reason that names a gap on this client rather than a fact\nabout the worker. `missing_status`, `host_unavailable`, and\n`capability_unsupported` are such gaps: the first means this runtime holds no\nstatus row, the second that it could not ask the execution host at all, and the\nthird that a stale peer answered but lacks the fleet-snapshot capability.\nAgainst any of them, a `worker-show` verdict sourced from the execution host is\nthe better evidence and outranks the row. Only `host_unavailable` is contact\nloss; the other two mean the host was never asked or answered without the\ncapability.\n\nThis never promotes absence. `unverifiable` from either command still authorizes\nnothing — only a positive `live` or `exited` verdict does.\n\nA worker started with `--on ` reads `unverifiable` until you\nenumerate with `--include-remote`, which asks its execution host for the\nverdict. Rows come newest first and past 100 the response pages, so follow\n`page.nextCursor` with `--cursor ` until `page.hasMore` is false.\n\n## Stall needs positive evidence\n\nLeave the wait only on positive proof the agent stopped: `exited` liveness, the\nworker's own observation of process exit, or a transcript whose final agent turn\nsent no `worker_done`. Only then choose `worker-stop` or `worker-abandon`.\n\n`unverifiable` is always absence — `missing_status`, `stale_status`,\n`restored_unconfirmed`, or a remote worker with no connection — and a null\n`agentWait` or an unchanged `worker-read` tail is that same absence seen again.\nAbsence never authorizes stop, abandon, retry, or release: keep waiting, or\ninspect until you hold one of the positive signals above. A `nextAction` that\nnames an inspecting command is asking for evidence, not for cleanup.\n\n`worker-read --source auto` uses a proven provider transcript when available and\notherwise returns bounded terminal output with a typed `fallbackReason`.\nContinue with its top-level cursor, which is pinned to that source. If Orca\nreports `source_changed`, restart without the old cursor. A bounded initial\ntranscript tail can return an EOF cursor that follows only newly appended records;\nread `contentComplete`, `clipping`, and `warnings` before assuming omitted older\nrecords are pageable. Never guess a provider session ID, transcript path, or\nremote terminal handle.\n\n## Was the mutation applied?\n\nWhen a mutation's response was lost and named no Dispatch, do not replay blind.\nEvery orchestration mutation accepts `--retry-request `, which reuses one\noperation identity so Orca can replay, join, or recover it instead of starting a\nduplicate. Ask what happened first:\n\n```text\nORCA orchestration request-show --request --json\n```\n\n`completed` means the mutation already took effect; read its recorded receipt\ninstead of rerunning. `pending` means the original mutation is still running or\nOrca restarted before recording its outcome; replay the original command with\n`--retry-request `. `absent` means this runtime holds no receipt\nunder your caller identity — that is not proof nothing happened, so inspect the\naffected Task, Dispatch, and terminal before deciding whether to retry.\n\nWhen a worker's terminal accepted input but the submit is unconfirmed, use\n`terminal send --wait-submit `: it observes the accepted prompt for that\nlong and, on timeout, returns the input-accepted receipt without resending.\n\n## Refused starts\n\n`dispatch` and `worker-start` refuse the following preflight cases with a stable\n`error.code`; read it before choosing a recovery, and treat `error.data.nextSteps`\nas the exact recovery text. Older hosts may omit `data`, so treat every field as\noptional.\n\n| Code | Meaning | Recovery |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `task_not_found` | No Task with that id, or not in the bound Run (`data.taskId`, `data.runId`) | Check `task-list --json`; create the Task with `task-create` if it does not exist |\n| `task_not_startable` | Task cannot start now: not `ready`, or invalid `--retry-of` (`data.status`, `data.unmetDependencies`, `data.retryOf`) | Wait for running dependencies with `check --wait`; retry or unblock failed ones; inspect `dispatch-show` if already dispatched |\n| `inject_rejected` | `--inject` refused because no recognized agent runs in the target (`data.terminal`, `data.reason`) | Start a recognized agent there or pick another terminal; or dispatch without `--inject` and use `terminal send` |\n| `runtime_error` | Any other failure, including a target terminal that already owns an active Dispatch | Read the message, inspect state, and do not retry unchanged |\n\n## Retry, stop, and abandon\n\nRetry only a positively proven failed or stopped attempt. Name the failed Task\nwith `--task`, since `--spec` creates a new one. Placement is never silently\ninherited:\n\n```text\nORCA orchestration worker-start --task --retry-of --worktree --agent --json\n```\n\nAfter three consecutive failures for one Task, its dispatch context\ncircuit-breaks and the Task is failed. Failed `worker-start` attempts count\nacross `--retry-of`; a worker's own `failed` report does not. `--retry-of` a\ncircuit-broken Dispatch is refused with `task_not_startable`: report the failure\nto the user instead. Do not route around that boundary with a new Run, Task, or\nunrelated Dispatch.\n\nFor `outcome_unknown`, inspect first, then make an explicit choice:\n\n```text\nORCA orchestration worker-stop --dispatch --json\nORCA orchestration worker-abandon --dispatch --json\n```\n\n`worker-stop` closes only the exact proven supervised agent terminal. It never\ndeletes the worktree, setup terminal, configured tabs, or unrelated processes.\n`worker-abandon` fences orchestration while accepting that resources may remain\nlive; it performs no remote, process, or filesystem action. When it settles a\nworker, it retains an owned terminal by the same rule as `worker-retain`, so Orca\nstops owing its release; a release already committed (`releasing`,\n`release_unknown`) is left as it is. An already-settled worker is left untouched.\n\nTo cancel a Task, settle its worker with `worker-stop` or `worker-abandon`, then\nrecord the reason. A cancelled Task is `failed`, so its dependents stay blocked\nuntil a `--retry-of` replacement completes it:\n\n```text\nORCA orchestration task-update --id --status failed --result cancelled --json\n```\n\n## Retain and release\n\n```text\nORCA orchestration worker-retain --dispatch --json\nORCA orchestration worker-release --dispatch --json\n```\n\nRetain only when the user explicitly wants the settled terminal kept live.\nRelease works after succeeded and failed reports, archives readable output, and\ncloses only the exact terminal owned by that settled Dispatch. Replays may call\nrelease again safely. Reused, pre-existing, setup, coordinator, active,\nuser-taken-over, and unproven terminals are retained.\n\nA `worker-start` that failed before its agent was ready still owns the terminal\nit created. Its receipt names `worker-release`, and `worker-list` reports that\nrow as `reclaimable`; release it there rather than closing the terminal by hand.\nA `--retry-of` replacement for a `failed` worker releases the terminal that\nworker still owns the same way.\n\nNever release because of timeout, TUI idle, heartbeat, status, question,\nescalation, or stale/rejected completion. If the receipt says `release_pending`\nor `release_unknown`, follow its exact recovery action. Never substitute\n`terminal close`.\n\n`orchestration reset` is destructive recovery. Do not run it during active\ncoordination unless the user explicitly abandons that state.\n" // oxfmt-ignore -const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nEach wait defaults to 600000 ms (10 minutes). `--timeout-ms` accepts a positive\ninteger up to 1800000 ms (30 minutes); larger values are rejected before creating\nor resuming a question. For decisions that take longer, resume the same question\nafter each timeout.\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use either takeover flag. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Helper workers\n\nWhen the preamble has a SUB-DISPATCH section, start helpers on your own\nDispatch's Run. Do not `run-create` or `run-use` for them.\n\n```text\nORCA orchestration worker-start --from --spec \"\" --worktree current --agent --json\nORCA orchestration reply --from --id --body \"\"\n```\n\nTheir questions, escalations, and `worker_done` arrive in your own `check`, next\nto your coordinator's mail. Answer their questions with `reply`, and wait for\nevery helper's `worker_done` before sending yours. After you settle, a helper\nstill running reports to your coordinator's Run.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" +const ORCHESTRATION_WORKER_CONTRACT_REFERENCE_MARKDOWN = "# Worker contract\n\nThe injected preamble is authoritative. Copy its command rather than\nreconstructing flags. In particular, preserve the exact executable, worker\nhandle, Task ID, and Dispatch ID, and keep any other flag it carries (an older\nOrca host adds `--dispatch-capability`).\n\n## Heartbeat\n\nSend heartbeats only at the cadence required by the live preamble. Skip them\nwhile blocked inside `ask` or `check --wait`; those calls are liveness signals.\n\n```text\nORCA orchestration send --from --type heartbeat --subject \"alive\" --task-id --dispatch-id --phase \"\"\n```\n\nUse typed lifecycle flags, not a hand-written JSON payload. A heartbeat proves\nliveness, never completion.\n\n## Ask and resume\n\nUse Orca `ask` whenever the coordinator must answer. Never open a local question\nTUI the coordinator cannot answer.\n\n```text\nORCA orchestration ask --from --question \"\" --options \",\" --timeout-ms 600000\n\nORCA orchestration ask --from --resume --timeout-ms 600000\n```\n\nEach wait defaults to 600000 ms (10 minutes). `--timeout-ms` accepts a positive\ninteger up to 1800000 ms (30 minutes); larger values are rejected before creating\nor resuming a question. For decisions that take longer, resume the same question\nafter each timeout.\n\nA timeout or disconnect leaves the original question pending. Resume its\nmessage ID; do not create a duplicate question.\n\n## Reading coordinator follow-ups\n\nThe coordinator steers a running worker with `send --to dispatch:`. That\nenqueue is durable but does not interrupt you, so nothing arrives unless you\nlook:\n\n```text\nORCA orchestration check --terminal --json\n```\n\nRun it at each natural checkpoint — before starting a new file, after a test\nrun — and once more immediately before `worker_done`, so a redirect or a\ncancellation lands before the Task settles. `check` names its caller with\n`--terminal`, never `--from`. Stop checking after `worker_done`.\n\nDo not use `run-use` to check your Dispatch's mail or bind the Run you work for.\nYou may resume a separate Run when your coordinator directs a handoff, but you\ncannot use `--takeover-legacy`. Plain `check` returns your Dispatch's mail\nunless you are coordinating a Run; report to your coordinator with `send`.\n\nIf a plain `check` (no `--run`) returns `consumer_fenced`, this process no longer owns its Dispatch:\nthe Attempt was re-attached to another worker or settled without you. Stop, do\nnot send `worker_done`, and do not retry the check. An empty `check` never means\nyou were replaced; `consumer_fenced` is the only way you learn that.\nIf `send` or `ask` returns `consumer_fenced`, the command ran from another\nparty's terminal (a coordinator or another worker); run it from your own terminal.\n\n## Helper workers\n\nWhen the preamble has a SUB-DISPATCH section, start helpers on your own\nDispatch's Run. Do not `run-create` or `run-use` for them.\n\n```text\nORCA orchestration worker-start --from --spec \"\" --worktree current --agent --json\nORCA orchestration reply --from --id --body \"\"\n```\n\nTheir questions, escalations, and `worker_done` arrive in your own `check`, next\nto your coordinator's mail. Answer their questions with `reply`, and wait for\nevery helper's `worker_done` before sending yours. After you settle, a helper\nstill running reports to your coordinator's Run.\n\n## Escalation\n\nEscalate only before completion and only when the coordinator must intervene:\n\n```text\nORCA orchestration send --from --type escalation --subject \"Blocked: \" --body \"
\" --task-id --dispatch-id \n```\n\n## Completion\n\nSend exactly one terminal report. `--body` is three sentences: what changed,\nwhat was found, and what remains. Use `--outcome failed` when the requested work\nis not complete; never hide failure in prose or silently exit.\n\nAppend `--files-modified` or `--report-path` only when applicable, using actual\npaths. Do not send documentation placeholders as metadata.\n\n```text\nORCA orchestration send --from --type worker_done --subject \"\" --body \"\" --task-id --dispatch-id --outcome succeeded\n```\n\nAfter `worker_done`, end the dispatched turn and idle. Do not poll, close your\nown terminal, or begin unrelated work. A later direct user instruction is new\nuser-owned work and must not reuse settled lifecycle IDs; a supervised follow-up\narrives with a fresh preamble and Task block.\n" // oxfmt-ignore export const BUNDLED_SKILL_GUIDES = [ diff --git a/src/cli/handlers/orchestration/run-handlers.ts b/src/cli/handlers/orchestration/run-handlers.ts index 8da0f45f2399..d8abe3533c2c 100644 --- a/src/cli/handlers/orchestration/run-handlers.ts +++ b/src/cli/handlers/orchestration/run-handlers.ts @@ -28,7 +28,6 @@ export const ORCHESTRATION_RUN_HANDLERS: Record = { }>(client, flags, 'orchestration.runUse', { id: getRequiredStringFlag(flags, 'id'), from, - ...(flags.has('takeover') ? { takeover: true } : {}), ...(flags.has('takeover-legacy') ? { takeoverLegacy: true } : {}) }) printResult(result, json, (r) => `Using Run ${r.run.id}: ${r.run.objective}`) diff --git a/src/cli/specs/orchestration.ts b/src/cli/specs/orchestration.ts index 146fe3b0a901..5fd45ba85ddc 100644 --- a/src/cli/specs/orchestration.ts +++ b/src/cli/specs/orchestration.ts @@ -18,11 +18,11 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [ path: ['orchestration', 'run-use'], summary: 'Resume coordinating a Run', usage: - 'orca orchestration run-use --id [--from ] [--takeover] [--takeover-legacy] [--retry-request ] [--json]', - allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover', 'takeover-legacy', 'retry-request'], + 'orca orchestration run-use --id [--from ] [--takeover-legacy] [--retry-request ] [--json]', + allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover-legacy', 'retry-request'], identityFlagRoles: { from: 'caller' }, notes: [ - 'Refused while another coordinator of the Run is still open; --takeover replaces it, and its unacknowledged mail is replayed to you. A worker cannot bind its own or ancestor Run or use either takeover flag. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' + 'A worker cannot bind its own or ancestor Run or use --takeover-legacy. --takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.' ] }, { diff --git a/src/main/runtime/orchestration-cli-restart-handoff.test.ts b/src/main/runtime/orchestration-cli-restart-handoff.test.ts index 0196747a6ed1..16c7a613a849 100644 --- a/src/main/runtime/orchestration-cli-restart-handoff.test.ts +++ b/src/main/runtime/orchestration-cli-restart-handoff.test.ts @@ -30,7 +30,7 @@ afterEach(() => { vi.restoreAllMocks() }) -it('retains CLI work and mail across restart, refuses takeover, and completes a separate Run handoff', async () => { +it("retains CLI work and mail across restart, refuses a worker's own Run, and completes a separate Run handoff", async () => { const home = mkdtempSync(join(tmpdir(), 'orca-cli-restart-handoff-')) for (const name of [ 'HOME', @@ -71,15 +71,10 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a const predecessor = 'term_predecessor' const predecessorTab = '55555555-5555-4555-8555-555555555555' const predecessorLeaf = '66666666-6666-4666-8666-666666666666' - const predecessorPane = `${predecessorTab}:${predecessorLeaf}` - let predecessorPresence: 'live' | 'exited' = 'live' let db = new OrchestrationDb(join(home, 'orchestration.db')) function runtimeForDatabase() { - const { runtime } = fixture.createRuntime(db, { - checkHookAgentPresence: async (pane) => - pane === predecessorPane ? predecessorPresence : 'live' - }) + const { runtime } = fixture.createRuntime(db) fixture.registerSecondPane(runtime) registerBoundPty(runtime, 'pty-predecessor', fixture.WORKTREE_ID, null, { tabId: predecessorTab, @@ -187,10 +182,6 @@ it('retains CLI work and mail across restart, refuses takeover, and completes a expect(await cli(coord, ['check', '--ack', retainedBatch.deliveryId])).toMatchObject({ result: { acknowledged: retainedBatch.deliveryId, count: 0 } }) - expect(await cli(worker, ['run-use', '--id', separate], 1)).toMatchObject({ - error: { code: 'run_coordinator_live', data: { effectsApplied: false } } - }) - predecessorPresence = 'exited' expect(await cli(worker, ['run-use', '--id', separate])).toMatchObject({ result: { run: { id: separate } } }) diff --git a/src/main/runtime/orchestration/db/runs/run-binding.ts b/src/main/runtime/orchestration/db/runs/run-binding.ts index 499e021956ce..450323df9bed 100644 --- a/src/main/runtime/orchestration/db/runs/run-binding.ts +++ b/src/main/runtime/orchestration/db/runs/run-binding.ts @@ -8,8 +8,7 @@ import { findCallerWorkerDispatches, workerRunBindingRefusal } from './worker-ru import { mailboxAddressOf, runBoundToCoordinator, - runCoordinatorKey, - type OrchestrationCoordinatorKey + runCoordinatorKey } from '../../orchestration-caller-identity' export function bindRun( @@ -21,13 +20,6 @@ export function bindRun( /** The coordinator's bare Orca session id when it is a structured session; see orca-session-address. */ coordinatorOrcaSessionId?: OrcaSessionId | null takeoverLegacy?: boolean - /** The user asked to replace a coordinator that is still open. */ - takeover?: boolean - /** - * What the RPC layer saw just before this call: the Run's coordinator then, and how a user - * recognizes it when it was proven open. A binding that moved since is refused, never bound blindly. - */ - probed?: { coordinator: OrchestrationCoordinatorKey; liveLabel: string | null } legacyCoordinatorAuthority?: { runId: string principalId: string | null @@ -50,7 +42,7 @@ export function bindRun( return undefined } const sameBinding = runBoundToCoordinator(run, coordinator) - const explicitTakeover = params.takeover === true || params.takeoverLegacy === true + const explicitTakeover = params.takeoverLegacy === true // Why: before the legacy gate, so a worker never sees its --takeover-legacy recovery command. const workerRefusal = sameBinding ? null @@ -148,23 +140,6 @@ export function bindRun( } ) } - const probed = params.probed - if (probed && !sameBinding && !provenLegacyBinding && !explicitTakeover) { - if (!sameCoordinatorKey(runCoordinatorKey(run), probed.coordinator)) { - throw new OrchestrationError( - 'run_binding_changed', - `Run ${params.runId}'s coordinator changed while this command ran. Nothing changed; run the same command again.`, - { effectsApplied: false, retryable: true } - ) - } - if (probed.liveLabel !== null) { - throw runCoordinatorLiveError( - params.runId, - probed.liveLabel, - findCallerWorkerDispatches(this, coordinator).length > 0 - ) - } - } this.unbindOtherRunsForCoordinator(coordinator, params.runId) // The mailbox address of the coordinator being replaced and of the one binding now. for (const address of new Set([ @@ -230,33 +205,6 @@ export function bindRun( return this.getRun(params.runId) } -function sameCoordinatorKey( - a: OrchestrationCoordinatorKey, - b: OrchestrationCoordinatorKey -): boolean { - return ( - a.terminalHandle === b.terminalHandle && - a.paneKey === b.paneKey && - a.orcaSessionId === b.orcaSessionId - ) -} - -// No recoveryCommand on purpose: agents run those mechanically, and the flag must carry user intent. -function runCoordinatorLiveError( - runId: string, - label: string, - worker: boolean -): OrchestrationError { - const takeoverAdvice = worker - ? '' - : ` Take it over only if the user asks: orca orchestration run-use --id ${runId} --takeover replaces it, and its unacknowledged mail is replayed to you.` - return new OrchestrationError( - 'run_coordinator_live', - `Run ${runId} is coordinated by ${label}, which is still open. Nothing changed. Closing it clears this.${takeoverAdvice}`, - { effectsApplied: false, runId, coordinator: label } - ) -} - export type RunBindingMethods = { bindRun: typeof bindRun } diff --git a/src/main/runtime/rpc/errors.ts b/src/main/runtime/rpc/errors.ts index ea7134ff3687..1d3f8ee2abf0 100644 --- a/src/main/runtime/rpc/errors.ts +++ b/src/main/runtime/rpc/errors.ts @@ -150,8 +150,6 @@ const STRUCTURED_RUNTIME_PASSTHROUGH_CODES: ReadonlySet = new Set([ 'terminal_is_coordinator', 'worker_cannot_coordinate_own_run', 'worker_cannot_take_over_run', - 'run_coordinator_live', - 'run_binding_changed', 'request_mismatch', 'mutation_ledger_full', 'legacy_read_only', diff --git a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts index 5e0327c1f8a8..d8ad40d65a01 100644 --- a/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/messaging/run-bound-mailbox-interleavings.test.ts @@ -108,8 +108,7 @@ describe('Run binding during Dispatch checks', () => { runId: run.id, messages: [{ subject: 'after bind' }] }) - // runUse awaits the target coordinator's liveness first, so its check has parked by then. - if (phase === 'recovering' && method === 'runCreate') { + if (phase === 'recovering') { expect(wait).not.toHaveBeenCalled() } } diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts deleted file mode 100644 index b1a11d662fdb..000000000000 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-coordinator-liveness.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { OrchestrationDb } from '../../../../orchestration/db' -import { OrchestrationError } from '../../../../orchestration/orchestration-error' -import type { OrcaRuntimeService } from '../../../../orca-runtime' -import type { RunRow } from '../../../../orchestration/types' -import { - mailboxAddressOf, - runBoundToCoordinator, - runCoordinatorKey, - type OrchestrationCoordinatorKey -} from '../../../../orchestration/orchestration-caller-identity' -import { readAgentSessionRecordStore } from '../../../../orchestration/structured-session-lineage' -import { - lookupOrcaAgentSession, - structuredSessionMailReach -} from '../../../../orchestration/structured-session-mail-address' -import { splitWorktreeId } from '../../../../../../shared/worktree/id' - -// Why: a binding that keeps moving under a probe is contention, not something to win by retrying forever. -const PROBE_ATTEMPTS = 3 - -type BindParams = Parameters[0] - -/** - * Probe the Run's coordinator, then bind with what was probed so bindRun can refuse a live one. - * When the binding moved between probe and transaction, probe again; after a few tries the refusal - * stands and says to retry, so a bind never displaces a coordinator nobody probed. - */ -export async function bindAfterProbe( - runtime: OrcaRuntimeService, - db: OrchestrationDb, - params: () => Omit -): Promise { - for (let attempt = 1; ; attempt++) { - const bind = params() - const target = db.getRunRaw(bind.runId) - const caller = { - terminalHandle: bind.coordinatorHandle, - paneKey: bind.coordinatorPaneKey, - orcaSessionId: bind.coordinatorOrcaSessionId ?? null - } - const coordinator = target ? runCoordinatorKey(target) : null - const liveLabel = - coordinator && target && !runBoundToCoordinator(target, caller) - ? await liveCoordinatorLabel(runtime, db, coordinator) - : null - try { - return db.bindRun({ ...bind, ...(coordinator ? { probed: { coordinator, liveLabel } } : {}) }) - } catch (error) { - if ( - attempt >= PROBE_ATTEMPTS || - !(error instanceof OrchestrationError) || - error.code !== 'run_binding_changed' - ) { - throw error - } - } - } -} - -/** - * How a user recognizes the coordinator, when there is positive evidence it is still open; else null. - * Only proof counts: a closed chat, an exited or unhooked terminal agent, another host, or anything - * that cannot be checked reads as not open, so a restart handoff never needs a flag. - */ -async function liveCoordinatorLabel( - runtime: OrcaRuntimeService, - db: OrchestrationDb, - key: OrchestrationCoordinatorKey -): Promise { - const address = mailboxAddressOf(key) - if (key.orcaSessionId !== null) { - // Why: the record store opens lazily, as for a send naming a session. - await runtime.ensureStructuredAgentSessionHost().catch(() => undefined) - const store = readAgentSessionRecordStore() - const found = store ? lookupOrcaAgentSession(store, key.orcaSessionId) : null - if (!store || found?.kind !== 'found') { - return null - } - const reach = structuredSessionMailReach(store, found.record, db) - if (reach.kind !== 'reachable') { - return null - } - return `the chat${inWorktree(reach.session.location.workspaceId)} (${address})` - } - if (key.paneKey === null || (await runtime.coordinatorAgentPresence(key.paneKey)) !== 'live') { - return null - } - const worktreeId = key.terminalHandle - ? runtime.getTerminalWorktreeIdForPaneKey(key.paneKey) - : null - return `the terminal${inWorktree(worktreeId ?? undefined)} (${address})` -} - -function inWorktree(worktreeId: string | undefined): string { - const path = worktreeId ? splitWorktreeId(worktreeId)?.worktreePath : undefined - const name = path?.split(/[\\/]/).findLast(Boolean) - return name ? ` in ${name}` : '' -} diff --git a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts b/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts deleted file mode 100644 index b915281449a8..000000000000 --- a/src/main/runtime/rpc/methods/orchestration/runs/run-use-takeover.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createRootDispatch } from '../../../../orchestration/db/root-dispatch-test-fixture' -import { registerBoundPty } from '../../../../bind-first-pane.test-fixture' -import { - createRuntime, - PANE_KEY, - TAB_ID, - LEAF_ID, - TERMINAL_HANDLE, - WORKTREE_ID -} from '../../../../orchestration-mailbox-notification-test-harness' -import { RpcDispatcher } from '../../../dispatcher' -import { ORCHESTRATION_METHODS } from '../../orchestration' -import { - createSessionCallerHarness, - idOf, - orchestrationRequest, - resultOf, - SESSION_X, - SESSION_Y, - WORKER_HANDLE, - type SessionCallerHarness -} from '../../../orchestration-session-caller-test-fixture' - -const hostRef = vi.hoisted((): { current: unknown } => ({ current: null })) -vi.mock('../../../../../native-chat/agent-session-wire/structured-agent-session-registry', () => ({ - getStructuredAgentSessionHost: () => hostRef.current -})) - -const COORD_PANE = 'tab_coord:12121212-1212-4212-8212-121212121212' - -describe('run-use against a Run whose coordinator is still running', () => { - let h: SessionCallerHarness - - beforeEach(() => { - h = createSessionCallerHarness(hostRef) - }) - afterEach(() => { - h.close() - vi.restoreAllMocks() - }) - - const as = (sessionId: string | undefined, method: string, params: Record) => - h.dispatch(orchestrationRequest(method, params, { sessionId })) - async function chatRun(): Promise { - return idOf(resultOf(await as(SESSION_X, 'orchestration.runCreate', { objective: 'o' })).run) - } - - it('refuses a second chat until it passes --takeover, then fences the first', async () => { - const runId = await chatRun() - const before = h.db.getRunRaw(runId) - - const refused = await as(SESSION_Y, 'orchestration.runUse', { id: runId }) - expect(refused).toMatchObject({ - ok: false, - error: { code: 'run_coordinator_live', data: { effectsApplied: false } } - }) - expect(JSON.stringify(refused)).toMatch( - /the chat in tree-x \(orca_session_id:[^)]+\), which is still open/ - ) - expect(h.db.getRunRaw(runId)).toEqual(before) - - resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true })) - expect(await as(SESSION_X, 'orchestration.check', { run: runId })).toMatchObject({ - ok: false, - error: { code: 'consumer_fenced' } - }) - }) - - it.each([4, 40])( - 'labels a live terminal without inspecting output among %i workspaces', - async (count) => { - const { runtime } = createRuntime(h.db, { checkHookAgentPresence: async () => 'live' }) - for (let index = 0; index < count; index++) { - registerBoundPty(runtime, `pty_${index}`, `repo::/work/other_${index}`, null, { - tabId: `tab_${index}`, - leafId: `leaf_${index}` - }) - } - const getPane = runtime.getTerminalPaneKey.bind(runtime) - vi.spyOn(runtime, 'getTerminalPaneKey').mockImplementation((handle) => - handle === WORKER_HANDLE ? 'tab_caller:caller' : getPane(handle) - ) - const metadata = vi.spyOn(runtime, 'getTerminalWorktreeIdForPaneKey') - const show = vi.spyOn(runtime, 'showTerminal') - const run = h.db.createRun({ - objective: 'live', - coordinatorHandle: TERMINAL_HANDLE, - coordinatorPaneKey: PANE_KEY - }) - const before = h.db.getRunRaw(run.id) - const dispatcher = new RpcDispatcher({ runtime, methods: ORCHESTRATION_METHODS }) - expect(runtime.getTerminalWorktreeIdForPaneKey(`${TAB_ID}:${LEAF_ID}`)).toBe(WORKTREE_ID) - metadata.mockClear() - const refused = await dispatcher.dispatch( - orchestrationRequest('orchestration.runUse', { id: run.id, from: WORKER_HANDLE }) - ) - expect(refused).toMatchObject({ - ok: false, - error: { - code: 'run_coordinator_live', - message: expect.stringContaining( - `the terminal in mailbox (${TERMINAL_HANDLE}), which is still open.` - ), - data: { effectsApplied: false } - } - }) - expect(metadata).toHaveBeenCalledTimes(1) - expect(show).not.toHaveBeenCalled() - expect(h.db.getRunRaw(run.id)).toEqual(before) - } - ) - - it.each([ - ['its chat was closed', 'closed'], - ['its terminal agent exited', 'exited'], - ['no hook identifies its terminal agent', null] - ] as const)('hands the Run over without a flag when %s', async (_label, state) => { - let runId: string - if (state === 'closed') { - runId = await chatRun() - hostRef.current = { - deps: { - store: { - getRecord: (id: string) => h.records.get(id) ?? null, - listRecords: () => [...h.records.values()], - getVisibleSessionTabIndex: () => ({ present: true, sessionIds: [SESSION_Y] }) - } - } - } - } else { - runId = h.db.createRun({ - objective: 'o', - coordinatorHandle: 'term_coord', - coordinatorPaneKey: COORD_PANE - }).id - vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockResolvedValue(state) - } - - resultOf(await as(SESSION_Y, 'orchestration.runUse', { id: runId })) - expect(h.db.getRunRaw(runId)?.coordinator_orca_session_id).toBe(SESSION_Y) - }) - - it('lets a worker resume a separate Run with plain run-use, never by takeover', async () => { - const stranger = h.db.createRun({ - objective: 'predecessor sub-Run', - coordinatorHandle: 'term_previous', - coordinatorPaneKey: COORD_PANE - }) - const presence = vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockResolvedValue('live') - const home = h.db.createRun({ - objective: 'home', - coordinatorHandle: null, - coordinatorPaneKey: null - }) - createRootDispatch(h.db, h.db.createTask({ spec: 'w', runId: home.id }).id, WORKER_HANDLE) - const runUse = (id: string, takeover: boolean) => - as(undefined, 'orchestration.runUse', { id, from: WORKER_HANDLE, takeover }) - const sub = idOf( - resultOf( - await as(undefined, 'orchestration.runCreate', { objective: 's', from: WORKER_HANDLE }) - ).run - ) - resultOf( - await as(undefined, 'orchestration.runCreate', { objective: 's2', from: WORKER_HANDLE }) - ) - - expect(await runUse(home.id, false)).toMatchObject({ - ok: false, - error: { code: 'worker_cannot_coordinate_own_run' } - }) - expect(await runUse(stranger.id, true)).toMatchObject({ - ok: false, - error: { code: 'worker_cannot_take_over_run' } - }) - const foreignRead = await as(undefined, 'orchestration.check', { - run: stranger.id, - terminal: WORKER_HANDLE, - peek: true - }) - expect(foreignRead).toMatchObject({ ok: false, error: { code: 'consumer_fenced' } }) - expect(JSON.stringify(foreignRead)).not.toContain('run-use') - const liveRefusal = await runUse(stranger.id, false) - expect(liveRefusal).toMatchObject({ ok: false, error: { code: 'run_coordinator_live' } }) - expect(JSON.stringify(liveRefusal)).not.toContain('run-use --id') - presence.mockResolvedValue('exited') - resultOf(await runUse(stranger.id, false)) - expect(await runUse(sub, true)).toMatchObject({ - error: { code: 'worker_cannot_take_over_run' } - }) - resultOf(await runUse(sub, false)) - resultOf(await runUse(sub, true)) - }) - - it('re-probes when the binding moves under it, then refuses rather than bind blindly', async () => { - const runId = h.db.createRun({ - objective: 'o', - coordinatorHandle: 'term_coord', - coordinatorPaneKey: COORD_PANE - }).id - let racers = 0 - const probe = vi.spyOn(h.runtime, 'coordinatorAgentPresence').mockImplementation(async () => { - racers += 1 - h.db.bindRun({ - runId, - coordinatorHandle: `term_racer${racers}`, - coordinatorPaneKey: `tab_r:0000000${racers}-0000-4000-8000-000000000000` - }) - return 'exited' - }) - - expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ - ok: false, - error: { code: 'run_binding_changed', data: { effectsApplied: false, retryable: true } } - }) - expect(probe).toHaveBeenCalledTimes(3) - expect(h.db.getRunRaw(runId)?.coordinator_handle).toBe('term_racer3') - }) -}) diff --git a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts index 4f9564f61b74..6af5ac6a2edb 100644 --- a/src/main/runtime/rpc/methods/orchestration/runs/runs.ts +++ b/src/main/runtime/rpc/methods/orchestration/runs/runs.ts @@ -4,7 +4,6 @@ import { assertCallerHandleMatchesEvidence, resolveOrchestrationCaller } from '. import { exposeRun } from './run-receipt' import type { OrcaRuntimeService } from '../../../../orca-runtime' import type { OrchestrationCallerIdentity } from '../../../../orchestration/orchestration-caller-identity' -import { bindAfterProbe } from './run-coordinator-liveness' import { currentDispatchAssigneeRun } from '../messaging/recipient-routing' import { RunCreateParams, @@ -58,7 +57,7 @@ export const ORCHESTRATION_RUN_METHODS = [ name: 'orchestration.runUse', permission: 'workspace', params: RunUseParams, - handler: async ( + handler: ( params, { runtime, @@ -90,15 +89,14 @@ export const ORCHESTRATION_RUN_METHODS = [ assertCallerHandleMatchesEvidence(runtime, params.from, orchestrationCompatibilityEvidence) const db = runtime.getOrchestrationDb() const priorRun = db.getCurrentRunForCoordinator(caller) - const run = await bindAfterProbe(runtime, db, () => ({ + const run = db.bindRun({ runId: params.id, coordinatorHandle: caller.terminalHandle, coordinatorPaneKey: caller.paneKey, coordinatorOrcaSessionId: caller.orcaSessionId, takeoverLegacy: params.takeoverLegacy, - takeover: params.takeover, legacyCoordinatorAuthority - })) + }) if (!run) { throw new OrchestrationError( 'run_not_found', diff --git a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts index cbd28328a783..1f5b77205c3f 100644 --- a/src/main/runtime/rpc/orchestration-session-coordinator.test.ts +++ b/src/main/runtime/rpc/orchestration-session-coordinator.test.ts @@ -239,9 +239,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( run: runId }) - expect( - await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true }) - ).toMatchObject({ + expect(await as(SESSION_Y, 'orchestration.runUse', { id: runId })).toMatchObject({ run: { id: runId } }) @@ -278,7 +276,7 @@ describe('a structured chat coordinates through the same verbs as a terminal', ( const deliver = vi.spyOn(h.db, 'getOrCreateRunDelivery') const acknowledge = vi.spyOn(h.db, 'acknowledgeRunDelivery') - await as(SESSION_Y, 'orchestration.runUse', { id: runId, takeover: true }) + await as(SESSION_Y, 'orchestration.runUse', { id: runId }) const fenced = await waiting expect(fenced).toMatchObject({ ok: false, error: { code: 'consumer_fenced' } }) @@ -536,11 +534,7 @@ describe('a structured worker that names itself by session id', () => { // A chat takes the Run over; the worker's addresses stay remembered as a former coordinator's. resultOf( await h.dispatch( - orchestrationRequest( - 'orchestration.runUse', - { id: runId, takeover: true }, - { sessionId: SESSION_X } - ) + orchestrationRequest('orchestration.runUse', { id: runId }, { sessionId: SESSION_X }) ) ) expect(h.db.getRunMailboxOwnerIdsForHandle(ADDRESS_Y)).toEqual([runId]) diff --git a/src/main/runtime/terminal/input/terminal-input-dispatch.ts b/src/main/runtime/terminal/input/terminal-input-dispatch.ts index 3b3e40d4baea..50a116edf768 100644 --- a/src/main/runtime/terminal/input/terminal-input-dispatch.ts +++ b/src/main/runtime/terminal/input/terminal-input-dispatch.ts @@ -75,11 +75,6 @@ export abstract class OrcaRuntimeWithControllerKnowsPtyIsLive extends OrcaRuntim ) } - /** Hook-proven agent presence in a coordinator's pane; null when no hook identified an owner. */ - coordinatorAgentPresence(paneKey: string): Promise<'live' | 'unverifiable' | 'exited' | null> { - return this.checkHookAgentPresenceFn?.(paneKey) ?? Promise.resolve(null) - } - protected controllerKnowsPtyIsLive(ptyId: string): boolean { try { return this.ptyController?.hasPty?.(ptyId) === true diff --git a/src/shared/cli-argument-boundary.ts b/src/shared/cli-argument-boundary.ts index 4b6fb17d6c61..1f8afb80f702 100644 --- a/src/shared/cli-argument-boundary.ts +++ b/src/shared/cli-argument-boundary.ts @@ -50,7 +50,6 @@ export const CLI_BOOLEAN_FLAGS = new Set([ 'staged', 'supervise', 'tab', - 'takeover', 'takeover-legacy', 'tasks', 'text-stdin', diff --git a/src/shared/rpc-contract/orchestration-runs-params.ts b/src/shared/rpc-contract/orchestration-runs-params.ts index e3737fe6cad1..30aad1e9eae6 100644 --- a/src/shared/rpc-contract/orchestration-runs-params.ts +++ b/src/shared/rpc-contract/orchestration-runs-params.ts @@ -10,8 +10,7 @@ export const RunCreateParams = z.object({ export const RunUseParams = z.object({ id: requiredString('Missing --id'), from: requiredString('Missing coordinator terminal'), - takeoverLegacy: OptionalBoolean, - takeover: OptionalBoolean + takeoverLegacy: OptionalBoolean }) export const RunCurrentParams = z.object({ from: requiredString('Missing coordinator terminal') }) From b9662bccc755da137d50a6f8d51f97f18694fe0a Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 25 Oct 2026 17:03:25 -0700 Subject: [PATCH 29/30] test(orchestration): cover delegated worker legacy takeover refusal --- .../orchestration-adopted-run-binding.test.ts | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts b/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts index 0fecbad18ede..cd88c1743927 100644 --- a/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts +++ b/src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts @@ -126,19 +126,25 @@ describe('adopted Run binding without --takeover-legacy', () => { }) }) - it('does not offer a legacy takeover command to a worker from another Run', () => { - const { db, adoptedRunId } = createAdoptedFixture({ settleWork: false }) + it('withholds legacy takeover advice and refuses explicit worker takeovers without effects', () => { + const { db, adoptedRunId, recoveryMessageId } = createAdoptedFixture({ settleWork: false }) const home = db.createRun({ objective: 'worker home', coordinatorHandle: CURRENT_COORDINATOR_HANDLE, coordinatorPaneKey: CURRENT_COORDINATOR_PANE }) - createRootDispatch( + const worker = createRootDispatch( db, db.createTask({ runId: home.id, spec: 'worker' }).id, 'term_foreign_worker', 'tab_foreign:22222222-2222-4222-8222-222222222222' ) + const before = { + adoptedRun: db.getRun(adoptedRunId), + homeRun: db.getRun(home.id), + dispatch: db.getDispatchContextById(worker.id), + mail: db.getMessageById(recoveryMessageId) + } try { db.bindRun({ runId: adoptedRunId, @@ -150,7 +156,30 @@ describe('adopted Run binding without --takeover-legacy', () => { expect(error).toMatchObject({ code: 'consumer_fenced', data: { effectsApplied: false } }) expect(JSON.stringify(error)).not.toContain('run-use') } - expect(db.getRun(adoptedRunId)?.coordinator_handle).toBeNull() + for (const [runId, code] of [ + [adoptedRunId, 'worker_cannot_take_over_run'], + [home.id, 'worker_cannot_coordinate_own_run'] + ] as const) { + expect(() => + db.bindRun({ + runId, + coordinatorHandle: 'term_foreign_worker', + coordinatorPaneKey: 'tab_foreign:22222222-2222-4222-8222-222222222222', + takeoverLegacy: true + }) + ).toThrowError( + expect.objectContaining({ + code, + data: { effectsApplied: false, dispatchId: worker.id } + }) + ) + expect({ + adoptedRun: db.getRun(adoptedRunId), + homeRun: db.getRun(home.id), + dispatch: db.getDispatchContextById(worker.id), + mail: db.getMessageById(recoveryMessageId) + }).toEqual(before) + } }) it('claims the settled adopted Run and inherits its recovered task graph', () => { From fc7b41582c1a196ee079d547bb0c76b6202e6d55 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 25 Oct 2026 17:19:45 -0700 Subject: [PATCH 30/30] test(orchestration): explain source CLI integration timeout --- src/main/runtime/orchestration-cli-restart-handoff.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/runtime/orchestration-cli-restart-handoff.test.ts b/src/main/runtime/orchestration-cli-restart-handoff.test.ts index 16c7a613a849..2a5244dc8c22 100644 --- a/src/main/runtime/orchestration-cli-restart-handoff.test.ts +++ b/src/main/runtime/orchestration-cli-restart-handoff.test.ts @@ -30,6 +30,7 @@ afterEach(() => { vi.restoreAllMocks() }) +// Sequential source-CLI subprocesses and a disk-backed runtime restart exceed the default test budget. it("retains CLI work and mail across restart, refuses a worker's own Run, and completes a separate Run handoff", async () => { const home = mkdtempSync(join(tmpdir(), 'orca-cli-restart-handoff-')) for (const name of [