Available Variables
Available Variables¶
Prodigy provides a comprehensive set of built-in variables that are automatically available based on your workflow context. All variables use the ${variable.name} interpolation syntax.
Standard Variables¶
These variables capture output from the most recently executed command:
| Variable | Description | Example |
|---|---|---|
${last.output} |
Output from the last command of any type (shell, claude, handler) | echo ${last.output} |
${last.exit_code} |
Exit code from the last command | if [ ${last.exit_code} -eq 0 ] |
${shell.output} |
Output from the last shell command specifically | echo ${shell.output} |
${claude.output} |
Output from the last Claude command specifically | echo ${claude.output} |
Note: Use ${last.output} when you need output from any command type. Use ${shell.output} or ${claude.output} when you specifically want output from that command type.
Example:
- shell: "cargo test --lib"
- shell: "echo 'Test output: ${shell.output}'"
# last.output works with any command type
- claude: "/analyze-code"
- shell: "echo 'Claude analysis: ${last.output}'"
Computed Variables¶
Computed variables are dynamically evaluated at runtime, providing access to external data sources and generated values. These variables are prefixed with specific identifiers that trigger their evaluation.
Source: src/cook/execution/variables.rs:100-305
| Variable Type | Syntax | Description | Cached | Example |
|---|---|---|---|---|
| Environment | ${env.VAR_NAME} |
Read environment variable | Yes | ${env.HOME}, ${env.PATH} |
| File Content | ${file:path/to/file} |
Read file contents | Yes | ${file:config.txt}, ${file:data.json} |
| Command Output | ${cmd:shell-command} |
Execute command and capture output | Yes | ${cmd:git rev-parse HEAD}, ${cmd:date +%Y} |
| JSON Path | ${json:path:from:source_var} |
Extract from JSON using JSONPath | No | ${json:$.items[0].name:from:data} |
| Date Format | ${date:format} |
Current date/time with format | No | ${date:%Y-%m-%d}, ${date:%H:%M:%S} |
| UUID | ${uuid} |
Generate random UUID v4 | No | ${uuid} (always unique) |
Available in: All phases
Environment Variables (env.*)¶
Access environment variables at runtime. Useful for reading system configuration or secrets.
Source: src/cook/execution/variables.rs:160-187
Examples:
# Read user's home directory
- shell: "echo 'Home: ${env.HOME}'"
# Use CI environment variables
- shell: "echo 'Running in ${env.CI_PROVIDER:-local}'"
# Access secrets from environment
- shell: "curl -H 'Authorization: Bearer ${env.API_TOKEN}' https://api.example.com"
Caching: Environment variable reads are cached for performance (LRU cache, 100 entries).
File Content (file:)¶
Read file contents directly into variables. Useful for configuration, templates, or data files.
Source: src/cook/execution/variables.rs:189-216
Examples:
# Read version from file
- shell: "echo 'Version: ${file:VERSION}'"
# Use file content in command
- shell: "git commit -m '${file:.commit-message.txt}'"
# Read JSON configuration
- shell: "echo '${file:config.json}' | jq '.database.host'"
Caching: File reads are cached (file content is expensive to read repeatedly).
Note: File paths are relative to workflow execution directory.
Command Output (cmd:)¶
Execute shell commands and capture their output as variable values. Powerful for dynamic configuration.
Source: src/cook/execution/variables.rs:218-256
Examples:
# Get current git commit
- shell: "echo 'Building from ${cmd:git rev-parse --short HEAD}'"
# Use command output in logic
- shell: "if [ '${cmd:uname}' = 'Darwin' ]; then echo 'macOS'; fi"
# Capture timestamp
- shell: "echo 'Build started at ${cmd:date +%Y-%m-%d_%H-%M-%S}'"
# Dynamic configuration
- shell: "cargo build --jobs ${cmd:nproc}"
Caching: Command execution results are cached (commands are expensive to execute repeatedly).
Security Warning: Be cautious with cmd: variables in untrusted workflows - they execute arbitrary shell commands.
JSON Path Extraction (json:)¶
Extract values from JSON data using JSONPath syntax. Useful for processing complex JSON structures.
Source: src/cook/execution/variables.rs:350-379
Syntax: ${json:path:from:source_variable}
Examples:
# Extract from captured variable
- shell: "curl https://api.example.com/data"
capture_output: "api_response"
- shell: "echo 'ID: ${json:$.id:from:api_response}'"
# Extract array element
- shell: "echo 'First item: ${json:$.items[0].name:from:api_response}'"
# Extract nested field
- shell: "echo 'Author: ${json:$.metadata.author:from:config}'"
Not Cached: JSON path extraction is fast and not cached.
Requires: Source variable must contain valid JSON.
Date Formatting (date:)¶
Generate current date/time with custom formatting using chrono format specifiers.
Source: src/cook/execution/variables.rs:278-305
Syntax: ${date:format} (uses chrono format specifiers)
Examples:
# ISO 8601 date
- shell: "echo 'Report generated: ${date:%Y-%m-%d}'"
# Full timestamp
- shell: "echo 'Build time: ${date:%Y-%m-%d %H:%M:%S}'"
# Custom format
- shell: "mkdir backup-${date:%Y%m%d-%H%M%S}"
# Use in filenames
- shell: "cp logs.txt logs-${date:%Y-%m-%d}.txt"
Common Format Specifiers:
- %Y - 4-digit year (2025)
- %m - Month (01-12)
- %d - Day (01-31)
- %H - Hour 24h (00-23)
- %M - Minute (00-59)
- %S - Second (00-59)
- %F - ISO 8601 date (2025-01-15)
- %T - ISO 8601 time (14:30:45)
Not Cached: Date values change over time and are not cached.
UUID Generation (uuid)¶
Generate a random UUID version 4. Useful for unique identifiers, temporary filenames, or correlation IDs.
Source: src/cook/execution/variables.rs:258-276
Examples:
# Generate unique identifier
- shell: "echo 'Request ID: ${uuid}'"
# Create unique temporary file
- shell: "mkdir /tmp/build-${uuid}"
# Correlation ID for tracking
- shell: "curl -H 'X-Correlation-ID: ${uuid}' https://api.example.com"
# Unique test run ID
- shell: "cargo test -- --test-id ${uuid}"
Not Cached: Each ${uuid} reference generates a NEW unique UUID. If you need the same UUID multiple times, capture it first:
- shell: "echo '${uuid}'"
capture_output: "run_id"
- shell: "echo 'Run ID: ${run_id}'" # Same UUID
- shell: "echo 'Same ID: ${run_id}'" # Still same UUID
Computed Variable Caching¶
Computed variables have different caching behaviors based on their expense and volatility:
Cached (Expensive Operations):
- env.* - Environment variable reads
- file:* - File system operations
- cmd:* - Shell command execution
Not Cached (Fast or Volatile):
- json:* - JSON parsing is fast
- date:* - Values change over time
- uuid - Must be unique each time
Cache Details: - Type: LRU (Least Recently Used) cache - Size: 100 entries maximum - Scope: Per workflow execution - Thread Safety: Async RwLock protection
Source: src/cook/execution/variables.rs:218-256 (caching implementation)
Workflow Context Variables¶
Variables that provide information about the current workflow execution:
| Variable | Description | Example |
|---|---|---|
${workflow.name} |
Workflow name from YAML config | echo "Running ${workflow.name}" |
${workflow.id} |
Unique workflow identifier | log-${workflow.id}.txt |
${workflow.iteration} |
Current iteration number (for loops) | Iteration ${workflow.iteration} |
Available in: All phases (setup, map, reduce, merge)
Step Context Variables¶
Variables providing information about the current execution step:
| Variable | Description | Example |
|---|---|---|
${step.name} |
Step name or identifier | echo "Step: ${step.name}" |
${step.index} |
Zero-based step index | Step ${step.index} of ${total_steps} |
Available in: All phases
Item Variables (Map Phase Only)¶
Variables for accessing work item data during parallel processing. The ${item.*} syntax supports arbitrary field access - you can access any field present in your JSON work items, not just the predefined ones shown below.
Source: src/cook/workflow/variables.rs:16-23 (item variable resolution)
| Variable | Description | Example |
|---|---|---|
${item} |
Full item object (as string) | echo ${item} |
${item.value} |
Item value for simple types | process ${item.value} |
${item.path} |
File path (for file inputs) | cat ${item.path} |
${item.name} |
Item display name | echo "Processing ${item.name}" |
${item_index} |
Zero-based item index | Item ${item_index} |
${item_total} |
Total number of items | of ${item_total} |
${item.*} |
Any JSON field - Access arbitrary fields from your work items | ${item.priority}, ${item.custom_field} |
Available in: Map phase only
Arbitrary Field Access¶
The ${item.*} syntax provides full access to any field in your JSON work items. This includes:
- Top-level fields:
${item.priority},${item.status},${item.category} - Nested fields:
${item.metadata.author},${item.config.database.host} - Array indices:
${item.tags[0]},${item.dependencies[2].version} - Mixed access:
${item.data.results[0].score}
Example with custom JSON structure:
# Input: items.json
# [
# {
# "file": "src/main.rs",
# "priority": 10,
# "owner": "backend-team",
# "metadata": {
# "last_modified": "2025-01-10",
# "reviewer": "alice"
# },
# "tags": ["critical", "security"]
# }
# ]
map:
input: "items.json"
json_path: "$[*]"
agent_template:
# Access any field from your JSON structure
- shell: "echo 'Processing ${item.file}'"
- shell: "echo 'Priority: ${item.priority}'"
- shell: "echo 'Owner: ${item.owner}'"
- shell: "echo 'Reviewer: ${item.metadata.reviewer}'"
- shell: "echo 'First tag: ${item.tags[0]}'"
- claude: "/analyze '${item.file}' --priority ${item.priority} --owner ${item.owner}"
Best Practice: Use descriptive field names in your JSON work items - they become your variable names.
MapReduce Variables (Reduce Phase Only)¶
Variables for accessing aggregated results from map phase. Map results support indexed access for retrieving individual agent results and nested field access for extracting specific properties.
Source: src/cook/execution/mapreduce/utils.rs:119-121, src/cook/execution/mapreduce/reduce_phase.rs:146
| Variable | Description | Example |
|---|---|---|
${map.total} |
Total items in map phase | echo "Processed ${map.total} items" |
${map.successful} |
Successfully processed items | echo "${map.successful} succeeded" |
${map.failed} |
Failed items count | echo "${map.failed} failed" |
${map.results} |
All map results as JSON array | echo '${map.results}' \| jq |
${map.results_json} |
Alias for map.results (same value) |
echo '${map.results_json}' \| jq |
${map.results[index]} |
Individual result by index (0-based) | ${map.results[0]}, ${map.results[5]} |
${map.results[index].field} |
Nested field access | ${map.results[0].output}, ${map.results[2].item_id} |
${map.key} |
Key for map output (optional) | ${map.key} |
${worker.id} |
Worker ID for tracking | Worker ${worker.id} |
Available in: Reduce phase only
Indexed Access to Map Results¶
You can access individual agent results using bracket notation [index] and drill into nested fields with dot notation.
Syntax patterns:
- ${map.results[0]} - First agent result (full object)
- ${map.results[0].output} - Output from first agent
- ${map.results[0].item_id} - Item ID processed by first agent
- ${map.results[0].success} - Success status ("true" or "false")
Example:
reduce:
# Access specific agent results
- shell: "echo 'First result: ${map.results[0]}'"
- shell: "echo 'First output: ${map.results[0].output}'"
- shell: "echo 'Second agent processed: ${map.results[1].item_id}'"
# Combine with shell commands
- shell: |
if [ "${map.results[0].success}" = "true" ]; then
echo "First agent succeeded"
fi
# Process multiple results
- shell: |
echo "Results 0-2:"
echo "${map.results[0].item_id}"
echo "${map.results[1].item_id}"
echo "${map.results[2].item_id}"
Full Array Processing¶
For processing all results, use ${map.results} with JSON tools like jq:
reduce:
# Count errors using jq
- shell: |
echo '${map.results}' | jq '[.[] | select(.status == "error")] | length'
capture_output: "error_count"
# Extract all item IDs
- shell: |
echo '${map.results}' | jq -r '.[].item_id'
capture_output: "processed_items"
# Calculate average score
- shell: |
echo '${map.results}' | jq '[.[].score] | add / length'
capture_output: "avg_score"
# Filter successful results
- shell: |
echo '${map.results}' | jq '[.[] | select(.success == true)]'
capture_output: "successful_results"
# Generate summary
- claude: "/summarize ${map.results} --total ${map.total} --failed ${map.failed}"
Note: ${map.results} and ${map.results_json} are equivalent - use whichever is clearer in your context.
Git Context Variables¶
Variables tracking git changes throughout workflow execution:
| Variable | Description | Example |
|---|---|---|
${step.files_added} |
Files added in current step | echo ${step.files_added} |
${step.files_modified} |
Files modified in current step | echo ${step.files_modified} |
${step.files_deleted} |
Files deleted in current step | echo ${step.files_deleted} |
${step.files_changed} |
All changed files (added + modified + deleted) | echo ${step.files_changed} |
${step.commits} |
Commits in current step | echo ${step.commits} |
${step.commit_count} |
Number of commits in step | echo "${step.commit_count} commits" |
${step.insertions} |
Lines inserted in step | echo "+${step.insertions}" |
${step.deletions} |
Lines deleted in step | echo "-${step.deletions}" |
${workflow.commits} |
All commits in workflow | git show ${workflow.commits} |
${workflow.commit_count} |
Total number of commits | echo "${workflow.commit_count} commits" |
Available in: All phases (requires git repository)
Format Modifiers¶
Important: These format modifiers work with all git context variables that return file or commit lists, not just the examples shown. Apply them to any of: step.files_added, step.files_modified, step.files_deleted, step.files_changed, step.commits, workflow.commits, and merge phase git variables.
Git context variables support multiple output formats:
| Modifier | Description | Example |
|---|---|---|
| (default) | Space-separated list | ${step.files_added} → file1.rs file2.rs |
:json |
JSON array format | ${step.files_added:json} → ["file1.rs", "file2.rs"] |
:lines |
Newline-separated list | ${step.files_added:lines} → file1.rs\nfile2.rs |
:csv |
Comma-separated list | ${step.files_added:csv} → file1.rs,file2.rs |
:*.ext |
Glob pattern filter | ${step.files_added:*.rs} → only Rust files |
:path/**/*.ext |
Path with glob | ${step.files_added:src/**/*.rs} → Rust files in src/ |
Format Examples:
# JSON format for jq processing
- shell: "echo '${step.files_added:json}' | jq -r '.[]'"
# Newline format for iteration
- shell: |
echo '${step.files_modified:lines}' | while read file; do
cargo fmt "$file"
done
# Glob filtering for language-specific operations
- shell: "cargo clippy ${step.files_modified:*.rs}"
# Multiple glob patterns
- shell: "git diff ${step.files_modified:*.rs,*.toml}"
Merge Variables (Merge Phase Only)¶
Variables available during the merge phase when integrating worktree changes. Merge variables include both basic context and comprehensive git tracking information.
Source: src/worktree/merge_orchestrator.rs:340-423
Basic Merge Context¶
| Variable | Description | Example |
|---|---|---|
${merge.worktree} |
Worktree name being merged | echo ${merge.worktree} |
${merge.source_branch} |
Source branch from worktree | git log ${merge.source_branch} |
${merge.target_branch} |
Target branch (where you started) | git merge ${merge.source_branch} |
${merge.session_id} |
Session ID for correlation | echo ${merge.session_id} |
Merge Git Context Variables¶
Additional variables tracking git changes during the merge operation:
| Variable | Description | Format | Example |
|---|---|---|---|
${merge.commits} |
All commits from worktree | JSON array | echo '${merge.commits}' \| jq |
${merge.commit_count} |
Number of commits | Integer | echo "${merge.commit_count} commits" |
${merge.commit_ids} |
Short commit IDs | Comma-separated | git show ${merge.commit_ids} |
${merge.modified_files} |
Modified files with metadata | JSON array | echo '${merge.modified_files}' \| jq |
${merge.file_count} |
Number of modified files | Integer | echo "${merge.file_count} files" |
${merge.file_list} |
File paths | Comma-separated | echo ${merge.file_list} |
Available in: Merge phase only
Limits: Capped at 100 commits and 500 files to prevent overwhelming workflows (configurable in GitOperationsConfig).
Merge Context Examples¶
Basic merge workflow:
merge:
commands:
- shell: "git fetch origin"
- shell: "git merge origin/${merge.target_branch}"
- claude: "/prodigy-merge-worktree ${merge.source_branch} ${merge.target_branch}"
Using git context variables:
merge:
commands:
# Show merge summary
- shell: |
echo "Merging worktree: ${merge.worktree}"
echo "Commits: ${merge.commit_count}"
echo "Files modified: ${merge.file_count}"
# List all commits being merged
- shell: "echo 'Commit IDs: ${merge.commit_ids}'"
# Process commits as JSON
- shell: |
echo '${merge.commits}' | jq -r '.[] | "\(.short_id): \(.message)"'
# Check specific files
- shell: |
echo '${merge.modified_files}' | jq -r '.[].path'
# Conditional merge based on file count
- shell: |
if [ ${merge.file_count} -gt 50 ]; then
echo "Large merge detected, requesting review"
fi
# Perform merge
- claude: "/prodigy-merge-worktree ${merge.source_branch} ${merge.target_branch}"
Commit Object Structure¶
The ${merge.commits} variable contains an array of commit objects with this structure:
[
{
"id": "full-sha-hash",
"short_id": "abc1234",
"author": {
"name": "Author Name",
"email": "author@example.com"
},
"message": "Commit message",
"timestamp": "2025-01-10T12:00:00Z",
"files_changed": ["file1.rs", "file2.rs"]
}
]
Source: src/cook/execution/mapreduce/resources/git_operations.rs:280-293
File Object Structure¶
The ${merge.modified_files} variable contains an array of file modification objects:
[
{
"path": "src/main.rs",
"modification_type": "Modified",
"size_before": 1024,
"size_after": 1156,
"last_modified": "2025-01-10T12:00:00Z",
"commit_id": "abc1234"
}
]
Source: src/cook/execution/mapreduce/resources/git_operations.rs:311-322
Validation Variables¶
Variables for workflow validation and completion tracking:
| Variable | Description | Example |
|---|---|---|
${validation.completion} |
Completion percentage (0-100) | echo "${validation.completion}%" |
${validation.gaps} |
Array of missing requirements | echo '${validation.gaps}' |
${validation.status} |
Status: complete/incomplete/failed | if [ "${validation.status}" = "complete" ] |
Available in: Validation phases
Variable Interpolation Syntax¶
Prodigy supports two interpolation syntaxes:
${VAR}- Preferred syntax, works in all contexts (recommended)$VAR- Shell-style syntax, simpler but may have limitations
When to use ${VAR}:
- In YAML values with special characters
- For nested field access: ${item.nested.field}
- When combining with text: prefix_${var}_suffix
- For format modifiers: ${step.files:json}
When $VAR works:
- Simple variable names in shell commands
- Environment variables in shell context
- Quick substitutions without special characters
Best Practice: Always use ${VAR} syntax for consistency and reliability.
Legacy Variable Aliases¶
For backward compatibility, Prodigy supports legacy variable aliases from earlier versions. These are still functional but deprecated - prefer the current variable names in new workflows.
Source: src/cook/workflow/variables.rs (legacy alias definitions)
| Legacy Alias | Current Variable | Context | Status |
|---|---|---|---|
${ARG} |
${item.value} |
Map phase | Deprecated |
${ARGUMENT} |
${item.value} |
Map phase | Deprecated |
${FILE} |
${item.path} |
Map phase | Deprecated |
${FILE_PATH} |
${item.path} |
Map phase | Deprecated |
Example:
# Old style (still works but discouraged)
map:
agent_template:
- shell: "process ${ARG}"
- shell: "cat ${FILE}"
# New style (recommended)
map:
agent_template:
- shell: "process ${item.value}"
- shell: "cat ${item.path}"
Migration Recommendation: Update legacy aliases to current variable names when maintaining older workflows. The current names are more explicit and work better with arbitrary JSON field access.
Default Values¶
Provide fallback values for undefined or missing variables using the :- syntax (bash/shell convention):
Syntax: ${variable:-default_value}
Source: src/cook/execution/interpolation.rs:277
Examples:
# Use default if variable is undefined
- shell: "echo 'Timeout: ${timeout:-600}'"
# Output: "Timeout: 600" if timeout is not defined
# Fallback for optional configuration
- shell: "cargo build --profile ${build_profile:-dev}"
# Uses "dev" profile if build_profile not set
# Default for MapReduce variables
- shell: "echo 'Processed ${map.successful:-0} items'"
# Shows "0" if map.successful is not available
Behavior with Interpolation Modes: - Non-strict mode (default): Uses default value if variable is undefined - Strict mode: Default value syntax prevents errors for optional variables
Interpolation Modes¶
Prodigy supports two modes for handling undefined variables:
Non-strict Mode (Default):
- Leaves placeholders unresolved when variable is undefined
- Example: ${undefined} remains as ${undefined} in output
- With default: ${undefined:-fallback} becomes fallback
- Use case: Workflows that can handle partial variable resolution
Strict Mode:
- Fails immediately on undefined variables
- Example: ${undefined} causes workflow to fail with comprehensive error
- Error message lists all available variables for debugging
- Use case: Production workflows requiring all variables to be properly defined
Source: src/cook/execution/interpolation.rs:16-17, 104-137
Configuration: Strict mode is configured per InterpolationEngine instance and controlled at the workflow execution level.
Best Practice: Use strict mode during development to catch variable name typos and scope issues early. Use default values (${var:-default}) for truly optional configuration.
Examples:
# Non-strict mode (graceful degradation)
- shell: "echo 'Config: ${optional_config:-none}'"
# Works even if optional_config is undefined
# Strict mode (fail fast)
# If required_var is undefined, workflow stops with error:
# "Variable interpolation failed: required_var not found.
# Available variables: workflow.name, workflow.id, step.index, ..."
- shell: "echo 'Required: ${required_var}'"
Variable Scoping and Precedence¶
Scope by Phase¶
| Phase | Variables Available |
|---|---|
| Setup | Standard, workflow context, step context, git context, custom captured |
| Map | Standard, workflow context, step context, git context, item variables, custom captured |
| Reduce | Standard, workflow context, step context, git context, MapReduce variables, custom captured |
| Merge | Standard, workflow context, step context, merge variables, custom captured |
Important: Setup phase captures are available in map and reduce phases. Map phase captures are only available within that specific agent. Reduce phase captures are available to subsequent reduce steps.
Variable Precedence (highest to lowest)¶
- Custom captured variables (
capture_output) - Phase-specific built-in variables (
item.*,map.*,merge.*) - Step context variables (
step.*) - Workflow context variables (
workflow.*) - Standard output variables (
last.output,shell.output) - Environment variables (static workflow-level
envblock) - Computed variables (
env.*,file:*,cmd:*,json:*,date:*,uuid)
Note: Computed variables have lowest precedence because they're evaluated on-demand. If a custom variable has the same name as a computed variable, the custom variable wins.
Shadowing Warning: Custom captures can shadow built-in variable names. Avoid using names like item, map, workflow, etc. as custom variable names.
Example:
# Bad: shadows built-in ${item}
- shell: "custom command"
capture_output: "item" # Don't do this!
# Good: descriptive custom name
- shell: "custom command"
capture_output: "custom_result"
Parent Context Resolution¶
Variable resolution walks up a parent context chain when variables are not found in the current context. This enables variable inheritance across workflow phases and nested contexts.
Source: src/cook/execution/interpolation.rs:200-226, InterpolationContext struct at :376-381
Resolution Order: 1. Check current context 2. If not found, check parent context 3. If not found in parent, check parent's parent 4. Continue until variable is found or no parent exists 5. If not found and has default value, use default 6. If not found in strict mode, fail with error listing available variables
Benefits: - Nested workflow contexts inherit variables from parent workflows - Foreach loops access both loop-level and workflow-level variables - Map agents access setup phase variables - Reduce phase accesses both map results and setup variables
Example:
setup:
- shell: "pwd"
capture_output: "workspace_root" # Available to all agents via parent context
- shell: "git rev-parse HEAD"
capture_output: "base_commit" # Also inherited by map agents
map:
input: "items.json"
json_path: "$.items[*]"
agent_template:
- shell: "echo 'processing ${item.name}'"
capture_output: "item_status" # Only in this agent's context
- shell: "cd ${workspace_root}" # Resolved from parent (setup) context
- shell: "git diff ${base_commit}" # Also from parent context
- shell: "echo 'Status: ${item_status}'" # From current agent context
reduce:
# Can access setup variables but NOT individual agent's item_status
- shell: "cd ${workspace_root}" # From setup phase parent context
- shell: "echo 'Base: ${base_commit}'" # Also from setup phase
Context Hierarchy:
Setup Context (workspace_root, base_commit)
↓ parent
Map Agent Context (item, item_status, workspace_root*, base_commit*)
↓ parent
Reduce Context (map.results, workspace_root*, base_commit*)
*Inherited from parent context
Reduce Phase Access to Item Data¶
In the reduce phase, individual item variables (${item.*}) are not directly available, but you can access all item data through ${map.results}, which contains the aggregated results from all map agents.
Examples:
reduce:
# Count items with specific property
- shell: |
echo '${map.results}' | jq '[.[] | select(.type == "error")] | length'
capture_output: "error_count"
# Extract all file paths processed
- shell: |
echo '${map.results}' | jq -r '.[].item.path'
capture_output: "all_paths"
# Aggregate numeric field
- shell: |
echo '${map.results}' | jq '[.[].coverage] | add / length'
capture_output: "avg_coverage"
# Filter and transform results
- shell: |
echo '${map.results}' | jq '[.[] | select(.item.priority > 5) | .item.name]'
capture_output: "high_priority_items"
Performance: Template Caching¶
Prodigy implements dual caching for optimal performance: template parsing cache and operation result cache.
Source: src/cook/execution/interpolation.rs:18-19, 68-75; src/cook/execution/variables.rs:218-256
Template Parse Caching¶
When the same variable template is used multiple times, the template is parsed once and reused:
How It Works: - First use: Template is parsed and cached - Subsequent uses: Cached template is reused (no re-parsing) - Cache key: Exact template string - Automatic: No configuration needed
Example:
# Template "${item.path} --priority ${item.metadata.priority:-5}"
# is parsed once, then reused for all 1000 items
map:
input: "items.json" # 1000 items
json_path: "$.items[*]"
agent_template:
- shell: "process ${item.path} --priority ${item.metadata.priority:-5}"
Computed Variable Caching¶
Expensive computed operations (file reads, command execution) have separate result caching:
Cached Operations:
- ${env.VAR} - Environment variable lookups
- ${file:path} - File system reads
- ${cmd:command} - Shell command execution
Not Cached:
- ${json:path:from:var} - JSON parsing is fast
- ${date:format} - Values change over time
- ${uuid} - Must be unique
Cache Details: - Type: LRU (Least Recently Used) cache - Size: 100 entries maximum - Scope: Per workflow execution - Thread Safety: Async RwLock protection
Performance Impact:
# First shell command: Reads .commit-message.txt from disk
- shell: "git commit -m '${file:.commit-message.txt}'"
# Second shell command: Uses cached file content (no disk read)
- shell: "echo 'Message: ${file:.commit-message.txt}'"
# Third shell command: Still uses cache
- shell: "test -n '${file:.commit-message.txt}'"
Benefits: - Faster interpolation for repeated templates (template cache) - Reduced I/O for repeated file reads (operation cache) - Lower CPU for repeated command execution (operation cache) - Reduced latency in MapReduce workflows
When It Matters Most: - MapReduce workflows with many work items (>100) - Workflows using the same computed variables repeatedly - High-frequency variable interpolation in loops - Templates with multiple variables and nested field access
Note: All caching is transparent and automatic. You don't need any configuration to benefit from it. Both caches persist for the lifetime of the workflow execution.