ADR 0001: First-class benchmark campaign orchestration¶
Status: Accepted
Date: 2026-04-17
Context: Benchmark comparison work (matrices, longitudinal reports, fingerprints) needs reproducible multi-run sweeps across suites, providers, scoring backends, execution modes, and browser configs—without ad hoc shell loops or undocumented local state.
Context¶
- Single-run harness (
alwm benchmark run) already emits a standard tree (cells/,matrices/,manifest.jsonasbenchmark_manifest). - Longitudinal tooling expects glob-addressable benchmark manifests under a stable directory layout.
- Sweeps must be git-reviewable: YAML definitions, JSON manifests, and Markdown summaries committed like other artifacts.
Decision¶
- Campaign definition —
BenchmarkCampaignDefinitionV1+schemas/v1/benchmark_campaign.schema.jsondescribe the Cartesian sweep (suites × provider refs × eval scoring × browser overrides, with optional execution-mode filter and registry override). - Campaign orchestrator —
run_benchmark_campaigninbenchmark/campaign_runner.pyexpands the sweep, runs each member withrun_benchmark, and writes: manifest.jsonat campaign root (BenchmarkCampaignManifest/benchmark_campaign_manifest)campaign-summary.json/campaign-summary.md(rollup for dashboards and humans)- Per-run trees under
runs/runNNNN/(same contract as a standalone benchmark run) - Fingerprints — The campaign root records
campaign_definition_fingerprintandcampaign_experiment_fingerprints(six per-axis hashes). Membercomparison_fingerprints(six axes, including prompt registry state) are copied onto eachBenchmarkCampaignRunEntryfor comparability checks across campaign rows. - Planning without execution —
dry_run=Truerecords planned run count, writescampaign-dry-run.json, and is exposed asalwm benchmark campaign run --dry-run(same entrypoint as execution; no separateplansubcommand).
Consequences¶
- Positive: One command produces a longitudinal-compatible directory;
alwm validateapplies to manifests and summaries; definitions stay in-repo and diff-friendly. - Negative: Large sweeps multiply disk and time; operators should use
--dry-runfirst and scopesuite_refs/ axes carefully.