prodigy 0.4.4

Turn ad-hoc Claude sessions into reproducible development pipelines with parallel AI agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# Command Types

## 1. Shell Commands

```yaml
# Simple shell command
- shell: "cargo test"

# With output capture
- shell: "ls -la | wc -l"
  capture_output: "file_count"

# With failure handling
- shell: "cargo clippy"
  on_failure:
    claude: "/fix-warnings ${shell.output}"

# With nested failure handlers (multi-level error recovery)
- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    on_failure:
      shell: "notify-team.sh 'Tests failed and debugging unsuccessful'"

# With timeout
- shell: "cargo bench"
  timeout: 600  # seconds

# With conditional execution
- shell: "cargo build --release"
  when: "${tests_passed}"

# Working directory control (use shell cd command)
- shell: "cd crates/prodigy-core && cargo test"

# Environment variables (use shell syntax)
- shell: "PATH=/custom/bin:$PATH rustfmt --check src/**/*.rs"
```

## 2. Claude Commands

```yaml
# Simple Claude command
- claude: "/prodigy-analyze"

# With arguments
- claude: "/prodigy-implement-spec ${spec_file}"

# With commit requirement
- claude: "/prodigy-fix-bugs"
  commit_required: true

# With output capture
- claude: "/prodigy-generate-plan"
  capture_output: "implementation_plan"
```

## 3. Foreach Commands

Iterate over a list with optional parallelism.

```yaml
- foreach:
    input: "find . -name '*.rs' -type f"  # Command
    # OR
    # input: ["file1.rs", "file2.rs"]    # List

    parallel: 5  # Number of parallel executions (or true/false)

    do:
      - claude: "/analyze-file ${item}"
      - shell: "cargo check ${item}"

    continue_on_error: true
    max_items: 50
```

**Variables and Error Handling:**
- **${item}**: Current item value available in loop body
- **continue_on_error: true** (default): Failed items don't stop the loop
- **Parallel execution caveat**: Output order is not guaranteed when using `parallel`
- **No built-in result aggregation**: Use `write_file` commands to collect results if needed

**Example with result collection:**
```yaml
- foreach:
    input: ["module1", "module2", "module3"]
    parallel: 3
    do:
      - shell: "cargo test --package ${item}"
      - write_file:
          path: "results/${item}.txt"
          content: "Test result: ${shell.output}"
          create_dirs: true
```

## 4. Write File Commands

Create or overwrite files with content from variables or literals. Supports text, JSON, and YAML formats with automatic validation and formatting.

```yaml
# Write plain text file
- write_file:
    path: "output/result.txt"
    content: "Build completed at ${shell.output}"
    format: text
    mode: "0644"
    create_dirs: true

# Write JSON file with validation
- write_file:
    path: "config/generated.json"
    content: |
      {
        "version": "${version}",
        "timestamp": "${timestamp}",
        "items": ${items_json}
      }
    format: json

# Write YAML file with formatting
- write_file:
    path: ".prodigy/metadata.yml"
    content: |
      workflow: ${workflow.name}
      iteration: ${workflow.iteration}
      results:
        success: ${map.successful}
        total: ${map.total}
    format: yaml
```

**WriteFileConfig Fields:**
- `path` - File path to write (supports variable interpolation)
- `content` - Content to write (supports variable interpolation)
- `format` - Output format: `text` (default), `json`, `yaml`
- `mode` - File permissions in octal (default: "0644")
- `create_dirs` - Create parent directories if they don't exist (default: false)

**Format Validation:**
- `json` - Validates JSON syntax and pretty-prints output
- `yaml` - Validates YAML syntax and formats output
- `text` - Writes content as-is without validation

**Best Practices:**
- **Use format validation for config files**: Set `format: json` or `format: yaml` when generating configuration files to catch syntax errors early
- **Set appropriate permissions**: Use `mode` field to control file permissions (e.g., `"0600"` for sensitive files)
- **Handle nested paths**: Set `create_dirs: true` when writing to paths that may not exist
- **Combine with validation**: Use `validate` field to ensure generated files meet requirements before proceeding
- **For logs and documentation**: Use `format: text` to write content as-is without validation overhead

## 5. Validation Commands

Validate implementation completeness with automatic retry.

> **Warning:** The `command` field in ValidationConfig is deprecated. Use `shell` instead for shell commands or `claude` for Claude commands. The `command` field is still supported for backward compatibility but will be removed in a future version.

```yaml
- claude: "/implement-auth-spec"
  validate:
    shell: "debtmap validate --spec auth.md --output result.json"
    result_file: "result.json"
    threshold: 95  # Percentage completion required (default: 100.0)
    timeout: 60
    expected_schema: "validation-schema.json"  # Optional JSON schema

    # What to do if incomplete
    on_incomplete:
      claude: "/complete-implementation ${validation.gaps}"
      max_attempts: 3
      fail_workflow: true
      commit_required: true
      prompt: "Implementation incomplete. Continue?"  # Optional interactive prompt
```

**ValidationConfig Fields:**
- `shell` or `claude` - Single validation command (use `shell`, not deprecated `command`)
- `commands` - Array of commands for multi-step validation
- `result_file` - Path to JSON file with validation results
- `threshold` - Minimum completion percentage (default: 100.0)
- `timeout` - Timeout in seconds
- `expected_schema` - JSON schema for validation output structure

**OnIncompleteConfig Fields:**
- `shell` or `claude` - Single gap-filling command
- `commands` - Array of commands for multi-step gap filling
- `max_attempts` - Maximum retry attempts (default: 2)
- `fail_workflow` - Whether to fail workflow if validation incomplete (default: true)
- `commit_required` - Whether to require commit after gap filling (default: false)
- `prompt` - Optional interactive prompt for user guidance

**Alternative: Array format for multi-step validation**

```yaml
- claude: "/implement-feature"
  validate:
    # When using array format, ValidationConfig uses default threshold (100.0)
    # and creates a commands array
    - shell: "run-tests.sh"
    - shell: "check-coverage.sh"
    - claude: "/validate-implementation --output validation.json"
      result_file: "validation.json"
```

**Alternative: Multi-step gap filling**

```yaml
- claude: "/implement-feature"
  validate:
    shell: "validate.sh"
    result_file: "result.json"
    on_incomplete:
      commands:
        - claude: "/analyze-gaps ${validation.gaps}"
        - shell: "run-fix-script.sh"
        - claude: "/verify-fixes"
      max_attempts: 2
```

---

## Command Reference

### Command Fields

All command types support these common fields:

**Source**: src/config/command.rs:320-401

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique identifier for referencing outputs |
| `timeout` | number | Command timeout in seconds |
| `commit_required` | boolean | Whether command should create a git commit |
| `when` | string | Conditional execution expression |
| `capture_output` | boolean/string | Capture command output to a variable (boolean for default "output" var, string for custom name) |
| `capture_format` | enum | Output parsing format: `string` (default), `number`, `json`, `lines`, `boolean` |
| `on_success` | object | Command to run on success |
| `on_failure` | object | Error handling configuration (see OnFailure Handler section below) |
| `validate` | object | Validation configuration for implementation completeness |
| `output_file` | string | Redirect command output to a file path |

**Note on Field Availability**: All fields in this table are available in the user-facing YAML WorkflowStepCommand format (src/config/command.rs:320-401). Additional fields like `working_dir`, `env`, `on_exit_code`, and `capture` exist in the internal WorkflowStep representation (src/cook/workflow/executor/data_structures.rs:35-157) but are NOT exposed in YAML. See the Technical Notes section below for workarounds.

### OnFailure Handler Configuration

The `on_failure` field accepts an `OnFailureConfig` which supports multiple configuration formats:

**Source**: src/cook/workflow/on_failure.rs:67-115

**Simple Format - Single Command:**
```yaml
- shell: "cargo clippy"
  on_failure:
    claude: "/fix-warnings ${shell.output}"
```

**Advanced Format - With Retry Control:**
```yaml
- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    max_retries: 3              # Maximum retry attempts (default: 1)
    fail_workflow: false        # Continue workflow even after failure handling
    retry_original: true        # Retry original command after handler (default: false)
```

**Detailed Format - With Handler Strategy:**

For fine-grained control over failure handling behavior, use the Detailed format with `FailureHandlerConfig` (src/cook/workflow/on_failure.rs:9-49):

```yaml
- shell: "cargo build"
  on_failure:
    commands:
      - claude: "/analyze-build-errors ${shell.output}"
      - shell: "cargo clean && cargo build"
    strategy: recovery          # recovery, fallback, cleanup, or custom
    timeout: 300                # Handler timeout in seconds
    fail_workflow: false        # Whether to fail workflow after handling
    handler_failure_fatal: true # Whether handler failure should be fatal
```

**Handler Strategies** (src/cook/workflow/on_failure.rs:9-22):
- `recovery` (default) - Attempt to fix the problem and retry the original operation. Use this when failures are transient or can be resolved programmatically.
- `fallback` - Use an alternative approach when the primary method fails. Best for scenarios with multiple valid execution paths.
- `cleanup` - Perform resource cleanup before failing. Use for releasing locks, closing connections, or removing temporary files.
- `custom` - Custom handler logic for specialized error recovery patterns not covered by other strategies.

**Real-World Examples:**

From `workflows/coverage.yml` (lines 13-17):
```yaml
- shell: "just test"
  on_failure:
    claude: "/prodigy-debug-test-failure --spec ${coverage.spec} --output ${shell.output}"
    max_attempts: 3
    fail_workflow: false
```

From `workflows/implement.yml` (lines 20-30):
```yaml
- shell: "just test"
  on_failure:
    claude: "/prodigy-debug-test-failure --spec $ARG --output ${shell.output}"
    max_attempts: 5
    fail_workflow: false
```

**Nested Failure Handlers:**
```yaml
- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    on_failure:
      shell: "notify-team.sh 'Tests failed and debugging unsuccessful'"
```

### Planned Feature: CaptureStreams

> **Note:** The `capture_streams` field is defined in WorkflowStepCommand (src/config/command.rs:394-396) but is **reserved for future use** and not yet functional in YAML workflows.
>
> **Why it's not available yet**: The field exists as a `String` placeholder in the YAML parser, but the execution engine doesn't yet support fine-grained stream capture control. This feature is planned to provide selective capture of stdout, stderr, exit codes, success status, and execution duration.

**Current Approach:** Use the `capture_output` and `capture_format` fields to control output capture, which cover most common use cases:

```yaml
# Capture stdout as string (most common use case)
- shell: "cargo test"
  capture_output: "test_output"
  capture_format: "string"

# Capture exit status as boolean
- shell: "cargo test"
  capture_output: "test_passed"
  capture_format: "boolean"

# Capture and parse JSON output
- shell: "cargo metadata --format-version 1"
  capture_output: "project_info"
  capture_format: "json"
```

**When will this be available?** This feature requires execution engine changes to support the internal `CaptureStreams` type (src/cook/workflow/executor/data_structures.rs:83). Until then, use the `capture_output` and `capture_format` fields which cover most common use cases.

### Capture Format Examples

The `capture_format` field controls how captured output is parsed:

```yaml
# String format (default) - raw text output
- shell: "git rev-parse HEAD"
  capture: "commit_hash"
  capture_format: "string"

# Number format - parses numeric output
- shell: "wc -l < file.txt"
  capture: "line_count"
  capture_format: "number"

# JSON format - parses JSON output
- shell: "cargo metadata --format-version 1"
  capture: "project_metadata"
  capture_format: "json"

# Lines format - splits output into array of lines
- shell: "git diff --name-only"
  capture: "changed_files"
  capture_format: "lines"

# Boolean format - true if command succeeds, false otherwise
- shell: "grep -q 'pattern' file.txt"
  capture: "pattern_found"
  capture_format: "boolean"
```

### Deprecated Fields

> **Warning:** The following fields are deprecated but still supported for backward compatibility. They will be removed in a future version. Please migrate to the recommended alternatives.

These fields are deprecated:

- `test:` - **Use `shell:` with `on_failure:` instead**
- `command:` in ValidationConfig - **Use `shell:` instead**
- Nested `commands:` in `agent_template` and `reduce` - **Use direct array format instead**
- Legacy variable aliases (`$ARG`, `$ARGUMENT`, `$FILE`, `$FILE_PATH`) - **Use modern `${item.*}` syntax**

**Using capture_output Field**

The `capture_output` field supports both Boolean and String variants for backward compatibility:

**Source**: src/config/command.rs:366-368 (CaptureOutputConfig enum: src/config/command.rs:403-411)

```yaml
# String variant - captures to named variable (recommended)
- shell: "git rev-parse HEAD"
  capture_output: "commit_hash"

# Boolean variant - captures to default "output" variable
- shell: "ls -la | wc -l"
  capture_output: true
```

**Migration Guide - capture_output Variants:**

The `capture_output` field accepts both boolean and string values for backward compatibility:

| Syntax | Variable Name | When to Use |
|--------|---------------|-------------|
| `capture_output: true` | `${output}` | Quick capture to default variable |
| `capture_output: "myvar"` | `${myvar}` | Recommended: explicit variable name |

**Before and After Examples:**

```yaml
# Boolean variant - captures to ${output}
# BEFORE:
- shell: "git rev-parse HEAD"
  capture_output: true
# Access with: ${output}

# AFTER (recommended):
- shell: "git rev-parse HEAD"
  capture_output: "commit_hash"
# Access with: ${commit_hash}

# String variant - already correct
- shell: "ls -la | wc -l"
  capture_output: "file_count"
# Access with: ${file_count}
```

**Note**: The internal WorkflowStep struct (src/cook/workflow/executor/data_structures.rs:75) uses a `capture` field internally, but this is NOT exposed in the user-facing YAML WorkflowStepCommand struct. Always use `capture_output` for capturing command output in YAML workflows.

You can then reference captured values using `${commit_hash}` or `${output}` in subsequent commands.

---

## Technical Notes

<details>
<summary>Internal Implementation Fields (for contributors)</summary>

The following fields exist in the internal WorkflowStep struct (src/cook/workflow/executor/data_structures.rs:35-157) but are NOT available in the user-facing WorkflowStepCommand YAML syntax (src/config/command.rs:320-401). These are implementation details managed by Prodigy's execution engine during workflow execution:

**Execution Control Fields (Internal Only):**
- `handler` - Internal HandlerStep for execution routing
- `retry` - Internal RetryConfig for automatic retry logic
- `auto_commit` - Internal commit tracking
- `commit_config` - Internal commit configuration
- `step_validate` - Internal validation state
- `skip_validation` - Internal validation control
- `validation_timeout` - Internal validation timing
- `ignore_validation_failure` - Internal validation handling

**Environment and Context Fields (Planned/Not Yet Exposed in YAML):**
- `working_dir` - Working directory control (WorkflowStep:99, NOT in WorkflowStepCommand)
- `env` - Environment variable overrides (WorkflowStep:103, NOT in WorkflowStepCommand)
- `on_exit_code` - Exit code specific handlers (WorkflowStep:119, NOT in WorkflowStepCommand)
- `capture` - Variable name for output (WorkflowStep:75, use `capture_output` in YAML instead)

**Why the separation?** WorkflowStepCommand defines what users can write in YAML. WorkflowStep is the internal runtime representation with additional fields populated during execution. Some features exist in the execution layer but aren't yet exposed in the YAML syntax.

**Workarounds for missing YAML fields:**

Since `cwd`, `env`, and `on_exit_code` are NOT available in WorkflowStepCommand, use these shell-level alternatives:

```yaml
# Instead of cwd field (NOT available):
# ❌ - shell: "cargo test"
#      cwd: "crates/prodigy-core"

# ✓ Use shell cd command:
- shell: "cd crates/prodigy-core && cargo test"

# Instead of env field (NOT available):
# ❌ - shell: "rustfmt --check src/**/*.rs"
#      env:
#        PATH: "/custom/bin:${PATH}"

# ✓ Use shell environment syntax:
- shell: "PATH=/custom/bin:$PATH rustfmt --check src/**/*.rs"

# Instead of on_exit_code field (NOT available):
# ❌ - shell: "cargo build"
#      on_exit_code:
#        1:
#          claude: "/fix-compile-errors"

# ✓ Use on_failure with conditional logic:
- shell: "cargo build"
  on_failure:
    claude: "/fix-compile-errors ${shell.output}"
```

These planned features may be exposed in future versions of Prodigy.

</details>

---

## Cross-References

For more information on related topics:
- **Variable Interpolation**: See the [Variables chapter](./variables/custom-variable-capture.md) for details on using captured outputs like `${variable_name}` in subsequent commands
- **Environment Variables**: See the [Environment Variables chapter](./environment/index.md) for global env, secrets, and profiles
- **Error Handling**: See the [Error Handling chapter](./workflow-basics/error-handling.md#command-level-error-handling) for advanced `on_failure` strategies and retry patterns
- **MapReduce Workflows**: See the [MapReduce chapter](./mapreduce/index.md#complete-structure) for large-scale parallel command execution with agent templates

**Example: Using Captured Output in Subsequent Commands**

```yaml
# Capture build output and use it in later commands
- shell: "cargo build --release 2>&1"
  capture: "build_output"
  capture_format: "string"

# Use the captured output in Claude command
- claude: "/analyze-warnings '${build_output}'"
  when: "${build_output contains 'warning'}"

# Store output to file for later analysis
- write_file:
    path: "logs/build-${workflow.iteration}.log"
    content: "${build_output}"
    create_dirs: true
```