| name | dispatch |
| description | Triage the GitHub issue queue and fan it out to baton sessions โ pull open issues with a queue label (default dispatch:queue), propose a grouping plan, get the user's confirmation, then create one session per group with a self-contained brief and mark the issues as taken. Use when the user says "/dispatch", "ๅฎๆไปปๅก", "ๆดพๅ", "ๆ้ๅ้็ issue ๆดพไธไธ", "ๆ issue ๅฎๆไธไธ", or asks to plan/distribute the pending GitHub issues.
|
dispatch โ triage the issue queue, fan out sessions
You are the dispatcher, not the executor. One run = pull โ plan โ confirm
with the user โ create sessions โ mark issues โ report links โ done. You
never fix the issues yourself, and you never wait for the spawned sessions to
finish. Everything goes through gh (current repo) and the baton CLI (both
on PATH; baton auth comes from the cwd .baton.json).
Flow
(defn dispatch [msg] ; "/dispatch [label] [extra instructions]"
;; 1. parse โ label defaults to the queue; free text may pin a worker
;; ("/dispatch bug ้ฝ็ป W-9" โ label=bug, default-worker=W-9)
(def label (or (:label msg) "dispatch:queue"))
;; 2. pull the queue from THIS session's repo
(def issues (gh issue list --label label --state open --limit 100
--json number,title,body,url,labels,assignees))
;; 3. drop issues already in flight โ any state:* label means a previous
;; dispatch (or the relay flow) owns it. Empty queue โ say so, stop.
(def pending (remove #(some (fn [l] (str/starts-with? l "state:")) (:labels %)) issues))
(when (empty? pending) (reply "queue is empty โ nothing to dispatch") (stop))
;; 4. plan โ read title+body, group by affinity: small same-module/same-kind
;; issues merge into one group; a big task stands alone. Per group:
;; session-name slug, issues, target worker (default: THIS worker),
;; one-line approach.
(def plan (group-by-affinity pending))
;; 5. CONFIRM GATE (hard rule): present the plan as a table
;; (group | issues | worker | approach) and STOP โ end the turn, wait
;; for the user. They may merge/split/drop groups or retarget workers.
;; Never dispatch without an explicit go-ahead.
(reply (render-plan plan) "confirm and I dispatch; ask for any adjustment")
;; ===== next turn, after the user confirms (possibly with edits) =====
;; 6. dispatch each group โ one failure must not block the others
(doseq [g plan]
;; same worker: omit --worker; other worker: the delegate skill's flow
(def s (baton session create "<g.slug>" [--worker W-<n>] --json))
;; wait for attached โ the server 409s messages until the child
;; subscribes; cold spawn takes ~10-30s, give up after ~60s:
;; until baton session get <s.id> --project <s.projectId> --json \
;; | grep -q '"attached": true'; do sleep 5; done
(baton session send <s.id> "<brief>" --project <s.projectId>)
;; mark each issue taken: lifecycle label + traceable link
(doseq [i (:issues g)]
(gh issue edit (:number i) --add-label "state:in-progress")
(gh issue comment (:number i)
--body (str "dispatched โ https://baton.fmap.dev/s/" (:shareToken s)))))
;; 7. report โ one line per group (name, issues, session share link);
;; failed groups listed separately with the reason. Then end the turn.
(reply (render-report plan)))
Commands (exact โ don't guess variants)
gh issue list --label "<label>" --state open --limit 100 \
--json number,title,body,url,labels,assignees
baton worker ls --project <pid> --json
baton session create "<slug>" [--worker W-N] --json
baton session get <id> --project <pid> --json
baton session send <id> "<brief>" --project <pid>
gh issue edit <n> --add-label "state:in-progress"
gh issue comment <n> --body "dispatched โ <share-link>"
The brief
The target session cold-starts with zero context โ the brief is everything it
gets (delegate skill's iron rule). Include, per group:
- goal: what done looks like for this group, in one paragraph
- per issue:
#N / title / URL / the body's key points (don't make the
target re-derive them)
- constraints the user stated during confirmation
- acceptance: work the issues per the github-issues skill โ
state:in-progress
while working, close via its two-step verification flow
(close โ state:needs-verification โ creator marks state:verified)
Discipline
- Never skip the confirm gate. Plan and dispatch are separate turns; an
unconfirmed plan is just a proposal.
- Don't execute the issues yourself โ even a "trivial" one gets a session.
Dispatcher and executor stay separate so the queue drains predictably.
- Idempotent by labels: anything carrying
state:* is already owned โ
re-running /dispatch must not double-assign. The comment with the session
link is the audit trail.
- Fire-and-forget: after sending briefs, reply with links and end. Progress
questions later โ
baton session get (the busy field), or the issue's
state labels.
- Offline workers don't take jobs: a session on an
alive: false worker
never attaches โ surface it and let the user retarget, don't pretend.
- One-time setup if the queue label is missing:
gh label create dispatch:queue --color 5319e7 --description "queued for /dispatch".
- Boundaries: GitHub issues are the source and the state machine
(github-issues skill owns execution-side conventions); baton R/T records
(baton skill) are a separate track โ don't create R/T rows here.