docs(orchestration): clarify coordinator authority for agents

This commit is contained in:
Brennan Benson
2026-08-27 16:00:58 -07:00
parent 876a0581c2
commit efad7a891c
7 changed files with 111 additions and 6 deletions
@@ -51,6 +51,31 @@ describe('orchestration skill guidance', () => {
)
})
it('defines coordinator authority and ordinary takeover for agents', () => {
const skill = readSkill()
const authority = getSection(skill, 'Run Coordinator Authority')
expect(authority).toContain('sole task-graph writer and consuming mailbox reader')
expect(authority).toContain('per-Run authority')
expect(authority).toContain('not an operating-system or global Orca privilege')
expect(authority).toContain('`run-use` is an authority claim, not a read-only selection')
expect(authority).toContain('`live`')
expect(authority).toContain('`unverifiable`')
expect(authority).toContain('`exited`')
expect(authority).toContain('Loss of contact is never evidence of exit')
expect(authority).toContain('No live-to-live transfer command exists')
expect(authority).toContain('`--takeover-legacy` is not a force override for ordinary Runs')
expect(authority).toContain('task-create')
expect(authority).toContain('task-update')
expect(authority).toContain('worker-start')
expect(authority).toContain('gate-create')
expect(authority).toContain('reply')
expect(authority).toContain('check')
expect(authority).toContain('run-show --id <run_id> --json')
expect(authority).toContain('binding.currentConsumer')
expect(authority).toContain('stop or exit the owning coordinator process')
})
it('teaches attested adoption without reviving the retired scheduler', () => {
const skill = readSkill()
const migration = getSection(skill, 'Contract Migration')
+24
View File
@@ -99,6 +99,30 @@ Takeover fences only the old coordinator, binds the current one, and moves pendi
Do not launch a replacement editor merely because the desktop app or runtime was updated. If adoption cannot prove continuing authority, keep the original worker as the only editor until it reaches a stable handoff point, then use a new current Dispatch in a conflict-free placement for any remaining work.
## Run Coordinator Authority
Orca orchestration is agent-operated. A Run therefore has one current coordinator agent, not a human operator. That coordinator is the sole task-graph writer and consuming mailbox reader for coordinator-side operations; workers retain only their exact Dispatch capabilities for heartbeat, questions, escalation, and `worker_done`. This is per-Run authority, not an operating-system or global Orca privilege.
The single-writer rule prevents two agents from changing Task, Dispatch, worker, or gate state concurrently and prevents two consumers from acknowledging different views of the same FIFO Delivery. The current coordinator may create and update Tasks (`task-create`, `task-update`), start or dispatch workers (`worker-start`, `dispatch`), create and resolve gates (`gate-create`, `gate-resolve`), answer worker questions (`reply`), and consume or acknowledge Run mail (`check`). Explicit `run-show`, `task-list --run`, inbox, and peek calls remain read-only for non-owners and headless callers.
`run-use` is an authority claim, not a read-only selection. Read `error.data.coordinatorStatus` and `error.data.nextSteps` from `--json` failures; never infer authority from a terminal handle alone.
- Same coordinator process, reminted handle: authority and any outstanding Delivery are preserved without advancing the consumer generation.
- Different process and `live` incumbent: `consumer_fenced`; continue from the owning coordinator terminal. To transfer intentionally, stop or exit the owning coordinator process before retrying from its replacement.
- Different process and `unverifiable` incumbent: `consumer_fenced`; restore connectivity to the owning host. Loss of contact is never evidence of exit, including SSH, relay, Windows, WSL, and federated runtimes.
- Different process and `exited` incumbent: the replacement may run `orca orchestration run-use --id <run_id> --json`. Orca advances the consumer generation, fences the old outstanding Delivery, preserves pending Run mail and worker assignments, and grants the replacement coordinator authority.
Inspect without claiming authority:
```bash
orca orchestration run-show --id <run_id> --json
orca orchestration task-list --run <run_id> --json
```
`binding.currentConsumer` is `true` only for the current coordinator. `false` permits inspection, not coordinator mutations or consuming `check` calls.
No live-to-live transfer command exists. A seamless live handoff would require a separate owner-authorized protocol; do not simulate one by retrying, changing `--from`, or replacing a terminal handle. `--takeover-legacy` is not a force override for ordinary Runs: it is limited to the automatically adopted legacy Run described above.
## Ownership
New orchestration messages and tasks belong to one explicitly bound Run. A Run is only a durable namespace and coordinator inbox; it never schedules or places workers. Lifecycle authority comes from the active Dispatch, and terminal handles remain routing metadata rather than durable identity. Send `worker_done` and `heartbeat` from the worker's own terminal; Orca routes them to that Dispatch's Run.
File diff suppressed because one or more lines are too long
+20
View File
@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest'
import { ORCHESTRATION_COMMAND_SPECS } from './orchestration'
describe('orchestration command specs', () => {
it('describes run-use as a fenced authority claim for agents', () => {
const runUse = ORCHESTRATION_COMMAND_SPECS.find(
(spec) => spec.path.join(' ') === 'orchestration run-use'
)
const guidance = [runUse?.summary, ...(runUse?.notes ?? [])].join('\n')
expect(guidance).toContain('coordinator authority')
expect(guidance).toContain('owning host proves the prior coordinator exited')
expect(guidance).toContain('live')
expect(guidance).toContain('unverifiable')
expect(guidance).toContain('consumer_fenced')
expect(guidance).toContain('Do not retry blindly')
expect(guidance).toContain('--takeover-legacy')
expect(guidance).toContain('not a force override for ordinary Runs')
})
})
+4 -2
View File
@@ -16,12 +16,14 @@ export const ORCHESTRATION_COMMAND_SPECS: CommandSpec[] = [
},
{
path: ['orchestration', 'run-use'],
summary: 'Bind this coordinator terminal to an existing Run',
summary: 'Claim coordinator authority for an existing Run',
usage:
'orca orchestration run-use --id <run_id> [--from <handle>] [--takeover-legacy] [--retry-request <id>] [--json]',
allowedFlags: [...GLOBAL_FLAGS, 'id', 'from', 'takeover-legacy', 'retry-request'],
notes: [
'--takeover-legacy must run in the live coordinator agent terminal it binds; it preserves existing worker assignments.'
'Rebinding the same live coordinator preserves authority and Delivery. A different coordinator succeeds only when the owning host proves the prior coordinator exited.',
'A live or unverifiable owner returns consumer_fenced with structured recovery steps. Do not retry blindly or treat loss of contact as exit.',
'--takeover-legacy is only for the automatically adopted legacy Run; it is not a force override for ordinary Runs.'
]
},
{
@@ -153,12 +153,28 @@ export function bindRun(
(!observationMatches || observation.status !== 'exited')
) {
const coordinatorStatus = observationMatches ? observation.status : 'unverifiable'
const inspectCommand = `orca orchestration run-show --id ${params.runId} --json`
const retryCommand = `orca orchestration run-use --id ${params.runId} --json`
const nextSteps =
coordinatorStatus === 'live'
? [
'Continue from the owning coordinator terminal; this caller has read-only inspection authority.',
`To intentionally transfer authority, stop or exit that coordinator process, then run ${retryCommand} from the replacement.`,
`Inspect current authority with ${inspectCommand}; binding.currentConsumer is true only for the owner.`,
'Do not retry while coordinatorStatus is live. No force-steal exists for an ordinary Run.'
]
: [
'Restore connectivity to the owning host. Loss of contact is not evidence of exit.',
`Inspect current authority with ${inspectCommand}; binding.currentConsumer is true only for the owner.`,
`Run ${retryCommand} only after the owning host proves the incumbent exited.`,
'If the incumbent is live, continue from its coordinator terminal; no force-steal exists for an ordinary Run.'
]
throw new OrchestrationError(
'consumer_fenced',
coordinatorStatus === 'live'
? 'This Run is owned by another live coordinator. No effects were applied.'
: 'This Run coordinator could not be proven exited. Retry from the owning coordinator terminal after connectivity is restored. No effects were applied.',
{ effectsApplied: false, coordinatorStatus }
{ effectsApplied: false, coordinatorStatus, nextSteps }
)
}
this.unbindOtherRunsForPane(params.coordinatorPaneKey, params.runId)
@@ -128,7 +128,16 @@ describe('orchestration RPC methods', () => {
})
).rejects.toMatchObject({
code: 'consumer_fenced',
data: { effectsApplied: false, coordinatorStatus: 'live' }
data: {
effectsApplied: false,
coordinatorStatus: 'live',
nextSteps: [
expect.stringContaining('owning coordinator terminal'),
expect.stringContaining('stop or exit that coordinator process'),
expect.stringContaining(`run-show --id ${created.run.id} --json`),
expect.stringContaining('No force-steal exists for an ordinary Run')
]
}
})
const inspected = (await call('orchestration.taskList', {
run: created.run.id,
@@ -186,7 +195,16 @@ describe('orchestration RPC methods', () => {
call('orchestration.runUse', { id: run.id, from: 'term_new' })
).rejects.toMatchObject({
code: 'consumer_fenced',
data: { effectsApplied: false, coordinatorStatus: 'unverifiable' }
data: {
effectsApplied: false,
coordinatorStatus: 'unverifiable',
nextSteps: expect.arrayContaining([
expect.stringContaining('Restore connectivity to the owning host'),
expect.stringContaining('Loss of contact is not evidence of exit'),
expect.stringContaining(`run-show --id ${run.id} --json`),
expect.stringContaining('only after the owning host proves the incumbent exited')
])
}
})
})