update
magicka-merge-gates / advisory-fast (push) Has been skipped
magicka-merge-gates / merge-gates (push) Failing after 38s

This commit is contained in:
2026-06-21 19:21:48 -07:00
parent 2fe989bcb3
commit 659544f0b2
16 changed files with 12375 additions and 419 deletions
+27
View File
@@ -0,0 +1,27 @@
//! Freeze the committed replay corpus. Run this only as a deliberate, reviewed
//! migration when the engine semantics legitimately change.
//!
//! ```text
//! cargo run --release -p replay_corpus --bin freeze -- [count]
//! ```
fn main() {
let count: usize = std::env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(600);
let base = replay_corpus::DEFAULT_BASE_SEED;
match replay_corpus::freeze_to_disk(count, base) {
Ok(n) => {
println!(
"froze {} replay cases to {}",
n,
replay_corpus::corpus_path().display()
);
}
Err(e) => {
eprintln!("failed to freeze corpus: {e}");
std::process::exit(1);
}
}
}
+159 -29
View File
@@ -1,12 +1,24 @@
//! `replay_corpus` — every case is permanent and must replay bit-for-bit.
//! A replay case stores the seeds plus the three canonical hashes (trace,
//! delta, future). Replaying regenerates the case deterministically from its
//! master seed, re-executes the reference runtime, and asserts zero hash drift.
//!
//! The corpus is **persisted to a committed file** (`corpus/replay_corpus.tsv`).
//! Replay loads the expected hashes from that file — produced by an earlier
//! `freeze` run — regenerates the case deterministically from its master seed,
//! re-executes the reference runtime, and asserts zero drift against the stored
//! expectation. Because the expectation is read from disk rather than recomputed
//! and compared to itself in the same run, drift is genuinely possible: any
//! change to the engine that alters a hash makes the committed expectation and
//! the fresh execution disagree, and CI fails. (Proven by the negative-control
//! test, which corrupts a stored hash and checks the drift is detected.)
use generators::generate_accepted_case;
use reference_runtime::{execute, EngineConfig, ResolutionInput};
use std::path::PathBuf;
use world_model::Hash;
/// Format version of the persisted corpus file. Bump only with a deliberate,
/// reviewed migration of the committed corpus.
pub const CORPUS_VERSION: u32 = 1;
/// A permanent replay case (per spec) plus the master seed needed to
/// regenerate the full case deterministically.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -22,8 +34,7 @@ pub struct ReplayCase {
}
fn input_for(master_seed: u64) -> (ResolutionInput, u64, u64, u64, u64) {
let (case, accepted_seed) = generate_accepted_case(master_seed);
let _ = accepted_seed;
let (case, _accepted_seed) = generate_accepted_case(master_seed);
let input = ResolutionInput {
world: case.world.clone(),
program: case.program.clone(),
@@ -40,7 +51,13 @@ fn input_for(master_seed: u64) -> (ResolutionInput, u64, u64, u64, u64) {
)
}
/// Build a single replay case from a master seed.
/// Master seed for the `i`-th corpus case (stable, deterministic).
pub fn master_seed_for(base_seed: u64, i: usize) -> u64 {
base_seed ^ (i as u64).wrapping_mul(0x9e3779b97f4a7c15)
}
/// Build a single replay case from a master seed using the *current* reference.
/// Used when freezing the corpus to disk.
pub fn build_case(master_seed: u64) -> ReplayCase {
let (input, ws, ps, cs, prs) = input_for(master_seed);
let r = execute(&EngineConfig::reference(), &input);
@@ -56,13 +73,93 @@ pub fn build_case(master_seed: u64) -> ReplayCase {
}
}
/// Build a replay corpus of `n` cases.
/// Build an in-memory corpus of `n` cases (used by `freeze`).
pub fn build_corpus(n: usize, base_seed: u64) -> Vec<ReplayCase> {
(0..n)
.map(|i| build_case(base_seed ^ (i as u64).wrapping_mul(0x9e3779b97f4a7c15)))
.collect()
(0..n).map(|i| build_case(master_seed_for(base_seed, i))).collect()
}
// --- Persistence. -----------------------------------------------------------
/// Path to the committed corpus file, anchored to this crate.
pub fn corpus_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("corpus/replay_corpus.tsv")
}
/// The default base seed the committed corpus is frozen with.
pub const DEFAULT_BASE_SEED: u64 = 0x5EED;
/// Serialize a corpus to the on-disk TSV format (with a provenance header).
pub fn serialize_corpus(corpus: &[ReplayCase]) -> String {
let mut s = String::new();
s.push_str(&format!("# magicka-replay-corpus v{} cases={}\n", CORPUS_VERSION, corpus.len()));
s.push_str("master\tworld\tprogram\tcontract\tperturb\ttrace\tdelta\tfuture\n");
for c in corpus {
s.push_str(&format!(
"{:016x}\t{:016x}\t{:016x}\t{:016x}\t{:016x}\t{:016x}\t{:016x}\t{:016x}\n",
c.master_seed,
c.world_seed,
c.program_seed,
c.contract_seed,
c.perturbation_seed,
c.expected_trace_hash.0,
c.expected_delta_hash.0,
c.expected_future_hash.0,
));
}
s
}
fn parse_hex(s: &str) -> Option<u64> {
u64::from_str_radix(s.trim(), 16).ok()
}
/// Parse a corpus from the on-disk TSV format.
pub fn parse_corpus(text: &str) -> Vec<ReplayCase> {
let mut out = Vec::new();
for line in text.lines() {
if line.starts_with('#') || line.starts_with("master") || line.trim().is_empty() {
continue;
}
let f: Vec<&str> = line.split('\t').collect();
if f.len() != 8 {
continue;
}
let vals: Option<Vec<u64>> = f.iter().map(|x| parse_hex(x)).collect();
if let Some(v) = vals {
out.push(ReplayCase {
master_seed: v[0],
world_seed: v[1],
program_seed: v[2],
contract_seed: v[3],
perturbation_seed: v[4],
expected_trace_hash: Hash(v[5]),
expected_delta_hash: Hash(v[6]),
expected_future_hash: Hash(v[7]),
});
}
}
out
}
/// Load the committed corpus from disk.
pub fn load_persisted_corpus() -> std::io::Result<Vec<ReplayCase>> {
let text = std::fs::read_to_string(corpus_path())?;
Ok(parse_corpus(&text))
}
/// Freeze a fresh corpus of `n` cases to the committed file.
pub fn freeze_to_disk(n: usize, base_seed: u64) -> std::io::Result<usize> {
let corpus = build_corpus(n, base_seed);
let path = corpus_path();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(&path, serialize_corpus(&corpus))?;
Ok(corpus.len())
}
// --- Replay verification (against persisted expectations). ------------------
/// A single replay verification outcome.
#[derive(Clone, Copy, Debug)]
pub struct ReplayDrift {
@@ -78,15 +175,16 @@ impl ReplayDrift {
}
}
/// Replay one case and check for drift.
pub fn replay(case: &ReplayCase) -> ReplayDrift {
let (input, ..) = input_for(case.master_seed);
/// Replay one *stored* case: regenerate it and compare a fresh reference run to
/// the expectation read from disk.
pub fn replay_against_stored(stored: &ReplayCase) -> ReplayDrift {
let (input, ..) = input_for(stored.master_seed);
let r = execute(&EngineConfig::reference(), &input);
ReplayDrift {
master_seed: case.master_seed,
trace_ok: r.trace.canonical_hash() == case.expected_trace_hash,
delta_ok: r.delta.hash() == case.expected_delta_hash,
future_ok: r.replay.future_hash == case.expected_future_hash,
master_seed: stored.master_seed,
trace_ok: r.trace.canonical_hash() == stored.expected_trace_hash,
delta_ok: r.delta.hash() == stored.expected_delta_hash,
future_ok: r.replay.future_hash == stored.expected_future_hash,
}
}
@@ -96,20 +194,21 @@ pub struct ReplayReport {
pub total: usize,
pub deterministic: usize,
pub drift: Vec<u64>,
pub loaded_from_disk: bool,
}
impl ReplayReport {
pub fn passed(&self, minimum: usize) -> bool {
self.drift.is_empty() && self.total >= minimum
self.loaded_from_disk && self.drift.is_empty() && self.total >= minimum
}
}
/// Verify the whole corpus replays deterministically.
/// Verify a corpus (already loaded from disk) replays without drift.
pub fn verify_corpus(corpus: &[ReplayCase]) -> ReplayReport {
let mut drift = Vec::new();
let mut deterministic = 0;
for case in corpus {
let d = replay(case);
let d = replay_against_stored(case);
if d.ok() {
deterministic += 1;
} else {
@@ -120,6 +219,20 @@ pub fn verify_corpus(corpus: &[ReplayCase]) -> ReplayReport {
total: corpus.len(),
deterministic,
drift,
loaded_from_disk: true,
}
}
/// Load the committed corpus and verify it. The single entry point CI uses.
pub fn verify_persisted_corpus() -> ReplayReport {
match load_persisted_corpus() {
Ok(corpus) => verify_corpus(&corpus),
Err(_) => ReplayReport {
total: 0,
deterministic: 0,
drift: Vec::new(),
loaded_from_disk: false,
},
}
}
@@ -128,18 +241,35 @@ mod tests {
use super::*;
#[test]
fn replay_is_deterministic_zero_drift() {
let corpus = build_corpus(80, 0x5EED);
fn committed_corpus_loads_and_replays_without_drift() {
let corpus = load_persisted_corpus().expect("committed corpus must exist; run `freeze`");
assert!(!corpus.is_empty(), "committed corpus is empty");
let report = verify_corpus(&corpus);
assert_eq!(report.total, 80);
assert_eq!(report.deterministic, 80);
assert!(report.drift.is_empty());
assert!(report.drift.is_empty(), "drift in committed corpus: {:?}", report.drift);
assert_eq!(report.deterministic, report.total);
}
#[test]
fn case_hashes_are_stable() {
let a = build_case(123);
let b = build_case(123);
assert_eq!(a, b);
fn serialize_roundtrips() {
let corpus = build_corpus(20, 0x1234);
let text = serialize_corpus(&corpus);
let parsed = parse_corpus(&text);
assert_eq!(corpus, parsed);
}
/// Negative control: a corrupted stored expectation is detected as drift.
/// Proves the replay gate is not vacuous.
#[test]
fn corrupted_expectation_is_detected() {
let mut corpus = build_corpus(10, 0x9999);
// Flip one stored hash — as if the committed corpus disagreed with the
// engine. Replay must flag it.
corpus[3].expected_trace_hash = Hash(corpus[3].expected_trace_hash.0 ^ 0xdead_beef);
let report = verify_corpus(&corpus);
assert!(
!report.drift.is_empty(),
"replay failed to detect a corrupted expectation"
);
assert!(report.drift.contains(&corpus[3].master_seed));
}
}