--- name: dispatch description: >- Turn "start a session in repo X, on order Y, at cost Z" into a complete, verified startup command — the prompt written to a file, the model and effort derived from the rubric, and the output form chosen by whether the target repo already has a terminal pane. Use whenever work is being handed to a session in another repo, or to this repo's own next session: "dispatch a session in repo X", "start a session there with this order", "give me the startup command for repo Y", "hand this work to ", "write the plan file for that session", "open a tab for X with this task", "how do I launch the next session on this". Also triggers on Norwegian phrasings: "dispatch en økt i repo X", "start en økt der med denne ordren", "gi meg oppstartskommandoen for Y", "send arbeidet til ", "skriv planfila for den økten", "åpne en tab for X med denne oppgaven", "hvordan starter jeg neste økt på dette". Trigger even when no tool is named — producing a runnable startup command for another session IS this skill. Not for choosing WHICH repo deserves the next session (that is `board`), not for scoring model and effort alone (that is `route`), and not for sending a message to another repo (that is `coord-send`). version: "0.26.0" --- # dispatch — hand a session a task it can actually start on A dispatch is one line the operator pastes and one file that line reads. Both halves are easy to get wrong in ways that look right: a command with no prompt in it, a plan file the driver silently discards, a `--no-go` that does not stop what everyone assumed it stopped. All three were measured on 2026-08-16 — four separate misfires in one day, by two different repos — and this skill exists so they are not re-derived a fifth time. **You produce the command. You never run it.** Starting a session in another repo spends the operator's quota and takes an action inside a repo this session does not own. Hand back the finished command and stop. ## The engine BOARD="${CLAUDE_PLUGIN_ROOT}/scripts/board.sh" "$BOARD" --dispatch --repo \ --prompt-file \ --target-pane \ --path \ --verification \ --reversibility \ --scope \ --rationale "why these four scores" It writes nothing and prints one block. Exit 2 means it refused — read stderr and fix the call; every refusal is a case where a command would have been wrong rather than merely imperfect. It is `board.sh` and not a script of its own because the block format (`tab=`/`repo=`/`dir=`/`command=`/`paste=`) has exactly one generator. Two emitters of one file format is the drift defect this repo's CLAUDE.md warns about. ## The four steps, in order ### 1. Write the prompt file The order goes in a file, in plain prose, and the file is what the session actually receives: ```bash mkdir -p "$HOME/.claude/dispatch" PF="$HOME/.claude/dispatch/-$(date -u +%Y%m%dT%H%M%SZ).prompt" ``` Runtime scratch, not repo content — but it must survive until the operator pastes the line, so never write it to a directory that gets cleaned between sessions. The path must be absolute and shell-clean (letters, digits, `/`, `.`, `_`, `-`); `--dispatch` refuses anything else, because the path is expanded inside the command while the *contents* are not. Write the whole order: the concrete task, which discipline applies (Iron Law, TDD, which file the test goes in), and what must **not** be triggered. A prompt that says only "continue" forces the receiving session to guess its task out of STATE.md, which is the thing handing over a prompt is supposed to prevent. Norwegian prose, `æøå`, quotes, `$` and backticks are all fine in the file. That is measured, not assumed: `"$(cat file)"` hands the bytes to the session as one argv element with no re-evaluation. The ASCII rule applies to shell and hook *code*, not to a prompt travelling through argv. ### 2. Measure whether the target already has a pane ```bash morning --probe-panes | grep "" ``` A hit means `--target-pane yes`. **Measure it; never assume it.** This is the one input `--dispatch` refuses to default, for the same reason `route.sh` refuses to default `--last-effort`: it is a fact about the world, and guessing it produces a dispatch that verifies green and opens nothing. Two facts about this measurement, both verified 2026-08-16 against the installed `morning`: - `--probe-panes` **works from a Claude session**, without a tty. It cannot identify the anchor pane, but the `DIR` column — the part you need — is there. - A session dispatching **its own next session** is always `--target-pane yes`. That is not a special case for one repo; it is what self-dispatch is, and it is where all four of the day's misfires landed. ### 3. Score the four traits and call `--dispatch` Scoring is judgement and it is yours; the model, effort and advisor flag are a lookup and are `route.sh`'s. Score the task **the dispatched session** will do, using the `route` skill's trait table. `--dispatch` deliberately takes no `--model`/`--effort`. `--advisor opus` is a property of the rubric *row* — two rows share a model/effort pair while differing on it, and the CLI accepts a wrong advisor silently — so a dispatch that took the model directly would have no honest source for that flag. If the right call is a Fable row, the rubric cannot produce it: write that command by hand, and say in the handover that it is a recorded override, running without an advisor. ### 4. Verify, then hand it over **`--target-pane no` (plan-file form).** Write the whole output to a file and dry-run it: ```bash "$BOARD" --dispatch ... > "$HOME/.claude/dispatch/-.plan" morning --plan-file "$HOME/.claude/dispatch/-.plan" --dry-run ``` Expect `opening: 1 of 1`. Then hand back: morning --plan-file --no-go **Know what that dry-run does not prove.** It proves the block parses and yields a command. It does *not* answer the pane question: run from a Claude session there is no tty, so `morning` reports `window: unknown ... assuming an empty window` and `plan_drop_open` never fires. A gate built on the dry-run would pass the self-dispatch case every single time — the one case it would exist to catch. Step 2 is the measurement; this is a parse check. **`--target-pane yes` (paste-only form).** There is no plan file, deliberately: `morning`'s `plan_drop_open` (morning:1788) drops a block whose repo already has a pane. Hand back the `paste=` line, and say it goes in the existing tab **after `/exit`**. Never prefix it with `cd` — one repo per terminal tab, and the operator is already standing in that one. `morning --relaunch` bypasses the filter but opens a *second* tab beside the existing one, which is rarely wanted. ## Three things that must reach the operator Say these in the handover, not only in the plan file. Each was a real correction, not a hypothetical: 1. **The command carries the prompt in argv.** `claude --model X --effort Y` on its own is not a dispatch; it is a session waiting for someone to tell it what to do. Delivered bare twice on 2026-08-16, corrected by the operator with "gi meg alltid komplette oppstartskommandoer for nye sesjoner". 2. **`--no-go` does not make the session wait.** It suppresses only the follow-up Go message — `morning:806` is exact: "--no-go says nothing is typed once the startup command is in". The startup command, prompt and all, is typed regardless, so the dispatched session starts working on its own. An operator decision was once taken on the opposite premise and had to be corrected before the run. 3. **Which form you produced, and why.** "Plan file, because `` has no pane" or "paste line, because `` already has one and a plan block for it would be dropped". The form is a consequence of a measurement, and the operator should be able to see the measurement. ## Where a dispatch is not enough The prompt file is scratch: it carries the order to one session and nothing records it afterwards. When the receiving repo needs a durable record of the order — something its next session will still see after this one is gone — send it a `coord-send` message as well. The dispatch starts the session; the mailbox is what makes the order survive it.