diff --git a/contracts/witness/811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850.json b/contracts/witness/811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850.json index 13582518f0..a5088c39a3 100644 --- a/contracts/witness/811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850.json +++ b/contracts/witness/811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850.json @@ -1,7 +1,6 @@ { "census_id_set_sha256": "063d371c514532fccc38497ed2f0d85711be3c35061de05df561446f16e93d99", "relations_sha256": "811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850", - "reasoner_git_sha": "56ae1edeaf9f0761d3e0f9bf0754fb735e7c3d05", "checkable_n": 87, "result": { "kind": "model", @@ -9,6 +8,5 @@ "false": [] } }, - "pc_reasoner": "fired", - "cpu_ms": 2 + "pc_reasoner": "fired" } diff --git a/crates/aprender-contracts/src/ontology/witness.rs b/crates/aprender-contracts/src/ontology/witness.rs index 710eb478ef..2c1e62bf54 100644 --- a/crates/aprender-contracts/src/ontology/witness.rs +++ b/crates/aprender-contracts/src/ontology/witness.rs @@ -160,11 +160,13 @@ pub struct Witness { pub census_id_set_sha256: String, pub relations_sha256: String, /// `git rev-parse HEAD` where pv-sat ran, when it could ask. + #[serde(default, skip_serializing)] pub reasoner_git_sha: Option, pub checkable_n: usize, pub result: WitnessResult, /// `fired` — pv-sat's plant `A⇒B, B⇒C, contradicts(A,C)` yielded the core `[A, B, C]` before it wrote this. pub pc_reasoner: String, + #[serde(default, skip_serializing)] pub cpu_ms: u64, } diff --git a/crates/aprender-contracts/src/ontology/witness_tests.rs b/crates/aprender-contracts/src/ontology/witness_tests.rs index 1c21ee1362..eda50ce344 100644 --- a/crates/aprender-contracts/src/ontology/witness_tests.rs +++ b/crates/aprender-contracts/src/ontology/witness_tests.rs @@ -263,3 +263,39 @@ fn implication_adjacency_groups_targets_by_source() { assert_eq!(adj["B"], vec!["C"]); assert!(implication_adjacency(&ClauseSet::default()).is_empty()); } + +fn volatile_witness(sha: Option<&str>, cpu_ms: u64) -> Witness { + Witness { + census_id_set_sha256: "0".repeat(64), + relations_sha256: "1".repeat(64), + reasoner_git_sha: sha.map(str::to_string), + checkable_n: 3, + result: good_core(), + pc_reasoner: FIRED.into(), + cpu_ms, + } +} + +/// P5: a regen at another HEAD or with another timing must leave the committed file byte-identical, +/// or every PR that regenerates conflicts with every other on the shared witness. +#[test] +fn volatile_fields_are_never_written() { + let a = serde_json::to_string_pretty(&volatile_witness(Some("aaaa"), 1)).expect("serializes"); + let b = serde_json::to_string_pretty(&volatile_witness(Some("bbbb"), 2)).expect("serializes"); + assert_eq!(a, b); + assert!( + !a.contains("cpu_ms") && !a.contains("reasoner_git_sha"), + "{a}" + ); +} + +/// Witnesses already on disk carry the two fields; they must still be read. +#[test] +fn a_witness_with_the_old_volatile_fields_still_parses() { + let mut v = serde_json::to_value(volatile_witness(None, 0)).expect("serializes"); + v["cpu_ms"] = serde_json::json!(7); + v["reasoner_git_sha"] = serde_json::json!("abc"); + let w: Witness = serde_json::from_value(v).expect("old shape parses"); + assert_eq!(w.cpu_ms, 7); + assert_eq!(w.reasoner_git_sha.as_deref(), Some("abc")); +}