@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.
- package/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +19 -0
- package/LICENSE +21 -0
- package/README.md +257 -0
- package/agents/fp-analyst.md +96 -0
- package/agents/fp-context-builder.md +87 -0
- package/agents/fp-critic.md +140 -0
- package/agents/fp-decomposer.md +261 -0
- package/agents/fp-interviewer.md +263 -0
- package/agents/fp-linear-sync.md +128 -0
- package/agents/fp-researcher.md +82 -0
- package/agents/fp-task-tracker.md +134 -0
- package/dist/cli/classify-intent.js +118 -0
- package/dist/cli/compute-signals.js +495 -0
- package/dist/cli/generate-plan.js +14209 -0
- package/dist/cli/load-config.js +13661 -0
- package/dist/cli/validate-tasks.js +467 -0
- package/dist/index.js +24598 -0
- package/dist/src/cli/classify-intent.d.ts +3 -0
- package/dist/src/cli/compute-signals.d.ts +14 -0
- package/dist/src/cli/generate-plan.d.ts +3 -0
- package/dist/src/cli/load-config.d.ts +3 -0
- package/dist/src/cli/validate-tasks.d.ts +3 -0
- package/dist/src/config.d.ts +182 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/phases/clearance.d.ts +12 -0
- package/dist/src/phases/decomposition.d.ts +41 -0
- package/dist/src/phases/interview.d.ts +17 -0
- package/dist/src/phases/planning.d.ts +21 -0
- package/dist/src/phases/research.d.ts +9 -0
- package/dist/src/types/index.d.ts +116 -0
- package/dist/src/utils/draft.d.ts +21 -0
- package/dist/src/utils/question-strategies.d.ts +24 -0
- package/dist/src/utils/task-parser.d.ts +3 -0
- package/hooks/hooks.json +27 -0
- package/hooks/nudge-teammate.sh +216 -0
- package/hooks/run-comment-checker.sh +91 -0
- package/package.json +65 -0
- package/skills/commit/SKILL.md +157 -0
- package/skills/fp/SKILL.md +857 -0
- package/skills/fp/scripts/resolve-env.sh +66 -0
- package/skills/handoff/SKILL.md +195 -0
- package/skills/implement/SKILL.md +783 -0
- package/skills/implement/reference.md +935 -0
- package/skills/retry/SKILL.md +333 -0
- package/skills/status/SKILL.md +182 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fp:implement
|
|
3
|
+
description: Execute a fractal-planner plan using builder agents with lead-spawned verification subagents. Takes a plan session ID and implements all tasks in dependency order.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
argument-hint: <plan-session-id> [--max-iterations N]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Fractal Planner: Implementation Phase
|
|
9
|
+
|
|
10
|
+
You are the **team lead** orchestrating implementation of a fractal-planner plan. You will load the plan, create a builder agent team with lead-spawned verification subagents, and coordinate execution of all tasks in dependency order.
|
|
11
|
+
|
|
12
|
+
## Step 1: Parse Arguments & Load Plan
|
|
13
|
+
|
|
14
|
+
Parse `$ARGUMENTS` to extract:
|
|
15
|
+
- `planId` — the first positional argument (required)
|
|
16
|
+
- `--max-iterations N` — optional, defaults to 3
|
|
17
|
+
- `--no-commit` — optional, skip git commits (defaults to false)
|
|
18
|
+
|
|
19
|
+
Usage examples:
|
|
20
|
+
- `/fp:implement abc123`
|
|
21
|
+
- `/fp:implement abc123 --max-iterations 5`
|
|
22
|
+
- `/fp:implement abc123 --no-commit`
|
|
23
|
+
- `/fp:implement abc123 --max-iterations 5 --no-commit`
|
|
24
|
+
|
|
25
|
+
If no `planId` is provided, report the error and stop:
|
|
26
|
+
> "Usage: `/fp:implement <plan-session-id> [--max-iterations N] [--no-commit]`"
|
|
27
|
+
|
|
28
|
+
Read both plan files from `.fractal-planner/plans/{planId}/`:
|
|
29
|
+
1. `tasks.md` — the task tree
|
|
30
|
+
2. `plan.md` — execution order and acceptance criteria
|
|
31
|
+
|
|
32
|
+
If either file does not exist, report the error and stop:
|
|
33
|
+
> "Plan '{planId}' not found. Run `/fp:plan` first to create a plan, then use the session ID."
|
|
34
|
+
|
|
35
|
+
## Step 1.5: Load Linear Mapping & Check for Resume
|
|
36
|
+
|
|
37
|
+
**Linear mapping**: Check if `.fractal-planner/plans/{planId}/linear-mapping.json` exists:
|
|
38
|
+
- **If yes**: Read its **raw content** as `linearMappingContent` (a string). This will be passed to the tracker teammate. The lead no longer uses it directly.
|
|
39
|
+
- **If no**: Set `linearMappingContent` to `null`. All Linear updates will be skipped.
|
|
40
|
+
|
|
41
|
+
**Resume detection**: Check if `.fractal-planner/plans/{planId}/execution-state.json` exists:
|
|
42
|
+
- **If yes**: Read and parse it. Set `isResume = true`. Extract:
|
|
43
|
+
- `taskMap` — object mapping plan task IDs to native task IDs (e.g., `{ "T1.1": "1", "T2.1": "2" }`)
|
|
44
|
+
- `skippedTasks` — object mapping plan task IDs to skip reasons
|
|
45
|
+
- `failureReasons` — object mapping plan task IDs to failure reasons
|
|
46
|
+
- `iterationMap` — object mapping plan task IDs to iteration numbers (default `{}` if absent)
|
|
47
|
+
- `clarificationsUsed` — object mapping plan task IDs to booleans (default `{}` if absent)
|
|
48
|
+
- `maxIterations`, `noCommit` — override command-line defaults if present
|
|
49
|
+
- **If no**: Set `executionState = null`, `isResume = false`.
|
|
50
|
+
|
|
51
|
+
## Step 2: Load Codebase Context
|
|
52
|
+
|
|
53
|
+
Load codebase context so builder teammates don't waste turns exploring the project from scratch. Use a three-tier fallback:
|
|
54
|
+
|
|
55
|
+
1. **Try Tier 1 — Plan-time context file**: Read `.fractal-planner/plans/{planId}/context.md`. If it exists, use its contents as `codebaseContext`.
|
|
56
|
+
|
|
57
|
+
2. **Try Tier 2 — Generate context now**: If the file doesn't exist (older plan, or plan was created before this feature), generate the context yourself:
|
|
58
|
+
- Read `package.json` for tech stack, scripts, and dependencies
|
|
59
|
+
- Use Glob to map the project structure (key directories and their purpose)
|
|
60
|
+
- Read key entry points and config files
|
|
61
|
+
- Identify patterns and conventions from existing source files
|
|
62
|
+
- Write the result to `.fractal-planner/plans/{planId}/context.md` using the format from [reference.md](./reference.md), then use it as `codebaseContext`.
|
|
63
|
+
|
|
64
|
+
3. **Tier 3 — Self-discovery fallback**: If context generation fails for any reason (e.g. empty repo, no package.json), set `codebaseContext` to an empty string. Builder will explore the codebase itself.
|
|
65
|
+
|
|
66
|
+
## Step 3: Determine Execution Order
|
|
67
|
+
|
|
68
|
+
Parse the task tree from `.fractal-planner/plans/{planId}/tasks.md`:
|
|
69
|
+
- Extract each task's **ID**, **description**, **acceptance criteria**, **dependencies**, and **metadata** (filesToModify, testsRequired, implementationHints, references, guardrails, testCommands)
|
|
70
|
+
- Identify **leaf tasks** (tasks with no subtasks) — only leaf tasks are executed
|
|
71
|
+
|
|
72
|
+
Topologically sort leaf tasks by their dependencies:
|
|
73
|
+
- A task is "ready" when all its dependencies are completed
|
|
74
|
+
- Tasks with no dependencies come first
|
|
75
|
+
- If circular dependencies are detected, warn the user and proceed with remaining tasks in document order
|
|
76
|
+
|
|
77
|
+
Display the execution plan to the user:
|
|
78
|
+
```
|
|
79
|
+
## Execution Plan ({planId})
|
|
80
|
+
{N} leaf tasks to implement:
|
|
81
|
+
1. [{id}] {description} (deps: {deps or "none"})
|
|
82
|
+
2. [{id}] {description} (deps: {deps or "none"})
|
|
83
|
+
...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Step 4: Create Agent Team & Initialize Native Task System
|
|
87
|
+
|
|
88
|
+
Create agent team **`fp-impl-{planId}`**.
|
|
89
|
+
|
|
90
|
+
**IMPORTANT**: When you create the team, you automatically become the team lead with agent name **`team-lead`**.
|
|
91
|
+
This is the name teammates must use when sending you messages via the SendMessage tool.
|
|
92
|
+
|
|
93
|
+
### 4.0: Register Tasks in Native System (fresh run only)
|
|
94
|
+
|
|
95
|
+
**This step runs immediately after team creation, before Step 4.1.**
|
|
96
|
+
|
|
97
|
+
If `isResume == false`:
|
|
98
|
+
```
|
|
99
|
+
taskMap = {}
|
|
100
|
+
For each leaf task in topological order:
|
|
101
|
+
nativeId = TaskCreate({
|
|
102
|
+
subject: "[{task.id}] {task.description truncated to 80 chars}",
|
|
103
|
+
description: {full static payload — see reference.md "Structured Delegation Format"},
|
|
104
|
+
activeForm: "Implementing [{task.id}]",
|
|
105
|
+
addBlockedBy: [taskMap[depId] for depId in task.dependencies if depId in taskMap]
|
|
106
|
+
}).id
|
|
107
|
+
taskMap[task.id] = nativeId
|
|
108
|
+
|
|
109
|
+
Write .fractal-planner/plans/{planId}/execution-state.json:
|
|
110
|
+
{
|
|
111
|
+
"planId": "{planId}",
|
|
112
|
+
"team": "fp-impl-{planId}",
|
|
113
|
+
"taskMap": taskMap,
|
|
114
|
+
"maxIterations": maxIterations,
|
|
115
|
+
"noCommit": noCommit,
|
|
116
|
+
"createdAt": "{ISO timestamp}",
|
|
117
|
+
"skippedTasks": {},
|
|
118
|
+
"failureReasons": {},
|
|
119
|
+
"iterationMap": {},
|
|
120
|
+
"clarificationsUsed": {}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
If `isResume == true`:
|
|
125
|
+
- Display: "Resuming — {count} tasks registered in native system."
|
|
126
|
+
|
|
127
|
+
### 4.1: Reconcile State on Resume
|
|
128
|
+
|
|
129
|
+
If `isResume == true`:
|
|
130
|
+
1. Call `TaskList()` to get the current state of all native tasks in the team
|
|
131
|
+
2. Initialize `completedTasks = {}`, `failedTasks = {}`, `skippedTasks = {}`
|
|
132
|
+
3. For each `(planTaskId, nativeId)` in `taskMap`:
|
|
133
|
+
- Find the native task in the live task list by `id`
|
|
134
|
+
- If `nativeTask.status == "in_progress"`: call `TaskUpdate(nativeId, status: "pending")` — reset orphaned in-progress task
|
|
135
|
+
- Else if `nativeTask.status == "completed"` AND `nativeTask.metadata.fpStatus == "FAILED"`: `failedTasks.add(planTaskId)`
|
|
136
|
+
- Else if `nativeTask.status == "completed"` (fpStatus absent or "COMPLETED"): `completedTasks.add(planTaskId)`
|
|
137
|
+
- `pending` or `deleted` tasks → handled naturally by builder self-claiming
|
|
138
|
+
4. `skippedTasks` = set of plan task IDs from `executionState.skippedTasks` keys
|
|
139
|
+
5. **Detect falsely-ready tasks** (critical for resume — prevents builders from claiming tasks whose dependencies failed):
|
|
140
|
+
```
|
|
141
|
+
For each pending task in liveTaskList where blockedBy is empty:
|
|
142
|
+
planTaskId = inverse taskMap lookup for task.id
|
|
143
|
+
For each depId in task's dependency list (from parsed tasks.md):
|
|
144
|
+
depNativeId = taskMap[depId]
|
|
145
|
+
depNativeTask = find in liveTaskList by id
|
|
146
|
+
if depNativeTask.status == "completed" AND depNativeTask.metadata.fpStatus == "FAILED":
|
|
147
|
+
TaskUpdate(task.id, status: "deleted", metadata: { fpStatus: "SKIPPED", reason: "Blocked by failed dependency {depId}" })
|
|
148
|
+
Read execution-state.json, set skippedTasks[planTaskId] = "Blocked by {depId}", write back
|
|
149
|
+
skippedTasks.add(planTaskId)
|
|
150
|
+
break
|
|
151
|
+
if depNativeTask.status == "deleted":
|
|
152
|
+
TaskUpdate(task.id, status: "deleted", metadata: { fpStatus: "SKIPPED", reason: "Blocked by skipped dependency {depId}" })
|
|
153
|
+
Read execution-state.json, set skippedTasks[planTaskId] = "Blocked by {depId}", write back
|
|
154
|
+
skippedTasks.add(planTaskId)
|
|
155
|
+
break
|
|
156
|
+
```
|
|
157
|
+
This must run before spawning builders so they never see these falsely-ready tasks.
|
|
158
|
+
6. Display resume stats:
|
|
159
|
+
```
|
|
160
|
+
## Resuming Implementation ({planId})
|
|
161
|
+
Previous progress found:
|
|
162
|
+
- Completed: {N}
|
|
163
|
+
- Failed: {N}
|
|
164
|
+
- Skipped: {N}
|
|
165
|
+
- Remaining: {N}
|
|
166
|
+
Continuing from where the previous session left off.
|
|
167
|
+
```
|
|
168
|
+
7. Check for `.fractal-planner/plans/{planId}/handoff.md`. If found:
|
|
169
|
+
- Read the "## Key Discoveries" section → store as `handoffDiscoveries`
|
|
170
|
+
- Read the "## Resume Notes" section → display to user
|
|
171
|
+
- Log: "Handoff context loaded from previous session"
|
|
172
|
+
|
|
173
|
+
If `isResume == false`:
|
|
174
|
+
- Initialize empty sets: `completedTasks = {}`, `failedTasks = {}`, `skippedTasks = {}`
|
|
175
|
+
|
|
176
|
+
### 4.2: Prepare Evidence Directory & Notepad
|
|
177
|
+
|
|
178
|
+
**Create evidence directory**:
|
|
179
|
+
```bash
|
|
180
|
+
mkdir -p .fractal-planner/plans/{planId}/evidence
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Initialize notepad**: If this is a fresh run (`isResume == false`), create an empty notepad file:
|
|
184
|
+
|
|
185
|
+
Write `.fractal-planner/plans/{planId}/notepad.md` with:
|
|
186
|
+
```markdown
|
|
187
|
+
# Implementation Notepad
|
|
188
|
+
|
|
189
|
+
Plan: {planId}
|
|
190
|
+
Started: {ISO timestamp}
|
|
191
|
+
|
|
192
|
+
## Entries (most recent last)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
If this is a resume (`isResume == true`), check if `notepad.md` already exists — if not, create it using the same format.
|
|
196
|
+
|
|
197
|
+
### 4.2.5: Detect Commit Style (once, before tracker spawn)
|
|
198
|
+
|
|
199
|
+
Run git log to detect the repository's commit style **once** and cache it for all subsequent committer spawns:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git log --oneline -10 2>/dev/null | head -10
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Analyze the output to determine:
|
|
206
|
+
- **`commitStyle`**: `SEMANTIC` (if commits use conventional format like `feat:`, `fix:`, `chore:`), `SHORT` (if messages are very brief, under 40 chars), or `PLAIN` (standard English sentences)
|
|
207
|
+
- **`commitLang`**: `KOREAN` (if commits are primarily in Korean), or `ENGLISH` (otherwise)
|
|
208
|
+
|
|
209
|
+
If git log fails or returns no commits, set `commitStyle = "PLAIN"`, `commitLang = "ENGLISH"`.
|
|
210
|
+
|
|
211
|
+
Store `commitStyle` and `commitLang` for injection into all committer spawns in Step 5.7.
|
|
212
|
+
|
|
213
|
+
### 4.3: Spawn Tracker Teammate
|
|
214
|
+
|
|
215
|
+
Spawn a **persistent tracker teammate** that lives for the entire session. It handles all Linear status updates.
|
|
216
|
+
|
|
217
|
+
Name: **tracker**
|
|
218
|
+
Agent: `fp-task-tracker`
|
|
219
|
+
Model: sonnet
|
|
220
|
+
|
|
221
|
+
Send the tracker its initialization payload via `SendMessage`:
|
|
222
|
+
```
|
|
223
|
+
INIT
|
|
224
|
+
LINEAR_MAPPING:
|
|
225
|
+
{linearMappingContent or "null"}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Wait for the tracker to respond with `TRACKER READY`** before proceeding.
|
|
229
|
+
|
|
230
|
+
**Note**: After sending INIT and receiving TRACKER READY, the tracker sits idle until you send `TASK_STARTED`. Do not send any other messages to the tracker between now and Step 5.3.
|
|
231
|
+
|
|
232
|
+
## Step 4.4: Define Builder & Verifier Specs
|
|
233
|
+
|
|
234
|
+
Builders are **persistent teammates** that run a self-claiming work loop across multiple tasks. Verification is handled by a **lead-spawned verification subagent** after the builder completes — no hooks or persistent verifier teammate needed.
|
|
235
|
+
|
|
236
|
+
### Builder Teammate Spec
|
|
237
|
+
|
|
238
|
+
Name: **builder-{N}** (where N = 1 to `maxParallelTasks`)
|
|
239
|
+
Tools: `Read`, `Write`, `Edit`, `Bash`, `Glob`, `Grep`, `TaskList`, `TaskUpdate`, `TaskGet`, `SendMessage`
|
|
240
|
+
|
|
241
|
+
Instructions for builder (inject `{builderName}`, `{codebaseContext}`, `{planId}`, `{peerBuilderNames}`, `{handoffDiscoveries}`):
|
|
242
|
+
```
|
|
243
|
+
You are {builderName}, a persistent builder agent on the fp-impl-{planId} team.
|
|
244
|
+
|
|
245
|
+
{codebaseContext}
|
|
246
|
+
|
|
247
|
+
{if handoffDiscoveries is non-empty:}
|
|
248
|
+
PREVIOUS SESSION DISCOVERIES (from handoff):
|
|
249
|
+
{handoffDiscoveries content}
|
|
250
|
+
{end if}
|
|
251
|
+
|
|
252
|
+
Your peer builders: {peerBuilderNames}
|
|
253
|
+
|
|
254
|
+
SELF-CLAIMING WORK LOOP:
|
|
255
|
+
Repeat the following loop until no tasks remain:
|
|
256
|
+
|
|
257
|
+
1. Call TaskList() → filter for tasks with status: "pending" and blockedBy: [] (empty).
|
|
258
|
+
2. Pick the lowest-ID task from the filtered list. If no tasks match → send "NO_MORE_TASKS: {builderName}" to "team-lead" and STOP.
|
|
259
|
+
3. Call TaskUpdate(taskId, status: "in_progress", owner: "{builderName}") to claim the task.
|
|
260
|
+
4. Call TaskGet(taskId) → read the full task description (contains the complete task spec).
|
|
261
|
+
5. Parse the plan task ID from the subject field: "[{planTaskId}] ...".
|
|
262
|
+
(Peer message budget resets to 0 for the new task.)
|
|
263
|
+
6. Read .fractal-planner/plans/{planId}/notepad.md. Filter for relevant entries: include if the entry is among the last 10, OR the entry's task had overlapping files with the current task. Cap at 10 entries.
|
|
264
|
+
7. Send to "team-lead":
|
|
265
|
+
"TASK_CLAIMED: {planTaskId}
|
|
266
|
+
Builder: {builderName}
|
|
267
|
+
NativeId: {nativeTaskId}"
|
|
268
|
+
8. Implement the task following ALL rules below.
|
|
269
|
+
9. When done, call TaskUpdate(taskId, metadata: { fpStatus: "AWAITING_VERIFICATION" }).
|
|
270
|
+
This signals to the nudge hook that you are legitimately waiting, not stalled.
|
|
271
|
+
10. Immediately after step 9, send to "team-lead":
|
|
272
|
+
"IMPLEMENTATION COMPLETE: {planTaskId}
|
|
273
|
+
|
|
274
|
+
FILES_MODIFIED:
|
|
275
|
+
- /absolute/path/to/file1.ts
|
|
276
|
+
- /absolute/path/to/file2.test.ts
|
|
277
|
+
|
|
278
|
+
NOTEPAD_ENTRY:
|
|
279
|
+
- PATTERN: description (only if you discovered something useful)"
|
|
280
|
+
Include ALL files you created or modified. Use absolute paths.
|
|
281
|
+
NOTEPAD_ENTRY is optional — only include if you discovered something genuinely useful.
|
|
282
|
+
THIS MUST BE YOUR LAST TOOL CALL FOR THIS TURN. Do not call any other tool after SendMessage.
|
|
283
|
+
11. YOUR TURN ENDS HERE. The SendMessage in step 10 must be the last tool call of your turn.
|
|
284
|
+
The lead's response will arrive as the START of your next turn. When your next turn begins,
|
|
285
|
+
read the lead's message and act accordingly:
|
|
286
|
+
- "VERIFICATION PASSED: {planTaskId}" → loop back to step 1
|
|
287
|
+
- "VERIFICATION FAILED: {planTaskId}\n..." → fix the issues described, then re-send IMPLEMENTATION COMPLETE (steps 9-10, which again ends your turn)
|
|
288
|
+
- "MAX_ITERATIONS_REACHED: {planTaskId}" → loop back to step 1
|
|
289
|
+
- "TASK_ALREADY_CLAIMED: {planTaskId}" → loop back to step 1
|
|
290
|
+
|
|
291
|
+
IMPLEMENTATION RULES:
|
|
292
|
+
- Implement with REAL code only. No stubs, placeholders, TODOs, or "coming soon" comments.
|
|
293
|
+
- If the task says testsRequired: true, write tests.
|
|
294
|
+
- Follow existing codebase patterns and conventions (see Codebase Context above if provided).
|
|
295
|
+
- If the task has "Implementation Hints", follow them as your implementation guide — they describe HOW to implement, not just WHAT.
|
|
296
|
+
- If the task has "References", read those files/lines BEFORE coding to understand the patterns you should follow.
|
|
297
|
+
- If the task has "MUST NOT DO" constraints, treat them as hard constraints — violating them will fail verification.
|
|
298
|
+
- Track which files you modify (every Write/Edit/creation operation).
|
|
299
|
+
- Never claim more than one task at a time.
|
|
300
|
+
- After sending IMPLEMENTATION COMPLETE, your turn MUST end — do not call any other tool. The lead's verification response arrives as your next turn.
|
|
301
|
+
|
|
302
|
+
TURN PROTOCOL (strict termination rules):
|
|
303
|
+
Every turn MUST end with exactly one of these SendMessage calls — no tool calls after it:
|
|
304
|
+
1. "IMPLEMENTATION COMPLETE: {planTaskId}" (step 10 — awaiting verification)
|
|
305
|
+
2. "CLARIFICATION NEEDED: {planTaskId}" (asking lead to relay a question)
|
|
306
|
+
3. "NO_MORE_TASKS: {builderName}" (no claimable tasks — going idle)
|
|
307
|
+
4. "TASK_CLAIMED: {planTaskId}" is the ONE exception — continue to step 8 on the same turn.
|
|
308
|
+
|
|
309
|
+
Strict pre-completion sequence:
|
|
310
|
+
1. TaskUpdate(taskId, metadata: { fpStatus: "AWAITING_VERIFICATION" }) ← second-to-last tool call
|
|
311
|
+
2. SendMessage("IMPLEMENTATION COMPLETE: ...") ← LAST tool call
|
|
312
|
+
|
|
313
|
+
Forbidden actions after sending IMPLEMENTATION COMPLETE:
|
|
314
|
+
- Do NOT call TaskList(), TaskGet(), or TaskUpdate()
|
|
315
|
+
- Do NOT call Read, Write, Edit, Bash, Glob, or Grep
|
|
316
|
+
- Do NOT send another SendMessage
|
|
317
|
+
Your turn must end immediately after the SendMessage for IMPLEMENTATION COMPLETE.
|
|
318
|
+
|
|
319
|
+
NUDGE RECOVERY (automatic re-injection):
|
|
320
|
+
If you receive a message about stalling or idle detection:
|
|
321
|
+
1. Do NOT panic or start over from scratch.
|
|
322
|
+
2. Do NOT call TaskList or attempt to claim a new task.
|
|
323
|
+
3. Call TaskGet on the task ID mentioned in the message to refresh your context.
|
|
324
|
+
4. If you already sent IMPLEMENTATION COMPLETE and are waiting for verification,
|
|
325
|
+
re-send IMPLEMENTATION COMPLETE to team-lead. Do NOT restart implementation
|
|
326
|
+
or claim a different task.
|
|
327
|
+
5. Otherwise, continue implementing from where you left off.
|
|
328
|
+
|
|
329
|
+
CLARIFICATION PROTOCOL (iteration 1 only, once per task):
|
|
330
|
+
If you encounter a genuine ambiguity, send to "team-lead":
|
|
331
|
+
"CLARIFICATION NEEDED: {planTaskId}
|
|
332
|
+
QUESTION: {question}
|
|
333
|
+
OPTIONS:
|
|
334
|
+
- {label} | {description}
|
|
335
|
+
..."
|
|
336
|
+
YOUR TURN ENDS HERE after sending CLARIFICATION NEEDED. Do not call any other tool.
|
|
337
|
+
The CLARIFICATION ANSWER will arrive as the start of your next turn. Then continue implementation.
|
|
338
|
+
|
|
339
|
+
PEER COMMUNICATION (parallel mode only — skip if peerBuilderNames is "none"):
|
|
340
|
+
Budget: 2 peer messages max per task. Do NOT exceed this.
|
|
341
|
+
When to notify peers (SendMessage to a specific peer builder by name):
|
|
342
|
+
- You created a new shared utility/interface that peers working on related files could reuse
|
|
343
|
+
- You discovered a pattern or constraint that affects a peer's likely work area
|
|
344
|
+
- You detected a conflict (e.g. both modifying the same file, incompatible interface changes)
|
|
345
|
+
- You moved/renamed a file that a peer's task references
|
|
346
|
+
|
|
347
|
+
Format — send to the specific peer(s) affected:
|
|
348
|
+
"PEER_NOTIFICATION
|
|
349
|
+
TYPE: INTERFACE_CREATED | PATTERN_FOUND | CONFLICT_WARNING | FILE_MOVED
|
|
350
|
+
DETAILS:
|
|
351
|
+
{1-3 lines: what you did, where, and what the peer should do about it}"
|
|
352
|
+
|
|
353
|
+
When NOT to send:
|
|
354
|
+
- Your discovery only matters for future tasks (use NOTEPAD_ENTRY instead)
|
|
355
|
+
- You're unsure if a peer is affected (don't guess — skip it)
|
|
356
|
+
- You've already used your 2-message budget for this task
|
|
357
|
+
- Only one builder is active (peerBuilderNames is "none")
|
|
358
|
+
|
|
359
|
+
Processing incoming peer messages:
|
|
360
|
+
- Read and integrate the information if relevant to your current task
|
|
361
|
+
- Do NOT reply — peer messages are one-way notifications
|
|
362
|
+
- Do NOT pause your work to wait for peer messages
|
|
363
|
+
- If a peer warns about a conflict, adapt your approach accordingly
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Verifier Subagent Spec
|
|
367
|
+
|
|
368
|
+
The verifier is spawned by the lead via Task tool after each builder iteration. It gets fresh context every time.
|
|
369
|
+
|
|
370
|
+
Spawn: `Task({ subagent_type: "general-purpose", prompt: <below> })`
|
|
371
|
+
|
|
372
|
+
Prompt template (lead injects values):
|
|
373
|
+
```
|
|
374
|
+
You are a verification agent for a fractal-planner implementation task.
|
|
375
|
+
|
|
376
|
+
Task ID: {id}
|
|
377
|
+
Description: {description}
|
|
378
|
+
|
|
379
|
+
Acceptance Criteria:
|
|
380
|
+
{numbered criteria from plan}
|
|
381
|
+
|
|
382
|
+
MUST NOT DO constraints:
|
|
383
|
+
{guardrails from task — always includes baseline + task-specific}
|
|
384
|
+
|
|
385
|
+
Files Allowed: {filesToModify list from task metadata}
|
|
386
|
+
|
|
387
|
+
Files Modified by Builder:
|
|
388
|
+
{FILES_MODIFIED list from builder's message}
|
|
389
|
+
|
|
390
|
+
Test Commands: {commands from task metadata or 'none'}
|
|
391
|
+
Tests Required: {yes/no}
|
|
392
|
+
|
|
393
|
+
{codebaseContext}
|
|
394
|
+
|
|
395
|
+
Instructions:
|
|
396
|
+
1. Read each modified file listed above.
|
|
397
|
+
2. For each acceptance criterion, verify it is met by the code.
|
|
398
|
+
3. Check MUST NOT DO constraints:
|
|
399
|
+
a. File boundary: Compare FILES_MODIFIED against Files Allowed. Any file modified that is NOT in the allowed list is a MUST NOT DO violation (exception: new test files co-located with allowed files are permitted).
|
|
400
|
+
b. New dependencies: Check if package.json was modified. If so, diff it to see if new dependencies were added — this is a violation.
|
|
401
|
+
c. Task-specific guardrails: Verify each additional MUST NOT DO constraint is respected.
|
|
402
|
+
If ANY MUST NOT DO violation is found, the overall result is FAIL regardless of criteria results.
|
|
403
|
+
4. If tests are required, run the test commands via Bash.
|
|
404
|
+
5. Run: bun run typecheck (if tsconfig.json exists in the project root).
|
|
405
|
+
6. Write your evidence to: {evidencePath}
|
|
406
|
+
|
|
407
|
+
Evidence file format:
|
|
408
|
+
```markdown
|
|
409
|
+
# Verification Evidence: Task {id}
|
|
410
|
+
|
|
411
|
+
Result: PASS | FAIL
|
|
412
|
+
Timestamp: {ISO timestamp}
|
|
413
|
+
Task: {description}
|
|
414
|
+
|
|
415
|
+
## Criteria Results
|
|
416
|
+
| # | Criterion | Result | Evidence |
|
|
417
|
+
|---|-----------|--------|---------|
|
|
418
|
+
| 1 | {text} | PASS/FAIL | {code snippet or "met" or specific failure} |
|
|
419
|
+
|
|
420
|
+
## MUST NOT DO Check
|
|
421
|
+
| Constraint | Result | Evidence |
|
|
422
|
+
|-----------|--------|---------|
|
|
423
|
+
| File boundary ({allowed files}) | PASS/FAIL | {list of violating files, or "all files within scope"} |
|
|
424
|
+
| No new dependencies | PASS/FAIL | {added deps, or "package.json unchanged"} |
|
|
425
|
+
| {task-specific guardrail} | PASS/FAIL | {evidence} |
|
|
426
|
+
|
|
427
|
+
## Test Output
|
|
428
|
+
\`\`\`
|
|
429
|
+
{test output or "Tests not required"}
|
|
430
|
+
\`\`\`
|
|
431
|
+
|
|
432
|
+
## Typecheck Output
|
|
433
|
+
\`\`\`
|
|
434
|
+
{typecheck output or "No tsconfig.json found"}
|
|
435
|
+
\`\`\`
|
|
436
|
+
|
|
437
|
+
## Files Reviewed
|
|
438
|
+
- {absolute file path}
|
|
439
|
+
|
|
440
|
+
## Summary
|
|
441
|
+
{1-2 sentence summary}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
7. After writing the evidence file, report your findings in this EXACT format:
|
|
445
|
+
|
|
446
|
+
If ALL criteria pass AND all MUST NOT DO checks pass AND tests/typecheck pass:
|
|
447
|
+
VERIFICATION PASSED
|
|
448
|
+
All {N} criteria met.
|
|
449
|
+
MUST NOT DO: All constraints respected.
|
|
450
|
+
[1-2 sentence summary of what was verified]
|
|
451
|
+
|
|
452
|
+
If ANY check fails:
|
|
453
|
+
VERIFICATION FAILED
|
|
454
|
+
Failed:
|
|
455
|
+
- Criterion {N}: {text} — {specific failure reason and fix instruction}
|
|
456
|
+
MUST NOT DO Violations:
|
|
457
|
+
- {constraint}: {specific violation and fix instruction}
|
|
458
|
+
Passed:
|
|
459
|
+
- Criterion {N}: {text}
|
|
460
|
+
Tests: {PASS/FAIL with relevant output}
|
|
461
|
+
Typecheck: {PASS/FAIL with errors if any}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
## Step 5: Execute Tasks (Builder Self-Claiming Loop)
|
|
466
|
+
|
|
467
|
+
**ROUTING RULES — read before executing the loop:**
|
|
468
|
+
- `tracker` receives ONLY: `TASK_STARTED`, `TASK_COMPLETED`, `TASK_FAILED`, `TASK_SKIPPED`, `ROLLUP_PARENTS`
|
|
469
|
+
- `builder-N` receives ONLY: `VERIFICATION PASSED`, `VERIFICATION FAILED`, `MAX_ITERATIONS_REACHED`, `CLARIFICATION ANSWER`, `TASKS_AVAILABLE`, `TASK_ALREADY_CLAIMED`
|
|
470
|
+
- Lead NEVER sends task descriptions/criteria/hints to builders (builders self-read via TaskGet)
|
|
471
|
+
- NEVER send task descriptions, acceptance criteria, code context, or implementation hints to the tracker
|
|
472
|
+
|
|
473
|
+
### 5.0: Initialize Lead State
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
builderTaskMap = {} // builderName → planTaskId (current assignment)
|
|
477
|
+
iterationMap = {} // planTaskId → current iteration number
|
|
478
|
+
iterationMaxMap = {} // planTaskId → effective max iterations (computed per-task)
|
|
479
|
+
clarificationsUsed = {} // planTaskId → boolean
|
|
480
|
+
activeBuilders = set() // builders that haven't sent NO_MORE_TASKS
|
|
481
|
+
idleBuilders = set() // builders that have sent NO_MORE_TASKS
|
|
482
|
+
commitQueue = [] // ordered list of {planTaskId, filesModified, builderName, iteration, nativeId}
|
|
483
|
+
completedTasks, failedTasks, skippedTasks (from Step 4.1 or empty for fresh runs)
|
|
484
|
+
|
|
485
|
+
If isResume == true:
|
|
486
|
+
iterationMap = executionState.iterationMap || {}
|
|
487
|
+
clarificationsUsed = executionState.clarificationsUsed || {}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Read `notepad.md` from the plan directory. Set `notepadContents` to the file contents, or empty string if not found.
|
|
491
|
+
|
|
492
|
+
### 5.1: Spawn Persistent Builders
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
N = maxParallelTasks (from config, default 1)
|
|
496
|
+
For i in 1..N:
|
|
497
|
+
Spawn teammate "builder-{i}" on team fp-impl-{planId}
|
|
498
|
+
- Use builder spec from Step 4.4
|
|
499
|
+
- Inject: builderName="builder-{i}", codebaseContext, planId
|
|
500
|
+
- Inject: peerBuilderNames = comma-separated list of all OTHER builder names (e.g. "builder-2, builder-3" for builder-1)
|
|
501
|
+
- If N == 1: peerBuilderNames = "none"
|
|
502
|
+
Add "builder-{i}" to activeBuilders
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Builders start their self-claiming loops immediately — no initial message from lead needed.
|
|
506
|
+
|
|
507
|
+
### 5.2: Lead Message-Handling Loop
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
While activeBuilders is not empty OR commitQueue is not empty:
|
|
511
|
+
Wait for next message from any teammate.
|
|
512
|
+
Route by message prefix:
|
|
513
|
+
"TASK_CLAIMED:" → Step 5.3
|
|
514
|
+
"IMPLEMENTATION COMPLETE:" → Step 5.4
|
|
515
|
+
"CLARIFICATION NEEDED:" → Step 5.5
|
|
516
|
+
"NO_MORE_TASKS:" → Step 5.6
|
|
517
|
+
|
|
518
|
+
After handling each message, process commitQueue if non-empty (Step 5.7).
|
|
519
|
+
|
|
520
|
+
If activeBuilders is empty AND commitQueue is empty:
|
|
521
|
+
Proceed to Step 6.
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### 5.3: Handle TASK_CLAIMED
|
|
525
|
+
|
|
526
|
+
1. Parse `planTaskId`, `builderName`, `nativeId` from the message.
|
|
527
|
+
2. **Check for duplicate claim**: if another builder already has this `planTaskId` in `builderTaskMap` values, send `TASK_ALREADY_CLAIMED: {planTaskId}` back to the sender. Return.
|
|
528
|
+
3. Record: `builderTaskMap[builderName] = planTaskId`
|
|
529
|
+
4. Set `iterationMap[planTaskId] = 1` if not already set. Persist: read `execution-state.json`, set `iterationMap[planTaskId]` to the current value, write back.
|
|
530
|
+
5. **Compute adaptive max iterations**: If `iterationScaling.enabled` (from config):
|
|
531
|
+
- Look up the task's complexity from the parsed task tree. Prefer `complexityDimensions.risk` if available, otherwise use `estimatedComplexity`.
|
|
532
|
+
- `effectiveMax = max(iterationScaling.base, ceil(taskComplexity * iterationScaling.factor))`
|
|
533
|
+
- `effectiveMax = min(effectiveMax, maxIterations)` — cap at the global maximum
|
|
534
|
+
- Store in `iterationMaxMap[planTaskId] = effectiveMax`
|
|
535
|
+
|
|
536
|
+
If `iterationScaling.enabled` is false: `iterationMaxMap[planTaskId] = maxIterations`
|
|
537
|
+
6. Send `TASK_STARTED: {planTaskId}` to tracker. Wait for `ACK_STARTED: {planTaskId}`.
|
|
538
|
+
|
|
539
|
+
### 5.4: Handle IMPLEMENTATION COMPLETE
|
|
540
|
+
|
|
541
|
+
1. Parse `planTaskId`, FILES_MODIFIED, optional NOTEPAD_ENTRY from the message.
|
|
542
|
+
2. If NOTEPAD_ENTRY present: append entries to `notepad.md` with `[Task {planTaskId}]` prefix, update `notepadContents`.
|
|
543
|
+
3. Identify sender `builderName`.
|
|
544
|
+
4. Look up `nativeId = taskMap[planTaskId]`.
|
|
545
|
+
5. Spawn verifier subagent (same spec as Step 4.4 verifier — inject task criteria, FILES_MODIFIED, codebaseContext, test commands, `evidencePath: .fractal-planner/plans/{planId}/evidence/task-{planTaskId}-verification.md`). Wait for result.
|
|
546
|
+
6. Read evidence file (primary) or parse agent message (fallback).
|
|
547
|
+
|
|
548
|
+
**If VERIFICATION PASSED:**
|
|
549
|
+
- Add to `commitQueue`: `{ planTaskId, filesModified, builderName, iteration: iterationMap[planTaskId], nativeId }`
|
|
550
|
+
- Send `VERIFICATION PASSED: {planTaskId}` to the builder (builder loops to claim next task)
|
|
551
|
+
|
|
552
|
+
**If VERIFICATION FAILED:**
|
|
553
|
+
- If `iterationMap[planTaskId] >= iterationMaxMap[planTaskId]`: go to Step 5.8.
|
|
554
|
+
- Else: increment `iterationMap[planTaskId]`. Persist: read `execution-state.json`, update `iterationMap[planTaskId]`, write back. Reset fpStatus: `TaskUpdate(nativeId, metadata: { fpStatus: "IMPLEMENTING" })`. Send to builder:
|
|
555
|
+
```
|
|
556
|
+
VERIFICATION FAILED: {planTaskId}
|
|
557
|
+
Iteration: {n}/{iterationMaxMap[planTaskId]}
|
|
558
|
+
|
|
559
|
+
{full VERIFICATION FAILED output from verifier}
|
|
560
|
+
|
|
561
|
+
Fix all issues listed above, then send IMPLEMENTATION COMPLETE again.
|
|
562
|
+
Remember: IMPLEMENTATION COMPLETE must be your last tool call — your turn ends after sending it.
|
|
563
|
+
```
|
|
564
|
+
Builder retries in-place (no re-spawn).
|
|
565
|
+
|
|
566
|
+
### 5.5: Handle CLARIFICATION NEEDED
|
|
567
|
+
|
|
568
|
+
1. Parse `planTaskId` and the question from the message.
|
|
569
|
+
2. Identify sender `builderName`.
|
|
570
|
+
3. Verify this is iteration 1 for this task (`iterationMap[planTaskId] == 1`) AND `clarificationsUsed[planTaskId]` is not true.
|
|
571
|
+
- If invalid: send `"Clarification only allowed on iteration 1. Proceed with the task spec and the failure report."` to the builder.
|
|
572
|
+
4. If valid: parse the CLARIFICATION NEEDED message (see reference.md for format), call `AskUserQuestion`, then forward the answer:
|
|
573
|
+
```
|
|
574
|
+
CLARIFICATION ANSWER: {planTaskId}
|
|
575
|
+
User selected: "{option}"
|
|
576
|
+
Context: {additional text if any}
|
|
577
|
+
```
|
|
578
|
+
5. Mark `clarificationsUsed[planTaskId] = true`. Persist: read `execution-state.json`, set `clarificationsUsed[planTaskId] = true`, write back.
|
|
579
|
+
|
|
580
|
+
### 5.6: Handle NO_MORE_TASKS
|
|
581
|
+
|
|
582
|
+
1. Parse `builderName` from the message.
|
|
583
|
+
2. Move `builderName` from `activeBuilders` to `idleBuilders`.
|
|
584
|
+
3. Clear `builderTaskMap[builderName]`.
|
|
585
|
+
4. If `activeBuilders` is empty AND `commitQueue` is empty: proceed to Step 6.
|
|
586
|
+
|
|
587
|
+
### 5.7: Serialize Commits (process commitQueue)
|
|
588
|
+
|
|
589
|
+
Process `commitQueue` one entry at a time, sorted by dependency order (dependencies first):
|
|
590
|
+
|
|
591
|
+
For each entry in `commitQueue`:
|
|
592
|
+
1. **Create git commit** — skip if `--no-commit` flag was set:
|
|
593
|
+
- Parse FILES_MODIFIED from the entry. If missing, log warning, set commit hash to `none`.
|
|
594
|
+
- Spawn committer teammate:
|
|
595
|
+
- Name: **committer-{planTaskId}**
|
|
596
|
+
- Tools: `Bash`, `Read`, `Grep`
|
|
597
|
+
- Instructions (inject `commitStyle` and `commitLang` from Step 4.2.5):
|
|
598
|
+
```
|
|
599
|
+
You are a git commit specialist for the fp-impl-{planId} team.
|
|
600
|
+
|
|
601
|
+
Follow the fp:commit skill instructions to create ONE git commit for this task.
|
|
602
|
+
|
|
603
|
+
TASK CONTEXT:
|
|
604
|
+
- Task ID: {planTaskId}
|
|
605
|
+
- Description: {description}
|
|
606
|
+
|
|
607
|
+
FILES_MODIFIED:
|
|
608
|
+
{paste the file list}
|
|
609
|
+
|
|
610
|
+
INSTRUCTIONS:
|
|
611
|
+
1. Use pre-detected commit style: {commitStyle} (detected from git history at session start)
|
|
612
|
+
2. Use pre-detected language: {commitLang}
|
|
613
|
+
3. Stage only these specific files
|
|
614
|
+
4. Create ONE commit with message based on task description, following the detected style
|
|
615
|
+
5. Report commit hash when done
|
|
616
|
+
|
|
617
|
+
Message me with "COMMIT COMPLETED" or "COMMIT FAILED" when finished.
|
|
618
|
+
```
|
|
619
|
+
- Handle committer response:
|
|
620
|
+
- `COMMIT COMPLETED` → extract hash, log `✓ Task {planTaskId} committed as {hash}`
|
|
621
|
+
- `COMMIT FAILED` → use `AskUserQuestion` ("Git commit failed for task {planTaskId}: {error}. How should we proceed?" with options "Continue without committing" / "Stop execution"). If continuing, set hash to `none`.
|
|
622
|
+
- `COMMIT SKIPPED` → log skip reason, set hash to `none`
|
|
623
|
+
- Shut down committer.
|
|
624
|
+
|
|
625
|
+
2. **Update native task status**:
|
|
626
|
+
```
|
|
627
|
+
TaskUpdate(nativeId, status: "completed", metadata: {
|
|
628
|
+
fpStatus: "COMPLETED",
|
|
629
|
+
iterations: "{iteration}/{maxIterations}",
|
|
630
|
+
commit: "{hash or none}",
|
|
631
|
+
summary: "{brief description}"
|
|
632
|
+
})
|
|
633
|
+
```
|
|
634
|
+
3. **Notify tracker**:
|
|
635
|
+
```
|
|
636
|
+
TASK_COMPLETED: {planTaskId}
|
|
637
|
+
Iterations: {iteration}/{maxIterations}
|
|
638
|
+
Commit: {hash or "none"}
|
|
639
|
+
Summary: {brief description of what was implemented}
|
|
640
|
+
```
|
|
641
|
+
4. Wait for `ACK_COMPLETED: {planTaskId}`.
|
|
642
|
+
5. Add `planTaskId` to `completedTasks`.
|
|
643
|
+
6. Clear `builderTaskMap[builderName]` for this entry.
|
|
644
|
+
7. Remove entry from `commitQueue`.
|
|
645
|
+
|
|
646
|
+
8. **Check for idle builder wake-up**: After each commit, call `TaskList()` to check for newly unblocked tasks (status: "pending", blockedBy: []). If found AND `idleBuilders` is non-empty:
|
|
647
|
+
- Pick an idle builder, move from `idleBuilders` to `activeBuilders`.
|
|
648
|
+
- Send: `TASKS_AVAILABLE\nNew tasks have been unblocked. Resume your self-claiming loop.`
|
|
649
|
+
|
|
650
|
+
### 5.8: On Max Iterations Reached
|
|
651
|
+
|
|
652
|
+
Triggered from Step 5.4 when `iterationMap[planTaskId] >= iterationMaxMap[planTaskId]` and verification fails.
|
|
653
|
+
|
|
654
|
+
1. Log: "Task {planTaskId} FAILED after {iterationMaxMap[planTaskId]} iterations"
|
|
655
|
+
2. Use `AskUserQuestion`:
|
|
656
|
+
- Question: "Task {planTaskId} failed verification after {iterationMaxMap[planTaskId]} iterations. What would you like to do?"
|
|
657
|
+
- Options:
|
|
658
|
+
- **Continue**: Skip this task and proceed
|
|
659
|
+
- **Stop**: End the implementation run and report current progress
|
|
660
|
+
3. **If Continue**:
|
|
661
|
+
a. `TaskUpdate(nativeId, status: "completed", metadata: { fpStatus: "FAILED", iterations: "{maxIterations}/{maxIterations}", reason: "{last VERIFICATION FAILED summary}" })`
|
|
662
|
+
b. Read `execution-state.json`, set `failureReasons[planTaskId] = "{reason}"`, write back.
|
|
663
|
+
c. `failedTasks.add(planTaskId)`
|
|
664
|
+
d. Notify tracker:
|
|
665
|
+
```
|
|
666
|
+
TASK_FAILED: {planTaskId}
|
|
667
|
+
Iterations: {maxIterations}/{maxIterations}
|
|
668
|
+
Reason: {last VERIFICATION FAILED report from the verifier subagent}
|
|
669
|
+
```
|
|
670
|
+
Wait for `ACK_FAILED: {planTaskId}`.
|
|
671
|
+
e. Compute transitive blocked dependents: find all tasks whose dependencies include this failed task ID (directly or transitively through other tasks already in `failedTasks`/`skippedTasks`).
|
|
672
|
+
f. For each blocked dependent (process in dependency order):
|
|
673
|
+
- `TaskUpdate(depNativeId, status: "deleted", metadata: { fpStatus: "SKIPPED", reason: "Blocked by {planTaskId}" })`
|
|
674
|
+
- Read `execution-state.json`, set `skippedTasks[depPlanId] = "Blocked by {planTaskId}"`, write back.
|
|
675
|
+
- Send `TASK_SKIPPED: {depPlanId}\nReason: Blocked by {planTaskId}` to tracker, wait `ACK_SKIPPED: {depPlanId}`.
|
|
676
|
+
- `skippedTasks.add(depPlanId)`
|
|
677
|
+
g. Send `MAX_ITERATIONS_REACHED: {planTaskId}` to the builder (builder loops to next task).
|
|
678
|
+
h. Clear `builderTaskMap[builderName]`.
|
|
679
|
+
i. Return to Step 5.2 message-handling loop.
|
|
680
|
+
4. **If Stop**:
|
|
681
|
+
a. Perform steps 3a through 3f (mark failed + blocked dependents).
|
|
682
|
+
b. Jump to Step 6.
|
|
683
|
+
|
|
684
|
+
## Step 6: Cleanup & Report
|
|
685
|
+
|
|
686
|
+
After all builders have finished (activeBuilders is empty, commitQueue is empty) or the user chose to stop:
|
|
687
|
+
|
|
688
|
+
1. **Roll up parent statuses**: Send `ROLLUP_PARENTS` to the tracker. Wait for `ROLLUP_COMPLETE`. The tracker handles all Linear parent status updates.
|
|
689
|
+
|
|
690
|
+
2. **Shut down all builders**: Send `shutdown_request` to each builder (both active and idle) via SendMessage. Wait for confirmations.
|
|
691
|
+
|
|
692
|
+
3. **Shut down tracker**: Send shutdown request to the tracker. Wait for confirmation.
|
|
693
|
+
|
|
694
|
+
4. **Clean up the team**: Delete team `fp-impl-{planId}`.
|
|
695
|
+
|
|
696
|
+
5. **Build summary from native task system**: Call `TaskList()` to get final task states. For each `(planTaskId, nativeId)` in `taskMap`:
|
|
697
|
+
- `status == "completed"` + `fpStatus == "COMPLETED"` (or absent) → COMPLETED (get iterations, commit from metadata)
|
|
698
|
+
- `status == "completed"` + `fpStatus == "FAILED"` → FAILED (get reason from metadata or `executionState.failureReasons`)
|
|
699
|
+
- `status == "deleted"` → SKIPPED (get reason from `executionState.skippedTasks[planTaskId]`)
|
|
700
|
+
- `status == "in_progress"` or `status == "pending"` → INCOMPLETE (should not occur at end — warn)
|
|
701
|
+
|
|
702
|
+
6. **Write final `progress.md` snapshot** (human-readable audit artifact — not a runtime source of truth):
|
|
703
|
+
```markdown
|
|
704
|
+
# Implementation Progress (Final Snapshot)
|
|
705
|
+
|
|
706
|
+
Plan: {planId}
|
|
707
|
+
Completed: {ISO timestamp}
|
|
708
|
+
Max Iterations: {maxIterations}
|
|
709
|
+
|
|
710
|
+
## Summary
|
|
711
|
+
- Total: {N} leaf tasks
|
|
712
|
+
- Completed: {count}
|
|
713
|
+
- Failed: {count}
|
|
714
|
+
- Skipped: {count}
|
|
715
|
+
|
|
716
|
+
## Tasks
|
|
717
|
+
|
|
718
|
+
| # | ID | Description | Status | Iterations | Commit | Notes |
|
|
719
|
+
|---|-----|-------------|--------|------------|--------|-------|
|
|
720
|
+
| 1 | {id} | {description} | COMPLETED | {n}/{max} | {hash} | |
|
|
721
|
+
| 2 | {id} | {description} | FAILED | {max}/{max} | - | {reason} |
|
|
722
|
+
| 3 | {id} | {description} | SKIPPED | - | - | Blocked by {dep} |
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
7. **Report to user**:
|
|
726
|
+
```
|
|
727
|
+
## Implementation Summary ({planId})
|
|
728
|
+
|
|
729
|
+
### Results
|
|
730
|
+
- Completed: {N}/{total} tasks
|
|
731
|
+
- Failed: {N}/{total} tasks
|
|
732
|
+
- Skipped: {N}/{total} tasks (blocked by failed dependencies)
|
|
733
|
+
|
|
734
|
+
### Task Details
|
|
735
|
+
| Task | Status | Iterations | Commit | Notes |
|
|
736
|
+
|------|--------|------------|--------|-------|
|
|
737
|
+
| {id} | PASSED | {n}/{max} | abc1234 | {brief note} |
|
|
738
|
+
| {id} | FAILED | {n}/{max} | - | {failure reason} |
|
|
739
|
+
| {id} | SKIPPED | - | - | Blocked by {dep} |
|
|
740
|
+
|
|
741
|
+
### Verification Reports
|
|
742
|
+
{For each completed task, include the verifier's VERIFICATION PASSED report}
|
|
743
|
+
|
|
744
|
+
### Failed Tasks
|
|
745
|
+
{For each failed task, include the last VERIFICATION FAILED report from the verifier subagent}
|
|
746
|
+
|
|
747
|
+
Progress snapshot: .fractal-planner/plans/{planId}/progress.md
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
## Reference
|
|
751
|
+
|
|
752
|
+
See [reference.md](./reference.md) for:
|
|
753
|
+
- Task interface definition
|
|
754
|
+
- VerificationReport format
|
|
755
|
+
- Plan file format examples
|
|
756
|
+
- Valid/invalid input examples
|
|
757
|
+
- Native Task Format
|
|
758
|
+
- Execution State File format
|
|
759
|
+
- Tracker communication protocol
|
|
760
|
+
- Nudge Mechanism (P3)
|
|
761
|
+
|
|
762
|
+
## Important Notes
|
|
763
|
+
|
|
764
|
+
- **Builders are persistent**: Each builder-N teammate lives for the entire session, running a self-claiming work loop across multiple tasks. Verifiers are still spawned fresh per iteration.
|
|
765
|
+
- **Builders drive scheduling**: Builders find and claim tasks via TaskList/TaskUpdate. The lead handles verification, commits, failures, and tracker communication.
|
|
766
|
+
- **Builder tools**: Builders have TaskList, TaskUpdate, TaskGet, and SendMessage in addition to code tools. They use these for self-claiming, not for modifying other tasks' state.
|
|
767
|
+
- **Self-claiming execution**: Builders self-organize via the native task list. `maxParallelTasks` determines the number of concurrent builders (default: 1 = sequential).
|
|
768
|
+
- **Builders accumulate context**: Persistent builders retain knowledge across tasks within a session. Verifiers are still spawned fresh per iteration.
|
|
769
|
+
- **Tracker is persistent**: The tracker teammate is spawned once and lives for the entire session.
|
|
770
|
+
- **Tracker owns Linear**: The lead never calls `mcp__linear-server__update_issue` directly. All Linear updates go through the tracker.
|
|
771
|
+
- **Tracker is stateless**: The tracker no longer reads or writes files. All state lives in its memory for the current session.
|
|
772
|
+
- **Native task system owns execution state**: The lead uses TaskCreate/TaskList/TaskUpdate to manage task lifecycle. `execution-state.json` is the resume artifact.
|
|
773
|
+
- **Resume is native-task-driven**: On resume, the lead reads `execution-state.json` for the `taskMap`, then calls `TaskList()` to get current statuses. No `progress.md` parsing.
|
|
774
|
+
- **No native "failed" status**: Failed tasks get `status: "completed"` + `metadata.fpStatus: "FAILED"`. This auto-unblocks dependents in the native system, which the lead then immediately marks `deleted` (skipped).
|
|
775
|
+
- **Skipped tasks use `deleted` status**: `deleted` tasks are invisible to `TaskList`, preventing builders from claiming them.
|
|
776
|
+
- **Step 4.0 requires team context**: `TaskCreate` calls in Step 4.0 must happen after `TeamCreate` in Step 4, since they register tasks in the current team's task list.
|
|
777
|
+
- **Codebase context injection**: The codebase context from Step 2 is injected into builder spawn instructions so builders don't waste turns re-exploring the project.
|
|
778
|
+
- **Commits are always serialized**: Even with parallel builders, commits happen one at a time in dependency order via the commitQueue.
|
|
779
|
+
- **Real code only**: The builder must never produce stubs or placeholder implementations.
|
|
780
|
+
- **Verification is lead-driven**: The lead spawns a fresh verification subagent (via Task tool) after each builder iteration. No hooks or persistent verifier teammate.
|
|
781
|
+
- **User decides on failure**: When max iterations are reached, always ask the user.
|
|
782
|
+
- **progress.md is generated once at Step 6**: Not a runtime artifact. Generated as a human-readable snapshot at the end.
|
|
783
|
+
- **Nudge mechanism**: A TeammateIdle hook (`hooks/nudge-teammate.sh`) detects when builders stall (go idle while owning an in_progress task) and re-injects a continuation prompt. The hook respects `fpStatus: "AWAITING_VERIFICATION"` in task metadata — builders waiting for verification are not nudged. After `nudge.maxRetries` (default: 3) re-injections with no progress, the hook stops. Configurable via `.fractal-planner/config.json` `nudge` section.
|