agileflow 2.81.0 → 2.82.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.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agileflow?color=brightgreen)](https://www.npmjs.com/package/agileflow)
6
- [![Commands](https://img.shields.io/badge/commands-68-blue)](docs/04-architecture/commands.md)
6
+ [![Commands](https://img.shields.io/badge/commands-71-blue)](docs/04-architecture/commands.md)
7
7
  [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-29-orange)](docs/04-architecture/subagents.md)
8
8
  [![Skills](https://img.shields.io/badge/skills-dynamic-purple)](docs/04-architecture/skills.md)
9
9
 
@@ -65,7 +65,7 @@ AgileFlow combines three proven methodologies:
65
65
 
66
66
  | Component | Count | Description |
67
67
  |-----------|-------|-------------|
68
- | [Commands](docs/04-architecture/commands.md) | 68 | Slash commands for agile workflows |
68
+ | [Commands](docs/04-architecture/commands.md) | 71 | Slash commands for agile workflows |
69
69
  | [Agents/Experts](docs/04-architecture/subagents.md) | 29 | Specialized agents with self-improving knowledge bases |
70
70
  | [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
71
71
 
@@ -76,7 +76,7 @@ AgileFlow combines three proven methodologies:
76
76
  Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
77
77
 
78
78
  ### Reference
79
- - [Commands](docs/04-architecture/commands.md) - All 68 slash commands
79
+ - [Commands](docs/04-architecture/commands.md) - All 71 slash commands
80
80
  - [Agents/Experts](docs/04-architecture/subagents.md) - 29 specialized agents with self-improving knowledge
81
81
  - [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.81.0",
3
+ "version": "2.82.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -129,6 +129,190 @@ function getCoverageReportPath(rootDir) {
129
129
  return 'coverage/coverage-summary.json';
130
130
  }
131
131
 
132
+ // ===== DISCRETION MARKERS =====
133
+ // Semantic conditions wrapped in **...**
134
+ // These are evaluated by the loop to determine completion
135
+
136
+ /**
137
+ * Built-in discretion conditions that can be evaluated programmatically
138
+ * Format: condition key -> evaluation function
139
+ */
140
+ const DISCRETION_CONDITIONS = {
141
+ // Test-related conditions
142
+ 'all tests passing': (rootDir, _ctx) => {
143
+ const testCommand = getTestCommand(rootDir);
144
+ const result = runTests(rootDir, testCommand);
145
+ return {
146
+ passed: result.passed,
147
+ message: result.passed
148
+ ? 'All tests passing'
149
+ : `Tests failing: ${result.output.split('\n').slice(-3).join(' ').substring(0, 100)}`,
150
+ };
151
+ },
152
+
153
+ 'tests pass': (rootDir, _ctx) => {
154
+ const testCommand = getTestCommand(rootDir);
155
+ const result = runTests(rootDir, testCommand);
156
+ return {
157
+ passed: result.passed,
158
+ message: result.passed ? 'Tests pass' : 'Tests failing',
159
+ };
160
+ },
161
+
162
+ // Coverage conditions (requires threshold in context)
163
+ 'coverage above threshold': (rootDir, ctx) => {
164
+ const threshold = ctx.coverageThreshold || 80;
165
+ const result = verifyCoverage(rootDir, threshold);
166
+ return {
167
+ passed: result.passed,
168
+ message: `Coverage: ${result.coverage?.toFixed(1) || 0}% (threshold: ${threshold}%)`,
169
+ };
170
+ },
171
+
172
+ // Lint conditions
173
+ 'no linting errors': (rootDir, _ctx) => {
174
+ try {
175
+ execSync('npm run lint', {
176
+ cwd: rootDir,
177
+ encoding: 'utf8',
178
+ stdio: ['pipe', 'pipe', 'pipe'],
179
+ timeout: 120000,
180
+ });
181
+ return { passed: true, message: 'No linting errors' };
182
+ } catch (e) {
183
+ return { passed: false, message: 'Linting errors found' };
184
+ }
185
+ },
186
+
187
+ // Type checking conditions
188
+ 'no type errors': (rootDir, _ctx) => {
189
+ try {
190
+ execSync('npx tsc --noEmit', {
191
+ cwd: rootDir,
192
+ encoding: 'utf8',
193
+ stdio: ['pipe', 'pipe', 'pipe'],
194
+ timeout: 120000,
195
+ });
196
+ return { passed: true, message: 'No type errors' };
197
+ } catch (e) {
198
+ return { passed: false, message: 'Type errors found' };
199
+ }
200
+ },
201
+
202
+ // Build conditions
203
+ 'build succeeds': (rootDir, _ctx) => {
204
+ try {
205
+ execSync('npm run build', {
206
+ cwd: rootDir,
207
+ encoding: 'utf8',
208
+ stdio: ['pipe', 'pipe', 'pipe'],
209
+ timeout: 300000,
210
+ });
211
+ return { passed: true, message: 'Build succeeds' };
212
+ } catch (e) {
213
+ return { passed: false, message: 'Build failed' };
214
+ }
215
+ },
216
+
217
+ // Screenshot/visual conditions
218
+ 'all screenshots verified': (rootDir, _ctx) => {
219
+ const result = verifyScreenshots(rootDir);
220
+ return {
221
+ passed: result.passed,
222
+ message: result.passed
223
+ ? 'All screenshots verified'
224
+ : `${result.unverified?.length || 0} unverified screenshots`,
225
+ };
226
+ },
227
+
228
+ // AC conditions (checks story acceptance criteria in status.json)
229
+ 'all acceptance criteria verified': (rootDir, ctx) => {
230
+ const storyId = ctx.currentStoryId;
231
+ if (!storyId) {
232
+ return { passed: false, message: 'No story ID in context' };
233
+ }
234
+ const status = getStatus(rootDir);
235
+ const story = status.stories?.[storyId];
236
+ if (!story) {
237
+ return { passed: false, message: `Story ${storyId} not found` };
238
+ }
239
+ // Check if story has AC and if they're marked complete
240
+ const ac = story.acceptance_criteria || story.ac || [];
241
+ if (!Array.isArray(ac) || ac.length === 0) {
242
+ return { passed: true, message: 'No AC defined (assuming complete)' };
243
+ }
244
+ // Check for ac_status field or assume AC are verified if tests pass
245
+ const acStatus = story.ac_status || {};
246
+ const allVerified = ac.every((_, i) => acStatus[i] === 'verified' || acStatus[i] === true);
247
+ return {
248
+ passed: allVerified,
249
+ message: allVerified
250
+ ? 'All AC verified'
251
+ : `${Object.values(acStatus).filter(v => v === 'verified' || v === true).length}/${ac.length} AC verified`,
252
+ };
253
+ },
254
+ };
255
+
256
+ /**
257
+ * Parse discretion condition from string
258
+ * @param {string} condition - e.g., "**all tests passing**" or "**coverage above 80%**"
259
+ * @returns {object} { key, threshold? }
260
+ */
261
+ function parseDiscretionCondition(condition) {
262
+ // Remove ** markers
263
+ const cleaned = condition.replace(/\*\*/g, '').trim().toLowerCase();
264
+
265
+ // Check for threshold patterns like "coverage above 80%"
266
+ const coverageMatch = cleaned.match(/coverage (?:above|>=?) (\d+)%?/);
267
+ if (coverageMatch) {
268
+ return { key: 'coverage above threshold', threshold: parseInt(coverageMatch[1]) };
269
+ }
270
+
271
+ return { key: cleaned };
272
+ }
273
+
274
+ /**
275
+ * Evaluate a discretion condition
276
+ * @param {string} condition - The condition string (with or without ** markers)
277
+ * @param {string} rootDir - Project root
278
+ * @param {object} ctx - Context (currentStoryId, coverageThreshold, etc.)
279
+ * @returns {object} { passed: boolean, message: string }
280
+ */
281
+ function evaluateDiscretionCondition(condition, rootDir, ctx = {}) {
282
+ const parsed = parseDiscretionCondition(condition);
283
+
284
+ // Set threshold in context if parsed from condition
285
+ if (parsed.threshold) {
286
+ ctx.coverageThreshold = parsed.threshold;
287
+ }
288
+
289
+ const evaluator = DISCRETION_CONDITIONS[parsed.key];
290
+ if (!evaluator) {
291
+ return {
292
+ passed: false,
293
+ message: `Unknown condition: "${parsed.key}". Available: ${Object.keys(DISCRETION_CONDITIONS).join(', ')}`,
294
+ };
295
+ }
296
+
297
+ return evaluator(rootDir, ctx);
298
+ }
299
+
300
+ /**
301
+ * Get discretion conditions from metadata
302
+ * @param {string} rootDir
303
+ * @returns {string[]} Array of condition strings
304
+ */
305
+ function getDiscretionConditions(rootDir) {
306
+ const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
307
+ const result = safeReadJSON(metadataPath, { defaultValue: {} });
308
+
309
+ if (result.ok && result.data?.ralph_loop?.conditions) {
310
+ return result.data.ralph_loop.conditions;
311
+ }
312
+
313
+ return [];
314
+ }
315
+
132
316
  // Parse coverage report (Jest/NYC format)
133
317
  function parseCoverageReport(rootDir) {
134
318
  const reportPath = getCoverageReportPath(rootDir);
@@ -351,8 +535,10 @@ function handleLoop(rootDir) {
351
535
  const visualMode = loop.visual_mode || false;
352
536
  const coverageMode = loop.coverage_mode || false;
353
537
  const coverageThreshold = loop.coverage_threshold || 80;
354
- // Visual and Coverage modes require at least 2 iterations for confirmation
355
- const minIterations = visualMode || coverageMode ? 2 : 1;
538
+ const discretionConditions = loop.conditions || getDiscretionConditions(rootDir);
539
+ // Visual, Coverage, and Discretion modes require at least 2 iterations for confirmation
540
+ const hasDiscretionConditions = discretionConditions.length > 0;
541
+ const minIterations = visualMode || coverageMode || hasDiscretionConditions ? 2 : 1;
356
542
 
357
543
  console.log('');
358
544
  console.log(
@@ -361,6 +547,7 @@ function handleLoop(rootDir) {
361
547
  let modeLabel = '';
362
548
  if (visualMode) modeLabel += ' [VISUAL]';
363
549
  if (coverageMode) modeLabel += ` [COVERAGE ≥${coverageThreshold}%]`;
550
+ if (hasDiscretionConditions) modeLabel += ` [${discretionConditions.length} CONDITIONS]`;
364
551
  console.log(
365
552
  `${c.brand}${c.bold} RALPH LOOP - Iteration ${iteration}/${maxIterations}${modeLabel}${c.reset}`
366
553
  );
@@ -368,6 +555,8 @@ function handleLoop(rootDir) {
368
555
  `${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`
369
556
  );
370
557
  console.log('');
558
+ // State Narration: Loop iteration marker
559
+ console.log(`🔄 Iteration ${iteration}/${maxIterations}`);
371
560
 
372
561
  // Check iteration limit
373
562
  if (iteration > maxIterations) {
@@ -392,9 +581,8 @@ function handleLoop(rootDir) {
392
581
  return;
393
582
  }
394
583
 
395
- console.log(
396
- `${c.cyan}Current Story:${c.reset} ${currentStoryId} - ${currentStory.title || 'Untitled'}`
397
- );
584
+ // State Narration: Current position marker
585
+ console.log(`📍 Working on: ${currentStoryId} - ${currentStory.title || 'Untitled'}`);
398
586
  console.log('');
399
587
 
400
588
  // Run tests
@@ -475,11 +663,38 @@ function handleLoop(rootDir) {
475
663
  return;
476
664
  }
477
665
 
666
+ // Evaluate discretion conditions
667
+ let discretionResults = [];
668
+ if (hasDiscretionConditions) {
669
+ console.log('');
670
+ console.log(`${c.blue}Evaluating discretion conditions...${c.reset}`);
671
+ const ctx = { currentStoryId, coverageThreshold };
672
+
673
+ for (const condition of discretionConditions) {
674
+ const result = evaluateDiscretionCondition(condition, rootDir, ctx);
675
+ discretionResults.push({ condition, ...result });
676
+ const marker = result.passed ? `${c.green}✓` : `${c.yellow}⏳`;
677
+ console.log(` ${marker} **${condition.replace(/\*\*/g, '')}**: ${result.message}${c.reset}`);
678
+ }
679
+
680
+ // Track which conditions have been verified
681
+ const allConditionsPassed = discretionResults.every(r => r.passed);
682
+ state.ralph_loop.conditions_verified = allConditionsPassed;
683
+ state.ralph_loop.condition_results = discretionResults.map(r => ({
684
+ condition: r.condition,
685
+ passed: r.passed,
686
+ message: r.message,
687
+ }));
688
+ }
689
+
478
690
  // Check if all verification modes passed
691
+ const allDiscretionPassed =
692
+ !hasDiscretionConditions || discretionResults.every(r => r.passed);
479
693
  const canComplete =
480
694
  testResult.passed &&
481
695
  (!visualMode || screenshotResult.passed) &&
482
- (!coverageMode || coverageResult.passed);
696
+ (!coverageMode || coverageResult.passed) &&
697
+ allDiscretionPassed;
483
698
 
484
699
  if (!canComplete) {
485
700
  // Something not verified yet
@@ -499,12 +714,21 @@ function handleLoop(rootDir) {
499
714
  console.log(`${c.dim} Target: ${coverageThreshold}%${c.reset}`);
500
715
  console.log(`${c.dim} Write more tests to cover uncovered code paths.${c.reset}`);
501
716
  }
717
+ if (hasDiscretionConditions && !allDiscretionPassed) {
718
+ const failedConditions = discretionResults.filter(r => !r.passed);
719
+ console.log(`${c.cyan}▶ Fix failing conditions:${c.reset}`);
720
+ for (const fc of failedConditions) {
721
+ console.log(`${c.dim} - ${fc.condition.replace(/\*\*/g, '')}: ${fc.message}${c.reset}`);
722
+ }
723
+ }
502
724
  return;
503
725
  }
504
726
  console.log('');
505
727
 
506
728
  // Mark story complete
507
729
  markStoryComplete(rootDir, currentStoryId);
730
+ // State Narration: Completion marker
731
+ console.log(`✅ Story complete: ${currentStoryId}`);
508
732
  console.log(`${c.green}✓ Marked ${currentStoryId} as completed${c.reset}`);
509
733
 
510
734
  // Get next story
@@ -558,6 +782,8 @@ function handleLoop(rootDir) {
558
782
  }
559
783
  } else {
560
784
  // Tests failed - feed back to Claude
785
+ // State Narration: Error marker
786
+ console.log(`⚠️ Error: Test failure - ${(testResult.duration / 1000).toFixed(1)}s`);
561
787
  console.log(`${c.red}✗ Tests failed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
562
788
  console.log('');
563
789
 
@@ -597,6 +823,8 @@ function handleCLI() {
597
823
  if (loop.visual_mode) modeLabel += ` ${c.cyan}[VISUAL]${c.reset}`;
598
824
  if (loop.coverage_mode)
599
825
  modeLabel += ` ${c.magenta}[COVERAGE ≥${loop.coverage_threshold}%]${c.reset}`;
826
+ if (loop.conditions?.length > 0)
827
+ modeLabel += ` ${c.blue}[${loop.conditions.length} CONDITIONS]${c.reset}`;
600
828
  console.log(`${c.green}Ralph Loop: active${c.reset}${modeLabel}`);
601
829
  console.log(` Epic: ${loop.epic}`);
602
830
  console.log(` Current Story: ${loop.current_story}`);
@@ -616,6 +844,16 @@ function handleCLI() {
616
844
  );
617
845
  console.log(` Baseline: ${(loop.coverage_baseline || 0).toFixed(1)}%`);
618
846
  }
847
+ if (loop.conditions?.length > 0) {
848
+ const verified = loop.conditions_verified
849
+ ? `${c.green}yes${c.reset}`
850
+ : `${c.yellow}no${c.reset}`;
851
+ console.log(` Discretion Conditions: ${loop.conditions.length} (All Verified: ${verified})`);
852
+ for (const result of loop.condition_results || []) {
853
+ const mark = result.passed ? `${c.green}✓${c.reset}` : `${c.yellow}⏳${c.reset}`;
854
+ console.log(` ${mark} ${result.condition.replace(/\*\*/g, '')}`);
855
+ }
856
+ }
619
857
  }
620
858
  return true;
621
859
  }
@@ -647,6 +885,9 @@ function handleCLI() {
647
885
  const maxArg = args.find(a => a.startsWith('--max='));
648
886
  const visualArg = args.includes('--visual') || args.includes('-v');
649
887
  const coverageArg = args.find(a => a.startsWith('--coverage='));
888
+ // Parse conditions (--condition="**all tests passing**" or -c "...")
889
+ const conditionArgs = args.filter(a => a.startsWith('--condition=') || a.startsWith('-c='));
890
+ const conditions = conditionArgs.map(a => a.split('=').slice(1).join('=').replace(/"/g, ''));
650
891
 
651
892
  if (!epicArg) {
652
893
  console.log(`${c.red}Error: --epic=EP-XXXX is required${c.reset}`);
@@ -711,6 +952,10 @@ function handleCLI() {
711
952
  }
712
953
  }
713
954
 
955
+ // Get conditions from metadata if not provided via CLI
956
+ const allConditions =
957
+ conditions.length > 0 ? conditions : getDiscretionConditions(rootDir);
958
+
714
959
  // Initialize loop state
715
960
  const state = getSessionState(rootDir);
716
961
  state.ralph_loop = {
@@ -726,6 +971,9 @@ function handleCLI() {
726
971
  coverage_baseline: coverageBaseline,
727
972
  coverage_current: coverageBaseline,
728
973
  coverage_verified: false,
974
+ conditions: allConditions,
975
+ conditions_verified: false,
976
+ condition_results: [],
729
977
  started_at: new Date().toISOString(),
730
978
  };
731
979
  saveSessionState(rootDir, state);
@@ -750,7 +998,13 @@ function handleCLI() {
750
998
  );
751
999
  console.log(` Baseline: ${coverageBaseline.toFixed(1)}%`);
752
1000
  }
753
- if (visualMode || coverageMode) {
1001
+ if (allConditions.length > 0) {
1002
+ console.log(` Conditions: ${c.blue}${allConditions.length} discretion conditions${c.reset}`);
1003
+ for (const cond of allConditions) {
1004
+ console.log(` - **${cond.replace(/\*\*/g, '')}**`);
1005
+ }
1006
+ }
1007
+ if (visualMode || coverageMode || allConditions.length > 0) {
754
1008
  console.log(` Min Iterations: 2 (for confirmation)`);
755
1009
  }
756
1010
  console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
@@ -783,15 +1037,17 @@ ${c.bold}Usage:${c.reset}
783
1037
  node scripts/ralph-loop.js --init --epic=EP-XXX Initialize loop for epic
784
1038
  node scripts/ralph-loop.js --init --epic=EP-XXX --visual Initialize with Visual Mode
785
1039
  node scripts/ralph-loop.js --init --epic=EP-XXX --coverage=80 Initialize with Coverage Mode
1040
+ node scripts/ralph-loop.js --init --epic=EP-XXX --condition="**all tests passing**"
786
1041
  node scripts/ralph-loop.js --status Check loop status
787
1042
  node scripts/ralph-loop.js --stop Stop the loop
788
1043
  node scripts/ralph-loop.js --reset Reset loop state
789
1044
 
790
1045
  ${c.bold}Options:${c.reset}
791
- --epic=EP-XXXX Epic ID to process (required for --init)
792
- --max=N Max iterations (default: 20)
793
- --visual, -v Enable Visual Mode (screenshot verification)
794
- --coverage=N Enable Coverage Mode (iterate until N% coverage)
1046
+ --epic=EP-XXXX Epic ID to process (required for --init)
1047
+ --max=N Max iterations (default: 20)
1048
+ --visual, -v Enable Visual Mode (screenshot verification)
1049
+ --coverage=N Enable Coverage Mode (iterate until N% coverage)
1050
+ --condition="..." Add discretion condition (can use multiple times)
795
1051
 
796
1052
  ${c.bold}Visual Mode:${c.reset}
797
1053
  When --visual is enabled, the loop also verifies that all screenshots
@@ -814,6 +1070,30 @@ ${c.bold}Coverage Mode:${c.reset}
814
1070
  3. Minimum 2 iterations → confirms coverage is stable
815
1071
  4. Only then → story marked complete
816
1072
 
1073
+ ${c.bold}Discretion Conditions:${c.reset}
1074
+ Semantic conditions that must pass before story completion.
1075
+ Use --condition multiple times for multiple conditions.
1076
+
1077
+ Built-in conditions:
1078
+ **all tests passing** Tests must pass
1079
+ **tests pass** Tests must pass (alias)
1080
+ **coverage above 80%** Coverage must meet threshold
1081
+ **no linting errors** npm run lint must pass
1082
+ **no type errors** npx tsc --noEmit must pass
1083
+ **build succeeds** npm run build must pass
1084
+ **all screenshots verified** Screenshots need verified- prefix
1085
+ **all acceptance criteria verified** AC marked complete in status.json
1086
+
1087
+ Configure in docs/00-meta/agileflow-metadata.json:
1088
+ {
1089
+ "ralph_loop": {
1090
+ "conditions": [
1091
+ "**all tests passing**",
1092
+ "**no linting errors**"
1093
+ ]
1094
+ }
1095
+ }
1096
+
817
1097
  ${c.bold}How it works:${c.reset}
818
1098
  1. Start loop with /agileflow:babysit EPIC=EP-XXX MODE=loop COVERAGE=80
819
1099
  2. Work on the current story
@@ -48,6 +48,22 @@ RULE #3: DEPENDENCY DETECTION
48
48
  | Same domain, different experts | PARALLEL | Security + Performance analyzing same code |
49
49
  | Best-of-N comparison | PARALLEL | Expert1 vs Expert2 vs Expert3 approaches |
50
50
 
51
+ RULE #3b: JOIN STRATEGIES (for parallel deployment)
52
+ | Strategy | When | Behavior |
53
+ |----------|------|----------|
54
+ | `all` | Full implementation | Wait for all, fail if any fails |
55
+ | `first` | Racing approaches | Take first completion |
56
+ | `any` | Fallback patterns | Take first success |
57
+ | `any-N` | Multiple perspectives | Take first N successes |
58
+ | `majority` | High-stakes decisions | Take consensus (2+ agree) |
59
+
60
+ RULE #3c: FAILURE POLICIES
61
+ | Policy | When | Behavior |
62
+ |--------|------|----------|
63
+ | `fail-fast` | Critical work (default) | Stop on first failure |
64
+ | `continue` | Analysis/review | Run all, report failures |
65
+ | `ignore` | Optional enrichments | Skip failures silently |
66
+
51
67
  RULE #4: SYNTHESIS REQUIREMENTS
52
68
  - NEVER give final answer without all expert results
53
69
  - Flag conflicts explicitly: "Expert A recommends X (rationale: ...), Expert B recommends Y (rationale: ...)"
@@ -238,11 +254,114 @@ TaskOutput(task_id: "<ui_expert_id>", block: true)
238
254
 
239
255
  ---
240
256
 
257
+ ## JOIN STRATEGIES
258
+
259
+ When spawning parallel experts, specify how to handle results:
260
+
261
+ | Strategy | Behavior | Use Case |
262
+ |----------|----------|----------|
263
+ | `all` | Wait for all, fail if any fails | Full feature implementation |
264
+ | `first` | Take first result, cancel others | Racing alternative approaches |
265
+ | `any` | Take first success, ignore failures | Fallback patterns |
266
+ | `any-N` | Take first N successes | Get multiple perspectives |
267
+ | `majority` | Take consensus result | High-stakes decisions |
268
+
269
+ ### Failure Policies
270
+
271
+ Combine with strategies to handle errors gracefully:
272
+
273
+ | Policy | Behavior | Use Case |
274
+ |--------|----------|----------|
275
+ | `fail-fast` | Stop all on first failure (default) | Critical operations |
276
+ | `continue` | Run all to completion, report failures | Comprehensive analysis |
277
+ | `ignore` | Skip failed branches silently | Optional enrichments |
278
+
279
+ **Usage:**
280
+ ```
281
+ Deploy parallel (strategy: all, on-fail: continue):
282
+ - agileflow-security (may fail if no vulnerabilities)
283
+ - agileflow-performance (may fail if no issues)
284
+ - agileflow-testing
285
+
286
+ Run all to completion. Report any failures at end.
287
+ ```
288
+
289
+ **When to use each policy:**
290
+
291
+ | Scenario | Recommended Policy |
292
+ |----------|-------------------|
293
+ | Implementation work | `fail-fast` (need all parts) |
294
+ | Code review/analysis | `continue` (want all perspectives) |
295
+ | Optional enrichments | `ignore` (nice-to-have) |
296
+
297
+ ### Strategy: all (Default)
298
+
299
+ Wait for all experts to complete. Report all results in synthesis.
300
+
301
+ ```
302
+ Deploy parallel (strategy: all):
303
+ - agileflow-api (endpoint)
304
+ - agileflow-ui (component)
305
+
306
+ Collect ALL results before synthesizing.
307
+ If ANY expert fails → report failure with details.
308
+ ```
309
+
310
+ ### Strategy: first
311
+
312
+ Take the first expert that completes. Useful for racing approaches.
313
+
314
+ ```
315
+ Deploy parallel (strategy: first):
316
+ - Expert A (approach: caching)
317
+ - Expert B (approach: pagination)
318
+ - Expert C (approach: batching)
319
+
320
+ First to complete wins → use that approach.
321
+ Cancel/ignore other results.
322
+
323
+ Use case: Finding ANY working solution when multiple approaches are valid.
324
+ ```
325
+
326
+ ### Strategy: any
327
+
328
+ Take first successful result. Ignore failures. Useful for fallbacks.
329
+
330
+ ```
331
+ Deploy parallel (strategy: any):
332
+ - Expert A (primary approach)
333
+ - Expert B (fallback approach)
334
+
335
+ First SUCCESS wins → use that result.
336
+ If A fails but B succeeds → use B.
337
+ If all fail → report all failures.
338
+
339
+ Use case: Resilient operations where any working solution is acceptable.
340
+ ```
341
+
342
+ ### Strategy: majority
343
+
344
+ Multiple experts analyze same thing. Take consensus.
345
+
346
+ ```
347
+ Deploy parallel (strategy: majority):
348
+ - Security Expert 1
349
+ - Security Expert 2
350
+ - Security Expert 3
351
+
352
+ If 2+ agree → use consensus recommendation.
353
+ If no consensus → report conflict, request decision.
354
+
355
+ Use case: High-stakes security reviews, architecture decisions.
356
+ ```
357
+
358
+ ---
359
+
241
360
  ## PARALLEL PATTERNS
242
361
 
243
362
  ### Full-Stack Feature
244
363
  ```
245
- Parallel:
364
+ Parallel (strategy: all):
246
365
  - agileflow-api (endpoint)
247
366
  - agileflow-ui (component)
248
367
  Then:
@@ -251,22 +370,32 @@ Then:
251
370
 
252
371
  ### Code Review/Analysis
253
372
  ```
254
- Parallel (analyze same code):
373
+ Parallel (strategy: all):
255
374
  - agileflow-security
256
375
  - agileflow-performance
257
376
  - agileflow-testing
258
377
  Then:
259
- - Synthesize findings
378
+ - Synthesize all findings
260
379
  ```
261
380
 
262
- ### Best-of-N
381
+ ### Best-of-N (Racing)
263
382
  ```
264
- Parallel (same task, different approaches):
383
+ Parallel (strategy: first):
265
384
  - Expert A (approach 1)
266
385
  - Expert B (approach 2)
267
386
  - Expert C (approach 3)
268
387
  Then:
269
- - Compare and select best
388
+ - Use first completion
389
+ ```
390
+
391
+ ### Consensus Decision
392
+ ```
393
+ Parallel (strategy: majority):
394
+ - Security Expert 1
395
+ - Security Expert 2
396
+ - Security Expert 3
397
+ Then:
398
+ - Take consensus recommendation
270
399
  ```
271
400
 
272
401
  ---