From baa6c4e967eb52f272854912eaba995d7cf95ded Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 23:50:05 +0000 Subject: [PATCH] =?UTF-8?q?gates:=20verdict=20pipeline=20=E2=80=94=20gates?= =?UTF-8?q?=20+=20bench=20+=20ledger=20row,=20pristine-rebuilt=20baselines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds crates/verdict ('just verdict'): runs the core gates on the candidate binary, then an interleaved A/B bench vs the baseline with the CI-lower-bound rule, and writes the append-only ledger row with per-side median CIs, workload statement, and env fingerprint. Gate failure short-circuits to rejected-gate; --needs-human-review caps an accepted verdict (never upgrades). CPU pinning moves into accept.toml (pin_prefix, machine-specific, applied to both sides identically). The smoke test caught a real trust gap: pointing --baseline-bin at the workspace build (phase1-verdict-smoke-001) measured patched-vs-patched and returned null, because gate runs rebuild the workspace binary from the patched tree. Fix: --rebuild-baseline stashes the patch, builds the baseline into isolated targets//baseline/, and restores the patch — SPEC 3.1's pristine-checkout rule, now enforced by the harness. Re-run (phase1-verdict-smoke-002) reproduces the Phase 0 result mechanically: +10.4% median, 95% CI [1.0932, 1.1137], accepted. Both smoke rows are in the ledger, including the null one. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2 --- .gitignore | 1 + CLAUDE.md | 1 + Cargo.lock | 12 ++ Cargo.toml | 1 + config/accept.toml | 5 + crates/bench-runner/src/config.rs | 6 + crates/bench-runner/src/stats.rs | 27 ++++ crates/diff-test/src/lib.rs | 1 + crates/diff-test/src/main.rs | 71 ++------- crates/diff-test/src/orchestrate.rs | 78 ++++++++++ crates/diff-test/src/target.rs | 9 ++ crates/verdict/Cargo.toml | 15 ++ crates/verdict/src/main.rs | 220 ++++++++++++++++++++++++++++ justfile | 9 ++ results/ledger.sqlite | Bin 24576 -> 32768 bytes 15 files changed, 394 insertions(+), 62 deletions(-) create mode 100644 crates/diff-test/src/orchestrate.rs create mode 100644 crates/verdict/Cargo.toml create mode 100644 crates/verdict/src/main.rs diff --git a/.gitignore b/.gitignore index 6cc2103..87a9738 100644 --- a/.gitignore +++ b/.gitignore @@ -234,3 +234,4 @@ targets/comrak/asan*/ targets/tokei/candidate-*/ targets/tokei/asan/ !results/calibration/*.json +targets/*/baseline/ diff --git a/CLAUDE.md b/CLAUDE.md index 253cb9c..216fab2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,6 +56,7 @@ just compare # interleaved A/B of two shell commands, bootstrap C just gates # corpus pin + upstream tests + golden replay just pin-check # verify corpus MANIFEST.sha256 just calibrate # automated A/A + regression-injection calibration +just verdict ... # gates + bench vs pristine-rebuilt baseline + ledger row ``` ## Definition of done — one optimization attempt diff --git a/Cargo.lock b/Cargo.lock index 8710e86..4daf4fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -558,6 +558,18 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "verdict" +version = "0.1.0" +dependencies = [ + "anyhow", + "bench-runner", + "clap", + "diff-test", + "ledger", + "serde_json", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index a0f93b8..f52d276 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "crates/diff-test", "crates/ledger", "crates/report", + "crates/verdict", ] [workspace.package] diff --git a/config/accept.toml b/config/accept.toml index 5da4e72..8497e82 100644 --- a/config/accept.toml +++ b/config/accept.toml @@ -18,3 +18,8 @@ confidence = 0.95 # Fixed PRNG seed so calibration runs are reproducible. bootstrap_seed = 1887343589 + +# Machine-specific CPU pinning applied identically to both bench sides. +# Calibration evidence for this machine shows pinning tightens the A/A +# CI ~5x (results/calibration/). Empty string disables. +pin_prefix = "taskset -c 2" diff --git a/crates/bench-runner/src/config.rs b/crates/bench-runner/src/config.rs index 437ff91..6f6d822 100644 --- a/crates/bench-runner/src/config.rs +++ b/crates/bench-runner/src/config.rs @@ -18,6 +18,11 @@ pub struct AcceptConfig { pub confidence: f64, /// PRNG seed for the bootstrap (reproducible calibration). pub bootstrap_seed: u64, + /// Machine-specific command prefix for CPU pinning (e.g. + /// "taskset -c 2"); empty disables pinning. Applied by verdict to + /// both sides identically. + #[serde(default)] + pub pin_prefix: String, } impl Default for AcceptConfig { @@ -29,6 +34,7 @@ impl Default for AcceptConfig { bootstrap_iters: 10_000, confidence: 0.95, bootstrap_seed: 0x707E_17E5, + pin_prefix: String::new(), } } } diff --git a/crates/bench-runner/src/stats.rs b/crates/bench-runner/src/stats.rs index e95180b..53576e0 100644 --- a/crates/bench-runner/src/stats.rs +++ b/crates/bench-runner/src/stats.rs @@ -80,10 +80,37 @@ pub fn bootstrap_ratio_ci( } } +/// Percentile-method bootstrap CI of a single sample's median, for +/// per-side evidence in ledger rows. +pub fn bootstrap_median_ci(sample: &[f64], iters: usize, confidence: f64, seed: u64) -> (f64, f64, f64) { + assert!(sample.len() >= 2 && (0.0..1.0).contains(&confidence)); + let mut rng = XorShift64::new(seed); + let mut medians = Vec::with_capacity(iters); + let mut resample = vec![0.0; sample.len()]; + for _ in 0..iters { + for slot in resample.iter_mut() { + *slot = sample[rng.index(sample.len())]; + } + medians.push(median(&resample)); + } + medians.sort_by(|a, b| a.partial_cmp(b).expect("NaN median")); + let alpha = (1.0 - confidence) / 2.0; + let lo = medians[((iters as f64) * alpha).floor() as usize]; + let hi = medians[(((iters as f64) * (1.0 - alpha)).ceil() as usize).min(iters - 1)]; + (median(sample), lo, hi) +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn median_ci_brackets_median() { + let s: Vec = (0..40).map(|i| 1.0 + 0.01 * ((i * 7) % 11) as f64).collect(); + let (m, lo, hi) = bootstrap_median_ci(&s, 1000, 0.95, 9); + assert!(lo <= m && m <= hi); + } + #[test] fn median_odd_and_even() { assert_eq!(median(&[3.0, 1.0, 2.0]), 2.0); diff --git a/crates/diff-test/src/lib.rs b/crates/diff-test/src/lib.rs index 0c44a8c..131e8db 100644 --- a/crates/diff-test/src/lib.rs +++ b/crates/diff-test/src/lib.rs @@ -7,6 +7,7 @@ //! per-target equivalence policy parser (byte-identical unless a target //! declares an explicit FP tolerance). +pub mod orchestrate; pub mod pin; pub mod target; pub mod policy; diff --git a/crates/diff-test/src/main.rs b/crates/diff-test/src/main.rs index 678356b..8dc5f77 100644 --- a/crates/diff-test/src/main.rs +++ b/crates/diff-test/src/main.rs @@ -1,15 +1,13 @@ -//! Gate orchestration: corpus pin → upstream tests → golden replay. -//! Refuses to run anything if the corpus manifest mismatches (SPEC §3.2). +//! Gate orchestration CLI: corpus pin → upstream tests → golden replay. //! Differential fuzz and sanitizers remain per-attempt manual steps in //! Phase 1 and are reported as skipped with a reason. use anyhow::Result; use clap::Parser; -use diff_test::target::{expected_golden_hash, TargetSpec}; -use diff_test::{all_passed, pin, GateLayer, GateOutcome}; -use sha2::{Digest, Sha256}; +use diff_test::orchestrate::run_core_gates; +use diff_test::target::TargetSpec; +use diff_test::{all_passed, GateOutcome}; use std::path::PathBuf; -use std::process::{Command, Stdio}; #[derive(Parser)] #[command(name = "diff-test", about = "hotpath equivalence-gate orchestrator")] @@ -21,67 +19,16 @@ struct Cli { root: PathBuf, } -fn sh(root: &PathBuf, cmd: &str) -> Result { - Ok(Command::new("sh") - .arg("-c") - .arg(cmd) - .current_dir(root) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output()?) -} - fn main() -> Result<()> { let cli = Cli::parse(); let root = cli.root.canonicalize()?; let spec = TargetSpec::load(&root, &cli.target)?; - let mut results: Vec<(GateLayer, GateOutcome)> = Vec::new(); - - // Corpus pin is a precondition, not a gate: mismatch = refuse to run. - let verified = pin::verify_manifest(&root.join(&spec.corpus.manifest), &root.join(&spec.corpus.root))?; - println!("corpus pin: {} files verified against {}", verified, spec.corpus.manifest.display()); - - print!("upstream tests ... "); - let t = sh(&root, &spec.gates.tests)?; - let tests_outcome = if t.status.success() { - println!("PASS"); - GateOutcome::Passed - } else { - println!("FAIL"); - GateOutcome::Failed { - detail: String::from_utf8_lossy(&t.stderr).chars().take(2000).collect(), - } - }; - results.push((GateLayer::UpstreamTests, tests_outcome)); - - print!("golden replay ... "); - let golden_cmd = spec.gates.golden.replace("{binary}", &spec.build.binary); - let g = sh(&root, &golden_cmd)?; - let expected = expected_golden_hash(&root.join(&spec.corpus.golden_sha256))?; - let actual = format!("{:x}", Sha256::digest(&g.stdout)); - let golden_outcome = if g.status.success() && actual == expected { - println!("PASS (stdout sha256 = pinned golden)"); - GateOutcome::Passed - } else { - println!("FAIL"); - GateOutcome::Failed { - detail: format!("exit={:?} expected={expected} actual={actual}", g.status.code()), - } - }; - results.push((GateLayer::GoldenReplay, golden_outcome)); - - results.push(( - GateLayer::DifferentialFuzz, - GateOutcome::Skipped { reason: "per-attempt manual step in Phase 1 (needs old/new pair)".into() }, - )); - results.push(( - GateLayer::Sanitizers, - GateOutcome::Skipped { reason: "per-attempt manual step in Phase 1 (nightly toolchain)".into() }, - )); - + let results = run_core_gates(&root, &spec, &spec.build.binary)?; for (layer, outcome) in &results { - if let GateOutcome::Skipped { reason } = outcome { - println!("{layer:?}: skipped — {reason}"); + match outcome { + GateOutcome::Passed => println!("{layer:?}: PASS"), + GateOutcome::Failed { detail } => println!("{layer:?}: FAIL — {detail}"), + GateOutcome::Skipped { reason } => println!("{layer:?}: skipped — {reason}"), } } if all_passed(&results) { diff --git a/crates/diff-test/src/orchestrate.rs b/crates/diff-test/src/orchestrate.rs new file mode 100644 index 0000000..b140b77 --- /dev/null +++ b/crates/diff-test/src/orchestrate.rs @@ -0,0 +1,78 @@ +//! Core gate sequence, callable from the diff-test CLI and from verdict: +//! corpus pin (refuses on mismatch) → upstream tests → golden replay. + +use crate::target::{expected_golden_hash, TargetSpec}; +use crate::{pin, GateLayer, GateOutcome}; +use sha2::{Digest, Sha256}; +use std::path::Path; +use std::process::{Command, Stdio}; + +fn sh(root: &Path, cmd: &str) -> anyhow::Result { + Ok(Command::new("sh") + .arg("-c") + .arg(cmd) + .current_dir(root) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output()?) +} + +/// Run the mechanical gates for `target`, testing `binary` (repo-root- +/// relative) in the golden replay. Corpus-pin mismatch is an error, not +/// a gate failure: nothing runs on a tampered corpus. +pub fn run_core_gates( + root: &Path, + spec: &TargetSpec, + binary: &str, +) -> anyhow::Result> { + let verified = pin::verify_manifest( + &root.join(&spec.corpus.manifest), + &root.join(&spec.corpus.root), + )?; + eprintln!( + "corpus pin: {verified} files verified against {}", + spec.corpus.manifest.display() + ); + + let mut results = Vec::new(); + + let t = sh(root, &spec.gates.tests)?; + results.push(( + GateLayer::UpstreamTests, + if t.status.success() { + GateOutcome::Passed + } else { + GateOutcome::Failed { + detail: String::from_utf8_lossy(&t.stderr).chars().take(2000).collect(), + } + }, + )); + + let g = sh(root, &spec.gates.golden.replace("{binary}", binary))?; + let expected = expected_golden_hash(&root.join(&spec.corpus.golden_sha256))?; + let actual = format!("{:x}", Sha256::digest(&g.stdout)); + results.push(( + GateLayer::GoldenReplay, + if g.status.success() && actual == expected { + GateOutcome::Passed + } else { + GateOutcome::Failed { + detail: format!("exit={:?} expected={expected} actual={actual}", g.status.code()), + } + }, + )); + + results.push(( + GateLayer::DifferentialFuzz, + GateOutcome::Skipped { + reason: "per-attempt manual step in Phase 1 (needs old/new pair)".into(), + }, + )); + results.push(( + GateLayer::Sanitizers, + GateOutcome::Skipped { + reason: "per-attempt manual step in Phase 1 (nightly toolchain)".into(), + }, + )); + Ok(results) +} diff --git a/crates/diff-test/src/target.rs b/crates/diff-test/src/target.rs index e11b9bc..0b7a4f4 100644 --- a/crates/diff-test/src/target.rs +++ b/crates/diff-test/src/target.rs @@ -9,10 +9,19 @@ use std::path::{Path, PathBuf}; pub struct TargetSpec { pub source: Source, pub build: Build, + pub bench: Bench, pub gates: Gates, pub corpus: Corpus, } +#[derive(Debug, Deserialize)] +pub struct Bench { + /// Bench command; `{binary}` substituted per side. Run from repo root. + pub command: String, + /// Human-readable workload statement, printed with every number. + pub workload: String, +} + #[derive(Debug, Deserialize)] pub struct Source { pub repo: String, diff --git a/crates/verdict/Cargo.toml b/crates/verdict/Cargo.toml new file mode 100644 index 0000000..e3373b7 --- /dev/null +++ b/crates/verdict/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "verdict" +description = "One-shot attempt verdict: equivalence gates + interleaved A/B bench + append-only ledger row" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +anyhow.workspace = true +bench-runner = { path = "../bench-runner" } +clap.workspace = true +diff-test = { path = "../diff-test" } +ledger = { path = "../ledger" } +serde_json.workspace = true diff --git a/crates/verdict/src/main.rs b/crates/verdict/src/main.rs new file mode 100644 index 0000000..999ad3c --- /dev/null +++ b/crates/verdict/src/main.rs @@ -0,0 +1,220 @@ +//! `just verdict`: the whole attempt pipeline in one command. +//! Gates on the candidate → interleaved A/B bench vs the baseline → +//! verdict by the CI-lower-bound rule → append-only ledger row. +//! Any gate failure is rejected-gate and the bench is skipped; the +//! --needs-human-review flag caps the verdict for FP/concurrency/ +//! sanitizer-flagged attempts (SPEC §8) — it never upgrades one. + +use anyhow::Result; +use bench_runner::{config::AcceptConfig, decide, exec, fingerprint::EnvFingerprint, stats}; +use clap::Parser; +use diff_test::{all_passed, orchestrate::run_core_gates, target::TargetSpec, GateLayer, GateOutcome}; +use ledger::{Attempt, BenchEvidence, GateResults, Ledger, Verdict}; +use std::path::PathBuf; + +#[derive(Parser)] +#[command(name = "verdict", about = "gates + bench + ledger row for one optimization attempt")] +struct Cli { + /// Target name under targets/. + target: String, + /// Pristine baseline binary (repo-root-relative). Prefer + /// --rebuild-baseline: workspace paths get silently rebuilt from the + /// patched tree by test runs (observed: phase1-verdict-smoke-001). + #[arg(long, conflicts_with = "rebuild_baseline")] + baseline_bin: Option, + /// Rebuild the baseline from the pristine checkout (patch stashed, + /// built to targets//baseline/, patch restored). SPEC §3.1. + #[arg(long)] + rebuild_baseline: bool, + /// Candidate binary (repo-root-relative); also used in golden replay. + #[arg(long)] + candidate_bin: String, + #[arg(long)] + run_id: String, + #[arg(long)] + playbook_class: u8, + #[arg(long)] + hypothesis: String, + #[arg(long)] + hotspot: String, + /// Unified-diff file recorded in the ledger (optional for build-config attempts). + #[arg(long)] + patch_file: Option, + /// Force the needs-human-review verdict tier (FP/concurrency/UB-adjacent). + #[arg(long)] + needs_human_review: bool, + #[arg(long, default_value = "results/ledger.sqlite")] + db: PathBuf, + #[arg(long, default_value = "config/accept.toml")] + config: PathBuf, + #[arg(long, default_value = ".")] + root: PathBuf, +} + +/// Stash any workspace patch, build the baseline into the isolated +/// targets//baseline/ dir, restore the patch. Serialized within +/// this process — nothing else may touch the tree while this runs. +fn rebuild_pristine_baseline( + root: &std::path::Path, + target: &str, + spec: &TargetSpec, +) -> Result { + let ws = format!("targets/{target}/workspace"); + let dirty = !std::process::Command::new("git") + .args(["-C", &ws, "status", "--porcelain"]) + .output()? + .stdout + .is_empty(); + if dirty { + anyhow::ensure!( + std::process::Command::new("git") + .args(["-C", &ws, "stash", "--include-untracked", "-q"]) + .status()? + .success(), + "git stash failed" + ); + } + let build = std::process::Command::new("sh") + .arg("-c") + .arg(&spec.build.baseline) + .current_dir(root) + .env("CARGO_TARGET_DIR", root.join(format!("targets/{target}/baseline"))) + .status(); + if dirty { + anyhow::ensure!( + std::process::Command::new("git") + .args(["-C", &ws, "stash", "pop", "-q"]) + .status()? + .success(), + "git stash pop failed — workspace patch may be stranded in stash" + ); + } + anyhow::ensure!(build?.success(), "baseline build failed"); + let bin_name = std::path::Path::new(&spec.build.binary) + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| anyhow::anyhow!("bad build.binary path"))?; + let baseline = format!("targets/{target}/baseline/release/{bin_name}"); + anyhow::ensure!(root.join(&baseline).exists(), "baseline binary missing: {baseline}"); + println!("baseline rebuilt from pristine checkout: {baseline}"); + Ok(baseline) +} + +fn now_utc() -> String { + let out = std::process::Command::new("date") + .args(["-u", "+%Y-%m-%dT%H:%M:%SZ"]) + .output(); + match out { + Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().to_string(), + _ => "unknown".to_string(), + } +} + +fn main() -> Result<()> { + let cli = Cli::parse(); + let root = cli.root.canonicalize()?; + std::env::set_current_dir(&root)?; + let spec = TargetSpec::load(&root, &cli.target)?; + let cfg = AcceptConfig::load(&root.join(&cli.config))?; + let started = std::time::Instant::now(); + + let baseline_bin = if cli.rebuild_baseline { + rebuild_pristine_baseline(&root, &cli.target, &spec)? + } else { + cli.baseline_bin.clone().ok_or_else(|| { + anyhow::anyhow!("pass --rebuild-baseline (preferred) or --baseline-bin") + })? + }; + + // Gates run on the candidate binary. + let gates = run_core_gates(&root, &spec, &cli.candidate_bin)?; + for (layer, outcome) in &gates { + match outcome { + GateOutcome::Passed => println!("{layer:?}: PASS"), + GateOutcome::Failed { detail } => println!("{layer:?}: FAIL — {detail}"), + GateOutcome::Skipped { reason } => println!("{layer:?}: skipped — {reason}"), + } + } + let gate_results = GateResults { + upstream_tests: matches!(gates[0].1, GateOutcome::Passed), + golden_replay: matches!(gates[1].1, GateOutcome::Passed), + fuzz_iters: 0, + fuzz_divergence: false, + sanitizers_clean: false, + }; + debug_assert!(matches!(gates[0].0, GateLayer::UpstreamTests)); + + let pin = &cfg.pin_prefix; + let wrap = |bin: &str| { + let cmd = spec.bench.command.replace("{binary}", bin); + if pin.is_empty() { format!("sh -c \"{cmd}\"") } else { format!("{pin} sh -c \"{cmd}\"") } + }; + + let (verdict, bench) = if !all_passed(&gates) { + println!("verdict: rejected-gate (bench skipped)"); + (Verdict::RejectedGate, None) + } else { + println!( + "bench: {} measured + {} warm-up runs/side, interleaved, pin='{}'", + cfg.runs_per_side, cfg.warmup_runs, pin + ); + let samples = exec::run_interleaved( + &wrap(&baseline_bin), &wrap(&cli.candidate_bin), + cfg.runs_per_side, cfg.warmup_runs, 0.0, + )?; + let ratio = stats::bootstrap_ratio_ci( + &samples.baseline_s, &samples.candidate_s, + cfg.bootstrap_iters, cfg.confidence, cfg.bootstrap_seed, + ); + let (bm, blo, bhi) = stats::bootstrap_median_ci( + &samples.baseline_s, cfg.bootstrap_iters, cfg.confidence, cfg.bootstrap_seed, + ); + let (cm, clo, chi) = stats::bootstrap_median_ci( + &samples.candidate_s, cfg.bootstrap_iters, cfg.confidence, cfg.bootstrap_seed, + ); + println!( + "speedup (baseline/candidate): median {:.4}, {:.0}% CI [{:.4}, {:.4}] | workload: {}", + ratio.median, cfg.confidence * 100.0, ratio.lo, ratio.hi, spec.bench.workload + ); + let mut v = decide(ratio, cfg.threshold); + if cli.needs_human_review && v == Verdict::Accepted { + v = Verdict::NeedsHumanReview; + } + let fp = EnvFingerprint::collect(pin, "system-default"); + (v, Some(BenchEvidence { + baseline_median: bm, baseline_ci: (blo, bhi), + candidate_median: cm, candidate_ci: (clo, chi), + speedup_median: ratio.median, speedup_ci: (ratio.lo, ratio.hi), + env_fingerprint: serde_json::json!({ + "fingerprint": fp, + "workload": spec.bench.workload, + "target_commit": spec.source.commit, + "gates_detail": "fuzz/sanitizers per-attempt manual in Phase 1", + }), + })) + }; + + let patch = match &cli.patch_file { + Some(p) => std::fs::read_to_string(p)?, + None => "(no source patch)".into(), + }; + let attempt = Attempt { + run_id: cli.run_id.clone(), + timestamp: now_utc(), + target: cli.target.clone(), + target_commit: spec.source.commit.clone(), + phase: 1, + hotspot: cli.hotspot, + playbook_class: cli.playbook_class, + hypothesis: cli.hypothesis, + patch, + gates: gate_results, + bench, + verdict, + tokens_spent: 0, + wall_time_s: started.elapsed().as_secs_f64(), + }; + Ledger::open(&root.join(&cli.db))?.record(&attempt)?; + println!("verdict: {} (ledger row {})", attempt.verdict.as_str(), attempt.run_id); + Ok(()) +} diff --git a/justfile b/justfile index f0fcc26..b394e2e 100644 --- a/justfile +++ b/justfile @@ -38,3 +38,12 @@ calibrate cmd out sessions="20": # upstream tests -> golden replay. Fuzz/sanitizers stay per-attempt. gates target: cargo run -p diff-test -- {{target}} + +# One-shot attempt verdict: gates on candidate -> interleaved A/B vs a +# pristine-rebuilt baseline -> ledger row. Extra flags pass through +# (--patch-file, --needs-human-review, --baseline-bin). +verdict target candidate run_id class hypothesis hotspot *flags: + cargo run -p verdict -- {{target}} --rebuild-baseline \ + --candidate-bin "{{candidate}}" --run-id "{{run_id}}" \ + --playbook-class {{class}} --hypothesis "{{hypothesis}}" \ + --hotspot "{{hotspot}}" {{flags}} diff --git a/results/ledger.sqlite b/results/ledger.sqlite index de577b7ab1d5dbb1ff42a7ef8a91776d6fb308eb..90050fbddd6c7856a6e1533ad048038cd92fd829 100644 GIT binary patch literal 32768 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 IU~ixR00x*K4FCWD