Skip to content

Commit 46c38bb

Browse files
committed
Overhaul dispatch skill
Rewrite dispatch around spawn_agent and wait_agents, named directors, and the git-worktrees background skill. Remove fake fan-out caps and pin the runtime agent_id flow in catalog coverage. Closes CL-7017
1 parent 9fadfe5 commit 46c38bb

2 files changed

Lines changed: 76 additions & 65 deletions

File tree

plugins/corbits-skills/skills/dispatch/SKILL.md

Lines changed: 61 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,82 @@
22
name: dispatch
33
user-invocable: false
44
argument-hint: "[<name> | dispatch/<name>/ | dispatch/<name>/dispatch.yaml | <spec-file> ]"
5-
description: Multi-lane DAG orchestration. Skywalker recipe — use_skill("dispatch"). Spawns explorer, intern, builder, counsel, and critic. DAG product tasks go through builder; Skywalker may DIY tiny edits outside the DAG.
5+
description: Multi-lane DAG orchestration. Skywalker recipe — use_skill("dispatch"). Spawns explorer, intern, builder, counsel, critic (and specialists like rand). DAG product tasks go through builder; Skywalker may DIY tiny edits outside the DAG.
66
---
77

88
# Dispatch
99

10-
You are Skywalker. This skill is loadable with `use_skill("dispatch")`. Follow this recipe. DAG product tasks go through builder workers. Do not write `dispatch.yaml` or `plan.md` yourself (intern cannot write; builder writes manifests). Tiny / single-file / one-route product edits outside this DAG may be DIY with write_file/edit_file/delete_file.
10+
You are Skywalker. Load with `use_skill("dispatch")`. Orchestrate parallel director runs across a dependency graph: classify lanes, spawn independent work together, wait/synthesize, critique, verify, re-dispatch fixes, report.
1111

12-
Orchestrate parallel director runs across a dependency graph. Fan out work, fan in reports, critique, verify, re-dispatch fixes, and synthesize until done.
12+
DAG product tasks go through **builder**. Do not write `dispatch.yaml` or `plan.md` yourself (intern cannot write; builder writes manifests). Tiny / single-file / one-route product edits outside this DAG may be DIY with write_file/edit_file/delete_file.
1313

14-
Default batch size: **4 live workers** (recipe default — not a hard runtime cap). Widen when lanes are named and non-overlapping, or when the operator asks. Track progress with `manage_tasks`.
14+
**Fleet default:** `spawn_agent` then `wait_agents` — fire independent workers in one turn, wait on the reports you need next. Prefer `task()` only when a single blocking spawn is enough. No worker-count or fan-out ceiling — width follows named, non-overlapping lanes (distinct path/package/ownership). Track progress with `manage_tasks`.
1515

16-
Closed directors used here: `explorer`, `intern`, `builder`, `counsel`, `critic`. Optional consults: `greybeard`, `tester`. Never a catch-all worker. DAG node agents are `explorer`, `intern`, and `builder` only.
16+
Closed directors used here: `explorer`, `intern`, `builder`, `counsel`, `critic`. Optional: `greybeard`, `tester`, `rand` (DESIGN.md / brand). Never a catch-all worker. DAG node agents are `explorer`, `intern`, and `builder` only.
1717

1818
## Input resolution
1919

20-
Figure out what to run from the argument:
21-
22-
- **No argument** → latest dispatch (newest `dispatch/<name>/` directory by creation time)
20+
- **No argument** → latest dispatch (newest `dispatch/<name>/` by creation time)
2321
- **Just a name** (e.g. `auth-fix`) → `dispatch/<name>/dispatch.yaml`
24-
- **Directory** (e.g. `dispatch/auth-fix/`) `dispatch.yaml` inside
22+
- **Directory**`dispatch.yaml` inside
2523
- **File ending in `dispatch.yaml`** → run it
26-
- **Any other file** → treat as a spec. If it still needs an eng plan, spawn `task(agent="counsel")` first, then run.
24+
- **Any other file** → treat as a spec. If it still needs an eng plan, spawn counsel first, then run.
2725

28-
If the spec is vague, incomplete, or contradictory: stop and report Blockers. Do not invent a DAG.
26+
Vague / incomplete / contradictory spec → stop and report Blockers. Do not invent a DAG.
2927

3028
## Who does what
3129

32-
| Work | Director |
33-
| ------------------------------------------------------------------------------------------------ | ------------------------- |
34-
| Map the codebase, gather facts | `task(agent="explorer")` |
35-
| Eng plan from a spec (no ship) | `task(agent="counsel")` |
36-
| Write `dispatch.yaml` / `plan.md` / status artifacts (mechanical brief; no product feature work) | `task(agent="builder")` |
37-
| Ship product code + tests | `task(agent="builder")` |
38-
| Review a landed task (defects, evidence, no fix) | `task(agent="critic")` |
39-
| Architecture judgment before a large DAG | `task(agent="greybeard")` |
40-
| Independent suite / repro evidence | `task(agent="tester")` |
41-
42-
Skywalker classifies, spawns, tracks, and synthesizes. Path tools (`write_file` / `edit_file` / `delete_file`) are mounted for DIY tiny/bounded product edits; spawn remains the default for DAG product work. Durable orchestration artifacts (`dispatch.yaml`, `plan.md`, status) still go through builder — intern does not have write tools (`INTERN_TOOLS` = run_shell, read_file, list_dir). Do not spawn a blob agent to author the manifest. Do not write those manifests on Skywalker.
30+
| Work | Director |
31+
| ---- | -------- |
32+
| Map the codebase, gather facts | `explorer` |
33+
| Eng plan from a spec (no ship) | `counsel` |
34+
| Write `dispatch.yaml` / `plan.md` / status (mechanical; no product feature work) | `builder` |
35+
| Ship product code + tests | `builder` |
36+
| Review a landed task (defects, evidence, no fix) | `critic` |
37+
| Architecture judgment before a large DAG | `greybeard` |
38+
| Independent suite / repro evidence | `tester` |
39+
| DESIGN.md / brand gate for UI lanes | `rand` |
4340

44-
Prefer typed briefs: `intent`, `success_criteria`, `do_not`, `report_focus`, and `agent`.
41+
Skywalker classifies, spawns, tracks, and synthesizes. Durable orchestration artifacts still go through builder — intern has no write tools. Prefer typed briefs: `intent`, `success_criteria`, `do_not`, `report_focus`, and `agent`.
4542

4643
## Agent type selection
4744

48-
Use **explorer** when the task is pure research. No code changes. Output is findings for downstream tasks.
49-
50-
Use **intern** when the work is mechanical and well-specified: git commit after a level fans in, exact shell, mechanical git. Intern cannot write files.
45+
- **explorer** pure research; no code changes; findings for downstream.
46+
- **intern** — mechanical, well-specified: level commits, exact shell, git via recipes. Cannot write files.
47+
- **builder** — ships product code (judgment, abstractions, tests) and mechanical writes of `dispatch.yaml` / `plan.md` / status.
5148

52-
Use **builder** when the task ships product code — including work that needs judgment, new abstractions, or tests — and for mechanical writes of `dispatch.yaml` / `plan.md` / status artifacts (write tools; intern does not have them). There is no catch-all implementation agent.
53-
54-
Critique is not a DAG node agent type. After builder (and after non-trivial intern landings), spawn `task(agent="critic")` with the task's objective, paths, and diff. Simple intern tasks may skip critique.
49+
Critique is not a DAG node type. After builder (and non-trivial intern landings), spawn `critic` with objective, paths, and diff. Simple intern tasks may skip critique.
5550

5651
Classify each product task as `feature` or `bugfix`:
5752

58-
- `bugfix`: incorrect behavior that exists today → test-first (fail, then fix, then pass)
59-
- `feature`: everything else → tests for the new behavior
53+
- `bugfix` → test-first (fail, then fix, then pass)
54+
- `feature` → tests for the new behavior
6055
- When unsure, default to `feature`
6156

57+
## Isolated worktree (when needed)
58+
59+
When the run needs a clean branch / isolated tree: load `use_skill("git-worktrees")`, copy the create (or teardown) recipe into an intern brief, spawn `intern`. Do not inline git worktree commands here. Skywalker does not run the git.
60+
6261
## Phase 1: Planning
6362

64-
Runs when the input is a spec (or a request with no existing manifest). The spec should be complete enough that a builder worker could succeed from it.
63+
Runs when the input is a spec (or a request with no existing manifest).
6564

66-
1. If the spec still needs an ordered eng plan, spawn `task(agent="counsel")`. Do not skip this when requirements are large or ambiguous.
67-
2. Spawn `explorer` workers only as needed to map scope. Distinct path/package lenses if parallel.
68-
3. Consult `greybeard` before large multi-lane work when architecture is in play.
65+
1. Spec needs an ordered eng planspawn `counsel`.
66+
2. Spawn `explorer` workers only as needed (distinct path/package lenses if parallel).
67+
3. Consult `greybeard` before large multi-lane work when architecture is in play; `rand` when UI/brand is in play.
6968
4. Break the goal into discrete tasks, each small enough for one director.
70-
5. Identify dependencies (DAG edges). Same-file writers at the same level must be merged or serialized via `depends-on`.
69+
5. Identify dependencies (DAG edges). Same-file writers at the same level → merge or serialize via `depends-on`.
7170
6. Assign `explorer` | `intern` | `builder` per the guide above.
72-
7. Detect verify commands from `package.json`, Makefile, or project docs.
73-
8. Add per-task verification to each plan (build for compiled changes, tests for test-writing tasks).
74-
9. Default commit strategy is **per-task** (debuggable). Use grouped only when the operator wants a cleaner history **and** Phase 5 will catch issues.
75-
10. Mark which tasks need critique (complex builder → yes; simple intern → no; when unsure, yes).
76-
11. Seed `manage_tasks` with one item per DAG task (plus plan / verify / critique items as needed).
71+
7. Detect verify commands from `package.json`, Makefile, or project docs; add per-task verification.
72+
8. Default commit strategy: **per-task**. Grouped only when the operator wants a cleaner history **and** Phase 5 will catch issues.
73+
9. Mark which tasks need critique (complex builder → yes; simple intern → no; unsure → yes).
74+
10. Seed `manage_tasks` with one item per DAG task (plus plan / verify / critique as needed).
7775

7876
If requirements are not actionable, stop. Ask: "Can a builder worker succeed with only this information?"
7977

8078
## Phase 2: Directory structure
8179

82-
Have **builder** write the run tree (mechanical brief; no product feature work). Do not write these files on Skywalker. Do not use intern — intern cannot write files. Do not use a catch-all worker.
80+
Have **builder** write the run tree (mechanical brief). Do not write these on Skywalker. Do not use intern for writes.
8381

8482
```
8583
dispatch/
@@ -94,7 +92,7 @@ dispatch/
9492

9593
Task directory names: `<level><sequence>-<short_description>`
9694

97-
- **Level** (1, 2, 3…): DAG depth. Roots are level 1. Level is longest path from a root, plus one.
95+
- **Level** (1, 2, 3…): DAG depth. Roots are level 1.
9896
- **Sequence** (a, b, c…): siblings at the same level (candidates for parallel).
9997
- **Description**: underscore-separated, from the objective.
10098

@@ -105,7 +103,6 @@ The directory name is the task `id`. After a worker runs, the task directory is
105103
```yaml
106104
goal: "Short description of the overall goal"
107105
status: pending # pending | in-progress | completed | failed
108-
max-parallel: 4 # recipe default batch size (not a hard runtime cap)
109106
created: YYYY-MM-DD
110107

111108
verify:
@@ -116,7 +113,7 @@ verify:
116113

117114
critique:
118115
enabled: true
119-
agent: critic # always task(agent="critic")
116+
agent: critic
120117

121118
commits:
122119
strategy: per-task # per-task | grouped
@@ -139,51 +136,51 @@ tasks:
139136
status: pending
140137
```
141138
142-
Task statuses: `pending` → `dispatched` → `completed` | `failed` | `fixing`. Downstream tasks wait for `completed`.
139+
Task statuses: `pending` → `dispatched` → `completed` | `failed` | `fixing`. Downstream waits for `completed`.
143140

144141
### Task `plan.md`
145142

146-
Builder writes one per task (mechanical brief). Include: objective, requirements covered, context (paths and symbols — no line numbers, no dispatch-dir cross-refs), files to modify, constraints, verification (test-first for bugfix), and `do_not`.
143+
Builder writes one per task. Include: objective, requirements covered, context (paths and symbols — no line numbers, no dispatch-dir cross-refs), files to modify, constraints, verification (test-first for bugfix), and `do_not`.
147144

148145
Every product-task brief must tell the worker:
149146

150147
- Do **not** run mutating git (`git add` / `commit` / `checkout` / `stash`). Intern commits after the level fans in.
151148
- Leave changes uncommitted. Multiple tasks may share a worktree.
152-
- Read the full `plan.md` before acting. If it is unclear, fail closed.
149+
- Read the full `plan.md` before acting. If unclear, fail closed.
153150

154151
## Phase 3: Validate, then present
155152

156153
Before any product spawn:
157154

158-
**Structural:** DAG is acyclic; every `depends-on` / `receives` id exists; `receives` ⊆ `depends-on`; every task has `plan.md`; no orphan directories.
155+
**Structural:** DAG acyclic; every `depends-on` / `receives` id exists; `receives` ⊆ `depends-on`; every task has `plan.md`; no orphan directories.
159156

160-
**Completeness:** clear objectives; files named; union of tasks covers the goal; every spec requirement maps to at least one task.
157+
**Completeness:** clear objectives; files named; union of tasks covers the goal; every spec requirement maps to ≥1 task.
161158

162-
**Coherence:** no two ready-in-parallel tasks write the same file; constraints do not contradict; `explorer` is never assigned product writes.
159+
**Coherence:** no two ready-in-parallel tasks write the same file; constraints do not contradict; `explorer` never gets product writes.
163160

164-
**Feasibility:** referenced files exist or are created by this task or an upstream dependency; scope fits one worker.
161+
**Feasibility:** referenced files exist or are created by this task or an upstream; scope fits one worker.
165162

166-
Empty task list → mark the run `completed` and report. Do not invent work.
163+
Empty task list → mark `completed` and report. Do not invent work.
167164

168-
Present the DAG (ids, agents, deps, critique flags, verify commands, commit strategy) to the operator. Wait for go-ahead on large or ambiguous runs. Then set status `in-progress` (builder updates the manifest if it is on disk).
165+
Present the DAG (ids, agents, deps, critique flags, verify commands, commit strategy) to the operator. Wait for go-ahead on large or ambiguous runs. Then set status `in-progress` (builder updates the manifest if on disk).
169166

170167
## Phase 4: Execute the DAG
171168

172169
1. **Ready set:** `pending` tasks whose `depends-on` are all `completed`.
173-
2. **Batch:** take a safe parallel subset; default batch size is **4 live workers** (including in-flight critique) — widen when lanes are named and non-overlapping. Same-file writers and shared mutable state (build artifacts, test DBs) must not share a batch — serialize with `depends-on`.
174-
3. **Spawn** each task with `task(agent="<id from manifest>")`. Inject upstream reports (not a rewritten `plan.md`) into the brief. Split ownership by path/package when two builder workers run together.
170+
2. **Parallel lanes:** spawn every independent ready task whose ownership does not overlap (same-file writers and shared mutable state must serialize via `depends-on`). No batch-size ceiling.
171+
3. **Spawn:** call `spawn_agent(agent=<task.agent director id>, …)` for each ready task in one turn; record each returned `agent_id`, then `wait_agents` on those runtime ids. Inject upstream reports (not a rewritten `plan.md`) into the brief. Split ownership by path/package when two builders run together.
175172
4. **Fan in:** trust the worker report (and `output.yaml` when builder wrote one). Missing report or `status: failed` → mark `failed`. Do not re-fan-out an identical brief; change `success_criteria` / `do_not` or tell the operator.
176173
5. **Level commit:** after a level's product tasks self-report complete, intern commits per the strategy (per-task default). Workers must not have committed.
177-
6. **Critique:** for tasks with `critique.enabled`, spawn `task(agent="critic")` on that commit/diff + objective. Blocking findings → re-dispatch `builder` with those findings in `success_criteria` / `do_not` (status `fixing`). Cap re-fix rounds (1–2), then report Blockers.
174+
6. **Critique:** for tasks with `critique.enabled`, spawn `critic` on that commit/diff + objective. Blocking findings → re-dispatch `builder` with those findings in `success_criteria` / `do_not` (status `fixing`). Cap re-fix rounds (1–2), then report Blockers.
178175
7. Repeat until no pending tasks remain, or deadlock / all remaining failed → stop and ask.
179176

180-
Keep `manage_tasks` in sync as items move `todo` → `doing` → `done` / stay blocked.
177+
Keep `manage_tasks` in sync (`todo` → `doing` → `done` / blocked).
181178

182179
If the working tree has unrelated uncommitted changes before Phase 4, ask the operator. Do not mix them into level commits.
183180

184181
## Phase 5: Verify
185182

186-
Must `task(agent="tester")` for the suite (or intern for one named mechanical command). Do not run the full verify pipeline on the parent via Skywalker `run_shell`. Compare against any baseline you captured.
183+
Spawn `tester` for the suite (or intern for one named mechanical command). Do not run the full verify pipeline on the parent via Skywalker `run_shell`. Compare against any baseline you captured.
187184

188185
- Green, or same failures as baseline → proceed.
189186
- New failures → attribute to a task/commit, re-dispatch `builder` on that lane, re-verify. Cap rounds, then Blockers.
@@ -201,7 +198,7 @@ Synthesize for the operator:
201198

202199
## Paths
203200

204-
Include: what landed, which directors ran, verify evidence, remaining failed/fixing tasks. Mark the run `completed` or `failed`. `manage_tasks` should reflect the same.
201+
Include: what landed, which directors ran, verify evidence, remaining failed/fixing tasks. Mark the run `completed` or `failed`. `manage_tasks` should match.
205202

206203
## Resume
207204

@@ -210,7 +207,7 @@ Re-resolve input to the existing `dispatch/<name>/`. Re-validate the remaining D
210207
## Non-negotiables
211208

212209
- You are Skywalker. Spawn directors. Do not implement product features. Do not author dispatch YAML/plan files yourself or via a catch-all worker. Durable orchestration files go through builder.
213-
- `use_skill("dispatch")` loads this recipe. It is a command.
214-
- Agents: `explorer`, `intern`, `builder` only for DAG nodes. Critique via `task(agent="critic")`. Plan via `task(agent="counsel")` when a spec needs an eng plan first.
215-
- Progress: `manage_tasks`.
216-
- Default batch size 4 live workers (recipe default, not a hard runtime cap); widen for named non-overlapping lanes or when the operator asks.
210+
- `use_skill("dispatch")` loads this recipe. It is not a slash command.
211+
- Fleet verbs: `spawn_agent` + `wait_agents` for multi-lane work. DAG nodes: `explorer`, `intern`, `builder` only. Critique via `critic`. Plan via `counsel` when a spec needs an eng plan first.
212+
- Isolated trees: `use_skill("git-worktrees")` → intern executes. Do not inline worktree git here.
213+
- Progress: `manage_tasks`. Parallelize named non-overlapping lanes — no worker-count cap.

tests/unit/corbits-skills-catalog.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const SKILL_DIRS = [
2626
"plan",
2727
] as const;
2828

29-
const SPAWN_RECIPE_SKILLS = ["implement", "scribe", "review", "dispatch", "plan"] as const;
29+
const SPAWN_RECIPE_SKILLS = ["implement", "scribe", "review", "plan"] as const;
3030

3131
/** use_skill listing + resolve; not slash. No disable-model-invocation. */
3232
const USE_SKILL_ONLY = [
@@ -112,6 +112,20 @@ test("spawn-recipe skills contain task(agent=", async () => {
112112
}
113113
});
114114

115+
test("dispatch skill prefers spawn_agent/wait_agents and references git-worktrees", async () => {
116+
const skill = await Bun.file(join(pluginRoot, "skills/dispatch/SKILL.md")).text();
117+
expect(skill).toContain("spawn_agent");
118+
expect(skill).toContain("wait_agents");
119+
expect(skill).toContain('use_skill("git-worktrees")');
120+
expect(skill).toContain(USER_INVOCABLE_FALSE);
121+
expect(skill).toContain("task.agent director id");
122+
expect(skill).toContain("returned `agent_id`");
123+
expect(skill).not.toContain('agent="<id from manifest>"');
124+
expect(skill).not.toContain("4 live workers");
125+
expect(skill).not.toContain("max-parallel");
126+
expect(skill).not.toContain("git worktree add");
127+
});
128+
115129
test("create-issue selects Linear MCP, GitHub gh, and MEMORY.md preference", async () => {
116130
const skill = await Bun.file(join(pluginRoot, "skills/create-issue/SKILL.md")).text();
117131
expect(skill).toContain("mcp__linear__");

0 commit comments

Comments
 (0)