Skip to content

test(orchestration): execute the skill guide's taught sequence end to end - #19818

Open
Jinwoo-H wants to merge 1 commit into
mainfrom
test/orchestration-guide-contract-e2e
Open

Jinwoo-H wants to merge 1 commit into
mainfrom
test/orchestration-guide-contract-e2e

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1206 $\color{#cf222e}{\Huge{\mathbf{−}}}$​26 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1180
Prod 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​65 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​65

What the user notices

Before: an orchestration-source PR could delete the unbound-Run write path and
merge green. It did: #19542 broke orca orchestration send --to <handle> between two
plain terminals — the first command skills get orchestration teaches — and users
reported "orchestration stopped working after updating". Every gate passed because no
gate ran the guide's argv through a real CLI binary against a real runtime: the CLI
tests vi.mock the runtime client, the RPC harness uses :memory: with a Run already
bound, and the guide-contract check only matched strings against
ORCHESTRATION_COMMAND_SPECS.

After: a PR that breaks any command the guide teaches fails
tests/e2e/orchestration-guide-contract.spec.ts. The spec boots Electron, spawns the
compiled out/cli/index.js, and runs the guide's sequence in the order the guide
prints it, asserting each receipt's shape. The same spec fails if the guide starts
teaching a command nobody executes, or if the spec starts executing a flag the guide
never teaches. Nothing about the shipped product changes; the change is entirely in
what can merge.

The delete question

Nothing was deleted outright, and one candidate was deliberately kept.

config/scripts/orchestration-guide-command-contract.test.mjs was the obvious delete:
the new spec executes the taught commands, which strictly dominates matching their
strings. It stays because it covers what the spec cannot and the spec cannot cover it:

  • it runs on every PR in ~200 ms with no build, while the E2E only runs on PRs
    routed to it and needs a full electron-vite + build:cli;
  • it is the only gate over the eleven documented (verb, flag) pairs one local profile
    cannot reach (remote placement, legacy takeover, abandon). Those are excused from the
    E2E's coverage gate by name; the static test still proves the CLI accepts them.

What was removed is the duplication between them: both now read the guide through one
parser, config/scripts/orchestration-guide-invocations.ts, so they cannot disagree
about what "documented" means. The spec's own duplication was also removed — its DB
reads went into the existing tests/e2e/helpers/orchestration-mail-store.ts rather
than a second SQLite reader beside it.

The stale comment at orchestration-idle-mail-delivery.spec.ts:353 ("the send is
refused outright") was deleted; the test under it was already correct.

Proof

1. The regression this exists to catch (mandatory proof)

Local scratch edit re-applying the #19542 deletion in
src/main/runtime/orchestration/db/messages/message-insert.ts — the unbound block and
its INSERT OR IGNORE INTO runs replaced with:

const runId = msg.runId
if (!runId) {
  throw new Error('Run is required')
}

Rebuilt (no SKIP_BUILD, so global setup ran electron-vite build --mode e2e and
pnpm run build:cli) and ran the spec:

ORCA_BACKGROUND_LAUNCH=1 npx playwright test tests/e2e/orchestration-guide-contract.spec.ts \
  --config tests/playwright.config.ts --project electron-headless --workers=1
  1) [electron-headless] › tests/e2e/orchestration-guide-contract.spec.ts › runs the orchestration guide sequence through the compiled CLI across a restart

    Error: orca orchestration send --to term_65925ab9-28c7-41e6-8513-d06915852cd8 --subject Guide contract: plain terminal mail --body Sent before any Run exists. --json
    exit=1
    stdout:
    {
      "id": "8b56f917-584f-42ec-bb10-544b3dfc2ec3",
      "ok": false,
      "error": {
        "code": "runtime_error",
        "message": "Run is required Orchestration mutation request ID: 206e2156-a37e-4366-bfb9-f278da52229d.",
        "data": {
          "orchestrationRequestId": "206e2156-a37e-4366-bfb9-f278da52229d",
          "originalCommand": ["orca","orchestration","send","--to","term_65925ab9-28c7-41e6-8513-d06915852cd8","--subject","Guide contract: plain terminal mail","--body","Sent before any Run exists.","--json"]
        }
      }
    }
  1 failed

Step (a) — the first command in the guide — fails, on the exact string users saw.
Reverted with git checkout -- src/main/runtime/orchestration/db/messages/message-insert.ts,
rebuilt, re-ran:

  ✓  1 [electron-headless] › tests/e2e/orchestration-guide-contract.spec.ts:220:5 › runs the orchestration guide sequence through the compiled CLI across a restart (20.6s)
  1 passed (30.7s)

2. The drift gate bites

Scratch edit appending one fenced line to
skill-guides/orchestration/references/coordinator-loop.md:

```text
ORCA orchestration task-update --id <task_id> --status completed --json
```

The static contract test stays green (the CLI accepts that verb and those flags). The
spec goes red:

    Error: expect(received).toEqual(expected) // deep equality

    - Expected  - 1
    + Received  + 5

      Object {
        "undocumented": Array [],
    -   "unexecuted": Array [],
    +   "unexecuted": Array [
    +     "task-update --id",
    +     "task-update --json",
    +     "task-update --status",
    +   ],
      }
  1 failed

Reverted with git checkout -- skill-guides/orchestration/references/coordinator-loop.md.

3. What the sequence actually executes

Through the compiled CLI, against a live runtime, across a restart, with
ORCA_TERMINAL_HANDLE set instead of an added --from (which is how the guide's argv
really runs — inside an Orca terminal that resolves the caller):

  • (a) send --to <handleB> --subject --body --json from a terminal in no Run →
    exit 0, message.id, and the row asserted directly in SQLite as
    run_id = 'run_unbound', read = 0.
  • (b) check --terminal <B> --json returns it; reply --id --body --json;
    check --terminal <A> --json sees the reply and thread_id equals the original id.
  • (c) restart the runtime on the same profile: the three message rows are
    byte-identical (toEqual on the pre-restart rows), SELECT COUNT(*) FROM legacy_adoptions
    is unchanged, check --terminal <B> --peek --format --json returns the unread row with
    its subject rendered, and the row is still read = 0 afterwards (peek is non-consuming).
  • (d) inbox --full --json; run-create --objective --json; run-list; run-show --id;
    run-use --id; task-create --spec and task-create --spec --deps;
    task-list --ready --brief (asserts the dependent Task is absent) and task-list --run;
    worker-start --spec --worktree current --agent codex --json with a fake codex on PATH.
  • (e) the Dispatch capability is read out of the preamble the worker pane actually
    received, then the worker runs check --terminal <worker>,
    send --type heartbeat --phase, ask --question --options --timeout-ms (exits 1 on
    timeout and returns the pending question id), ask --resume (returns the coordinator's
    answer), send --type escalation, a second check before settling, and
    send --type worker_done --outcome succeeded.
  • (f) the coordinator runs check --wait --types "worker_done,escalation,question" --timeout-ms 60000 --json → the question with a delivery id; reply --id;
    check --ack <delivery_id> --wait --types --timeout-ms --json → the batch containing
    worker_done; worker-list --run --include-remote; worker-show --dispatch;
    worker-read --dispatch --limit 50; worker-retain (asserts state: 'retained') then
    worker-release.
  • (g) address forms: --to <handle>, --to run:<id> (asserted to land in that Run's
    rows), --to dispatch:<id> before release (and the worker sees it on its next check),
    and the group forms the guide documents in messaging-and-gates.md: --to @all and
    --to @worktree:<id>, each asserted on recipients and the returned messages array.
  • plus dispatch --task --to --inject --json into an operator-created agent terminal
    (low-level-topology.md), gate-create/gate-list/gate-resolve,
    request-show --request on the request id the worker_done receipt returned (asserted
    completed), check --run <id>, worker-start --task, and worker-stop --dispatch
    (asserted to close that exact terminal and leave the inject target alive).

The coverage gate is what keeps that list honest: it fails if the guide documents
something the spec does not run. Eleven pairs are excused by name with a reason, in
tests/e2e/helpers/orchestration-guide-command-ledger.ts:
worker-start --on/--repo/--setup (needs a second execution host),
worker-start --name/--retry-of/--terminal (needs a new worktree / a proven failed
Dispatch / an idle pre-existing agent terminal), worker-start --model/--effort (only
forwarded when the worker server advertises support, and the fake agent takes no model
argv), run-use --takeover-legacy (needs a pre-cutover database), and
worker-abandon --dispatch/--json (already driven by
orchestration-low-level-dispatch-release.spec.ts). One flag is excused in the other
direction: ask --json, because the guide prints ask without it.
An excuse that stops matching the guide is itself a failure (findStaleExcuses), and
that check runs in vitest on every PR, not only in E2E.

Wire / SSH / folder workspace

  • Remote wire: no wire change. The spec adds no RPC, no stream opcode, and no
    published field; it only calls existing ones. It does not cover the mixed-version case
    — a client and host at different versions is PR 3's scope.
  • SSH: not covered and not affected. Everything runs on one local execution host;
    worker-start --on <environment> is on the coverage gate's excuse list for exactly
    that reason. Nothing here assumes local-only behaviour in product code.
  • Folder workspace: not covered. The spec uses the seeded git repo the E2E global
    setup produces, because the restart fixture (attachRepoAndOpenTerminal) needs one.
    A non-git folder workspace would exercise the same orchestration paths; that gap is
    named in the report.

Testing

Gate Result
pnpm tc pass (exit 0)
pnpm test tests/e2e/helpers/orchestration-guide-command-ledger.unit.test.ts config/scripts/orchestration-guide-command-contract.test.mjs pass — Test Files 2 passed (2) / Tests 5 passed (5), 220 ms
same command, second run (flake) pass — Test Files 2 passed (2) / Tests 5 passed (5), 204 ms
pnpm run check:code-quality:changed pass — code quality: 0 new finding(s) across 10 changed file(s). / type-aware code quality: 0 new finding(s) / React Doctor: 0 new finding(s) / Changed-code quality gate passed since aac38d698ff7.
pnpm run check:react-doctor:changed pass — orca no score 0 issues
git diff --check clean
git status clean; nothing untracked
New spec, full build (no SKIP_BUILD) pass — ✓ 1 ... (21.9s) / 1 passed (32.1s)
New spec, SKIP_BUILD=1 run 2 pass — 1 passed (22.8s)
New spec, SKIP_BUILD=1 run 3 pass — 1 passed (20.4s)
tests/e2e/orchestration-idle-mail-delivery.spec.ts (comment-only edit) 10 passed, 2 failed; both failures are the pre-existing 15 s waitForActivePanePtyId default timing out under machine load, and both pass on re-run: 2 passed (15.7s)

Two additional runs failed for an environment reason worth stating plainly: this
worktree symlinks a node_modules shared with four other concurrent agents, and any
pnpm <script> in this repo re-verifies dependencies and re-runs postinstall. Twice a
sibling agent's node-pty rebuild landed mid-run, once removing spawn-helper
("Daemon's node-pty install is gone (worktree deleted?)" from worker-start) and once
failing pnpm run build:cli with a node-gyp error. Both name the primary worktree's
node_modules path in the error. Neither is reachable in CI, where each job installs its
own tree, and both passed on immediate re-run with no code change.

Spec file is 729 lines (cap 800); no lint disables were added.

… end

#19542 broke `orchestration send --to <handle>` between two terminals in no
Run — the FIRST command the orchestration guide teaches — and every gate was
green. The CLI tests mock the runtime client, so the runtime's throw was never
produced; the RPC harness uses `:memory:` with a bound Run, so the unbound path
was never taken; the guide-contract check only matched strings against
ORCHESTRATION_COMMAND_SPECS. No gate ran the taught argv through a real binary
against a real runtime.

This spec does. It boots Electron, spawns the compiled out/cli/index.js, and
runs the guide's sequence in the printed order — plain-terminal send, check,
reply, restart, run-create, task-create, worker-start, the worker's own
check/heartbeat/ask/escalation/worker_done, the coordinator's wait/reply/ack,
retain/release, the run:/dispatch:/@group address forms, gates, low-level
dispatch --inject, and worker-stop — asserting each receipt's shape rather than
a zero exit. Commands run with ORCA_TERMINAL_HANDLE set instead of an added
--from, because that is how the guide's argv actually runs.

The second half is a drift gate: the spec records every argv it ran and
compares it with the commands parsed out of skill-guides/orchestration*.md at
test time. A documented command nobody executes fails, and so does a flag the
spec executes that the guide never teaches. Excuses are per (verb, flag) with a
stated reason, and an excuse that stops matching the guide fails too.

The static contract test stays: it runs on every PR in seconds with no build and
is the only gate covering the documented commands one local profile cannot
reach. Both now read the guide through one parser so they cannot disagree about
what is documented.
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 59250476-691e-4c6d-b47d-018ce07ba752

📥 Commits

Reviewing files that changed from the base of the PR and between aac38d6 and 22ef580.

📒 Files selected for processing (10)
  • config/scripts/orchestration-guide-command-contract.test.mjs
  • config/scripts/orchestration-guide-invocations.ts
  • tests/e2e/helpers/orchestration-guide-cli.ts
  • tests/e2e/helpers/orchestration-guide-command-ledger.ts
  • tests/e2e/helpers/orchestration-guide-command-ledger.unit.test.ts
  • tests/e2e/helpers/orchestration-guide-fake-agent.ts
  • tests/e2e/helpers/orchestration-guide-terminal-topology.ts
  • tests/e2e/helpers/orchestration-mail-store.ts
  • tests/e2e/orchestration-guide-contract.spec.ts
  • tests/e2e/orchestration-idle-mail-delivery.spec.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

Adds a shared Markdown parser for documented orchestration commands. Adds a command ledger that detects guide coverage gaps and stale excuses. Adds helpers for compiled CLI execution, fake agents, terminal topology, and mail inspection. Adds a Playwright E2E contract covering orchestration flows across an application restart. Updates the static contract test to use the shared parser and corrects an idle-mail test comment.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 22ef5

The new orchestration guide contract coverage is ready to merge without identified current risk.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description clearly explains the change, motivation, implementation, regression proof, scope, and testing. However, it does not follow the required template: it omits the required Linked Issue ent… Add the missing template sections. Provide a linked issue after Fixes #, state N/A with a reason for Visual Proof if no UI or interaction changed, complete or explicitly address AI Disclosure, Review, Agent skill upstream boundary, Note…
Docstring Coverage ⚠️ Warning Docstring coverage is 32.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 10 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the primary change: executing the orchestration skill guide sequence end to end as a test.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description clearly explains the change, motivation, implementation, regression proof, scope, and testing. However, it does not follow the required template: it omits the required Linked Issue entry, Visual Proof or an explicit N/A statement, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist sections.

Resolution

Add the missing template sections. Provide a linked issue after Fixes #, state N/A with a reason for Visual Proof if no UI or interaction changed, complete or explicitly address AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist items, and preserve the existing testing evidence.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No blocking issues — two loose assertions inline worth tightening; the rest is a careful, well-grounded E2E contract.

Reviewed changes

  • Shared guide parser (config/scripts/orchestration-guide-invocations.ts) — extracts the guide's documented verb --flag pairs; both the existing static contract test and the new spec now read the guide through it, so they cannot disagree about what is documented. projectDir is a parameter rather than import.meta.dirname, correctly, since Playwright transpiles specs to CJS.
  • Drift-gate ledger (tests/e2e/helpers/orchestration-guide-command-ledger.ts + its unit test) — records every orchestration argv the spec runs and, at the end, fails if the guide documents an unexecuted (and unexcused) pair or the spec executes an undocumented (and unexcused) pair; findStaleExcuses keeps the excuse list honest on every PR via vitest.
  • End-to-end spec (tests/e2e/orchestration-guide-contract.spec.ts) — boots Electron twice across a restart, spawns the compiled out/cli/index.js, runs the guide's sequence in order against a live runtime, asserts receipt shapes, and reads SQLite directly for durable/run_unbound/legacy-adoption state.
  • Supporting helpers — compiled-CLI runner, fake codex agent with per-handle stdin ledger, terminal topology/resolution, and thin additions to the existing mailbox reader (readMailRowsById, countLegacyAdoptions).
  • Comment-only fix in orchestration-idle-mail-delivery.spec.ts — corrects a stale comment that described the pre-#19696 refusal behavior.

ℹ️ Nitpicks

  • The (a)–(g) sections are one ~700-line test(); wrapping each in test.step(...) would make a failure in this 10-minute sequence report which stage collapsed instead of one flat stack trace.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

'implementing',
'--json'
])
expect(receipt<{ message?: { id: string }; relay?: unknown }>(heartbeat)).toBeTruthy()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toBeTruthy() wrapper is dead: receipt already throws on a non-zero exit or ok !== true, and the envelope object it returns is always truthy. Call receipt<{ message?: { id: string }; relay?: unknown }>(heartbeat) directly — the send is still asserted by its exit code and ok flag.

const workerCheck = receipt<{ count: number }>(
orca(workerHandle, ['orchestration', 'check', '--terminal', workerHandle, '--json'])
)
expect(workerCheck.count).toBeGreaterThanOrEqual(0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count >= 0 cannot fail for the { count: number } shape, and receipt already proves the command exited 0, so this assertion pins nothing. The worker inbox is empty at this point — toBe(0) (or asserting on messages) would actually pin the behavior this line claims to check.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant