ai-fob 1.7.2 → 1.9.0

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 (34) hide show
  1. package/assets/pi/extensions/task-list/README.md +30 -0
  2. package/assets/pi/extensions/task-list/index.ts +345 -0
  3. package/assets/pi/prompts/build-phase.md +157 -0
  4. package/assets/pi/skills/FOB-state-context/SKILL.md +123 -0
  5. package/assets/pi/skills/FOB-state-context/references/project-context-detection.md +82 -0
  6. package/assets/pi/skills/FOB-state-context/references/state-format.md +127 -0
  7. package/assets/skills/pi-primitives/SKILL.md +1 -3
  8. package/manifest.json +7 -16
  9. package/package.json +1 -1
  10. package/assets/commands/modify-pi-assets.md +0 -1273
  11. package/assets/pi/agents/phase-architect.md +0 -328
  12. package/assets/pi/agents/phase-build-validator.md +0 -508
  13. package/assets/pi/agents/phase-builder.md +0 -402
  14. package/assets/pi/agents/phase-docs-researcher.md +0 -90
  15. package/assets/pi/agents/phase-explorer.md +0 -174
  16. package/assets/pi/agents/phase-plan-validator.md +0 -227
  17. package/assets/pi/extensions/task-state/checks.ts +0 -177
  18. package/assets/pi/extensions/task-state/index.ts +0 -649
  19. package/assets/pi/extensions/task-state/persistence.ts +0 -43
  20. package/assets/pi/extensions/task-state/reconcile.ts +0 -374
  21. package/assets/pi/extensions/task-state/state-md.ts +0 -575
  22. package/assets/pi/extensions/task-state/widget.ts +0 -80
  23. package/assets/pi/prompts/build-phase-pi.md +0 -936
  24. package/assets/pi/skills/fob-state-context/SKILL.md +0 -206
  25. package/assets/pi/skills/phase-build-workflow/SKILL.md +0 -383
  26. package/assets/pi/skills/phase-build-workflow/references/fix-loops.md +0 -189
  27. package/assets/pi/skills/phase-build-workflow/references/hl-criteria-injection.md +0 -151
  28. package/assets/pi/skills/phase-build-workflow/references/parallel-domains.md +0 -160
  29. package/assets/pi/skills/phase-build-workflow/references/phase-completion-report-template.md +0 -105
  30. package/assets/pi/skills/phase-build-workflow/references/result-vocabulary.md +0 -49
  31. package/assets/pi/skills/phase-build-workflow/references/resume-reconciliation-table.md +0 -153
  32. package/assets/pi/skills/phase-build-workflow/references/state-md-schema.md +0 -198
  33. package/assets/pi/skills/testing-and-validation/SKILL.md +0 -114
  34. package/assets/skills/pi-primitives/reference/pi-asset-modification-guide.md +0 -170
@@ -0,0 +1,30 @@
1
+ # Task List Extension
2
+
3
+ Project-local Pi extension that gives the agent a persistent visual checklist for multi-step work.
4
+
5
+ ## Tools
6
+
7
+ - `task_list_set` — create or replace the task list
8
+ - `task_list_add` — add a task
9
+ - `task_list_update` — update task status/title/note
10
+ - `task_list_complete` — mark a task complete
11
+ - `task_list_show` — show current tasks
12
+ - `task_list_clear` — clear the list
13
+
14
+ ## Commands
15
+
16
+ - `/tasks` — show current task list
17
+ - `/tasks-clear` — clear task list
18
+
19
+ ## Statuses
20
+
21
+ - `pending`
22
+ - `in_progress`
23
+ - `completed`
24
+ - `blocked`
25
+ - `cancelled`
26
+
27
+ ## Notes
28
+
29
+ This extension is local to this repository because it lives under `.pi/extensions/task-list/`.
30
+ Run `/reload` in Pi after changes so Pi discovers/reloads it.
@@ -0,0 +1,345 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { StringEnum } from "@earendil-works/pi-ai";
3
+ import { Type } from "typebox";
4
+
5
+ type TaskStatus = "pending" | "in_progress" | "completed" | "blocked" | "cancelled";
6
+
7
+ type TaskItem = {
8
+ id: string;
9
+ title: string;
10
+ status: TaskStatus;
11
+ note?: string;
12
+ createdAt: number;
13
+ updatedAt: number;
14
+ };
15
+
16
+ type TaskListState = {
17
+ title: string;
18
+ tasks: TaskItem[];
19
+ updatedAt: number;
20
+ };
21
+
22
+ const CUSTOM_TYPE = "task-list-state";
23
+ const WIDGET_KEY = "task-list";
24
+
25
+ const statusSchema = StringEnum(["pending", "in_progress", "completed", "blocked", "cancelled"] as const);
26
+
27
+ export default function taskListExtension(pi: ExtensionAPI) {
28
+ let state: TaskListState = emptyState();
29
+
30
+ function emptyState(): TaskListState {
31
+ return {
32
+ title: "Task List",
33
+ tasks: [],
34
+ updatedAt: Date.now(),
35
+ };
36
+ }
37
+
38
+ function now() {
39
+ return Date.now();
40
+ }
41
+
42
+ function nextId() {
43
+ return `task_${Math.random().toString(36).slice(2, 8)}`;
44
+ }
45
+
46
+ function normalizeTaskTitle(title: string) {
47
+ return title.trim().replace(/\s+/g, " ");
48
+ }
49
+
50
+ function persist() {
51
+ pi.appendEntry(CUSTOM_TYPE, state);
52
+ }
53
+
54
+ function restoreFromSession(ctx: ExtensionContext) {
55
+ const entries = ctx.sessionManager.getBranch();
56
+
57
+ for (let i = entries.length - 1; i >= 0; i--) {
58
+ const entry = entries[i] as any;
59
+ if (entry.type !== "custom" || entry.customType !== CUSTOM_TYPE || !entry.data) continue;
60
+
61
+ state = {
62
+ title: typeof entry.data.title === "string" && entry.data.title.trim() ? entry.data.title : "Task List",
63
+ tasks: Array.isArray(entry.data.tasks) ? entry.data.tasks : [],
64
+ updatedAt: typeof entry.data.updatedAt === "number" ? entry.data.updatedAt : now(),
65
+ };
66
+ return;
67
+ }
68
+
69
+ state = emptyState();
70
+ }
71
+
72
+ function formatTask(task: TaskItem) {
73
+ const icon =
74
+ task.status === "completed"
75
+ ? "[x]"
76
+ : task.status === "in_progress"
77
+ ? "[~]"
78
+ : task.status === "blocked"
79
+ ? "[!]"
80
+ : task.status === "cancelled"
81
+ ? "[-]"
82
+ : "[ ]";
83
+
84
+ const note = task.note ? ` — ${task.note}` : "";
85
+ return `${icon} ${task.id}: ${task.title}${note}`;
86
+ }
87
+
88
+ function renderLines() {
89
+ if (state.tasks.length === 0) {
90
+ return ["Task List", "No active tasks."];
91
+ }
92
+
93
+ const completed = state.tasks.filter((task) => task.status === "completed").length;
94
+ const total = state.tasks.length;
95
+
96
+ return [`${state.title} — ${completed}/${total} complete`, ...state.tasks.map(formatTask)];
97
+ }
98
+
99
+ function updateUi(ctx: ExtensionContext) {
100
+ if (!ctx.hasUI) return;
101
+
102
+ if (state.tasks.length === 0) {
103
+ ctx.ui.setWidget(WIDGET_KEY, undefined);
104
+ } else {
105
+ ctx.ui.setWidget(WIDGET_KEY, renderLines());
106
+ }
107
+
108
+ const active = state.tasks.filter((task) => task.status !== "completed" && task.status !== "cancelled").length;
109
+ ctx.ui.setStatus(WIDGET_KEY, active > 0 ? `tasks: ${active} active` : "tasks: clear");
110
+ }
111
+
112
+ function saveAndRender(ctx: ExtensionContext) {
113
+ state.updatedAt = now();
114
+ persist();
115
+ updateUi(ctx);
116
+ }
117
+
118
+ function findTask(id: string) {
119
+ return state.tasks.find((task) => task.id === id);
120
+ }
121
+
122
+ pi.on("session_start", async (_event, ctx) => {
123
+ restoreFromSession(ctx);
124
+ updateUi(ctx);
125
+ });
126
+
127
+ pi.on("before_agent_start", async (event) => {
128
+ return {
129
+ systemPrompt:
130
+ event.systemPrompt +
131
+ `
132
+
133
+ Task List extension guidance:
134
+ - When the user request involves multiple meaningful tasks, use the task_list tools to create a concise checklist before doing the work.
135
+ - Keep tasks action-oriented and short.
136
+ - Mark one task as in_progress when you begin it.
137
+ - Mark tasks completed as soon as they are actually achieved.
138
+ - Keep the task list current; do not leave stale in_progress tasks.
139
+ - Do not create a task list for trivial one-step requests.`,
140
+ };
141
+ });
142
+
143
+ pi.registerTool({
144
+ name: "task_list_set",
145
+ label: "Set Task List",
146
+ description: "Create or replace the current visual task list.",
147
+ promptSnippet: "Create or replace the visual task checklist for multi-step work.",
148
+ promptGuidelines: [
149
+ "Use task_list_set when a user request has multiple meaningful steps that should be tracked.",
150
+ ],
151
+ parameters: Type.Object({
152
+ title: Type.Optional(Type.String({ description: "Optional task list title." })),
153
+ tasks: Type.Array(Type.String({ description: "Short action-oriented task title." })),
154
+ }),
155
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
156
+ const timestamp = now();
157
+
158
+ state = {
159
+ title: params.title?.trim() || "Task List",
160
+ tasks: params.tasks
161
+ .map(normalizeTaskTitle)
162
+ .filter(Boolean)
163
+ .map((title) => ({
164
+ id: nextId(),
165
+ title,
166
+ status: "pending" as TaskStatus,
167
+ createdAt: timestamp,
168
+ updatedAt: timestamp,
169
+ })),
170
+ updatedAt: timestamp,
171
+ };
172
+
173
+ saveAndRender(ctx);
174
+
175
+ return {
176
+ content: [{ type: "text", text: `Created task list with ${state.tasks.length} task(s).\n\n${renderLines().join("\n")}` }],
177
+ details: state,
178
+ };
179
+ },
180
+ });
181
+
182
+ pi.registerTool({
183
+ name: "task_list_add",
184
+ label: "Add Task",
185
+ description: "Add a task to the current visual task list.",
186
+ promptSnippet: "Add a new item to the visual task checklist.",
187
+ parameters: Type.Object({
188
+ title: Type.String({ description: "Short action-oriented task title." }),
189
+ status: Type.Optional(statusSchema),
190
+ note: Type.Optional(Type.String()),
191
+ }),
192
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
193
+ const timestamp = now();
194
+ const title = normalizeTaskTitle(params.title);
195
+
196
+ if (!title) {
197
+ return {
198
+ isError: true,
199
+ content: [{ type: "text", text: "Task title cannot be empty." }],
200
+ details: state,
201
+ };
202
+ }
203
+
204
+ const task: TaskItem = {
205
+ id: nextId(),
206
+ title,
207
+ status: params.status || "pending",
208
+ note: params.note?.trim() || undefined,
209
+ createdAt: timestamp,
210
+ updatedAt: timestamp,
211
+ };
212
+
213
+ state.tasks.push(task);
214
+ saveAndRender(ctx);
215
+
216
+ return {
217
+ content: [{ type: "text", text: `Added task: ${task.id} — ${task.title}` }],
218
+ details: task,
219
+ };
220
+ },
221
+ });
222
+
223
+ pi.registerTool({
224
+ name: "task_list_update",
225
+ label: "Update Task",
226
+ description: "Update a task status, title, or note in the visual task list.",
227
+ promptSnippet: "Update progress on an existing visual checklist item.",
228
+ promptGuidelines: [
229
+ "Use task_list_update to mark tasks in_progress, completed, blocked, cancelled, or to revise stale task text.",
230
+ ],
231
+ parameters: Type.Object({
232
+ id: Type.String({ description: "Task id to update." }),
233
+ status: Type.Optional(statusSchema),
234
+ title: Type.Optional(Type.String()),
235
+ note: Type.Optional(Type.String()),
236
+ }),
237
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
238
+ const task = findTask(params.id);
239
+
240
+ if (!task) {
241
+ return {
242
+ isError: true,
243
+ content: [{ type: "text", text: `Task not found: ${params.id}` }],
244
+ details: { availableTasks: state.tasks.map(({ id, title, status }) => ({ id, title, status })) },
245
+ };
246
+ }
247
+
248
+ if (params.status) task.status = params.status;
249
+ if (params.title !== undefined) task.title = normalizeTaskTitle(params.title);
250
+ if (params.note !== undefined) task.note = params.note.trim() || undefined;
251
+
252
+ task.updatedAt = now();
253
+ saveAndRender(ctx);
254
+
255
+ return {
256
+ content: [{ type: "text", text: `Updated task: ${task.id} — ${task.status} — ${task.title}` }],
257
+ details: task,
258
+ };
259
+ },
260
+ });
261
+
262
+ pi.registerTool({
263
+ name: "task_list_complete",
264
+ label: "Complete Task",
265
+ description: "Mark a task as completed.",
266
+ promptSnippet: "Check off a completed task in the visual checklist.",
267
+ parameters: Type.Object({
268
+ id: Type.String({ description: "Task id to complete." }),
269
+ note: Type.Optional(Type.String()),
270
+ }),
271
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
272
+ const task = findTask(params.id);
273
+
274
+ if (!task) {
275
+ return {
276
+ isError: true,
277
+ content: [{ type: "text", text: `Task not found: ${params.id}` }],
278
+ details: state,
279
+ };
280
+ }
281
+
282
+ task.status = "completed";
283
+ task.note = params.note?.trim() || task.note;
284
+ task.updatedAt = now();
285
+ saveAndRender(ctx);
286
+
287
+ return {
288
+ content: [{ type: "text", text: `Completed task: ${task.id} — ${task.title}` }],
289
+ details: task,
290
+ };
291
+ },
292
+ });
293
+
294
+ pi.registerTool({
295
+ name: "task_list_show",
296
+ label: "Show Task List",
297
+ description: "Show the current task list.",
298
+ promptSnippet: "Inspect the current visual task checklist.",
299
+ parameters: Type.Object({}),
300
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
301
+ updateUi(ctx);
302
+
303
+ return {
304
+ content: [{ type: "text", text: renderLines().join("\n") }],
305
+ details: state,
306
+ };
307
+ },
308
+ });
309
+
310
+ pi.registerTool({
311
+ name: "task_list_clear",
312
+ label: "Clear Task List",
313
+ description: "Clear the current visual task list.",
314
+ promptSnippet: "Clear the visual task checklist when work is done or no longer relevant.",
315
+ parameters: Type.Object({
316
+ reason: Type.Optional(Type.String()),
317
+ }),
318
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
319
+ state = emptyState();
320
+ saveAndRender(ctx);
321
+
322
+ return {
323
+ content: [{ type: "text", text: params.reason ? `Cleared task list. Reason: ${params.reason}` : "Cleared task list." }],
324
+ details: state,
325
+ };
326
+ },
327
+ });
328
+
329
+ pi.registerCommand("tasks", {
330
+ description: "Show the current task list",
331
+ handler: async (_args, ctx) => {
332
+ updateUi(ctx);
333
+ ctx.ui.notify(renderLines().join("\n"), "info");
334
+ },
335
+ });
336
+
337
+ pi.registerCommand("tasks-clear", {
338
+ description: "Clear the current task list",
339
+ handler: async (_args, ctx) => {
340
+ state = emptyState();
341
+ saveAndRender(ctx);
342
+ ctx.ui.notify("Task list cleared.", "success");
343
+ },
344
+ });
345
+ }
@@ -0,0 +1,157 @@
1
+ ---
2
+ description: Build one phase of a phased high-level plan using the Pi workflow
3
+ argument-hint: "<path to HL plan> <phase number>"
4
+ ---
5
+
6
+ # Build Phase Workflow — Step 0 Only
7
+
8
+ You are running the Pi re-engineered build-phase workflow.
9
+
10
+ Current implementation status: **Step 0: Parse & Prepare only**.
11
+
12
+ Do not proceed to research, planning, validation, build, or final reporting yet. Stop after presenting the Step 0 execution overview.
13
+
14
+ ## Required skill
15
+
16
+ Load and follow the `FOB-state-context` skill before reading or modifying `specs/STATE.md` or detecting project context.
17
+
18
+ ## Arguments
19
+
20
+ Raw arguments: `$ARGUMENTS`
21
+
22
+ Parse into:
23
+
24
+ - `HL_PLAN_PATH`: first argument
25
+ - `PHASE_NUMBER`: second argument
26
+
27
+ If either is missing, stop with:
28
+
29
+ ```txt
30
+ Usage: /build-phase <path to HL plan> <phase number>
31
+ ```
32
+
33
+ ## Step 0: Parse & Prepare
34
+
35
+ Perform these tasks directly as the main/orchestrator agent.
36
+
37
+ ### 0.1 Validate inputs
38
+
39
+ 1. Verify `HL_PLAN_PATH` exists.
40
+ 2. Verify `HL_PLAN_PATH` is a Markdown file ending in `.md`.
41
+ 3. Read the YAML frontmatter.
42
+ 4. Verify `PHASE_NUMBER` is an integer.
43
+ 5. Verify `PHASE_NUMBER` is within the frontmatter `phases:` count.
44
+ 6. If the phase is out of range, stop with:
45
+
46
+ ```txt
47
+ Phase {N} is out of range. This plan has {count} phases (1-{count}).
48
+ ```
49
+
50
+ ### 0.2 Derive variables
51
+
52
+ Compute:
53
+
54
+ - `SPEC_DIR`: parent directory of `HL_PLAN_PATH`
55
+ - `SPEC_DIR_BASENAME`: basename of `SPEC_DIR`
56
+ - `TASK_NAME`: frontmatter `task:` value
57
+ - `CATEGORY`: frontmatter `category:` value if present
58
+ - `SPEC_NUMBER`: numeric prefix from `SPEC_DIR_BASENAME` if present
59
+ - `PHASE_NAME`: from `### Phase {N}: {Name}` in the HL plan body
60
+ - `PHASE_NAME_KEBAB`: kebab-case of `PHASE_NAME`
61
+ - `PHASE_DIR`: `{SPEC_DIR}/phase{N}_{PHASE_NAME_KEBAB}/`
62
+ - `REFERENCE_DOCUMENTS`: frontmatter `reference-documents:` list, or empty list
63
+
64
+ ### 0.3 Initialize state
65
+
66
+ Using the `FOB-state-context` skill:
67
+
68
+ 1. Read `specs/STATE.md` if it exists.
69
+ 2. If it does not exist, create a minimal valid state file.
70
+ 3. Find or create the current task entry matching `TASK_NAME`.
71
+ 4. Find or create the `Phase {PHASE_NUMBER}: {PHASE_NAME}` block under that task.
72
+ 5. Ensure Step 1 through Step 6 lines exist under the phase block.
73
+ 6. Ensure `pre-phase-sha` and `post-build-sha` lines exist.
74
+ 7. Mark the task and phase as in-progress (`[~]`) if they are still pending.
75
+
76
+ For Step 0-only mode, do **not** mark Step 1 started.
77
+
78
+ ### 0.4 Git pre-phase checkpoint
79
+
80
+ 1. Run `git status --porcelain`.
81
+ 2. If git is unavailable, set `GIT_AVAILABLE = false` and warn that checkpoint commits are skipped.
82
+ 3. If git is available:
83
+ - Set `GIT_AVAILABLE = true`.
84
+ - If there are uncommitted changes, commit them with:
85
+
86
+ ```txt
87
+ checkpoint: pre-phase-{PHASE_NUMBER} ({PHASE_NAME_KEBAB})
88
+ ```
89
+
90
+ - Capture `PRE_PHASE_SHA` with `git rev-parse HEAD`.
91
+ - Record it in the phase block as `pre-phase-sha`.
92
+
93
+ ### 0.5 Extract phase context
94
+
95
+ From the HL plan, extract and summarize:
96
+
97
+ - Task Overview, usually Section 1
98
+ - User Stories and Anti-Stories, usually Section 3
99
+ - Current Phase section from Section 4
100
+ - High-Level Approach, usually Section 6
101
+ - Key Considerations, usually Section 7
102
+ - Detailed Specifications, usually Section 8 if present
103
+
104
+ Do not create Step 1 research artifacts yet.
105
+
106
+ ### 0.6 Read prior phase reports
107
+
108
+ If `PHASE_NUMBER > 1`:
109
+
110
+ 1. Look for prior phase reports at `{SPEC_DIR}/phase*/phase_completion_report.md` for phases before the current phase.
111
+ 2. Extract:
112
+ - phase number
113
+ - phase name
114
+ - status
115
+ - `## What Was Built`
116
+ - `## Deviations from HL Plan`
117
+ - `## Impacts on Future Phases`
118
+ 3. Compile a `PRIOR_PHASE_CONTEXT` summary.
119
+ 4. Highlight any direct impacts that mention the current phase.
120
+
121
+ If `PHASE_NUMBER == 1`, set prior phase context to `N/A — this is Phase 1`.
122
+
123
+ ### 0.7 Detect project context and skills
124
+
125
+ Using the `FOB-state-context` skill:
126
+
127
+ 1. Detect package manager from lock files.
128
+ 2. Scan available skill directories for skills matching technologies mentioned in the HL plan and phase section.
129
+ 3. Read matched skill files enough to identify skill names and CRITICAL/HIGH rules, if present.
130
+
131
+ ### 0.8 Create phase directory
132
+
133
+ Create `PHASE_DIR` idempotently.
134
+
135
+ ### 0.9 Present Step 0 execution overview
136
+
137
+ Print:
138
+
139
+ ```txt
140
+ BUILD PHASE {N} — {PHASE_NAME}
141
+
142
+ Spec: {SPEC_DIR_BASENAME} ({TASK_NAME})
143
+ Phase Directory: {PHASE_DIR}
144
+ Dependencies: {phase dependencies from HL plan}
145
+ Success Criteria: {count} defined
146
+ Prior Phase Reports: {count read | N/A (Phase 1)}
147
+ Skills Detected: {list or "None"}
148
+ Reference Documents: {count or "None"}
149
+ Resume Status: Step 0 initialized only
150
+ Pre-phase SHA: {PRE_PHASE_SHA | N/A (git unavailable)}
151
+
152
+ Step 0 complete. Workflow intentionally stopped before Step 1 Research.
153
+ ```
154
+
155
+ ## Stop condition
156
+
157
+ After the overview, stop. Do not spawn sub-agents. Do not create `explorer_findings.md`, `docs_research.md`, `plan_V1.md`, or any later-step artifacts.
@@ -0,0 +1,123 @@
1
+ # FOB State Context
2
+
3
+ Use this skill when a workflow needs to read, scaffold, update, or summarize project state for FOB-style build workflows.
4
+
5
+ This skill is required for the Pi build-phase workflow. It defines how the orchestrator manages `specs/STATE.md`, detects project context, and prepares state so later research/build agents can resume safely.
6
+
7
+ ## Core responsibilities
8
+
9
+ 1. Read and update `specs/STATE.md` without destroying unrelated task history.
10
+ 2. Maintain nested task → phase → step progress markers.
11
+ 3. Record git checkpoint SHAs for phase-level resumability.
12
+ 4. Detect package manager and basic project context.
13
+ 5. Discover relevant technology/convention skills for the current plan.
14
+
15
+ ## State marker semantics
16
+
17
+ - `[ ]` means pending / not started.
18
+ - `[~]` means in progress / started but not confirmed complete.
19
+ - `[x]` means completed / output verified.
20
+
21
+ Never mark a step complete unless the expected artifact or action has been verified.
22
+
23
+ ## Expected phase step names
24
+
25
+ Every build phase has these six workflow steps:
26
+
27
+ 1. `Step 1 - Research`
28
+ 2. `Step 2 - Plan`
29
+ 3. `Step 3 - Validate Plan`
30
+ 4. `Step 4 - Build`
31
+ 5. `Step 5 - Validate Build`
32
+ 6. `Step 6 - Report`
33
+
34
+ Step 0 is orchestration setup and is not represented as a numbered step inside the phase block.
35
+
36
+ ## Required phase metadata lines
37
+
38
+ Every phase block must include:
39
+
40
+ ```txt
41
+ pre-phase-sha: (pending)
42
+ post-build-sha: (pending)
43
+ ```
44
+
45
+ `pre-phase-sha` is recorded during Step 0. `post-build-sha` is recorded after the build step.
46
+
47
+ ## Standard state operations
48
+
49
+ ### Initialize or repair state file
50
+
51
+ If `specs/STATE.md` does not exist, create a minimal state file:
52
+
53
+ ```markdown
54
+ # Project State
55
+
56
+ ## Current Tasks
57
+
58
+ ## Completed Tasks
59
+ ```
60
+
61
+ If the file exists, preserve its content and only make targeted updates.
62
+
63
+ ### Initialize task entry
64
+
65
+ Find the current task line by matching the task name. If it does not exist under `## Current Tasks`, add:
66
+
67
+ ```markdown
68
+ - [~] {TASK_NAME}
69
+ ```
70
+
71
+ If the task line exists with `[ ]`, update it to `[~]` when a phase is initialized.
72
+
73
+ ### Initialize phase entry
74
+
75
+ Under the matching task line, ensure this block exists:
76
+
77
+ ```markdown
78
+ - [~] Phase {PHASE_NUMBER}: {PHASE_NAME}
79
+ - [ ] Step 1 - Research
80
+ - [ ] Step 2 - Plan
81
+ - [ ] Step 3 - Validate Plan
82
+ - [ ] Step 4 - Build
83
+ - [ ] Step 5 - Validate Build
84
+ - [ ] Step 6 - Report
85
+ pre-phase-sha: (pending)
86
+ post-build-sha: (pending)
87
+ ```
88
+
89
+ If the phase block exists, repair missing step or SHA lines rather than duplicating the phase.
90
+
91
+ ### Mark step start
92
+
93
+ When a workflow step starts, update its marker from `[ ]` to `[~]`. Also ensure the parent phase and task are marked `[~]` unless already `[x]`.
94
+
95
+ ### Mark step complete
96
+
97
+ When a workflow step has been verified, update its marker from `[~]` to `[x]`. If all six steps under a phase are `[x]`, mark the phase `[x]`. If all phases under a task are `[x]`, mark the task `[x]` and move it to `## Completed Tasks` with the current date.
98
+
99
+ ### Record SHA
100
+
101
+ To record a git SHA, replace the value on the matching line inside the current phase block:
102
+
103
+ ```txt
104
+ pre-phase-sha: {sha}
105
+ post-build-sha: {sha}
106
+ ```
107
+
108
+ Do not update SHA lines in other phases.
109
+
110
+ ## Project context detection
111
+
112
+ Read `references/project-context-detection.md` for package manager detection and skill discovery rules.
113
+
114
+ ## State format reference
115
+
116
+ Read `references/state-format.md` for examples and exact indentation conventions.
117
+
118
+ ## Important constraints
119
+
120
+ - Preserve unrelated tasks, completed task history, comments, and manually added notes whenever possible.
121
+ - Do not invent completed state. Completion requires verified output.
122
+ - Prefer small, targeted edits over rewriting the entire state file when possible.
123
+ - If state and artifacts disagree, future resume logic should trust verified artifacts over stale state markers, but Step 0 only needs to initialize and repair the state skeleton.