Dispatch agent
Skill davidlee/doctrine/plugins/doctrine/skills/dispatch-agent
bathe your agents in engineering rigour and flames
npx -y skills add davidlee/doctrine --skill dispatch-agentAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
The claude arm of `/dispatch` — spawn a worker via the `Agent` tool using the dispatch-worker subagent type with worktree isolation. Base is explicit — `dispatch arm-spawn --base B` writes the base file, then cd into the spawn dir before the Agent spawn so the WorktreeCreate hook forks at B. Worker self-commits via the gated `worker_commit` MCP tool; the orchestrator then does whatever `doctrine dispatch next` prescribes, one action at a time. Reached only from the `/dispatch` router on a claude↔env-marker agreement; do not invoke directly.
SKILL.md
13.3 KB, as published. Nobody here has run it
Dispatch — claude arm
Spawn a worker via the Agent tool. The harness-identical funnel and drive loop
live in the /dispatch router — this skill is the spawn
template plus this arm's landing mechanics.
Pre-spawn — arm the base, cd into the spawn dir
The worker's worktree is created by doctrine's WorktreeCreate hook
(doctrine worktree create-fork), not natively by the harness. The hook
discriminates a dispatch worker positionally: a spawn is a dispatch worker iff
the Agent payload cwd is the arming dir
<coord>/.doctrine/state/dispatch/spawn/. The base it forks is explicit — the
base file in that dir — never cwd HEAD. cwd is the discriminator, not the base
source.
Arming does double duty: the hook also provisions the worker's
DispatchRecord — the registration worker_commit resolves the worker's
opaque agent id against. An unarmed spawn is a benign pass-through worktree with
no record, so its worker_commit refuses unknown-agent and it cannot land
a fork commit at all.
Base-clean beat (before arm-spawn). Assert the base is prove-clean —
doctrine check prove (NON-mutating fmt-check + lint) — before arming/spawning,
and on main before you branch the fork. A RED base is a BASE defect (operator
format-and-commit / prep worktree), NEVER folded into a worker delta, NEVER
auto-fixed. This is the same pre-spawn beat the funnel documents; run it once per
batch.
Before every spawn (or parallel batch):
doctrine dispatch arm-spawn --base <B> [--slice <N>]— writes<coord>/.doctrine/state/dispatch/spawn/base = <B>and prints the spawn dir's absolute path. Idempotent: re-arming at B′ rewritesbase.cdinto that spawn dir. This is the arming signal — the hook forks at B only when the payload cwd is the spawn dir.- Issue the Agent spawn(s) (below). File-disjoint parallel batch: arm once, then
issue N spawns from the spawn dir — all read the same B; each hook derives its
own
branch/dirfrom its ownname. cdback to the coord root after the spawn(s) — positional disarm. A benignisolation:worktreespawn issued from the coord root passes through (provisioned, not worker-forked); only a spawn from inside the spawn dir is a dispatch worker. A lingeringbasefile is inert — the trigger is cwd-position, not file-presence.
Serial drive: re-arm each phase — arm at B, cd in, spawn, cd back; the funnel commit advances coord HEAD; arm at B′ for the next phase. Base is explicit, so coord-HEAD drift between arm and create is irrelevant. Default cwd is the coord root; step into the spawn dir only to issue worker spawns.
Placement precondition (ISS-031): the coordination worktree (and so its arming
dir) MUST live inside the project root — convention .dispatch/SL-<n> (dispatch setup --dir .dispatch/SL-<n>). Under a cwd-confining jail (bubblewrap rooted at
/workspace/<repo>), a cd to an outside sibling silently reverts to the project
root on the next Bash call. dispatch setup fails closed on the claude arm when
--dir resolves outside the root. Confirm placement with a bare
cd <spawn-dir> ; pwd in a separate Bash call before trusting it.
Spawn
BASE GUARD — run FIRST, before any read/edit/commit. STOP and write nothing if any check fails:
- git status --porcelain → MUST be empty (clean tree)
- git rev-parse --git-dir vs --git-common-dir → MUST differ (isolated linked worktree, not main tree)
- git merge-base --is-ancestor <B> HEAD → MUST exit 0 (HEAD descends from base <B>)
- grep prerequisite seams: <seams> → MUST be present On any failure: STOP, author/commit nothing, report "base-guard-failed: <check>".
Check #2 is the in-worker mirror of the orchestrator's not-isolated belt —
if the worker's git-dir equals git-common-dir, it is not an isolated worktree.
subagent_type: dispatch-worker
isolation: worktree
prompt: <pre-distilled worker prompt, including the base-guard block above>
The prompt MUST instruct the self-commit: finish by calling the
worker_commit MCP tool with the worker's own worktree NAME (never a path)
plus the commit message, and report the returned oid (fork_tip) in the
structured hand-back. Raw git commit fails in the jail (ro .git) —
worker_commit is the worker's only commit path. On a Refused outcome the
worker reports the reason verbatim and stops; it never retries around a
forbidden-zone/commit-gate-red refusal.
Post-spawn funnel — worker self-commit, orchestrator lands
Primary (B): the worker's delta arrives as ONE committed non-merge commit C
(C^ == B) on its own dispatch/<agent> branch, landed server-side by the
gated worker_commit tool (belts are the security boundary: non-empty delta →
forbidden-zone scope → HEAD == B → the check commit gate). The delta is
therefore fork-durable — it survives the worktree, and a revive/fixup resumes
from the committed tip.
Ordering is the oracle's job, not yours. doctrine dispatch next --slice <N>
prescribes the one action due; the router documents the loop. What follows is the
mechanics of each prescription on this arm, not a sequence to hold.
Arrival (before the funnel can see the delta at all).
- Footer. Read the Agent return footer for
worktreePath:. NO footer / noworktreePath:⇒ no isolated tree was created (hook abort or fallback-to-main) ⇒ ABORT, do NOT enter the funnel. Re-dispatch, or switch to the subprocess arm if the hook is failing. - Identity. Derive from
worktreePath(the normative datum, live-proven):name = basename(worktreePath),branch = dispatch/<name>. Do NOT read the footer'sworktreeBranchfield — it isundefinedfor the hook-created tree. - Verify the worker.
doctrine worktree verify-worker --base <B> --dir <worktreePath> --branch <branch>Abort on any refusal: no-worker-head / not-isolated / unstamped / wrong-base / branch-mismatch. It accepts the post-commit HEAD (testsmerge-base --is-ancestor B HEAD, a descendant — notHEAD == B);--branchbinds dir↔branch so both belts verify ONE worker state.
import — dispatch_import{slice, name: <branch>} (MCP; next renders the
call). Resolves the coord tree server-side by slice, runs the classify_import
scope belt as a HARD pre-compose gate (.doctrine//.claude/ reject,
undeclared-scope reject — nothing lands on a refusal), composes coord-tip ⊕
worker-tip via merge-tree (object-db only, working-tree-free), and lands ONE
non-merge commit preserving the worker AUTHOR. Import and commit are folded —
no separate orchestrator commit step. Any Refused{reason} ⇒ report-and-halt
(merge-conflict, head-moved, undeclared-scope, … are never auto-resolved).
The delta is already commit-gate-green (worker_commit ran the gate), so no
separate post-import prove run on this path.
verify — doctrine dispatch verify --slice <N> --phase PHASE-NN (CLI).
Runs the phase suite in the coordination worktree and lands pass/fail evidence;
red evidence is what makes the next prescription triage-verify-failure.
conclude — dispatch_conclude_phase{slice, phase, code_start, code_end}
(MCP; next renders it fully parameterised from the funnel row). One call, two
tiers: flips the gitignored phase sheet to completed AND lands the committed
(code_start, code_end) boundary row (UPSERT-by-phase, working-tree-free). The
flip reaches BOTH the coord sheet (next-ready authority) and the PRIMARY sheet
(the tree prepare-review's completeness gate reads its completed-set from) — no
manual slice phase --status completed -p <primary> re-flip is needed. That
coord→primary mirror lives in the single writer set_phase_status (ISS-212), so
it is not conclude-specific: any completed flip in the coord tree under a live
dispatch/<slice> worktree mirrors down, whether it rode this tool or a raw
slice phase --status. Idempotent on retry; the only fault outcome is a flipped
sheet with no committed boundary, which a retry re-composes.
reap — dispatch_reap{slice, name: <branch>} (MCP). Runs the patch-id
landed-oracle (git cherry): it REFUSES a fork whose patch is not in coord
history, then removes the landed fork's worktree + branch. Reap ONLY through this
oracle — never a raw git worktree remove --force against an unimported tree
(that deletes the sole copy of an unlanded delta; the tool's refusal is the guard
a raw remove lacks).
Fallback (A) — live-worktree import
The in-a-pinch path when the primary cannot run: the MCP server is down, or the
worker was never provisioned (worker_commit → unknown-agent) so no fork
commit exists. The worker's uncommitted delta still lives in the worktree,
which persists after the Agent returns (no WorktreeRemove hook — the
harness does not auto-reap). NOT a bypass: a forbidden-zone /
commit-gate-red / undeclared-scope refusal is a worker-delta defect — fix up
or halt, never import around it.
verify-workeras above (HEAD == B in this mode — no commit).doctrine worktree import --base <B> --from-worktree <worktreePath> --slice <N>— gathers the live tracked+untracked delta, runs the sameclassify_importbelt, applies ontoBNON-committing, then runs the reject-and-halt prove gate in-process (an unformatted/lint-red delta HALTS staged, never auto-fixed).- Commit ONE on the coordination branch yourself, then record the boundary:
doctrine dispatch record-boundary --slice <N> --phase PHASE-NN --code-start <B> --code-end <B+1>. - Reap gated on the landed delta — on this path the tree is the sole copy until
the coord commit lands;
&&-chain the removal behind it.
This is also the pi/subprocess arm's only mode — a bwrap-confined worker
cannot commit at all (/dispatch-subprocess). Never instruct or expect
self-commit off the claude arm.
Worker confinement (SL-182)
The claude worker is confined by two installed PreToolUse hooks (plugin
hooks.json → doctrine worktree pretooluse): Bash is opaquely rewritten
into a nested bwrap jail (rw the worktree, ro everything else); Edit|Write
outside the worktree is denied. The orchestrator (no agent_id) passes through.
Confinement is by construction — no cooperative worker flag. worker_commit is
the deliberate, single-purpose bypass of that wall: the unconfined server
commits on the worker's behalf, and the tool's belts — not the wall — are the
security boundary there.
- No hot-reload. Plugin
hooks/changes load at session start only (docs/claude/plugins-reference.md:394). Afterdoctrine install(or any hooks edit), pick them up with/reload-plugins(lighter) or a session restart — otherwise the OLD hooks (or none) are live. - Fail-closed install. The installed command is the resolved absolute
exec with a
|| exit 2guard, so a missing/stale binary denies rather than running the tool unconfined (PreToolUse otherwise fails open — only exit 2 blocks). - Escape hatch. A broken Bash wrapper can lock you out of Bash, but Edit/Write
are not Bash-gated: disable the offending hook via Edit, then
/reload-plugins.
Red Flags
Never: spawn without first arm-spawn-ing and cd'ing INTO the spawn dir (a
spawn from the coord root is a benign pass-through, never a dispatch worker —
and its worker_commit refuses unknown-agent); read the footer's
worktreeBranch (undefined for the hook-created tree — derive
branch = dispatch/basename(worktreePath) instead); funnel a worker that
returned no worktreePath: footer; import the live tree when a fork commit
exists (the delta is the commit — land it via dispatch_import); use fallback
(A) to route around a worker_commit/dispatch_import refusal (a refusal is a
defect or a halt, never a detour); raw git worktree remove --force an
unlanded tree (reap through dispatch_reap's patch-id oracle); claim
self-commit on the pi arm; point dispatch setup --dir at an outside-root
sibling (ISS-031); spawn with a subagent_type other than dispatch-worker;
run fork or bwrap here (that's /dispatch-subprocess); claim parallel landing
(v1 lands one per base); hand-sequence the landing verbs from memory or work the
funnel state out of git by hand (ask doctrine dispatch next --slice <N>).
Always: arm-spawn --base B then cd into the spawn dir before the spawn, cd
back to the coord root after; pin subagent_type to dispatch-worker; embed
the base-guard block AND the worker_commit self-commit instruction in the
distilled worker prompt; derive branch from worktreePath; run verify-worker
before landing; do the ONE thing dispatch next prescribes and ask again; return
to the router for the drive cadence.