@engineereddev/fractal-planner 0.1.1

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.
Files changed (46) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +19 -0
  3. package/LICENSE +21 -0
  4. package/README.md +257 -0
  5. package/agents/fp-analyst.md +96 -0
  6. package/agents/fp-context-builder.md +87 -0
  7. package/agents/fp-critic.md +140 -0
  8. package/agents/fp-decomposer.md +261 -0
  9. package/agents/fp-interviewer.md +263 -0
  10. package/agents/fp-linear-sync.md +128 -0
  11. package/agents/fp-researcher.md +82 -0
  12. package/agents/fp-task-tracker.md +134 -0
  13. package/dist/cli/classify-intent.js +118 -0
  14. package/dist/cli/compute-signals.js +495 -0
  15. package/dist/cli/generate-plan.js +14209 -0
  16. package/dist/cli/load-config.js +13661 -0
  17. package/dist/cli/validate-tasks.js +467 -0
  18. package/dist/index.js +24598 -0
  19. package/dist/src/cli/classify-intent.d.ts +3 -0
  20. package/dist/src/cli/compute-signals.d.ts +14 -0
  21. package/dist/src/cli/generate-plan.d.ts +3 -0
  22. package/dist/src/cli/load-config.d.ts +3 -0
  23. package/dist/src/cli/validate-tasks.d.ts +3 -0
  24. package/dist/src/config.d.ts +182 -0
  25. package/dist/src/index.d.ts +12 -0
  26. package/dist/src/phases/clearance.d.ts +12 -0
  27. package/dist/src/phases/decomposition.d.ts +41 -0
  28. package/dist/src/phases/interview.d.ts +17 -0
  29. package/dist/src/phases/planning.d.ts +21 -0
  30. package/dist/src/phases/research.d.ts +9 -0
  31. package/dist/src/types/index.d.ts +116 -0
  32. package/dist/src/utils/draft.d.ts +21 -0
  33. package/dist/src/utils/question-strategies.d.ts +24 -0
  34. package/dist/src/utils/task-parser.d.ts +3 -0
  35. package/hooks/hooks.json +27 -0
  36. package/hooks/nudge-teammate.sh +216 -0
  37. package/hooks/run-comment-checker.sh +91 -0
  38. package/package.json +65 -0
  39. package/skills/commit/SKILL.md +157 -0
  40. package/skills/fp/SKILL.md +857 -0
  41. package/skills/fp/scripts/resolve-env.sh +66 -0
  42. package/skills/handoff/SKILL.md +195 -0
  43. package/skills/implement/SKILL.md +783 -0
  44. package/skills/implement/reference.md +935 -0
  45. package/skills/retry/SKILL.md +333 -0
  46. package/skills/status/SKILL.md +182 -0
@@ -0,0 +1,935 @@
1
+ # fp:implement Reference
2
+
3
+ ## Task Interface
4
+
5
+ Each task in the task tree has this structure:
6
+
7
+ ```typescript
8
+ interface Task {
9
+ id: string; // e.g. "1.2.1"
10
+ description: string; // what to implement
11
+ acceptanceCriteria: string[]; // measurable criteria for verification
12
+ estimatedComplexity: number; // 1-10 scale
13
+ dependencies: string[]; // task IDs that must complete first
14
+ subtasks?: Task[]; // if present, this is NOT a leaf task
15
+ status?: 'pending' | 'in-progress' | 'completed' | 'failed';
16
+ metadata?: {
17
+ filesToModify?: string[]; // files the builder should focus on
18
+ testsRequired?: boolean; // whether tests must be written
19
+ };
20
+ }
21
+ ```
22
+
23
+ Only **leaf tasks** (those without `subtasks`) are executed.
24
+
25
+ ## VerificationReport Format
26
+
27
+ The verification subagent produces structured verification reports:
28
+
29
+ ```typescript
30
+ interface VerificationReport {
31
+ verified: boolean; // true if ALL criteria passed
32
+ passedCriteria: number[]; // indices of passed acceptance criteria (0-based)
33
+ failedCriteria: number[]; // indices of failed acceptance criteria (0-based)
34
+ feedback: string; // human-readable summary
35
+ suggestions?: string[]; // optional improvement suggestions
36
+ }
37
+ ```
38
+
39
+ ## Plan File Formats
40
+
41
+ ### `.fractal-planner/plans/{planId}/tasks.md`
42
+
43
+ The task tree file uses this markdown structure:
44
+
45
+ ```markdown
46
+ # Task Decomposition
47
+
48
+ ## Root Task
49
+ - [ID: root] Main goal description (Complexity: 8)
50
+
51
+ ### Subtasks
52
+ - [ID: 1] First major component (Complexity: 6)
53
+ - [ID: 1.1] Sub-component A (Complexity: 3)
54
+ - Acceptance: Code compiles, tests pass, follows patterns
55
+ - Dependencies: none
56
+ - Files: src/foo.ts
57
+ - Tests Required: yes
58
+ - [ID: 1.2] Sub-component B (Complexity: 4)
59
+ - Acceptance: Integration works, error handling present
60
+ - Dependencies: 1.1
61
+ - Files: src/bar.ts, src/baz.ts
62
+ - Tests Required: yes
63
+ - [ID: 2] Second major component (Complexity: 5)
64
+ - [ID: 2.1] Setup config (Complexity: 2)
65
+ - Acceptance: Config file created, validated by schema
66
+ - Dependencies: none
67
+ - Files: config/setup.json
68
+ - Tests Required: no
69
+ ```
70
+
71
+ ### `.fractal-planner/plans/{planId}/plan.md`
72
+
73
+ The execution plan file uses this markdown structure:
74
+
75
+ ```markdown
76
+ # Implementation Plan
77
+
78
+ ## Execution Order
79
+ 1. [Task 1.1]: Sub-component A
80
+ - Dependencies: none
81
+ - Acceptance:
82
+ 1. Code compiles without errors
83
+ 2. Tests pass
84
+ 3. Follows existing patterns
85
+ 2. [Task 2.1]: Setup config
86
+ - Dependencies: none
87
+ - Acceptance:
88
+ 1. Config file created
89
+ 2. Validated by schema
90
+ 3. [Task 1.2]: Sub-component B
91
+ - Dependencies: 1.1
92
+ - Acceptance:
93
+ 1. Integration works
94
+ 2. Error handling present
95
+ ```
96
+
97
+ ## Valid/Invalid Input Examples
98
+
99
+ ### Valid
100
+
101
+ ```
102
+ /fp:implement abc123
103
+ /fp:implement abc123 --max-iterations 5
104
+ /fp:implement my-session-id-here
105
+ ```
106
+
107
+ ### Invalid
108
+
109
+ ```
110
+ /fp:implement # missing planId
111
+ /fp:implement --max-iterations 3 # missing planId (flag is not a planId)
112
+ ```
113
+
114
+ ## Codebase Context File
115
+
116
+ ### Format
117
+
118
+ `.fractal-planner/plans/{planId}/context.md`:
119
+
120
+ ```markdown
121
+ # Codebase Context
122
+
123
+ ## Project Overview
124
+ [1-2 sentence description of what this project is]
125
+
126
+ ## Tech Stack
127
+ - Language: [e.g. TypeScript]
128
+ - Runtime: [e.g. Bun]
129
+ - Build: [e.g. bun build + tsc]
130
+ - Test: [e.g. bun test / vitest]
131
+ - Package manager: [e.g. bun]
132
+
133
+ ## Project Structure
134
+ [Key directories and their purpose, 5-10 lines max]
135
+
136
+ ## Key Files
137
+ [Entry points, configs, shared types — the files you'd read first]
138
+
139
+ ## Patterns & Conventions
140
+ [Naming, module structure, error handling, export style — what a new contributor needs to know]
141
+
142
+ ## Build & Test Commands
143
+ [Exact commands to build, test, lint]
144
+ ```
145
+
146
+ ### Three-Tier Fallback
147
+
148
+ The implementation lead loads codebase context using this fallback chain:
149
+
150
+ 1. **Tier 1 — Plan-time context file**: `fp:plan` Phase 1 produces `.fractal-planner/plans/{planId}/context.md` as a byproduct of research. If this file exists, use it directly.
151
+ 2. **Tier 2 — Lead-generated context**: If the file doesn't exist (older plan or plan created before this feature), the lead generates it by reading `package.json`, scanning project structure, and identifying patterns. The result is written to `.fractal-planner/plans/{planId}/context.md` for reuse.
152
+ 3. **Tier 3 — Self-discovery**: If both fail, `codebaseContext` is set to empty string and builder explores the codebase itself (no injection).
153
+
154
+ The context is injected into:
155
+ - Builder spawn instructions (so it knows the tech stack, patterns, and conventions)
156
+ - Verification subagent prompt (so it knows what patterns to check against)
157
+
158
+ ## Native Task Format
159
+
160
+ ### TaskCreate Fields
161
+
162
+ | Field | Value |
163
+ |-------|-------|
164
+ | `subject` | `[{planTaskId}] {description truncated to 80 chars}` |
165
+ | `description` | Full static builder payload (see template below) |
166
+ | `activeForm` | `Implementing [{planTaskId}]` |
167
+ | `addBlockedBy` | Array of native task IDs for plan-level dependencies (only those already in `taskMap`) |
168
+
169
+ The `owner` field is set by the builder itself during the claim step (TaskUpdate), using its own name (e.g., `builder-1`). This naming convention enables P3 tooling that reads `owner` from task JSON files.
170
+
171
+ ### Structured Delegation Format
172
+
173
+ The `description` field of each native task holds a rigid 6-section builder payload. This format constrains builder behavior by making guardrails explicit and always present.
174
+
175
+ ```markdown
176
+ ## TASK
177
+ {one-sentence atomic goal from task.description}
178
+
179
+ ## EXPECTED OUTCOME
180
+ 1. {criterion from task.acceptanceCriteria}
181
+ 2. {criterion from task.acceptanceCriteria}
182
+
183
+ ## MUST DO
184
+ - {hint from task.metadata.hints}
185
+ - {reference from task.metadata.references, if any}
186
+
187
+ ## MUST NOT DO
188
+ - Do NOT modify files outside: {task.metadata.filesToModify list}
189
+ - Do NOT add new dependencies
190
+ - Do NOT refactor existing code beyond the task scope
191
+ - {additional guardrail from task.metadata.guardrails}
192
+
193
+ ## CONTEXT
194
+ - Files: {task.metadata.filesToModify}
195
+ - Tests Required: {task.metadata.testsRequired — yes/no}
196
+ - References: {task.metadata.references, or omit line if none}
197
+
198
+ ## VERIFICATION
199
+ - Test Commands: {task.metadata.testCommands, or "none"}
200
+ - Evidence: .fractal-planner/plans/{planId}/evidence/task-{id}-verification.md
201
+ ```
202
+
203
+ #### Section Construction Rules
204
+
205
+ | Section | Source Fields | Notes |
206
+ |---------|-------------|-------|
207
+ | TASK | `task.description` | One sentence only |
208
+ | EXPECTED OUTCOME | `task.acceptanceCriteria` | Numbered list |
209
+ | MUST DO | `task.metadata.hints` + `task.metadata.references` | References formatted as "Read {file}:{line} — {explanation}" |
210
+ | MUST NOT DO | Baseline guardrails + `task.metadata.guardrails` | 3 baseline items always present (see below), then task-specific |
211
+ | CONTEXT | `task.metadata.filesToModify`, `task.metadata.testsRequired`, `task.metadata.references` | Static context only |
212
+ | VERIFICATION | `task.metadata.testCommands`, `task.metadata.testsRequired` | Evidence file path included |
213
+
214
+ **Baseline guardrails** (always in MUST NOT DO, regardless of task metadata):
215
+ 1. `Do NOT modify files outside: {filesToModify list}`
216
+ 2. `Do NOT add new dependencies`
217
+ 3. `Do NOT refactor existing code beyond the task scope`
218
+
219
+ Task-specific guardrails from `task.metadata.guardrails` are appended after these three.
220
+
221
+ **Note**: Dependencies are NOT included in the payload — they are handled by native `addBlockedBy` on TaskCreate.
222
+
223
+ Do NOT include dynamic content (notepad entries, codebase context) in the native task description. Codebase context is injected at builder spawn time; notepad entries are read by the builder per-task from `notepad.md`.
224
+
225
+ ### TaskUpdate Status Patterns
226
+
227
+ | Scenario | `status` | `metadata` |
228
+ |----------|----------|------------|
229
+ | Task claimed by builder | `in_progress` | `{ owner: "builder-N" }` |
230
+ | Verification passed + committed | `completed` | `{ fpStatus: "COMPLETED", iterations: "n/max", commit: "hash", summary: "text" }` |
231
+ | Max iterations reached | `completed` | `{ fpStatus: "FAILED", iterations: "max/max", reason: "text" }` |
232
+ | Builder finished implementation | `in_progress` | `{ fpStatus: "AWAITING_VERIFICATION" }` |
233
+ | Verification failed, retry cycle | `in_progress` | `{ fpStatus: "IMPLEMENTING" }` |
234
+ | Blocked by failed dependency | `deleted` | `{ fpStatus: "SKIPPED", reason: "Blocked by {planTaskId}" }` |
235
+
236
+ **Why `completed` for FAILED?** Native statuses are `pending`, `in_progress`, `completed`, `deleted`. Marking a failed task `completed` causes the native system to auto-clear `blockedBy` on its dependents. The lead then immediately marks those dependents `deleted` (SKIPPED) before the next `TaskList()` call. `metadata.fpStatus` is the authoritative status for FAILED vs COMPLETED tasks.
237
+
238
+ **Why `deleted` for SKIPPED?** `deleted` tasks are invisible to `TaskList()`, preventing them from appearing as ready in future computations.
239
+
240
+ ## Self-Claiming Protocol
241
+
242
+ ### Overview
243
+
244
+ Builders are persistent teammates that run a self-claiming work loop. Instead of the lead computing waves and assigning tasks, builders call `TaskList()` themselves, claim available tasks via `TaskUpdate(owner)`, and read task payloads via `TaskGet()`. The lead shrinks to: spawn builders, handle verification, serialize commits, manage failures.
245
+
246
+ ### Builder Naming Convention
247
+
248
+ Builders are named `builder-1` through `builder-N` (where N = `maxParallelTasks`). The builder name matches the `owner` value set in native task JSON — this is critical for the nudge mechanism (P3) where the TeammateIdle hook reads `owner` from task files.
249
+
250
+ ### Claiming Rules
251
+
252
+ - **One task at a time**: A builder must complete (or be told to move on from) its current task before claiming the next.
253
+ - **Lowest ID first**: When multiple tasks are available, pick the one with the lowest native task ID.
254
+ - **Race handling**: If two builders claim the same task (last-write-wins on `TaskUpdate`), the lead detects duplicate `TASK_CLAIMED` messages and tells the second builder `TASK_ALREADY_CLAIMED`.
255
+
256
+ ### Builder → Lead Messages
257
+
258
+ | Message | When | Payload |
259
+ |---------|------|---------|
260
+ | `TASK_CLAIMED: {planTaskId}` | After claiming via TaskUpdate | `Builder: {builderName}`, `NativeId: {nativeTaskId}` |
261
+ | `IMPLEMENTATION COMPLETE: {planTaskId}` | After finishing implementation | `FILES_MODIFIED:` list, optional `NOTEPAD_ENTRY:` |
262
+ | `CLARIFICATION NEEDED: {planTaskId}` | Iteration 1, before IMPLEMENTATION COMPLETE | `QUESTION:`, `OPTIONS:`, `HEADER:`, `MULTI_SELECT:` |
263
+ | `NO_MORE_TASKS: {builderName}` | When TaskList returns no claimable tasks | None |
264
+
265
+ ### Lead → Builder Messages
266
+
267
+ | Message | When | Effect on Builder |
268
+ |---------|------|-------------------|
269
+ | `VERIFICATION PASSED: {planTaskId}` | After verifier passes | Builder loops to claim next task |
270
+ | `VERIFICATION FAILED: {planTaskId}` | After verifier fails, iterations remain | Builder fixes issues and re-sends IMPLEMENTATION COMPLETE |
271
+ | `MAX_ITERATIONS_REACHED: {planTaskId}` | After verifier fails, no iterations remain | Builder loops to claim next task |
272
+ | `CLARIFICATION ANSWER: {planTaskId}` | After user answers clarification | Builder continues implementation |
273
+ | `TASKS_AVAILABLE` | After commit unblocks new tasks | Idle builder resumes self-claiming loop |
274
+ | `TASK_ALREADY_CLAIMED: {planTaskId}` | Duplicate claim detected | Builder loops to claim a different task |
275
+
276
+ ### TASK_CLAIMED Message Format
277
+
278
+ ```
279
+ TASK_CLAIMED: {planTaskId}
280
+ Builder: {builderName}
281
+ NativeId: {nativeTaskId}
282
+ ```
283
+
284
+ ## Execution State File
285
+
286
+ ### Location
287
+
288
+ `.fractal-planner/plans/{planId}/execution-state.json`
289
+
290
+ ### Format
291
+
292
+ ```json
293
+ {
294
+ "planId": "20260219-153000",
295
+ "team": "fp-impl-20260219-153000",
296
+ "taskMap": {
297
+ "T1.1": "1",
298
+ "T1.2": "2",
299
+ "T2.1": "3"
300
+ },
301
+ "maxIterations": 3,
302
+ "noCommit": false,
303
+ "createdAt": "2026-02-19T15:30:00Z",
304
+ "skippedTasks": {
305
+ "T2.1": "Blocked by T1.1"
306
+ },
307
+ "failureReasons": {
308
+ "T1.1": "Criterion 2 failed: catch block never triggered"
309
+ },
310
+ "iterationMap": {
311
+ "T1.1": 3,
312
+ "T1.2": 1
313
+ },
314
+ "clarificationsUsed": {
315
+ "T1.1": true
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### Field Descriptions
321
+
322
+ | Field | Description |
323
+ |-------|-------------|
324
+ | `planId` | Plan session ID |
325
+ | `team` | Native team name (`fp-impl-{planId}`) |
326
+ | `taskMap` | Maps plan task IDs to native task IDs returned by `TaskCreate` |
327
+ | `maxIterations` | Max builder iterations per task |
328
+ | `noCommit` | Whether `--no-commit` was passed |
329
+ | `createdAt` | ISO timestamp when the file was first written |
330
+ | `skippedTasks` | Map of plan task ID → skip reason (updated incrementally) |
331
+ | `failureReasons` | Map of plan task ID → failure reason (updated incrementally) |
332
+ | `iterationMap` | Map of plan task ID → current iteration number (updated incrementally) |
333
+ | `clarificationsUsed` | Map of plan task ID → boolean, tracks whether clarification was used (updated incrementally) |
334
+
335
+ ### When Written vs. Updated
336
+
337
+ - **Written once**: After all `TaskCreate` calls complete on a fresh run (Step 4.0)
338
+ - **Updated incrementally**:
339
+ - `skippedTasks` and `failureReasons` are updated in-place during Step 5.8 as tasks fail and their dependents are cascade-skipped
340
+ - `iterationMap` is updated when a task is first claimed (Step 5.3) and after each failed verification iteration (Step 5.4)
341
+ - `clarificationsUsed` is updated when a builder uses its clarification for a task (Step 5.5)
342
+
343
+ The file is the sole resume artifact. On resume, the lead reads `taskMap` to map plan task IDs to native task IDs, then calls `TaskList()` to get current statuses.
344
+
345
+ ## Communication Protocol Summary
346
+
347
+ ### Standard Flow (self-claiming with verification subagent)
348
+
349
+ ```
350
+ Team-Lead creates team fp-impl-{planId}
351
+ Team-Lead calls TaskCreate for each leaf task (fresh run only) → writes execution-state.json
352
+ Team-Lead spawns Tracker
353
+ Team-Lead ──INIT──> Tracker (LINEAR_MAPPING only)
354
+ Tracker parses linearMapping into memory
355
+ Tracker ──TRACKER READY──> Team-Lead
356
+
357
+ Team-Lead spawns builder-1..builder-N (persistent)
358
+ Builder: TaskList() → finds pending task with empty blockedBy
359
+ Builder: TaskUpdate(taskId, in_progress, owner: "builder-N") → claims task
360
+ Builder: TaskGet(taskId) → reads full task spec
361
+ Builder ──TASK_CLAIMED: {id}──> Team-Lead
362
+ Team-Lead ──TASK_STARTED: {id}──> Tracker
363
+ Tracker ──ACK_STARTED: {id}──> Team-Lead
364
+ Builder implements → IMPLEMENTATION COMPLETE: {id} with FILES_MODIFIED
365
+ (Parallel mode: builder may SendMessage peer notifications to other builders — max 2 per task)
366
+ Team-Lead spawns Verifier subagent (Task tool):
367
+ Reads modified files, runs tests, checks criteria
368
+ Returns VERIFICATION PASSED or VERIFICATION FAILED
369
+ If PASSED:
370
+ Team-Lead ──VERIFICATION PASSED: {id}──> Builder (loops to claim next)
371
+ Team-Lead adds to commitQueue → processes commit
372
+ Team-Lead ──task──> Committer → COMMIT COMPLETED
373
+ TaskUpdate(nativeId, completed, fpStatus: "COMPLETED", commit: hash)
374
+ Team-Lead ──TASK_COMPLETED: {id}──> Tracker
375
+ Tracker ──ACK_COMPLETED: {id}──> Team-Lead
376
+ If FAILED (iterations remain):
377
+ Team-Lead ──VERIFICATION FAILED: {id}──> Builder (retries in-place)
378
+ No tasks available:
379
+ Builder ──NO_MORE_TASKS──> Team-Lead
380
+ Later: commit unblocks tasks → Team-Lead ──TASKS_AVAILABLE──> idle Builder
381
+ All done → Step 6 (shutdown builders, tracker, delete team)
382
+ ```
383
+
384
+ ### Failure Flow
385
+
386
+ ```
387
+ Builder retries exhaust all iterations without passing verification
388
+ Team-Lead ──ask──> User: "Task {id} failed. Continue or stop?"
389
+ TaskUpdate(nativeId, status: "completed", fpStatus: "FAILED") ← auto-unblocks dependents
390
+ Update execution-state.json failureReasons
391
+ Team-Lead ──TASK_FAILED: {id}──> Tracker (+ Linear failed)
392
+ Tracker ──ACK_FAILED: {id}──> Team-Lead
393
+ Team-Lead computes blocked dependents from plan-level dependency graph
394
+ For each blocked dependent:
395
+ TaskUpdate(depNativeId, status: "deleted", fpStatus: "SKIPPED")
396
+ Update execution-state.json skippedTasks
397
+ Team-Lead ──TASK_SKIPPED: {blocked_id}\nReason: Blocked by {id}──> Tracker
398
+ Tracker ──ACK_SKIPPED: {blocked_id}──> Team-Lead
399
+ Team-Lead ──MAX_ITERATIONS_REACHED: {id}──> Builder (loops to next task)
400
+ ```
401
+
402
+ ### FILES_MODIFIED Message Format
403
+
404
+ Builder completion message (sent to team-lead when implementation is done):
405
+ ```
406
+ IMPLEMENTATION COMPLETE: {id}
407
+
408
+ FILES_MODIFIED:
409
+ - /absolute/path/to/file1.ts
410
+ - /absolute/path/to/file2.test.ts
411
+ - /absolute/path/to/component.tsx
412
+ ```
413
+
414
+ Verifier pass report (returned by verification subagent to lead):
415
+ ```
416
+ VERIFICATION PASSED
417
+ All {N} criteria met.
418
+ [1-2 sentence summary of what was verified]
419
+ ```
420
+
421
+ Verifier failure report (returned by verification subagent to lead):
422
+ ```
423
+ VERIFICATION FAILED
424
+ Failed:
425
+ - Criterion {N}: {text} — {specific failure reason and fix instruction}
426
+ Passed:
427
+ - Criterion {N}: {text}
428
+ Tests: {PASS/FAIL with relevant output}
429
+ Typecheck: {PASS/FAIL with errors if any}
430
+ ```
431
+
432
+ ## Linear Integration
433
+
434
+ ### Overview
435
+
436
+ When `linear.enabled` is `true` in `.fractal-planner/config.json`, the planning phase creates Linear issues mirroring the task tree, and the implementation phase updates issue statuses as work progresses.
437
+
438
+ ### Mapping File Format
439
+
440
+ `.fractal-planner/plans/{planId}/linear-mapping.json`:
441
+
442
+ ```json
443
+ {
444
+ "planId": "session-id",
445
+ "teamId": "team-uuid",
446
+ "projectId": "project-uuid-or-null",
447
+ "resolvedStatuses": {
448
+ "pending": "status-uuid-1",
449
+ "in-progress": "status-uuid-2",
450
+ "completed": "status-uuid-3",
451
+ "failed": "status-uuid-4",
452
+ "review": "status-uuid-5"
453
+ },
454
+ "tasks": {
455
+ "root": { "linearIssueId": "issue-uuid", "linearIdentifier": "TEAM-42" },
456
+ "1": { "linearIssueId": "issue-uuid", "linearIdentifier": "TEAM-43" },
457
+ "1.1": { "linearIssueId": "issue-uuid", "linearIdentifier": "TEAM-44" }
458
+ }
459
+ }
460
+ ```
461
+
462
+ This file is created during `fp:plan` Phase 2.5 and consumed during `fp:implement`. It is gitignored (under `.fractal-planner/`).
463
+
464
+ ### Status Update Points
465
+
466
+ During implementation, the **tracker teammate** (not the team-lead or builder) updates Linear issues at these points:
467
+
468
+ | Event | Linear Status Update |
469
+ |-------|---------------------|
470
+ | Task claimed by builder | `in-progress` |
471
+ | Verification passed | `review` |
472
+ | Task failed (max iterations) | `failed` |
473
+ | Task skipped (blocked dep) | (no change) |
474
+
475
+ After all leaf tasks are processed, parent issue statuses are rolled up bottom-to-top:
476
+ - All children completed → parent marked `completed`
477
+ - Any child in-progress → parent marked `in-progress`
478
+ - All children failed → parent marked `failed`
479
+
480
+ ### Status Resolution Strategy
481
+
482
+ Statuses are resolved once during planning (Phase 2.5) and stored in the mapping file.
483
+
484
+ **When `statusMap` is configured**: Each name (e.g. `"Todo"`, `"In Progress"`) is matched against the team's available statuses by name. If a name doesn't match, it falls back to auto-detect for that status with a warning.
485
+
486
+ **When `statusMap` is NOT configured** (default): Auto-detect by Linear status **type**:
487
+ - `pending` → first status of type `backlog` (or `unstarted` if no backlog type exists)
488
+ - `in-progress` → first status of type `started`
489
+ - `completed` → first status of type `completed`
490
+ - `failed` → first status of type `canceled`
491
+ - `review` → first status with name matching "In Review" (case-insensitive); falls back to resolved `completed` UUID
492
+
493
+ ### Graceful Degradation
494
+
495
+ - If `linear.enabled` is `false` (default), zero Linear calls are made
496
+ - If Linear MCP server is unavailable, planning logs a warning and skips Linear sync — the plan still completes normally
497
+ - If `linearMapping` is `null` during implementation (no mapping file), all Linear updates are silently skipped
498
+
499
+ ## Progress Snapshot
500
+
501
+ `.fractal-planner/plans/{planId}/progress.md` is generated **once at the end of implementation** (Step 6) as a human-readable audit artifact. It is NOT a runtime artifact and is NOT read by any skill for execution state.
502
+
503
+ All runtime state lives in:
504
+ - The native task system (`~/.claude/tasks/fp-impl-{planId}/`)
505
+ - `execution-state.json` (resume artifact)
506
+
507
+ ## Tracker Communication Protocol
508
+
509
+ The `fp-task-tracker` teammate handles Linear sync. The team lead communicates with it via structured text messages.
510
+
511
+ ### Lead → Tracker
512
+
513
+ | Command | When | Payload |
514
+ |---------|------|---------|
515
+ | `INIT` | At spawn | `LINEAR_MAPPING:` section with JSON or `"null"` |
516
+ | `TASK_STARTED: {id}` | After builder claims task | None |
517
+ | `TASK_COMPLETED: {id}` | After verification + commit | `Iterations: n/max`, `Commit: hash`, `Summary: text` |
518
+ | `TASK_FAILED: {id}` | After max iterations | `Iterations: n/max`, `Reason: text` |
519
+ | `TASK_SKIPPED: {id}` | Dep failed/skipped | `Reason: text` |
520
+ | `ROLLUP_PARENTS` | After all tasks | None |
521
+
522
+ ### Tracker → Lead
523
+
524
+ | Response | Meaning |
525
+ |----------|---------|
526
+ | `TRACKER READY` | Init complete, includes `Linear: enabled/disabled` |
527
+ | `ACK_STARTED: {id}` | Linear updated to in-progress |
528
+ | `ACK_COMPLETED: {id}` | Linear updated to review/completed |
529
+ | `ACK_FAILED: {id}` | Linear updated to failed |
530
+ | `ACK_SKIPPED: {id}` | Acknowledged (no Linear update for skips) |
531
+ | `ROLLUP_COMPLETE` | Parent statuses rolled up in Linear |
532
+
533
+ ### INIT Message Format
534
+
535
+ ```
536
+ INIT
537
+ LINEAR_MAPPING:
538
+ {raw JSON content of linear-mapping.json, or the literal string "null"}
539
+ ```
540
+
541
+ ### Message Examples
542
+
543
+ **Lead reports completion:**
544
+ ```
545
+ TASK_COMPLETED: 1.1
546
+ Iterations: 1/3
547
+ Commit: abc1234
548
+ Summary: JWT utility with sign/verify and full test coverage
549
+ ```
550
+
551
+ **Tracker acknowledges:**
552
+ ```
553
+ ACK_COMPLETED: 1.1
554
+ ```
555
+
556
+ **Lead reports failure:**
557
+ ```
558
+ TASK_FAILED: 1.3
559
+ Iterations: 3/3
560
+ Reason: Criterion 2 (error handling) consistently fails — catch block not triggered
561
+ ```
562
+
563
+ **Tracker acknowledges:**
564
+ ```
565
+ ACK_FAILED: 1.3
566
+ ```
567
+
568
+ ## Self-Claiming Execution Protocol
569
+
570
+ Builders self-organize via the native task list. The lead's role is verification, commits, failure handling, and tracker communication.
571
+
572
+ ### Sequential Mode (maxParallelTasks = 1)
573
+
574
+ A single `builder-1` runs the self-claiming loop. Behavior is equivalent to the previous wave-of-1: tasks execute one at a time in dependency order. No race conditions possible.
575
+
576
+ ### Parallel Mode (maxParallelTasks > 1)
577
+
578
+ N builders (`builder-1` through `builder-N`) claim tasks independently. The native `blockedBy` system ensures dependency ordering — builders can only see tasks whose dependencies are all completed. Multiple builders may work on independent tasks simultaneously. Builders may also send up to 2 peer notifications per task to coordinate shared interfaces and flag conflicts (see Peer Communication Protocol).
579
+
580
+ ### Race Condition Handling
581
+
582
+ If two builders call `TaskUpdate(taskId, in_progress, owner)` on the same task, last-write-wins — both builders think they claimed it. The lead detects the race when it receives two `TASK_CLAIMED` messages for the same `planTaskId`:
583
+ 1. First `TASK_CLAIMED` is accepted normally (recorded in `builderTaskMap`).
584
+ 2. Second `TASK_CLAIMED` for the same `planTaskId` triggers: lead sends `TASK_ALREADY_CLAIMED: {planTaskId}` to the second builder.
585
+ 3. The second builder loops back to `TaskList()` and claims a different task.
586
+
587
+ ### Task Unblocking
588
+
589
+ After each commit in Step 5.7, the lead calls `TaskList()` to check for newly available tasks (status: "pending", blockedBy: []). If new tasks appear and idle builders exist:
590
+ - Lead picks an idle builder, moves it from `idleBuilders` to `activeBuilders`.
591
+ - Lead sends `TASKS_AVAILABLE` to wake the builder, which resumes its self-claiming loop.
592
+
593
+ ### Commit Serialization
594
+
595
+ Unchanged from previous protocol — the `commitQueue` is processed one entry at a time in dependency order. Git index cannot handle parallel commits.
596
+
597
+ ### Failure Handling
598
+
599
+ - Builder retries in-place when verification fails (no re-spawn).
600
+ - When max iterations reached: lead asks user Continue/Stop.
601
+ - If Continue: mark failed (TaskUpdate completed+FAILED), compute blocked dependents, mark deleted+SKIPPED for each. Send `MAX_ITERATIONS_REACHED` to builder — builder loops to next task.
602
+ - Other builders are unaffected and continue their self-claiming loops during failure handling.
603
+
604
+ ---
605
+
606
+ ## Cross-Task Notepad
607
+
608
+ File: `.fractal-planner/plans/{planId}/notepad.md`
609
+
610
+ Builders can contribute discoveries via an optional `NOTEPAD_ENTRY` section in their `IMPLEMENTATION COMPLETE` message.
611
+
612
+ ### Builder Message Format (with notepad entry)
613
+
614
+ ```
615
+ IMPLEMENTATION COMPLETE: {id}
616
+
617
+ FILES_MODIFIED:
618
+ - /abs/path/to/file.ts
619
+
620
+ NOTEPAD_ENTRY:
621
+ - PATTERN: SecureHash from src/utils/crypto.ts — use instead of raw Buffer
622
+ - GOTCHA: jsonwebtoken v9 requires explicit algorithm in verify() or throws
623
+ - UTIL: src/utils/validate.ts:45 — input validation helper already exists
624
+ ```
625
+
626
+ `NOTEPAD_ENTRY` is optional. Builders should include it only when they discover something genuinely useful to future tasks.
627
+
628
+ ### Entry Types
629
+
630
+ - `PATTERN`: A useful code pattern or utility found in the codebase
631
+ - `GOTCHA`: A non-obvious constraint or library behavior that caused issues
632
+ - `UTIL`: An existing utility or helper that future builders should reuse
633
+ - `WARN`: A risky area or fragile code that future builders should be careful around
634
+
635
+ ### Notepad.md Format
636
+
637
+ ```markdown
638
+ # Implementation Notepad
639
+
640
+ Plan: {planId}
641
+ Started: {ISO timestamp}
642
+
643
+ ## Entries (most recent last)
644
+ - [Task 1.1] PATTERN: SecureHash from src/utils/crypto.ts — use instead of raw Buffer
645
+ - [Task 1.1] GOTCHA: jsonwebtoken v9 requires explicit algorithm in verify() or throws
646
+ - [Task 1.2] UTIL: src/utils/validate.ts:45 — input validation helper already exists
647
+ ```
648
+
649
+ ### Injection Rules
650
+
651
+ Builders read `notepad.md` themselves as part of the self-claiming loop (Step 4.4, loop step 6). The relevance filter:
652
+ - Include an entry if: it's in the **last 10 entries** overall, OR the task that wrote it had overlapping `filesToModify` with the current task
653
+ - Cap at 10 entries per task to limit context growth
654
+
655
+ ---
656
+
657
+ ## Peer Communication Protocol
658
+
659
+ ### Overview
660
+
661
+ When `maxParallelTasks > 1`, builders can send direct messages to peer builders via `SendMessage`. This enables real-time coordination during parallel work — sharing new interfaces, flagging conflicts, and propagating discoveries without waiting for the notepad cycle (which only propagates after commit).
662
+
663
+ Peer messages are **informational and one-way**. Builders do not wait for responses. The lead sees peer DM summaries in idle notifications automatically (built into agent teams framework).
664
+
665
+ In sequential mode (`maxParallelTasks = 1`), peer communication is disabled (`peerBuilderNames = "none"`).
666
+
667
+ ### Message Format
668
+
669
+ ```
670
+ PEER_NOTIFICATION
671
+ TYPE: {notification_type}
672
+ DETAILS:
673
+ {1-3 lines describing what happened and what the peer should do}
674
+ ```
675
+
676
+ Sent via: `SendMessage(type: "message", recipient: "{peer-builder-name}", content: "...", summary: "...")`
677
+
678
+ ### Notification Types
679
+
680
+ | Type | When to Send | Example |
681
+ |------|-------------|---------|
682
+ | `INTERFACE_CREATED` | Created a new export/type/utility that a peer's task could consume | `Created validateInput(s: string): boolean at src/utils/validate.ts:10. Use this instead of inline validation.` |
683
+ | `PATTERN_FOUND` | Discovered a codebase convention or library quirk affecting a peer's work area | `This codebase uses zod v4 parse() not safeParse(). Your task touches the same validation layer.` |
684
+ | `CONFLICT_WARNING` | Detected overlapping file modifications or incompatible interface assumptions | `I'm modifying src/config.ts exports — your task also imports from it. I renamed loadConfig to loadConfigAsync.` |
685
+ | `FILE_MOVED` | Moved or renamed a file that a peer's task references | `Moved src/utils/helpers.ts to src/utils/string-helpers.ts. Your task references the old path.` |
686
+
687
+ ### Budget Rules
688
+
689
+ - **2 messages max per task** — counter resets when claiming a new task
690
+ - Send to the **specific peer(s) affected**, not broadcast to all peers
691
+ - If unsure whether a peer is affected, don't send — false positives waste turns
692
+ - Each message consumes one tool call (one builder turn), so budget is self-limiting
693
+
694
+ ### Receiving Protocol
695
+
696
+ Builders may receive `PEER_NOTIFICATION` messages at any point during their work:
697
+ 1. Read the message content
698
+ 2. If relevant to current task: adapt approach (e.g., reuse a newly created utility, adjust for a renamed import)
699
+ 3. If not relevant: ignore and continue
700
+ 4. **Never reply** to peer messages — they are one-way
701
+ 5. **Never block** waiting for peer input
702
+
703
+ ### Interaction with Notepad
704
+
705
+ Peer messages and notepad entries serve different timescales:
706
+ - **Peer messages**: real-time, within the current wave (reaches concurrent builders immediately)
707
+ - **Notepad entries**: post-commit, for future waves (reaches builders who claim tasks later)
708
+
709
+ If a discovery is useful for both current peers AND future tasks, do both:
710
+ 1. Send a `PEER_NOTIFICATION` to affected concurrent peers
711
+ 2. Include it as a `NOTEPAD_ENTRY` in your `IMPLEMENTATION COMPLETE` message
712
+
713
+ ### When NOT to Send
714
+
715
+ - Discovery only matters for future tasks → use `NOTEPAD_ENTRY` only
716
+ - Not sure if peer is affected → skip (avoid false positives)
717
+ - Budget exhausted (2 messages sent for current task) → save it for `NOTEPAD_ENTRY`
718
+ - Sequential mode (`peerBuilderNames` is "none") → protocol is disabled
719
+ - Trivial changes that don't affect peers → skip
720
+
721
+ ---
722
+
723
+ ## Evidence File Format
724
+
725
+ File: `.fractal-planner/plans/{planId}/evidence/task-{id}-verification.md`
726
+
727
+ Written by the verifier subagent after each verification pass. Used by the lead to extract PASS/FAIL (more robust than parsing agent messages), and by `fp:status` for reporting.
728
+
729
+ ### Format
730
+
731
+ ```markdown
732
+ # Verification Evidence: Task {id}
733
+
734
+ Result: PASS | FAIL
735
+ Timestamp: {ISO timestamp}
736
+ Task: {description}
737
+
738
+ ## Criteria Results
739
+ | # | Criterion | Result | Evidence |
740
+ |---|-----------|--------|---------|
741
+ | 1 | {text} | PASS/FAIL | {code snippet, "met", or specific failure reason} |
742
+ | 2 | {text} | PASS/FAIL | {code snippet, "met", or specific failure reason} |
743
+
744
+ ## Test Output
745
+ ```
746
+ {test command output or "Tests not required"}
747
+ ```
748
+
749
+ ## Typecheck Output
750
+ ```
751
+ {typecheck output or "No tsconfig.json found"}
752
+ ```
753
+
754
+ ## Files Reviewed
755
+ - /absolute/path/to/reviewed/file.ts
756
+
757
+ ## Summary
758
+ {1-2 sentence summary of the verification result}
759
+ ```
760
+
761
+ ### Lead Behavior with Evidence Files
762
+
763
+ - Lead reads the evidence file (not just the agent message) to extract `Result: PASS | FAIL`
764
+ - On resume: evidence files from the previous session provide failure context for builders retrying tasks
765
+ - In `fp:status` output: evidence files are linked for FAILED tasks
766
+
767
+ ---
768
+
769
+ ## Handoff File Format
770
+
771
+ File: `.fractal-planner/plans/{planId}/handoff.md`
772
+
773
+ Generated by `/fp:handoff` to provide a dense context summary for session continuation. When a large plan exhausts the context window mid-implementation, the handoff file captures volatile state (discoveries, iteration counts, failure details) so the next `/fp:implement` session resumes with full context.
774
+
775
+ ### When Generated
776
+
777
+ Run `/fp:handoff {planId}` after an implementation session ends (or is interrupted). Can be run multiple times — each run overwrites the previous handoff.
778
+
779
+ ### How Consumed
780
+
781
+ During `/fp:implement` resume (Step 4.1):
782
+ 1. Lead checks for `handoff.md` in the plan directory
783
+ 2. If found, extracts "Key Discoveries" section → injected into builder spawn context (Step 5.1)
784
+ 3. "Resume Notes" section → displayed to user
785
+ 4. Structural resume still uses `execution-state.json` + `TaskList()` — handoff.md adds curated context only
786
+
787
+ ### Format
788
+
789
+ ```markdown
790
+ # Handoff: {planId}
791
+ Generated: {ISO timestamp}
792
+
793
+ ## Progress
794
+ Completed: N/{total} | Failed: M | Skipped: S | Remaining: R
795
+
796
+ ## Completed Tasks
797
+ - {planTaskId}: {description} | Iterations: n/max | Commit: {hash}
798
+
799
+ ## Failed Tasks
800
+ - {planTaskId}: {description} | Iterations: max/max | Reason: {reason}
801
+
802
+ ## Skipped Tasks
803
+ - {planTaskId}: {description} | Reason: Blocked by {depId}
804
+
805
+ ## In Progress (Interrupted)
806
+ - {planTaskId}: {description} | Iteration: n/max | Owner: {builder}
807
+
808
+ ## Pending Tasks
809
+ - {planTaskId}: {description} | Blocked by: {deps or "none (ready)"}
810
+
811
+ ## Key Discoveries
812
+ {verbatim entries from notepad.md ## Entries section}
813
+
814
+ ## Resume Notes
815
+ - Iteration counts and clarification state preserved in execution-state.json
816
+ - {task-specific warnings based on failure patterns, if any}
817
+ - {note if all tasks completed: "All tasks complete. No resume needed."}
818
+
819
+ ## Resume Command
820
+ /fp:implement {planId}
821
+ ```
822
+
823
+ ### Section Rules
824
+
825
+ - Omit "Failed Tasks", "Skipped Tasks", "In Progress" sections when their count is zero
826
+ - Use description from `plan.md` lookup, not truncated subject from native task
827
+ - For IN_PROGRESS: include owner from task JSON metadata, iteration count from `iterationMap` in execution-state.json
828
+ - For FAILED: prefer evidence file reason, fall back to `failureReasons` in execution-state.json
829
+
830
+ ### Relationship to Other Artifacts
831
+
832
+ | Artifact | Purpose | Handoff Interaction |
833
+ |----------|---------|---------------------|
834
+ | `execution-state.json` | Structural resume (taskMap, statuses, iterations) | Handoff reads; implement resumes from it |
835
+ | `progress.md` | Human-readable snapshot (written at end of implement) | Handoff is independent — may exist before progress.md |
836
+ | `notepad.md` | Builder discoveries during implementation | Handoff copies Key Discoveries from notepad entries |
837
+ | Native task files | Runtime task state | Handoff reads for classification; implement resumes via TaskList() |
838
+
839
+ ---
840
+
841
+ ## Builder Clarification Protocol
842
+
843
+ Builders may request one clarification per task, on iteration 1 only. This allows builders to surface genuine ambiguities without wasting a full iteration on a wrong assumption.
844
+
845
+ ### CLARIFICATION NEEDED Format
846
+
847
+ ```
848
+ CLARIFICATION NEEDED: {id}
849
+
850
+ QUESTION:
851
+ {clear, specific question about the task — one question only}
852
+
853
+ OPTIONS:
854
+ - {option 1 label} | {option 1 description}
855
+ - {option 2 label} | {option 2 description}
856
+ - {option 3 label} | {option 3 description}
857
+
858
+ HEADER: {12 chars max}
859
+ MULTI_SELECT: false
860
+ ```
861
+
862
+ ### Lead Response Protocol
863
+
864
+ 1. Intercept `CLARIFICATION NEEDED` message from builder
865
+ 2. Verify this is iteration 1 (tracked in `clarificationsUsed` map). If `clarificationsUsed[taskId]` is already true OR iteration > 1:
866
+ - Reject: `SendMessage(recipient: builder, "Clarification only allowed on iteration 1. Proceed with task spec only.")`
867
+ 3. If valid: parse the message, call `AskUserQuestion` with the question
868
+ 4. Mark `clarificationsUsed[taskId] = true`
869
+ 5. Forward answer to builder:
870
+ ```
871
+ CLARIFICATION ANSWER: {id}
872
+ User selected: "{option}"
873
+ Context: {additional text if any}
874
+ ```
875
+ 6. Builder continues implementation with the answer
876
+
877
+ ### Budget Enforcement
878
+
879
+ - 1 clarification maximum per task, iteration 1 only
880
+ - Subsequent iterations: lead rejects with "Proceed with failure report and task spec only"
881
+ - In parallel mode: clarification requests are processed one at a time (AskUserQuestion is sequential)
882
+
883
+ ---
884
+
885
+ ## Nudge Mechanism (P3)
886
+
887
+ ### Overview
888
+
889
+ A `TeammateIdle` hook that detects stalled builders and re-injects continuation prompts.
890
+ Prevents builders from going permanently idle while still owning in-progress tasks.
891
+
892
+ ### Hook Behavior
893
+
894
+ | Teammate | Hook behavior |
895
+ |----------|---------------|
896
+ | `builder-*` on `fp-impl-*` | Active -- scans tasks, may re-inject |
897
+ | `tracker` | Filtered out |
898
+ | `committer-*` | Filtered out |
899
+ | Non-fp-impl teams | Filtered out |
900
+
901
+ ### State File
902
+
903
+ Location: `~/.claude/teams/{team_name}/nudge-{teammate_name}.json`
904
+
905
+ ```json
906
+ { "retries": 1, "lastRetryAt": "2026-02-20T12:00:00Z", "taskId": "5" }
907
+ ```
908
+
909
+ Lifecycle:
910
+ - Created on first stall detection for a task
911
+ - Retries increment on subsequent stalls for the same task
912
+ - **Reset** when builder moves to a different task (taskId mismatch)
913
+ - **Deleted** when builder has no in_progress tasks (legitimate idle)
914
+ - **Deleted** when retries >= maxRetries (give up)
915
+
916
+ ### Configuration
917
+
918
+ ```json
919
+ { "nudge": { "enabled": true, "maxRetries": 3 } }
920
+ ```
921
+
922
+ ### Verification Guard
923
+
924
+ The nudge hook reads `fpStatus` from task metadata to distinguish "stuck implementing" from "waiting for verification":
925
+
926
+ - **`fpStatus: "AWAITING_VERIFICATION"`** — Builder has sent `IMPLEMENTATION COMPLETE` and is waiting for the lead's verification response. The hook exits 0 (no nudge).
927
+ - **`fpStatus: "IMPLEMENTING"`** (or absent) — Builder is actively implementing. If idle, this is a genuine stall and the hook fires the continuation prompt.
928
+
929
+ Lifecycle:
930
+ 1. Builder finishes implementation → sets `fpStatus: "AWAITING_VERIFICATION"` (TaskUpdate, step 9)
931
+ 2. Builder sends `IMPLEMENTATION COMPLETE` (SendMessage, step 10)
932
+ 3. Lead spawns verifier, gets result
933
+ 4. If **VERIFICATION PASSED**: lead proceeds to commit (fpStatus becomes "COMPLETED" in Step 5.7)
934
+ 5. If **VERIFICATION FAILED**: lead resets `fpStatus: "IMPLEMENTING"` (Step 5.4), then sends failure details to builder. This re-enables nudging if the builder stalls during the retry.
935
+