Files
orca/config/scripts/orchestration-skill-guidance.test.mjs
Jinwoo Hong 06a607a1d7 feat(orchestration): make multi-agent workflows durable (#16904)
<!-- orca-pr-loc -->
<!-- Programmatic LoC summary. Do not edit by hand; rewritten on every commit. -->

| | Files | Added | Deleted | Net |
| :--- | ---: | ---: | ---: | ---: |
| Test | 225 | $\color{#1a7f37}{\Huge{\mathbf{+}}}$​21666 | $\color{#cf222e}{\Huge{\mathbf{−}}}$​2820 | $\color{#1a7f37}{\Huge{\mathbf{+}}}$​18846 |
| Prod | 348 | $\color{#1a7f37}{\Huge{\mathbf{+}}}$​17107 | $\color{#cf222e}{\Huge{\mathbf{−}}}$​4706 | $\color{#1a7f37}{\Huge{\mathbf{+}}}$​12401 |

<!-- /orca-pr-loc -->

## ELI5

Orca now treats orchestration like a durable control plane instead of inferring success from terminal keystrokes. Agents can tell whether a prompt was accepted or a turn started, replay an ambiguous request without sending twice, and recover coordinator mail after a crash. Completed workers can be inspected, released, or retained, and their panes no longer auto-resume as if the work were still running.

## What changed

- **Run receipts** from `run-create/use/current/show/list` are the row without routing plumbing (`home_database`, `coordinator_pane_key`) and without the duplicate `binding` object.
- **`terminal send` receipts are honest and idempotent.** `input_accepted` and `turn_started` are the only stages; `--wait-submit` observes without resending; `--retry-request <uuid>` replays the exact request against the same process incarnation. A transport timeout keeps the retry ID; only a different runtime answering strips it. Value-less or non-UUID `--retry-request` is rejected on the CLI and the SSH shim.
- **Mailbox delivery is committed before wakeup.** Pointer writes are staged in the DB before any PTY byte, replayed once after restart, and never emit a naked Enter. The watermark that parks concurrent deliveries is released with the DB reservation. Restart rescans pointer-pending and `dispatch:` mailboxes.
- **Lifecycle is a guarded transition graph** (`lifecycle-transition.ts`) with a table-driven test over every caller edge. Task reopen/overturn stays in the public contract. A PTY exit during `worker-stop` is the stop succeeding, not a failure.
- **Worker lifecycle CLI:** `worker-start` (`--spec` creates Task + attempt in one call), `worker-show`, `worker-read` (provider transcript first, bounded terminal fallback with a typed reason, local/WSL/SSH), `worker-stop`, `worker-abandon`, `worker-release`, `worker-retain`, `worker-list` (rowid-fenced pagination, fleet liveness, `attention`, literal `nextAction`).
- **Release is an explicit ownership table** (`decideWorkerTerminalRelease`): only an `owned` resource can be settled, the archive is mandatory where reachable, and an owner whose process is proven exited can always get out of `retained` via `archive_status: unavailable`. User-taken-over, external, and transferred panes stay retained.
- **Settled-worker resume fence** (folds in #17651): a settled dispatch whose pane is still open is fenced at settlement, on stop/abandon/exit, and at startup; lifted on release, retain, takeover, and pane reuse.
- **Liveness is `live` / `unverifiable` / `exited` only**, from execution-host evidence. Fleet projection reads the evidence clock, not the relay delivery clock. A host-certified exit outranks the worker's settled state. `unverifiable` never authorizes stop, abandon, retry, or release, in code or in the guide.
- **Federation:** structured reads negotiate by `method_not_found` so every shipped host keeps transcript-first output; exited remote workers are closed before being reported closed; epoch fencing holds across peer restart, downgrade, and pairing rotation; no per-second forced capability probe.
- **Schema v35:** repairs databases stamped v34 by the pre-fix branch (mailbox_handle default, index predicates), drops the write-only `lifecycle_transition_receipts` ledger and five never-read v31 identity columns.
- **Schema v36:** `dispatch:<id>` mailboxes get a real consumer generation on `dispatch_contexts` and `remote_dispatch_attachments`, bumped and fenced in the same transaction on every re-attach (manual inject, worker-start, federated attach). A stale worker whose Dispatch moved to another process now gets `consumer_fenced` instead of silently acking the new worker's Delivery. Run mailboxes already worked this way.
- **Schema v37:** `dispatch_contexts` records its creator (`creator_handle`, `creator_pane_key`), so a coordinator's context-only self-dispatch is bookkeeping rather than a nesting parent; before this, one self-dispatch made every later `worker-start` from that coordinator fail the depth cap. Pre-v37 rows keep counting (fails closed).
- **Dispatch-mailbox ownership is checked, not inferred.** A `check` from a process whose pane no longer holds the Dispatch, or whose last Attempt was abandoned/failed and moved to another terminal, gets `consumer_fenced` instead of an empty inbox that reads as "no mail yet". `--peek`/`--all` stay readable. A paneless caller still gets `stable_pane_required` with the rebind recovery.
- **Liveness certification is stricter:** a `process_exited` stage whose termination reason is `unknown` (a stop that was issued but never observed) projects `unverifiable`, not `exited`. Federated `worker-show` carries the execution host's verdict and host kind instead of a local guess. A live, ready worker with nothing pending has `nextAction: none` rather than pointing at the `worker-show` that produced it.
- **Wire:** `workerShow` keeps `dispatch.task_id` next to `taskId` for shipped CLIs. `ask --json` uses the standard `{ok, result}` envelope like every sibling verb.
- **Migration start-version detection** treats the two v32 recovery columns as versioned. Before this, every shipped database stamped below 32 resolved to the v6 floor and replayed the whole chain (the v23 backfill synthesized 68 phantom retained workers on a real v30 profile). Verified on a copy of a real 62 MB v30 profile: starts at 30, no row delta, integrity ok, 11 ms.
- **Skill guide** rewritten as a ≤200-line kernel plus seven references, to the outcome-first standard (Result / Done / Safe failure first, conditions not case lists, one done bar, references loaded at the point of use). The canonical loop uses `worker-start --spec`, names `worker-list` for completion accounting, documents `--retry-request` / `request-show` / `--wait-submit`, and requires positive evidence before any stall action. The other seven guides get the same treatment in #18724, split out so this PR stays orchestration-only.
- **`rpc/methods/orchestration-*`** (126 flat files) regrouped into `orchestration/{worker,federation,messaging,runs,gates}/`.

## Why

User reports showed the same boundary failures: false `agent_prompt_stalled` causing duplicate sends (#15180), coordinators unable to trust screen scrapes, cold-parked terminals receiving a pointer without the submit, settled workers accumulating as live tabs and auto-resuming after restart, and no way to tell a stalled worker from a working one.

## Linked issues

Fixes #15180. Fixes #17935 (orchestration skill description is 866 characters; a guard now caps every bundled skill at 1,024). Supersedes #17651 (fence folded in). Advances #16660, #16522, #14907, #13047.

## Review record

This PR was reviewed adversarially after revival: eight independent lenses (lifecycle, mailbox, send, worker, federation, transcript, complexity, live ergonomics), each required to prove findings with a failing test. That produced 16 proven blockers, all fixed with red-then-green regression tests, followed by two re-review rounds and a third fix wave that caught 3 regressions introduced by the fixes and 7 fixes that missed their target; all closed. A final pass (five lenses incl. a live built-runtime smoke, then a re-review of the fix wave) found and fixed seven more, chiefly the stale-worker mailbox steal, the self-dispatch depth wedge, and the unproven-exit certification. Three independent Codex (gpt-6-astra) passes followed: the first found nothing new, the second found and fixed 3 defects (task-status reachability, WSL-local host classification, peer-capability epoch), the third found and fixed 6 (production PTY controller never installed settled writes, ambiguous in-flight pointer failures allowed duplicate replay, SSH/relay deadlines cut off a valid `--wait-submit`, stop-vs-exit race during inspection, and two release-recovery paths for vanished or exited terminals). The full record (findings, proof tests, triage, declines with reasons) is archived outside the repo.

**Rework after the live smoke.** A first live cross-host run on the shipped adhoc build (this Mac, a paired Windows host on the same build, a paired Mac on 1.4.195, and an SSH host) found a P1: a running local worker read `unverifiable`/`missing_status` because the fleet snapshot rows lacked the terminal handle the matcher keyed on. A 59-row failure table over every bug fixed during review showed the same two classes recurring: a fact dropped in transit through optional fields, and two authorities for one fact. Two blind designs (Opus, Codex) converged on the same mechanisms, and the scoped tranches landed here with red-then-green seam tests from the real producer to the real consumer, faults injected only at the transport or hook-ingest boundary:

- **Settlement (data-loss class):** one three-valued `WriteSettlement` (`accepted | refused{reason} | unverifiable{reason, bytesHandedToTransport}`) from the SSH multiplexer through daemon client, providers, controller, to pointer staging. No boolean, no rejection-as-third-state. The two silent degrades that fabricated a handoff are deleted; a provider that cannot settle refuses before any effect. Pointer text and Enter share the contract; a partial flush is `unverifiable`, never `refused`.
- **Evidence identity (false-liveness class):** fleet agent-status evidence is a tagged union (`binding: worker | pane | unresolved{reason}`, `clock: observed | delivery`) minted once at ingest, so a hook row captured on one process incarnation can never bind to a later dispatch on the same pane. The matcher's `!worker.paneKey ||` defaults are gone. One host-scope parser replaces two.
- **Small pre-merge items:** `capability_unsupported` from an old peer is no longer relabelled `host_unavailable`; a producer census test asserts every agent-status consumer path projects a pane-only hook row as `live`.

Two ergonomics defects the second live run surfaced on a real database are fixed here too: a pre-v3 dispatch already marked `completed` projected as `outcome_unknown` / `requiresAction: true` forever (three copies of the outcome ladder disagreed on legacy rows; now one resolver, legacy `completed` reads `succeeded` with nothing to act on, legacy `failed` stays actionable on the failure), and an unscoped `worker-list` enumerated the entire database (now defaults to the Run bound to the calling terminal, `--run` overrides, and the receipt's additive `scope` field says which).

A third live round on the shipped adhoc build of `b082443e1f` (same four hosts) plus an unscripted run in the user's own prompt style (a plain Claude Code shell, `/orchestration`, three workers, zero errors, bound-Run default confirmed) found two more branch defects, fixed with red-then-green tests: a worker freshly started on a paired server projected `unverifiable`/`host_indeterminate` with `requiresAction` for ~3 minutes, including after its own `worker_done`, because the host's federation observation returned `missing_liveness_verdict` for any PTY the liveness register had not yet swept (the host now reads a connected pane it owns locally as `live`; disconnected or SSH-scoped panes stay `unverifiable`); and six pre-v3 completed rows still carried an `input` category because settling through the task-status path or `failDispatch` never closed the Dispatch's pending question threads (both paths close them now, and schema v38 closes threads already pending on settled rows). The guide's `worker-start` examples now show `--model sonnet`, since an omitted model inherits the launcher's default.

A Codex adversarial pass on the tranche diff found one real design hole (identity minted at read time instead of ingest, now closed) and two daemon settlement paths that threw instead of settling (fixed). Two `@ts-nocheck` runtime mixins on these paths were extracted into checked modules; the repo-wide `@ts-nocheck` count is unchanged at 171.

Deletions during review: ~1,900 lines (write-only ledger, unread columns, dead v1 archive path, test harnesses shipped in prod, duplicated liveness and state-machine copies, self-capability checks that were compile-time true).

## Testing

- `pnpm typecheck:tsc:node|cli|web` clean
- `pnpm run check:code-quality:changed` 0 findings; `check:react-doctor:changed` 0
- `pnpm verify:bundled-skill-guides`, `verify:skill-bundle-manifest`
- full `pnpm test` on the integrated head: 72,332 pass / 292 skipped; the only failures were three non-PR files (two zsh live-shell suites hit a node-pty spawn-helper ENOENT while a concurrent native rebuild ran, 44/44 in isolation; `release-checkout.unit.test.ts` is a known 30 s load timeout that passes in isolation on `origin/main` too).
- CI on 70b4811267 (rerun, pre-Codex): the only reds are five SSH e2e specs plus `terminal-send-agent-prompt-submit:198`, each shown failing identically on main (main's E2E workflow is red on its last 40 runs). The terminal-send spec is root-caused and fixed separately in #18707. The Windows hook-service flake (#17721) and the federation load flake did not recur.
- Skills: `pnpm exec vitest run` over the skill gate files plus `src/cli`, `config/scripts`, `src/main/skills` pass; live smoke on the built CLI of `skills get orchestration` and `--full` (7 references).
- live headless runtime (`orca-dev serve`, isolated profile): canonical loop, stop, release, archive read, retry rejection, stale-handle check, SIGKILL-and-replay all verified with receipts
- Live cross-host smoke on the shipped adhoc build of `0d465e7931` (this Mac and a paired Windows host on the build, a paired Mac left on 1.4.195, an SSH host): local, paired-new, paired-old and SSH loops all settle; running workers read `live` on every host and `exited` after release; the old peer reads `capability_unsupported` and refuses release honestly. Injected 10 s relay stall with a send in flight: delivered exactly once after recovery, zero duplicates. Every liveness field across 104 receipts is only `live` / `unverifiable` / `exited`.
- Final live cross-host smoke on the shipped adhoc build of `b082443e1f` (same hosts): every loop settles; 942 of 948 legacy completed rows read settled with `requiresAction: false` before the question-thread fix and all of them after; `worker-list` scope reads `bound` / `flag` / `all` correctly; 122 JSON receipts carry only `live` / `unverifiable` / `exited`. Unscripted prompt-style run: clean.
- Confirmation smoke on the shipped adhoc build of `2da076d4e9` (this Mac and the paired Windows host, both updated): a freshly started Windows worker reads `live` on the first fleet poll and on all 20 that follow, with no `host_indeterminate` at any point, and `exited` after release; all 948 legacy completed rows read `requiresAction: false` with `nextAction: none` after schema v38; every verdict across 60 receipts is `live` / `unverifiable` / `exited`.
- Not physically exercised: WSL hosts, the renderer notification bell (headless has no renderer), same-session fence via a real pane close (renderer-only state), restart mid-delivery on a real app (covered by e2e only).

## Notes

- Remote-wire additions are optional fields or `method_not_found`-negotiated methods; one new Electron-only IPC channel (`agentStatus:legacyWorkerTerminalResumeFence`) never crosses the wire.
- SSH contact loss remains `unverifiable`; the execution host stays authoritative.
- Intentional wire projection change: an SSH host scope with an empty `targetId` now projects host id `ssh` instead of an empty string (remote-wire-compatibility rule 3, old clients decode the same field). A fleet pane key without a terminal handle is now `unidentifiable` rather than matched by pane key alone.
- Found live but pre-existing on main, filed separately: a relay daemon-start collision during transport loss rewrites the endpoint credential and wedges the surviving relay (host needs a manual kill); `terminal create` on a reconnecting SSH host reports an opaque `No PTY provider for connection`; `terminal list` reports `orphaned:false` and `terminal close` reports `ptyKilled:true` for a pane whose relay is gone (orchestration's own projection reads `unverifiable` correctly at the same moment).
- Downgrade after this PR is not a supported path: main opens a v37 database and early-returns (its inserts still work against the v36/v37 defaulted columns), but its one-outstanding-Delivery-per-Run index is a no-op against the branch's mailbox-scoped index of the same name.
- Known follow-ups (not blockers): `worker-list` materializes every dispatch row per call; a positive "agent absent" signal distinct from PTY liveness is a product decision left open (a headless fake agent never reaches `live`, so its `nextAction` stays `inspect`); a context-only self-dispatch still lists as `role: worker` in `worker-list`; `dispatch` task-not-found / task-not-ready / inject-rejected still surface as `runtime_error`; task and inbox receipts still expose raw row columns. Deferred skill product decisions live on #18724.
2026-09-06 14:34:03 -04:00

505 lines
24 KiB
JavaScript

import { readFileSync, readdirSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
const projectDir = resolve(import.meta.dirname, '../..')
const guidePath = join(projectDir, 'skill-guides', 'orchestration.md')
const referenceRoot = join(projectDir, 'skill-guides', 'orchestration', 'references')
const stubPath = join(projectDir, 'skills', 'orchestration', 'SKILL.md')
function readKernel() {
return readFileSync(guidePath, 'utf8')
}
function readReference(name) {
return readFileSync(join(referenceRoot, name), 'utf8')
}
function frontmatter(text) {
return /^---\n[\s\S]*?\n---\n/u.exec(text)?.[0]
}
function squash(text) {
return text.replace(/\s+/gu, ' ').trim()
}
// Routing lives in the frontmatter description alone; the body must not satisfy these.
function readDescription() {
return squash(frontmatter(readKernel()))
}
describe('orchestration skill routing', () => {
it('keeps the verbatim routing triggers a model matches the skill on', () => {
const description = readDescription()
for (const trigger of [
'threaded messages',
'worker_done/escalation waits',
'decision gates',
'decomposing work across agents',
'"hand off"',
'"handoff"',
'"handover"',
'"give this to another agent"',
'"another worktree"',
'lightweight terminal prompts',
'shell commands',
'Orca worktree management',
'reading or waiting on terminals'
]) {
expect(description).toContain(trigger)
}
})
it('keeps external browser routing at the OS/page boundary', () => {
const description = readDescription()
expect(description).toContain(
"Use Computer Use for external browser windows, webviews, Orca app UI, or desktop UI outside Orca's embedded browser only when the task requires OS/window-level control such as focus, menus, dialogs, coordinates, or screenshots."
)
expect(description).toContain(
"`orca-cli` for Orca's embedded pages and a page-automation tool such as Playwright or CDP for external pages."
)
})
})
describe('orchestration kernel', () => {
it('keeps the always-loaded guide compact and ordered around the normal protocol', () => {
const kernel = readKernel()
const headings = [
'## Outcome',
'## Classify the role',
'## Authority and safety floor',
'## Worker obligations',
'## Canonical supervised loop',
'## Task-spec contract',
'## Completion accounting',
'## Conditional references'
]
// Why: 202 is the budget after the anti-loop nextAction rule; the kernel is always in context.
expect(kernel.split('\n').length).toBeLessThanOrEqual(202)
for (let index = 1; index < headings.length; index += 1) {
expect(kernel.indexOf(headings[index])).toBeGreaterThan(kernel.indexOf(headings[index - 1]))
}
expect(kernel).not.toContain('## Contract Migration')
expect(kernel).not.toContain('## Full Handoffs')
expect(kernel).not.toContain('## Worker Terminals')
})
it('classifies coordinator, dispatched worker, handoff, compatibility, and ordinary roles', () => {
const kernel = readKernel()
expect(kernel).toContain('explicitly asks to supervise, monitor, wait for results')
expect(kernel).toContain('live injected preamble with Task and Dispatch IDs')
expect(kernel).toContain('Handoff owner')
expect(kernel).toContain('create no Run, Task, or Dispatch and do not monitor completion')
expect(kernel).toContain('Compatibility operator')
expect(kernel).toContain('Ordinary terminal agent')
expect(kernel).toContain('Model or effort selection does not make a handoff supervised')
expect(squash(kernel)).toContain('Never substitute a non-Orca subagent tool')
})
it('makes Dispatch identity, remote uncertainty, folders, and mixed versions a safety floor', () => {
const kernel = readKernel()
expect(kernel).toContain('A Dispatch is one authoritative Task attempt')
expect(kernel).toContain('Lifecycle authority comes from the active Dispatch')
expect(kernel).toContain('execution host owns')
expect(squash(kernel)).toContain('`live` / `unverifiable` / `exited`')
expect(kernel).toContain('contact loss is not process death')
expect(kernel).toContain('Folder workspaces are valid')
expect(squash(kernel)).toContain('Treat unknown optional fields as absent')
expect(kernel).toContain('new stream operation requires advertised capability')
expect(kernel).toContain('Never fall back to local execution')
})
it('puts exactly-once worker completion and post-completion idle before coordinator mechanics', () => {
const kernel = readKernel()
expect(kernel.indexOf('## Worker obligations')).toBeLessThan(
kernel.indexOf('## Canonical supervised loop')
)
expect(kernel).toContain('The injected preamble is authoritative')
expect(kernel).toContain('Send `worker_done` exactly once')
expect(kernel).toContain('three-sentence executive summary')
expect(kernel).toContain('`--outcome succeeded` or `--outcome failed`')
// Why: the runnable worker_done command is the preamble's; its flag spellings are pinned
// on worker-contract.md by 'keeps heartbeat and worker_done recipes bound to the injected
// capability', so the kernel carries the obligations as prose and no third copy.
expect(kernel).not.toContain('--type worker_done')
expect(kernel).toContain('After `worker_done`, end the dispatched turn and idle')
expect(kernel).toContain('Do not reuse the settled lifecycle IDs')
})
it('teaches worker-start as the only normal-path launch and starts the wave before waiting', () => {
const kernel = readKernel()
const firstStart = kernel.indexOf('worker-start --spec "<worker A task>"')
const secondStart = kernel.indexOf('worker-start --spec "<worker B task>"')
const firstWait = kernel.indexOf('check --wait')
expect(firstStart).toBeGreaterThan(kernel.indexOf('run-create'))
expect(secondStart).toBeGreaterThan(firstStart)
expect(firstWait).toBeGreaterThan(secondStart)
expect(squash(kernel)).toContain('start the full independent wave before waiting')
expect(kernel).toContain('`worker-start` is the normal path')
expect(squash(kernel)).toContain(
"If `worker-start` exits non-zero, do not relaunch. Read the receipt's `failedStage` and `residualResources`"
)
expect(kernel).toContain('operator-created process unsupervised')
expect(kernel).not.toMatch(/^ORCA terminal create/mu)
})
it('makes worker-start --spec the default and keeps task-create for planned fan-out', () => {
const kernel = squash(readKernel())
expect(kernel).toContain('`worker-start --spec` creates the Task and its attempt in one call')
expect(kernel).toContain('Use `task-create` plus `worker-start --task <task_id>`')
})
it('gives the supervised loop an exit condition for a live terminal with a dead agent', () => {
const kernel = squash(readKernel())
expect(kernel).toContain("`worker-list`'s `projection.liveness` is the fleet verdict")
expect(kernel).toContain("`worker-show`'s `observation.status` is PTY liveness only")
expect(kernel).toContain('After three consecutive empty waits')
expect(kernel).toContain('`ORCA orchestration worker-list --include-remote --json`')
expect(kernel).toContain('defaults to the bound Run; `--run <run_id>` overrides')
expect(kernel).toContain(
'`projection.attention` categories, `projection.attention.requiresAction`, and literal `projection.nextAction` argv'
)
expect(kernel).toContain(
'An `inspect` `nextAction` on a `live` row with `attention.requiresAction` false is informational, not a command to re-run: keep waiting with `check --wait`'
)
expect(kernel).toContain('choose `worker-stop` or `worker-abandon`')
})
it('lets only positive evidence of exit end a wait', () => {
const kernel = squash(readKernel())
expect(kernel).toContain('Leave the wait only on positive proof the agent stopped')
expect(kernel).toContain('`exited` liveness')
expect(kernel).toContain("the worker's own observation of process exit")
expect(kernel).toContain('transcript whose final agent turn sent no `worker_done`')
expect(kernel).toContain(
'`unverifiable` is absence, including when `worker-show` reports `agentWait` null. Absence never authorizes stop, abandon, retry, or release'
)
})
it('names --terminal, never --from, as the check caller flag', () => {
const kernel = squash(readKernel())
expect(kernel).toContain('`check` names its caller with `--terminal <handle>`, never `--from`')
expect(kernel).not.toContain('check --from')
})
it('makes a dispatched worker read coordinator follow-ups on a cadence', () => {
const kernel = squash(readKernel())
expect(kernel).toContain('Read coordinator follow-ups at each natural checkpoint')
expect(kernel).toContain('once more immediately before `worker_done`')
expect(kernel).toContain('`ORCA orchestration check --terminal <your_handle> --json`')
})
it('requires full Delivery processing and settled-terminal accounting before ack', () => {
const kernel = readKernel()
expect(squash(kernel)).toContain(
'oldest FIFO Delivery and replays that batch until acknowledged'
)
expect(squash(kernel)).toContain('Process every message')
expect(squash(kernel)).toContain("decide each settled terminal's next owner before the ack")
expect(squash(kernel)).toContain('reused, explicitly retained, or released')
expect(squash(kernel)).toContain(
'the turn ends only when the report to that user names, per Task, its outcome, the evidence behind it, and any unresolved blocker'
)
expect(kernel).toContain('worker-release --dispatch <dispatch_id>')
expect(kernel).toContain('check --ack <delivery_id> --wait')
expect(squash(kernel)).toContain(
'`worker-list --run <run_id> --terminal-state reclaimable --json`'
)
expect(squash(kernel)).toContain('do not follow it with `task-update --status completed`')
})
it('treats long waits and release uncertainty as safe checkpoints', () => {
const kernel = readKernel()
// Why: e92d7812d91 and c78f40fdd0b protect one rule; `## Outcome` states it once and each
// gate cites it, so these pin the condition rather than a per-gate list of non-proofs.
expect(squash(kernel)).toContain(
'Only positive proof of exit authorizes stop, abandon, or retry, and only an accepted settlement authorizes release. Every other observation, absence included, is a checkpoint'
)
expect(squash(kernel)).toContain('A timeout or empty result is a checkpoint, not a failure')
expect(squash(kernel)).toContain('Do not stop, retry, release, or launch a duplicate editor')
expect(squash(kernel)).toContain('without the positive proof `## Outcome` requires')
expect(squash(kernel)).toContain(
'Only an accepted settlement authorizes it; no other observation does'
)
expect(kernel).toContain('never substitute `terminal close`')
})
it('defines self-contained task specs and honest send attention semantics', () => {
const kernel = readKernel()
for (const field of [
'**Target:**',
'**Change:**',
'**Constraints:**',
'**Ownership:**',
'**Observable acceptance:**'
]) {
expect(kernel).toContain(field)
}
expect(kernel).toContain('successful `orchestration send` proves durable enqueue')
expect(kernel).toContain('best-effort attention only')
expect(squash(kernel)).toContain('does not prove the recipient read or accepted it')
})
})
describe('owned orchestration references', () => {
it('routes every conditional read to exactly one shipped reference', () => {
const kernel = readKernel()
const routed = [...kernel.matchAll(/`references\/([^`]+\.md)`/gu)].map((match) => match[1])
const shipped = readdirSync(referenceRoot)
.filter((name) => name.endsWith('.md'))
.sort()
const tableRoutes = [...kernel.matchAll(/^\|.*`references\/([^`]+\.md)`.*\|$/gmu)].map(
(match) => match[1]
)
expect([...new Set(routed)].sort()).toEqual(shipped)
// Why the table and not every mention: prose may cite a reference the gate table already routes.
expect(tableRoutes.sort()).toEqual(shipped)
expect(kernel).toContain('ORCA skills get orchestration --full')
// Why: the selector is the cheap path, so the kernel must teach it first and keep
// `--full` only as the fallback for a CLI build that predates it.
expect(squash(kernel)).toContain(
'run `ORCA skills get orchestration --reference references/<file>.md`'
)
expect(squash(kernel)).toContain(
'If the CLI rejects `--reference`, run `ORCA skills get orchestration --full`'
)
expect(squash(kernel)).toContain('If an older CLI rejects `--full`')
})
it('owns expanded waves, launch preferences, reuse, and review boundaries', () => {
const reference = readReference('coordinator-loop.md')
expect(reference).toContain('task-list --ready --brief --json')
expect(reference).toContain('`--effort` requires `--model`')
expect(reference).toContain('neither option combines with `--terminal`')
expect(reference).toContain('`launch.requested` with `launch.effective`')
expect(reference).toContain('worker-start --task <next_task_id> --terminal')
expect(reference).toContain('A review-only `worker_done` authorizes synthesis')
expect(squash(reference)).toContain(
'post-review fixes and PR preparation remain with that owner'
)
})
it('owns worker heartbeat, ask resume, escalation, failure, and idle', () => {
const reference = readReference('worker-contract.md')
expect(reference).toContain('--type heartbeat')
expect(reference).toContain('--task-id <task_id> --dispatch-id <dispatch_id>')
expect(reference).toContain('--phase "<investigating|implementing|reviewing|waiting>"')
expect(reference).toContain('--resume <message_id>')
expect(reference).toContain('do not create a duplicate question')
expect(reference).toContain('--type escalation')
expect(reference).toContain('Send exactly one terminal report')
expect(reference).toContain('Use `--outcome failed`')
expect(reference).toContain('After `worker_done`, end the dispatched turn and idle')
expect(squash(reference)).toContain(
'ORCA orchestration check --terminal <worker_handle> --json'
)
expect(squash(reference)).toContain('once more immediately before `worker_done`')
expect(squash(reference)).toContain(
'`check` names its caller with `--terminal`, never `--from`'
)
expect(squash(reference)).toContain('If `check` returns `consumer_fenced`')
expect(squash(reference)).toContain('An empty `check` never means you were replaced')
})
it('keeps heartbeat and worker_done recipes bound to the injected capability', () => {
const reference = readReference('worker-contract.md')
const recipes = [...reference.matchAll(/```text\n([\s\S]*?)```/gu)].map((match) => match[1])
const heartbeat = recipes.find((recipe) => recipe.includes('--type heartbeat'))
const workerDone = recipes.find((recipe) => recipe.includes('--type worker_done'))
for (const recipe of [heartbeat, workerDone]) {
expect(recipe).toContain('--from <worker_handle>')
expect(recipe).toContain('--dispatch-capability <capability>')
expect(recipe).toContain('--task-id <task_id> --dispatch-id <dispatch_id>')
}
expect(workerDone).not.toContain('--files-modified')
expect(workerDone).not.toContain('--report-path')
expect(squash(reference)).toContain('only when applicable, using actual paths')
expect(reference).toContain('Do not send documentation placeholders as metadata')
})
it('owns local, folder, worktree, SSH, WSL, remote, and mixed-version placement', () => {
const reference = readReference('placement-and-remote.md')
expect(reference).toContain('--worktree current --agent codex')
expect(squash(reference)).toContain(
'A worktree selector needs the full `<repo-id>::<path>` value Orca returned, passed as `id:<newFullWorktreeId>`; a bare repo id is not a worktree id'
)
expect(reference).toContain('--worktree new-child')
expect(reference).toContain('--worktree new-top-level')
expect(reference).toContain('Folder workspaces are first-class')
expect(reference).toContain('Remote `current` and `new-child` are invalid')
expect(squash(reference)).toContain("`--on` selects only the worker's execution server")
expect(squash(reference)).toContain(
'route every follow-up, read, stop, and cleanup by Dispatch ID'
)
expect(reference).toContain('`live`, `unverifiable`, or `exited`')
expect(squash(reference)).toContain('unknown stream opcodes can be silently dropped')
expect(reference).toContain('printed `orca-ide`')
expect(squash(reference)).toContain(
'ORCA project setup-existing-folder --project <project_id> --host <host_id> --path <abs_path> --kind folder --json'
)
expect(squash(reference)).toContain('and rejects a plain directory')
expect(reference).toContain(
'ORCA orchestration worker-list --run <run_id> --include-remote --json'
)
expect(squash(reference)).toContain(
'enumerate remote workers with `--include-remote` or every one of them reads `unverifiable`'
)
})
it('owns FIFO mail, Dispatch addresses, groups, questions, and gates', () => {
const reference = readReference('messaging-and-gates.md')
expect(reference).toContain('oldest FIFO Delivery')
expect(squash(reference)).toContain('Process every row')
expect(squash(reference)).toContain(
'A Delivery therefore always carries the whole FIFO batch whatever its types, and a `check` without `--wait` hands that batch over unfiltered'
)
expect(reference).toContain('send --to dispatch:<dispatch_id>')
for (const group of ['@all', '@grok', '@cursor', '@worktree:<id>']) {
expect(reference).toContain(group)
}
expect(reference).toContain('Dispatch lifecycle messages never target groups')
expect(reference).toContain('gate-create --task <task_id>')
expect(reference).toContain("Do not create a gate merely to answer a worker's `ask`")
expect(reference).toContain('successful `send` proves durable enqueue')
expect(squash(reference)).toContain('Wake and nudge are best-effort attention only')
expect(squash(reference)).toContain(
'`check` names its caller with `--terminal <handle>` and is the only verb that rejects `--from`'
)
})
it('owns positive-evidence retry, unknown outcomes, retain/release, and no terminal close', () => {
const reference = readReference('recovery-and-cleanup.md')
expect(squash(reference)).toContain('| `ready` or active | Keep waiting')
expect(squash(reference)).toContain('| `outcome_unknown` | Inspect')
expect(squash(reference)).toContain('| Remote contact lost | Preserve `unverifiable`')
expect(reference).toContain('--retry-of <dispatch_id>')
expect(squash(reference)).toContain('Placement is never silently inherited')
expect(reference).toContain('worker-abandon --dispatch')
expect(reference).toContain('worker-retain --dispatch')
expect(reference).toContain('worker-release --dispatch')
expect(squash(reference)).toContain('`release_pending` or `release_unknown`')
expect(squash(reference)).toContain('Never substitute `terminal close`')
})
it('owns the lost-response question and the request-show verdicts', () => {
const reference = squash(readReference('recovery-and-cleanup.md'))
expect(reference).toContain('request-show --request <request_id> --json')
expect(reference).toContain('--retry-request <request_id>')
expect(reference).toContain('`completed` means the mutation already took effect')
expect(reference).toContain('`pending` means the original mutation is still running')
expect(reference).toContain('that is not proof nothing happened')
expect(reference).toContain('terminal send --wait-submit <seconds>')
})
it('names worker-list as the enumerating command and the agent-liveness authority', () => {
const reference = squash(readReference('recovery-and-cleanup.md'))
expect(reference).toContain('ORCA orchestration worker-list --run <run_id> --json')
expect(reference).toContain("`worker-show`'s `observation.status` is PTY liveness only")
expect(reference).toContain(
'`projection.attention.categories`, `projection.attention.requiresAction`'
)
expect(reference).toContain('`projection.nextAction` argv')
expect(reference).toContain('the fleet verdict decides')
expect(reference).toContain(
'ORCA orchestration worker-list --run <run_id> --include-remote --json'
)
expect(reference).toContain('reads `unverifiable` until you enumerate with `--include-remote`')
expect(reference).toContain('follow `page.nextCursor` with `--cursor <value>`')
})
it('requires positive evidence of exit before stop, abandon, retry, or release', () => {
const reference = squash(readReference('recovery-and-cleanup.md'))
expect(reference).toContain('Leave the wait only on positive proof the agent stopped')
expect(reference).toContain('`unverifiable` is always absence')
expect(reference).toContain('Absence never authorizes stop, abandon, retry, or release')
expect(reference).toContain(
'| `unverifiable` liveness | Keep waiting or inspect; never stop, abandon, retry, or release |'
)
})
it('owns the custom topology exception without claiming process ownership', () => {
const reference = readReference('low-level-topology.md')
expect(reference).toContain('only when `worker-start` cannot express')
expect(reference).toContain('terminal create --worktree active')
expect(reference).toContain('dispatch --task <task_id> --to <handle> --inject')
expect(reference).toContain('operator-created process unsupervised')
expect(squash(reference)).toContain('creates no supervised worker resource row')
expect(reference).toContain('Use `worker-start --terminal <handle>`')
expect(squash(reference)).toContain('never use it for an ownership handoff')
})
it('owns legacy labels, read-only degradation, exact recovery, and takeover', () => {
const reference = readReference('legacy-contract-migration.md')
expect(reference).toContain('[LEGACY COMPATIBILITY]')
expect(reference).toContain('[LEGACY RECOVERY REPLAY — MAY HAVE BEEN SEEN]')
expect(reference).toContain('[LEGACY READ-ONLY]')
expect(squash(reference)).toContain(
'degrade to read-only inspection and never fall back to local execution'
)
expect(squash(reference)).toContain(
'must not spawn, write, signal, stop, switch, focus, split, or inject'
)
expect(reference).toContain('launcher status `75`')
expect(reference).toContain('run_legacy_local')
expect(reference).toContain('Recovered orchestration work from a contract update')
expect(reference).toContain('run-use --id <adopted_run_id> --takeover-legacy')
expect(reference).toContain(
'Never take over while the original coordinator is actively coordinating'
)
})
})
describe('orchestration install stub', () => {
it('preserves the safe version-matched resolver and bounded old-binary fallback', () => {
const stub = readFileSync(stubPath, 'utf8')
expect(stub).toContain('discovery stub')
expect(stub).toContain('ORCA skills get orchestration')
expect(stub).toContain('ORCA_CLI_COMMAND')
expect(stub).toContain('orca-dev')
expect(stub).toContain('orca-ide')
expect(stub).toContain('GNOME Orca screen reader')
expect(squash(stub)).toContain('explicitly reports that `skills get` is an unknown command')
expect(stub).toContain('do not invent commands')
expect(stub).not.toMatch(/^orca /mu)
})
it('performs no orchestration mutation before loading the guide', () => {
const stub = readFileSync(stubPath, 'utf8')
const preGuide = stub.split('## Load the full guide')[0]
expect(preGuide).not.toContain('orchestration task-create')
expect(preGuide).not.toContain('orchestration dispatch')
expect(frontmatter(stub)).toBe(frontmatter(readKernel()))
expect(stub.length).toBeLessThan(readKernel().length)
})
})