mirror of
https://github.com/bdbrown4/peltier.io.git
synced 2026-11-07 10:58:28 +00:00
gates: verdict pipeline — gates + bench + ledger row, pristine-rebuilt baselines
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/<name>/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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2
This commit is contained in:
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<f64> = (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);
|
||||
|
||||
Reference in New Issue
Block a user