| name | dispatch-governance |
| description | Partial Skill: invoke by name only — the Legate's routing brain — picks and executes a dispatch strategy for a role intent. Loaded in-session by the legate gateway, and by the headless-legate agent headless. Not triggered by users directly. |
| user-invocable | false |
Dispatch Governance
The judgment the cyberlegion CLI deliberately does not carry. The CLI is dumb hands — it never
auto-routes, never picks a backend, never invokes a Task tool itself. This governance is the one
place that judgment lives, loaded in-session by the legate gateway on a dispatch intent, and
realized headless by the headless-legate agent. Both loads run the exact same procedure below.
Input: an intent to fulfill role R with brief B, optionally expecting a result that
satisfies verdict schema V.
1. Resolve the agent definition
npx cyberlegion@0.3.1 agent resolve <R> --format json
Read model, effort, harness, warm, interactive off the resolved def. warm marks a role
that should run as a live peer session (not a fire-and-forget unit); interactive marks a role that
may need to grill the caller or converse over multiple rounds, not just return one result.
2. Probe the environment
npx cyberlegion@0.3.1 mux doctor --format json
Read whether a multiplexer was ancestry-discovered (tmux/herdr) — a channel strategy needs one
to open a pane in; its absence rules that strategy out regardless of what the agent def wants.
3. Pick exactly one strategy
warm | interactive | mux present | Strategy |
|---|
| true | true | true | channel |
| true | true | false | run-inline |
| false or true | false | — | subagent |
| false | true | — | error — unroutable def |
-
channel — warm, interactive, and a pane is available: spin up a live peer that can converse
and mail back over rounds. Compose the primitives directly — no single CLI command does this:
mint a thread id, weave it (and an instruction to reply on it) into brief B, then:
npx cyberlegion@0.3.1 unit spawn --agent <R> --brief-file <B> --at <placement>
npx cyberlegion@0.3.1 mail await --thread <the-minted-thread-id> [--timeout <ms>] [--max-wait <s>]
unit spawn launches the peer (its brief tells it what thread to reply on); mail await then
blocks on that reply thread. Use --at to place the pane, --timeout/--max-wait to bound the
wait; a waiting outcome at --max-wait means re-run mail await on the same thread to keep
polling, not a failure.
The wake-matrix decision (which sub-mode of channel to run) belongs here too, since the CLI
carries no selectWakePath helper of its own:
| Environment | Wake mode |
|---|
| Portable default (mux unknown, or nothing verified) | Bounded await — poll mail await with --max-wait, re-arming on waiting ("A-loop"). |
| Claude Code, with an observable background task | Bounded await plus a background-task check-in ("A-prime") — same polling, but the harness's own background-task surface can short-circuit a wait. |
A live foreign session behind a verified multiplexer (mux doctor reports tmux/herdr, not none) | Doorbell — unit nudge the peer's pane, then mail await ("B"). |
Multiplexer is none | Never pick doorbell (B) — there is no pane to ring. Fall back to bounded await (A). |
-
run-inline — the role is warm and interactive (needs a live back-and-forth), but there is no
multiplexer to host a peer in. Do not delegate. Return a run-inline verdict and let the
caller (the session that loaded this governance — e.g. the SDD conductor) do the work itself,
in-session. A cold subagent is the wrong substitute here: it cold-reloads its whole context every
round and holds no user channel, so a role that needs to converse cannot be served that way.
The CLI never chooses between these on its own — unit spawn + mail await only run in this
sequence when this governance picked channel; the caller's Task tool only runs when it picked
subagent; picking run-inline means neither runs at all.
The subagent | channel seam
This is the seam a dependent (e.g. SDD, ADR-0023) references by intent — "dispatch a role
fulfillment and expect a verdict" — never by pinning subagent or channel as a literal command
name. The dependent states its intent (role, brief, verdict schema) and this governance decides the
mechanism; a dependent that hardcodes "always subagent" or "always channel" has broken the seam and
coupled itself to a mechanism instead of an intent.
Result shape — DispatchResult
Every strategy resolves to one shape the caller can handle uniformly:
{
"strategy": "subagent" | "channel" | "run-inline",
"id": "<unit id, when one was spawned>",
"verdict": "matched" | "waiting" | "timed-out" | "run-inline",
"result": { },
"needsInput": ["<question>", "..."]
}
result is carried through unvalidated today — structured verdict-schema checking against V is a
deferred capability (mail --verdict-schema), not present in this CLI.
waiting (channel path, --max-wait cap hit) and timed-out (channel path, --timeout elapsed)
are re-armable and terminal respectively — treat them the same way mail await's own three
outcomes are treated; never silently retry past a timed-out. A needsInput result on the subagent
path means the unit hit its own leash and returned a needs-input result rather than guessing.
How you relay that needsInput (or your own) is relay-governance, not this skill. Report/ask
transport is keyed on the reporting agent's own lifecycle — a framed callee returns needsInput to
its caller; a bare cron session with no frame pushes mail to the standing owner and exits. Load
relay-governance for the fork; this skill owns strategy choice (channel / run-inline /
subagent), relay owns how the result or an unanswerable question gets home.
Non-goals
- No auto-routing in the CLI. The CLI carries no
dispatch command group at all — unit spawn,
mail await, unit nudge, and the caller's own Task tool are invoked deliberately by this
governance (or subagent-backend-governance), never by a --backend auto flag — no such flag
exists.
- No mid-flight strategy switch. Once a strategy is picked for one dispatch, it runs to its one
of the three outcomes above; a failed subagent is not silently retried as a channel.