fix(orchestration): enforce nested worker depth instead of an accidental fence (#16668)

* fix(orchestration): enforce nested worker depth instead of an accidental fence

Orca documented that "dispatched workers cannot spawn their own sub-workers
(worker-start is coordinator-fenced)". No such check existed. What existed was a
single Run-binding check in the workerStart RPC: a worker's terminal is not bound
to a Run, so worker-start happened to fail. The rule was emergent, asserted by no
test, and written in no doc — and it leaked. A worker could run-create its own
Run, task-create, and worker-start: now bound, the check passed.

Replace it with a real, configurable depth cap.

Depth is derived from the caller's own active Dispatch rather than from Run
binding, which is what dissolves the run-create bypass: creating a Run does not
stop you being a worker. Enforcement lives in a single dispatch-row writer that
owns all three INSERTs that mint a live worker — the generic claim, the supervised
worker-start path (including every retry), and the remote attachment. Two of those
were missed by earlier drafts of this change, so `creator` and `maxDepth` are
required parameters: a new spawn path cannot compile without deciding, and a
boundary test refuses the SQL anywhere else.

Schema v30 adds depth to dispatch_contexts and remote_dispatch_attachments,
NOT NULL DEFAULT 1 and backfilled to 1 so an unstamped or pre-upgrade row fails
closed rather than reading as a root coordinator. The attachment pane indexes
widen to the five states in which a remote worker may still be running:
loss of contact is not evidence of process death, so an unverifiable worker still
counts as a nesting parent.

Also adds the caller-evidence assertion that workerStart was the only Run-scoped
verb to skip, so a declared --from cannot name another terminal's pane and inherit
its depth.

Default is 1, so behaviour is unchanged unless the new setting is raised. Two
limitations are deliberate and documented rather than papered over: this is a
guardrail and not a security boundary, since a caller whose launch evidence is
unverifiable (any ordinary restored terminal) can declare another handle; and it
is enforced at supervised dispatch creation, so a settled worker whose process is
still alive counts as a root again.

* fix(orchestration): share caller resolution and pin worker gaps

* refactor(orchestration): make the caller resolver's pane contract explicit

Overloads so requireStablePane callers get a non-null string instead of casting,
and rename the attestation opt-out to say what it means: the caller asserts it
itself. A flag called assertEvidence:false reads as "attestation optional",
which is the hole this helper exists to close.

* fix(orchestration): propagate dispatch depth to federated workers

* chore(cli): refresh bundled orchestration guide
This commit is contained in:
Brennan Benson
2026-08-26 13:22:09 -07:00
committed by GitHub
parent 256f23c7a0
commit 8a07bbd8cf
96 changed files with 1788 additions and 356 deletions
@@ -4,6 +4,7 @@ import { createOrchestrationRpcHarness } from './orchestration-rpc-test-harness'
import type { OrchestrationDb } from '../../orchestration/db'
import type { OrcaRuntimeService } from '../../orca-runtime'
import { ORCHESTRATION_ASK_MAX_TIMEOUT_MS } from '../../../../shared/orchestration-ask-timeout'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('orchestration RPC methods', () => {
const h = createOrchestrationRpcHarness()
@@ -59,7 +60,7 @@ describe('orchestration RPC methods', () => {
it('records one idempotent answer from the current Run consumer', async () => {
setup()
const task = db.createTask({ spec: 'question work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
const created = db.createQuestion({
runId: activeRunId!,
dispatchId: dispatch.id,
@@ -103,7 +104,7 @@ describe('orchestration RPC methods', () => {
describe('orchestration.ask', () => {
function createAskingDispatch(handle = 'term_worker') {
const task = db.createTask({ spec: 'question work' })
const dispatch = db.createDispatchContext(task.id, handle)
const dispatch = createRootDispatch(db, task.id, handle)
return { task, dispatch }
}
@@ -4,6 +4,7 @@ import { createOrchestrationRpcHarness } from './orchestration-rpc-test-harness'
import type { OrchestrationDb } from '../../orchestration/db'
import { reconcileLifecycleMessage } from '../../orchestration/lifecycle-reconciliation'
import type { OrcaRuntimeService } from '../../orca-runtime'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('orchestration RPC methods', () => {
const h = createOrchestrationRpcHarness()
@@ -28,7 +29,7 @@ describe('orchestration RPC methods', () => {
describe('orchestration.check', () => {
function createDispatchedTask(assigneeHandle = 'term_worker', assigneePaneKey?: string) {
const task = db.createTask({ spec: 'manual check work' })
const dispatch = db.createDispatchContext(task.id, assigneeHandle, assigneePaneKey)
const dispatch = createRootDispatch(db, task.id, assigneeHandle, assigneePaneKey)
return { task, dispatch }
}
@@ -432,9 +433,9 @@ describe('orchestration RPC methods', () => {
it('does not complete worker_done for a stale inactive dispatch', async () => {
setup()
const task = db.createTask({ spec: 'retry-sensitive work' })
const staleDispatch = db.createDispatchContext(task.id, 'term_old')
const staleDispatch = createRootDispatch(db, task.id, 'term_old')
db.failDispatch(staleDispatch.id, 'retry elsewhere')
const activeDispatch = db.createDispatchContext(task.id, 'term_current')
const activeDispatch = createRootDispatch(db, task.id, 'term_current')
insertWorkerDone({
from: 'term_old',
taskId: task.id,
@@ -66,6 +66,65 @@ describe('orchestration RPC methods', () => {
})
}
it('rejects a declared caller that disagrees with complete attested evidence', async () => {
setup()
mockCurrentWorkerStart()
vi.mocked(runtime.getTerminalPaneKey).mockImplementation((handle) =>
handle === 'term_coord' || handle === 'term_other'
? coordinatorPaneKey
: handle === 'term_worker'
? 'tab_worker:leaf_worker'
: null
)
const attestedEvidence = {
terminalHandle: 'term_attested',
paneKey: 'tab_attested:leaf_attested',
launchToken: 'attested-launch-token'
} as const
vi.spyOn(runtime, 'verifyOrchestrationCompatibilityCaller').mockReturnValue({
terminalHandle: attestedEvidence.terminalHandle,
paneKey: attestedEvidence.paneKey,
processIncarnation: 'runtime_test:attested:1',
launchTokenHash: 'attested-launch-token-hash',
hostScope: { kind: 'local', hostId: 'local' }
})
ctx = { ...ctx, orchestrationCompatibilityEvidence: attestedEvidence }
const task = db.createTask({ spec: 'mismatched caller' })
await expect(
call('orchestration.workerStart', {
task: task.id,
from: 'term_other',
agent: 'codex'
})
).rejects.toMatchObject({ code: 'consumer_fenced' })
expect(db.getDispatchContext(task.id)).toBeUndefined()
})
it('deliberately permits present but unverifiable restored-terminal evidence', async () => {
setup()
mockCurrentWorkerStart()
// Restored/adopted terminals have no launch token, so verification returns null; this
// fail-open is deliberate compatibility behavior, not an oversight.
const task = db.createTask({ spec: 'restored caller limitation' })
ctx = {
...ctx,
orchestrationCompatibilityEvidence: {
terminalHandle: 'term_worker',
paneKey: 'tab_worker:leaf_worker'
}
}
const result = (await call('orchestration.workerStart', {
task: task.id,
from: 'term_coord',
agent: 'codex'
})) as { state: string }
expect(result.state).toBe('ready')
expect(db.getDispatchContext(task.id)).toBeDefined()
})
it('starts a fresh agent in the coordinator current worktree', async () => {
setup()
mockCurrentWorkerStart()
@@ -0,0 +1,27 @@
import type { DispatchCreator } from '../../orchestration/db/dispatch-depth'
import type { OrcaRuntimeService } from '../../orca-runtime'
/**
* Identify a CLI caller for nesting-depth purposes.
*
* Pane key and process incarnation come from the runtime's dispatch authority
* rather than the caller's params: remote attachment matching needs the exact
* incarnation, and a caller cannot be trusted to report its own.
*/
export function resolveDispatchCreator(
runtime: OrcaRuntimeService,
callerHandle: string | undefined
): DispatchCreator {
if (!callerHandle) {
// No declared caller means no resolvable parent. Depth 0 is the same answer
// the pre-existing Run-binding check already gives this case.
return { kind: 'system' }
}
const authority = runtime.getOrchestrationDispatchAuthority?.(callerHandle)
return {
kind: 'terminal',
handle: callerHandle,
paneKey: authority?.paneKey ?? runtime.getTerminalPaneKey(callerHandle) ?? undefined,
processIncarnation: authority?.processIncarnation ?? undefined
}
}
@@ -21,6 +21,7 @@ import {
type OrchestrationWorkerLaunchReceipt
} from './orchestration-worker-launch-preferences'
import { validateFederatedWorkerStartPlacement } from './orchestration-worker-start-validation'
import { resolveDispatchCreator } from './orchestration-dispatch-creator'
export async function startFederatedWorker(args: {
params: WorkerStartInput
@@ -97,6 +98,8 @@ export async function startFederatedWorker(args: {
const setupDecision = createsWorktree ? (params.setup ?? 'run') : 'not_applicable'
const started = db.createStartingWorkerDispatch({
creator: resolveDispatchCreator(runtime, params.from),
maxDepth: runtime.getNestedWorkerMaxDepth(),
taskId: task.id,
retryOf: params.retryOf,
startOptions: {
@@ -135,6 +138,9 @@ export async function startFederatedWorker(args: {
dispatchId: started.dispatch.id,
taskId: task.id,
taskSpec: task.spec,
// Carry the home dispatch depth across the federation boundary so a
// remote worker cannot be mistaken for a root when it dispatches again.
depth: started.dispatch.depth,
protocolVersion: federationProtocolVersion,
worktree,
name: params.name,
@@ -59,6 +59,7 @@ describe('federated worker agent launch', () => {
dispatchId: 'ctx_remote',
taskId: 'task_remote',
taskSpec: 'remote cursor worker',
depth: 2,
protocolVersion: 3,
worktree: 'folder:remote-workspace',
agent: 'cursor',
@@ -90,6 +91,7 @@ describe('federated worker agent launch', () => {
effective: { agent: 'cursor', model: 'gpt-5.3-codex', effort: 'high' }
}
})
expect(db.getRemoteDispatchAttachment('ctx_remote')?.depth).toBe(2)
expect(createTerminal).toHaveBeenCalledWith(
'id:folder:remote-workspace',
expect.objectContaining({
@@ -91,6 +91,8 @@ describe('orchestration federation control mail', () => {
runId = run.id
const task = homeDb.createTask({ spec: 'Wait for coordinator guidance', runId })
const started = homeDb.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
federation: {
@@ -118,6 +118,8 @@ describe('orchestration federated setup evidence', () => {
})
const task = db.createTask({ spec: 'remote setup', runId: run.id })
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
runtimeEpoch: runtime.getRuntimeId(),
@@ -6,6 +6,8 @@ export const FederationAttachStartParams = z.object({
dispatchId: requiredString('Missing Dispatch ID'),
taskId: requiredString('Missing Task ID'),
taskSpec: requiredString('Missing Task spec'),
/** Depth stamped by the Run home; omitted by older clients and defaults to 1. */
depth: z.number().int().min(1).optional(),
protocolVersion: z.union([z.literal(1), z.literal(2), z.literal(3)]),
worktree: requiredString('Missing remote worktree selector'),
name: OptionalString,
@@ -57,6 +57,7 @@ export const ORCHESTRATION_FEDERATION_ATTACH_METHODS: RpcMethod[] = [
homePeerFingerprint: orchestrationMutation.callerFingerprint,
protocolVersion: params.protocolVersion,
runtimeEpoch: runtime.getRuntimeId(),
depth: params.depth,
mutationReceipt: orchestrationMutation
})
const effects: FederationEffect[] = []
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../../orca-runtime'
import { OrchestrationDb } from '../../orchestration/db'
import { ORCHESTRATION_METHODS } from './orchestration'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('manual Dispatch observation', () => {
let db: OrchestrationDb | undefined
@@ -111,7 +112,8 @@ describe('manual Dispatch observation', () => {
coordinatorPaneKey: 'tab_coord:leaf_coord'
})
const task = db.createTask({ spec: 'injected lane', runId: run.id })
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
'term_worker',
'tab_worker:leaf_worker',
@@ -207,6 +209,39 @@ describe('manual Dispatch observation', () => {
})
})
it('lists an unsupervised context-only dispatch even when process identity is absent', async () => {
db = new OrchestrationDb(':memory:')
const runtime = new OrcaRuntimeService()
runtime.setOrchestrationDb(db)
const run = db.createRun({
objective: 'context-only listing',
coordinatorHandle: 'term_coord',
coordinatorPaneKey: 'tab_coord:leaf_coord'
})
const task = db.createTask({ spec: 'operator lane', runId: run.id })
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
const workerListMethod = ORCHESTRATION_METHODS.find(
(candidate) => candidate.name === 'orchestration.workerList'
)
if (!workerListMethod) {
throw new Error('Missing method orchestration.workerList')
}
const result = (await workerListMethod.handler(
workerListMethod.params?.parse({ run: run.id }),
{ runtime }
)) as { workers: { dispatchId: string; workerState: string; terminalState: string | null }[] }
expect(result.workers).toEqual([
expect.objectContaining({
dispatchId: dispatch.id,
workerState: 'unsupervised',
terminalState: 'retained'
})
])
expect(db.getDispatchContextById(dispatch.id)?.process_incarnation).toBeNull()
})
it.each([
['orchestration.workerStop', 'stopped'],
['orchestration.workerAbandon', 'abandoned']
@@ -216,7 +251,8 @@ describe('manual Dispatch observation', () => {
runtime.setOrchestrationDb(db)
const closeTerminal = vi.spyOn(runtime, 'closeTerminal')
const task = db.createTask({ spec: 'operator-owned lane' })
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
'term_worker',
'tab_worker:leaf_worker',
@@ -158,6 +158,8 @@ describe('manual Dispatch release', () => {
function createSupervisedWorker(): string {
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: createTask('supervised'),
startOptions: {}
})
@@ -8,6 +8,7 @@ import { OrchestrationDb } from '../../orchestration/db'
import { RpcDispatcher } from '../dispatcher'
import { ORCHESTRATION_METHODS } from './orchestration'
import { startFederatedWorker } from './orchestration-federated-worker-start'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('orchestration migration behavior', () => {
const databases: OrchestrationDb[] = []
@@ -124,7 +125,7 @@ describe('orchestration migration behavior', () => {
coordinatorPaneKey: 'tab_coord:leaf_coord'
})
const task = db.createTask({ spec: 'legacy worker', runId: run.id })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatcher = new RpcDispatcher({ runtime, methods: ORCHESTRATION_METHODS })
const response = await dispatcher.dispatch({
@@ -7,6 +7,7 @@ import type { RpcContext, RpcRequest } from '../core'
import { RpcDispatcher } from '../dispatcher'
import { ORCHESTRATION_METHODS } from './orchestration'
import { createOrchestrationRpcHarness } from './orchestration-rpc-test-harness'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
type SendWarning = { code: string; recipient: string; message: string }
type SendResult = {
@@ -170,7 +171,7 @@ describe('orchestration recipient routing oracle', () => {
it('normalizes an active Dispatch owner even when no pane is live', async () => {
setup()
const task = db.createTask({ spec: 'detached worker' })
const dispatch = db.createDispatchContext(task.id, 'term_detached', 'tab_gone:leaf_gone')
const dispatch = createRootDispatch(db, task.id, 'term_detached', 'tab_gone:leaf_gone')
const result = (await call({
from: 'term_coord',
@@ -194,7 +195,7 @@ describe('orchestration recipient routing oracle', () => {
coordinatorPaneKey: 'tab_foreign:leaf_coord'
})
const task = db.createTask({ spec: 'detached foreign worker', runId: foreignRun.id })
db.createDispatchContext(task.id, 'term_detached_foreign', 'tab_gone:leaf_gone')
createRootDispatch(db, task.id, 'term_detached_foreign', 'tab_gone:leaf_gone')
await expect(
call({
@@ -211,7 +212,7 @@ describe('orchestration recipient routing oracle', () => {
setup()
const overlapPane = 'tab_overlap:leaf_overlap'
const task = db.createTask({ spec: 'overlapped worker' })
db.createDispatchContext(task.id, 'term_overlap', overlapPane)
createRootDispatch(db, task.id, 'term_overlap', overlapPane)
const recipientRun = db.createRun({
objective: 'Overlapping coordinator',
coordinatorHandle: 'term_overlap',
@@ -2,7 +2,10 @@ import type { OrchestrationCompatibilityEvidence } from '../../../../shared/orch
import { orchestrationSkillRecoveryData } from '../../../../shared/orchestration-rpc-contract'
import { OrchestrationError } from '../../orchestration/orchestration-error'
import type { RunRow } from '../../orchestration/types'
import type { OrcaRuntimeService } from '../../orca-runtime'
import type {
OrcaRuntimeService,
OrchestrationCompatibilityCallerAuthority
} from '../../orca-runtime'
export type RunScopeParams = {
runId?: string
@@ -33,6 +36,49 @@ export function assertCallerHandleMatchesEvidence(
}
}
export type OrchestrationCallerParams = {
callerTerminalHandle: string
callerEvidence?: OrchestrationCompatibilityEvidence
callerAuthority?: OrchestrationCompatibilityCallerAuthority
/** Preserve legacy callers that treated a missing pane as an ordinary fence. */
requireStablePane?: boolean
/**
* Skip attestation here because the caller performs it itself — run-use must run
* its legacy-takeover check between pane resolution and attestation. Setting this
* without asserting elsewhere reopens the hole this helper exists to close.
*/
evidenceAssertedByCaller?: boolean
}
/** Resolve the caller's runtime pane and, by default, attest its declared handle. */
export function resolveOrchestrationCaller(
runtime: OrcaRuntimeService,
params: OrchestrationCallerParams & { requireStablePane: true }
): string
export function resolveOrchestrationCaller(
runtime: OrcaRuntimeService,
params: OrchestrationCallerParams
): string | null
export function resolveOrchestrationCaller(
runtime: OrcaRuntimeService,
params: OrchestrationCallerParams
): string | null {
if (!params.evidenceAssertedByCaller) {
assertCallerHandleMatchesEvidence(runtime, params.callerTerminalHandle, params.callerEvidence)
}
const paneKey =
params.callerAuthority?.terminalHandle === params.callerTerminalHandle
? params.callerAuthority.paneKey
: runtime.getTerminalPaneKey(params.callerTerminalHandle)
if (!paneKey && params.requireStablePane) {
throw new OrchestrationError(
'stable_pane_required',
'The coordinator terminal has no stable pane identity. Run this command inside a live Orca terminal.'
)
}
return paneKey ?? null
}
// Why: task and gate mutations must share one Run-binding rule.
export function resolveRunScope(runtime: OrcaRuntimeService, params: RunScopeParams): RunRow {
const db = runtime.getOrchestrationDb()
@@ -2,12 +2,11 @@ import { z } from 'zod'
import { defineMethod, type RpcMethod } from '../core'
import { OptionalBoolean, OptionalString, requiredString } from '../schemas'
import { ORCHESTRATION_RUN_PAGE_LIMIT } from '../../../../shared/orchestration-run-pagination'
import type {
OrcaRuntimeService,
OrchestrationCompatibilityCallerAuthority
} from '../../orca-runtime'
import { OrchestrationError } from '../../orchestration/orchestration-error'
import { assertCallerHandleMatchesEvidence } from './orchestration-run-scope'
import {
assertCallerHandleMatchesEvidence,
resolveOrchestrationCaller
} from './orchestration-run-scope'
const RunCreateParams = z.object({
objective: requiredString('Missing --objective'),
@@ -27,31 +26,16 @@ const RunListParams = z.object({
})
const RunShowParams = z.object({ id: requiredString('Missing --id'), from: OptionalString })
function requireCallerPane(
runtime: OrcaRuntimeService,
handle: string,
callerAuthority?: OrchestrationCompatibilityCallerAuthority
): string {
const paneKey =
callerAuthority?.terminalHandle === handle
? callerAuthority.paneKey
: runtime.getTerminalPaneKey(handle)
if (!paneKey) {
throw new OrchestrationError(
'stable_pane_required',
'The coordinator terminal has no stable pane identity. Run this command inside a live Orca terminal.'
)
}
return paneKey
}
export const ORCHESTRATION_RUN_METHODS: RpcMethod[] = [
defineMethod({
name: 'orchestration.runCreate',
params: RunCreateParams,
handler: (params, { orchestrationCompatibilityEvidence, runtime }) => {
assertCallerHandleMatchesEvidence(runtime, params.from, orchestrationCompatibilityEvidence)
const paneKey = requireCallerPane(runtime, params.from)
const paneKey = resolveOrchestrationCaller(runtime, {
callerTerminalHandle: params.from,
callerEvidence: orchestrationCompatibilityEvidence,
requireStablePane: true
})
const db = runtime.getOrchestrationDb()
const priorRun = db.getCurrentRunForPane(paneKey)
const run = db.createRun({
@@ -78,7 +62,13 @@ export const ORCHESTRATION_RUN_METHODS: RpcMethod[] = [
orchestrationCompatibilityCallerAuthority: callerAuthority
}
) => {
const paneKey = requireCallerPane(runtime, params.from, callerAuthority)
const paneKey = resolveOrchestrationCaller(runtime, {
callerTerminalHandle: params.from,
callerEvidence: orchestrationCompatibilityEvidence,
callerAuthority,
requireStablePane: true,
evidenceAssertedByCaller: true
})
if (
params.takeoverLegacy &&
(callerAuthority?.terminalHandle !== params.from || callerAuthority.paneKey !== paneKey)
@@ -117,8 +107,11 @@ export const ORCHESTRATION_RUN_METHODS: RpcMethod[] = [
name: 'orchestration.runCurrent',
params: RunCurrentParams,
handler: (params, { orchestrationCompatibilityEvidence, runtime }) => {
assertCallerHandleMatchesEvidence(runtime, params.from, orchestrationCompatibilityEvidence)
const paneKey = requireCallerPane(runtime, params.from)
const paneKey = resolveOrchestrationCaller(runtime, {
callerTerminalHandle: params.from,
callerEvidence: orchestrationCompatibilityEvidence,
requireStablePane: true
})
return { run: runtime.getOrchestrationDb().getCurrentRunForPane(paneKey) ?? null }
}
}),
@@ -5,6 +5,7 @@ import type { OrcaRuntimeService } from '../../orca-runtime'
import { openDecisionGateFromMessage } from '../../orchestration/coordinator-decision-gates'
import { applyEscalationToDispatch } from '../../orchestration/coordinator-escalation-triage'
import { createOrchestrationRpcHarness } from './orchestration-rpc-test-harness'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('orchestration.send Dispatch authority', () => {
const harness = createOrchestrationRpcHarness()
@@ -29,7 +30,8 @@ describe('orchestration.send Dispatch authority', () => {
async (legacyAuthority) => {
setup()
const attackerTask = db.createTask({ spec: 'attacker assignment' })
const attacker = db.createDispatchContext(
const attacker = createRootDispatch(
db,
attackerTask.id,
'term_attacker',
'tab_attacker:leaf_attacker',
@@ -37,7 +39,7 @@ describe('orchestration.send Dispatch authority', () => {
legacyAuthority ? undefined : 'runtime_test:term_attacker:1'
)
const victimTask = db.createTask({ spec: 'victim assignment' })
const victim = db.createDispatchContext(victimTask.id, 'term_victim')
const victim = createRootDispatch(db, victimTask.id, 'term_victim')
vi.mocked(runtime.getTerminalPaneKey).mockImplementation((handle) =>
handle === 'term_attacker' ? 'tab_attacker:leaf_attacker' : harness.coordinatorPaneKey
)
@@ -74,7 +76,7 @@ describe('orchestration.send Dispatch authority', () => {
it('rejects a caller-spoofed canonical Dispatch sender', async () => {
setup()
const task = db.createTask({ spec: 'legacy victim assignment' })
const dispatch = db.createDispatchContext(task.id, 'term_victim')
const dispatch = createRootDispatch(db, task.id, 'term_victim')
const result = (await send({
from: `dispatch:${dispatch.id}`,
@@ -97,7 +99,7 @@ describe('orchestration.send Dispatch authority', () => {
async (type) => {
setup()
const task = db.createTask({ spec: 'legacy owned assignment' })
db.createDispatchContext(task.id, 'term_legacy')
createRootDispatch(db, task.id, 'term_legacy')
vi.mocked(runtime.getTerminalPaneKey).mockImplementation((handle) =>
handle === 'term_legacy' ? 'tab_legacy:leaf_legacy' : harness.coordinatorPaneKey
)
@@ -123,7 +125,7 @@ describe('orchestration.send Dispatch authority', () => {
async (type) => {
setup()
const task = db.createTask({ spec: 'legacy re-dispatch target' })
const first = db.createDispatchContext(task.id, 'term_legacy')
const first = createRootDispatch(db, task.id, 'term_legacy')
const sent = (await send({
from: 'term_legacy',
@@ -137,7 +139,7 @@ describe('orchestration.send Dispatch authority', () => {
expect(JSON.parse(sent.message.payload)).toMatchObject({ dispatchId: first.id })
db.failDispatch(first.id, 'worker stopped before coordinator read its mail')
const second = db.createDispatchContext(task.id, 'term_legacy')
const second = createRootDispatch(db, task.id, 'term_legacy')
if (type === 'escalation') {
applyEscalationToDispatch(db, db.getMessageById(sent.message.id)!, () => {})
@@ -7,6 +7,7 @@ import type { OrchestrationDb } from '../../orchestration/db'
import type { OrcaRuntimeService } from '../../orca-runtime'
import type { RuntimeTerminalSummary } from '../../../../shared/runtime-types'
import { ORCHESTRATION_CONTRACT_VERSION } from '../../../../shared/protocol-version'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
function lifecycleGroupRecipientError(
type: 'worker_done' | 'heartbeat' | 'escalation' | 'decision_gate'
@@ -92,7 +93,7 @@ describe('orchestration RPC methods', () => {
it('routes exact Dispatch mail independently of terminal handles', async () => {
setup()
const task = db.createTask({ spec: 'controlled worker' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
const result = (await call('orchestration.send', {
from: 'term_coord',
@@ -117,7 +118,8 @@ describe('orchestration RPC methods', () => {
it('routes Dispatch mail by stable pane identity after worker handle remint', async () => {
setup()
const task = db.createTask({ spec: 'controlled worker after restart' })
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
'term_worker_before',
'tab_worker:leaf_worker'
@@ -185,7 +187,7 @@ describe('orchestration RPC methods', () => {
it('completes an identity-less injected send through its explicit worker handle', async () => {
setup()
const task = db.createTask({ spec: 'work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
vi.spyOn(runtime, 'getTerminalPaneKey').mockImplementation((handle) =>
handle === 'term_worker' ? 'tab_worker:leaf_worker' : null
)
@@ -211,7 +213,8 @@ describe('orchestration RPC methods', () => {
it('fences a replacement process for a capability-less manual Dispatch', async () => {
setup()
const task = db.createTask({ spec: 'process-bound manual work' })
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
'term_worker',
'tab_worker:leaf_worker',
@@ -257,7 +260,8 @@ describe('orchestration RPC methods', () => {
async (type) => {
setup()
const task = db.createTask({ spec: `process-bound ${type}` })
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
'term_worker',
'tab_worker:leaf_worker',
@@ -313,7 +317,7 @@ describe('orchestration RPC methods', () => {
setup()
const task = db.createTask({ spec: 'work' })
const dependent = db.createTask({ spec: 'dependent', deps: [task.id] })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
vi.spyOn(runtime, 'getTerminalPaneKey').mockImplementation((handle) =>
handle === 'term_coord' ? 'tab_coord:leaf_coord' : null
)
@@ -338,7 +342,7 @@ describe('orchestration RPC methods', () => {
it('ignores caller-supplied pane claims and uses the runtime-observed pane', async () => {
setup()
const task = db.createTask({ spec: 'work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
vi.spyOn(runtime, 'getTerminalPaneKey').mockReturnValue('tab_worker:leaf_worker')
vi.spyOn(runtime, 'deliverPendingMessagesForHandle').mockImplementation(() => {})
vi.spyOn(runtime, 'notifyMessageArrived').mockImplementation(() => {})
@@ -374,7 +378,7 @@ describe('orchestration RPC methods', () => {
it('requires the minted capability, exact pane, and process incarnation', async () => {
setup()
const task = db.createTask({ spec: 'capability work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
const capability = db.mintDispatchCapability({
dispatchId: dispatch.id,
paneKey: 'tab_worker:leaf_worker',
@@ -456,7 +460,7 @@ describe('orchestration RPC methods', () => {
it('does not wake waiters for a heartbeat suppressed at send time', async () => {
setup()
const task = db.createTask({ spec: 'work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
db.updateTaskStatus(task.id, 'completed')
vi.spyOn(runtime, 'deliverPendingMessagesForHandle').mockImplementation(() => {})
const notify = vi.spyOn(runtime, 'notifyMessageArrived').mockImplementation(() => {})
@@ -476,7 +480,7 @@ describe('orchestration RPC methods', () => {
it('still wakes waiters for a heartbeat on an active dispatch', async () => {
setup()
const task = db.createTask({ spec: 'work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
vi.spyOn(runtime, 'deliverPendingMessagesForHandle').mockImplementation(() => {})
const notify = vi.spyOn(runtime, 'notifyMessageArrived').mockImplementation(() => {})
@@ -685,7 +689,7 @@ describe('orchestration RPC methods', () => {
it('continues to send worker_done to a concrete terminal handle', async () => {
setup()
const task = db.createTask({ spec: 'work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
const result = (await call('orchestration.send', {
from: 'term_worker',
@@ -832,7 +836,7 @@ describe('orchestration RPC methods', () => {
it('releases dispatch lock before waking recipients when worker_done is sent via send', async () => {
setup()
const task = db.createTask({ spec: 'lock-release work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
// Why: waiter notification must observe the settled Dispatch, not stale lifecycle state.
vi.spyOn(runtime, 'notifyMessageArrived').mockImplementation(() => {
@@ -857,13 +861,13 @@ describe('orchestration RPC methods', () => {
expect(db.getActiveDispatchForTerminal('term_worker')).toBeUndefined()
// Lock released — a new dispatch to the same terminal must succeed.
const t2 = db.createTask({ spec: 'follow-up work' })
expect(() => db.createDispatchContext(t2.id, 'term_worker')).not.toThrow()
expect(() => createRootDispatch(db, t2.id, 'term_worker')).not.toThrow()
})
it('records heartbeat when heartbeat is sent via send', async () => {
setup()
const task = db.createTask({ spec: 'heartbeat work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
vi.spyOn(runtime, 'deliverPendingMessagesForHandle').mockImplementation(() => {})
await call('orchestration.send', {
@@ -883,7 +887,7 @@ describe('orchestration RPC methods', () => {
it('does not release dispatch lock for non-lifecycle sends', async () => {
setup()
const task = db.createTask({ spec: 'in-flight work' })
const dispatch = db.createDispatchContext(task.id, 'term_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker')
vi.spyOn(runtime, 'deliverPendingMessagesForHandle').mockImplementation(() => {})
await call('orchestration.send', {
@@ -4,6 +4,7 @@ import { createOrchestrationRpcHarness } from './orchestration-rpc-test-harness'
import type { OrchestrationDb } from '../../orchestration/db'
import type { OrcaRuntimeService } from '../../orca-runtime'
import { buildInjectRejectionMessage } from './orchestration-inject-rejection-message'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
describe('orchestration RPC methods', () => {
const h = createOrchestrationRpcHarness()
@@ -116,7 +117,7 @@ describe('orchestration RPC methods', () => {
setup()
const t1 = db.createTask({ spec: 'ready work' })
const t2 = db.createTask({ spec: 'active work' })
const ctx = db.createDispatchContext(t2.id, 'term_worker')
const ctx = createRootDispatch(db, t2.id, 'term_worker')
const result = (await call('orchestration.taskList', {})) as {
tasks: {
@@ -176,7 +177,7 @@ describe('orchestration RPC methods', () => {
it('completion frees the active dispatch context', async () => {
setup()
const task = db.createTask({ spec: 'work' })
db.createDispatchContext(task.id, 'term_a')
createRootDispatch(db, task.id, 'term_a')
await call('orchestration.taskUpdate', {
id: task.id,
@@ -397,7 +398,7 @@ describe('orchestration RPC methods', () => {
setup()
const t1 = db.createTask({ spec: 'first' })
const t2 = db.createTask({ spec: 'second' })
db.createDispatchContext(t1.id, 'term_a')
createRootDispatch(db, t1.id, 'term_a')
await expect(call('orchestration.dispatch', { task: t2.id, to: 'term_a' })).rejects.toThrow(
/already has an active dispatch/
@@ -453,7 +454,7 @@ describe('orchestration RPC methods', () => {
it('shows dispatch context for a task', async () => {
setup()
const task = db.createTask({ spec: 'work' })
db.createDispatchContext(task.id, 'term_a')
createRootDispatch(db, task.id, 'term_a')
const result = (await call('orchestration.dispatchShow', {
task: task.id
@@ -474,7 +475,7 @@ describe('orchestration RPC methods', () => {
it('--preamble returns the preamble text', async () => {
setup()
const task = db.createTask({ spec: 'refactor auth' })
db.createDispatchContext(task.id, 'term_a')
createRootDispatch(db, task.id, 'term_a')
const result = (await call('orchestration.dispatchShow', {
task: task.id,
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../../orca-runtime'
import { OrchestrationDb } from '../../orchestration/db'
import { ORCHESTRATION_METHODS } from './orchestration'
import { createRootDispatch } from '../../orchestration/db/root-dispatch-test-fixture'
vi.mock('electron', () => ({
BrowserWindow: { fromId: vi.fn(() => null) },
@@ -100,12 +101,12 @@ describe('worker-show interactive wait (STA-3714, STA-4513)', () => {
if (!paneKey || !incarnation) {
throw new Error('Runtime did not expose the worker pane identity.')
}
const dispatch = db.createDispatchContext(
const dispatch = createRootDispatch(
db,
task.id,
terminal.handle,
paneKey,
'launch-hash',
// A dispatch recorded against a process that has since been replaced.
'launch-hash', // A dispatch recorded against a process that has since been replaced.
opts?.breakIdentity === true ? `${incarnation}:replaced` : incarnation
)
db.mintDispatchCapability({
@@ -44,6 +44,8 @@ describe('worker-stop against a terminal we lost contact with', () => {
})
const task = db.createTask({ spec: 'stop worker', runId: run.id })
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
runtimeEpoch: runtime.getRuntimeId()
@@ -61,6 +61,8 @@ describe('orchestration worker recovery', () => {
})
const task = db.createTask({ spec: 'recover worker', runId: run.id })
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
runtimeEpoch
@@ -206,6 +208,8 @@ describe('orchestration worker recovery', () => {
})
const task = db.createTask({ spec: 'interrupted', runId: run.id })
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
runtimeEpoch: 'previous_runtime'
@@ -244,6 +248,8 @@ describe('orchestration worker recovery', () => {
})
const task = db.createTask({ spec: 'stop remote worker', runId: run.id })
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {},
runtimeEpoch: runtime.getRuntimeId(),
@@ -20,14 +20,24 @@ import {
} from './orchestration-worker-setup-gate'
import { failWorkerStartWithReceipt } from './orchestration-worker-start-receipt'
import { prepareLocalWorkerStart } from './orchestration-worker-start-validation'
import { resolveDispatchCreator } from './orchestration-dispatch-creator'
import { resolveOrchestrationCaller } from './orchestration-run-scope'
export const ORCHESTRATION_WORKER_START_METHODS: RpcMethod[] = [
defineMethod({
name: 'orchestration.workerStart',
params: WorkerStartParams,
handler: async (params, { runtime, orchestrationMutation }) => {
handler: async (
params,
{ runtime, orchestrationMutation, orchestrationCompatibilityEvidence }
) => {
const db = runtime.getOrchestrationDb()
const coordinatorPane = runtime.getTerminalPaneKey(params.from)
// Why: worker-start was the only Run-scoped verb that skipped this, so a
// declared --from could name someone else's pane and inherit their depth.
const coordinatorPane = resolveOrchestrationCaller(runtime, {
callerTerminalHandle: params.from,
callerEvidence: orchestrationCompatibilityEvidence
})
const run = coordinatorPane ? db.getCurrentRunForPane(coordinatorPane) : undefined
if (!run || (params.run && params.run !== run.id)) {
throw new OrchestrationError(
@@ -110,6 +120,8 @@ export const ORCHESTRATION_WORKER_START_METHODS: RpcMethod[] = [
: 'existing_worktree'
}
const started = db.createStartingWorkerDispatch({
creator: resolveDispatchCreator(runtime, params.from),
maxDepth: runtime.getNestedWorkerMaxDepth(),
taskId: task.id,
retryOf: params.retryOf,
startOptions,
@@ -2,6 +2,7 @@
import { z } from 'zod'
import { setImmediate as yieldToEventLoop } from 'node:timers/promises'
import { defineMethod, type RpcMethod } from '../core'
import { resolveDispatchCreator } from './orchestration-dispatch-creator'
import { OptionalFiniteNumber, OptionalString, OptionalBoolean, requiredString } from '../schemas'
import {
LEGACY_CONTRACT_VERSION,
@@ -1663,13 +1664,15 @@ export const ORCHESTRATION_METHODS: RpcMethod[] = [
}
revalidateLegacyCoordinator?.()
const ctx = db.createDispatchContext(
params.task,
to,
const ctx = db.createDispatchContext({
taskId: params.task,
assigneeHandle: to,
assigneePaneKey,
dispatchAuthority?.launchTokenHash ?? undefined,
processIncarnation
)
launchTokenHash: dispatchAuthority?.launchTokenHash ?? undefined,
processIncarnation,
creator: resolveDispatchCreator(runtime, params.from),
maxDepth: runtime.getNestedWorkerMaxDepth()
})
const dispatchCapability = params.inject
? db.mintDispatchCapability({
dispatchId: ctx.id,
@@ -23,6 +23,7 @@ import {
request,
type LegacyCompatibilityDispatcherHarness
} from './orchestration-legacy-compatibility-dispatcher-test-fixture'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
// Why: an unrelated caller the runtime CAN resolve to a pane — otherwise the refusal would be
// stable_pane_required and would prove nothing about Run authorization.
@@ -642,7 +643,8 @@ function createAdoptedDb(options: { settleWork: boolean }): {
const before = new OrchestrationDb(dbPath)
const task = before.createTask({ spec: 'legacy assignment', createdByTerminalHandle: 'term_old' })
before.createDispatchContext(
createRootDispatch(
before,
task.id,
'term_old_worker',
'tab_old:33333333-3333-4333-8333-333333333333'
@@ -15,6 +15,7 @@ import {
WORKER_HANDLE,
WORKER_PANE
} from './orchestration-legacy-compatibility-dispatcher-test-fixture'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
afterEach(() => {
cleanupLegacyCompatibilityDispatcherHarnesses()
@@ -287,7 +288,8 @@ function createCurrentDispatch(harness: ReturnType<typeof createHarness>): {
coordinatorPaneKey: CURRENT_COORDINATOR_PANE
})
const task = harness.db.createTask({ spec: 'current assignment', runId: run.id })
const dispatch = harness.db.createDispatchContext(
const dispatch = createRootDispatch(
harness.db,
task.id,
CURRENT_WORKER_HANDLE,
CURRENT_WORKER_PANE
@@ -326,7 +328,7 @@ async function createReusedCurrentDispatch(
coordinatorPaneKey: CURRENT_COORDINATOR_PANE
})
const task = harness.db.createTask({ spec: 'reused terminal assignment', runId: run.id })
const dispatch = harness.db.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(harness.db, task.id, WORKER_HANDLE, WORKER_PANE)
const capability = harness.db.mintDispatchCapability({
dispatchId: dispatch.id,
paneKey: WORKER_PANE,
@@ -11,6 +11,7 @@ import { OrchestrationDb } from '../orchestration/db'
import type { RpcRequest, RpcResponse } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
export const WORKER_HANDLE = 'term_legacy_worker'
export const WORKER_PANE = 'tab_worker:33333333-3333-4333-8333-333333333333'
@@ -55,7 +56,7 @@ export function createHarness(): LegacyCompatibilityDispatcherHarness {
spec: 'legacy assignment',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = before.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(before, task.id, WORKER_HANDLE, WORKER_PANE)
before.close()
const raw = new Database(dbPath)
@@ -15,6 +15,7 @@ import {
WORKER_HANDLE,
WORKER_PANE
} from './orchestration-legacy-compatibility-dispatcher-test-fixture'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
afterEach(() => {
cleanupLegacyCompatibilityDispatcherHarnesses()
@@ -191,7 +192,8 @@ describe('legacy compatibility through RpcDispatcher', () => {
processIncarnation: 'process-1'
})
harness.db.updateTaskStatus(harness.taskId, 'ready')
const currentDispatch = harness.db.createDispatchContext(
const currentDispatch = createRootDispatch(
harness.db,
harness.taskId,
'term_current_worker',
'tab_current_worker:77777777-7777-4777-8777-777777777777',
@@ -395,7 +397,8 @@ describe('legacy compatibility through RpcDispatcher', () => {
coordinatorPaneKey: 'tab_current_coord:55555555-5555-4555-8555-555555555555'
})
const task = harness.db.createTask({ spec: 'current assignment', runId: run.id })
const dispatch = harness.db.createDispatchContext(
const dispatch = createRootDispatch(
harness.db,
task.id,
'term_current_worker',
'tab_current_worker:66666666-6666-4666-8666-666666666666',
@@ -11,6 +11,7 @@ import { OrchestrationDb } from '../orchestration/db'
import type { RpcRequest, RpcResponse } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const COORDINATOR_HANDLE = 'term_legacy_coord'
const COORDINATOR_PANE = 'tab_coord:44444444-4444-4444-8444-444444444444'
@@ -45,7 +46,7 @@ function createHarness(): Harness {
spec: 'legacy assignment',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = before.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(before, task.id, WORKER_HANDLE, WORKER_PANE)
before.close()
const raw = new Database(dbPath)
@@ -11,6 +11,7 @@ import { OrcaRuntimeService } from '../orca-runtime'
import type { RpcRequest } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const COORDINATOR_HANDLE = 'term_legacy_coord'
const CURRENT_COORDINATOR_HANDLE = 'term_current_coord'
@@ -42,7 +43,7 @@ function createHarness(options?: { seedCutoverQuestion?: boolean; seedCutoverAns
spec: 'legacy assignment',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = before.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(before, task.id, WORKER_HANDLE, WORKER_PANE)
const cutoverQuestion = options?.seedCutoverQuestion
? before.insertMessage({
from: WORKER_HANDLE,
@@ -11,6 +11,7 @@ import { OrchestrationDb } from '../orchestration/db'
import type { RpcRequest } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const WORKER_HANDLE = 'term_legacy_worker'
const WORKER_PANE = 'tab_worker:33333333-3333-4333-8333-333333333333'
@@ -53,7 +54,7 @@ function createHarness(): Harness {
spec: 'legacy assignment',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = before.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(before, task.id, WORKER_HANDLE, WORKER_PANE)
before.close()
const raw = new Database(dbPath)
@@ -11,6 +11,7 @@ import { OrchestrationDb } from '../orchestration/db'
import type { RpcRequest } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const WORKER_HANDLE = 'term_legacy_worker'
const WORKER_PANE = 'tab_worker:33333333-3333-4333-8333-333333333333'
@@ -48,7 +49,7 @@ function createHarness(): Harness {
spec: 'legacy assignment',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = before.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(before, task.id, WORKER_HANDLE, WORKER_PANE)
before.close()
const raw = new Database(dbPath)
@@ -10,6 +10,7 @@ import { OrchestrationDb } from '../orchestration/db'
import { defineMethod, type RpcRequest } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const Params = z.object({ subject: z.string() })
@@ -304,6 +305,8 @@ describe('durable orchestration mutation ledger', () => {
.update(JSON.stringify({ method: 'orchestration.workerStart', params }))
.digest('hex')
const started = db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: params.task,
startOptions: {},
mutationReceipt: {
@@ -371,7 +374,7 @@ describe('durable orchestration mutation ledger', () => {
coordinatorPaneKey: 'tab_coord:leaf_coord'
})
const task = db.createTask({ spec: 'ask', runId: run.id })
const dispatch = db.createDispatchContext(task.id, 'term_worker', 'tab_worker:leaf_worker')
const dispatch = createRootDispatch(db, task.id, 'term_worker', 'tab_worker:leaf_worker')
const capability = db.mintDispatchCapability({
dispatchId: dispatch.id,
paneKey: 'tab_worker:leaf_worker',
@@ -10,6 +10,7 @@ import { OrchestrationDb } from '../orchestration/db'
import type { RpcRequest, RpcResponse } from './core'
import { RpcDispatcher } from './dispatcher'
import { ORCHESTRATION_METHODS } from './methods/orchestration'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
const WORKER_HANDLE = 'term_pre_update_worker'
const WORKER_PANE = 'tab_pre_update:33333333-3333-4333-8333-333333333333'
@@ -64,7 +65,7 @@ function createUpdateHarness(): Harness {
spec: 'finish work across an app update',
createdByTerminalHandle: COORDINATOR_HANDLE
})
const dispatch = oldRuntimeDb.createDispatchContext(task.id, WORKER_HANDLE, WORKER_PANE)
const dispatch = createRootDispatch(oldRuntimeDb, task.id, WORKER_HANDLE, WORKER_PANE)
const capability = oldRuntimeDb.mintDispatchCapability({
dispatchId: dispatch.id,
paneKey: WORKER_PANE,
@@ -44,7 +44,12 @@ describe('Task/Dispatch state invariant', () => {
const task = harness.db.createTask({ spec: 'retain assignment', runId: harness.runId })
const dispatch =
dispatchStatus === 'pending'
? harness.db.createStartingWorkerDispatch({ taskId: task.id, startOptions: {} }).dispatch
? harness.db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId: task.id,
startOptions: {}
}).dispatch
: await dispatchTask(harness, task.id, WORKER_HANDLE)
const response = await updateTask(harness, task.id, 'ready', 'must not persist')
@@ -285,7 +290,12 @@ async function createCapableDispatch(
status: 'pending' | 'dispatched'
): Promise<{ dispatch: { id: string }; capability: string }> {
if (status === 'pending') {
const dispatch = harness.db.createStartingWorkerDispatch({ taskId, startOptions: {} }).dispatch
const dispatch = harness.db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId,
startOptions: {}
}).dispatch
const capability = harness.db.prepareStartingWorkerAuthority({
dispatchId: dispatch.id,
handle: WORKER_HANDLE,
@@ -312,7 +322,12 @@ function createSupervisedDispatch(
taskId: string,
status: 'pending' | 'dispatched'
): { dispatch: { id: string }; capability: string } {
const dispatch = harness.db.createStartingWorkerDispatch({ taskId, startOptions: {} }).dispatch
const dispatch = harness.db.createStartingWorkerDispatch({
creator: { kind: 'system' },
maxDepth: Number.MAX_SAFE_INTEGER,
taskId,
startOptions: {}
}).dispatch
const capability = harness.db.prepareStartingWorkerAuthority({
dispatchId: dispatch.id,
handle: WORKER_HANDLE,