specsmd 0.0.0-dev.55 → 0.0.0-dev.57

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.
@@ -1,6 +1,7 @@
1
1
  # Skill: Run Execute
2
2
 
3
- Execute a work item based on its assigned mode (autopilot, confirm, validate).
3
+ Execute work items based on their assigned mode (autopilot, confirm, validate).
4
+ Supports both single-item and multi-item (batch/wide) runs.
4
5
 
5
6
  ---
6
7
 
@@ -33,6 +34,7 @@ Before executing scripts, ensure required dependencies are installed:
33
34
 
34
35
  - Pending work item ready for execution
35
36
  - Resumed from interrupted run
37
+ - Batch of work items passed from run-plan
36
38
 
37
39
  ---
38
40
 
@@ -45,26 +47,82 @@ Before executing scripts, ensure required dependencies are installed:
45
47
 
46
48
  ---
47
49
 
50
+ ## Critical Requirements
51
+
52
+ ### MUST Use Scripts - Never Bypass
53
+
54
+ **CRITICAL**: You MUST call the scripts. DO NOT use mkdir or manual file creation.
55
+
56
+ | Action | CORRECT | WRONG |
57
+ |--------|---------|-------|
58
+ | Initialize run | `node scripts/init-run.js ...` | `mkdir .specs-fire/runs/run-001` |
59
+ | Complete item | `node scripts/complete-run.js ... --complete-item` | Manual state editing |
60
+ | Complete run | `node scripts/complete-run.js ... --complete-run` | Manual state editing |
61
+
62
+ The scripts:
63
+ - Create run folder AND run.md together
64
+ - Update state.yaml atomically
65
+ - Track run history in runs.completed
66
+ - Handle batch run state transitions
67
+
68
+ ### Batch Run Execution Flow
69
+
70
+ For runs with multiple work items:
71
+
72
+ ```
73
+ 1. Call init-run.js ONCE at start (creates run.md with ALL items)
74
+ 2. Execute each work item sequentially:
75
+ - Load item context
76
+ - Execute based on mode (autopilot/confirm/validate)
77
+ - Call complete-run.js --complete-item after each
78
+ 3. Call complete-run.js --complete-run after final item
79
+ ```
80
+
81
+ ---
82
+
48
83
  ## Workflow
49
84
 
50
85
  ```xml
51
86
  <skill name="run-execute">
52
87
 
53
88
  <mandate>
89
+ USE SCRIPTS — Never bypass init-run.js or complete-run.js.
54
90
  TRACK ALL FILE OPERATIONS — Every create, modify must be recorded.
55
91
  NEVER skip tests — Tests are mandatory, not optional.
56
92
  FOLLOW BROWNFIELD RULES — Read before write, match existing patterns.
57
93
  </mandate>
58
94
 
59
95
  <step n="1" title="Initialize Run">
60
- <action script="scripts/init-run.js">Create run record</action>
61
- <action>Generate run ID: run-{NNN}</action>
62
- <action>Create folder: .specs-fire/runs/{run-id}/</action>
63
- <action>Set active_run in state.yaml</action>
96
+ <critical>
97
+ MUST call init-run.js script. DO NOT use mkdir directly.
98
+ The script creates BOTH the folder AND run.md file.
99
+ </critical>
100
+
101
+ <action>Prepare work items JSON array:</action>
102
+ <code>
103
+ # For single item:
104
+ node scripts/init-run.js {rootPath} {workItemId} {intentId} {mode}
105
+
106
+ # For batch/wide (multiple items):
107
+ node scripts/init-run.js {rootPath} --batch '[
108
+ {"id": "item-1", "intent": "intent-1", "mode": "autopilot"},
109
+ {"id": "item-2", "intent": "intent-1", "mode": "confirm"}
110
+ ]' --scope=batch
111
+ </code>
112
+
113
+ <action>Parse script output for runId and runPath</action>
114
+ <action>Verify run.md was created in .specs-fire/runs/{run-id}/</action>
115
+
116
+ <check if="run.md not found">
117
+ <error>init-run.js failed to create run.md. Check script output.</error>
118
+ </check>
64
119
  </step>
65
120
 
66
- <step n="2" title="Load Context">
67
- <action>Read work item from .specs-fire/intents/{intent}/work-items/{id}.md</action>
121
+ <step n="2" title="Execute Work Items Loop">
122
+ <note>For batch runs, repeat steps 2-6 for each work item</note>
123
+
124
+ <action>Get current_item from state.yaml active_run</action>
125
+ <action>Load work item from .specs-fire/intents/{intent}/work-items/{id}.md</action>
68
126
  <action>Read intent brief for broader context</action>
69
127
  <action>Load ALL project standards:</action>
70
128
  <substep>.specs-fire/standards/tech-stack.md — Technology choices</substep>
@@ -183,15 +241,36 @@ Before executing scripts, ensure required dependencies are installed:
183
241
  </check>
184
242
 
185
243
  <action>Validate acceptance criteria from work item</action>
186
- <action>Measure coverage against target from testing-standards.md</action>
187
- <action>Generate test report using template: templates/test-report.md.hbs</action>
188
- <action>Save to: .specs-fire/runs/{run-id}/test-report.md</action>
189
244
  </step>
190
245
 
191
- <step n="7" title="Complete Run">
192
- <action script="scripts/complete-run.js">Finalize run record</action>
193
- <action>Update state.yaml (clear active_run, mark work item complete)</action>
194
- <action>Generate run log: .specs-fire/runs/{run-id}/run.md</action>
246
+ <step n="7" title="Complete Current Work Item">
247
+ <critical>
248
+ MUST call complete-run.js script. Check if more items remain.
249
+ </critical>
250
+
251
+ <check if="batch run with more items pending">
252
+ <action>Call complete-run.js with --complete-item flag:</action>
253
+ <code>
254
+ node scripts/complete-run.js {rootPath} {runId} --complete-item
255
+ </code>
256
+ <action>Parse output for nextItem</action>
257
+ <output>
258
+ Work item {current_item} complete.
259
+ Next: {nextItem}
260
+ </output>
261
+ <goto step="2">Continue with next work item</goto>
262
+ </check>
263
+
264
+ <check if="last item or single item run">
265
+ <action>Call complete-run.js with --complete-run flag:</action>
266
+ <code>
267
+ node scripts/complete-run.js {rootPath} {runId} --complete-run \
268
+ --files-created='[{"path":"...","purpose":"..."}]' \
269
+ --files-modified='[{"path":"...","changes":"..."}]' \
270
+ --tests=5 --coverage=85
271
+ </code>
272
+ <goto step="8"/>
273
+ </check>
195
274
  </step>
196
275
 
197
276
  <step n="8" title="Generate Walkthrough">
@@ -200,18 +279,16 @@ Before executing scripts, ensure required dependencies are installed:
200
279
 
201
280
  <step n="9" title="Report Completion">
202
281
  <output>
203
- Run {run-id} completed for "{title}".
282
+ Run {run-id} completed.
204
283
 
284
+ Work items completed: {count}
205
285
  Files created: {count}
206
286
  Files modified: {count}
207
287
  Tests added: {count}
208
- Coverage: {percentage}%
209
288
 
210
289
  Artifacts:
211
- - Test Report: .specs-fire/runs/{run-id}/test-report.md
290
+ - Run Log: .specs-fire/runs/{run-id}/run.md
212
291
  - Walkthrough: .specs-fire/runs/{run-id}/walkthrough.md
213
-
214
- Continue to next work item? [Y/n]
215
292
  </output>
216
293
  </step>
217
294
 
@@ -222,10 +299,71 @@ Before executing scripts, ensure required dependencies are installed:
222
299
 
223
300
  ## Scripts
224
301
 
225
- | Script | Purpose |
226
- |--------|---------|
227
- | `scripts/init-run.js` | Initialize run record and folder |
228
- | `scripts/complete-run.js` | Finalize run and update state |
302
+ | Script | Purpose | Usage |
303
+ |--------|---------|-------|
304
+ | `scripts/init-run.js` | Initialize run record and folder | Creates run.md with all work items |
305
+ | `scripts/complete-run.js` | Finalize run and update state | `--complete-item` or `--complete-run` |
306
+
307
+ ### init-run.js Usage
308
+
309
+ ```bash
310
+ # Single work item
311
+ node scripts/init-run.js /project work-item-id intent-id autopilot
312
+
313
+ # Batch/wide (multiple items)
314
+ node scripts/init-run.js /project --batch '[
315
+ {"id": "wi-1", "intent": "int-1", "mode": "autopilot"},
316
+ {"id": "wi-2", "intent": "int-1", "mode": "confirm"}
317
+ ]' --scope=batch
318
+ ```
319
+
320
+ **Output:**
321
+ ```json
322
+ {
323
+ "success": true,
324
+ "runId": "run-001",
325
+ "runPath": "/project/.specs-fire/runs/run-001",
326
+ "scope": "batch",
327
+ "workItems": [...],
328
+ "currentItem": "wi-1"
329
+ }
330
+ ```
331
+
332
+ ### complete-run.js Usage
333
+
334
+ ```bash
335
+ # Complete current item (batch runs - moves to next item)
336
+ node scripts/complete-run.js /project run-001 --complete-item
337
+
338
+ # Complete entire run (single runs or final item in batch)
339
+ node scripts/complete-run.js /project run-001 --complete-run \
340
+ --files-created='[{"path":"src/new.ts","purpose":"New feature"}]' \
341
+ --files-modified='[{"path":"src/old.ts","changes":"Added import"}]' \
342
+ --tests=5 --coverage=85
343
+ ```
344
+
345
+ **--complete-item Output:**
346
+ ```json
347
+ {
348
+ "success": true,
349
+ "runId": "run-001",
350
+ "completedItem": "wi-1",
351
+ "nextItem": "wi-2",
352
+ "remainingItems": 1,
353
+ "allItemsCompleted": false
354
+ }
355
+ ```
356
+
357
+ **--complete-run Output:**
358
+ ```json
359
+ {
360
+ "success": true,
361
+ "runId": "run-001",
362
+ "scope": "batch",
363
+ "workItemsCompleted": 2,
364
+ "completedAt": "2026-01-20T..."
365
+ }
366
+ ```
229
367
 
230
368
  ---
231
369
 
@@ -244,3 +382,23 @@ decisions:
244
382
  - decision: Use JWT for tokens
245
383
  rationale: Stateless, works with load balancer
246
384
  ```
385
+
386
+ ---
387
+
388
+ ## Run Folder Structure
389
+
390
+ After init-run.js creates a run:
391
+
392
+ ```
393
+ .specs-fire/runs/run-001/
394
+ ├── run.md # Created by init-run.js, updated by complete-run.js
395
+ ├── plan.md # Created during confirm/validate mode (optional)
396
+ └── walkthrough.md # Created by walkthrough-generate skill
397
+ ```
398
+
399
+ The run.md contains:
400
+ - All work items with their statuses
401
+ - Current item being executed
402
+ - Files created/modified (after completion)
403
+ - Decisions made (after completion)
404
+ - Summary (after completion)
@@ -3,15 +3,24 @@
3
3
  /**
4
4
  * FIRE Run Completion Script
5
5
  *
6
- * Finalizes a run by:
7
- * 1. Recording the completed run in state.yaml runs.completed history
8
- * 2. Updating work item status to completed
9
- * 3. Clearing active_run
10
- * 4. Updating run.md with completion data
6
+ * Supports both single and batch/wide runs.
11
7
  *
12
- * Usage: node complete-run.js <rootPath> <runId> [--files-created=JSON] [--files-modified=JSON] [--decisions=JSON] [--tests=N] [--coverage=N]
8
+ * For single runs: Completes the run and clears active_run.
9
+ * For batch/wide runs:
10
+ * - --complete-item: Marks current work item done, moves to next
11
+ * - --complete-run: Marks all items done and finalizes entire run
13
12
  *
14
- * Example: node complete-run.js /project run-003 --tests=5 --coverage=85
13
+ * Usage:
14
+ * Complete current item: node complete-run.js <rootPath> <runId> --complete-item [options]
15
+ * Complete entire run: node complete-run.js <rootPath> <runId> --complete-run [options]
16
+ * Complete (single/auto): node complete-run.js <rootPath> <runId> [options]
17
+ *
18
+ * Options:
19
+ * --files-created=JSON - JSON array of {path, purpose}
20
+ * --files-modified=JSON - JSON array of {path, changes}
21
+ * --decisions=JSON - JSON array of {decision, choice, rationale}
22
+ * --tests=N - Number of tests added
23
+ * --coverage=N - Coverage percentage
15
24
  */
16
25
 
17
26
  const fs = require('fs');
@@ -131,7 +140,7 @@ function writeState(statePath, state) {
131
140
  // Run Log Operations
132
141
  // =============================================================================
133
142
 
134
- function updateRunLog(runLogPath, params, completedTime) {
143
+ function updateRunLog(runLogPath, activeRun, params, completedTime, isFullCompletion) {
135
144
  let content;
136
145
  try {
137
146
  content = fs.readFileSync(runLogPath, 'utf8');
@@ -143,40 +152,72 @@ function updateRunLog(runLogPath, params, completedTime) {
143
152
  );
144
153
  }
145
154
 
146
- // Update status
147
- content = content.replace(/status: in_progress/, 'status: completed');
148
- content = content.replace(/completed: null/, `completed: ${completedTime}`);
155
+ // If full completion, update run status
156
+ if (isFullCompletion) {
157
+ content = content.replace(/status: in_progress/, 'status: completed');
158
+ content = content.replace(/completed: null/, `completed: ${completedTime}`);
159
+ }
160
+
161
+ // Update work items status in frontmatter
162
+ if (activeRun.work_items && Array.isArray(activeRun.work_items)) {
163
+ for (const item of activeRun.work_items) {
164
+ // Update status in YAML frontmatter
165
+ const statusPattern = new RegExp(`(id: ${item.id}\\n\\s+intent: [^\\n]+\\n\\s+mode: [^\\n]+\\n\\s+status: )(\\w+)`);
166
+ content = content.replace(statusPattern, `$1${item.status}`);
167
+
168
+ // Update status in markdown body
169
+ const bodyPattern = new RegExp(`(\\*\\*${item.id}\\*\\* \\([^)]+\\) — )(\\w+)`);
170
+ content = content.replace(bodyPattern, `$1${item.status}`);
171
+ }
172
+
173
+ // Update current_item in frontmatter
174
+ content = content.replace(/current_item: [^\n]+/, `current_item: ${activeRun.current_item || 'none'}`);
175
+
176
+ // Update Current Item section
177
+ if (activeRun.current_item) {
178
+ const currentItem = activeRun.work_items.find(i => i.id === activeRun.current_item);
179
+ if (currentItem) {
180
+ content = content.replace(/## Current Item\n[^\n]+/, `## Current Item\n${currentItem.id} (${currentItem.mode})`);
181
+ }
182
+ } else {
183
+ content = content.replace(/## Current Item\n[^\n]+/, `## Current Item\n(all completed)`);
184
+ }
185
+ }
149
186
 
150
- // Format file lists
151
- const filesCreatedText = params.filesCreated.length > 0
152
- ? params.filesCreated.map(f => `- \`${f.path}\`: ${f.purpose || '(no purpose)'}`).join('\n')
153
- : '(none)';
187
+ // Format file lists (only on full completion)
188
+ if (isFullCompletion) {
189
+ const filesCreatedText = params.filesCreated.length > 0
190
+ ? params.filesCreated.map(f => `- \`${f.path}\`: ${f.purpose || '(no purpose)'}`).join('\n')
191
+ : '(none)';
154
192
 
155
- const filesModifiedText = params.filesModified.length > 0
156
- ? params.filesModified.map(f => `- \`${f.path}\`: ${f.changes || '(no changes)'}`).join('\n')
157
- : '(none)';
193
+ const filesModifiedText = params.filesModified.length > 0
194
+ ? params.filesModified.map(f => `- \`${f.path}\`: ${f.changes || '(no changes)'}`).join('\n')
195
+ : '(none)';
158
196
 
159
- const decisionsText = params.decisions.length > 0
160
- ? params.decisions.map(d => `- **${d.decision}**: ${d.choice} (${d.rationale || 'no rationale'})`).join('\n')
161
- : '(none)';
197
+ const decisionsText = params.decisions.length > 0
198
+ ? params.decisions.map(d => `- **${d.decision}**: ${d.choice} (${d.rationale || 'no rationale'})`).join('\n')
199
+ : '(none)';
162
200
 
163
- // Replace placeholder sections
164
- content = content.replace('## Files Created\n(none yet)', `## Files Created\n${filesCreatedText}`);
165
- content = content.replace('## Files Modified\n(none yet)', `## Files Modified\n${filesModifiedText}`);
166
- content = content.replace('## Decisions\n(none yet)', `## Decisions\n${decisionsText}`);
201
+ // Replace placeholder sections
202
+ content = content.replace('## Files Created\n(none yet)', `## Files Created\n${filesCreatedText}`);
203
+ content = content.replace('## Files Modified\n(none yet)', `## Files Modified\n${filesModifiedText}`);
204
+ content = content.replace('## Decisions\n(none yet)', `## Decisions\n${decisionsText}`);
167
205
 
168
- // Add summary if not present
169
- if (!content.includes('## Summary')) {
170
- content += `
206
+ // Add summary if not present
207
+ if (!content.includes('## Summary')) {
208
+ const itemCount = activeRun.work_items ? activeRun.work_items.length : 1;
209
+ content += `
171
210
 
172
211
  ## Summary
173
212
 
213
+ - Work items completed: ${itemCount}
174
214
  - Files created: ${params.filesCreated.length}
175
215
  - Files modified: ${params.filesModified.length}
176
216
  - Tests added: ${params.testsAdded}
177
217
  - Coverage: ${params.coverage}%
178
218
  - Completed: ${completedTime}
179
219
  `;
220
+ }
180
221
  }
181
222
 
182
223
  try {
@@ -191,11 +232,10 @@ function updateRunLog(runLogPath, params, completedTime) {
191
232
  }
192
233
 
193
234
  // =============================================================================
194
- // Main Function
235
+ // Complete Current Item (for batch runs)
195
236
  // =============================================================================
196
237
 
197
- function completeRun(rootPath, runId, params = {}) {
198
- // Defaults for optional params
238
+ function completeCurrentItem(rootPath, runId, params = {}) {
199
239
  const completionParams = {
200
240
  filesCreated: params.filesCreated || [],
201
241
  filesModified: params.filesModified || [],
@@ -204,16 +244,97 @@ function completeRun(rootPath, runId, params = {}) {
204
244
  coverage: params.coverage || 0,
205
245
  };
206
246
 
207
- // Validate inputs
208
247
  validateInputs(rootPath, runId);
209
-
210
- // Validate FIRE project structure
211
248
  const { statePath, runLogPath } = validateFireProject(rootPath, runId);
249
+ const state = readState(statePath);
250
+
251
+ if (!state.active_run) {
252
+ throw fireError(
253
+ 'No active run found in state.yaml.',
254
+ 'COMPLETE_040',
255
+ 'The run may have already been completed or was never started.'
256
+ );
257
+ }
258
+
259
+ if (state.active_run.id !== runId) {
260
+ throw fireError(
261
+ `Run ID mismatch. Active run is "${state.active_run.id}" but trying to complete "${runId}".`,
262
+ 'COMPLETE_041',
263
+ `Complete the active run "${state.active_run.id}" first.`
264
+ );
265
+ }
266
+
267
+ const completedTime = new Date().toISOString();
268
+ const workItems = state.active_run.work_items || [];
269
+ const currentItemId = state.active_run.current_item;
270
+
271
+ // Find and mark current item as completed
272
+ let currentItemIndex = -1;
273
+ for (let i = 0; i < workItems.length; i++) {
274
+ if (workItems[i].id === currentItemId) {
275
+ workItems[i].status = 'completed';
276
+ workItems[i].completed_at = completedTime;
277
+ currentItemIndex = i;
278
+ break;
279
+ }
280
+ }
281
+
282
+ if (currentItemIndex === -1) {
283
+ throw fireError(
284
+ `Current item "${currentItemId}" not found in work items.`,
285
+ 'COMPLETE_050',
286
+ 'The run state may be corrupted.'
287
+ );
288
+ }
212
289
 
213
- // Read state
290
+ // Find next pending item
291
+ let nextItem = null;
292
+ for (let i = currentItemIndex + 1; i < workItems.length; i++) {
293
+ if (workItems[i].status === 'pending') {
294
+ workItems[i].status = 'in_progress';
295
+ nextItem = workItems[i];
296
+ break;
297
+ }
298
+ }
299
+
300
+ // Update state
301
+ state.active_run.work_items = workItems;
302
+ state.active_run.current_item = nextItem ? nextItem.id : null;
303
+
304
+ // Update run log
305
+ updateRunLog(runLogPath, state.active_run, completionParams, completedTime, false);
306
+
307
+ // Save state
308
+ writeState(statePath, state);
309
+
310
+ return {
311
+ success: true,
312
+ runId: runId,
313
+ completedItem: currentItemId,
314
+ nextItem: nextItem ? nextItem.id : null,
315
+ remainingItems: workItems.filter(i => i.status === 'pending').length,
316
+ allItemsCompleted: nextItem === null,
317
+ completedAt: completedTime,
318
+ };
319
+ }
320
+
321
+ // =============================================================================
322
+ // Complete Entire Run
323
+ // =============================================================================
324
+
325
+ function completeRun(rootPath, runId, params = {}) {
326
+ const completionParams = {
327
+ filesCreated: params.filesCreated || [],
328
+ filesModified: params.filesModified || [],
329
+ decisions: params.decisions || [],
330
+ testsAdded: params.testsAdded || 0,
331
+ coverage: params.coverage || 0,
332
+ };
333
+
334
+ validateInputs(rootPath, runId);
335
+ const { statePath, runLogPath } = validateFireProject(rootPath, runId);
214
336
  const state = readState(statePath);
215
337
 
216
- // Validate active run matches
217
338
  if (!state.active_run) {
218
339
  throw fireError(
219
340
  'No active run found in state.yaml.',
@@ -230,19 +351,33 @@ function completeRun(rootPath, runId, params = {}) {
230
351
  );
231
352
  }
232
353
 
233
- // Extract work item and intent from active run
234
- const workItemId = state.active_run.work_item;
235
- const intentId = state.active_run.intent;
236
354
  const completedTime = new Date().toISOString();
355
+ const workItems = state.active_run.work_items || [];
356
+ const scope = state.active_run.scope || 'single';
357
+
358
+ // Mark all items as completed
359
+ for (const item of workItems) {
360
+ if (item.status !== 'completed') {
361
+ item.status = 'completed';
362
+ item.completed_at = completedTime;
363
+ }
364
+ }
365
+
366
+ state.active_run.work_items = workItems;
367
+ state.active_run.current_item = null;
237
368
 
238
369
  // Update run log
239
- updateRunLog(runLogPath, completionParams, completedTime);
370
+ updateRunLog(runLogPath, state.active_run, completionParams, completedTime, true);
240
371
 
241
372
  // Build completed run record
242
373
  const completedRun = {
243
374
  id: runId,
244
- work_item: workItemId,
245
- intent: intentId,
375
+ scope: scope,
376
+ work_items: workItems.map(i => ({
377
+ id: i.id,
378
+ intent: i.intent,
379
+ mode: i.mode,
380
+ })),
246
381
  completed: completedTime,
247
382
  };
248
383
 
@@ -255,16 +390,17 @@ function completeRun(rootPath, runId, params = {}) {
255
390
 
256
391
  // Update work item status in intents
257
392
  if (Array.isArray(state.intents)) {
258
- for (const intent of state.intents) {
259
- if (intent.id === intentId && Array.isArray(intent.work_items)) {
260
- for (const workItem of intent.work_items) {
261
- if (workItem.id === workItemId) {
262
- workItem.status = 'completed';
263
- workItem.run_id = runId;
264
- break;
393
+ for (const workItem of workItems) {
394
+ for (const intent of state.intents) {
395
+ if (intent.id === workItem.intent && Array.isArray(intent.work_items)) {
396
+ for (const wi of intent.work_items) {
397
+ if (wi.id === workItem.id) {
398
+ wi.status = 'completed';
399
+ wi.run_id = runId;
400
+ break;
401
+ }
265
402
  }
266
403
  }
267
- break;
268
404
  }
269
405
  }
270
406
  }
@@ -278,12 +414,11 @@ function completeRun(rootPath, runId, params = {}) {
278
414
  // Save state
279
415
  writeState(statePath, state);
280
416
 
281
- // Return result
282
417
  return {
283
418
  success: true,
284
419
  runId: runId,
285
- workItemId: workItemId,
286
- intentId: intentId,
420
+ scope: scope,
421
+ workItemsCompleted: workItems.length,
287
422
  completedAt: completedTime,
288
423
  filesCreated: completionParams.filesCreated.length,
289
424
  filesModified: completionParams.filesModified.length,
@@ -300,6 +435,8 @@ function parseArgs(args) {
300
435
  const result = {
301
436
  rootPath: args[0],
302
437
  runId: args[1],
438
+ completeItem: false,
439
+ completeRunFlag: false,
303
440
  filesCreated: [],
304
441
  filesModified: [],
305
442
  decisions: [],
@@ -309,7 +446,11 @@ function parseArgs(args) {
309
446
 
310
447
  for (let i = 2; i < args.length; i++) {
311
448
  const arg = args[i];
312
- if (arg.startsWith('--files-created=')) {
449
+ if (arg === '--complete-item') {
450
+ result.completeItem = true;
451
+ } else if (arg === '--complete-run') {
452
+ result.completeRunFlag = true;
453
+ } else if (arg.startsWith('--files-created=')) {
313
454
  try {
314
455
  result.filesCreated = JSON.parse(arg.substring('--files-created='.length));
315
456
  } catch (e) {
@@ -337,6 +478,32 @@ function parseArgs(args) {
337
478
  return result;
338
479
  }
339
480
 
481
+ function printUsage() {
482
+ console.error('Usage:');
483
+ console.error(' Complete current item: node complete-run.js <rootPath> <runId> --complete-item [options]');
484
+ console.error(' Complete entire run: node complete-run.js <rootPath> <runId> --complete-run [options]');
485
+ console.error(' Auto (single runs): node complete-run.js <rootPath> <runId> [options]');
486
+ console.error('');
487
+ console.error('Arguments:');
488
+ console.error(' rootPath - Project root directory');
489
+ console.error(' runId - Run ID to complete (e.g., run-003)');
490
+ console.error('');
491
+ console.error('Flags:');
492
+ console.error(' --complete-item - Complete only the current work item (batch/wide runs)');
493
+ console.error(' --complete-run - Complete the entire run');
494
+ console.error('');
495
+ console.error('Options:');
496
+ console.error(' --files-created=JSON - JSON array of {path, purpose}');
497
+ console.error(' --files-modified=JSON - JSON array of {path, changes}');
498
+ console.error(' --decisions=JSON - JSON array of {decision, choice, rationale}');
499
+ console.error(' --tests=N - Number of tests added');
500
+ console.error(' --coverage=N - Coverage percentage');
501
+ console.error('');
502
+ console.error('Examples:');
503
+ console.error(' node complete-run.js /project run-003 --complete-item');
504
+ console.error(' node complete-run.js /project run-003 --complete-run --tests=5 --coverage=85');
505
+ }
506
+
340
507
  // =============================================================================
341
508
  // CLI Interface
342
509
  // =============================================================================
@@ -345,34 +512,32 @@ if (require.main === module) {
345
512
  const args = process.argv.slice(2);
346
513
 
347
514
  if (args.length < 2) {
348
- console.error('Usage: node complete-run.js <rootPath> <runId> [options]');
349
- console.error('');
350
- console.error('Arguments:');
351
- console.error(' rootPath - Project root directory');
352
- console.error(' runId - Run ID to complete (e.g., run-003)');
353
- console.error('');
354
- console.error('Options:');
355
- console.error(' --files-created=JSON - JSON array of {path, purpose}');
356
- console.error(' --files-modified=JSON - JSON array of {path, changes}');
357
- console.error(' --decisions=JSON - JSON array of {decision, choice, rationale}');
358
- console.error(' --tests=N - Number of tests added');
359
- console.error(' --coverage=N - Coverage percentage');
360
- console.error('');
361
- console.error('Example:');
362
- console.error(' node complete-run.js /my/project run-003 --tests=5 --coverage=85');
515
+ printUsage();
363
516
  process.exit(1);
364
517
  }
365
518
 
366
519
  const params = parseArgs(args);
367
520
 
368
521
  try {
369
- const result = completeRun(params.rootPath, params.runId, {
370
- filesCreated: params.filesCreated,
371
- filesModified: params.filesModified,
372
- decisions: params.decisions,
373
- testsAdded: params.testsAdded,
374
- coverage: params.coverage,
375
- });
522
+ let result;
523
+ if (params.completeItem) {
524
+ result = completeCurrentItem(params.rootPath, params.runId, {
525
+ filesCreated: params.filesCreated,
526
+ filesModified: params.filesModified,
527
+ decisions: params.decisions,
528
+ testsAdded: params.testsAdded,
529
+ coverage: params.coverage,
530
+ });
531
+ } else {
532
+ // Default: complete entire run
533
+ result = completeRun(params.rootPath, params.runId, {
534
+ filesCreated: params.filesCreated,
535
+ filesModified: params.filesModified,
536
+ decisions: params.decisions,
537
+ testsAdded: params.testsAdded,
538
+ coverage: params.coverage,
539
+ });
540
+ }
376
541
  console.log(JSON.stringify(result, null, 2));
377
542
  process.exit(0);
378
543
  } catch (err) {
@@ -381,4 +546,4 @@ if (require.main === module) {
381
546
  }
382
547
  }
383
548
 
384
- module.exports = { completeRun };
549
+ module.exports = { completeRun, completeCurrentItem };
@@ -4,13 +4,19 @@
4
4
  * FIRE Run Initialization Script
5
5
  *
6
6
  * Creates run record in state.yaml and run folder structure.
7
+ * Supports both single work item and batch/wide runs with multiple items.
8
+ *
7
9
  * Ensures deterministic run ID generation by checking BOTH:
8
10
  * - runs.completed history in state.yaml
9
11
  * - existing run folders in .specs-fire/runs/
10
12
  *
11
- * Usage: node init-run.js <rootPath> <workItemId> <intentId> <mode>
13
+ * Usage:
14
+ * Single item: node init-run.js <rootPath> <workItemId> <intentId> <mode>
15
+ * Batch/Wide: node init-run.js <rootPath> --batch '<workItemsJson>'
12
16
  *
13
- * Example: node init-run.js /project login-endpoint user-auth confirm
17
+ * Examples:
18
+ * node init-run.js /project login-endpoint user-auth confirm
19
+ * node init-run.js /project --batch '[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]'
14
20
  */
15
21
 
16
22
  const fs = require('fs');
@@ -33,35 +39,58 @@ function fireError(message, code, suggestion) {
33
39
  // =============================================================================
34
40
 
35
41
  const VALID_MODES = ['autopilot', 'confirm', 'validate'];
42
+ const VALID_SCOPES = ['single', 'batch', 'wide'];
36
43
 
37
- function validateInputs(rootPath, workItemId, intentId, mode) {
44
+ function validateRootPath(rootPath) {
38
45
  if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
39
46
  throw fireError('rootPath is required.', 'INIT_001', 'Provide a valid project root path.');
40
47
  }
41
48
 
42
- if (!workItemId || typeof workItemId !== 'string' || workItemId.trim() === '') {
43
- throw fireError('workItemId is required.', 'INIT_010', 'Provide a valid work item ID.');
49
+ if (!fs.existsSync(rootPath)) {
50
+ throw fireError(
51
+ `Project root not found: "${rootPath}".`,
52
+ 'INIT_040',
53
+ 'Ensure the path exists and is accessible.'
54
+ );
55
+ }
56
+ }
57
+
58
+ function validateWorkItem(item, index) {
59
+ if (!item.id || typeof item.id !== 'string' || item.id.trim() === '') {
60
+ throw fireError(
61
+ `Work item at index ${index} missing 'id'.`,
62
+ 'INIT_010',
63
+ 'Each work item must have an id.'
64
+ );
44
65
  }
45
66
 
46
- if (!intentId || typeof intentId !== 'string' || intentId.trim() === '') {
47
- throw fireError('intentId is required.', 'INIT_020', 'Provide a valid intent ID.');
67
+ if (!item.intent || typeof item.intent !== 'string' || item.intent.trim() === '') {
68
+ throw fireError(
69
+ `Work item "${item.id}" missing 'intent'.`,
70
+ 'INIT_020',
71
+ 'Each work item must have an intent.'
72
+ );
48
73
  }
49
74
 
50
- if (!mode || !VALID_MODES.includes(mode)) {
75
+ if (!item.mode || !VALID_MODES.includes(item.mode)) {
51
76
  throw fireError(
52
- `Invalid mode: "${mode}".`,
77
+ `Work item "${item.id}" has invalid mode: "${item.mode}".`,
53
78
  'INIT_030',
54
79
  `Valid modes are: ${VALID_MODES.join(', ')}`
55
80
  );
56
81
  }
82
+ }
57
83
 
58
- if (!fs.existsSync(rootPath)) {
84
+ function validateWorkItems(workItems) {
85
+ if (!Array.isArray(workItems) || workItems.length === 0) {
59
86
  throw fireError(
60
- `Project root not found: "${rootPath}".`,
61
- 'INIT_040',
62
- 'Ensure the path exists and is accessible.'
87
+ 'Work items array is empty or invalid.',
88
+ 'INIT_011',
89
+ 'Provide at least one work item.'
63
90
  );
64
91
  }
92
+
93
+ workItems.forEach((item, index) => validateWorkItem(item, index));
65
94
  }
66
95
 
67
96
  function validateFireProject(rootPath) {
@@ -171,6 +200,19 @@ function generateRunId(runsPath, state) {
171
200
  return `run-${String(nextNum).padStart(3, '0')}`;
172
201
  }
173
202
 
203
+ // =============================================================================
204
+ // Scope Detection
205
+ // =============================================================================
206
+
207
+ function detectScope(workItems) {
208
+ if (workItems.length === 1) {
209
+ return 'single';
210
+ }
211
+ // For multiple items, default to batch
212
+ // (wide would be explicitly set by the caller if all compatible items are included)
213
+ return 'batch';
214
+ }
215
+
174
216
  // =============================================================================
175
217
  // Run Folder Creation
176
218
  // =============================================================================
@@ -187,12 +229,21 @@ function createRunFolder(runPath) {
187
229
  }
188
230
  }
189
231
 
190
- function createRunLog(runPath, runId, workItemId, intentId, mode, startTime) {
232
+ function createRunLog(runPath, runId, workItems, scope, startTime) {
233
+ // Format work items for run.md
234
+ const workItemsList = workItems.map((item, index) => {
235
+ const status = index === 0 ? 'in_progress' : 'pending';
236
+ return ` - id: ${item.id}\n intent: ${item.intent}\n mode: ${item.mode}\n status: ${status}`;
237
+ }).join('\n');
238
+
239
+ const currentItem = workItems[0];
240
+
191
241
  const runLog = `---
192
242
  id: ${runId}
193
- work_item: ${workItemId}
194
- intent: ${intentId}
195
- mode: ${mode}
243
+ scope: ${scope}
244
+ work_items:
245
+ ${workItemsList}
246
+ current_item: ${currentItem.id}
196
247
  status: in_progress
197
248
  started: ${startTime}
198
249
  completed: null
@@ -200,8 +251,14 @@ completed: null
200
251
 
201
252
  # Run: ${runId}
202
253
 
203
- ## Work Item
204
- ${workItemId}
254
+ ## Scope
255
+ ${scope} (${workItems.length} work item${workItems.length > 1 ? 's' : ''})
256
+
257
+ ## Work Items
258
+ ${workItems.map((item, i) => `${i + 1}. **${item.id}** (${item.mode}) — ${i === 0 ? 'in_progress' : 'pending'}`).join('\n')}
259
+
260
+ ## Current Item
261
+ ${currentItem.id} (${currentItem.mode})
205
262
 
206
263
  ## Files Created
207
264
  (none yet)
@@ -229,9 +286,30 @@ ${workItemId}
229
286
  // Main Function
230
287
  // =============================================================================
231
288
 
232
- function initRun(rootPath, workItemId, intentId, mode) {
233
- // Validate inputs
234
- validateInputs(rootPath, workItemId, intentId, mode);
289
+ /**
290
+ * Initialize a run with one or more work items.
291
+ *
292
+ * @param {string} rootPath - Project root directory
293
+ * @param {Array<{id: string, intent: string, mode: string}>} workItems - Work items to include in run
294
+ * @param {string} [scope] - Optional scope override ('single', 'batch', 'wide')
295
+ * @returns {object} Result with runId, runPath, workItems, scope, started
296
+ */
297
+ function initRun(rootPath, workItems, scope) {
298
+ // Validate root path
299
+ validateRootPath(rootPath);
300
+
301
+ // Validate work items
302
+ validateWorkItems(workItems);
303
+
304
+ // Detect or validate scope
305
+ const detectedScope = scope || detectScope(workItems);
306
+ if (scope && !VALID_SCOPES.includes(scope)) {
307
+ throw fireError(
308
+ `Invalid scope: "${scope}".`,
309
+ 'INIT_035',
310
+ `Valid scopes are: ${VALID_SCOPES.join(', ')}`
311
+ );
312
+ }
235
313
 
236
314
  // Validate FIRE project structure
237
315
  const { statePath, runsPath } = validateFireProject(rootPath);
@@ -257,14 +335,22 @@ function initRun(rootPath, workItemId, intentId, mode) {
257
335
 
258
336
  // Create run log
259
337
  const startTime = new Date().toISOString();
260
- createRunLog(runPath, runId, workItemId, intentId, mode, startTime);
338
+ createRunLog(runPath, runId, workItems, detectedScope, startTime);
339
+
340
+ // Prepare work items for state with status tracking
341
+ const stateWorkItems = workItems.map((item, index) => ({
342
+ id: item.id,
343
+ intent: item.intent,
344
+ mode: item.mode,
345
+ status: index === 0 ? 'in_progress' : 'pending',
346
+ }));
261
347
 
262
348
  // Update state with active run
263
349
  state.active_run = {
264
350
  id: runId,
265
- work_item: workItemId,
266
- intent: intentId,
267
- mode: mode,
351
+ scope: detectedScope,
352
+ work_items: stateWorkItems,
353
+ current_item: workItems[0].id,
268
354
  started: startTime,
269
355
  };
270
356
 
@@ -276,9 +362,9 @@ function initRun(rootPath, workItemId, intentId, mode) {
276
362
  success: true,
277
363
  runId: runId,
278
364
  runPath: runPath,
279
- workItemId: workItemId,
280
- intentId: intentId,
281
- mode: mode,
365
+ scope: detectedScope,
366
+ workItems: stateWorkItems,
367
+ currentItem: workItems[0].id,
282
368
  started: startTime,
283
369
  };
284
370
  }
@@ -287,27 +373,76 @@ function initRun(rootPath, workItemId, intentId, mode) {
287
373
  // CLI Interface
288
374
  // =============================================================================
289
375
 
376
+ function printUsage() {
377
+ console.error('Usage:');
378
+ console.error(' Single item: node init-run.js <rootPath> <workItemId> <intentId> <mode>');
379
+ console.error(' Batch/Wide: node init-run.js <rootPath> --batch \'<workItemsJson>\' [--scope=<scope>]');
380
+ console.error('');
381
+ console.error('Arguments:');
382
+ console.error(' rootPath - Project root directory');
383
+ console.error(' workItemId - Work item ID (single mode)');
384
+ console.error(' intentId - Intent ID (single mode)');
385
+ console.error(' mode - Execution mode: autopilot, confirm, validate');
386
+ console.error('');
387
+ console.error('Options:');
388
+ console.error(' --batch - JSON array of work items');
389
+ console.error(' --scope - Override scope: single, batch, wide');
390
+ console.error('');
391
+ console.error('Work item JSON format:');
392
+ console.error(' [{"id": "wi-1", "intent": "int-1", "mode": "autopilot"}, ...]');
393
+ console.error('');
394
+ console.error('Examples:');
395
+ console.error(' node init-run.js /project login-endpoint user-auth confirm');
396
+ console.error(' node init-run.js /project --batch \'[{"id":"wi-1","intent":"int-1","mode":"autopilot"}]\'');
397
+ }
398
+
290
399
  if (require.main === module) {
291
400
  const args = process.argv.slice(2);
292
401
 
293
- if (args.length < 4) {
294
- console.error('Usage: node init-run.js <rootPath> <workItemId> <intentId> <mode>');
295
- console.error('');
296
- console.error('Arguments:');
297
- console.error(' rootPath - Project root directory');
298
- console.error(' workItemId - Work item ID to execute');
299
- console.error(' intentId - Intent ID containing the work item');
300
- console.error(' mode - Execution mode (autopilot, confirm, validate)');
301
- console.error('');
302
- console.error('Example:');
303
- console.error(' node init-run.js /my/project login-endpoint user-auth confirm');
402
+ if (args.length < 2) {
403
+ printUsage();
304
404
  process.exit(1);
305
405
  }
306
406
 
307
- const [rootPath, workItemId, intentId, mode] = args;
407
+ const rootPath = args[0];
408
+ let workItems = [];
409
+ let scope = null;
410
+
411
+ // Check if batch mode
412
+ if (args[1] === '--batch') {
413
+ if (args.length < 3) {
414
+ console.error('Error: --batch requires a JSON array of work items');
415
+ printUsage();
416
+ process.exit(1);
417
+ }
418
+
419
+ try {
420
+ workItems = JSON.parse(args[2]);
421
+ } catch (err) {
422
+ console.error(`Error: Failed to parse work items JSON: ${err.message}`);
423
+ process.exit(1);
424
+ }
425
+
426
+ // Check for --scope option
427
+ for (let i = 3; i < args.length; i++) {
428
+ if (args[i].startsWith('--scope=')) {
429
+ scope = args[i].substring('--scope='.length);
430
+ }
431
+ }
432
+ } else {
433
+ // Single item mode (backwards compatible)
434
+ if (args.length < 4) {
435
+ printUsage();
436
+ process.exit(1);
437
+ }
438
+
439
+ const [, workItemId, intentId, mode] = args;
440
+ workItems = [{ id: workItemId, intent: intentId, mode: mode }];
441
+ scope = 'single';
442
+ }
308
443
 
309
444
  try {
310
- const result = initRun(rootPath, workItemId, intentId, mode);
445
+ const result = initRun(rootPath, workItems, scope);
311
446
  console.log(JSON.stringify(result, null, 2));
312
447
  process.exit(0);
313
448
  } catch (err) {
@@ -18,6 +18,29 @@ Plan the scope of a run by discovering available work items and suggesting group
18
18
 
19
19
  ---
20
20
 
21
+ ## Critical Clarifications
22
+
23
+ ### Dependencies Mean Sequential Execution, NOT Separate Runs
24
+
25
+ **IMPORTANT**: When work items have dependencies:
26
+ - They execute **sequentially within the SAME run**
27
+ - They do **NOT** require separate runs
28
+ - The dependent item waits for its dependency to complete before starting
29
+
30
+ **Example**: If item 05 depends on item 04:
31
+ - **CORRECT**: ONE run with both items, 04 executes first, then 05
32
+ - **WRONG**: TWO separate runs
33
+
34
+ ### All Options Can Include Multiple Items Per Run
35
+
36
+ | Option | Items Per Run | Execution |
37
+ |--------|---------------|-----------|
38
+ | Single | 1 item | One at a time, separate runs |
39
+ | Batch | Multiple items (same mode) | Sequential within run |
40
+ | Wide | All compatible items | Sequential within run |
41
+
42
+ ---
43
+
21
44
  ## Workflow
22
45
 
23
46
  ```xml
@@ -28,6 +51,7 @@ Plan the scope of a run by discovering available work items and suggesting group
28
51
  SUGGEST smart groupings based on mode, dependencies, and user history.
29
52
  LEARN from user choices to improve future recommendations.
30
53
  NEVER force a scope - always let user choose.
54
+ DEPENDENCIES = SEQUENTIAL EXECUTION, NOT SEPARATE RUNS.
31
55
  </mandate>
32
56
 
33
57
  <step n="1" title="Discover Available Work">
@@ -71,10 +95,10 @@ Plan the scope of a run by discovering available work items and suggesting group
71
95
  <action>Read workspace.run_scope_preference from state.yaml (if exists)</action>
72
96
 
73
97
  <grouping-rules>
74
- <rule>Same mode items CAN be batched together</rule>
75
- <rule>Items with dependencies CANNOT be in same batch</rule>
76
- <rule>Cross-intent batching allowed if items are independent</rule>
77
- <rule>Validate mode items should generally run alone</rule>
98
+ <rule>Dependencies = SEQUENTIAL execution in SAME run (NOT separate runs)</rule>
99
+ <rule>Different modes CAN be in same run (executed sequentially)</rule>
100
+ <rule>Cross-intent items allowed in same run if compatible</rule>
101
+ <rule>Validate mode items may benefit from running alone (more checkpoints)</rule>
78
102
  </grouping-rules>
79
103
 
80
104
  <generate-options>
@@ -84,22 +108,22 @@ Plan the scope of a run by discovering available work items and suggesting group
84
108
  </option>
85
109
 
86
110
  <option name="batch">
87
- Group by mode (autopilot together, confirm together)
88
- Respect dependencies
89
- Total runs: {count of mode groups}
111
+ All pending items in ONE run
112
+ Execute sequentially (dependencies respected by order)
113
+ Checkpoints pause at confirm/validate items
114
+ Total runs: 1
90
115
  </option>
91
116
 
92
117
  <option name="wide">
93
- All compatible items in one run
94
- Only separate if dependencies require it
95
- Total runs: {minimum possible}
118
+ Same as batch - all items in one run
119
+ Total runs: 1
96
120
  </option>
97
121
  </generate-options>
98
122
  </step>
99
123
 
100
124
  <step n="4" title="Present Options">
101
125
  <action>Determine recommended option based on:</action>
102
- <substep>autonomy_bias (autonomous→wide, controlled→single)</substep>
126
+ <substep>autonomy_bias (autonomous→batch, controlled→single)</substep>
103
127
  <substep>run_scope_preference (user's historical choice)</substep>
104
128
  <substep>Number of pending items (few items→single is fine)</substep>
105
129
 
@@ -115,20 +139,24 @@ Plan the scope of a run by discovering available work items and suggesting group
115
139
  {/for}
116
140
  {/for}
117
141
 
142
+ {if dependencies exist}
143
+ **Dependencies** (determines execution order):
144
+ - {dependent_item} depends on {dependency_item}
145
+ {/if}
146
+
118
147
  ---
119
148
 
120
149
  **How would you like to execute?**
121
150
 
122
151
  **[1] One at a time** — {single_count} separate runs
123
- Most controlled, review after each
152
+ Most controlled, review after each run
124
153
 
125
- **[2] Batch by mode** — {batch_count} runs {if recommended}(Recommended){/if}
126
- {for each batch}
127
- Run {n}: {item_names} ({mode})
128
- {/for}
154
+ **[2] Sequential chain** — 1 run with {count} items (Recommended)
155
+ Execute in order: {item1} {item2} → ...
156
+ Checkpoints pause at confirm/validate items
129
157
 
130
- **[3] All together** — {wide_count} run(s)
131
- Fastest, minimal interruption
158
+ **[3] All together** — Same as [2]
159
+ 1 run, sequential execution
132
160
 
133
161
  Choose [1/2/3]:
134
162
  </output>
@@ -139,13 +167,9 @@ Plan the scope of a run by discovering available work items and suggesting group
139
167
  <set>run_scope = single</set>
140
168
  <set>work_items_for_run = [first_pending_item]</set>
141
169
  </check>
142
- <check if="response == 2">
170
+ <check if="response == 2 or response == 3">
143
171
  <set>run_scope = batch</set>
144
- <set>work_items_for_run = first_batch_items</set>
145
- </check>
146
- <check if="response == 3">
147
- <set>run_scope = wide</set>
148
- <set>work_items_for_run = all_compatible_items</set>
172
+ <set>work_items_for_run = all_pending_items_in_dependency_order</set>
149
173
  </check>
150
174
  </step>
151
175
 
@@ -170,14 +194,12 @@ Plan the scope of a run by discovering available work items and suggesting group
170
194
  Starting run with {count} work item(s):
171
195
 
172
196
  {for each item in work_items_for_run}
173
- - {item.title} ({item.mode})
197
+ {index}. {item.title} ({item.mode})
174
198
  {/for}
175
199
 
176
- {if run_scope == batch or wide}
177
200
  Items will execute sequentially within this run.
178
201
  {if any item is confirm or validate}
179
- Checkpoints will pause for approval as needed.
180
- {/if}
202
+ Checkpoints will pause for approval at confirm/validate items.
181
203
  {/if}
182
204
 
183
205
  ---
@@ -185,7 +207,7 @@ Plan the scope of a run by discovering available work items and suggesting group
185
207
  Begin execution? [Y/n]
186
208
  </output>
187
209
  <check if="response == y">
188
- <invoke-skill args="work_items_for_run">run-execute</invoke-skill>
210
+ <invoke-skill args="work_items_for_run, run_scope">run-execute</invoke-skill>
189
211
  </check>
190
212
  </step>
191
213
 
@@ -255,14 +277,13 @@ active_run:
255
277
  ```
256
278
  1. Collect all pending items with their modes
257
279
  2. Build dependency graph
258
- 3. For "batch" option:
259
- - Group by mode
260
- - Within each mode group, check dependencies
261
- - Split if dependency exists within group
262
- 4. For "wide" option:
263
- - Start with all items in one group
264
- - Split only where dependencies require
265
- 5. Return groupings with run counts
280
+ 3. Sort items in dependency order (dependencies first)
281
+ 4. For "single" option:
282
+ - Each item is its own run
283
+ 5. For "batch" or "wide" option:
284
+ - ALL items in ONE run
285
+ - Execution order follows dependency graph
286
+ - Checkpoints pause at confirm/validate items
266
287
  ```
267
288
 
268
289
  ---
@@ -274,7 +295,7 @@ IF run_scope_history has 3+ same choices:
274
295
  pre_selected = most_common_choice
275
296
 
276
297
  ELSE IF autonomy_bias == autonomous:
277
- recommended = wide
298
+ recommended = batch (all in one run)
278
299
 
279
300
  ELSE IF autonomy_bias == controlled:
280
301
  recommended = single
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.0.0-dev.55",
3
+ "version": "0.0.0-dev.57",
4
4
  "description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {