Skip to content

Commit ea04151

Browse files
committed
Add dispatch-start skill and update docs/tests
Introduce a new bundled skill 'dispatch-start' (skills/dispatch-start/SKILL.md and agents/openai.yaml) that serves as the entrypoint for Dispatch planning/runtime decisions. Update README to mention the new skill and its usage. Adjust tests (tests/skills.test.js) to include the new skill in the bundled list, update expected counts, assert output includes dispatch-start, and run the build step before packing in the pack/test to ensure the skill is included.
1 parent 0acfa43 commit ea04151

4 files changed

Lines changed: 157 additions & 13 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ Checklist for a new Codex thread:
339339
```
340340

341341
5. In a fresh Codex session, use the installed skills when their triggers match:
342+
- `$dispatch-start` as the first skill for broad goals, async requests,
343+
multi-repo work, or "spawn chats" requests.
342344
- `$dispatch-root-runtime` for broad root-chat coordination.
343345
- `$dispatch-code-routing` before code edits that might split into lanes,
344346
worktrees, child chats, or domain-owner chats.
@@ -393,6 +395,8 @@ The `skills/` directory is part of the package. These skills ship with
393395
`@async/dispatch` and are installed into Codex with
394396
`async-dispatch skills install`.
395397

398+
- `dispatch-start`: entrypoint routing for raw goals; starts planning when
399+
broad async work has no approved board yet.
396400
- `dispatch-root-runtime`: goal, draft, board, runtime, worker, receipt, idle,
397401
and wake coordination.
398402
- `dispatch-code-routing`: quick code-routing decisions before edits, including

skills/dispatch-start/SKILL.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
name: dispatch-start
3+
description: Start async-dispatch as the entrypoint for broad Codex root-chat work. Use when a user asks to run async, spawn chats, coordinate multi-repo or multi-slice work, start a Dispatch-managed goal, resume an existing Dispatch board or runtime, or decide whether a raw request should enter planning before implementation.
4+
---
5+
6+
# Dispatch Start
7+
8+
## Overview
9+
10+
Use this as the first Dispatch skill for a raw user goal. It decides whether the
11+
root can work directly, must resume existing Dispatch state, or must enter the
12+
goal -> draft -> board -> runtime planning path before any worker or child-chat
13+
launch.
14+
15+
The important default is: no approved board means start the board phase, not
16+
root-only implementation.
17+
18+
## Start Decision
19+
20+
First inspect local state and the request:
21+
22+
```bash
23+
async-dispatch snapshot
24+
```
25+
26+
Then choose one mode:
27+
28+
- `resume-runtime`: a matching active runtime ledger already exists. Continue
29+
with `dispatch-root-runtime`.
30+
- `resume-board`: a matching board exists but is not approved or started.
31+
Present the board/draft for review before `board approve` or `runtime start`.
32+
- `planning-required`: the request is broad, async, multi-repo, multi-slice, or
33+
asks to spawn chats, but no approved board exists.
34+
- `root-only`: the request is small enough for the root chat, or the user
35+
explicitly asked for root-only execution.
36+
37+
For `planning-required`, do not edit repos, create worker chats, or commit code.
38+
Start the Dispatch planning phase.
39+
40+
## Missing State Recovery
41+
42+
When Dispatch state is missing, report the recovery path instead of stopping at
43+
"no plan found."
44+
45+
- No matching plan exists: keep using `dispatch-start`; create/refine the goal,
46+
draft a plan, and give the human the draft path plus approval choices.
47+
- Plan exists but no board exists: keep using `dispatch-start`; ask the human to
48+
approve or correct the draft, then run `plan ready` and `plan compile`.
49+
- Board exists but no runtime exists: keep using `dispatch-start`; ask the human
50+
to approve or correct the board, then run `board approve` and `runtime start`.
51+
- Runtime exists: hand off to `dispatch-root-runtime` for nodes, workers,
52+
receipts, idle/wake, and closeout.
53+
- Code work is ready inside an active runtime: use `dispatch-code-routing`
54+
before root edits or worker launches.
55+
56+
For a missing plan, the human action must be concrete:
57+
58+
```text
59+
Reply "approve draft" to compile the board, or reply with corrections such as
60+
"split core graph and runtime into separate phases" or "keep this root-only."
61+
```
62+
63+
## Planning Phase
64+
65+
Create a durable goal and gather enough context to draft a real plan:
66+
67+
```bash
68+
async-dispatch goal init --seed "..."
69+
async-dispatch context add <goalId> --note "..."
70+
async-dispatch goal refine <goalId> --statement "..." --purpose "..." --success-proof "..." --constraint "..." --why "..."
71+
```
72+
73+
Draft a human-readable plan before runtime work:
74+
75+
```bash
76+
async-dispatch plan template human-draft --seed "..."
77+
async-dispatch plan draft <goalId> --from <draft.md>
78+
```
79+
80+
Present the draft or board shape to the user for review. Ask for approval or
81+
corrections, not whether planning should happen. Record corrections with:
82+
83+
```bash
84+
async-dispatch plan decision <planId> --note "..."
85+
async-dispatch plan enrich <planId> --note "..."
86+
async-dispatch plan resolve <planId> --decision-id D001 --resolution "..."
87+
```
88+
89+
Only after the human-reviewed draft is ready:
90+
91+
```bash
92+
async-dispatch plan ready <planId> --note "Human reviewed the draft."
93+
async-dispatch plan compile <planId>
94+
```
95+
96+
Do not approve the board on behalf of the user unless their latest message
97+
clearly approved the specific draft/board. After approval:
98+
99+
```bash
100+
async-dispatch board approve <boardId> --note "Human reviewed the board."
101+
async-dispatch runtime start <boardId>
102+
```
103+
104+
## Runtime Handoff
105+
106+
After `runtime start`, switch to `dispatch-root-runtime` for nodes, domain
107+
owners, workers, receipts, idle/wake, and closeout. Use `dispatch-code-routing`
108+
before code edits that might become root work, child chats, domain-owner chats,
109+
or worktree lanes.
110+
111+
Launch workers only from approved runtime state with ownership, verification,
112+
stop conditions, and a root integration point recorded.
113+
114+
## Output Contract
115+
116+
For the first response after this skill triggers, report:
117+
118+
```markdown
119+
## Dispatch Start
120+
Mode: planning-required | resume-runtime | resume-board | root-only
121+
Source state: [matching goal/plan/board/runtime or none]
122+
Goal: [short restatement]
123+
Next phase: [planning, board review, runtime, or root-only]
124+
Root action: [exact command or review step being performed now]
125+
Human review needed: [what the user must approve or correct before workers]
126+
Missing state action: [skill to continue with, exact command family, and concrete human reply]
127+
```
128+
129+
If the mode is `planning-required`, include a concise draft plan or the command
130+
sequence used to create one. If the mode is `root-only`, say why the request is
131+
small enough or quote the explicit root-only instruction.
132+
133+
Never end a missing-plan response with only "review needed." Include the next
134+
agent action, the next human action, and the skill that owns the next phase.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface:
2+
display_name: "Dispatch Start"
3+
short_description: "Start Dispatch planning and runtime"
4+
default_prompt: "Use $dispatch-start to start this goal through Dispatch planning and runtime."

tests/skills.test.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@ import {
1616
const execFile = promisify(execFileCallback);
1717
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
1818
const cliPath = path.join(repoRoot, "src", "cli.js");
19+
const bundledSkillNames = [
20+
"dispatch-code-routing",
21+
"dispatch-root-runtime",
22+
"dispatch-skill-evolution",
23+
"dispatch-start"
24+
];
1925

2026
test("bundled skills are listed and validated for CLI installation", async () => {
2127
const skills = await listBundledSkills();
2228
const names = skills.map((skill) => skill.name);
2329
const validation = await validateBundledSkills();
2430

25-
assert.deepEqual(names, [
26-
"dispatch-code-routing",
27-
"dispatch-root-runtime",
28-
"dispatch-skill-evolution"
29-
]);
31+
assert.deepEqual(names, bundledSkillNames);
3032
assert.ok(skills.every((skill) => skill.description.includes("Dispatch") || skill.description.includes("async-dispatch")));
3133
assert.equal(validation.ok, true);
32-
assert.equal(validation.skills.length, 3);
34+
assert.equal(validation.skills.length, 4);
3335
});
3436

3537
test("bundled skill names must match safe folder basenames", async () => {
@@ -49,7 +51,7 @@ test("skill installer records metadata, status, and preserves unmanaged folders"
4951
const targetDir = await fs.mkdtemp(path.join(os.tmpdir(), "dispatch-skills-"));
5052
try {
5153
const initial = await installBundledSkills({ targetDir });
52-
assert.equal(initial.results.length, 3);
54+
assert.equal(initial.results.length, 4);
5355
assert.ok(initial.results.every((result) => result.action === "installed"));
5456

5557
const current = await getSkillInstallStatus({ targetDir, skills: "dispatch-root-runtime" });
@@ -109,11 +111,7 @@ test("skills CLI supports JSON list, status, install, and unknown skill errors",
109111
const targetDir = await fs.mkdtemp(path.join(os.tmpdir(), "dispatch-skills-cli-"));
110112
try {
111113
const listed = JSON.parse((await execCli(["skills", "list", "--json"])).stdout);
112-
assert.deepEqual(listed.skills.map((skill) => skill.name), [
113-
"dispatch-code-routing",
114-
"dispatch-root-runtime",
115-
"dispatch-skill-evolution"
116-
]);
114+
assert.deepEqual(listed.skills.map((skill) => skill.name), bundledSkillNames);
117115

118116
const install = JSON.parse((await execCli([
119117
"skills",
@@ -135,7 +133,7 @@ test("skills CLI supports JSON list, status, install, and unknown skill errors",
135133
targetDir,
136134
"--json"
137135
])).stdout);
138-
assert.deepEqual(status.results.map((result) => result.status), ["current", "current", "missing"]);
136+
assert.deepEqual(status.results.map((result) => result.status), ["current", "current", "missing", "missing"]);
139137

140138
await assert.rejects(
141139
() => execCli(["skills", "install", "--target", targetDir, "--skill", "../escape"]),
@@ -149,6 +147,9 @@ test("skills CLI supports JSON list, status, install, and unknown skill errors",
149147
test("packed package exposes the async-dispatch CLI and bundled skills", async () => {
150148
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "dispatch-pack-"));
151149
try {
150+
await execFile(process.execPath, [path.join(repoRoot, "scripts", "build-dist.js")], {
151+
cwd: repoRoot
152+
});
152153
const pack = JSON.parse((await execFile("pnpm", ["pack", "--json", "--pack-destination", tmp], {
153154
cwd: repoRoot
154155
})).stdout);
@@ -158,6 +159,7 @@ test("packed package exposes the async-dispatch CLI and bundled skills", async (
158159
const bin = path.join(installDir, "node_modules", ".bin", "async-dispatch");
159160
const listed = await execFile(bin, ["skills", "list"]);
160161
assert.match(listed.stdout, /dispatch-root-runtime/);
162+
assert.match(listed.stdout, /dispatch-start/);
161163

162164
const targetDir = path.join(tmp, "codex-skills");
163165
const installed = await execFile(bin, ["skills", "install", "--target", targetDir, "--skill", "dispatch-root-runtime"]);

0 commit comments

Comments
 (0)