use wasm4pm_compat::prediction::{
ComplianceKind, ComplianceTarget, DriftSignal, NextActivity, OutcomeLabel, PredictionHorizon,
PredictionProblem, PredictionRefusal, PredictionTarget, PrefixTrace, RemainingTime, RiskScore,
};
fn main() {
println!("=== Prediction problem shapes (structure only) ===\n");
println!("Part 1: PredictionHorizon variants");
let h_full = PredictionHorizon::FullCase;
let h_events = PredictionHorizon::Events(5);
let h_time = PredictionHorizon::TimeUnits(3600);
assert_eq!(format!("{h_full}"), "full-case");
assert_eq!(format!("{h_events}"), "events(5)");
assert_eq!(format!("{h_time}"), "time(3600s)");
assert_eq!(PredictionHorizon::default(), PredictionHorizon::FullCase);
println!(" ✓ FullCase: \"{h_full}\"");
println!(" ✓ Events(5): \"{h_events}\"");
println!(" ✓ TimeUnits(3600): \"{h_time}\"");
println!(" ✓ Default: FullCase");
println!("\nPart 2: PredictionTarget — all six kinds");
let targets = [
(PredictionTarget::NextActivity, "next-activity"),
(PredictionTarget::OutcomeLabel, "outcome-label"),
(PredictionTarget::RemainingTime, "remaining-time"),
(PredictionTarget::DriftSignal, "drift-signal"),
(PredictionTarget::Risk, "risk"),
(
PredictionTarget::ComplianceConstraint,
"compliance-constraint",
),
];
for (target, expected) in &targets {
let displayed = format!("{target}");
assert_eq!(&displayed, expected, "PredictionTarget::Display mismatch");
println!(" ✓ {expected}");
}
println!("\nPart 3: PredictionProblem witnesses");
let p_next = PredictionProblem::<NextActivity>::new(
vec!["register".into(), "review".into()],
PredictionTarget::NextActivity,
)
.with_horizon(5);
assert_eq!(p_next.prefix_len(), 2);
assert_eq!(p_next.horizon, Some(5));
assert_eq!(p_next.target, PredictionTarget::NextActivity);
println!(
" ✓ NextActivity: prefix len={}, horizon={:?}",
p_next.prefix_len(),
p_next.horizon
);
let p_outcome = PredictionProblem::<OutcomeLabel>::new(
vec!["submit".into()],
PredictionTarget::OutcomeLabel,
);
assert_eq!(p_outcome.horizon, None); println!(" ✓ OutcomeLabel: horizon=None (FullCase)");
let p_time = PredictionProblem::<RemainingTime>::new(
vec!["start".into(), "approve".into(), "escalate".into()],
PredictionTarget::RemainingTime,
);
assert_eq!(p_time.prefix_len(), 3);
println!(" ✓ RemainingTime: prefix len={}", p_time.prefix_len());
let p_drift = PredictionProblem::<DriftSignal>::new(
vec!["a".into(), "b".into()],
PredictionTarget::DriftSignal,
);
assert_eq!(p_drift.target, PredictionTarget::DriftSignal);
println!(" ✓ DriftSignal: target={}", p_drift.target);
let p_risk = PredictionProblem::<RiskScore>::new(vec!["login".into()], PredictionTarget::Risk)
.with_horizon(1);
assert_eq!(p_risk.horizon, Some(1));
println!(" ✓ RiskScore: horizon=Some(1)");
let p_prefix = PredictionProblem::<PrefixTrace>::new(
vec!["step-1".into()],
PredictionTarget::NextActivity,
);
assert_eq!(p_prefix.prefix_len(), 1);
println!(" ✓ PrefixTrace witness: prefix_len=1");
let p_compliance = PredictionProblem::<ComplianceTarget>::new(
vec!["file".into(), "approve".into()],
PredictionTarget::ComplianceConstraint,
);
assert_eq!(p_compliance.target, PredictionTarget::ComplianceConstraint);
println!(" ✓ ComplianceTarget: target={}", p_compliance.target);
println!("\nPart 4: ComplianceKind");
assert_eq!(format!("{}", ComplianceKind::Monitoring), "monitoring");
assert_eq!(format!("{}", ComplianceKind::Audit), "audit");
assert_eq!(
format!("{}", ComplianceKind::Certification),
"certification"
);
assert_eq!(ComplianceKind::default(), ComplianceKind::Monitoring);
println!(" ✓ Monitoring / Audit / Certification Display");
println!(" ✓ Default: Monitoring");
println!("\nPart 5: PredictionRefusal — named laws");
let refusals: &[PredictionRefusal] = &[
PredictionRefusal::MissingPrefix,
PredictionRefusal::MissingTarget,
PredictionRefusal::EmptyPrefix,
PredictionRefusal::TargetUnsupported,
PredictionRefusal::NonPrefixTrace,
PredictionRefusal::ConstraintNotNamed,
];
let expected_displays = [
"prediction problem refused: MissingPrefix",
"prediction problem refused: MissingTarget",
"prediction problem refused: EmptyPrefix",
"prediction problem refused: TargetUnsupported",
"prediction problem refused: NonPrefixTrace",
"prediction problem refused: ConstraintNotNamed",
];
for (r, expected) in refusals.iter().zip(expected_displays.iter()) {
let displayed = format!("{r}");
assert_eq!(&displayed, expected, "PredictionRefusal::Display mismatch");
println!(" ✓ {expected}");
}
println!("\nPart 6: structure-only contract");
let question = PredictionProblem::<NextActivity>::new(
vec!["open".into(), "assign".into(), "work".into()],
PredictionTarget::NextActivity,
);
assert_eq!(question.prefix_len(), 3);
assert!(question.horizon.is_none());
println!(" ✓ PredictionProblem exposes only shape (prefix_len, target, horizon)");
println!(" ✓ No predict() method — structure only. Graduate to wasm4pm.");
println!("\n=== All assertions passed — prediction module surface is witnessed ===");
println!(" Documented: PredictionHorizon, PredictionTarget, PredictionProblem<T>,");
println!(" ComplianceKind, PredictionRefusal (6 named laws)");
println!(" Witness: Display strings + field values asserted; breaks on rename or removal.");
}