Skip to content

fix(orchestration): keep an injected dispatch active when its turn start is unobserved - #18021

Closed
Tauri-EPO wants to merge 1 commit into
stablyai:mainfrom
Tauri-EPO:Tauri-EPO/inject-stall-keeps-dispatch
Closed

Tauri-EPO wants to merge 1 commit into
stablyai:mainfrom
Tauri-EPO:Tauri-EPO/inject-stall-keeps-dispatch

Conversation

@Tauri-EPO

Copy link
Copy Markdown
Contributor

ELI5

dispatch --inject types the task into an agent's terminal and then waits to see the agent start working. When it could not see that, it marked the dispatch failed and revoked the worker's credentials — even though the task was already sitting in the agent's input box and the agent went on to do the work. Every progress report the worker then sent was rejected. Now, when the only problem is "we did not see the turn start", the dispatch stays active and the response says so.

What Changed

  • src/main/runtime/rpc/methods/orchestration-dispatch-methods.ts: when sendTerminalAgentPrompt throws agent_prompt_stalled, the dispatch is left dispatched, the capability stays valid, the response gains promptUnobserved: true, and a warning is logged. Any other error still fails the dispatch as before. This mirrors the coordinator's dispatched-unobserved handling from fix(agent-prompt): stop reporting delivered prompts as stalled (#16095) #16590.
  • src/cli/handlers/orchestration/dispatch-handlers.ts: the human-readable output tells the caller the preamble is in the composer and to read the terminal before re-sending anything.
  • src/main/runtime/rpc/methods/orchestration-tasks-dispatch.test.ts: a stalled injection keeps the dispatch dispatched, the task dispatched, and capability_revoked_at null.

Why

#17066: ten consecutive dispatch --inject runs against an idle Codex terminal returned agent_prompt_stalled about 7 s after injection, the preamble sat unsubmitted in the composer, a manual Enter made Codex run the task, and every heartbeat / worker_done was rejected with Dispatch <id> capability is revoked. The same pattern is reported for Hermes/opencode (#16660) and Grok (#16902). #16590 already stopped worker-start and the coordinator from revoking on this verdict; the --inject path still called db.failDispatch, which sets capability_revoked_at. The prompt bytes are written before verification, so a stall on this path can never prove the preamble is missing — failing the dispatch and revoking is the wrong default. Making the first Enter land reliably is a separate PR (agent-prompt composer observation); this one stops the lifecycle damage when it does not.

Linked Issue

Refs #17066, #16660, #16902

Visual Proof

N/A — RPC/CLI behavior only. orca orchestration dispatch --inject on a stall now prints the dispatch as [dispatched] plus a one-line note instead of {"ok": false, "error": {"code": "agent_prompt_stalled"}}.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Ran on Windows 11:

pnpm test src/main/runtime/rpc/methods/orchestration-tasks-dispatch.test.ts   # 33 passed
pnpm tc:node && pnpm tc:cli
oxlint + oxfmt on the changed files

AI Disclosure

Written with Claude Code (Claude Fable 5.1) under direction of @Tauri-EPO.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Wire compatibility: promptUnobserved is a new optional response field; old CLIs ignore it and see injected: true. Behavior change to weigh: a caller that used the stall error as a signal to retry no longer gets an error. #17066 and #15976 both document that retry as harmful (it pastes on top of the parked preamble), so the new default matches what the field reports asked for.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred) — tc:node, tc:cli, the dispatch RPC tests and oxlint on changed files pass locally; full lint/build left to CI

🤖 Generated with Claude Code

…art is unobserved

`orca orchestration dispatch --inject` wrote the preamble into the agent's
composer and then, when the submission verifier reported
agent_prompt_stalled, failed the dispatch and revoked its capability. The
preamble was already in the pane, so the worker went on to execute the task
and had every heartbeat and worker_done rejected with
"capability is revoked" (stablyai#17066; same shape as stablyai#16660 and stablyai#16902).

A stall on the inject path now mirrors the coordinator's
dispatched-unobserved handling from stablyai#16590: the dispatch stays
`dispatched`, the capability stays valid, and the response carries
`promptUnobserved: true` so the caller knows to read the terminal instead of
re-injecting. Any other failure still fails the dispatch as before.

Refs stablyai#17066

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The orchestration dispatch RPC now distinguishes stalled prompt observation from other injection failures. It keeps the dispatch active, returns promptUnobserved, and preserves task and capability state. The CLI mutation result exposes this flag and prints a warning when it is set. A regression test covers the stalled prompt case.

Merge Risk: 🔵 Low · up to 9702c

The change prevents valid worker progress from being rejected after an unobserved prompt start, but a terminal that never begins the task can leave the dispatch and its capability active indefinitely, and the new warning is English-only for localized CLI users. The PR is mergeable with explicit owner awareness and follow-up on cleanup and localization.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: keeping an injected dispatch active when turn-start observation is unobserved.
Description check ✅ Passed The description is complete and relevant. It covers the motivation, implementation, linked issues, behavior proof, testing, AI disclosure, compatibility considerations, and checklist status. The linke…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
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 is complete and relevant. It covers the motivation, implementation, linked issues, behavior proof, testing, AI disclosure, compatibility considerations, and checklist status. The linked issue section uses explicit issue references instead of a Fixes # line, but it still identifies the related issues.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3 files.


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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 18b23f2c-dc04-46c2-8e04-318b1eb48e75

📥 Commits

Reviewing files that changed from the base of the PR and between 7314ada and 9702cad.

📒 Files selected for processing (3)
  • src/cli/handlers/orchestration/dispatch-handlers.ts
  • src/main/runtime/rpc/methods/orchestration-dispatch-methods.ts
  • src/main/runtime/rpc/methods/orchestration-tasks-dispatch.test.ts

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

Comment thread src/cli/handlers/orchestration/dispatch-handlers.ts

@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 new issues found.

Reviewed changes

  • orchestration.dispatch stall handling — agent_prompt_stalled now leaves the dispatch dispatched (capability stays valid) and adds an optional promptUnobserved response field; every other error still calls db.failDispatch and rethrows.
  • CLI human-readable output — orca orchestration dispatch --inject prints a one-line "read the terminal before re-sending anything" note when the turn start was unobserved.
  • Test coverage — a new case asserts the dispatch and task stay dispatched and capability_revoked_at stays null on a stalled injection.

The core premise is sound: writeTerminalAgentPrompt writes the paste bytes and the submit Enter before verifyAgentPromptSubmission runs, so agent_prompt_stalled can only mean "turn start not observed" — never "preamble missing". The change is consistent with the coordinator's dispatched-unobserved path in coordinator-task-dispatch.ts:145 and the worker-start receipt's retainCapability in orchestration-worker-start-receipt.ts:34. The promptUnobserved field is additive, so old CLIs degrade cleanly (they see injected: true and no error, which is the intended no-retry behavior). The test re-reads DB state (getTask, getDispatchContextById), so it genuinely fails if the fix is reverted.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@Tauri-EPO

Copy link
Copy Markdown
Contributor Author

Closing: #16904 (merged 2026-09-06, 06a607a1d7) resolves the lifecycle damage this PR targeted, through a different mechanism.

  • orchestration.dispatch --inject now sends the preamble with acceptQueued: true (src/main/runtime/rpc/methods/orchestration/runs/dispatch-methods.ts:161).
  • On that path a stall no longer throws: src/main/runtime/orca-runtime-write-terminal-agent-prompt.ts:169 catches agent_prompt_stalled and returns prompt.stages: ['input_accepted'], so db.failDispatch is never reached, the Dispatch stays dispatched, and the capability is not revoked.
  • worker-start keeps the capability on the same verdict via retainCapability (src/main/runtime/rpc/methods/orchestration/worker/worker-start-receipt.ts:34).

The promptUnobserved flag this PR added is expressed upstream as the absence of turn_started in prompt.stages on the JSON receipt. The human-readable dispatch output does not surface it yet; that is a possible small follow-up after the release. #17066, #16660 and #16902 stay open until I can retest on a released build.

@Tauri-EPO Tauri-EPO closed this Sep 7, 2026
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