Scenario runner
The runner (crates/scenario-runner) executes a Scenario through a
ScenarioContext and produces a classified ScenarioOutcome. It is the
only place a scenario’s operations touch the adapter services; packs, the
harness, and the CLI all go through it.
Responsibilities
- Lifecycle — validate → prepare → initialize → execute → observe → assert → invariants → classify → report → cleanup. The executor records a per-phase timing for each stage on the outcome (spec §67), so reports can decompose time instead of collapsing it.
- Isolation — the harness that wires the runner builds a fresh simulator per run, so a failed scenario cannot contaminate the next.
- Timeout & cancellation — a whole-run deadline derived from the configured clock, and a cancellation token the executor checks between operations.
- Controlled retries — a
RetryPolicythat only retries explicitly configured infrastructure-level failures. Deterministic failures never retry and never get hidden. - Cleanup — a
Cleanupchain that runs after the outcome is fixed; cleanup problems are recorded as observations, never silently dropped. - Hooks —
HookChainwithbefore/afterphase hooks for instrumentation. - Invariant evaluation — the executor runs the scenario’s declared
invariants through an
InvariantRegistryand records held/violated counts on the outcome.
Classification
The executor never returns “the scenario errored” for a failed expectation.
It classifies the run: expected rejections in negative/adversarial packs are
successful runs of a failing scenario, distinct from real failures (see
scenario-core’s failure model).
Determinism
The runner is clock-driven. The mock harness injects a fixed clock, so every phase timing and outcome is byte-deterministic. Real deployments inject a wall clock; the structure is identical.