feat(dispatch): background form (claude --bg) with the T4 guards - GREEN

The dispatch skill gains a third form beside paste and plan-file: the
PM starts the child itself (operator decision 2026-09-22). Template:
cd <dir> && claude --model <m> --effort <e> --disallowedTools
AskUserQuestion,SendMessage,ListAgents --bg --name <name> "<starter>",
with --model first because pre-dispatch-budget.sh counts only that
position, and model/effort spliced from route.sh's command= so the order
holds by construction. The ASCII starter claims and closes by full path,
says claim KUN denne, pins the branch with git checkout -B, and forbids
push/tag/release. The skill says what follows: watch the order file,
re-measure on the commit, merge in the operator's push round. The
description triggers on the form (EN + NO).

valgt: no bg= line in board.sh, fordi the background form uses none of
the plan-block format (no tab=, no pane, no morning consumer) and its one
generated part already has a single generator in route.sh. §19b pins
that --target-pane still refuses 'bg'. Stated overlap: starter steps
(1)(2)(4) repeat board.sh's thin-starter protocol.

board-selftest 478 -> 493 (§19b: 12 red -> green). Suites under bash
3.2: coord 257, board 493, route 73, orders 116, guard 54 = 993.
npm test 12/12. No version bump: release is the operator's push round.

Order 20260923T070445Z-9974217048-from-.claude.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 09:27:23 +02:00
commit 24adf594bf
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
4 changed files with 135 additions and 8 deletions

View file

@ -12,7 +12,11 @@ description: >-
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
oppgaven", "hvordan starter jeg neste økt på dette". Also covers the
background form, where the dispatching session starts the child itself with
`claude --bg`: "start it in the background", "run that as a background
session", "launch a bg session on this order", "start en bakgrunnsøkt i repo
X", "kjør det i bakgrunnen", "start barnet selv". 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
@ -29,9 +33,12 @@ 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.
**In the paste and plan-file forms you produce the command and 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 one exception is **the background form** (its own section below):
since the operator's decision of 2026-09-22 the PM starts sessions itself with
`claude --bg`, and that form is run, not handed over.
## The engine
@ -181,6 +188,91 @@ a pane. Hand back the `paste=` line, and say it goes in the existing tab **after
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.
## The background form
**When:** the PM starts the child session itself (operator decision
2026-09-22, `~/.claude/docs/2026-09-22-tos-bakgrunnsoekter-og-natt.md`). Not
when the operator is to paste a line into a tab. That is still steps 35
above. The background form replaces steps 35 only. Steps 12 are unchanged:
the order goes into the recipient's queue first, and the id is the handle.
There is no pane question, because a background session has no pane.
The command, run by the dispatching session through its Bash tool:
```bash
cd <absolute repo dir> && claude --model <m> --effort <e> --disallowedTools AskUserQuestion,SendMessage,ListAgents --bg --name <short-name> "<starter>"
```
- **`--model` directly after `claude`, always.**
`~/.claude/hooks/pre-dispatch-budget.sh` counts a start only in that position.
Measured 2026-09-22: `claude --model … --bg --name x` is counted, but a flag
with a value in front of `--model` breaks the pattern and the start goes
uncounted. The form must not depend on the hook learning more forms. Take the
model and effort from `route.sh`'s `command=` line (score the traits as in
step 4). That line already begins `claude --model <m> --effort <e>`, so
appending the rest keeps the order by construction. A Fable override is
written by hand, in the same order.
- **`--disallowedTools AskUserQuestion,SendMessage,ListAgents`** (hardening T4,
`~/.claude/docs/2026-09-22-herding-s1-steg4.md` § 3). A background child
cannot be answered. `AskUserQuestion` was open and stopped two sessions
before this guard existed. `SendMessage`/`ListAgents` close the child→PM
direction. The PM's own messages to the child are still delivered.
- **The `cd` is right here, and only here.** The no-`cd` rule is for a line
pasted into a terminal tab, where the operator already stands in the repo.
This line is run by a session standing in another repo, and `claude --bg`
starts in the working directory, so the `cd` is what picks the repo.
- **`--name`**: a short name (`<repo>-<topic>`), so `claude agents` and
`claude logs <id>` can be read at a glance.
The starter is **one line of ASCII, with no `$`, backtick or double quote**.
It sits inside the double quotes above and is evaluated by the shell, exactly
like `board.sh`'s thin starter. `<PLUGIN_ROOT>` is the absolute install
directory the engine lines at the top of this skill resolve to (the parent of
`dirname "$ORDER"`, e.g. the version-pinned plugin cache). Write it out in
full, because a child executes the starter literally and the order verbs are
not on PATH (ORDRE 65).
```text
ORDRE <id> ligger i dette repoets ordrekoe. Gjor dette i rekkefolge: (1) claim den - bash <PLUGIN_ROOT>/scripts/coord-order-claim.sh <id> - claim skriver ut hele ordreteksten. FULL STI ER MED VILJE. Claim KUN denne ordren. (2) Sammenlign mot STATE.md NESTE og uttal avviket i ditt FORSTE svar. (3) Du er en bakgrunnsoekt isolert i en NY worktree: sett grenen din til <hash> (git checkout -B <gren> <hash>) og verifiser. Commit paa den grenen, ingen fletting, INGEN push/tag/release - operatoren pusher. Du kan ikke stille spoersmaal; ved et teknisk valg, ta det anbefalte og skriv valgt X fordi Y. Lange kjoringer i forgrunnen, aldri arm venter. (4) Lukk med bash <PLUGIN_ROOT>/scripts/coord-order-done.sh <id> --commit HASH - eller --return --reason. Rapport som coord til <sender> via coord-send.
```
- **`Claim KUN denne ordren`** stays in even when the queue holds only this
one. Another order can land between the dispatch and the child's first turn.
- **`<hash>` is the commit the child must build on, not automatically `main`.**
If the repo tracks STATE.md/PLAN.md and the newest of those commits sits on a
branch that has not been merged, the hash is that branch's tip. Measured
wrong 2026-09-23: `d5a6bac` was given where `8bd27d8` was meant.
- **`INGEN push/tag/release`**: the child commits on its branch and stops. The
operator's push round merges, pushes, and runs `release-plugin.mjs`.
**Measured platform facts** (agent-view docs + 2026-09-22): a background
session is forced into its own worktree. It cannot write in the main checkout,
not even after `ExitWorktree`. It never merges. That is why the starter pins
the branch and forbids the merge rather than asking for either.
**What happens afterwards, and it is the PM's:**
1. **Watch the order, not the process.** The child's claim moves the order to
`~/.claude/coord/<repo>/orders/claimed/<id>.md`. Closing moves it to
`orders/archive/<id>.md` (executed), or back to `orders/<id>.md` (returned,
with the reason written into it). `claude agents` / `claude logs <id>` show
the session. The file shows whether the work reached a terminal state.
2. **Re-measure on the commit.** Read the `commit=` the child wrote into the
archived order. Then run that repo's suites at that hash yourself. The
child's report is a claim, not a measurement.
3. **The merge is the operator's.** The branch waits for the operator's push
round, which merges, pushes and releases. A background child never does
any of the three.
**Why this is not a `board.sh` rendering.** `board.sh --dispatch` is the one
generator of the plan-block format (`tab=`/`dir=`/`command=`/`paste=`), and
this form uses none of it. There is no block, no pane and no `morning`
consumer. The one generated part, model and effort, already has its single
generator in `route.sh`. So `--target-pane` stays exactly `yes|no`, and the
selftest pins that. Stated overlap: steps (1), (2) and (4) of the starter
repeat the protocol in `board.sh`'s thin starter. If the protocol changes,
both copies must move together.
## Three things that must reach the operator
Say these in the handover, not only in the plan file. Each was a real