execution¶
The execution block describes what to run and how cases are processed —
independent of the runner (which agent runtime) and the backend (Local,
Harbor, EvalHub — always a --runner CLI flag). It separates how many invocations
(mode) from what to execute (skill or prompt).
execution:
mode: case # per-case (default) or batch
skill: my-skill-name # skill to test (mutually exclusive with prompt)
arguments: "{prompt}" # resolved per case from input.yaml fields
# timeout: 3600 # per-invocation wall-clock seconds
# max_budget_usd: 5.0 # per-invocation cost cap
# parallelism: 4 # concurrent cases (case mode only)
# env:
# JIRA_TOKEN: $JIRA_TOKEN # $VAR resolved from the caller's environment
Fields¶
| Field | Type | Default | Notes |
|---|---|---|---|
mode |
case | batch |
case |
One invocation per case, or one invocation for all cases. |
skill |
string | "" |
Skill name to invoke (/skill-name). Mutually exclusive with prompt. |
prompt |
string | "" |
Direct prompt template — no skill wrapper. Mutually exclusive with skill. |
arguments |
string | "" |
Template resolved per case from input.yaml. See Templating. |
timeout |
int | null | null → 3600 |
Per-invocation wall-clock timeout in seconds. |
max_budget_usd |
float | null | null → 100.0 |
Per-invocation cost cap. |
parallelism |
int | null | null → 1 |
Max concurrent case executions (case mode only). |
env |
map | {} |
Env vars injected into each workspace's .claude/settings.json. See env. |
Nulls become harness defaults
timeout, max_budget_usd, and parallelism are optional in the config (None).
They only acquire their effective values (3600 s, $100, sequential) at run time.
Explicit checks preserve 0 — set max_budget_usd: 0 and you get a $0 cap, not
the default.
Choosing what to execute¶
flowchart TD
E[execution block] --> M{mode}
M -->|case| C[one invocation per case]
M -->|batch| B[one invocation, all cases via batch.yaml]
E --> W{skill or prompt?}
W -->|skill:| S[/skill-name + arguments/]
W -->|prompt:| P[direct prompt template]
mode (how many invocations) and skill/prompt (what to execute) are orthogonal —
any of the four combinations is valid. See
the execution model for the conceptual overview
and skill vs prompt for when to use each.
One invocation per case; arguments is resolved from each case's input.yaml.
One invocation processes all cases; the skill loops internally over batch.yaml.
Argument templating¶
arguments (skill mode) and prompt (prompt mode) support two mutually exclusive
placeholder styles. The style is auto-detected: if the template contains {{ or
{%, it is rendered as Jinja2; otherwise brace substitution is used.
Rendered with input bound to the case's input.yaml. Uses StrictUndefined, so a
missing field raises an error rather than rendering empty.
For genuinely optional fields, guard them explicitly:
The {prompt} shortcut
{prompt} is the conventional single-field template — /eval-analyze generates it
by default. In batch mode a bare {prompt} is filled from the first entry of the
workspace batch.yaml.
Don't mix the two styles
A template with any {{ … }} is treated as Jinja2 in full — bare {field} braces in
the same string are not substituted. Pick one style per template.
Injecting environment variables¶
execution.env writes variables into each case workspace's .claude/settings.json, so
they are visible to both the skill and its hooks. Values beginning with $
are resolved from the caller's environment; a $VAR that is unset is silently
omitted. Literal values pass through unchanged.
execution:
env:
JIRA_SERVER: http://localhost:8080 # literal
JIRA_TOKEN: $JIRA_TOKEN # resolved from os.environ["JIRA_TOKEN"]
execution.env vs runner.env
execution.env targets the workspace (available to the skill and its hooks).
runner.env targets the runner subprocess itself. Both support the
$VAR syntax.
Precedence¶
CLI flags on /eval-run (and execute.py) always override the config:
| Config field | CLI override | Falls back to |
|---|---|---|
execution.timeout |
--timeout |
3600 s |
execution.max_budget_usd |
--max-budget |
$100.0 |
execution.parallelism |
--parallelism |
sequential (1) |
execution.arguments / prompt |
--skill-args |
config value |
models.skill |
--model |
(required) |
Validation¶
These errors are raised at config load time (EvalConfig.from_yaml), not mid-run:
| Condition | Error |
|---|---|
mode not in case/batch |
execution.mode must be one of ['case', 'batch'] |
Both skill and prompt set |
execution.skill and execution.prompt are mutually exclusive |
| Required template field missing at run time | Missing required field in template: … |
Deprecated top-level skill:
A top-level skill: (outside the execution block) still works but is
auto-normalized into execution.skill with a deprecation warning. Author new configs
with execution.skill.
Gotchas¶
parallelismis case-mode only. Batch mode is a single invocation, so there is nothing to parallelize. Withrunner.workspace_mode: repoparallelism is forced to1(all cases share the repo checkout).- Per-case hooks need case/prompt mode.
hooks.before_each/after_eachare ignored in batch mode (a warning is emitted); usebefore_all/after_allthere. See hooks. timeoutandmax_budget_usdare per invocation, not per run. In case mode they apply to each case; the run's total budget is their sum.
See also¶
- eval.yaml reference — all top-level keys
- runner — the agent runtime and
runner.env/workspace_mode - models — model-per-role precedence
- Execution model — case vs batch, skill vs prompt
- CLI —
--model,--timeout,--parallelism, and other flags