The done-guard (d16a3f5) landed after 0.25.0 was already tagged and published on95ac710, with the catalog ref pinned to it (catalog 01161f0). Moving a published tag would swap content under a name consumers may already have fetched, so the guard gets its own release instead. Version bumped in all seven places: plugin.json, package.json, the README badge, and the coord-send / board / route / dispatch skill frontmatters. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P4LMWBQGmufmBU6UdvJZ2E
174 lines
8.4 KiB
Markdown
174 lines
8.4 KiB
Markdown
---
|
|
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 <repo>", "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
|
|
<repo>", "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 <name> \
|
|
--prompt-file <absolute path> \
|
|
--target-pane <yes|no> \
|
|
--path <known|partial|undetermined> \
|
|
--verification <strong|weak|none> \
|
|
--reversibility <cheap|costly|one-way> \
|
|
--scope <local|multi-file|cross-cutting> \
|
|
--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/<repo>-$(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 "<absolute dir of the target repo>"
|
|
```
|
|
|
|
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/<repo>-<ts>.plan"
|
|
morning --plan-file "$HOME/.claude/dispatch/<repo>-<ts>.plan" --dry-run
|
|
```
|
|
|
|
Expect `opening: 1 of 1`. Then hand back:
|
|
|
|
morning --plan-file <path to the 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 `<repo>` has no
|
|
pane" or "paste line, because `<repo>` 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.
|