#![allow(
dead_code,
unused_variables,
unused_assignments,
unused_mut,
clippy::all,
unused_imports,
unused_features
)]
#![feature(generic_const_exprs)]
#![feature(adt_const_params)]
#![feature(const_trait_impl)]
#![feature(min_specialization)]
#![feature(portable_simd)]
#![allow(incomplete_features)]
extern crate alloc;
pub mod admission;
pub mod bcinr_compat;
pub mod cache_resident;
pub mod compat_api_probe;
pub mod conformance_authority;
pub mod error;
#[cfg(feature = "ocel")]
pub mod foundry;
pub mod graduation;
pub mod io;
pub mod lifecycle;
pub mod lsa;
pub mod ml_algorithms;
pub mod model_registry;
pub mod models;
pub mod receipt;
pub mod replay;
pub mod soundness;
pub mod state;
pub mod types;
pub mod wf_to_powl;
#[cfg(feature = "ocel")]
pub mod ocpq_parser;
#[cfg(feature = "ocel")]
pub mod ocpq_runtime;
use std::cell::RefCell;
#[wasm_bindgen(start)]
pub fn main() {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
#[cfg(not(target_arch = "wasm32"))]
{
tracing_subscriber::fmt()
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
.init();
}
}
const DRIFT_THRESHOLD_LOW_DEFAULT: f32 = 0.3;
const DRIFT_THRESHOLD_HIGH_DEFAULT: f32 = 0.7;
#[cfg(feature = "cloud")]
const CYCLE_LATENCY_BUDGET_US: u64 = 5_000;
use std::sync::atomic::{AtomicU32, Ordering};
static DRIFT_THRESHOLD_LOW: AtomicU32 = AtomicU32::new(0x3e99999a); static DRIFT_THRESHOLD_HIGH: AtomicU32 = AtomicU32::new(0x3f333333);
fn get_drift_threshold_low() -> f32 {
f32::from_bits(DRIFT_THRESHOLD_LOW.load(Ordering::Relaxed))
}
#[cfg(feature = "cloud")]
pub(crate) fn wall_clock_us() -> u64 {
#[cfg(target_arch = "wasm32")]
{
(js_sys::Date::now() * 1000.0) as u64
}
#[cfg(not(target_arch = "wasm32"))]
{
use once_cell::sync::Lazy;
use std::time::Instant;
static ANCHOR: Lazy<Instant> = Lazy::new(Instant::now);
ANCHOR.elapsed().as_micros() as u64
}
}
fn get_drift_threshold_high() -> f32 {
f32::from_bits(DRIFT_THRESHOLD_HIGH.load(Ordering::Relaxed))
}
#[must_use = "returns computed duration in seconds"]
pub fn parse_iso8601_duration(first: &str, last: &str) -> f64 {
fn parse_ts(s: &str) -> Option<i64> {
let s = s.trim();
if s.is_empty() {
return None;
}
let s = if let Some(stripped) = s.strip_suffix('Z') {
stripped
} else if s.len() > 6
&& s.chars().nth(s.len() - 3) == Some(':')
&& s.chars().nth(s.len() - 6) == Some('+')
{
&s[..s.len() - 6]
} else if s.len() > 6
&& s.chars().nth(s.len() - 3) == Some(':')
&& s.chars().nth(s.len() - 6) == Some('-')
{
&s[..s.len() - 6]
} else {
s
};
let parts: Vec<&str> = s.split('T').collect();
if parts.len() != 2 {
return None;
}
let date_parts: Vec<&str> = parts[0].split('-').collect();
if date_parts.len() != 3 {
return None;
}
let time_parts: Vec<&str> = parts[1].split('.').collect(); let time_main: Vec<&str> = time_parts[0].split(':').collect();
if time_main.len() != 3 {
return None;
}
let year: i64 = date_parts[0].parse().ok()?;
let month: i64 = date_parts[1].parse().ok()?;
let day: i64 = date_parts[2].parse().ok()?;
let hour: i64 = time_main[0].parse().ok()?;
let minute: i64 = time_main[1].parse().ok()?;
let second: i64 = time_main[2].parse().ok()?;
let days_in_month: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
let mut total_days: i64 = 0;
for y in 1970..year {
total_days += if (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0) {
366
} else {
365
};
}
for m in 1..month {
total_days += days_in_month[(m - 1) as usize];
if m == 2 && is_leap {
total_days += 1;
}
}
total_days += day - 1;
let ms: i64 = total_days * 86_400_000 + hour * 3_600_000 + minute * 60_000 + second * 1_000;
Some(ms)
}
match (parse_ts(first), parse_ts(last)) {
(Some(a), Some(b)) => (b - a) as f64,
_ => 0.0,
}
}
#[allow(dead_code)]
pub mod proof_gate_registry;
#[cfg(feature = "poc_gate_validator")]
pub mod gate_validator;
pub mod algorithms;
#[cfg(feature = "conformance_basic")]
pub mod analysis;
pub mod binary_format;
pub mod branchless;
pub mod cache;
pub mod capability_registry;
#[cfg(feature = "conformance_basic")]
pub mod conformance;
#[cfg(feature = "conformance_basic")]
pub mod conformance_guards;
pub mod conformance_reporting;
#[cfg(feature = "conformance_basic")]
pub mod data_quality;
#[cfg(feature = "streaming_basic")]
pub mod dfg_io;
pub mod discovery;
pub mod discovery_determinism_guards;
#[cfg(feature = "discovery_advanced")]
pub mod ensemble;
pub mod fast_discovery;
#[cfg(feature = "ml")]
pub mod feature_extraction;
#[cfg(feature = "ml")]
pub mod feature_importance;
pub mod filters;
#[cfg(feature = "ml")]
pub mod final_analytics;
#[cfg(feature = "hand_rolled_stats")]
pub mod hand_stats;
#[cfg(feature = "discovery_advanced")]
pub mod hierarchical;
pub mod hot_kernels;
pub mod incremental_dfg;
#[cfg(feature = "discovery_advanced")]
pub mod more_discovery;
pub mod network_metrics;
pub mod parallel_executor;
#[cfg(all(feature = "conformance_basic", feature = "discovery_advanced"))]
pub mod pattern_analysis;
#[cfg(feature = "petri_net_playout")]
pub mod playout;
pub mod probabilistic;
pub mod process_tree;
#[cfg(feature = "cloud")]
pub mod rl_state_serialization;
#[cfg(feature = "discovery_advanced")]
pub mod smart_engine;
pub mod social_network;
pub mod testing;
pub mod text_encoding;
pub mod utilities;
pub mod wasm_utils;
pub mod xes_format;
#[cfg(feature = "ocel")]
pub mod oc_conformance;
#[cfg(feature = "ocel")]
pub mod oc_performance;
#[cfg(feature = "ocel")]
pub mod oc_petri_net;
#[cfg(feature = "ocel")]
pub mod ocel_csv;
#[cfg(feature = "ocel")]
pub mod ocel_flatten;
#[cfg(feature = "ocel")]
pub mod ocel_io;
#[cfg(feature = "ocel")]
pub mod ocel_tests;
#[cfg(feature = "ocel")]
pub mod ocel_v2;
#[cfg(feature = "discovery_advanced")]
pub mod advanced_algorithms;
#[cfg(feature = "discovery_advanced")]
pub mod batches;
#[cfg(feature = "discovery_advanced")]
pub mod causal_graph;
#[cfg(feature = "discovery_advanced")]
pub mod genetic_discovery;
#[cfg(feature = "discovery_advanced")]
pub mod ilp_discovery; #[cfg(feature = "discovery_advanced")]
pub mod log_to_trie;
#[cfg(feature = "discovery_advanced")]
pub mod performance_spectrum;
#[cfg(feature = "discovery_advanced")]
pub mod transition_system;
#[cfg(feature = "conformance_full")]
pub mod generalization;
#[cfg(feature = "miniml")]
pub mod actor_envelope;
#[cfg(feature = "ml")]
pub mod anomaly;
#[cfg(feature = "miniml")]
pub mod automembrane;
#[cfg(feature = "miniml")]
pub mod automl_envelope;
#[cfg(feature = "miniml")]
pub mod benchmark_runner;
#[cfg(feature = "miniml")]
pub mod drift_manager;
#[cfg(feature = "miniml")]
pub mod object_envelope;
#[cfg(feature = "ml")]
pub mod prediction;
#[cfg(feature = "ml")]
pub mod prediction_additions;
#[cfg(feature = "ml")]
pub mod prediction_drift;
#[cfg(feature = "ml")]
pub mod prediction_features;
#[cfg(feature = "ml")]
pub mod prediction_next_activity;
#[cfg(feature = "ml")]
pub mod prediction_outcome;
#[cfg(feature = "ml")]
pub mod prediction_remaining_time;
#[cfg(feature = "ml")]
pub mod prediction_resource;
#[cfg(feature = "miniml")]
pub mod prediction_rf;
#[cfg(feature = "miniml")]
pub mod route_envelope;
#[cfg(all(feature = "ml", feature = "miniml"))]
pub mod statistical_analysis;
#[cfg(feature = "miniml")]
pub mod time_envelope;
pub mod duration_utils;
pub mod trace_embeddings;
pub mod wasm_testing_utils;
#[cfg(feature = "streaming_basic")]
pub mod simd_streaming_dfg;
#[cfg(feature = "streaming_basic")]
pub mod streaming;
#[cfg(feature = "streaming_full")]
pub mod streaming_conformance;
#[cfg(feature = "streaming_full")]
pub mod streaming_pipeline;
#[cfg(feature = "streaming_basic")]
pub mod streaming_wasm;
#[cfg(feature = "conformance_basic")]
pub mod declare;
#[cfg(feature = "conformance_basic")]
pub mod declare_conformance;
#[cfg(feature = "conformance_basic")]
pub mod simd_token_replay;
#[cfg(feature = "conformance_basic")]
pub mod temporal_profile;
pub mod simd_inner_loops;
#[cfg(feature = "conformance_full")]
pub mod alignments;
#[cfg(feature = "conformance_full")]
pub mod etconformance_precision;
#[cfg(feature = "conformance_full")]
pub mod marking_equation;
#[cfg(feature = "conformance_full")]
pub mod petri_net_reduction;
#[cfg(feature = "align_etconformance")]
pub mod align_etconformance;
#[cfg(feature = "alignment_fitness")]
pub mod alignment_fitness;
#[cfg(feature = "montecarlo")]
pub mod montecarlo;
#[cfg(feature = "petri_net_playout")]
pub mod petri_net_playout;
#[cfg(feature = "conformance_basic")]
pub mod performance_dfg;
#[cfg(feature = "ocel")]
pub mod resource_analysis;
#[cfg(feature = "powl")]
pub mod complexity_metrics;
#[cfg(feature = "powl")]
pub mod powl;
#[cfg(feature = "powl")]
pub mod powl_api;
#[cfg(feature = "powl")]
pub mod powl_arena;
#[cfg(feature = "powl")]
pub mod powl_event_log;
#[cfg(feature = "powl")]
pub mod powl_models;
#[cfg(feature = "powl")]
pub mod powl_parser;
#[cfg(feature = "powl")]
pub mod powl_petri_net;
#[cfg(feature = "powl")]
pub mod powl_process_tree;
#[cfg(feature = "powl")]
pub mod powl_to_process_tree;
#[cfg(feature = "powl")]
pub mod yawl_export;
pub mod pnml_io;
pub mod bpmn_import;
#[cfg(feature = "streaming_basic")]
pub use streaming::{
StreamStats, StreamingAlgorithm, StreamingDfgBuilder, StreamingHeuristicBuilder,
StreamingSkeletonBuilder,
};
pub mod recommendations;
#[cfg(feature = "conformance_basic")]
pub mod conformance_cache;
pub mod correlation_miner;
#[cfg(feature = "cloud")]
pub mod self_healing;
#[cfg(feature = "cloud")]
pub mod spc;
#[cfg(feature = "cloud")]
pub mod spc_history;
#[cfg(feature = "cloud")]
pub mod guards;
#[cfg(feature = "discovery_advanced")]
pub mod pattern_dispatch;
#[cfg(feature = "cloud")]
pub mod reinforcement;
#[cfg(feature = "cloud")]
pub mod rl_orchestrator;
#[cfg(feature = "cloud")]
pub use rl_orchestrator::{RlOrchestrator, StateCoverage};
#[cfg(feature = "cloud")]
pub mod rl_stability_monitor;
#[cfg(feature = "cloud")]
pub use rl_stability_monitor::RlStabilityMonitor;
#[cfg(feature = "cloud")]
pub mod rl_dimensionality_analysis;
#[cfg(feature = "cloud")]
pub use rl_dimensionality_analysis::{
analyze_dimension_usage, format_dimensionality_report, DimensionUsageReport,
DimensionalityAnalyzer, StateClustering,
};
#[cfg(feature = "cloud")]
pub mod action_dispatch;
#[cfg(feature = "cloud")]
pub mod agentic;
thread_local! {
#[cfg(feature = "cloud")]
pub static RL_ORCHESTRATOR: RefCell<rl_orchestrator::RlOrchestrator> =
RefCell::new(rl_orchestrator::RlOrchestrator::new());
#[cfg(feature = "cloud")]
pub static CIRCUIT_BREAKER: RefCell<self_healing::CircuitBreaker> =
RefCell::new(self_healing::CircuitBreaker::new());
#[cfg(feature = "cloud")]
pub static SPC_HISTORY: RefCell<spc_history::SpcHistory> =
RefCell::new(spc_history::SpcHistory::new());
#[cfg(feature = "cloud")]
pub static SCALE_BOOST_REMAINING: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
#[cfg(feature = "cloud")]
pub static LAST_ALGORITHM: RefCell<String> = RefCell::new("dfg".to_string());
#[cfg(feature = "cloud")]
pub static AUTO_PROCESS_AGENT: RefCell<autoprocess::AutoProcessAgent> =
RefCell::new(autoprocess::AutoProcessAgent::new());
#[cfg(feature = "ml")]
pub static FALLBACK_BANDIT: RefCell<Option<crate::prediction_resource::BanditState>> =
const { RefCell::new(None) };
}
#[cfg(feature = "ml")]
pub mod ml;
#[cfg(feature = "cognition")]
pub use wasm4pm_cognition as cognition;
pub use wasm4pm_compat as data_types;
#[cfg(not(target_arch = "wasm32"))]
pub mod gpu;
#[cfg(feature = "cloud")]
pub mod autoprocess;
pub mod trace_correlation;
pub mod policy_persistence;
pub mod advanced;
pub mod autonomic_audit_trail;
pub mod oc_orchestrator;
pub use autonomic_audit_trail::*;
pub use discovery::discover_dfg;
pub use state::delete_object;
pub use xes_format::{load_eventlog_from_xes, load_eventlog_from_xes_cached};
#[allow(unused)]
use state::*;
#[allow(unused)]
use types::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn init() -> Result<String, JsValue> {
init_state();
Ok("Rust4PM WASM initialized successfully".to_string())
}
fn init_state() {
let _ = state::get_or_init_state();
}
#[wasm_bindgen]
pub fn get_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn get_capabilities() -> Result<JsValue, JsValue> {
#[derive(serde::Serialize)]
struct Features {
discovery: bool,
conformance: bool,
ml: bool,
streaming: bool,
powl: bool,
ocel: bool,
alignment_fitness: bool,
petri_net_playout: bool,
extensive_playout: bool,
align_etconformance: bool,
montecarlo: bool,
}
#[derive(serde::Serialize)]
struct Capabilities {
version: &'static str,
features: Features,
}
let caps = Capabilities {
version: env!("CARGO_PKG_VERSION"),
features: Features {
discovery: true,
conformance: cfg!(feature = "conformance_full"),
ml: cfg!(feature = "ml"),
streaming: cfg!(feature = "streaming_full"),
powl: cfg!(feature = "powl"),
ocel: cfg!(feature = "ocel"),
alignment_fitness: cfg!(feature = "alignment_fitness"),
petri_net_playout: cfg!(feature = "petri_net_playout"),
extensive_playout: cfg!(feature = "extensive_playout"),
align_etconformance: cfg!(feature = "align_etconformance"),
montecarlo: cfg!(feature = "montecarlo"),
},
};
serde_wasm_bindgen::to_value(&caps).map_err(|e| crate::error::js_val(&e.to_string()))
}
#[wasm_bindgen]
pub fn clear_all_caches() {
crate::cache::cache_clear();
}
#[wasm_bindgen]
pub fn get_cache_stats() -> Result<JsValue, JsValue> {
#[derive(serde::Serialize)]
struct CacheStats {
parse_hits: u64,
parse_misses: u64,
columnar_entries: usize,
interner_entries: usize,
}
let raw = crate::cache::cache_stats();
let stats = CacheStats {
parse_hits: raw.parse_hits,
parse_misses: raw.parse_misses,
columnar_entries: raw.columnar_entries,
interner_entries: raw.interner_entries,
};
serde_wasm_bindgen::to_value(&stats).map_err(|e| crate::error::js_val(&e.to_string()))
}
#[wasm_bindgen]
pub fn set_drift_thresholds(low: f32, high: f32) -> Result<String, JsValue> {
if !(0.0..=1.0).contains(&low) {
return Err(crate::error::js_val(&format!(
"Invalid low threshold {}: must be in [0.0, 1.0]",
low
)));
}
if !(0.0..=1.0).contains(&high) {
return Err(crate::error::js_val(&format!(
"Invalid high threshold {}: must be in [0.0, 1.0]",
high
)));
}
if low >= high {
return Err(crate::error::js_val(&format!(
"Invalid thresholds: low ({}) must be less than high ({})",
low, high
)));
}
DRIFT_THRESHOLD_LOW.store(low.to_bits(), Ordering::Relaxed);
DRIFT_THRESHOLD_HIGH.store(high.to_bits(), Ordering::Relaxed);
Ok(format!(
"Drift thresholds updated: low={}, high={}",
low, high
))
}
#[wasm_bindgen]
pub fn get_drift_thresholds() -> String {
format!(
r#"{{"low":{},"high":{}}}"#,
get_drift_threshold_low(),
get_drift_threshold_high()
)
}
#[wasm_bindgen]
pub fn reset_drift_thresholds() -> String {
DRIFT_THRESHOLD_LOW.store(DRIFT_THRESHOLD_LOW_DEFAULT.to_bits(), Ordering::Relaxed);
DRIFT_THRESHOLD_HIGH.store(DRIFT_THRESHOLD_HIGH_DEFAULT.to_bits(), Ordering::Relaxed);
format!(
"Drift thresholds reset to defaults: low={}, high={}",
DRIFT_THRESHOLD_LOW_DEFAULT, DRIFT_THRESHOLD_HIGH_DEFAULT
)
}
#[cfg(feature = "conformance_basic")]
#[wasm_bindgen]
pub fn simd_token_replay(log_handle: &str, activity_key: &str) -> String {
crate::simd_token_replay::replay_log(log_handle, activity_key)
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn autonomic_execute_cycle(
log_handle: &str,
activity_key: &str,
_config_json: &str,
) -> Result<String, JsValue> {
let state = get_or_init_state();
let _cycle_span = tracing::info_span!(
"autonomic.cycle",
log_handle = log_handle,
activity_key = activity_key,
)
.entered();
let t_cycle_start = wall_clock_us();
let t_perception_start = wall_clock_us();
let perception_result = get_or_init_state().with_event_log(log_handle, |log| {
let trace_count = log.traces.len();
let event_count: usize = log.traces.iter().map(|t| t.events.len()).sum();
let mut activity_set = std::collections::HashSet::new();
for trace in &log.traces {
for event in &trace.events {
if let Some(models::AttributeValue::String(name)) =
event.attributes.get(activity_key)
{
activity_set.insert(name.clone());
}
}
}
let unique_activities = activity_set.len();
let time_key = "time:timestamp";
let mut trace_durations: Vec<f64> = Vec::new();
let mut has_timestamps = false;
for trace in &log.traces {
let first_ts = trace
.events
.first()
.and_then(|e| e.attributes.get(time_key));
let last_ts = trace.events.last().and_then(|e| e.attributes.get(time_key));
if let (Some(first), Some(last)) = (first_ts, last_ts) {
has_timestamps = true;
let first_str = first.as_string().unwrap_or("");
let last_str = last.as_string().unwrap_or("");
let dur = parse_iso8601_duration(first_str, last_str);
trace_durations.push(dur);
}
}
let mut activity_freq: std::collections::BTreeMap<String, usize> =
std::collections::BTreeMap::new();
for trace in &log.traces {
for event in &trace.events {
if let Some(models::AttributeValue::String(name)) =
event.attributes.get(activity_key)
{
*activity_freq.entry(name.clone()).or_default() += 1;
}
}
}
let health_state = if event_count == 0 || unique_activities == 0 {
4 } else if trace_count == 0 {
3 } else if unique_activities == 1 && event_count < 5 {
2 } else if unique_activities <= 2 && event_count < 20 {
1 } else {
0 };
let health_label = match health_state {
0 => "Normal",
1 => "Warning",
2 => "Degraded",
3 => "Critical",
_ => "Failed",
};
let col = log.to_columnar(activity_key);
let rework_count = col.count_traces_with_rework();
let loop_count_l1 = col.count_loops_length_1();
let loop_count_l2 = col.count_loops_length_2();
let rework_ratio = if trace_count > 0 {
rework_count as f32 / trace_count as f32
} else {
0.0
};
let total_loops = loop_count_l1 + loop_count_l2;
let loop_density = if event_count > 0 {
total_loops as f32 / event_count as f32
} else {
0.0
};
let mut dfg_edges = std::collections::HashSet::new();
for trace in &log.traces {
let trace_activities: Vec<String> = trace
.events
.iter()
.filter_map(|event| {
event
.attributes
.get(activity_key)
.and_then(|attr| attr.as_string())
.map(|s| s.to_string())
})
.collect();
for i in 0..trace_activities.len().saturating_sub(1) {
let edge = (trace_activities[i].clone(), trace_activities[i + 1].clone());
dfg_edges.insert(edge);
}
}
let unique_edges = dfg_edges.len();
let n = unique_activities as f32;
let max_edges = (n * (n - 1.0)).max(1.0) as usize;
let dfg_density = (unique_edges as f32 / max_edges as f32).min(1.0);
let health_score_dfg = ((dfg_density * 70.0 - rework_ratio * 30.0).clamp(0.0, 100.0)) as u8;
let health_state_from_dfg = if dfg_density < 0.2 {
4 } else if dfg_density < 0.4 {
3 } else if dfg_density < 0.6 {
2 } else if dfg_density < 0.8 {
1 } else {
0 };
Ok::<serde_json::Value, JsValue>(serde_json::json!({
"event_count": event_count,
"trace_count": trace_count,
"unique_activities": unique_activities,
"has_timestamps": has_timestamps,
"trace_durations": trace_durations,
"activity_frequencies": activity_freq,
"health_state": health_state,
"health_label": health_label,
"rework_ratio": rework_ratio,
"loop_count_l1": loop_count_l1,
"loop_count_l2": loop_count_l2,
"loop_density": loop_density,
"unique_edges": unique_edges,
"dfg_density": dfg_density,
"health_score_dfg": health_score_dfg,
"health_state_from_dfg": health_state_from_dfg,
}))
})?;
let _perception_ns = 0;
let pattern_analysis = get_or_init_state().with_event_log(log_handle, |log| {
let traces_for_analysis: Vec<Vec<String>> = log
.traces
.iter()
.map(|trace| {
trace
.events
.iter()
.filter_map(|event| {
event
.attributes
.get(activity_key)
.and_then(|attr| attr.as_string())
.map(|s| s.to_string())
})
.collect()
})
.collect();
let analysis = pattern_analysis::analyze_trace_structure(&traces_for_analysis);
Ok::<pattern_analysis::TraceStructureAnalysis, JsValue>(analysis)
})?;
let t_perception_end = wall_clock_us();
let t_decision_start = wall_clock_us();
let perception = &perception_result;
let event_count_val = perception["event_count"].as_u64().unwrap_or(0);
let trace_count_val = perception["trace_count"].as_u64().unwrap_or(0);
let unique_activities_val = perception["unique_activities"].as_u64().unwrap_or(0);
let health_state_val = perception["health_state"].as_u64().unwrap_or(4);
let exec_ctx = guards::ExecutionContext {
task_id: 1,
timestamp: 0,
resources: guards::ResourceState {
cpu_available: 100,
memory_available: 100,
io_capacity: 100,
queue_depth: 0,
},
observations: guards::ObservationBuffer {
count: 4,
observations: [
event_count_val,
trace_count_val,
unique_activities_val,
health_state_val,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
},
state_flags: guards::StateFlags::INITIALIZED.bits() | guards::StateFlags::RUNNING.bits(),
};
let guard_state = guards::Guard {
guard_type: guards::GuardType::State,
predicate: guards::Predicate::BitSet,
operand_a: guards::StateFlags::RUNNING.bits(),
operand_b: 0,
children: vec![],
};
let guard_counter = guards::Guard {
guard_type: guards::GuardType::Counter,
predicate: guards::Predicate::GreaterThanOrEqual,
operand_a: 0,
operand_b: 1, children: vec![],
};
let compound_guard = guards::Guard {
guard_type: guards::GuardType::And,
predicate: guards::Predicate::Equal,
operand_a: 0,
operand_b: 0,
children: vec![guard_state, guard_counter],
};
let guard_result = compound_guard.evaluate(&exec_ctx);
let pattern_ctx = pattern_dispatch::PatternContext {
pattern_type: pattern_analysis.primary_pattern,
pattern_id: 0,
config: pattern_dispatch::PatternConfig {
max_instances: 1,
join_threshold: 1,
timeout_ticks: 100,
flags: pattern_dispatch::PatternFlags::default(),
},
input_mask: 0,
output_mask: 0,
state: std::sync::atomic::AtomicU32::new(0),
tick_budget: 100,
};
let dispatcher = pattern_dispatch::PatternDispatcher::new();
let pattern_result = dispatcher.dispatch(&pattern_ctx);
let guard_pass = guard_result;
let pattern_name = if pattern_result.success {
match pattern_analysis.primary_pattern {
pattern_dispatch::PatternType::Sequence => "Sequence",
pattern_dispatch::PatternType::ParallelSplit => "ParallelSplit",
pattern_dispatch::PatternType::StructuredLoop => "StructuredLoop",
pattern_dispatch::PatternType::ExclusiveChoice => "ExclusiveChoice",
_ => "Unknown",
}
} else {
"Failed"
};
let pattern_ticks = pattern_result.ticks_used;
let t_decision_end = wall_clock_us();
let t_protection_start = wall_clock_us();
let (circuit_allowed, circuit_state) = CIRCUIT_BREAKER.with(|cb| {
let mut cb = cb.borrow_mut();
let allowed = cb.allow_request();
let state = format!("{:?}", cb.state());
(allowed, state)
});
self_healing::advance_clock(1000);
let mut all_special_causes: Vec<String> = Vec::new();
let mut spc_rule_types: Vec<&'static str> = Vec::new();
let mut spc_results = serde_json::Map::new();
let event_counts_per_trace: Vec<f64> = perception["activity_frequencies"]
.as_object()
.map(|m| m.values().map(|v| v.as_f64().unwrap_or(0.0)).collect())
.unwrap_or_default();
if event_counts_per_trace.len() >= 9 {
let mean_er =
event_counts_per_trace.iter().sum::<f64>() / event_counts_per_trace.len() as f64;
let std_er = (event_counts_per_trace
.iter()
.map(|x| (x - mean_er).powi(2))
.sum::<f64>()
/ event_counts_per_trace.len() as f64)
.sqrt();
let chart_data: Vec<spc::ChartData> = event_counts_per_trace
.iter()
.map(|&v| spc::ChartData {
timestamp: String::new(),
value: v,
ucl: mean_er + 3.0 * std_er,
cl: mean_er,
lcl: (mean_er - 3.0 * std_er).max(0.0),
subgroup_data: None,
})
.collect();
let causes = spc::check_western_electric_rules(&chart_data);
spc_results.insert(
"event_rate".to_string(),
serde_json::json!(if causes.is_empty() { "OK" } else { "ALERT" }),
);
for c in &causes {
let (rule_violated, rule_number, rule_attrs) = match c {
spc::SpecialCause::OutOfControl { value, .. } => {
let z_score = if !chart_data.is_empty() {
let data_values: Vec<f64> = chart_data.iter().map(|cd| cd.value).collect();
let mean = data_values.iter().sum::<f64>() / data_values.len() as f64;
let std = (data_values.iter().map(|x| (x - mean).powi(2)).sum::<f64>()
/ data_values.len() as f64)
.sqrt();
if std > 0.0 {
((value - mean) / std).abs()
} else {
0.0
}
} else {
0.0
};
(
"rule_1_outlier",
1u8,
vec![
("z_score", format!("{:.2}", z_score)),
("outlier_value", format!("{:.2}", value)),
],
)
}
spc::SpecialCause::Shift { direction, count } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_2_shift",
2u8,
vec![
("direction", direction_str.to_string()),
("consecutive_points", count.to_string()),
],
)
}
spc::SpecialCause::Trend { direction, count } => {
let direction_str = match direction {
spc::TrendDirection::Increasing => "increasing",
spc::TrendDirection::Decreasing => "decreasing",
};
(
"rule_3_trend",
3u8,
vec![
("trend_direction", direction_str.to_string()),
("monotonic_sequence_length", count.to_string()),
],
)
}
spc::SpecialCause::TwoOfThree { direction } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_4_two_of_three",
4u8,
vec![("direction", direction_str.to_string())],
)
}
};
let current_cycle = 0u64; let spc_value = if let Some(last_chart) = chart_data.last() {
last_chart.value
} else {
0.0
};
let spc_ucl = if let Some(last_chart) = chart_data.last() {
last_chart.ucl
} else {
0.0
};
let spc_lcl = if let Some(last_chart) = chart_data.last() {
last_chart.lcl
} else {
0.0
};
let spc_cl = if let Some(last_chart) = chart_data.last() {
last_chart.cl
} else {
0.0
};
let sigma_distance = if spc_cl > 0.0 {
((spc_value - spc_cl) / ((spc_ucl - spc_cl) / 3.0)).abs()
} else {
0.0
};
tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "event_rate",
spc_value = spc_value,
spc_ucl = spc_ucl,
spc_lcl = spc_lcl,
spc_cl = spc_cl,
spc_sigma_distance = sigma_distance,
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
rule_details = rule_attrs.iter().map(|(k, v)| format!("{k}={v}")).collect::<Vec<_>>().join(", "),
"SPC rule violation classified"
);
all_special_causes.push(format!("event_rate: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert(
"event_rate".to_string(),
serde_json::json!("INSUFFICIENT_DATA"),
);
}
let trace_durations: Vec<f64> = perception["trace_durations"]
.as_array()
.map(|a| a.iter().map(|v| v.as_f64().unwrap_or(0.0)).collect())
.unwrap_or_default();
if trace_durations.len() >= 9 {
let mean_td = trace_durations.iter().sum::<f64>() / trace_durations.len() as f64;
let std_td = (trace_durations
.iter()
.map(|x| (x - mean_td).powi(2))
.sum::<f64>()
/ trace_durations.len() as f64)
.sqrt();
let chart_data: Vec<spc::ChartData> = trace_durations
.iter()
.map(|&v| spc::ChartData {
timestamp: String::new(),
value: v,
ucl: mean_td + 3.0 * std_td,
cl: mean_td,
lcl: (mean_td - 3.0 * std_td).max(0.0),
subgroup_data: None,
})
.collect();
let causes = spc::check_western_electric_rules(&chart_data);
spc_results.insert(
"trace_duration".to_string(),
serde_json::json!(if causes.is_empty() { "OK" } else { "ALERT" }),
);
for c in &causes {
let (rule_violated, rule_number, rule_attrs) = match c {
spc::SpecialCause::OutOfControl { value, .. } => {
let z_score = if !chart_data.is_empty() {
let data_values: Vec<f64> = chart_data.iter().map(|cd| cd.value).collect();
let mean = data_values.iter().sum::<f64>() / data_values.len() as f64;
let std = (data_values.iter().map(|x| (x - mean).powi(2)).sum::<f64>()
/ data_values.len() as f64)
.sqrt();
if std > 0.0 {
((value - mean) / std).abs()
} else {
0.0
}
} else {
0.0
};
(
"rule_1_outlier",
1u8,
vec![
("z_score", format!("{:.2}", z_score)),
("outlier_value", format!("{:.2}", value)),
],
)
}
spc::SpecialCause::Shift { direction, count } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_2_shift",
2u8,
vec![
("direction", direction_str.to_string()),
("consecutive_points", count.to_string()),
],
)
}
spc::SpecialCause::Trend { direction, count } => {
let direction_str = match direction {
spc::TrendDirection::Increasing => "increasing",
spc::TrendDirection::Decreasing => "decreasing",
};
(
"rule_3_trend",
3u8,
vec![
("trend_direction", direction_str.to_string()),
("monotonic_sequence_length", count.to_string()),
],
)
}
spc::SpecialCause::TwoOfThree { direction } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_4_two_of_three",
4u8,
vec![("direction", direction_str.to_string())],
)
}
};
let current_cycle = 0u64; let spc_value = if let Some(last_chart) = chart_data.last() {
last_chart.value
} else {
0.0
};
let spc_ucl = if let Some(last_chart) = chart_data.last() {
last_chart.ucl
} else {
0.0
};
let spc_lcl = if let Some(last_chart) = chart_data.last() {
last_chart.lcl
} else {
0.0
};
let spc_cl = if let Some(last_chart) = chart_data.last() {
last_chart.cl
} else {
0.0
};
let sigma_distance = if spc_cl > 0.0 {
((spc_value - spc_cl) / ((spc_ucl - spc_cl) / 3.0)).abs()
} else {
0.0
};
tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "trace_duration",
spc_value = spc_value,
spc_ucl = spc_ucl,
spc_lcl = spc_lcl,
spc_cl = spc_cl,
spc_sigma_distance = sigma_distance,
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
rule_details = rule_attrs.iter().map(|(k, v)| format!("{k}={v}")).collect::<Vec<_>>().join(", "),
"SPC rule violation classified"
);
all_special_causes.push(format!("trace_duration: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert(
"trace_duration".to_string(),
serde_json::json!("INSUFFICIENT_DATA"),
);
}
let freq_values: Vec<f64> = perception["activity_frequencies"]
.as_object()
.map(|m| m.values().map(|v| v.as_f64().unwrap_or(0.0)).collect())
.unwrap_or_default();
if freq_values.len() >= 9 {
let mean_af = freq_values.iter().sum::<f64>() / freq_values.len() as f64;
let std_af = (freq_values
.iter()
.map(|x| (x - mean_af).powi(2))
.sum::<f64>()
/ freq_values.len() as f64)
.sqrt();
let chart_data: Vec<spc::ChartData> = freq_values
.iter()
.map(|&v| spc::ChartData {
timestamp: String::new(),
value: v,
ucl: mean_af + 3.0 * std_af,
cl: mean_af,
lcl: (mean_af - 3.0 * std_af).max(0.0),
subgroup_data: None,
})
.collect();
let causes = spc::check_western_electric_rules(&chart_data);
spc_results.insert(
"activity_frequency".to_string(),
serde_json::json!(if causes.is_empty() { "OK" } else { "ALERT" }),
);
for c in &causes {
let (rule_violated, rule_number, rule_attrs) = match c {
spc::SpecialCause::OutOfControl { value, .. } => {
let z_score = if !chart_data.is_empty() {
let data_values: Vec<f64> = chart_data.iter().map(|cd| cd.value).collect();
let mean = data_values.iter().sum::<f64>() / data_values.len() as f64;
let std = (data_values.iter().map(|x| (x - mean).powi(2)).sum::<f64>()
/ data_values.len() as f64)
.sqrt();
if std > 0.0 {
((value - mean) / std).abs()
} else {
0.0
}
} else {
0.0
};
(
"rule_1_outlier",
1u8,
vec![
("z_score", format!("{:.2}", z_score)),
("outlier_value", format!("{:.2}", value)),
],
)
}
spc::SpecialCause::Shift { direction, count } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_2_shift",
2u8,
vec![
("direction", direction_str.to_string()),
("consecutive_points", count.to_string()),
],
)
}
spc::SpecialCause::Trend { direction, count } => {
let direction_str = match direction {
spc::TrendDirection::Increasing => "increasing",
spc::TrendDirection::Decreasing => "decreasing",
};
(
"rule_3_trend",
3u8,
vec![
("trend_direction", direction_str.to_string()),
("monotonic_sequence_length", count.to_string()),
],
)
}
spc::SpecialCause::TwoOfThree { direction } => {
let direction_str = match direction {
spc::ShiftDirection::Above => "above",
spc::ShiftDirection::Below => "below",
};
(
"rule_4_two_of_three",
4u8,
vec![("direction", direction_str.to_string())],
)
}
};
let current_cycle = 0u64; let spc_value = if let Some(last_chart) = chart_data.last() {
last_chart.value
} else {
0.0
};
let spc_ucl = if let Some(last_chart) = chart_data.last() {
last_chart.ucl
} else {
0.0
};
let spc_lcl = if let Some(last_chart) = chart_data.last() {
last_chart.lcl
} else {
0.0
};
let spc_cl = if let Some(last_chart) = chart_data.last() {
last_chart.cl
} else {
0.0
};
let sigma_distance = if spc_cl > 0.0 {
((spc_value - spc_cl) / ((spc_ucl - spc_cl) / 3.0)).abs()
} else {
0.0
};
tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "activity_frequency",
spc_value = spc_value,
spc_ucl = spc_ucl,
spc_lcl = spc_lcl,
spc_cl = spc_cl,
spc_sigma_distance = sigma_distance,
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
rule_details = rule_attrs.iter().map(|(k, v)| format!("{k}={v}")).collect::<Vec<_>>().join(", "),
"SPC rule violation classified"
);
all_special_causes.push(format!("activity_frequency: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert(
"activity_frequency".to_string(),
serde_json::json!("INSUFFICIENT_DATA"),
);
}
let cycle_num = SPC_HISTORY.with(|history| history.borrow().cycle_count + 1);
let timestamp = format!("cycle-{cycle_num}");
let event_rate_mean = if event_counts_per_trace.is_empty() {
0.0
} else {
event_counts_per_trace.iter().sum::<f64>() / event_counts_per_trace.len() as f64
};
let trace_duration_mean = if trace_durations.is_empty() {
0.0
} else {
trace_durations.iter().sum::<f64>() / trace_durations.len() as f64
};
let activity_freq_mean = if freq_values.is_empty() {
0.0
} else {
freq_values.iter().sum::<f64>() / freq_values.len() as f64
};
let snapshot = spc_history::SpcSnapshot::new(
timestamp.clone(),
event_rate_mean,
trace_duration_mean,
activity_freq_mean,
health_state_val as u8,
);
let (history_cycle_count, history_len, has_sufficient_data) = SPC_HISTORY.with(|history| {
let mut history = history.borrow_mut();
history.record_snapshot(snapshot);
(
history.cycle_count,
history.history.len(),
history.has_sufficient_data(),
)
});
spc_results.insert(
"history_cycle_count".to_string(),
serde_json::json!(history_cycle_count),
);
spc_results.insert("history_len".to_string(), serde_json::json!(history_len));
spc_results.insert(
"has_sufficient_data".to_string(),
serde_json::json!(has_sufficient_data),
);
if has_sufficient_data {
let historical_event_rates = SPC_HISTORY.with(|history| history.borrow().get_event_rates());
let historical_trace_durations =
SPC_HISTORY.with(|history| history.borrow().get_trace_durations());
let historical_activity_freqs =
SPC_HISTORY.with(|history| history.borrow().get_activity_frequencies());
if historical_event_rates.len() >= 9 {
let mean_er_hist = spc::spc_mean(&historical_event_rates);
let std_er_hist = spc::spc_std_dev(&historical_event_rates);
let chart_data_hist: Vec<spc::ChartData> = historical_event_rates
.iter()
.enumerate()
.map(|(i, &v)| spc::ChartData {
timestamp: format!("cycle-{i}"),
value: v,
ucl: mean_er_hist + 3.0 * std_er_hist,
cl: mean_er_hist,
lcl: (mean_er_hist - 3.0 * std_er_hist).max(0.0),
subgroup_data: None,
})
.collect();
let causes_hist = spc::check_western_electric_rules(&chart_data_hist);
if !causes_hist.is_empty() {
spc_results.insert(
"event_rate_historical".to_string(),
serde_json::json!("ALERT"),
);
for c in &causes_hist {
let (rule_violated, rule_number) = match c {
spc::SpecialCause::OutOfControl { .. } => ("rule_1_outlier", 1u8),
spc::SpecialCause::Shift { .. } => ("rule_2_shift", 2u8),
spc::SpecialCause::Trend { .. } => ("rule_3_trend", 3u8),
spc::SpecialCause::TwoOfThree { .. } => ("rule_4_two_of_three", 4u8),
};
let current_cycle = 0u64; tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "event_rate_historical",
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
"SPC rule violation classified (historical)"
);
all_special_causes.push(format!("event_rate_historical: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert("event_rate_historical".to_string(), serde_json::json!("OK"));
}
}
if historical_trace_durations.len() >= 9 {
let mean_td_hist = spc::spc_mean(&historical_trace_durations);
let std_td_hist = spc::spc_std_dev(&historical_trace_durations);
let chart_data_hist: Vec<spc::ChartData> = historical_trace_durations
.iter()
.enumerate()
.map(|(i, &v)| spc::ChartData {
timestamp: format!("cycle-{i}"),
value: v,
ucl: mean_td_hist + 3.0 * std_td_hist,
cl: mean_td_hist,
lcl: (mean_td_hist - 3.0 * std_td_hist).max(0.0),
subgroup_data: None,
})
.collect();
let causes_hist = spc::check_western_electric_rules(&chart_data_hist);
if !causes_hist.is_empty() {
spc_results.insert(
"trace_duration_historical".to_string(),
serde_json::json!("ALERT"),
);
for c in &causes_hist {
let (rule_violated, rule_number) = match c {
spc::SpecialCause::OutOfControl { .. } => ("rule_1_outlier", 1u8),
spc::SpecialCause::Shift { .. } => ("rule_2_shift", 2u8),
spc::SpecialCause::Trend { .. } => ("rule_3_trend", 3u8),
spc::SpecialCause::TwoOfThree { .. } => ("rule_4_two_of_three", 4u8),
};
let current_cycle = 0u64; tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "trace_duration_historical",
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
"SPC rule violation classified (historical)"
);
all_special_causes.push(format!("trace_duration_historical: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert(
"trace_duration_historical".to_string(),
serde_json::json!("OK"),
);
}
}
if historical_activity_freqs.len() >= 9 {
let mean_af_hist = spc::spc_mean(&historical_activity_freqs);
let std_af_hist = spc::spc_std_dev(&historical_activity_freqs);
let chart_data_hist: Vec<spc::ChartData> = historical_activity_freqs
.iter()
.enumerate()
.map(|(i, &v)| spc::ChartData {
timestamp: format!("cycle-{i}"),
value: v,
ucl: mean_af_hist + 3.0 * std_af_hist,
cl: mean_af_hist,
lcl: (mean_af_hist - 3.0 * std_af_hist).max(0.0),
subgroup_data: None,
})
.collect();
let causes_hist = spc::check_western_electric_rules(&chart_data_hist);
if !causes_hist.is_empty() {
spc_results.insert(
"activity_frequency_historical".to_string(),
serde_json::json!("ALERT"),
);
for c in &causes_hist {
let (rule_violated, rule_number) = match c {
spc::SpecialCause::OutOfControl { .. } => ("rule_1_outlier", 1u8),
spc::SpecialCause::Shift { .. } => ("rule_2_shift", 2u8),
spc::SpecialCause::Trend { .. } => ("rule_3_trend", 3u8),
spc::SpecialCause::TwoOfThree { .. } => ("rule_4_two_of_three", 4u8),
};
let current_cycle = 0u64; tracing::warn!(
target: "autonomic.spc",
spc_rule_type = rule_violated,
spc_metric = "activity_frequency_historical",
rule_number = rule_number,
cycle_count = current_cycle,
service_name = "wpm",
status = "error",
"SPC rule violation classified (historical)"
);
all_special_causes.push(format!("activity_frequency_historical: {:?}", c));
spc_rule_types.push(rule_violated); }
} else {
spc_results.insert(
"activity_frequency_historical".to_string(),
serde_json::json!("OK"),
);
}
}
}
let t_protection_end = wall_clock_us();
let time_so_far = t_protection_end.saturating_sub(t_cycle_start);
let cycle_latency_budget_exceeded = time_so_far > CYCLE_LATENCY_BUDGET_US;
let t_optimization_start = wall_clock_us();
let health_level = health_state_val as u8;
let rework_ratio_val = {
let trace_rework = perception["rework_ratio"].as_f64().unwrap_or(0.0) as f32;
let loop_density = perception["loop_density"].as_f64().unwrap_or(0.0) as f32;
(trace_rework * 0.7 + loop_density * 0.3).min(1.0)
};
let circuit_state_u8 = CIRCUIT_BREAKER.with(|cb| {
let cb = cb.borrow();
cb.as_rl_circuit_state()
});
let (
action_label,
reward_val,
agent_name,
cycle_count,
cumulative_reward,
autoprocess_state_id,
autoprocess_q_value,
) = RL_ORCHESTRATOR.with(|orch_cell| {
let mut orch = orch_cell.borrow_mut();
let activity_entropy = {
let freqs: Vec<usize> = perception["activity_frequencies"]
.as_object()
.map(|m| {
m.values()
.map(|v| v.as_f64().unwrap_or(0.0) as usize)
.collect()
})
.unwrap_or_default();
if freqs.is_empty() {
0.0
} else {
let total: usize = freqs.iter().sum();
let mut entropy = 0.0_f32;
for &count in &freqs {
if total > 0 {
let p = (count as f32) / (total as f32);
if p > 0.0 {
entropy -= p * p.log2();
}
}
}
if freqs.len() > 1 {
entropy / (freqs.len() as f32).log2()
} else {
entropy
}
}
};
let features: [f32; 8] = [
(event_count_val as f32 / 10_000.0).min(1.0), (trace_count_val as f32 / 1_000.0).min(1.0), (unique_activities_val as f32 / 100.0).min(1.0), (health_level as f32 / 4.0).min(1.0), if circuit_allowed { 1.0f32 } else { 0.0f32 }, (all_special_causes.len() as f32 / 10.0).min(1.0), activity_entropy, (orch.telemetry().cycle_count as f32 / 1_000.0).min(1.0), ];
let rl_state = RlState::from_features(&features, health_level, rework_ratio_val);
const IMPROVEMENT_THRESHOLD: u32 = 3;
let consecutive_successes = orch.telemetry().consecutive_successes;
let next_health_level = if guard_pass && circuit_allowed {
if consecutive_successes >= IMPROVEMENT_THRESHOLD {
health_level.saturating_sub(1) } else {
health_level }
} else {
(health_level + 1).min(4) };
let rl_next_state = RlState::from_features(&features, next_health_level, rework_ratio_val);
let (autoprocess_state_id, autoprocess_q_value) = {
#[cfg(feature = "cloud")]
{
AUTO_PROCESS_AGENT.with(|agent_cell| {
let mut agent = agent_cell.borrow_mut();
let decision = agent.run_cycle(
&rl_state,
&features,
orch.telemetry().last_reward,
&rl_next_state,
next_health_level == 4, guard_pass, circuit_state_u8,
);
(decision.state_id, decision.q_value)
})
}
#[cfg(not(feature = "cloud"))]
{
(0u32, 0.0f32) }
};
let (label, _reward) = orch.run_cycle(
&features,
&rl_state,
&rl_next_state,
all_special_causes.len(),
guard_pass,
circuit_allowed,
cycle_latency_budget_exceeded,
);
(
label,
orch.telemetry().last_reward,
orch.telemetry().active_agent_name.clone(),
orch.telemetry().cycle_count,
orch.telemetry().cumulative_reward,
autoprocess_state_id,
autoprocess_q_value,
)
});
CIRCUIT_BREAKER.with(|cb| {
let mut cb = cb.borrow_mut();
if circuit_allowed {
if guard_pass {
cb.record_success();
} else {
cb.record_failure();
}
}
});
let t_optimization_end = wall_clock_us();
let action_rationale = match action_label.as_str() {
"Continue" => "no degradation detected; maintain status quo",
"Scale" => "spc alerts or instability; increase exploration",
"Retry" => "transient failure; exponential backoff attempted",
"Fallback" => "persistent failure; fallback algorithm invoked",
"Restart" => "critical state or drift; system restart required",
_ => "unknown action; fallback applied",
};
let spc_primary_rule_type = spc_rule_types.first().copied().unwrap_or("none");
let circuit_recovery_signal = circuit_state.contains("Closed") && circuit_allowed;
let action_matches_spc_rule = match (spc_primary_rule_type, action_label.as_str()) {
("rule_1_outlier", "Retry" | "Scale") => true,
("rule_2_shift", "Scale" | "Fallback") => true,
("rule_3_trend", "Scale" | "Restart") => true,
("rule_4_two_of_three", "Scale" | "Continue") => true,
("none", _) => true, _ => false,
};
tracing::info!(
target: "autonomic",
action = %action_label,
action_rationale = action_rationale,
agent = %agent_name,
reward = %reward_val,
cumulative_reward = %cumulative_reward,
cycle = %cycle_count,
health = %health_state_val,
spc_alerts = %all_special_causes.len(),
spc_primary_rule_type = spc_primary_rule_type,
action_matches_spc_rule = action_matches_spc_rule,
circuit_state = %circuit_state,
circuit_allowed = %circuit_allowed,
circuit_recovery_signal = circuit_recovery_signal,
guard_pass = %guard_pass,
service_name = "wpm",
status = if guard_pass && circuit_allowed { "ok" } else { "warning" },
"autonomic.decision_action_selected"
);
let perception_us = t_perception_end.saturating_sub(t_perception_start);
let decision_us = t_decision_end.saturating_sub(t_decision_start);
let protection_us = t_protection_end.saturating_sub(t_protection_start);
let optimization_us = t_optimization_end.saturating_sub(t_optimization_start);
let total_us = t_optimization_end.saturating_sub(t_cycle_start);
let cycle_latency_budget_exceeded = total_us > CYCLE_LATENCY_BUDGET_US;
let (action_dispatch_detail, action_taken) = match action_label.as_str() {
"Continue" => (
"no-op: continue normal operation".to_string(),
"continue".to_string(),
),
"Scale" => {
RL_ORCHESTRATOR.with(|orch_cell| {
let mut orch = orch_cell.borrow_mut();
orch.set_exploration_rate(0.5);
});
SCALE_BOOST_REMAINING.with(|sb| {
sb.set(3); });
(
"exploration boost: epsilon=0.5 x3 cycles".to_string(),
"scale".to_string(),
)
}
"Retry" => {
(
"backoff: exponential retry scheduling".to_string(),
"retry".to_string(),
)
}
"Fallback" => {
#[cfg(feature = "ml")]
let fallback_algo = FALLBACK_BANDIT.with(|fb| {
let mut guard = fb.borrow_mut();
if guard.is_none() {
*guard = Some(crate::prediction_resource::BanditState {
arms: vec![
crate::prediction_resource::BanditArm {
name: "dfg".to_string(),
total_reward: 0.0,
pull_count: 0,
},
crate::prediction_resource::BanditArm {
name: "heuristic_miner".to_string(),
total_reward: 0.0,
pull_count: 0,
},
crate::prediction_resource::BanditArm {
name: "alpha_plus_plus".to_string(),
total_reward: 0.0,
pull_count: 0,
},
],
total_pulls: 0,
});
}
crate::prediction_resource::compute_ucb1_selection(
guard.as_ref().expect(
"invariant: guard is Some (set to Some in the is_none branch above)",
),
std::f64::consts::SQRT_2,
)
.map(|sel| sel.selected.clone())
.unwrap_or_else(|_| "dfg".to_string())
});
#[cfg(not(feature = "ml"))]
let fallback_algo = "dfg".to_string();
LAST_ALGORITHM.with(|la| {
*la.borrow_mut() = fallback_algo.clone();
});
(
format!(
"fallback: switch to {}, reduce event budget 10%",
fallback_algo
),
"fallback".to_string(),
)
}
"Restart" => {
SPC_HISTORY.with(|spc| {
*spc.borrow_mut() = spc_history::SpcHistory::new();
});
CIRCUIT_BREAKER.with(|cb| {
*cb.borrow_mut() = self_healing::CircuitBreaker::new();
});
RL_ORCHESTRATOR.with(|orch_cell| {
let mut orch = orch_cell.borrow_mut();
orch.reset_all_exploration_rates();
});
(
"restart: reset SPC, circuit breaker, epsilon=1.0".to_string(),
"restart".to_string(),
)
}
_ => ("unknown action".to_string(), "unknown".to_string()),
};
let result = serde_json::json!({
"cycle_result": {
"success": guard_pass && circuit_allowed,
"perception": {
"event_count": event_count_val,
"trace_count": trace_count_val,
"unique_activities": unique_activities_val,
"has_timestamps": perception["has_timestamps"],
"health_state": perception["health_label"],
"health_score": health_state_val,
},
"decision": {
"guard_result": guard_pass,
"pattern_result": pattern_name,
"pattern_ticks": pattern_ticks,
},
"protection": {
"circuit_state": circuit_state,
"circuit_allowed": circuit_allowed,
"special_causes": all_special_causes,
"spc_results": spc_results,
},
"optimization": {
"rl_action": action_label,
"rl_agent": agent_name,
"reward": reward_val,
"cycle_count": cycle_count,
"cumulative_reward": cumulative_reward,
"health_score": health_state_val,
"action_taken": action_taken,
"dispatch_detail": action_dispatch_detail,
"autoprocess_state_id": autoprocess_state_id,
"autoprocess_q_value": autoprocess_q_value,
},
},
"timing": {
"perception_us": perception_us,
"decision_us": decision_us,
"protection_us": protection_us,
"optimization_us": optimization_us,
"total_us": total_us,
"precision": "1ms_clock",
"cycle_latency_budget_exceeded": cycle_latency_budget_exceeded,
},
});
Ok(serde_json::to_string(&result).unwrap_or_else(|_| "{}".to_string()))
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[wasm_bindgen]
pub struct RlState {
pub health_level: u8, pub event_rate_q: u8, pub activity_count_q: u8, pub spc_alert_level: u8, pub drift_status: u8, pub rework_ratio_q: u8, pub circuit_state: u8, pub cycle_phase: u8, }
impl RlState {
#[must_use = "returns constructed RlState"]
pub fn from_features(features: &[f32; 8], health_level: u8, rework_ratio: f32) -> Self {
let activity_count = (features[2] * 100.0) as u32;
let activity_count_q = Self::quantize_activity_count(activity_count);
let drift_low = get_drift_threshold_low();
let drift_high = get_drift_threshold_high();
let drift_status = if features[6] < drift_low {
0
} else if features[6] < drift_high {
1
} else {
2
};
let event_rate_q = Self::quantize_event_rate(features[0]);
let spc_alert_level = Self::quantize_spc_alerts(features[5]);
let circuit_state = if features[4] > 0.5 { 0 } else { 1 };
let cycle_phase = Self::quantize_cycle_phase(features[7]);
let rework_ratio_q = Self::quantize_rework_ratio(rework_ratio);
Self {
health_level,
event_rate_q,
activity_count_q,
spc_alert_level,
drift_status,
rework_ratio_q,
circuit_state,
cycle_phase,
}
}
fn quantize_activity_count(count: u32) -> u8 {
match count {
0..=10 => 0,
11..=20 => 1,
21..=30 => 2,
31..=40 => 3,
41..=50 => 4,
51..=60 => 5,
61..=70 => 6,
_ => 7,
}
}
fn quantize_event_rate(normalized_rate: f32) -> u8 {
let rate = (normalized_rate * 10000.0) as u32;
match rate {
0..=500 => 0,
501..=1000 => 1,
1001..=2000 => 2,
2001..=3000 => 3,
3001..=4000 => 4,
4001..=5000 => 5,
5001..=7500 => 6,
_ => 7,
}
}
fn quantize_spc_alerts(normalized_alerts: f32) -> u8 {
let alert_count = (normalized_alerts * 10.0) as u32;
match alert_count {
0 => 0,
1..=2 => 1,
3..=5 => 2,
_ => 3,
}
}
fn quantize_cycle_phase(normalized_cycles: f32) -> u8 {
let cycles = (normalized_cycles * 1000.0) as u32;
match cycles {
0..=10 => 0, 11..=50 => 1, 51..=100 => 2, _ => 3, }
}
fn quantize_rework_ratio(rework_ratio: f32) -> u8 {
let ratio_percent = (rework_ratio * 100.0) as u32;
match ratio_percent {
0..=5 => 0, 6..=15 => 1, 16..=25 => 2, 26..=40 => 3, 41..=55 => 4, 56..=70 => 5, 71..=85 => 6, _ => 7, }
}
}
#[cfg(feature = "cloud")]
impl reinforcement::WorkflowState for RlState {
fn features(&self) -> Vec<f32> {
vec![
self.health_level as f32 / 4.0,
self.event_rate_q as f32 / 7.0,
self.activity_count_q as f32 / 7.0,
self.spc_alert_level as f32 / 3.0,
self.drift_status as f32 / 2.0,
self.rework_ratio_q as f32 / 7.0,
self.circuit_state as f32 / 2.0,
self.cycle_phase as f32 / 3.0,
]
}
fn is_terminal(&self) -> bool {
self.health_level == 4 }
}
#[derive(Clone, Copy, PartialEq, Eq, std::hash::Hash, std::fmt::Debug)]
pub enum RlAction {
Continue = 0,
Scale = 1,
Retry = 2,
Fallback = 3,
Restart = 4,
}
impl RlAction {
#[must_use = "returns action name"]
pub fn name(&self) -> &'static str {
match self {
RlAction::Continue => "Continue",
RlAction::Scale => "Scale",
RlAction::Retry => "Retry",
RlAction::Fallback => "Fallback",
RlAction::Restart => "Restart",
}
}
}
#[cfg(feature = "cloud")]
impl reinforcement::WorkflowAction for RlAction {
const ACTION_COUNT: usize = 5;
fn to_index(&self) -> usize {
*self as usize
}
fn from_index(idx: usize) -> Option<Self> {
match idx {
0 => Some(RlAction::Continue),
1 => Some(RlAction::Scale),
2 => Some(RlAction::Retry),
3 => Some(RlAction::Fallback),
4 => Some(RlAction::Restart),
_ => None,
}
}
}
#[wasm_bindgen]
pub fn create_rl_state(
health_level: u8,
event_rate_q: u8,
activity_count_q: u8,
spc_alert_level: u8,
drift_status: u8,
rework_ratio_q: u8,
circuit_state: u8,
cycle_phase: u8,
) -> RlState {
RlState {
health_level,
event_rate_q,
activity_count_q,
spc_alert_level,
drift_status,
rework_ratio_q,
circuit_state,
cycle_phase,
}
}
#[wasm_bindgen]
pub fn rl_state_from_features(features: &[f32], health_level: u8, rework_ratio: f32) -> RlState {
let mut arr = [0.0f32; 8];
for (i, &val) in features.iter().enumerate() {
if i < 8 {
arr[i] = val;
}
}
RlState::from_features(&arr, health_level, rework_ratio)
}
#[wasm_bindgen]
pub fn rl_state_health_level(state: &RlState) -> u8 {
state.health_level
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_reset() -> Result<String, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
Ok("RL orchestrator reset".to_string())
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_active_agent() -> Result<u8, JsValue> {
RL_ORCHESTRATOR.with(|orch| Ok(orch.borrow().active_agent() as u8))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_switch_agent(agent_type: u8) -> Result<String, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
let mut orch = orch.borrow_mut();
match rl_orchestrator::AgentType::from_u8(agent_type) {
Some(at) => {
orch.switch_agent(at);
Ok(format!("Switched to {}", at.name()))
}
None => Err(crate::error::js_val(&format!(
"Invalid agent type: {} (must be 0-4)",
agent_type
))),
}
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_set_linucb(enabled: bool) -> Result<String, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
orch.borrow_mut().set_linucb_selection(enabled);
Ok(format!("LinUCB selection: {enabled}"))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_telemetry() -> Result<String, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
let orch = orch.borrow();
let t = orch.telemetry();
Ok(serde_json::to_string(t).unwrap_or_else(|_| "{}".to_string()))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn rl_orchestrator_get_telemetry() -> Result<JsValue, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
let orch = orch.borrow();
let t = orch.telemetry();
let obj = js_sys::Object::new();
js_sys::Reflect::set(
&obj,
&crate::error::js_val("cycle_count"),
&JsValue::from(t.cycle_count),
)
.map_err(|e| crate::error::js_val(&format!("Failed to set cycle_count: {:?}", e)))?;
js_sys::Reflect::set(
&obj,
&crate::error::js_val("last_health_state"),
&JsValue::from(t.last_health_state),
)
.map_err(|e| crate::error::js_val(&format!("Failed to set last_health_state: {:?}", e)))?;
js_sys::Reflect::set(
&obj,
&crate::error::js_val("cumulative_reward"),
&JsValue::from(t.cumulative_reward),
)
.map_err(|e| crate::error::js_val(&format!("Failed to set cumulative_reward: {:?}", e)))?;
js_sys::Reflect::set(
&obj,
&crate::error::js_val("last_reward"),
&JsValue::from(t.last_reward),
)
.map_err(|e| crate::error::js_val(&format!("Failed to set last_reward: {:?}", e)))?;
js_sys::Reflect::set(
&obj,
&crate::error::js_val("last_spc_alert_count"),
&JsValue::from(t.last_spc_alert_count),
)
.map_err(|e| {
crate::error::js_val(&format!("Failed to set last_spc_alert_count: {:?}", e))
})?;
Ok(JsValue::from(obj))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn serialize_rl_state() -> Result<String, JsValue> {
RL_ORCHESTRATOR.with(|orch| {
let orch_ref = orch.borrow();
let telemetry = orch_ref.telemetry();
let agent_q_tables = orch_ref.export_all_q_tables();
let state = rl_state_serialization::SerializedRlState {
telemetry: rl_state_serialization::RlTelemetry {
cycle_count: telemetry.cycle_count,
last_health_state: telemetry.last_health_state,
last_action_label: telemetry.last_action_label.clone(),
last_spc_alert_count: telemetry.last_spc_alert_count,
cumulative_reward: telemetry.cumulative_reward as f64,
},
active_agent: orch_ref.active_agent() as u8,
linucb_enabled: orch_ref.linucb_selection_enabled(),
agent_q_tables,
};
serde_json::to_string(&state)
.map_err(|e| crate::error::js_val(&format!("Serialization failed: {e}")))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn restore_rl_state(json: &str) -> Result<String, JsValue> {
let state: rl_state_serialization::SerializedRlState = serde_json::from_str(json)
.map_err(|e| crate::error::js_val(&format!("Invalid JSON: {e}")))?;
RL_ORCHESTRATOR
.with(|orch| {
let mut orch_ref = orch.borrow_mut();
if let Some(agent_type) = rl_orchestrator::AgentType::from_u8(state.active_agent) {
orch_ref.switch_agent(agent_type);
}
orch_ref.set_linucb_selection(state.linucb_enabled);
let mut restored_telemetry = rl_orchestrator::CycleTelemetry {
cycle_count: state.telemetry.cycle_count,
last_health_state: state.telemetry.last_health_state,
last_action_label: state.telemetry.last_action_label.clone(),
last_spc_alert_count: state.telemetry.last_spc_alert_count,
cumulative_reward: state.telemetry.cumulative_reward as f32,
..Default::default()
};
if let Some(agent_type) = rl_orchestrator::AgentType::from_u8(state.active_agent) {
restored_telemetry.active_agent_name = agent_type.name().to_string();
}
orch_ref.restore_telemetry(restored_telemetry);
let num_q_tables = state.agent_q_tables.len();
let mut restoration_status = String::new();
if num_q_tables > 0 {
let (restored, skipped) = orch_ref.restore_all_q_tables(state.agent_q_tables);
restoration_status = if skipped > 0 {
format!(
"; {} Q-tables restored, {} skipped (policy divergence risk)",
restored, skipped
)
} else {
format!("; all {restored} Q-tables restored successfully")
};
}
Ok::<String, JsValue>(format!(
"Restored RL state from cycle {} (agent {}, linucb={}){}",
state.telemetry.cycle_count,
state.active_agent,
state.linucb_enabled,
restoration_status
))
})
.map_err(|_e| crate::error::js_val("Failed to restore RL state"))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn get_spc_history() -> Result<String, JsValue> {
SPC_HISTORY.with(|history| {
let history_ref = history.borrow();
let snapshots = history_ref.get_all_snapshots();
let serialized = serde_json::json!({
"snapshots": snapshots,
"cycle_count": history_ref.cycle_count
});
serde_json::to_string(&serialized)
.map_err(|e| crate::error::js_val(&format!("Serialization failed: {e}")))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn set_spc_history(json: &str) -> Result<String, JsValue> {
#[derive(serde::Deserialize)]
struct SpcHistoryJson {
snapshots: Vec<spc_history::SpcSnapshot>,
cycle_count: u64,
}
let data: SpcHistoryJson = serde_json::from_str(json)
.map_err(|e| crate::error::js_val(&format!("Invalid JSON: {e}")))?;
let snapshot_count = data.snapshots.len();
let cycle_count = data.cycle_count;
SPC_HISTORY.with(|history| {
history.borrow_mut().restore(data.snapshots, cycle_count);
});
Ok(format!("Restored {snapshot_count} SPC snapshots"))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn circuit_breaker_get_state() -> Result<String, JsValue> {
CIRCUIT_BREAKER.with(|cb| {
let cb_ref = cb.borrow();
let state_json = cb_ref.to_state_json();
serde_json::to_string(&state_json)
.map_err(|e| crate::error::js_val(&format!("Serialization failed: {e}")))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn circuit_breaker_set_state(json: &str) -> Result<String, JsValue> {
let state_json: self_healing::CircuitBreakerStateJson = serde_json::from_str(json)
.map_err(|e| crate::error::js_val(&format!("Invalid JSON: {e}")))?;
CIRCUIT_BREAKER.with(|cb| {
let mut cb_ref = cb.borrow_mut();
*cb_ref = self_healing::CircuitBreaker::from_state_json(state_json);
});
Ok("Circuit breaker state restored".to_string())
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn circuit_breaker_configure(config_json: &str) -> Result<String, JsValue> {
CIRCUIT_BREAKER.with(
|breaker| match self_healing::CircuitBreaker::from_json(config_json) {
Ok(new_breaker) => {
*breaker.borrow_mut() = new_breaker;
Ok("Circuit breaker configured".to_string())
}
Err(e) => Err(crate::error::js_val(&e)),
},
)
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn circuit_breaker_get_config() -> Result<String, JsValue> {
CIRCUIT_BREAKER.with(|breaker| {
let breaker_ref = breaker.borrow();
let config = self_healing::CircuitBreakerConfigJson {
failure_threshold: breaker_ref.failure_threshold(),
success_threshold: breaker_ref.success_threshold(),
open_timeout_ms: breaker_ref.open_timeout_ms(),
half_open_timeout_ms: breaker_ref.half_open_timeout_ms(),
};
serde_json::to_string(&config)
.map_err(|e| crate::error::js_val(&format!("Serialization failed: {e}")))
})
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn circuit_breaker_reset() -> Result<String, JsValue> {
CIRCUIT_BREAKER.with(|cb| {
*cb.borrow_mut() = self_healing::CircuitBreaker::new();
Ok("Circuit breaker reset".to_string())
})
}
#[cfg(feature = "statrs")]
pub use statrs::statistics::{Data, Median};
#[cfg(all(feature = "hand_rolled_stats", not(feature = "statrs")))]
pub use hand_stats::Median;
#[cfg(all(feature = "hand_rolled_stats", not(feature = "statrs")))]
pub struct Data {
inner: Vec<f64>,
}
#[cfg(all(feature = "hand_rolled_stats", not(feature = "statrs")))]
impl Data {
#[must_use = "returns constructed Data"]
pub fn new(data: Vec<f64>) -> Self {
Self { inner: data }
}
#[must_use = "returns computed median"]
pub fn median(&self) -> f64 {
hand_stats::median(&mut self.inner.clone()).unwrap_or(0.0)
}
#[must_use = "returns computed mean"]
pub fn mean(&self) -> f64 {
hand_stats::mean(&self.inner).unwrap_or(0.0)
}
#[must_use = "returns computed percentile"]
pub fn percentile(&self, p: f64) -> f64 {
hand_stats::percentile(&mut self.inner.clone(), p).unwrap_or(0.0)
}
#[must_use = "returns computed standard deviation"]
pub fn std_deviation(&self) -> f64 {
hand_stats::std_deviation(&self.inner).unwrap_or(0.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_iso8601_duration_basic() {
let dur = parse_iso8601_duration("2026-04-13T10:00:00Z", "2026-04-13T11:00:00Z");
assert_eq!(dur, 3_600_000.0); }
#[test]
fn test_parse_iso8601_duration_same_timestamp() {
let dur = parse_iso8601_duration("2026-04-13T10:00:00Z", "2026-04-13T10:00:00Z");
assert_eq!(dur, 0.0);
}
#[test]
fn test_parse_iso8601_duration_reverse_order() {
let dur = parse_iso8601_duration("2026-04-13T11:00:00Z", "2026-04-13T10:00:00Z");
assert_eq!(dur, -3_600_000.0); }
#[test]
fn test_parse_iso8601_duration_empty_strings() {
let dur = parse_iso8601_duration("", "");
assert_eq!(dur, 0.0);
}
#[test]
fn test_parse_iso8601_duration_with_offset() {
let dur = parse_iso8601_duration("2026-04-13T10:00:00+00:00", "2026-04-13T10:00:01+00:00");
assert_eq!(dur, 1_000.0); }
#[test]
fn test_parse_iso8601_duration_cross_day() {
let dur = parse_iso8601_duration("2026-04-13T23:00:00Z", "2026-04-14T01:00:00Z");
assert_eq!(dur, 7_200_000.0); }
#[test]
fn test_parse_iso8601_duration_with_fractional_seconds() {
let dur = parse_iso8601_duration("2026-04-13T10:00:00.500Z", "2026-04-13T10:00:01.500Z");
assert_eq!(dur, 1_000.0); }
#[test]
fn test_parse_iso8601_duration_string_lengths_differ_but_times_dont() {
let dur = parse_iso8601_duration("2026-01-13T10:00:00Z", "2026-11-13T10:00:00Z");
assert!(dur > 26_000_000_000.0); assert!(dur < 27_000_000_000.0);
}
#[test]
fn test_parse_iso8601_duration_string_lengths_same_but_times_differ() {
let dur = parse_iso8601_duration("2026-01-01T00:00:00Z", "2026-12-31T23:59:59Z");
assert!(dur > 31_000_000_000.0); }
#[test]
#[cfg(feature = "cloud")]
fn test_restore_rl_state_actually_restores_cycle_count() {
let state_json = r#"{
"telemetry": {
"cycle_count": 42,
"last_health_state": 1,
"last_action_label": "ADAPT_TIMEOUT",
"last_spc_alert_count": 1,
"cumulative_reward": -3.5
},
"active_agent": 0,
"linucb_enabled": true
}"#;
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
RL_ORCHESTRATOR.with(|orch| {
assert_eq!(orch.borrow().telemetry().cycle_count, 0);
});
restore_rl_state(state_json).expect("restore should succeed");
RL_ORCHESTRATOR.with(|orch| {
let orch = orch.borrow();
assert_eq!(
orch.telemetry().cycle_count,
42,
"cycle_count should be restored"
);
assert_eq!(
orch.active_agent() as u8,
0,
"active agent should be restored"
);
assert!(orch.linucb_selection_enabled(), "linucb should be restored");
});
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
}
#[test]
#[cfg(feature = "cloud")]
fn test_restore_rl_state_keeps_active_agent_name_consistent() {
let state_json = r#"{
"telemetry": {
"cycle_count": 7,
"last_health_state": 0,
"last_action_label": "Continue",
"last_spc_alert_count": 0,
"cumulative_reward": 1.5
},
"active_agent": 2,
"linucb_enabled": false
}"#;
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
restore_rl_state(state_json).expect("restore should succeed");
RL_ORCHESTRATOR.with(|orch| {
let orch = orch.borrow();
assert_eq!(
orch.active_agent() as u8,
2,
"active_agent must be restored to DoubleQLearning (2)"
);
assert_eq!(orch.telemetry().cycle_count, 7);
assert_eq!(
orch.telemetry().active_agent_name,
"DoubleQLearning",
"telemetry.active_agent_name must agree with active_agent enum"
);
});
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
}
#[test]
#[cfg(feature = "cloud")]
fn test_serialize_restore_rl_state_full_roundtrip() {
RL_ORCHESTRATOR.with(|orch| {
let mut o = orch.borrow_mut();
*o = rl_orchestrator::RlOrchestrator::new();
o.switch_agent(rl_orchestrator::AgentType::ExpectedSARSA);
o.set_linucb_selection(true);
let t = o.telemetry_mut();
t.cycle_count = 123;
t.last_health_state = 3;
t.last_action_label = "Restart".to_string();
t.last_spc_alert_count = 4;
t.cumulative_reward = -7.25; });
let serialized = serialize_rl_state().expect("serialize must succeed");
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
restore_rl_state(&serialized).expect("restore must succeed");
RL_ORCHESTRATOR.with(|orch| {
let o = orch.borrow();
assert_eq!(o.active_agent() as u8, 3, "active_agent (ExpectedSARSA=3)");
assert!(o.linucb_selection_enabled(), "linucb flag");
let t = o.telemetry();
assert_eq!(t.cycle_count, 123, "cycle_count");
assert_eq!(t.last_health_state, 3, "last_health_state");
assert_eq!(t.last_action_label, "Restart", "last_action_label");
assert_eq!(t.last_spc_alert_count, 4, "last_spc_alert_count");
assert!(
(t.cumulative_reward - (-7.25_f32)).abs() < f32::EPSILON,
"cumulative_reward"
);
assert_eq!(t.active_agent_name, "ExpectedSARSA");
});
RL_ORCHESTRATOR.with(|orch| {
*orch.borrow_mut() = rl_orchestrator::RlOrchestrator::new();
});
}
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn run_agentic_pipeline(task_json: &str) -> Result<String, JsValue> {
use crate::agentic::prelude::*;
let task: agentic::types::TaskContext = serde_json::from_str(task_json)
.map_err(|e| crate::error::js_val(&format!("invalid TaskContext JSON: {e}")))?;
let compiler = DefaultPromptBindingCompiler;
let bindings = compiler
.compile_bindings(&task)
.map_err(|e| crate::error::js_val(&format!("compile_bindings failed: {e}")))?;
let evidence_checker = DefaultEvidenceSufficiencyChecker;
let evidence_sufficient = evidence_checker
.is_sufficient(&task)
.map_err(|e| crate::error::js_val(&format!("is_sufficient failed: {e}")))?;
let gaps = evidence_checker
.summarize_gaps(&task)
.map_err(|e| crate::error::js_val(&format!("summarize_gaps failed: {e}")))?;
let escalation_engine = DefaultEscalationEngine;
let escalation = escalation_engine
.evaluate_escalation(&task)
.map_err(|e| crate::error::js_val(&format!("evaluate_escalation failed: {e}")))?;
let result = serde_json::json!({
"bindings": bindings,
"evidence_sufficient": evidence_sufficient,
"should_escalate": escalation.should_escalate,
"escalation_target": escalation.target_role,
"gaps": gaps,
});
serde_json::to_string(&result)
.map_err(|e| crate::error::js_val(&format!("serialization failed: {e}")))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn validate_agentic_handoff(request_json: &str) -> Result<String, JsValue> {
use crate::agentic::prelude::*;
let req: agentic::types::HandoffRequest = serde_json::from_str(request_json)
.map_err(|e| crate::error::js_val(&format!("invalid HandoffRequest JSON: {e}")))?;
let validator = DefaultHandoffValidator;
let decision = validator
.validate_handoff(&req)
.map_err(|e| crate::error::js_val(&format!("validate_handoff failed: {e}")))?;
serde_json::to_string(&decision)
.map_err(|e| crate::error::js_val(&format!("serialization failed: {e}")))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn evaluate_agentic_counterfactuals(task_json: &str) -> Result<String, JsValue> {
use crate::agentic::prelude::*;
let task: agentic::types::TaskContext = serde_json::from_str(task_json)
.map_err(|e| crate::error::js_val(&format!("invalid TaskContext JSON: {e}")))?;
let evaluator = DefaultCounterfactualEvaluator;
let result = evaluator
.evaluate_options(&task)
.map_err(|e| crate::error::js_val(&format!("evaluate_options failed: {e}")))?;
serde_json::to_string(&result)
.map_err(|e| crate::error::js_val(&format!("serialization failed: {e}")))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn run_agentic_jtbd_suite(cases_json: &str) -> Result<String, JsValue> {
use crate::agentic::prelude::*;
let cases: Vec<agentic::types::JtbdCase> = serde_json::from_str(cases_json)
.map_err(|e| crate::error::js_val(&format!("invalid JtbdCase[] JSON: {e}")))?;
let runner = DefaultJtbdRunner;
let results = runner
.run_suite(&cases)
.map_err(|e| crate::error::js_val(&format!("run_suite failed: {e}")))?;
let passed = results.iter().filter(|r| r.passed).count();
let failed = results.len() - passed;
let output = serde_json::json!({
"passed": passed,
"failed": failed,
"results": results,
});
serde_json::to_string(&output)
.map_err(|e| crate::error::js_val(&format!("serialization failed: {e}")))
}
#[cfg(feature = "cloud")]
#[wasm_bindgen]
pub fn truex_verify_receipt(envelope_json: &str) -> Result<String, JsValue> {
let envelope: serde_json::Value = serde_json::from_str(envelope_json)
.map_err(|e| crate::error::js_val(&format!("Invalid JSON: {e}")))?;
let report = crate::receipt::ReceiptDoctor::audit(&envelope);
let status = match report.state {
crate::receipt::ReceiptDoctorState::Admitted => "ReceiptAdmitted",
crate::receipt::ReceiptDoctorState::Refused => "ReceiptRefused",
};
let batch = ""; let receipt = "";
let out = serde_json::json!({
"status": status,
"admitted": report.admitted,
"findings": report.findings,
"equivalence_class": "EquivalentUnderProfileV1",
"computed_batch_hash": batch,
"computed_receipt_hash": receipt
});
Ok(serde_json::to_string(&out).expect("invariant: json! literal is always serializable"))
}
#[cfg(feature = "ocel")]
#[wasm_bindgen]
pub fn evaluate_ocpq(ocel_json: &str, query_str: &str) -> Result<String, JsValue> {
let ocel: crate::models::OCEL = serde_json::from_str(ocel_json)
.map_err(|e| crate::error::js_val(&format!("Invalid OCEL JSON: {e}")))?;
let query = ocpq_parser::parse(query_str)
.map_err(|e| crate::error::js_val(&format!("OCPQ Parse Error: {e}")))?;
let verdict = ocpq_runtime::evaluate(&ocel, &query);
serde_json::to_string(&verdict)
.map_err(|e| crate::error::js_val(&format!("Serialization failed: {e}")))
}
#[cfg(feature = "feature-gall")]
pub mod gall;