Recipe Runner Architecture¶
Why the recipe runner is an external binary, how amplihack locates and invokes it, and how recipe execution stays predictable.
Contents¶
- Why external
- Binary resolution
- Invocation contract
- Data flow
- Logging responsibility split
- What amplihack does NOT do
- Operational contract
Why external¶
The recipe runner (recipe-runner-rs) is a separate Rust binary maintained
in its own repository (rysweet/amplihack-recipe-runner). This separation
exists because:
- Independent release cadence — Recipe execution semantics change more frequently than CLI behavior.
- Build isolation — The runner has different dependencies (YAML parsing, step execution, agent spawning) that would bloat the CLI.
- Replaceability — The CLI treats the runner as a black box behind a stable CLI interface.
Binary resolution¶
amplihack resolves the runner binary at launch time using freshness.rs:
$PATH lookup for `recipe-runner-rs`
│
├── found → check freshness against GitHub HEAD
│ │
│ ├── up-to-date → use it
│ └── stale → `cargo install --git` to upgrade
│
└── not found → `cargo install --git` to install
The freshness check compares the locally installed commit SHA against the
remote main branch HEAD, throttled by a cooldown file at
~/.amplihack/state/recipe_runner.json.
Source: crates/amplihack-cli/src/freshness.rs, lines 108–176.
Invocation contract¶
amplihack invokes the runner as a subprocess:
The runner: 1. Resolves the recipe YAML from the search path 2. Validates schema and step dependencies 3. Executes steps sequentially, threading context variables between them 4. Streams live progress, heartbeat, and failure diagnostics to stderr 5. Writes the final structured result to stdout 6. Returns exit code 0 on success, 1 on failure
Data flow¶
┌─────────────┐ CLI args ┌──────────────────┐
│ amplihack │──────────────────▶│ recipe-runner-rs │
│ (CLI) │ │ (external binary) │
└─────────────┘ └──────────────────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ bash │ │ agent │ │ recipe │
│ step │ │ step │ │ step │
└────────┘ └────────┘ └────────┘
Context variables flow forward: each step's output key becomes available
to subsequent steps via {{variable_name}} interpolation.
Logging responsibility split¶
recipe-runner-rs owns execution transparency because it is the process that
knows which step, child process, agent, or nested recipe is active.
| Component | Responsibility |
|---|---|
recipe-runner-rs |
Emit step lifecycle progress, heartbeats, JSONL log events, bounded recent stdout/stderr snippets, and failure context |
amplihack CLI |
Resolve and launch the runner, forward stderr progress by default, preserve stdout for final structured output, and preserve additive JSON fields without requiring them |
This split keeps recipe semantics inside the runner while letting the CLI remain
a stable process supervisor. The CLI must not reinterpret step order or child
state. It forwards the runner's stderr progress, captures stdout for the final
--format result, and displays optional diagnostic fields when present. When
the runner emits additive diagnostic fields, the CLI must not drop them while
formatting JSON or YAML output.
What amplihack does NOT do¶
- Does not parse recipes — YAML parsing is the runner's responsibility.
- Does not execute steps — Step dispatch (bash/agent/recipe) is handled by the runner.
- Does not manage step state — Context threading, condition evaluation, and output capture are runner internals.
- Does not embed the runner — There is no compiled-in recipe execution engine.
amplihack is responsible for: binary resolution, freshness checks, argument forwarding, and exit code propagation.
Operational contract¶
The goal is a single native recipe runner. Consolidation requires:
- Keeping all recipe step types covered by native execution
- Avoiding language-specific recipe shims in
amplifier-bundle/tools/ - Validating all recipes against the native runner
- Failing loudly when a recipe depends on an unavailable helper
Related¶
- amplihack recipe — CLI reference for the
recipesubcommand - Recipe Runner Logging — Progress, heartbeat, snippets, and JSON schema
- Recipe Execution Flow — Step-by-step execution semantics
- Recipe Executor Environment — Environment variables for recipe steps