fix(ruview-gamma): safety becomes a property of construction (safety review, 2 critical + 3 major)

- safety stop is now control flow: run_calibration terminates the sweep on
  any stop (partial calibration recorded)
- cross-session terminate-and-lock: persisted latched ParticipantSafetyState;
  locked participants refuse sessions across governor instances until
  unlock_with_acknowledgment writes an audit record; documented class->lock map
- SafetyEnvelope safe-by-construction: private fields, validated try_new/with_*
  constructors, serde routed through the validator, compiled-in absolute bounds
  (floor >=30Hz keeps the whole band above the 15-25Hz photosensitive zone;
  caps <=0.6; <=30min) — an 18-22Hz config fails to deserialize
- dose governance: <=4 sittings/24h + >=60min cooldown from the persisted ledger
- per-tick latched SafetyMonitor wired into run_session (simulator emits ticks;
  mid-session latch truncates delivered duration); clean-session witness
  byte-identical (pinned proof unchanged)
- cohort warm-start k-floor (>=3 profiles)
ADR-250 + crate README updated. 125+13 tests green.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-06-11 15:26:17 -04:00
parent 246b5ba4af
commit 192b4c6b4a
22 changed files with 1963 additions and 426 deletions
+3 -3
View File
@@ -385,8 +385,8 @@ impl ClosedLoopController {
/// The 0.1 Hz candidate grid over the envelope (ADR-250 §18 ±0.1 Hz precision).
fn fine_grid(envelope: &SafetyEnvelope) -> Vec<f64> {
let lo = (envelope.min_hz * 10.0).round() as i64;
let hi = (envelope.max_hz * 10.0).round() as i64;
let lo = (envelope.min_hz() * 10.0).round() as i64;
let hi = (envelope.max_hz() * 10.0).round() as i64;
(lo..=hi).map(|i| i as f64 / 10.0).collect()
}
@@ -433,7 +433,7 @@ mod tests {
}
let rec = bo.recommend(&env, &StimulusParameters::prior());
assert!(env.contains(&rec.stimulus));
assert!(rec.stimulus.frequency_hz <= env.max_hz);
assert!(rec.stimulus.frequency_hz <= env.max_hz());
}
#[test]