Skip to content

fix(orchestrate): a dead Orca runtime is exit 4, and a timed-out ask is resumed, not decided - #53

Merged
choiyounggi merged 2 commits into
mainfrom
fix/orca-runtime-outage-and-ask-timeout
Aug 7, 2026
Merged

fix(orchestrate): a dead Orca runtime is exit 4, and a timed-out ask is resumed, not decided#53
choiyounggi merged 2 commits into
mainfrom
fix/orca-runtime-outage-and-ask-timeout

Conversation

@choiyounggi

Copy link
Copy Markdown
Owner

Two failure modes measured on a live 3-worker Orca run. Both read as normal operation at the time, which is what makes them worth closing.

1. orca-wait.sh could not tell a runtime outage from a quiet window

The check result went through .result.count // 0, so all three of these scored "0 messages" and continued:

reality what orca-wait saw
ordinary timeout — ok:true, count:0, timedOut:true, status 0 0 messages → checkpoint ✅
runtime fault — {"ok":false,"error":{"code":"runtime_unavailable"}}, status 1 0 messages → checkpoint ❌
CLI killed — no output at all, nonzero status 0 messages → checkpoint ❌

An outage therefore burned the entire budget one silent 15s window at a time and finished on no message in <tmo>ms — checkpoint, keep waiting. The coordinator never learned the runtime was gone.

New exit 4, fired only on an explicit fault signal: nonzero status, ok:false, or connectionLost:true mid-wait. A missing ok still checkpoints, so a partial or replayed payload can never be reported as an outage.

Exit 4 is deliberately not a restart signal: a dead runtime does not stop a worker session. Measured — a worker landed three commits and a push while the runtime was down.

Envelopes verified against the live CLI, not assumed:

$ orca orchestration check --run <fresh> --wait --types worker_done --timeout-ms 20000 --json
{"ok":true,"result":{"count":0,"timedOut":true,"connectionLost":false,...}}     rc=0

$ orca orchestration worker-show --dispatch bogus --json
{"ok":false,"error":{"code":"dispatch_not_found",...}}                          rc=1

2. The worker prompt never said what a timed-out ask means

Rule [4] told workers to ask and end their turn, and stopped there. Orca leaves a timed-out question pending, to be resumed by its original message id — but with nothing said about it, both workers proceeded on a self-chosen "conservative assumption" (at 600s and 900s) and reported the guess only afterwards. One guess happened to match the human decision; that is luck, not a protocol.

Rule [4] now states that a timeout is not an answer, requires ask --resume <message_id>, and forbids re-asking (a second --question creates a second thread the coordinator cannot disambiguate). The same rule is added to SKILL.md's worker contract.

Tests

386/386 bats green. Six new tests on the outage split — the real timeout envelope (regression guard), ok:false, a killed CLI through the live orca path, connectionLost while ok:true and count:0 (the boundary that looks exactly like a quiet window), and no-ack-on-fault. The orca test double gained an rc-fail marker so the nonzero path is exercised through the real code path, not only through canned JSON.

The Orca prompt-set checksum is bumped in this commit, per the guard's own instruction, and a new test names the ask-timeout obligation so a reword that keeps the command but drops the rule shows up in that test's diff.

Not changed, and why

  • Worktree reuse (--worktree id:<repoId>::<path>) — already the documented path in O3; the run rediscovered it rather than finding a gap.
  • Usage-limit stalls invisible in worker-show — already covered by orca-worker-stalled.sh (agent updatedAt silence, not lastOutputAt); the run simply did not use it.
  • _keepalive lines breaking JSON parsing — keepalive goes to stderr and is documented in orca agent-context --json; our scripts already drop stderr. Caused by merging streams with 2>&1 by hand.
  • worker-release returning retained — documented behavior for user-taken-over terminals.
  • zsh glob on ?/* in a message body — an interactive-typing artifact; no script builds --body/--question through an unquoted expansion.

🤖 Generated with Claude Code

…is resumed, not decided

Two failure modes measured on a live 3-worker run, both of which read as normal
operation at the time.

1. orca-wait.sh could not tell a runtime outage from a quiet window. The check
   result went through `.result.count // 0`, so `{"ok":false,"error":{"code":
   "runtime_unavailable"}}` (status 1) and a killed CLI (no output) both scored
   0 messages and continued as checkpoints until the whole budget was gone,
   ending on "no message — keep waiting". New exit 4 fires only on an explicit
   fault: nonzero status, `ok:false`, or `connectionLost:true` mid-wait. A
   missing `ok` still checkpoints, so a partial payload cannot read as an
   outage. Envelopes verified against the live CLI: an ordinary timeout is
   ok:true / count 0 / timedOut:true with status 0; `worker-show` on an unknown
   dispatch is ok:false with status 1.

   Exit 4 is deliberately not a restart signal — a dead runtime does not stop a
   worker session (measured: a worker committed and pushed three times while the
   runtime was down).

2. The worker prompt told workers to `ask` and end their turn, but said nothing
   about the window expiring. Orca leaves a timed-out question pending, to be
   resumed by its original message id; both workers instead proceeded on a
   self-chosen "conservative assumption" at 600s and 900s and reported the guess
   afterwards. One happened to match the human decision. Rule [4] now states
   that a timeout is not an answer, requires `ask --resume <message_id>`, and
   forbids re-asking (a second --question creates a second thread).

386/386 bats green. The Orca prompt-set checksum is bumped in this commit, and a
new test names the ask-timeout rule so a reword that keeps the command but drops
the obligation shows up in that test's diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@choiyounggi
choiyounggi force-pushed the fix/orca-runtime-outage-and-ask-timeout branch from d105dc1 to 64e3913 Compare August 7, 2026 04:12
…iate-return list

Self-review of the previous commit found two defects in it.

The outage line was the only primary exit-classification message written to
stderr; exits 2, 3, 5 and 6 all write theirs to stdout. A coordinator that reads
stdout — which is where `ack <id>` and `completed=<c>/<n>` already go — would
have received a bare exit 4 with no error code to act on. Moved to stdout and
pinned with a test that discards stderr, since bats merges the two streams and
could not otherwise tell them apart.

The `--until-all` contract still listed 3/5/6 as the codes that return
immediately; exit 4 does too. Corrected in both the script header and SKILL.md,
and the exit-4 entry now carries the same already-acked-batches caveat exit 2
has. The "(3/5/6) are deliberately NOT acked" sentence is left alone: it is about
Deliveries, and exit 4 never receives one.

387/387 bats green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@choiyounggi
choiyounggi merged commit 7f2da63 into main Aug 7, 2026
2 checks passed
@choiyounggi
choiyounggi deleted the fix/orca-runtime-outage-and-ask-timeout branch August 7, 2026 04:27
choiyounggi added a commit that referenced this pull request Aug 7, 2026
#54)

Ships #53: orca-wait.sh exit 4 for a runtime outage (a normal timeout still checkpoints at 2), and the worker prompt's requirement that a timed-out `ask` be resumed by its message id rather than decided by the worker.

Bumps .claude-plugin/plugin.json and .claude-plugin/marketplace.json together — the release workflow gates only on plugin.json, so marketplace.json drifts silently when left behind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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