Execution model
Execution is owned by crates/scenario-runner (introduced after
scenario-core). This document records the model the runner implements.
Lifecycle
Every scenario follows the same stages; a failure at any stage is classified with the stage it happened in:
- Discover — find the scenario through the registry.
- Validate — the scenario was already validated at build time; re-check against the current context (capabilities, environment, isolation).
- Prepare — prepare environment/fixtures; nothing here may run the scenario early.
- Initialize — build the
ScenarioContext, load initial state, seed the deterministic stream. - Execute — run the ordered operations through the context’s services.
- Observe — capture results as classified observations.
- Assert — evaluate the scenario’s assertions.
- Invariants — evaluate declared cross-operation invariants.
- Classify — produce the
ScenarioOutcomewith aStatusand, on failure, a classifiedFailure. - Report — hand the outcome to reporting.
- Cleanup — tear down in all paths.
Determinism and replay
- Every randomized scenario carries a
Seed; the seed is reported with the outcome. - Consumers derive isolated child seeds (
ScenarioContext::seed_for) so fixtures, generators, and sequences never interfere. - A dedicated replay command
(
crucible-scenarios replay --scenario <ID> --seed <SEED>) is planned; today determinism comes from the harness itself (fresh isolated simulator per run, fixed clock, validated definitions), so a re-run of the same scenario reproduces the same observations and outcome without a seed. - Replay output never contains secrets.
Retries
Retries are permitted only where a scenario explicitly allows them, and must never hide nondeterministic failures: for deterministic scenarios a retry is normally suspicious. Security-sensitive failures (authorization, privacy, unexpected acceptance, verification) must never be retried away.
Parallel execution vs. protocol concurrency
The model distinguishes parallel execution (multiple scenarios run in
parallel threads — an orchestration concern) from valid protocol
concurrency (operations racing against the same state — a scenario concern
requiring the concurrency capability). Parallel execution is not yet
implemented: the harness runs one isolated scenario at a time, and protocol
concurrency is modeled at the scenario level (a single-threaded surface
executes racing operations sequentially and asserts the outcome). Parallel
execution, when added, will never be assumed semantically safe on its own;
expected behavior stays defined per scenario.
Timeouts and cancellation
Each scenario may declare a positive timeout (milliseconds). A run exceeding
it is classified TIMEOUT at the executing stage. Runs may be cancelled,
classified CANCELLED. Both are distinct from FAIL and from ERROR
(harness problems) so reports can tell them apart.
Failure classification
FailureCategory distinguishes harness defects (scenario-definition, fixture,
environment, infrastructure) from findings about the system under test
(assertion, invariant, proof, verification, state, authorization, privacy,
compatibility, unexpected acceptance). Security-sensitive categories default
to elevated severities and are flagged, so they can never be buried.
Statuses
PASS, FAIL, SKIPPED, EXPECTED_FAILURE, ERROR, TIMEOUT,
CANCELLED. EXPECTED_FAILURE (the declared failure occurred exactly as
declared) counts as a pass; SKIPPED is the honest result of a capability or
environment mismatch, never a silent mis-execution.