Recipe Runner Logging Reference¶
Reference for live recipe progress, heartbeat events, bounded subprocess output
snippets, and additive JSON fields emitted by amplihack recipe run.
Status: Planned finished-state contract for the recipe-runner transparency feature.
Contents¶
- Output contract
- Live progress lines
- Heartbeat events
- Recent output snippets
- Failure context
- JSON result fields
- JSONL log events
- Configuration
- Examples
Output contract¶
amplihack recipe run keeps human-readable progress and machine-readable final
results on separate streams.
| Stream | Contents | Intended consumer |
|---|---|---|
stderr |
Live recipe progress, step lifecycle lines, heartbeats, failure diagnostics, bounded snippets | Humans, CI logs, terminal UIs |
stdout |
Final command output in the requested --format (table, json, or yaml) |
Shell pipelines, scripts, jq |
Default runs emit progress to stderr without requiring --verbose.
--verbose increases detail; it is not required for basic progress.
amplihack recipe run default-workflow \
-c task_description="Add cache headers" \
-c repo_path=. \
--format json > result.json
The terminal still shows live progress because progress is written to stderr,
while result.json contains only the final JSON result from stdout.
Live progress lines¶
Every step emits lifecycle progress in real time.
[recipe default-workflow] started (23 steps)
[step 04/23 requirements-clarification] started agent=prompt-writer
[step 04/23 requirements-clarification] heartbeat elapsed=60s status=running phase=agent
[step 04/23 requirements-clarification] completed elapsed=91s
[step 05/23 implementation-planning] started agent=architect
[step 05/23 implementation-planning] failed elapsed=34s error="agent exited with code 1"
Progress lines include:
| Field | Description |
|---|---|
| Recipe name | The recipe being executed, such as default-workflow |
| Step index | Current 1-based step number and total step count when known |
| Step id/name | Stable step identifier from the recipe YAML |
| Phase | recipe, agent, subprocess, bash, or finalize |
| Status | started, running, completed, failed, or skipped |
| Elapsed time | Wall-clock time since the step or recipe started |
| Child identity | Agent name, subprocess command label, PID, or nested recipe name when available |
Heartbeat events¶
Long-running recipe, agent, subprocess, and nested-recipe steps emit periodic heartbeats so callers can distinguish active work from a hung process.
Default heartbeat behavior:
| Setting | Default | Description |
|---|---|---|
| First heartbeat | After one interval | Avoids noise for short steps |
| Interval | 60 seconds | Rate-limited per active step |
| Stream | stderr |
Human-readable line plus structured log event |
| Contents | Step id/name, phase, status, elapsed time, child identity when known | Enough context to know what is still running |
Heartbeat lines never include full child output. They may include a short progress hint such as the active agent name or subprocess label.
Recent output snippets¶
The runner keeps bounded rolling buffers for each active subprocess, agent, and nested recipe. Snippets are attributed by source and stream.
| Attribute | Behavior |
|---|---|
| Source | agent:<name>, subprocess:<pid>, recipe:<name>, or step:<id> |
| Stream | stdout, stderr, or combined |
| Bound | Limited by line count and byte count |
| Retention | Recent output only; older lines are dropped |
| Redaction | No additional redaction guarantee; snippets are bounded but may contain whatever the child printed |
| Live output | Snippets are summarized in progress logs; noisy child output is not streamed unbounded |
Do not print secrets from recipe steps. The transparency feature limits snippet size and attribution, but it is not a substitute for secret-safe child tools.
Snippets appear in:
- step failure diagnostics printed to
stderr - JSON result fields when
--format jsonis used - JSONL recipe log events when recipe logging is enabled
Failure context¶
Failures include actionable context instead of only a generic step failure.
[step 08/23 code-implementation] failed elapsed=12m14s agent=builder
error: agent exited with code 1
recent stderr from agent:builder (last 20 lines, 8192 bytes max):
error[E0425]: cannot find value `cache_policy` in this scope
--> src/http/cache.rs:42:18
|
42 | apply_policy(cache_policy);
| ^^^^^^^^^^^^ not found in this scope
recent stdout from agent:builder:
Running cargo test --quiet
Failure diagnostics include these fields when known:
| Field | Description |
|---|---|
step_id |
Stable recipe step id |
step_name |
Human-readable step name or label |
phase |
Current phase at failure time |
status |
failed |
elapsed_seconds |
Step duration |
child |
Agent, subprocess, or nested recipe identity |
exit_code |
Child process exit code when applicable |
recent_output |
Bounded attributed stdout/stderr snippets |
JSON result fields¶
The final JSON result remains backward-compatible. Existing fields are preserved; new fields are optional and additive.
Top-level result:
| Field | Type | Required | Description |
|---|---|---|---|
recipe_name |
string | yes | Recipe name |
success |
boolean | yes, when used by the existing result schema | Boolean overall result |
status |
string | yes, when used by the existing result schema | Overall result such as SUCCESS, FAILURE, or PARTIAL |
step_results |
array | yes | Ordered step results |
context |
object | no | Final context values |
duration_seconds |
number | no | Total recipe duration |
progress_summary |
object | no | Last known phase/status, heartbeat count, and log path |
failure_context |
object | no | Failure context for the first failing step |
Step result additive fields:
| Field | Type | Required | Description |
|---|---|---|---|
step_id |
string | yes | Stable step id |
status |
string | yes | completed, failed, or skipped |
output |
string | no | Step output or summary |
error |
string | no | Error message |
step_name |
string | no | Human-readable name |
phase |
string | no | Last phase for the step |
elapsed_seconds |
number | no | Step duration |
started_at |
string | no | RFC 3339 timestamp |
completed_at |
string | no | RFC 3339 timestamp |
child |
object | no | Agent, subprocess, or nested recipe identity |
last_heartbeat_at |
string | no | RFC 3339 timestamp of last heartbeat |
recent_output |
array | no | Bounded attributed snippets |
child object:
recent_output entry:
{
"source": "agent:builder",
"stream": "stderr",
"line_count": 20,
"byte_count": 4096,
"truncated": true,
"text": "error[E0425]: cannot find value `cache_policy` in this scope\n..."
}
Consumers should ignore unknown fields and treat every field in this section as
optional unless listed as required in the existing schema. The amplihack CLI
must preserve additive fields emitted by recipe-runner-rs when it formats
--format json or --format yaml; parsing the runner JSON and then dropping
unknown diagnostic fields is a bug.
JSONL log events¶
When recipe logging is enabled, the log file contains JSONL events with enough context to reconstruct progress.
Lifecycle event:
{
"type": "step_lifecycle",
"recipe_name": "default-workflow",
"step_index": 8,
"total_steps": 23,
"step_id": "code-implementation",
"step_name": "Code implementation",
"phase": "agent",
"status": "started",
"child": { "kind": "agent", "name": "builder" },
"elapsed_seconds": 0.0,
"timestamp": "2026-06-03T18:12:00Z"
}
Heartbeat event:
{
"type": "heartbeat",
"recipe_name": "default-workflow",
"step_id": "code-implementation",
"step_name": "Code implementation",
"phase": "agent",
"status": "running",
"child": { "kind": "agent", "name": "builder" },
"elapsed_seconds": 180.0,
"timestamp": "2026-06-03T18:15:00Z"
}
Output snippet event:
{
"type": "output_snippet",
"recipe_name": "default-workflow",
"step_id": "code-implementation",
"source": "agent:builder",
"stream": "stderr",
"recent_output": {
"line_count": 12,
"byte_count": 2048,
"truncated": false,
"text": "Running cargo test --quiet\n..."
},
"timestamp": "2026-06-03T18:15:30Z"
}
Configuration¶
Defaults are conservative and bounded. Configure them through environment
variables or the matching keys in ~/.amplihack/config.
| Environment variable | Config key | Default | Description |
|---|---|---|---|
AMPLIHACK_RECIPE_HEARTBEAT_INTERVAL_SECONDS |
recipe.heartbeat_interval_seconds |
60 |
Seconds between heartbeat lines per active step. Set 0 to disable heartbeats. |
AMPLIHACK_RECIPE_SNIPPET_LINES |
recipe.snippet_lines |
20 |
Maximum recent lines retained per source/stream. |
AMPLIHACK_RECIPE_SNIPPET_BYTES |
recipe.snippet_bytes |
8192 |
Maximum bytes retained per source/stream. |
AMPLIHACK_RECIPE_LOG_JSONL |
recipe.log_jsonl |
unset | Optional path for structured JSONL recipe events. |
Environment variables take precedence over config-file keys.
Use --verbose to include child launch details and additional snippet context:
--progress is not a supported amplihack recipe run flag. Passing it should
fail fast with an actionable message explaining that progress is already emitted
to stderr by default and that --verbose only increases diagnostic detail.
Examples¶
Capture final JSON while watching progress:
amplihack recipe run default-workflow \
-c task_description="Add input validation" \
-c repo_path=. \
--format json > /tmp/recipe-result.json
Write a JSONL event log:
AMPLIHACK_RECIPE_LOG_JSONL=/tmp/default-workflow.jsonl \
amplihack recipe run default-workflow \
-c task_description="Fix flaky retry tests" \
-c repo_path=.
Show the failing step and recent stderr from the final JSON:
jq '.step_results[]
| select(.status == "failed")
| {step_id, elapsed_seconds, child, recent_output}' /tmp/recipe-result.json
Related¶
- Run a Recipe End-to-End - Usage guide
- RecipeResult Reference - Final result schema
- amplihack recipe Reference - Command-line flags
- Recipe Runner Architecture - CLI and runner responsibility split