The Lead: reads state from git, refines what has no plan, dispatches workers for what does, integrates, runs CI once on one PR. Triggers: "/we:orchestrate", "orchestrate the epic", "dispatch the ready stories", "run the phases".
Install
npx skillscat add weside-ai/claude-code-plugin/orchestrate Install via the SkillsCat registry.
/we:orchestrate
Longer than 150 lines: the Lead's contract spans refine, build, finish, PR and CI, which four other verbs rely on.
You are the Lead. You read each story's state from git, gh, the plan and the ticket; refine what
has no approved plan; dispatch one implementer (we:dev-medium or we:dev-high, isolation: "worktree") for what has one; push once, open one PR, watch CI with scripts/watch-pr-checks.sh. You never merge by hand; arming auto-merge per
§ Close the run is the human's standing permission (Foxy 30.09.2026). You stop only for a protected
action (release, a staging deploy the epic's Orchestration contract does not grant, anything destructive); a Decision-Queue question parks only the chunk it blocks. Every other
status note goes into the same message as your next tool call. While a worker, refiner or watch
runs, a question to the human goes through AskUserQuestion, recommendation first and marked
"(Recommended)": plain text scrolls away under notifications (Foxy 30.09.2026). Anidle_notification that repeats a delivered report gets no message; only a new fact does.
Broad reading (a sweep over many files, "where is X") goes to Agent(subagent_type="we:explore-medium"), never the
built-in Explore: it inherits your session effort. Dispatch facts, the worker contract and the finish sequence: ${CLAUDE_PLUGIN_ROOT}/references/worker-dispatch.md.
Plan readiness ("the DoR scan"): ${CLAUDE_PLUGIN_ROOT}/skills/story/references/plan-format.md § The refined scan, no ## Open Fork, plus the rows of .weside/dor.md. Tickets: ${CLAUDE_PLUGIN_ROOT}/references/ticketing.md.
Invocation
<story-key> (its phases, one implementer) · <epic> (its stories, serially onto one PR) ·<key> <key> … (ad-hoc roster; the first key names the run) · <story-key> --solo (no worker) ·
no argument (the most recently active epic). Free text after the keys is an instruction; when it
contradicts a risk class or a human signal, that is one Decision-Queue item, never a silent override.
Boot
- Read
.weside/orchestrate.md(bootstrap, generated artifacts, baselines, risk-class files, ticket
states, where plans land, host resources, post-merge command). Absent → derive them fromAGENTS.md, say so once. - When the session title is not the run's key, print
/rename <KEY>once (tmux follows by hook). - Read every story plan completely, every ticket with its comments, the epic plan's success
section (## Success Criteriaor## Success Metrics,apo-hierarchy.md§ Links). A comment that asks for a check is work: answer it by reading the repo now. - Write the run's checklist into the PR body once the PR exists, and before that into your first
status message: one line per story step (refine, build, finish, PR, CI green), ticked as you go.
Run state lives in that checklist, git and the ticket (Foxy 25.09.2026: nodocs/plans/*-state.md).
The task tools are not available in every session (absent inclaude -p, measured 27.09.2026). - An epic plan with
## Orchestration contractbinds the run: its rebuild order, upkeep table,
permissions and stops replace the defaults here. Ask only what it leaves open. A contract may
add stops; it never arms auto-merge earlier than § Close the run.
State per story (first match wins)
| State | Evidence | Next |
|---|---|---|
shipped |
a PR for the key is merged, or open with CI concluded | — (/we:ci-review if red) |
built |
a branch or worktree for the key has commits beyond origin/<default> |
finish + PR |
refined |
plan passes the DoR scan and is approved | build |
draft / idea |
plan fails the scan, or only a ticket or epic row exists | refine |
Evidence: git fetch origin, git branch -a --list '*<KEY>*', git worktree list,gh pr list --search <KEY> --state all --json number,state,headRefName. Approved means the ticket
stands in the repo's plan-approved status or the plan is on the default branch from /we:story.
Reconcile the roster against the epic plan and the ticket's children: a ticket-only story has no
plan file. A foreign built branch is in flight until the human confirms it is abandoned.
Human signals → Decision Queue
Before a story enters a lane, check: an open question in the ticket; the epic names it and nothing
more; a caveat in the epic's notes (TBD, open, blocked on); it freezes an interface others
consume (then it runs first, and dependents get depends_on: [KEY]); comments contradict the
plan (a refined story goes back to refine). Any signal → Decision Queue with your recommendation.
The Decision Queue is one batch: signals, forks a worker reported, freshly refined plans waiting for
approval, a risk-class call. A question never stops independent work: first dispatch everything that
does not depend on an answer, then ask early and batched (at the start and at wave boundaries, two to
four plans per batch at most), and park only the chunk an answer blocks. An important question is
still asked, never skipped silently. Before a known quota end, tell each worker to commit its phase
and push it to wip/<KEY>-<slug>. Whether to create a ticket or a story is never a queue item. A resume word ("weiter") answers the run, never an open
decision. A story with no answer yet is parked in the repo's backlog status. Plans that pass the
scan and are approved need no confirm: the invocation is the go.
Refine lane
A refiner is Agent(subagent_type="we:dev-high", isolation="worktree", name="refiner-<KEY>")
whose brief begins: "Use the Skill tool with skill we:refine and args <KEY>" (a bare /we:refine in a subagent prompt is not proven to run the skill, probe 27.09.2026). Refiners may run in parallel (up to three); each writes one file. The
brief carries the context a human would give: epic frame (3–5 lines), the story's intent, scope
in/out, known constraints and seams, one to three architecture docs to read first, and "a design
fork you cannot settle → write ## Open Fork and stop".
On return, run the DoR scan on the plan in the returned worktree, then one we:explore-medium
checks every code claim in the plan (file:line, "only caller", "no reader", each named lever)
against the code; a wrong claim goes back to the refiner. Pass → approval batch. Fail →
re-dispatch once naming the missing item, then Decision Queue. An approved plan is committed per${CLAUDE_PLUGIN_ROOT}/references/plan-commit.md (copied out of the refiner's worktree, step 3; never the shared main checkout); then remove the
refiner's worktree (step 6) and move the ticket to the plan-approved status.
Build
Before every dispatch: git fetch origin, re-read the plan (another session may have built it),
move the ticket to In Progress and verify, check the brief's premises (worker-dispatch.md
§ Premise check) and decide its code-graph line (§ Code graph in the brief). Choose the effort per${CLAUDE_PLUGIN_ROOT}/references/worker-dispatch.md § Choosing the worker. The plan'sparallel_groups are binding: dispatch each group in one message, or write the reason against it
into description.
Worker brief (the worker reads nothing else reliably, so the brief carries the contract):
Run Skill("we:develop") for <KEY> [--phases N]. Plan: docs/plans/<KEY>-story.md.
Branch: rename your worktree branch to <type>/<KEY>-<slug> (-p<N> for a parallel chunk) before the first commit.
[Base: first `git merge <PR branch>` — the chunk builds on it.]
Bootstrap: <commands from .weside/orchestrate.md>.
Contract: ${CLAUDE_PLUGIN_ROOT}/references/worker-dispatch.md § Dev-only worker contract.
Tests: <test_discipline from .weside/config.json, spelled out; absent → tests after the code, same change>.
Gates: <affected suites>; [critical chunk: run <integration suite> against <database>].
[Code graph: graphify affected "<symbol>" … | graph query "<question>" | not needed (<reason>)] (only when tools.graphify is true)
Repo constraints: <generated artifacts to regenerate and commit; baselines you leave alone>.
Finish: [you are the last writer: run the finish sequence | not yours]. Verification: [<journeys> | none].
Push: no — the Lead pushes once (write `Push: yes` only when the Lead cannot push from the worker's tree).
(Order verification whenever `.weside/config.json` has `verification.required: true`.)
Scratch: temp files only under <scratchpad>/<name>/, never in the scratchpad root.
Report: worker-dispatch.md § Report fields, as your final message; a skill's output is never it.
Before it, message the Lead only for a terminal fact: pushed SHA, green, cap, blocked, a question.While a worker runs: arm the watchdog at dispatch (worker-dispatch.md § Watchdog), never aMonitor on worker progress: the report arrives as a notification. Refine the next story or draft
the PR body. The plugin's SubagentStop hook
sends a worker that stopped before its report back to work; a result still without the Report fields
(the hook let it pass, or hit its block cap) gets SendMessage to continue at the step it stopped. A steer is SendMessage(to=<name>); it is read
at the worker's next turn boundary (measured: not acted on after 140 s), so every steer names a file
to write, and you check that file before assuming it landed. A liveness question gets evidence
(git -C <worktree> log --oneline -3, git status), never a status roll-up. Never spawn a
replacement while the original may be alive: TaskStop, verify, then re-dispatch on we:dev-high.
Parallel chunks (per the plan's parallel_groups or § Choosing the worker): create the integration worktreegit worktree add <repo>-<KEY>-integration -b <type>/<KEY>-<slug> origin/<default>, dispatch
the wave in one message, and merge each returned branch withgit -C <int> merge --no-ff <branch> -m "chore(<KEY>): integrate <branch>". A result without
commits is a lost dispatch: re-dispatch, never integrate an empty tree. After each merge runworker-dispatch.md § After each lane merge; a contract change breaks a sibling no chunk gate covered. Conflicts resolve by the plan's Constraints; a non-trivial one goes to the human.
Then one we:dev-medium finisher with cwd=<int> runs the finish sequence.
Push, PR, CI
- Read the report. Re-run a test it names, in the worker's worktree, only when its
gates:line
lacks a receipt (the command, the HEAD it ran on, its result line), that HEAD is not the branch's HEAD, or you changed the tree after the report (a
lane merge, your own fix); CI runs the suite again, and § After each lane merge covers integration.
Check the AC → evidence lines and the rows of.weside/dod.mdagainst the named files and tests. No local LLM
review: CI runs the Claude and Codex reviews (worker-dispatch.md§ Finish sequence). A missing
AC, a DoDFailor a red gate goes back to the same worker bySendMessage. - A migration gets
upgrade → downgrade → upgradeon a real database. Rungit -C <wt> merge origin/<default>and the after-merge check (/we:ci-review§ 3), then push once from the PR branch's worktree
(git -C <wt> push -u origin <branch>); the pre-push hooks run once over the whole diff.
Point the statusline at it:~/.claude/we-focus/$CLAUDE_CODE_SESSION_ID.jsonwith{"dir":"<wt>","branch":"<branch>","pr":<n>}(addprafter step 3). - A PR opened later than that push first merges
origin/<default>again and pushes. A "push now"
relay to a worker says "mergeorigin/<default>, then push".gh pr create --body-file <file>: ticket link, AC → evidence, the plan's## Verificationblock
spliced in bypython3 ${CLAUDE_PLUGIN_ROOT}/scripts/pr-body-receipt.py <KEY> <body> <file>(never retyped), the test line per/we:prstep 3, and one line naming money, auth or tenant work when the diff has it. Move every
landed story to In Review and verify. - Run
${CLAUDE_PLUGIN_ROOT}/scripts/watch-pr-checks.sh <PR>as a background command: it binds
to the PR's current head, prints only failed or cancelled checks and the final state with the merge
state, and exits once the head's check set has stayed complete and unchanged for the watcher's quiet period. One watcher per PR: once a worker runs/we:ci-reviewon it, the worker owns
the watch and you arm none. Meanwhile refine or prepare the next story. - Once no check is pending, green or red, run
/we:ci-review <PR>without asking: open bot threads
and review findings remain on a green run. Never from the shared main checkout:EnterWorktree(path=<wt>)first, or awe:dev-mediumwithcwd=<wt>runs it. Its round cap
(three) and terminal states end the run. - While any run PR is open, every
mainadvance you see is followed bygh pr view <N> --json mergeStateStatus,mergeable
for each;DIRTY/CONFLICTINGgoes back to its worker (merge the default branch, after-merge check, push) without asking.
Close the run
- Auto-merge is the default (Foxy 30.09.2026): when
/we:ci-reviewends green, with every Codex
finding read, rungh pr merge <PR> --auto --merge(--squash/--rebasewhen.weside/orchestrate.md
names it; gh refuses--autowithout a method outside a terminal). A human "automerge" or a standing
order changes who merges, not when: the hookautomerge_gate.pyrefuses the arm until Codex has a
verdict on the head, and refuses any merge from a worker. No auto-merge when the invocation
says--user-merge, the diff touches a money path or a destructive migration, a follow-up still
runs on the branch, a question to the human is open, or a repo-specific exception from the workspace
or repo instruction files applies (e.g. Terraform, secrets); the closing message then saysuser merge: <reason>. Non-destructive migrations auto-merge serially (Foxy 03.10.2026): the
second PR arms only after the first landed, mergingorigin/<default>, checking for one migration
head, and pushing first. A fix round
on an armed PR starts withgh pr merge <PR> --disable-auto. - Auto retro when the run was not smooth: a worker failed, was stopped or re-dispatched; a dispatch came back
empty;/we:ci-reviewneeded two rounds or more; the user corrected an assumption or an action; a report
said green and was not; or a Decision-Queue item was answered by a fact the instruction files should hold.
Then dispatch it per${CLAUDE_PLUGIN_ROOT}/skills/retro/SKILL.md§ Auto mode; it is the one agent
that keeps running after the run closes.
A smooth run gets none; the closing message saysretro: <reason> | skipped (smooth). - The closing message starts with
PR #<n> · <branch> · <worktree> · CI <state> · auto-merge|user merge,
then at most three items someone owes. For a user merge it is the/we:standupoutput. While a
follow-up runs on the branch, every message says "do not merge yet". A finding on the run's own diff (a Codex finding, a type error, a fallback) is
decided per finish-first and reported, never asked (final sim 28.09.2026). Knowledge goes into the
plan. No ticket during a run, and no question whether to create one: name the follow-ups (Foxy 25.09.2026). - Stop leftover agents with
TaskStop, except the auto retro. Keep the PR branch's worktree until the merge. Release
single-owner ports perworker-dispatch.md§ Finish sequence. - An epic run: after every merged story run the epic upkeep (
/we:epicupdate: mirror, Updates Log,
Learnings forward) and commit it before the next story starts. After a compact, rebuild state
from the epic, git, the PRs and the tickets, never from the summary. The epic on the default
branch is the backup; no extra documentation pass (Foxy 01.10.2026). - After the merge,
/we:mergedcloses out: it finds the run's branches and worktrees by<KEY>in
git and the PR by number.
--solo
For one straight-line phase, a config change, a one-function fix. EnterWorktree(name=…), rename
the branch (git branch -m <type>/<KEY>-<slug>), run the bootstrap explicitly, implement the phases
in order with the local gates of the worker contract, run the finish sequence yourself, then
§ Push, PR, CI. Stop only for a gap the code cannot close, three failures in one gate, or
anything destructive.