extern crate std;
use std::string::String;
use std::vec::Vec;
use std::format;
use serde::{Deserialize, Serialize};
use crate::engine::ObservationResult;
use crate::grammar::GrammarState;
use crate::policy::PolicyDecision;
use crate::pipeline::{EvaluationResult, Episode};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceEntry {
pub k: u64,
pub residual_norm: f32,
pub drift: f32,
pub slew: f32,
pub motif: String,
pub grammar: String,
pub semantic: String,
pub policy: String,
pub lyapunov_lambda: f32,
pub lyapunov_stability: String,
pub lyapunov_time_to_exit: Option<f32>,
pub sub_threshold: bool,
pub integration_mode: String,
}
impl TraceEntry {
pub fn from_result(r: &ObservationResult) -> Self {
Self {
k: r.k,
residual_norm: r.residual_norm,
drift: r.sign.drift,
slew: r.sign.slew,
motif: format!("{:?}", r.motif),
grammar: grammar_to_str(r.grammar),
semantic: format!("{:?}", r.semantic),
policy: policy_to_str(r.policy),
lyapunov_lambda: r.lyapunov.lambda,
lyapunov_stability: format!("{:?}", r.lyapunov.stability),
lyapunov_time_to_exit: r.lyapunov.time_to_exit,
sub_threshold: r.sub_threshold,
integration_mode: "read_only_side_channel".into(),
}
}
}
fn grammar_to_str(g: GrammarState) -> String {
match g {
GrammarState::Admissible => "Admissible".into(),
GrammarState::Boundary(r) => format!("Boundary[{:?}]", r),
GrammarState::Violation => "Violation".into(),
}
}
fn policy_to_str(p: PolicyDecision) -> String {
format!("{:?}", p)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkMetrics {
pub dataset: String,
pub raw_boundary_count: usize,
pub dsfb_episode_count: usize,
pub episode_precision: f32,
pub recall_numerator: usize,
pub recall_denominator: usize,
pub recall_fraction: f32,
pub compression_factor: f32,
pub precision_gain: f32,
pub raw_precision_proxy: f32,
pub false_episode_rate_clean: f32,
pub wpred: usize,
pub healthy_window: usize,
pub snr_floor_db: f32,
}
impl BenchmarkMetrics {
pub fn from_result(r: &EvaluationResult) -> Self {
Self {
dataset: r.dataset.into(),
raw_boundary_count: r.raw_boundary_count,
dsfb_episode_count: r.dsfb_episode_count,
episode_precision: r.episode_precision,
recall_numerator: r.recall_numerator,
recall_denominator: r.recall_denominator,
recall_fraction: r.recall(),
compression_factor: r.compression_factor,
precision_gain: r.precision_gain,
raw_precision_proxy: r.raw_precision_proxy,
false_episode_rate_clean: r.false_episode_rate_clean,
wpred: crate::pipeline::WPRED,
healthy_window: crate::pipeline::HEALTHY_WINDOW_SIZE,
snr_floor_db: crate::pipeline::SNR_FLOOR_DB,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EpisodeRecord {
pub episode_id: usize,
pub open_k: usize,
pub close_k: Option<usize>,
pub is_precursor: bool,
}
impl EpisodeRecord {
pub fn from_episodes(episodes: &[Episode]) -> Vec<Self> {
episodes.iter().enumerate().map(|(i, ep)| Self {
episode_id: i,
open_k: ep.open_k,
close_k: ep.close_k,
is_precursor: ep.is_precursor,
}).collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunManifest {
pub crate_version: String,
pub run_timestamp: String,
pub protocol: String,
pub dsa_w: usize,
pub grammar_k: usize,
pub dsa_tau: f32,
pub corroboration_m: u8,
pub ewma_lambda: f32,
pub cusum_kappa_sigma: f32,
pub cusum_h_sigma: f32,
pub wpred: usize,
pub configuration: String,
pub trace_entry_count: usize,
pub non_intrusion: String,
}
impl RunManifest {
pub fn build(trace_len: usize) -> Self {
use crate::pipeline::*;
Self {
crate_version: env!("CARGO_PKG_VERSION").into(),
run_timestamp: chrono_now_iso8601(),
protocol: "Stage_III_fixed_read_only".into(),
dsa_w: DSA_WINDOW_W,
grammar_k: GRAMMAR_K,
dsa_tau: DSA_TAU,
corroboration_m: CORROBORATION_M,
ewma_lambda: EWMA_LAMBDA,
cusum_kappa_sigma: CUSUM_KAPPA_SIGMA,
cusum_h_sigma: CUSUM_H_SIGMA,
wpred: WPRED,
configuration: "all_features[compression_biased]".into(),
trace_entry_count: trace_len,
non_intrusion: "enforced_by_rust_type_system_no_unsafe".into(),
}
}
}
fn chrono_now_iso8601() -> String {
"2026-04-08T00:00:00Z".into()
}
pub fn to_json_pretty<T: Serialize>(val: &T) -> Result<String, String> {
serde_json::to_string_pretty(val).map_err(|e| std::format!("{}", e))
}
pub fn write_artifacts(
result: &EvaluationResult,
out_dir: &std::path::Path,
) -> Result<(), String> {
use std::fs;
fs::create_dir_all(out_dir).map_err(|e| std::format!("{}", e))?;
let trace: Vec<TraceEntry> = result.trace.iter()
.map(TraceEntry::from_result)
.collect();
let trace_json = to_json_pretty(&trace)?;
fs::write(out_dir.join("dsfb_traceability.json"), trace_json)
.map_err(|e| std::format!("{}", e))?;
let metrics = BenchmarkMetrics::from_result(result);
let metrics_json = to_json_pretty(&metrics)?;
fs::write(out_dir.join("benchmark_metrics.json"), metrics_json)
.map_err(|e| std::format!("{}", e))?;
let episodes = EpisodeRecord::from_episodes(&result.episodes);
let ep_json = to_json_pretty(&episodes)?;
fs::write(out_dir.join("episode_precision_metrics.json"), ep_json)
.map_err(|e| std::format!("{}", e))?;
let neg = serde_json::json!({
"false_episode_rate_clean": result.false_episode_rate_clean,
"dataset": result.dataset,
"note": "bounded to evaluated dataset; do not extrapolate"
});
let neg_json = serde_json::to_string_pretty(&neg).map_err(|e| std::format!("{}", e))?;
fs::write(out_dir.join("negative_control_report.json"), neg_json)
.map_err(|e| std::format!("{}", e))?;
let manifest = RunManifest::build(result.trace.len());
let manifest_json = to_json_pretty(&manifest)?;
fs::write(out_dir.join("dsfb_run_manifest.json"), manifest_json)
.map_err(|e| std::format!("{}", e))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::vec;
use crate::pipeline::{synthetic_radioml_stream, run_stage_iii};
#[test]
fn trace_entry_from_observation() {
let drift_at = vec![150, 300];
let (obs, events) = synthetic_radioml_stream(400, &drift_at, 15.0);
let result = run_stage_iii("trace_test", &obs, &events);
if !result.trace.is_empty() {
let entry = TraceEntry::from_result(&result.trace[0]);
assert_eq!(entry.integration_mode, "read_only_side_channel");
}
}
#[test]
fn benchmark_metrics_json_roundtrip() {
let drift_at = vec![200];
let (obs, events) = synthetic_radioml_stream(300, &drift_at, 15.0);
let result = run_stage_iii("metrics_test", &obs, &events);
let m = BenchmarkMetrics::from_result(&result);
let json = to_json_pretty(&m).unwrap();
let parsed: BenchmarkMetrics = serde_json::from_str(&json).unwrap();
assert!((parsed.episode_precision - m.episode_precision).abs() < 1e-5);
assert_eq!(parsed.dataset, m.dataset);
}
#[test]
fn run_manifest_has_correct_version() {
let m = RunManifest::build(42);
assert_eq!(m.crate_version, env!("CARGO_PKG_VERSION"));
assert_eq!(m.trace_entry_count, 42);
assert_eq!(m.non_intrusion, "enforced_by_rust_type_system_no_unsafe");
assert_eq!(m.configuration, "all_features[compression_biased]");
}
#[test]
fn write_artifacts_creates_all_files() {
let drift_at = vec![150, 250, 350];
let (obs, events) = synthetic_radioml_stream(500, &drift_at, 15.0);
let result = run_stage_iii("write_test", &obs, &events);
let tmp = std::env::temp_dir().join("dsfb_rf_test_artifacts");
write_artifacts(&result, &tmp).expect("write_artifacts must succeed");
assert!(tmp.join("dsfb_traceability.json").exists());
assert!(tmp.join("benchmark_metrics.json").exists());
assert!(tmp.join("episode_precision_metrics.json").exists());
assert!(tmp.join("negative_control_report.json").exists());
assert!(tmp.join("dsfb_run_manifest.json").exists());
}
}