//! Believability regression harness (T-1083) — the D-245 acceptance-gate enforcer. //! //! D-245 makes "the nature layers read alive *anywhere*" the deliverable of the //! cascade's nature half. This harness is its repeatable instrument: for a set of //! validation bodies at fixed seeds it runs the real deterministic cascade, computes //! the [`BelievabilityReport`] (contrast + coherence — never marginal per-tile counts, //! the T-1083 lesson), and: //! //! 1. **Determinism** — derives each body twice and asserts the reports are identical //! (the D-010 contract; the strongest invariant, platform-independent). //! 2. **Golden baseline** — snapshots the reports so any future cascade change that //! moves believability (a regression *or* an improvement, e.g. when T-1080/T-1082 //! land) is caught and must be acknowledged. Regenerate with //! `UPDATE_GOLDEN=1 cargo test --test believability_harness`. //! 3. **D-245 criteria (advisory → strict)** — prints each criterion's pass/fail per //! body. Per D-245 the gate starts *budgeted + advisory* (thresholds are Q-123-TBD); //! set `BELIEVABILITY_STRICT=1` to make unmet criteria *fail* the test (they do //! today — that is the point: T-1080/T-1081/T-1082 are the open work to flip them). //! //! Distinct from `derivation_harness` (T-1031), which checks the binding *laws*; this //! is its believability sibling — laws are necessary, this is the sufficiency gate. //! //! Bodies whose committed data (systems.db / heightmap) is absent are **skipped** with //! a logged note (matching `derivation_harness`), so the harness is robust in trimmed //! checkouts. The golden is an x86_64 reference (the cascade has f32 warp paths, per //! `cascade_golden`). use settled_reach_server::atlas::believability::{ analyze, cascade_snapshot_for_body, evaluate_criteria, seed_to_u64, BelievabilityReport, }; const GOLDEN_FILE: &str = "tests/golden/believability.json"; /// `(label, body_id, seed)` — lore-anchored validation bodies spanning the climate /// extremes the gate must handle: a temperate ocean world (should read lush) and a /// frozen ice world (must read as a living *cold* landscape, not blank — D-245). const VALIDATION_BODIES: &[(&str, &str, &str)] = &[ ("Arbour (temperate/ocean)", "GJ338Bd", "believability-v1"), ("Edict (frozen/ice)", "GJ244Ad", "believability-v1"), ]; /// Run the cascade + analyze for one body, or `None` if its committed data is absent. fn report_for(body_id: &str, seed: &str) -> Option { let world_seed = seed_to_u64(seed); match cascade_snapshot_for_body(world_seed, body_id) { Ok((snapshot, params)) => { let bws = snapshot.into_body_world_state(); Some(analyze( world_seed, body_id, &bws.districts, bws.heightmap_width, bws.heightmap_height, params.body_radius_km, )) } Err(e) => { eprintln!("[believability] SKIP {body_id}: {e}"); None } } } #[test] fn believability_determinism_and_golden() { let mut reports: Vec = Vec::new(); for (label, body_id, seed) in VALIDATION_BODIES { let Some(first) = report_for(body_id, seed) else { continue; }; // Determinism (D-010): a second full cascade + analyze must match exactly. let second = report_for(body_id, seed).expect("body resolved once, must resolve again"); assert_eq!( first, second, "[{label}] non-deterministic believability report — D-010 broken" ); // Structural sanity that holds regardless of believability quality or platform. assert!( first.district_count > 0, "[{label}] cascade produced no districts" ); assert!( first.coherence.water_districts_wet <= first.coherence.water_districts, "[{label}] wet water districts exceed total" ); // Advisory D-245 criteria report (strict via BELIEVABILITY_STRICT=1). let strict = std::env::var("BELIEVABILITY_STRICT").is_ok(); let crit = evaluate_criteria(&first); let passes = crit.iter().filter(|c| c.pass).count(); eprintln!( "[believability] {label}: {passes}/{} D-245 criteria pass", crit.len() ); for c in &crit { eprintln!( " [{}] {} — {}", if c.pass { "PASS" } else { "FAIL" }, c.name, c.detail ); if strict { assert!( c.pass, "[{label}] D-245 criterion failed (strict): {}", c.name ); } } reports.push(first); } if reports.is_empty() { eprintln!("[believability] no validation bodies available — skipping golden"); return; } // ── Golden baseline ───────────────────────────────────────────────────── let golden_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(GOLDEN_FILE); let actual_json = serde_json::to_string_pretty(&reports).expect("serialize reports") + "\n"; if std::env::var("UPDATE_GOLDEN").is_ok() { std::fs::create_dir_all(golden_path.parent().unwrap()).expect("mkdir golden"); std::fs::write(&golden_path, &actual_json).expect("write golden"); eprintln!("Golden written: {}", golden_path.display()); return; } let golden_json = std::fs::read_to_string(&golden_path).unwrap_or_else(|e| { panic!( "Golden not found: {}.\n\ First run: UPDATE_GOLDEN=1 cargo test --test believability_harness\n{e}", golden_path.display() ) }); let actual_v: serde_json::Value = serde_json::from_str(&actual_json).expect("reparse actual"); let golden_v: serde_json::Value = serde_json::from_str(&golden_json).expect("parse golden"); assert!( actual_v == golden_v, "Believability golden mismatch — the cascade moved believability (regression OR \ improvement). If intended (e.g. T-1080/T-1082 landed), update:\n\ UPDATE_GOLDEN=1 cargo test --test believability_harness\n\nActual:\n{}", actual_json.trim() ); }