Replay and portable artifacts
evalctl has two distinct “replay” ideas: execution replay (re-run the failed cases) and artifact replay (re-score a copied run directory without re-running anything).
Execution replay
evalctl replay --failed <run-id> --acknowledge-unsandboxed-runner --json
Like run, replay re-executes local runner and scorer commands, so it
requires the invoker’s acknowledgment (--acknowledge-unsandboxed-runner or
EVALCTL_ACKNOWLEDGE_UNSANDBOXED_RUNNER=1) or it refuses with exit 2. Artifact
replay (below) only re-scores stored artifacts and needs no acknowledgment.
replay --failed selects failed/errored cases from the recomputed report
projection of the source run — not from score.json or the stored manifest
status. It re-runs only those cases against the current suite and writes a fresh
partial run whose manifest replayed_from names the source run.
--run-id <NEW>names the destination run; it never resolves the source.- Pass
--suite <suite-or-path>when replaying a copied run, or when the suite can’t be resolved from the manifest suite name. W_NOTHING_TO_REPLAYmeans there were no failed/errored cases;W_REPLAY_CASE_ABSENTmeans a selected case is missing from the current suite.
Typical loop: fix the runner or fixture, then replay only what failed instead of re-running the whole suite.
Artifact replay
The run directory is portable. Copy it anywhere and re-score it:
evalctl report --run-dir <copied-run-dir> --format json
evalctl recomputes scores from the report artifacts. Artifact replay does not
require run.json, .reservation.json, .spoolctl.db, state.json, or
job.json — only the report artifacts. This is what makes a run reviewable by
another agent or teammate who never had your shell history.
Determinism
The plain synchronous report_hash is byte-stable across versions for the same
inputs (it stayed identical from v0.2 to v0.3). SOURCE_DATE_EPOCH controls the
run’s created_ts, so manifest-shape parity is reproducible. Durability sidecars
are excluded from report_hash by design.