opencodekit 0.15.10 → 0.15.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Swarm Enforcer Plugin
3
+ *
4
+ * Beads is the single source of truth for the swarm board.
5
+ * This plugin nudges agents to:
6
+ * - Claim a Beads task before making code changes
7
+ * - Ensure `spec.md` exists for in-progress tasks
8
+ * - Close/sync in-progress work at session end
9
+ *
10
+ * This plugin is intentionally non-destructive: it never runs `bd update/close/sync`.
11
+ */
12
+
13
+ import fsPromises from "node:fs/promises";
14
+ import path from "node:path";
15
+ import type { Plugin } from "@opencode-ai/plugin";
16
+
17
+ type BeadsIssue = {
18
+ id: string;
19
+ title?: string;
20
+ status?: string;
21
+ };
22
+
23
+ const BEADS_DIR = ".beads";
24
+ const ISSUES_FILE = "issues.jsonl";
25
+
26
+ const CODE_EXTENSIONS = [
27
+ ".ts",
28
+ ".tsx",
29
+ ".js",
30
+ ".jsx",
31
+ ".mjs",
32
+ ".cjs",
33
+ ".py",
34
+ ".go",
35
+ ".rs",
36
+ ".java",
37
+ ".c",
38
+ ".cpp",
39
+ ".h",
40
+ ".hpp",
41
+ ];
42
+
43
+ const WORK_INTENT_PATTERNS = [
44
+ /\b(implement|fix|refactor|add|remove|delete|update|change|modify|create|build)\b/i,
45
+ /\b(edit|patch)\b/i,
46
+ ];
47
+
48
+ function looksLikeWorkIntent(text: string): boolean {
49
+ return WORK_INTENT_PATTERNS.some((p) => p.test(text));
50
+ }
51
+
52
+ function isCodeFile(filePath: string): boolean {
53
+ return CODE_EXTENSIONS.some((ext) => filePath.endsWith(ext));
54
+ }
55
+
56
+ function isIgnoredPath(repoDir: string, filePath: string): boolean {
57
+ const absPath = path.isAbsolute(filePath)
58
+ ? filePath
59
+ : path.join(repoDir, filePath);
60
+ const rel = path.relative(repoDir, absPath);
61
+
62
+ // Outside repo: ignore
63
+ if (rel.startsWith("..")) return true;
64
+
65
+ const normalized = rel.replace(/\\/g, "/");
66
+ return (
67
+ normalized.startsWith("node_modules/") ||
68
+ normalized.startsWith("dist/") ||
69
+ normalized.startsWith(".beads/") ||
70
+ normalized.startsWith(".git/")
71
+ );
72
+ }
73
+
74
+ function summarizeIssues(issues: BeadsIssue[], limit = 5): string {
75
+ return issues
76
+ .slice(0, limit)
77
+ .map((i) => `${i.id}${i.title ? `: ${i.title}` : ""}`)
78
+ .join("\n");
79
+ }
80
+
81
+ async function readIssuesJsonl(repoDir: string): Promise<BeadsIssue[]> {
82
+ const issuesPath = path.join(repoDir, BEADS_DIR, ISSUES_FILE);
83
+
84
+ let content: string;
85
+ try {
86
+ content = await fsPromises.readFile(issuesPath, "utf-8");
87
+ } catch {
88
+ return [];
89
+ }
90
+
91
+ const issues: BeadsIssue[] = [];
92
+ const lines = content.split(/\r?\n/);
93
+ for (const line of lines) {
94
+ const trimmed = line.trim();
95
+ if (!trimmed) continue;
96
+ try {
97
+ const parsed = JSON.parse(trimmed);
98
+ if (parsed && typeof parsed.id === "string") {
99
+ issues.push({
100
+ id: parsed.id,
101
+ title: typeof parsed.title === "string" ? parsed.title : undefined,
102
+ status: typeof parsed.status === "string" ? parsed.status : undefined,
103
+ });
104
+ }
105
+ } catch {
106
+ // Ignore malformed JSONL lines
107
+ }
108
+ }
109
+
110
+ return issues;
111
+ }
112
+
113
+ async function specExists(repoDir: string, issueId: string): Promise<boolean> {
114
+ const specPath = path.join(
115
+ repoDir,
116
+ BEADS_DIR,
117
+ "artifacts",
118
+ issueId,
119
+ "spec.md",
120
+ );
121
+ try {
122
+ await fsPromises.access(specPath);
123
+ return true;
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+
129
+ function buildNudge(params: {
130
+ inProgress: BeadsIssue[];
131
+ missingSpec: BeadsIssue[];
132
+ }): string {
133
+ const { inProgress, missingSpec } = params;
134
+
135
+ if (inProgress.length === 0) {
136
+ return `
137
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
138
+ ⚡ [SWARM PROTOCOL]
139
+
140
+ Beads is the swarm board. Before any code changes:
141
+
142
+ 1) Pick a task: \`bd ready\` (or \`bd list\`)
143
+ 2) Inspect: \`bd show <id>\`
144
+ 3) Claim: \`bd update <id> --status=in_progress\`
145
+
146
+ Then proceed with work and collect verification evidence.
147
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
148
+ `;
149
+ }
150
+
151
+ if (missingSpec.length > 0) {
152
+ return `
153
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
154
+ ⚡ [SWARM PROTOCOL]
155
+
156
+ In-progress Beads exist, but \`spec.md\` is missing for:
157
+
158
+ ${summarizeIssues(missingSpec)}
159
+
160
+ Create \`.beads/artifacts/<id>/spec.md\` before implementation.
161
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
162
+ `;
163
+ }
164
+
165
+ return "";
166
+ }
167
+
168
+ export const SwarmEnforcer: Plugin = async ({ client, directory }) => {
169
+ const repoDir = directory || process.cwd();
170
+ let lastStateAt = 0;
171
+ let cachedInProgress: BeadsIssue[] = [];
172
+ let cachedMissingSpec: BeadsIssue[] = [];
173
+
174
+ const refreshState = async () => {
175
+ const now = Date.now();
176
+ if (now - lastStateAt < 1500) return;
177
+ lastStateAt = now;
178
+
179
+ const issues = await readIssuesJsonl(repoDir);
180
+ const inProgress = issues.filter((i) => i.status === "in_progress");
181
+
182
+ const missingSpec: BeadsIssue[] = [];
183
+ for (const issue of inProgress.slice(0, 10)) {
184
+ if (!(await specExists(repoDir, issue.id))) {
185
+ missingSpec.push(issue);
186
+ }
187
+ }
188
+
189
+ cachedInProgress = inProgress;
190
+ cachedMissingSpec = missingSpec;
191
+ };
192
+
193
+ const showToast = async (
194
+ title: string,
195
+ message: string,
196
+ variant: "info" | "success" | "warning" | "error" = "info",
197
+ ) => {
198
+ try {
199
+ await client.tui.showToast({
200
+ body: {
201
+ title,
202
+ message,
203
+ variant,
204
+ duration: variant === "error" ? 8000 : 5000,
205
+ },
206
+ });
207
+ } catch {
208
+ // If toast is unavailable, fail silently
209
+ }
210
+ };
211
+
212
+ return {
213
+ // Nudge early when user expresses implementation intent
214
+ "chat.message": async (input, output) => {
215
+ const { sessionID, messageID } = input;
216
+ const { message, parts } = output;
217
+ if (message.role !== "user") return;
218
+
219
+ const fullText = parts
220
+ .filter((p) => p.type === "text" && !("synthetic" in p && p.synthetic))
221
+ .map((p) => ("text" in p ? p.text : ""))
222
+ .join(" ");
223
+
224
+ if (!looksLikeWorkIntent(fullText)) return;
225
+
226
+ await refreshState();
227
+
228
+ const nudge = buildNudge({
229
+ inProgress: cachedInProgress,
230
+ missingSpec: cachedMissingSpec,
231
+ });
232
+ if (!nudge) return;
233
+
234
+ parts.push({
235
+ id: `swarm-nudge-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
236
+ sessionID,
237
+ messageID: messageID || "",
238
+ type: "text",
239
+ text: nudge,
240
+ synthetic: true,
241
+ } as import("@opencode-ai/sdk").Part);
242
+ },
243
+
244
+ // Warn if code gets edited while no task is claimed / spec missing
245
+ "file.edited": async ({ event }) => {
246
+ const filePath = event.properties?.file || event.properties?.path;
247
+ if (!filePath || typeof filePath !== "string") return;
248
+ if (isIgnoredPath(repoDir, filePath)) return;
249
+
250
+ const absPath = path.isAbsolute(filePath)
251
+ ? filePath
252
+ : path.join(repoDir, filePath);
253
+
254
+ if (!isCodeFile(absPath)) return;
255
+
256
+ await refreshState();
257
+
258
+ if (cachedInProgress.length === 0) {
259
+ await showToast(
260
+ "Swarm: No task claimed",
261
+ "Beads is the board. Claim a task before code edits (bd ready/show/update).",
262
+ "warning",
263
+ );
264
+ return;
265
+ }
266
+
267
+ if (cachedMissingSpec.length > 0) {
268
+ await showToast(
269
+ "Swarm: Missing spec.md",
270
+ `Create .beads/artifacts/<id>/spec.md for: ${cachedMissingSpec
271
+ .slice(0, 3)
272
+ .map((i) => i.id)
273
+ .join(", ")}`,
274
+ "warning",
275
+ );
276
+ }
277
+ },
278
+
279
+ // Session end reminder: close/sync if tasks still in progress
280
+ "session.idle": async () => {
281
+ await refreshState();
282
+ if (cachedInProgress.length === 0) return;
283
+
284
+ const list = cachedInProgress
285
+ .slice(0, 5)
286
+ .map((i) => i.id)
287
+ .join(", ");
288
+ await showToast(
289
+ "Swarm: Work still in progress",
290
+ `In-progress Beads: ${list}. Close with bd close + bd sync when done.`,
291
+ "info",
292
+ );
293
+ },
294
+ };
295
+ };
296
+
297
+ export default SwarmEnforcer;
@@ -0,0 +1,405 @@
1
+ ---
2
+ name: swarm-coordination
3
+ description: >
4
+ Use when implementing plans with multiple independent tasks that can run in parallel.
5
+ Enables leader agents to spawn, coordinate, and monitor worker swarms. Covers delegation
6
+ packets, mailbox communication, task assignment, and graceful shutdown patterns.
7
+ version: "1.0.0"
8
+ license: MIT
9
+ ---
10
+
11
+ # Swarm Coordination - Multi-Agent Parallel Execution
12
+
13
+ Coordinate multiple agents working on independent tasks in parallel. Leader orchestrates, workers execute, mailbox communicates.
14
+
15
+ ## Overview
16
+
17
+ **Swarm = Leader + Workers + Mailbox**
18
+
19
+ - **Leader (build agent)**: Orchestrates the swarm - spawns workers, monitors progress, synthesizes results
20
+ - **Workers (general agents)**: Execute independent tasks - read delegation, make changes, report back
21
+ - **Mailbox (swarm-mail.jsonl)**: Append-only log for coordination messages
22
+
23
+ **Key Distinction**:
24
+
25
+ - **Swarm**: Parallel execution of independent tasks from a plan
26
+ - **Beads**: Task tracking and dependency management across sessions
27
+ - **Task tool**: Spawning individual subagents for research/execution
28
+
29
+ **When to Use Swarm Coordination**:
30
+
31
+ - "Does this plan have 3+ independent tasks?" → **YES** = Swarm
32
+ - "Can multiple tasks run in parallel without conflicts?" → **YES** = Swarm
33
+ - "Do I need to coordinate multiple agents?" → **YES** = Swarm
34
+ - "Is this a single task or sequential dependency chain?" → **NO** = Single agent
35
+
36
+ ## Architecture
37
+
38
+ ```
39
+ ┌─────────────────────────────────────────────────────────────────┐
40
+ │ BUILD AGENT (Leader) │
41
+ │ - Parses plan into tasks │
42
+ │ - Creates delegation packets │
43
+ │ - Spawns worker agents via Task tool │
44
+ │ - Monitors mailbox for progress │
45
+ │ - Synthesizes final results │
46
+ └─────────────────────────────────────────────────────────────────┘
47
+ │ │ │
48
+ ▼ ▼ ▼
49
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
50
+ │ WORKER-1 │ │ WORKER-2 │ │ WORKER-3 │
51
+ │ (general) │ │ (general) │ │ (general) │
52
+ │ │ │ │ │ │
53
+ │ - Read │ │ - Read │ │ - Read │
54
+ │ delegation│ │ delegation│ │ delegation│
55
+ │ - Execute │ │ - Execute │ │ - Execute │
56
+ │ - Report │ │ - Report │ │ - Report │
57
+ └─────────────┘ └─────────────┘ └─────────────┘
58
+ │ │ │
59
+ └────────────────────┼────────────────────┘
60
+
61
+ ┌─────────────────┐
62
+ │ SWARM MAILBOX │
63
+ │ (swarm-mail │
64
+ │ .jsonl) │
65
+ └─────────────────┘
66
+ ```
67
+
68
+ ## Swarm Launch Flow (5 Steps)
69
+
70
+ ### Step 1: Parse Plan into Tasks
71
+
72
+ Extract independent tasks from the approved plan:
73
+
74
+ ```typescript
75
+ // Read the plan
76
+ const plan = read({ filePath: ".beads/artifacts/<bead-id>/plan.md" });
77
+
78
+ // Identify parallelizable tasks
79
+ // Tasks are parallel if they:
80
+ // - Don't modify the same files
81
+ // - Don't have sequential dependencies
82
+ // - Can verify independently
83
+ ```
84
+
85
+ ### Step 2: Create Delegation Packets
86
+
87
+ For each task, create a delegation packet:
88
+
89
+ ```typescript
90
+ swarm_delegate({
91
+ bead_id: "task-1",
92
+ title: "Implement auth service",
93
+ expected_outcome: "Auth service with JWT tokens, tests pass",
94
+ required_tools: "read, grep, lsp, edit, bash",
95
+ must_do: "LSP before edits, run npm test after changes",
96
+ must_not_do: "No new dependencies, don't edit config files",
97
+ acceptance_checks: "typecheck: npm run typecheck, lint: npm run lint, test: npm test",
98
+ context: "See .beads/artifacts/task-1/spec.md for requirements",
99
+ write: true,
100
+ });
101
+ ```
102
+
103
+ ### Step 3: Spawn Worker Agents
104
+
105
+ Use Task tool to spawn workers in parallel:
106
+
107
+ ```typescript
108
+ // Multiple Task calls in one message run simultaneously
109
+ Task({
110
+ subagent_type: "general",
111
+ description: "Execute task-1",
112
+ prompt: `Execute bead task-1: Implement auth service
113
+
114
+ Read delegation packet at: .beads/artifacts/task-1/delegation.md
115
+
116
+ Requirements:
117
+ 1. Follow all MUST DO constraints
118
+ 2. Avoid all MUST NOT DO items
119
+ 3. Run acceptance checks before claiming done
120
+ 4. Report completion via swarm-helper sendTeamMessage
121
+
122
+ Team: plan-implementation
123
+ Worker: worker-1`,
124
+ });
125
+
126
+ Task({
127
+ subagent_type: "general",
128
+ description: "Execute task-2",
129
+ prompt: `Execute bead task-2: Add user routes
130
+ ...same pattern...`,
131
+ });
132
+
133
+ Task({
134
+ subagent_type: "general",
135
+ description: "Execute task-3",
136
+ prompt: `Execute bead task-3: Create frontend forms
137
+ ...same pattern...`,
138
+ });
139
+ // All three run in parallel
140
+ ```
141
+
142
+ ### Step 4: Monitor Progress
143
+
144
+ Check mailbox for worker reports:
145
+
146
+ ```typescript
147
+ swarm_helper({
148
+ operation: "getTeamStatus",
149
+ team_name: "plan-implementation",
150
+ limit: 20,
151
+ });
152
+ // Returns messages from workers about progress
153
+ ```
154
+
155
+ ### Step 5: Synthesize Results
156
+
157
+ When all workers complete:
158
+
159
+ 1. Read their completion messages from mailbox
160
+ 2. Verify all acceptance checks passed
161
+ 3. Run full test suite
162
+ 4. Summarize what was accomplished
163
+ 5. Close the parent bead
164
+
165
+ ## Delegation Packet Structure
166
+
167
+ ```markdown
168
+ # Delegation Packet
169
+
170
+ - TASK: task-1 - Implement auth service
171
+ - EXPECTED OUTCOME: Auth service with JWT tokens, tests pass
172
+ - REQUIRED TOOLS:
173
+ - read
174
+ - grep
175
+ - lsp
176
+ - edit
177
+ - bash
178
+ - MUST DO:
179
+ - LSP before edits
180
+ - Run npm test after changes
181
+ - Follow existing code patterns
182
+ - MUST NOT DO:
183
+ - No new dependencies
184
+ - Don't edit config files
185
+ - Don't modify shared utilities
186
+ - ACCEPTANCE CHECKS:
187
+ - typecheck: npm run typecheck
188
+ - lint: npm run lint
189
+ - test: npm test
190
+ - CONTEXT:
191
+ See .beads/artifacts/task-1/spec.md for requirements
192
+ ```
193
+
194
+ ## Worker Protocol
195
+
196
+ Workers follow this execution pattern:
197
+
198
+ ### 1. Read Delegation
199
+
200
+ ```typescript
201
+ // First action: read the delegation packet
202
+ read({ filePath: ".beads/artifacts/<task-id>/delegation.md" });
203
+ ```
204
+
205
+ ### 2. Announce Start
206
+
207
+ ```typescript
208
+ swarm_helper({
209
+ operation: "sendTeamMessage",
210
+ team_name: "plan-implementation",
211
+ from_worker: "worker-1",
212
+ to_worker: "leader",
213
+ message: "Starting: <task-title>",
214
+ });
215
+ ```
216
+
217
+ ### 3. Execute Task
218
+
219
+ Follow the MUST DO constraints. Avoid MUST NOT DO items. Use required tools only.
220
+
221
+ ### 4. Run Acceptance Checks
222
+
223
+ ```bash
224
+ # Run each check from the delegation packet
225
+ npm run typecheck
226
+ npm run lint
227
+ npm test
228
+ ```
229
+
230
+ ### 5. Report Completion
231
+
232
+ ```typescript
233
+ swarm_helper({
234
+ operation: "sendTeamMessage",
235
+ team_name: "plan-implementation",
236
+ from_worker: "worker-1",
237
+ to_worker: "leader",
238
+ message: "DONE: <task-title>. All checks passed. Changes: <summary>",
239
+ });
240
+ ```
241
+
242
+ ## Mailbox Message Format
243
+
244
+ Messages in `.beads/swarm-mail.jsonl`:
245
+
246
+ ```json
247
+ {
248
+ "timestamp": "2025-01-27T10:30:00.000Z",
249
+ "team_name": "plan-implementation",
250
+ "from_worker": "worker-1",
251
+ "to_worker": "leader",
252
+ "message": "DONE: Implement auth service. All checks passed.",
253
+ "status": "unread"
254
+ }
255
+ ```
256
+
257
+ ### Message Types
258
+
259
+ | Type | From | To | Purpose |
260
+ | -------- | ------ | -------- | --------------------------- |
261
+ | START | worker | leader | Worker beginning task |
262
+ | PROGRESS | worker | leader | Intermediate update |
263
+ | BLOCKED | worker | leader | Worker needs help |
264
+ | DONE | worker | leader | Task completed successfully |
265
+ | ERROR | worker | leader | Task failed |
266
+ | HELP | worker | worker-N | Request assistance |
267
+ | ASSIGN | leader | worker | New task assignment |
268
+ | SHUTDOWN | leader | all | Graceful shutdown signal |
269
+
270
+ ## Conflict Prevention
271
+
272
+ ### File Reservation
273
+
274
+ Before workers start, leader reserves files:
275
+
276
+ ```typescript
277
+ // Reserve files for each worker
278
+ bd_reserve({
279
+ paths: ["src/auth/service.ts", "src/auth/types.ts"],
280
+ reason: "worker-1: auth service implementation",
281
+ });
282
+ ```
283
+
284
+ ### Non-Overlapping Assignments
285
+
286
+ Ensure workers don't edit same files:
287
+
288
+ | Worker | Assigned Files |
289
+ | -------- | ----------------------- |
290
+ | worker-1 | src/auth/\* |
291
+ | worker-2 | src/routes/user/\* |
292
+ | worker-3 | src/components/forms/\* |
293
+
294
+ ## Error Handling
295
+
296
+ ### Worker Fails Acceptance Checks
297
+
298
+ ```typescript
299
+ // Worker sends error message
300
+ swarm_helper({
301
+ operation: "sendTeamMessage",
302
+ team_name: "plan-implementation",
303
+ from_worker: "worker-1",
304
+ to_worker: "leader",
305
+ message: "ERROR: typecheck failed. Issue: missing type for AuthToken",
306
+ });
307
+ ```
308
+
309
+ ### Leader Response
310
+
311
+ 1. Read error from mailbox
312
+ 2. Decide: fix locally or reassign
313
+ 3. Either spawn fix-agent or adjust task
314
+
315
+ ### Worker Gets Blocked
316
+
317
+ ```typescript
318
+ // Worker asks for help
319
+ swarm_helper({
320
+ operation: "sendTeamMessage",
321
+ team_name: "plan-implementation",
322
+ from_worker: "worker-2",
323
+ to_worker: "leader",
324
+ message: "BLOCKED: Need auth service types. Waiting on worker-1.",
325
+ });
326
+ ```
327
+
328
+ ## Graceful Shutdown
329
+
330
+ Leader signals completion:
331
+
332
+ ```typescript
333
+ // After all workers done
334
+ swarm_helper({
335
+ operation: "sendTeamMessage",
336
+ team_name: "plan-implementation",
337
+ from_worker: "leader",
338
+ to_worker: "all",
339
+ message: "SHUTDOWN: All tasks complete. Final verification passed.",
340
+ });
341
+ ```
342
+
343
+ ## When to Use Swarm vs Single Agent
344
+
345
+ | Scenario | Approach |
346
+ | ----------------------------- | ------------ |
347
+ | 1-2 file changes | Single agent |
348
+ | Sequential dependencies | Single agent |
349
+ | 3+ independent parallel tasks | Swarm |
350
+ | Cross-domain work (FE/BE/DB) | Swarm |
351
+ | Time-sensitive parallel work | Swarm |
352
+
353
+ ## Integration with Beads
354
+
355
+ Swarm works on top of Beads:
356
+
357
+ 1. **Plan creates beads** for each task
358
+ 2. **Leader claims parent** bead
359
+ 3. **Workers claim child** beads
360
+ 4. **Completion closes** beads via `bd_done()`
361
+
362
+ ```typescript
363
+ // Leader workflow
364
+ bd_claim(); // Gets parent task
365
+ // ... spawn swarm ...
366
+ // ... monitor completion ...
367
+ bd_done({ id: "parent-task", msg: "Swarm completed all subtasks" });
368
+ ```
369
+
370
+ ## Quick Reference
371
+
372
+ ```
373
+ SWARM LAUNCH:
374
+ 1. Parse plan → identify parallel tasks
375
+ 2. Create delegation packets (swarm-delegate)
376
+ 3. Spawn workers (Task tool, multiple in one message)
377
+ 4. Monitor mailbox (swarm-helper getTeamStatus)
378
+ 5. Synthesize results
379
+
380
+ WORKER EXECUTION:
381
+ 1. Read delegation packet
382
+ 2. Announce start via mailbox
383
+ 3. Execute with constraints
384
+ 4. Run acceptance checks
385
+ 5. Report completion via mailbox
386
+
387
+ COORDINATION:
388
+ - Mailbox: .beads/swarm-mail.jsonl
389
+ - Delegation: .beads/artifacts/<id>/delegation.md
390
+ - File locks: bd_reserve() before spawning
391
+
392
+ SHUTDOWN:
393
+ - All workers done → leader sends SHUTDOWN
394
+ - Run full test suite
395
+ - Close parent bead
396
+ ```
397
+
398
+ ## Rules
399
+
400
+ 1. **Leader spawns, workers execute** - Clear role separation
401
+ 2. **Delegation packets are contracts** - Workers follow them strictly
402
+ 3. **Mailbox for coordination** - All communication through swarm-mail
403
+ 4. **No file conflicts** - Reserve before spawning workers
404
+ 5. **Acceptance checks required** - Workers verify before reporting done
405
+ 6. **Graceful shutdown** - Leader waits for all workers, then shuts down