Skip to content

RecipeResult Reference

RecipeResult represents the outcome of running a workflow recipe. It captures whether the recipe succeeded, how many steps ran, and the result of each individual step.

Contents


Class Overview

RecipeResult is the structured output of a recipe run. It is returned by the amplihack recipe run CLI command when --format json is used. The JSON schema uses the same field names documented below.


Attributes

Attribute Type Description
recipe_name str The name of the recipe that was run (e.g. "default-workflow").
status RecipeStatus Overall outcome: SUCCESS, FAILURE, or PARTIAL.
success bool \| None Optional boolean outcome field used by CLI JSON output when present.
step_results list[StepResult] Ordered list of results, one per step that executed.
duration_seconds float Wall-clock time from recipe start to finish.
error str \| None Human-readable error message if status is FAILURE. None on success.
progress_summary dict \| None Optional live-progress summary: last phase/status, heartbeat count, and log path.
failure_context dict \| None Optional actionable context for the first failing step.
run_id str \| None Stable UUID generated for this amplihack recipe run invocation.
log_pointer dict \| None Concise final pointer summary with status, worktree, child PID, runner path, and log paths.

String Representation

str(result) returns a one-line summary suitable for logging:

RecipeResult(<recipe-name>: <STATUS>, <N> steps)

Examples:

result = runner.run("default-workflow", context)

print(str(result))
# RecipeResult(default-workflow: SUCCESS, 22 steps)

failed_result = runner.run("quick-fix", context)
print(str(failed_result))
# RecipeResult(quick-fix: FAILURE, 3 steps)

The summary includes:

  • The recipe name as written in the recipe YAML file
  • The overall STATUS in uppercase
  • The count of steps that completed (not the total steps defined in the recipe)

Note: Prior to v0.9.2, str(result) returned a verbose multi-line representation that included individual step IDs (e.g. step-1). Code that parsed str(result) should be updated to use result.step_results directly for structured access.


Step Results

Each entry in step_results is a StepResult:

@dataclass
class StepResult:
    step_id: str          # e.g. "step-1", "step-2"
    step_name: str        # Human-readable step name from recipe YAML
    status: StepStatus    # SUCCESS, FAILURE, SKIPPED
    output: str | None    # Agent output or None if skipped
    duration_seconds: float
    phase: str | None     # agent, bash, subprocess, recipe, finalize
    child: dict | None    # Agent, subprocess, or nested recipe identity
    last_heartbeat_at: str | None
    recent_output: list[dict]

Accessing step results directly:

result = runner.run("default-workflow", context)

for step in result.step_results:
    print(f"{step.step_id}: {step.status.value} ({step.duration_seconds:.1f}s)")

# step-1: SUCCESS (2.3s)
# step-2: SUCCESS (8.1s)
# step-3: FAILURE (1.0s)

Progress, Failure, and Correlation Context

The JSON schema is backward-compatible. Existing fields remain unchanged; newer runner versions add optional progress and diagnostic fields.

progress_summary

progress_summary describes the last known live-progress state for the recipe.

{
  "last_step_id": "code-implementation",
  "last_step_name": "Code implementation",
  "phase": "agent",
  "status": "completed",
  "heartbeat_count": 7,
  "log_path": "/tmp/default-workflow.jsonl"
}

failure_context

When a recipe fails, failure_context repeats the most actionable step-level details at the top level.

{
  "step_id": "code-implementation",
  "step_name": "Code implementation",
  "phase": "agent",
  "status": "failed",
  "elapsed_seconds": 734.2,
  "child": {
    "kind": "agent",
    "name": "builder"
  },
  "recent_output": [
    {
      "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..."
    }
  ]
}

run_id

run_id is the stable UUID for the whole recipe invocation. It matches:

  • AMPLIHACK_RECIPE_RUN_ID in the runner child environment
  • the run_id in early and final stderr pointer events
  • the run_id in JSONL events when recipe logging is enabled and the runner copies AMPLIHACK_RECIPE_RUN_ID into those events
{
  "run_id": "5b60657b-76ef-4f49-8a22-8b89ed75f43e"
}

log_pointer

log_pointer is the final pointer summary included in JSON/YAML results when a result can be produced.

{
  "log_pointer": {
    "run_id": "5b60657b-76ef-4f49-8a22-8b89ed75f43e",
    "recipe_name": "default-workflow",
    "status": "success",
    "worktree": "/home/user/src/myapp",
    "branch": "main",
    "child_pid": 41822,
    "exit_code": 0,
    "runner_path": "/home/user/.local/bin/recipe-runner-rs",
    "log_paths": {
      "jsonl": "/tmp/default-workflow.jsonl"
    }
  }
}

The full stderr pointer event can include additional display-only context such as task_description, issue fields, and PR fields. The result summary stays concise so scripts can consume it safely.

Pointer status values are lowercase wrapper statuses such as success, failure, spawn_failure, and parse_failure. Top-level result statuses keep their existing schema casing, commonly SUCCESS, FAILURE, or PARTIAL, or the boolean success field.

recent_output

recent_output contains bounded rolling snippets from child stdout/stderr.

Field Type Description
source str agent:<name>, subprocess:<pid>, recipe:<name>, or step:<id>
stream str stdout, stderr, or combined
line_count int Number of retained lines
byte_count int Number of retained bytes
truncated bool true when older output was dropped
text str Recent output text; bounded but not additionally redacted by this feature

Consumers should ignore unknown fields and handle all progress fields as optional. The amplihack CLI must preserve additive fields emitted by recipe-runner-rs when formatting JSON or YAML; parsing the runner output and then dropping unknown diagnostic fields is a bug. See Recipe Runner Logging Reference for the full logging contract.


Terminal-state failures

Development workflows include terminal-state evidence in the failing step output when completion cannot be proven. These fields are additive and must be preserved by JSON/YAML consumers. The target terminal-state feature emits boolean markers as canonical lowercase strings ("true" or "false") because the shell helper output is key/value text; consumers should accept either those strings or native booleans:

{
  "terminal_success": "false",
  "terminal_state": "FAILED_MISSING_TERMINAL_EVIDENCE",
  "terminal_reason": "development workflow stopped after workflow-worktree; implementation, verification, publish, or explicit no-op evidence is required",
  "observed_phases": "workflow-prep,workflow-worktree",
  "missing_evidence": "implementation_completed,verification_completed,publish_state_reached,terminal_no_op"
}

When workflow-terminal-state fails, the overall RecipeResult.status is FAILURE and the process exits nonzero. Callers must not reinterpret missing terminal evidence as a successful planning run. See Workflow Terminal-State Reference for the full field contract.


Usage Examples

Check overall status

amplihack recipe run default-workflow \
  -c task_description="add input validation" \
  --format json | jq '.status // .success'
# true

Count successful steps

amplihack recipe run default-workflow \
  -c task_description="add input validation" \
  --format json | jq '[.step_results[] | select(.status == "completed" or .status == "SUCCESS")] | length'

Serialise to JSON

The --format json flag produces the full RecipeResult as JSON:

amplihack recipe run default-workflow \
  -c task_description="add input validation" \
  --format json | jq .

Inspect failure context

amplihack recipe run default-workflow \
  -c task_description="fix cache policy failure" \
  --format json > result.json

jq '.failure_context
  | {step_id, step_name, elapsed_seconds, child, recent_output}' result.json

Log a one-line summary

Without --format json, the CLI prints the final result in the default human-readable table format on stdout:

Recipe: default-workflow
Status: ✓ Success

Steps:
  ✓ requirements-clarification: completed

Integration with Recipe Runner

RecipeResult is what the CLI surfaces when --format json is requested:

amplihack recipe run default-workflow \
  -c task_description="add login endpoint" \
  --format json | jq '.status // .success'
# true

The JSON schema matches the fields documented in Attributes. Existing result producers may expose either success (boolean) or status ("SUCCESS", "FAILURE", "PARTIAL"); consumers should accept both.


See Also