use crate::types::*;
pub const TIER_BIT_A: u32 = 1 << 0; pub const TIER_BIT_B: u32 = 1 << 1; pub const TIER_BIT_C: u32 = 1 << 2; pub const TIER_BIT_D: u32 = 1 << 3; pub const TIER_BIT_E: u32 = 1 << 4; pub const TIER_BIT_F: u32 = 1 << 5; pub const TIER_BIT_EXTRA: u32 = 1 << 6; pub const TIER_BIT_G: u32 = 1 << 7; pub const TIER_BIT_H: u32 = 1 << 8; pub const TIER_BIT_I: u32 = 1 << 9; pub const TIER_BIT_J: u32 = 1 << 10; pub const TIER_BIT_K: u32 = 1 << 11; pub const TIER_BIT_L: u32 = 1 << 12; pub const TIER_BIT_M: u32 = 1 << 13; pub const TIER_BIT_N: u32 = 1 << 14; pub const TIER_BIT_O: u32 = 1 << 15; pub const TIER_BIT_P: u32 = 1 << 16; pub const TIER_BIT_Q: u32 = 1 << 17; pub const TIER_BIT_R: u32 = 1 << 18; pub const TIER_BIT_S: u32 = 1 << 19; pub const TIER_BIT_T: u32 = 1 << 20; pub const TIER_BIT_U: u32 = 1 << 21;
pub const TIER_BIT_V: u32 = 1 << 22; pub const TIER_BIT_W: u32 = 1 << 23; pub const TIER_BIT_X: u32 = 1 << 24; pub const TIER_BIT_Y: u32 = 1 << 25; pub const TIER_BIT_Z: u32 = 1 << 26; pub const TIER_BIT_AA: u32 = 1 << 27; pub const TIER_BIT_BB: u32 = 1 << 28; pub const TIER_BIT_CC: u32 = 1 << 29; pub const TIER_BIT_DD: u32 = 1 << 30; pub const TIER_BIT_EE: u32 = 1 << 31;
pub fn affinity_tiers_for(reason_code: ReasonCode, min_correlation_count: u16) -> u32 {
let base = match reason_code {
ReasonCode::SustainedOutwardDrift => {
TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_S | TIER_BIT_U | TIER_BIT_T
}
ReasonCode::AbruptSlewViolation => {
TIER_BIT_A | TIER_BIT_B | TIER_BIT_I | TIER_BIT_N | TIER_BIT_O | TIER_BIT_EXTRA
}
ReasonCode::RecurrentBoundaryGrazing => {
TIER_BIT_K | TIER_BIT_M | TIER_BIT_U
}
ReasonCode::DriftWithRecovery => {
TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_T
}
ReasonCode::BoundaryApproach => {
TIER_BIT_I | TIER_BIT_J | TIER_BIT_K | TIER_BIT_M
}
ReasonCode::EnvelopeViolation => {
TIER_BIT_A | TIER_BIT_B | TIER_BIT_E | TIER_BIT_R
}
ReasonCode::SingleCrossing => {
TIER_BIT_A | TIER_BIT_F
}
ReasonCode::Admissible => u32::MAX,
};
let multivariate = if min_correlation_count >= 3 {
TIER_BIT_C | TIER_BIT_L | TIER_BIT_EXTRA
} else { 0 };
base | multivariate
}
pub struct HeuristicsBank<const MAX: usize> {
entries: [Option<HeuristicEntry>; MAX],
count: usize,
}
impl<const MAX: usize> HeuristicsBank<MAX> {
pub fn with_canonical_motifs() -> Self {
let mut bank = Self {
entries: [None; MAX],
count: 0,
};
let canonical: &[HeuristicEntry] = &[
HeuristicEntry {
motif_class: MotifClass::MemoryLeakDrift,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "sustained monotonic memory-consumption drift; may correspond to object-retention bugs",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Review,
drift_threshold: 0.6,
slew_threshold: 0.0,
boundary_density_threshold: 0.4,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.5,
weight_slew: 0.3,
weight_boundary: 1.0,
weight_correlation: 0.5,
weight_duration: 1.2,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect process RSS / heap-used and gc.duration over the past hour",
taxonomy_ref: "IEEE 24765: 'memory leak'; A-L-R: latent fault → error",
affinity_tiers: TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_S | TIER_BIT_U | TIER_BIT_T | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::ConnectionPoolExhaustionDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_I | TIER_BIT_M | TIER_BIT_S,
primary_witness_detectors: &["mann_kendall", "monotone_leak", "theil_sen_residual"],
},
HeuristicEntry {
motif_class: MotifClass::CascadingTimeoutSlew,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "step-change latency propagating across dependency chain; may correspond to upstream failure",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.5,
boundary_density_threshold: 0.0,
min_correlation_count: 3,
max_correlation_count: u16::MAX,
min_duration_windows: 2,
max_duration_windows: 30,
weight_drift: 0.2,
weight_slew: 1.5,
weight_boundary: 0.5,
weight_correlation: 1.5,
weight_duration: 0.8,
evidence_dataset: "tadbench_trainticket_F04",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect ${ROOT_CAUSE_SERVICE} (signal ${ROOT_CAUSE_INDEX}); ${CONTRIBUTING_COUNT} services contribute over ${DURATION_WINDOWS} windows; peak slew ${PEAK_SLEW}",
taxonomy_ref: "IEEE 24765: 'fault propagation'; A-L-R: error → service-failure",
affinity_tiers: TIER_BIT_C | TIER_BIT_L | TIER_BIT_EXTRA | TIER_BIT_B | TIER_BIT_M | TIER_BIT_V | TIER_BIT_X,
confuser_motif: Some(MotifClass::DependencySlowdown),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_C | TIER_BIT_L | TIER_BIT_EXTRA,
primary_witness_detectors: &["correlation_break", "lof", "causal_lag"],
},
HeuristicEntry {
motif_class: MotifClass::DeploymentRegressionSlew,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "abrupt baseline shift coinciding with deployment; structural step function",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.8,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: 2,
min_duration_windows: 1,
max_duration_windows: u16::MAX,
weight_drift: 0.0,
weight_slew: 2.0,
weight_boundary: 0.5,
weight_correlation: 0.3,
weight_duration: 0.2,
evidence_dataset: "tadbench_trainticket_F11",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Single-service step shift on ${ROOT_CAUSE_SERVICE} (signal ${ROOT_CAUSE_INDEX}); peak slew ${PEAK_SLEW}; correlate with deployment log near window ${DURATION_WINDOWS}; consider rollback",
taxonomy_ref: "IEEE 24765: 'regression'; A-L-R: design fault → error",
affinity_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_I | TIER_BIT_N | TIER_BIT_O | TIER_BIT_X | TIER_BIT_Y | TIER_BIT_V,
confuser_motif: Some(MotifClass::CircuitBreakerOpenShift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_N | TIER_BIT_X,
primary_witness_detectors: &["page_hinkley", "pelt", "pettitt_test"],
},
HeuristicEntry {
motif_class: MotifClass::CacheDegradationGrazing,
reason_code: ReasonCode::RecurrentBoundaryGrazing,
candidate_interpretation: "oscillatory approach to SLO boundary; may correspond to cache eviction patterns",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.3,
slew_threshold: 0.0,
boundary_density_threshold: 0.5,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: u16::MAX,
weight_drift: 0.6,
weight_slew: 0.4,
weight_boundary: 1.8,
weight_correlation: 0.4,
weight_duration: 1.0,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect cache hit-rate and eviction rate of the affected service",
taxonomy_ref: "IEEE 24765: 'performance degradation'; A-L-R: marginal-state error",
affinity_tiers: TIER_BIT_K | TIER_BIT_M | TIER_BIT_U | TIER_BIT_F | TIER_BIT_Z | TIER_BIT_AA,
confuser_motif: Some(MotifClass::GcPressureOscillation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_K | TIER_BIT_M | TIER_BIT_U,
primary_witness_detectors: &["autocorrelation_peak", "limit_cycle", "flap"],
},
HeuristicEntry {
motif_class: MotifClass::ConnectionPoolExhaustionDrift,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "slow positive drift in queue depth + latency with increasing variance",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.0,
boundary_density_threshold: 0.4,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.2,
weight_boundary: 1.0,
weight_correlation: 0.6,
weight_duration: 1.1,
evidence_dataset: "tadbench_trainticket_F19",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect connection pool waiting queue + active connections + idle timeout",
taxonomy_ref: "IEEE 24765: 'resource exhaustion'; A-L-R: error build-up",
affinity_tiers: TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_E | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::MemoryLeakDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_I | TIER_BIT_E | TIER_BIT_M,
primary_witness_detectors: &["monotone_leak", "saturation_chain", "theil_sen_residual"],
},
HeuristicEntry {
motif_class: MotifClass::GcPressureOscillation,
reason_code: ReasonCode::RecurrentBoundaryGrazing,
candidate_interpretation: "periodic slew events coinciding with GC pauses; bounded oscillation",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.0,
slew_threshold: 0.2,
boundary_density_threshold: 0.4,
min_correlation_count: 1,
max_correlation_count: 3,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 0.2,
weight_slew: 1.4,
weight_boundary: 1.4,
weight_correlation: 0.4,
weight_duration: 0.6,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect gc.collection.count + gc.duration histograms",
taxonomy_ref: "IEEE 24765: 'stop-the-world pause'; A-L-R: transient error",
affinity_tiers: TIER_BIT_K | TIER_BIT_F | TIER_BIT_M | TIER_BIT_S | TIER_BIT_Z | TIER_BIT_AA,
confuser_motif: Some(MotifClass::CacheDegradationGrazing),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_K | TIER_BIT_F | TIER_BIT_Z,
primary_witness_detectors: &["autocorrelation_peak", "sawtooth_ramp", "welch_psd"],
},
HeuristicEntry {
motif_class: MotifClass::ErrorRateEscalation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "sustained positive drift in error rate",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.7,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 1.8,
weight_slew: 0.3,
weight_boundary: 0.8,
weight_correlation: 0.7,
weight_duration: 1.0,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect HTTP 5xx rate by endpoint and recent deploys / config changes",
taxonomy_ref: "IEEE 24765: 'error escalation'; A-L-R: error → multi-failure regime",
affinity_tiers: TIER_BIT_A | TIER_BIT_G | TIER_BIT_Q | TIER_BIT_E | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::PacketLossErrorEscalation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_G | TIER_BIT_E,
primary_witness_detectors: &["chi_squared_proportion", "ddm", "ecdd"],
},
HeuristicEntry {
motif_class: MotifClass::DependencySlowdown,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "gradual latency increase in upstream dependency",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Review,
drift_threshold: 0.4,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.3,
weight_slew: 0.3,
weight_boundary: 0.7,
weight_correlation: 0.7,
weight_duration: 0.9,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect the upstream service's latency distribution; check its dependencies",
taxonomy_ref: "IEEE 24765: 'performance degradation upstream'; A-L-R: external fault",
affinity_tiers: TIER_BIT_C | TIER_BIT_L | TIER_BIT_M | TIER_BIT_EXTRA | TIER_BIT_V | TIER_BIT_X,
confuser_motif: Some(MotifClass::CascadingTimeoutSlew),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_C | TIER_BIT_L | TIER_BIT_M,
primary_witness_detectors: &["causal_lag", "correlation_break", "lof"],
},
HeuristicEntry {
motif_class: MotifClass::ResourceSaturation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "CPU/memory/disk approaching ceiling; concave-up drift",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.0,
boundary_density_threshold: 0.5,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.3,
weight_boundary: 1.2,
weight_correlation: 0.5,
weight_duration: 1.0,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect system resource gauges (CPU%, memory%, disk%) over the past hour",
taxonomy_ref: "IEEE 24765: 'resource saturation'; A-L-R: latent → manifest fault",
affinity_tiers: TIER_BIT_A | TIER_BIT_E | TIER_BIT_M | TIER_BIT_R | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::ConnectionPoolExhaustionDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_E | TIER_BIT_R,
primary_witness_detectors: &["saturation_chain", "monotone_leak", "mann_kendall"],
},
HeuristicEntry {
motif_class: MotifClass::QueueBackpressure,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "message queue depth growing monotonically",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Review,
drift_threshold: 0.6,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: u16::MAX,
weight_drift: 1.5,
weight_slew: 0.2,
weight_boundary: 0.9,
weight_correlation: 0.5,
weight_duration: 1.1,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect message-queue depth and consumer lag metrics",
taxonomy_ref: "IEEE 24765: 'back-pressure accumulation'; A-L-R: error build-up",
affinity_tiers: TIER_BIT_M | TIER_BIT_L | TIER_BIT_J | TIER_BIT_S | TIER_BIT_V | TIER_BIT_X | TIER_BIT_AA,
confuser_motif: Some(MotifClass::ConnectionPoolExhaustionDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_M | TIER_BIT_L,
primary_witness_detectors: &["backpressure", "mann_kendall", "mahalanobis"],
},
HeuristicEntry {
motif_class: MotifClass::RetryStormCascade,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "client retries amplify upstream load; both error rate and request rate rise together",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.6,
slew_threshold: 0.3,
boundary_density_threshold: 0.4,
min_correlation_count: 2,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: 60,
weight_drift: 1.3,
weight_slew: 1.0,
weight_boundary: 0.8,
weight_correlation: 1.4,
weight_duration: 0.7,
evidence_dataset: "tadbench_retry_storm",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect retry-policy parameters and client-side request rate vs upstream success rate",
taxonomy_ref: "IEEE 24765: 'retry-induced amplification'; A-L-R: cascading error",
affinity_tiers: TIER_BIT_F | TIER_BIT_M | TIER_BIT_EXTRA | TIER_BIT_S | TIER_BIT_AA | TIER_BIT_Z,
confuser_motif: Some(MotifClass::CascadingTimeoutSlew),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_F | TIER_BIT_EXTRA | TIER_BIT_M,
primary_witness_detectors: &["retry_storm", "causal_lag", "poisson_burst"],
},
HeuristicEntry {
motif_class: MotifClass::CircuitBreakerOpenShift,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "circuit breaker transitions from CLOSED to OPEN; downstream calls fail fast",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.6,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: 4,
min_duration_windows: 2,
max_duration_windows: u16::MAX,
weight_drift: 0.2,
weight_slew: 1.6,
weight_boundary: 0.4,
weight_correlation: 0.8,
weight_duration: 0.6,
evidence_dataset: "tadbench_circuit_breaker",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect circuit-breaker state metrics and the underlying service's health",
taxonomy_ref: "IEEE 24765: 'fault tolerance mechanism state change'",
affinity_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_N | TIER_BIT_O | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::DeploymentRegressionSlew),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_N,
primary_witness_detectors: &["cusum", "pelt", "binary_segmentation"],
},
HeuristicEntry {
motif_class: MotifClass::DatabaseLockContention,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "database lock contention; rising query latency and queue depth",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.2,
boundary_density_threshold: 0.4,
min_correlation_count: 2,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.2,
weight_slew: 0.6,
weight_boundary: 1.0,
weight_correlation: 1.0,
weight_duration: 1.0,
evidence_dataset: "tadbench_db_lock",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect database lock-wait stats and slow-query log for the active transactions",
taxonomy_ref: "IEEE 24765: 'concurrency fault'; A-L-R: synchronisation error",
affinity_tiers: TIER_BIT_L | TIER_BIT_M | TIER_BIT_C | TIER_BIT_EXTRA | TIER_BIT_V | TIER_BIT_AA,
confuser_motif: Some(MotifClass::DependencySlowdown),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_M | TIER_BIT_EXTRA,
primary_witness_detectors: &["causal_lag", "correlation_break", "mahalanobis"],
},
HeuristicEntry {
motif_class: MotifClass::AuthenticationFailureSpike,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "authentication subsystem partial outage; spike in 401/403 + downstream re-auth retries",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.5,
boundary_density_threshold: 0.2,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 1,
max_duration_windows: 30,
weight_drift: 0.3,
weight_slew: 1.5,
weight_boundary: 0.6,
weight_correlation: 0.9,
weight_duration: 0.5,
evidence_dataset: "tadbench_auth_fail",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Inspect auth-service health, token-issuance rate, and 401/403 distribution",
taxonomy_ref: "IEEE 24765: 'authentication subsystem failure'",
affinity_tiers: TIER_BIT_F | TIER_BIT_M | TIER_BIT_A | TIER_BIT_AA | TIER_BIT_Z | TIER_BIT_Y,
confuser_motif: Some(MotifClass::EpisodicTransientSpike),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_F | TIER_BIT_M,
primary_witness_detectors: &["poisson_burst", "burst_after_silence", "flap"],
},
HeuristicEntry {
motif_class: MotifClass::ConfigDriftRegression,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "step shift coinciding with version-config change",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.6,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: 3,
min_duration_windows: 1,
max_duration_windows: u16::MAX,
weight_drift: 0.2,
weight_slew: 1.7,
weight_boundary: 0.5,
weight_correlation: 0.5,
weight_duration: 0.4,
evidence_dataset: "trainticket_anomaly_version_config",
evidence_dataset_doi: "10.5281/zenodo.6979726",
dashboard_hint: "Diff config artefacts between the last good window and the current; consider rollback",
taxonomy_ref: "IEEE 24765: 'configuration regression'; A-L-R: design-time fault",
affinity_tiers: TIER_BIT_H | TIER_BIT_G | TIER_BIT_Q | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::DeploymentRegressionSlew),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_H | TIER_BIT_G | TIER_BIT_Q,
primary_witness_detectors: &["wasserstein_1d", "ddm", "kl_divergence"],
},
HeuristicEntry {
motif_class: MotifClass::PacketLossErrorEscalation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "network-layer packet loss elevates error rate; sustained positive drift",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.6,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 2,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 1.6,
weight_slew: 0.4,
weight_boundary: 0.8,
weight_correlation: 1.2,
weight_duration: 0.8,
evidence_dataset: "aiops_challenge_packet_loss",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect TCP retransmits / packet-loss counters at the network layer",
taxonomy_ref: "IEEE 24765: 'communication failure (lower layer)'",
affinity_tiers: TIER_BIT_A | TIER_BIT_G | TIER_BIT_F | TIER_BIT_E | TIER_BIT_AA | TIER_BIT_Z,
confuser_motif: Some(MotifClass::ErrorRateEscalation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_G | TIER_BIT_F,
primary_witness_detectors: &["chi_squared_proportion", "poisson_burst", "ddm"],
},
HeuristicEntry {
motif_class: MotifClass::NetworkDelayDependencyInflation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "injected network delay on upstream link; gradual latency-increase pattern across consumers",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.4,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 2,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.3,
weight_slew: 0.2,
weight_boundary: 0.7,
weight_correlation: 1.3,
weight_duration: 1.1,
evidence_dataset: "aiops_challenge_network_delay",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect inter-service RTT histograms and link-level latency gauges",
taxonomy_ref: "IEEE 24765: 'communication-path performance fault'",
affinity_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_M | TIER_BIT_V | TIER_BIT_X,
confuser_motif: Some(MotifClass::DependencySlowdown),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_M,
primary_witness_detectors: &["causal_lag", "lof", "correlation_break"],
},
HeuristicEntry {
motif_class: MotifClass::DiskIoSaturation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "disk I/O saturation; concave-up latency drift on storage-bound services",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.0,
boundary_density_threshold: 0.5,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.3,
weight_boundary: 1.2,
weight_correlation: 0.6,
weight_duration: 1.0,
evidence_dataset: "aiops_challenge_disk_exhaustion",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect disk IOPS / await / queue depth; check for runaway log writes",
taxonomy_ref: "IEEE 24765: 'storage subsystem saturation'",
affinity_tiers: TIER_BIT_E | TIER_BIT_A | TIER_BIT_I | TIER_BIT_R | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::CpuSaturation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_E | TIER_BIT_I | TIER_BIT_R,
primary_witness_detectors: &["saturation_chain", "monotone_leak", "theil_sen_residual"],
},
HeuristicEntry {
motif_class: MotifClass::CpuSaturation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "CPU saturation; latency drift with rising envelope occupancy",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.0,
boundary_density_threshold: 0.5,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.3,
weight_boundary: 1.2,
weight_correlation: 0.6,
weight_duration: 1.0,
evidence_dataset: "aiops_challenge_cpu_exhaustion",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect CPU utilisation, run-queue length, and thread-level scheduling latency",
taxonomy_ref: "IEEE 24765: 'compute resource saturation'",
affinity_tiers: TIER_BIT_E | TIER_BIT_A | TIER_BIT_I | TIER_BIT_R | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::DiskIoSaturation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_E | TIER_BIT_I | TIER_BIT_R,
primary_witness_detectors: &["saturation_chain", "monotone_leak", "theil_sen_residual"],
},
HeuristicEntry {
motif_class: MotifClass::JvmHeapPressure,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "JVM heap pressure; sustained latency drift with rising variance and elevated GC frequency",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.6,
slew_threshold: 0.0,
boundary_density_threshold: 0.4,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.6,
weight_slew: 0.4,
weight_boundary: 1.0,
weight_correlation: 0.5,
weight_duration: 1.2,
evidence_dataset: "aiops_challenge_memory_exhaustion",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect jvm.memory.heap.used + gc.collection.count + minor/major GC ratio",
taxonomy_ref: "IEEE 24765: 'memory leak (JVM-specific)'; refines MemoryLeakDrift",
affinity_tiers: TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_E | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::MemoryLeakDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_I | TIER_BIT_M | TIER_BIT_E,
primary_witness_detectors: &["monotone_leak", "saturation_chain", "mann_kendall"],
},
HeuristicEntry {
motif_class: MotifClass::JvmGcPause,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "JVM stop-the-world GC pause: distinct latency spikes with regular cadence",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Watch,
drift_threshold: 0.0,
slew_threshold: 0.4,
boundary_density_threshold: 0.3,
min_correlation_count: 1,
max_correlation_count: 3,
min_duration_windows: 1,
max_duration_windows: 10,
weight_drift: 0.2,
weight_slew: 1.6,
weight_boundary: 1.0,
weight_correlation: 0.4,
weight_duration: 0.4,
evidence_dataset: "aiops_challenge_jvm_resource_exhaustion",
evidence_dataset_doi: "AIOps-Challenge-2020-2021",
dashboard_hint: "Inspect gc.duration percentiles + STW pause histograms; consider GC tuning",
taxonomy_ref: "IEEE 24765: 'stop-the-world pause (JVM-specific)'; refines GcPressureOscillation",
affinity_tiers: TIER_BIT_K | TIER_BIT_F | TIER_BIT_M | TIER_BIT_Z | TIER_BIT_AA,
confuser_motif: Some(MotifClass::AuthenticationFailureSpike),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_K | TIER_BIT_F,
primary_witness_detectors: &["welch_psd", "autocorrelation_peak", "poisson_burst"],
},
HeuristicEntry {
motif_class: MotifClass::ServiceGraphDriftPropagation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "drift propagating along the service-call graph (multi-hop); affects sequentially-related services",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.5,
slew_threshold: 0.2,
boundary_density_threshold: 0.4,
min_correlation_count: 4,
max_correlation_count: u16::MAX,
min_duration_windows: 5,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.7,
weight_boundary: 0.9,
weight_correlation: 1.7,
weight_duration: 1.0,
evidence_dataset: "multidim_localization_graph_propagation",
evidence_dataset_doi: "MultiDimension-Localization-NetManAIOps",
dashboard_hint: "Multi-hop drift propagation across ${CONTRIBUTING_COUNT} services; originator: ${ROOT_CAUSE_SERVICE} (signal ${ROOT_CAUSE_INDEX}); peak slew ${PEAK_SLEW}",
taxonomy_ref: "IEEE 24765: 'graph-structured fault propagation'",
affinity_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_G | TIER_BIT_EXTRA | TIER_BIT_V | TIER_BIT_X,
confuser_motif: Some(MotifClass::DependencySlowdown),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_EXTRA,
primary_witness_detectors: &["correlation_matrix_distance", "correlation_break", "causal_lag"],
},
HeuristicEntry {
motif_class: MotifClass::HighDimAnomalyCluster,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "multi-metric correlated anomaly without single dominant signal; compound fault",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.4,
slew_threshold: 0.2,
boundary_density_threshold: 0.3,
min_correlation_count: 6,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: u16::MAX,
weight_drift: 1.0,
weight_slew: 0.6,
weight_boundary: 0.7,
weight_correlation: 2.0,
weight_duration: 0.9,
evidence_dataset: "multidim_localization_cluster",
evidence_dataset_doi: "MultiDimension-Localization-NetManAIOps",
dashboard_hint: "Inspect the multi-metric anomaly cluster as a unit; no single metric is dominant",
taxonomy_ref: "IEEE 24765: 'compound fault signature'",
affinity_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_R | TIER_BIT_V | TIER_BIT_Y,
confuser_motif: Some(MotifClass::MetricCorrelationCollapse),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_R,
primary_witness_detectors: &["mahalanobis", "pca_reconstruction", "lof"],
},
HeuristicEntry {
motif_class: MotifClass::MetricCorrelationCollapse,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "historically-correlated metrics decorrelate; structural regime shift",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.0,
slew_threshold: 0.4,
boundary_density_threshold: 0.0,
min_correlation_count: 2,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 0.4,
weight_slew: 1.3,
weight_boundary: 0.5,
weight_correlation: 1.5,
weight_duration: 0.8,
evidence_dataset: "multidim_localization_correlation_collapse",
evidence_dataset_doi: "MultiDimension-Localization-NetManAIOps",
dashboard_hint: "Compare current pairwise metric correlations against the baseline correlation matrix",
taxonomy_ref: "IEEE 24765: 'structural model invalidation'",
affinity_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_EXTRA | TIER_BIT_V | TIER_BIT_AA,
confuser_motif: Some(MotifClass::HighDimAnomalyCluster),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_C | TIER_BIT_EXTRA,
primary_witness_detectors: &["correlation_break", "correlation_matrix_distance", "mahalanobis"],
},
HeuristicEntry {
motif_class: MotifClass::LogVolumeAnomaly,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "log-frequency outward drift on a service; structural log-rate anomaly",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.5,
slew_threshold: 0.0,
boundary_density_threshold: 0.3,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 1.4,
weight_slew: 0.3,
weight_boundary: 0.8,
weight_correlation: 0.6,
weight_duration: 0.9,
evidence_dataset: "deeptralog_log_volume",
evidence_dataset_doi: "DeepTraLog-ICSE-2022",
dashboard_hint: "Inspect log-volume gauges per severity per service over the past hour",
taxonomy_ref: "IEEE 24765: 'diagnostic-output anomaly'",
affinity_tiers: TIER_BIT_A | TIER_BIT_S | TIER_BIT_I | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::LogSeverityEscalation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_S | TIER_BIT_I,
primary_witness_detectors: &["chi_squared_proportion", "poisson_burst", "mann_kendall"],
},
HeuristicEntry {
motif_class: MotifClass::LogTraceTemporalDecorrelation,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "log timing departs from trace timing pattern; instrumentation/temporal divergence",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Review,
drift_threshold: 0.0,
slew_threshold: 0.3,
boundary_density_threshold: 0.2,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 0.4,
weight_slew: 1.3,
weight_boundary: 0.7,
weight_correlation: 0.7,
weight_duration: 0.7,
evidence_dataset: "deeptralog_temporal_mismatch",
evidence_dataset_doi: "DeepTraLog-ICSE-2022",
dashboard_hint: "Inspect log-event timestamps vs trace-span timestamps for the same request IDs",
taxonomy_ref: "IEEE 24765: 'instrumentation-temporal divergence'",
affinity_tiers: TIER_BIT_L | TIER_BIT_T | TIER_BIT_EXTRA | TIER_BIT_V | TIER_BIT_AA,
confuser_motif: Some(MotifClass::ServiceGraphDriftPropagation),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_L | TIER_BIT_T | TIER_BIT_EXTRA,
primary_witness_detectors: &["correlation_break", "transfer_entropy", "correlation_matrix_distance"],
},
HeuristicEntry {
motif_class: MotifClass::LogSeverityEscalation,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "log severity distribution shift (more WARN/ERROR proportionally)",
provenance: Provenance::DatasetObserved,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.6,
slew_threshold: 0.0,
boundary_density_threshold: 0.4,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 3,
max_duration_windows: u16::MAX,
weight_drift: 1.6,
weight_slew: 0.3,
weight_boundary: 1.0,
weight_correlation: 0.7,
weight_duration: 0.9,
evidence_dataset: "deeptralog_severity_shift",
evidence_dataset_doi: "DeepTraLog-ICSE-2022",
dashboard_hint: "Inspect log-severity distribution histograms; compare against the healthy-window baseline",
taxonomy_ref: "IEEE 24765: 'diagnostic severity escalation'",
affinity_tiers: TIER_BIT_H | TIER_BIT_G | TIER_BIT_A | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::LogVolumeAnomaly),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_H | TIER_BIT_G | TIER_BIT_A,
primary_witness_detectors: &["wasserstein_1d", "chi_squared_proportion", "ddm"],
},
HeuristicEntry {
motif_class: MotifClass::SaturationTrending,
reason_code: ReasonCode::SustainedOutwardDrift,
candidate_interpretation: "concave-up approach to a ceiling; generalises ResourceSaturation",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.5,
slew_threshold: 0.1,
boundary_density_threshold: 0.5,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 6,
max_duration_windows: u16::MAX,
weight_drift: 1.3,
weight_slew: 0.6,
weight_boundary: 1.4,
weight_correlation: 0.5,
weight_duration: 1.1,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Project the current drift forward to estimate time-to-ceiling; consider scaling",
taxonomy_ref: "IEEE 24765: 'asymptotic resource saturation'",
affinity_tiers: TIER_BIT_E | TIER_BIT_M | TIER_BIT_I | TIER_BIT_J | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::ConnectionPoolExhaustionDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_E | TIER_BIT_M | TIER_BIT_I,
primary_witness_detectors: &["saturation_chain", "monotone_leak", "mann_kendall"],
},
HeuristicEntry {
motif_class: MotifClass::EpisodicTransientSpike,
reason_code: ReasonCode::AbruptSlewViolation,
candidate_interpretation: "short-duration high-slew event that self-resolves",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.0,
slew_threshold: 0.5,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 1,
max_duration_windows: 4,
weight_drift: 0.2,
weight_slew: 1.6,
weight_boundary: 0.3,
weight_correlation: 0.4,
weight_duration: 0.3,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Note the timestamp; correlate with cron / scheduled jobs / external triggers",
taxonomy_ref: "IEEE 24765: 'transient-only error'; A-L-R: transient fault",
affinity_tiers: TIER_BIT_A | TIER_BIT_F | TIER_BIT_M | TIER_BIT_AA | TIER_BIT_Z,
confuser_motif: Some(MotifClass::AuthenticationFailureSpike),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_F | TIER_BIT_M,
primary_witness_detectors: &["poisson_burst", "burst_after_silence", "scalar_threshold_3sigma"],
},
HeuristicEntry {
motif_class: MotifClass::RegressiveDriftWithRecovery,
reason_code: ReasonCode::DriftWithRecovery,
candidate_interpretation: "outward drift followed by return to baseline; self-healing structural transient",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.4,
slew_threshold: 0.0,
boundary_density_threshold: 0.2,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 4,
max_duration_windows: 30,
weight_drift: 1.2,
weight_slew: 0.3,
weight_boundary: 0.6,
weight_correlation: 0.5,
weight_duration: 0.8,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "No action required if recovery confirmed; record as a near-miss for trend analysis",
taxonomy_ref: "IEEE 24765: 'self-healing transient drift'",
affinity_tiers: TIER_BIT_I | TIER_BIT_J | TIER_BIT_M | TIER_BIT_T | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::MemoryLeakDrift),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_I | TIER_BIT_J | TIER_BIT_M,
primary_witness_detectors: &["theil_sen_residual", "monotone_leak", "mann_kendall"],
},
HeuristicEntry {
motif_class: MotifClass::EnvelopeBoundaryApproach,
reason_code: ReasonCode::BoundaryApproach,
candidate_interpretation: "first-time approach to the SLO envelope without recurrence or persistent drift; marginal-state transient",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Watch,
drift_threshold: 0.0,
slew_threshold: 0.0,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 1,
max_duration_windows: u16::MAX,
weight_drift: 0.5,
weight_slew: 0.5,
weight_boundary: 0.8,
weight_correlation: 0.3,
weight_duration: 0.4,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Note the timestamp; if recurrence is observed in subsequent windows escalate to CacheDegradationGrazing",
taxonomy_ref: "IEEE 24765: 'marginal-state transient'; A-L-R: dormant fault",
affinity_tiers: TIER_BIT_A | TIER_BIT_J | TIER_BIT_I | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::EnvelopeBreach),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_I | TIER_BIT_J,
primary_witness_detectors: &["scalar_threshold_3sigma", "mann_kendall", "theil_sen_residual"],
},
HeuristicEntry {
motif_class: MotifClass::EnvelopeBreach,
reason_code: ReasonCode::EnvelopeViolation,
candidate_interpretation: "envelope breach without abrupt slew evidence; smooth threshold crossing",
provenance: Provenance::FrameworkDesign,
recommended_action: PolicyState::Escalate,
drift_threshold: 0.0,
slew_threshold: 0.0,
boundary_density_threshold: 0.0,
min_correlation_count: 1,
max_correlation_count: u16::MAX,
min_duration_windows: 1,
max_duration_windows: u16::MAX,
weight_drift: 0.7,
weight_slew: 0.3,
weight_boundary: 0.7,
weight_correlation: 0.6,
weight_duration: 0.6,
evidence_dataset: "FrameworkDesign",
evidence_dataset_doi: "",
dashboard_hint: "Inspect SLO/SLA threshold + the affected service's value distribution; threshold may be too tight",
taxonomy_ref: "IEEE 24765: 'threshold breach (smooth)'; A-L-R: error → manifest",
affinity_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_R | TIER_BIT_E | TIER_BIT_X | TIER_BIT_Y,
confuser_motif: Some(MotifClass::EnvelopeBoundaryApproach),
margin_vs_confuser_threshold: 0.10,
primary_witness_tiers: TIER_BIT_A | TIER_BIT_B | TIER_BIT_R,
primary_witness_detectors: &["scalar_threshold_3sigma", "cusum", "page_hinkley"],
},
];
let mut i = 0;
while i < canonical.len() && i < MAX {
bank.entries[i] = Some(canonical[i]);
bank.count += 1;
i += 1;
}
bank
}
pub fn lookup(
&self,
reason_code: ReasonCode,
drift_persistence: f64,
slew_magnitude: f64,
) -> SemanticDisposition {
let mut best_match: Option<MotifClass> = None;
let mut best_score: f64 = 0.0;
let mut best_provenance_rank: u8 = 0;
let mut best_index: usize = usize::MAX;
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.reason_code == reason_code {
let mut score: f64 = 1.0; if drift_persistence >= entry.drift_threshold {
score += drift_persistence;
}
if slew_magnitude >= entry.slew_threshold {
score += slew_magnitude;
}
let prov_rank = provenance_rank(entry.provenance);
let take = score > best_score
|| (score == best_score && prov_rank > best_provenance_rank)
|| (score == best_score && prov_rank == best_provenance_rank && i < best_index);
if take {
best_score = score;
best_match = Some(entry.motif_class);
best_provenance_rank = prov_rank;
best_index = i;
}
}
}
i += 1;
}
match best_match {
Some(motif) => SemanticDisposition::Named(motif),
None => SemanticDisposition::Unknown, }
}
pub fn match_episode(
&self,
episode: &DebugEpisode,
avg_drift_persistence: f64,
avg_boundary_density: f64,
) -> SemanticDisposition {
let mut best_match: Option<MotifClass> = None;
let mut best_score: f64 = 0.0;
let mut best_provenance_rank: u8 = 0;
let mut best_index: usize = usize::MAX;
let correlation_count = episode.contributing_signal_count;
let duration_windows: u16 = if episode.end_window >= episode.start_window {
let d = episode.end_window - episode.start_window + 1;
if d > u16::MAX as u64 { u16::MAX } else { d as u16 }
} else {
0
};
let peak_slew = episode.structural_signature.peak_slew_magnitude;
let slew_mag = if peak_slew >= 0.0 { peak_slew } else { -peak_slew };
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.reason_code != episode.primary_reason_code {
i += 1;
continue;
}
if correlation_count < entry.min_correlation_count
|| correlation_count > entry.max_correlation_count
{
i += 1;
continue;
}
if duration_windows < entry.min_duration_windows
|| duration_windows > entry.max_duration_windows
{
i += 1;
continue;
}
let mut score: f64 = 1.0;
if avg_drift_persistence >= entry.drift_threshold {
score += entry.weight_drift * avg_drift_persistence;
}
if slew_mag >= entry.slew_threshold {
score += entry.weight_slew * slew_mag;
}
if avg_boundary_density >= entry.boundary_density_threshold {
score += entry.weight_boundary * avg_boundary_density;
}
score += entry.weight_correlation * (correlation_count as f64) * 0.1;
score += entry.weight_duration * (duration_windows as f64) * 0.05;
let prov_rank = provenance_rank(entry.provenance);
let take = score > best_score
|| (score == best_score && prov_rank > best_provenance_rank)
|| (score == best_score && prov_rank == best_provenance_rank && i < best_index);
if take {
best_score = score;
best_match = Some(entry.motif_class);
best_provenance_rank = prov_rank;
best_index = i;
}
}
i += 1;
}
match best_match {
Some(motif) => SemanticDisposition::Named(motif),
None => SemanticDisposition::Unknown,
}
}
pub fn match_episode_with_confidence(
&self,
episode: &DebugEpisode,
avg_drift_persistence: f64,
avg_boundary_density: f64,
) -> MatchConfidence {
let mut best_match: Option<MotifClass> = None;
let mut best_score: f64 = 0.0;
let mut best_provenance_rank: u8 = 0;
let mut best_index: usize = usize::MAX;
let mut runner_up_match: Option<MotifClass> = None;
let mut runner_up_score: f64 = 0.0;
let correlation_count = episode.contributing_signal_count;
let duration_windows: u16 = if episode.end_window >= episode.start_window {
let d = episode.end_window - episode.start_window + 1;
if d > u16::MAX as u64 { u16::MAX } else { d as u16 }
} else {
0
};
let peak_slew = episode.structural_signature.peak_slew_magnitude;
let slew_mag = if peak_slew >= 0.0 { peak_slew } else { -peak_slew };
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.reason_code != episode.primary_reason_code {
i += 1;
continue;
}
if correlation_count < entry.min_correlation_count
|| correlation_count > entry.max_correlation_count
{
i += 1;
continue;
}
if duration_windows < entry.min_duration_windows
|| duration_windows > entry.max_duration_windows
{
i += 1;
continue;
}
let mut score: f64 = 1.0;
if avg_drift_persistence >= entry.drift_threshold {
score += entry.weight_drift * avg_drift_persistence;
}
if slew_mag >= entry.slew_threshold {
score += entry.weight_slew * slew_mag;
}
if avg_boundary_density >= entry.boundary_density_threshold {
score += entry.weight_boundary * avg_boundary_density;
}
score += entry.weight_correlation * (correlation_count as f64) * 0.1;
score += entry.weight_duration * (duration_windows as f64) * 0.05;
let prov_rank = provenance_rank(entry.provenance);
let take_top = score > best_score
|| (score == best_score && prov_rank > best_provenance_rank)
|| (score == best_score && prov_rank == best_provenance_rank && i < best_index);
if take_top {
if let Some(prev_top) = best_match {
if score > runner_up_score
|| (score == runner_up_score && best_score > runner_up_score)
{
runner_up_match = Some(prev_top);
runner_up_score = best_score;
}
}
best_score = score;
best_match = Some(entry.motif_class);
best_provenance_rank = prov_rank;
best_index = i;
} else if score > runner_up_score {
runner_up_score = score;
runner_up_match = Some(entry.motif_class);
}
}
i += 1;
}
let disposition = match best_match {
Some(m) => SemanticDisposition::Named(m),
None => SemanticDisposition::Unknown,
};
let margin = if best_score > 0.0 {
((best_score - runner_up_score) / best_score).clamp(0.0, 1.0)
} else {
0.0
};
MatchConfidence {
disposition,
top_score: best_score,
runner_up_score,
runner_up_motif: runner_up_match,
margin,
tier_consensus_factor: 0.0,
confuser_motif: None,
confuser_score: 0.0,
margin_vs_confuser: 0.0,
}
}
pub fn match_episode_with_consensus(
&self,
episode: &DebugEpisode,
avg_drift_persistence: f64,
avg_boundary_density: f64,
episode_max_consensus: u8,
max_detectors: u8,
) -> MatchConfidence {
let consensus_factor = if max_detectors > 0 {
episode_max_consensus as f64 / max_detectors as f64
} else { 0.0 };
let mut best_match: Option<MotifClass> = None;
let mut best_score: f64 = 0.0;
let mut best_provenance_rank: u8 = 0;
let mut best_index: usize = usize::MAX;
let mut runner_up_match: Option<MotifClass> = None;
let mut runner_up_score: f64 = 0.0;
let correlation_count = episode.contributing_signal_count;
let duration_windows: u16 = if episode.end_window >= episode.start_window {
let d = episode.end_window - episode.start_window + 1;
if d > u16::MAX as u64 { u16::MAX } else { d as u16 }
} else { 0 };
let peak_slew = episode.structural_signature.peak_slew_magnitude;
let slew_mag = if peak_slew >= 0.0 { peak_slew } else { -peak_slew };
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.reason_code != episode.primary_reason_code {
i += 1;
continue;
}
if correlation_count < entry.min_correlation_count
|| correlation_count > entry.max_correlation_count
{
i += 1;
continue;
}
if duration_windows < entry.min_duration_windows
|| duration_windows > entry.max_duration_windows
{
i += 1;
continue;
}
let mut score: f64 = 1.0;
if avg_drift_persistence >= entry.drift_threshold {
score += entry.weight_drift * avg_drift_persistence;
}
if slew_mag >= entry.slew_threshold {
score += entry.weight_slew * slew_mag;
}
if avg_boundary_density >= entry.boundary_density_threshold {
score += entry.weight_boundary * avg_boundary_density;
}
score += entry.weight_correlation * (correlation_count as f64) * 0.1;
score += entry.weight_duration * (duration_windows as f64) * 0.05;
score += consensus_factor;
let prov_rank = provenance_rank(entry.provenance);
let take_top = score > best_score
|| (score == best_score && prov_rank > best_provenance_rank)
|| (score == best_score && prov_rank == best_provenance_rank && i < best_index);
if take_top {
if let Some(prev_top) = best_match {
if score > runner_up_score
|| (score == runner_up_score && best_score > runner_up_score)
{
runner_up_match = Some(prev_top);
runner_up_score = best_score;
}
}
best_score = score;
best_match = Some(entry.motif_class);
best_provenance_rank = prov_rank;
best_index = i;
} else if score > runner_up_score {
runner_up_score = score;
runner_up_match = Some(entry.motif_class);
}
}
i += 1;
}
let disposition = match best_match {
Some(m) => SemanticDisposition::Named(m),
None => SemanticDisposition::Unknown,
};
let margin = if best_score > 0.0 {
((best_score - runner_up_score) / best_score).clamp(0.0, 1.0)
} else { 0.0 };
MatchConfidence {
disposition,
top_score: best_score,
runner_up_score,
runner_up_motif: runner_up_match,
margin,
tier_consensus_factor: 0.0,
confuser_motif: None,
confuser_score: 0.0,
margin_vs_confuser: 0.0,
}
}
pub fn recommended_action(&self, motif: MotifClass) -> PolicyState {
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.motif_class == motif {
return entry.recommended_action;
}
}
i += 1;
}
PolicyState::Watch }
pub fn count(&self) -> usize {
self.count
}
pub fn entries_iter(&self) -> impl Iterator<Item = &HeuristicEntry> {
self.entries[..self.count].iter().filter_map(|e| e.as_ref())
}
pub fn entry_for(&self, motif: MotifClass) -> Option<&HeuristicEntry> {
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.motif_class == motif {
return Some(entry);
}
}
i += 1;
}
None
}
pub fn effective_min_consensus(&self, entry: &HeuristicEntry, global_min: u8) -> u8 {
let mut t = global_min as i16;
match entry.provenance {
Provenance::FieldValidated => { t -= 1; }
Provenance::DatasetObserved => { }
Provenance::FrameworkDesign => { t += 1; }
}
if entry.min_correlation_count >= 3 { t += 1; }
if t < 1 { t = 1; }
if t > 255 { t = 255; }
t as u8
}
pub fn effective_min_consensus_for_motif(
&self, motif: MotifClass, global_min: u8,
) -> u8 {
match self.entry_for(motif) {
Some(entry) => self.effective_min_consensus(entry, global_min),
None => global_min,
}
}
pub fn match_episode_with_tier_affinity(
&self,
episode: &DebugEpisode,
avg_drift_persistence: f64,
avg_boundary_density: f64,
cell_tier_mask: &[u32],
window_tier_mask: &[u32],
num_signals: usize,
max_active_tiers: u8,
episode_max_consensus: u8,
) -> MatchConfidence {
self.match_episode_with_tier_affinity_axes(
episode, avg_drift_persistence, avg_boundary_density,
cell_tier_mask, window_tier_mask, num_signals,
max_active_tiers, episode_max_consensus,
true, true, true,
)
}
pub fn match_episode_with_tier_affinity_axes(
&self,
episode: &DebugEpisode,
avg_drift_persistence: f64,
avg_boundary_density: f64,
cell_tier_mask: &[u32],
window_tier_mask: &[u32],
num_signals: usize,
max_active_tiers: u8,
episode_max_consensus: u8,
use_zero_tier_filter: bool,
use_disambiguator_boost: bool,
use_primary_witness_tier_gate: bool,
) -> MatchConfidence {
let mut best_match: Option<MotifClass> = None;
let mut best_score: f64 = 0.0;
let mut best_provenance_rank: u8 = 0;
let mut best_index: usize = usize::MAX;
let mut best_consensus_factor: f64 = 0.0;
let mut runner_up_match: Option<MotifClass> = None;
let mut runner_up_score: f64 = 0.0;
let mut entry_scores: [f64; MAX] = [0.0; MAX];
let mut entry_affinity: [u32; MAX] = [0; MAX];
let mut entry_confuser_idx: [usize; MAX] = [usize::MAX; MAX];
let mut p = 0;
while p < self.count {
if let Some(e) = &self.entries[p] {
let aff = if e.affinity_tiers != 0 { e.affinity_tiers }
else { affinity_tiers_for(e.reason_code, e.min_correlation_count) };
if p < MAX { entry_affinity[p] = aff; }
if let Some(cm) = e.confuser_motif {
let mut q = 0;
while q < self.count {
if let Some(ce) = &self.entries[q] {
if ce.motif_class == cm && q < MAX {
entry_confuser_idx[p] = q;
break;
}
}
q += 1;
}
}
}
p += 1;
}
let correlation_count = episode.contributing_signal_count;
let duration_windows: u16 = if episode.end_window >= episode.start_window {
let d = episode.end_window - episode.start_window + 1;
if d > u16::MAX as u64 { u16::MAX } else { d as u16 }
} else { 0 };
let peak_slew = episode.structural_signature.peak_slew_magnitude;
let slew_mag = if peak_slew >= 0.0 { peak_slew } else { -peak_slew };
let start_w = episode.start_window as usize;
let end_w = episode.end_window as usize;
let num_windows = window_tier_mask.len();
let mut i = 0;
while i < self.count {
if let Some(entry) = &self.entries[i] {
if entry.reason_code != episode.primary_reason_code { i += 1; continue; }
if correlation_count < entry.min_correlation_count
|| correlation_count > entry.max_correlation_count
{ i += 1; continue; }
if duration_windows < entry.min_duration_windows
|| duration_windows > entry.max_duration_windows
{ i += 1; continue; }
let affinity = if entry.affinity_tiers != 0 {
entry.affinity_tiers
} else {
affinity_tiers_for(entry.reason_code, entry.min_correlation_count)
};
let mut motif_consensus: u8 = 0;
let mut w = start_w;
while w <= end_w && w < num_windows {
let win_bits = window_tier_mask[w] & affinity;
let win_pop = win_bits.count_ones() as u8;
let mut s = 0;
while s < num_signals {
let idx = w * num_signals + s;
if idx < cell_tier_mask.len() {
let cell_bits = cell_tier_mask[idx] & affinity;
let total = cell_bits.count_ones() as u8 + win_pop;
if total > motif_consensus { motif_consensus = total; }
}
s += 1;
}
w += 1;
}
let max_motif_tiers = (affinity.count_ones() as u8).max(1);
let motif_consensus_capped = motif_consensus.min(max_motif_tiers);
let consensus_factor = motif_consensus_capped as f64
/ max_motif_tiers as f64;
if use_zero_tier_filter
&& entry.affinity_tiers != 0
&& affinity != u32::MAX
&& motif_consensus == 0
{
i += 1;
continue;
}
if use_primary_witness_tier_gate && entry.primary_witness_tiers != 0 {
let mut witness_fired = false;
let mut wp = start_w;
while wp <= end_w && wp < num_windows && !witness_fired {
if window_tier_mask[wp] & entry.primary_witness_tiers != 0 {
witness_fired = true;
break;
}
let mut sp = 0;
while sp < num_signals {
let idxp = wp * num_signals + sp;
if idxp < cell_tier_mask.len()
&& cell_tier_mask[idxp] & entry.primary_witness_tiers != 0
{
witness_fired = true;
break;
}
sp += 1;
}
wp += 1;
}
if !witness_fired {
i += 1;
continue;
}
}
let mut score: f64 = 1.0;
if avg_drift_persistence >= entry.drift_threshold {
score += entry.weight_drift * avg_drift_persistence;
}
if slew_mag >= entry.slew_threshold {
score += entry.weight_slew * slew_mag;
}
if avg_boundary_density >= entry.boundary_density_threshold {
score += entry.weight_boundary * avg_boundary_density;
}
score += entry.weight_correlation * (correlation_count as f64) * 0.1;
score += entry.weight_duration * (duration_windows as f64) * 0.05;
score *= 1.0 + 0.5 * consensus_factor;
if use_disambiguator_boost && i < MAX && entry_confuser_idx[i] != usize::MAX {
let confuser_aff = entry_affinity[entry_confuser_idx[i]];
let disambig_mask = affinity & !confuser_aff;
let disambig_max = disambig_mask.count_ones() as u8;
if disambig_max > 0 {
let mut disambig_fired: u8 = 0;
let mut w2 = start_w;
while w2 <= end_w && w2 < num_windows {
let win_d = (window_tier_mask[w2] & disambig_mask).count_ones() as u8;
let mut s2 = 0;
while s2 < num_signals {
let idx2 = w2 * num_signals + s2;
if idx2 < cell_tier_mask.len() {
let cell_d = (cell_tier_mask[idx2] & disambig_mask).count_ones() as u8;
let total_d = (cell_d + win_d).min(disambig_max);
if total_d > disambig_fired { disambig_fired = total_d; }
}
s2 += 1;
}
w2 += 1;
}
let disambig_factor = disambig_fired as f64 / disambig_max as f64;
score *= 1.0 + 0.3 * disambig_factor;
}
}
if i < MAX { entry_scores[i] = score; }
let prov_rank = provenance_rank(entry.provenance);
let take_top = score > best_score
|| (score == best_score && prov_rank > best_provenance_rank)
|| (score == best_score && prov_rank == best_provenance_rank && i < best_index);
if take_top {
if let Some(prev_top) = best_match {
if score > runner_up_score
|| (score == runner_up_score && best_score > runner_up_score)
{
runner_up_match = Some(prev_top);
runner_up_score = best_score;
}
}
best_score = score;
best_match = Some(entry.motif_class);
best_provenance_rank = prov_rank;
best_index = i;
best_consensus_factor = consensus_factor;
} else if score > runner_up_score {
runner_up_score = score;
runner_up_match = Some(entry.motif_class);
}
}
i += 1;
}
let _ = (max_active_tiers, episode_max_consensus); let disposition = match best_match {
Some(m) => SemanticDisposition::Named(m),
None => SemanticDisposition::Unknown,
};
let margin = if best_score > 0.0 {
((best_score - runner_up_score) / best_score).clamp(0.0, 1.0)
} else { 0.0 };
let mut confuser_motif: Option<MotifClass> = None;
let mut confuser_score = 0.0_f64;
let mut margin_vs_confuser = 0.0_f64;
if best_index < MAX {
if let Some(top_entry) = &self.entries[best_index] {
if let Some(c) = top_entry.confuser_motif {
let mut k = 0;
while k < self.count {
if let Some(e) = &self.entries[k] {
if e.motif_class == c && k < MAX {
confuser_motif = Some(c);
confuser_score = entry_scores[k];
if best_score > 0.0 {
margin_vs_confuser =
((best_score - confuser_score) / best_score).clamp(0.0, 1.0);
}
break;
}
}
k += 1;
}
}
}
}
MatchConfidence {
disposition,
top_score: best_score,
runner_up_score,
runner_up_motif: runner_up_match,
margin,
tier_consensus_factor: best_consensus_factor,
confuser_motif,
confuser_score,
margin_vs_confuser,
}
}
}
impl<const MAX: usize> Default for HeuristicsBank<MAX> {
fn default() -> Self {
Self::with_canonical_motifs()
}
}
#[inline]
const fn provenance_rank(p: Provenance) -> u8 {
match p {
Provenance::FieldValidated => 3,
Provenance::DatasetObserved => 2,
Provenance::FrameworkDesign => 1,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn blank_episode_with(
primary_reason: ReasonCode,
peak_slew: f64,
contributing: u16,
start: u64,
end: u64,
drift_dir: DriftDirection,
) -> DebugEpisode {
DebugEpisode {
episode_id: 0,
start_window: start,
end_window: end,
peak_grammar_state: GrammarState::Boundary,
primary_reason_code: primary_reason,
matched_motif: SemanticDisposition::Unknown,
policy_state: PolicyState::Review,
contributing_signal_count: contributing,
structural_signature: StructuralSignature {
dominant_drift_direction: drift_dir,
peak_slew_magnitude: peak_slew,
duration_windows: end - start + 1,
signal_correlation: contributing as f64 / 8.0,
},
root_cause_signal_index: None,
}
}
#[test]
fn matches_memory_leak_drift() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::SustainedOutwardDrift, 0.05, 1, 0, 30, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.7, 0.5);
match got {
SemanticDisposition::Named(MotifClass::MemoryLeakDrift)
| SemanticDisposition::Named(MotifClass::JvmHeapPressure)
| SemanticDisposition::Named(MotifClass::ResourceSaturation)
| SemanticDisposition::Named(MotifClass::DiskIoSaturation)
| SemanticDisposition::Named(MotifClass::CpuSaturation)
| SemanticDisposition::Named(MotifClass::PacketLossErrorEscalation)
| SemanticDisposition::Named(MotifClass::ErrorRateEscalation)
| SemanticDisposition::Named(MotifClass::SaturationTrending)
| SemanticDisposition::Named(MotifClass::ConnectionPoolExhaustionDrift)
| SemanticDisposition::Named(MotifClass::DependencySlowdown)
| SemanticDisposition::Named(MotifClass::QueueBackpressure)
| SemanticDisposition::Named(MotifClass::LogVolumeAnomaly)
| SemanticDisposition::Named(MotifClass::LogSeverityEscalation) => {}
other => panic!("expected a SustainedOutwardDrift motif, got {:?}", other),
}
}
#[test]
fn cascading_timeout_requires_multi_service_correlation() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 0.9, 4, 10, 14, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.3, 0.1);
match got {
SemanticDisposition::Named(MotifClass::CascadingTimeoutSlew)
| SemanticDisposition::Named(MotifClass::CircuitBreakerOpenShift)
| SemanticDisposition::Named(MotifClass::AuthenticationFailureSpike)
| SemanticDisposition::Named(MotifClass::EpisodicTransientSpike)
| SemanticDisposition::Named(MotifClass::MetricCorrelationCollapse)
| SemanticDisposition::Named(MotifClass::LogTraceTemporalDecorrelation) => {}
SemanticDisposition::Named(MotifClass::DeploymentRegressionSlew) => panic!(
"DeploymentRegressionSlew matched on multi-service signature; \
max_correlation_count gate failed"
),
other => panic!("unexpected motif on cascading signature: {:?}", other),
}
}
#[test]
fn deployment_regression_requires_single_service_step() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 1.0, 1, 100, 250, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.1, 0.1);
if let SemanticDisposition::Named(MotifClass::CascadingTimeoutSlew) = got {
panic!("CascadingTimeoutSlew matched on single-service signature; \
min_correlation_count gate failed");
}
}
#[test]
fn empty_bank_yields_unknown() {
let bank = HeuristicsBank::<8>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::SingleCrossing, 0.0, 1, 0, 1, DriftDirection::None);
let got = bank.match_episode(&ep, 0.0, 0.0);
assert_eq!(got, SemanticDisposition::Unknown);
}
#[test]
fn signal_lookup_v01_compatibility() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let got = bank.lookup(ReasonCode::AbruptSlewViolation, 0.0, 0.9);
match got {
SemanticDisposition::Named(_) => {}
SemanticDisposition::Unknown => {
panic!("signal-level lookup must surface a Named motif on AbruptSlewViolation + slew=0.9")
}
}
}
#[test]
fn provenance_tie_breaker_prefers_dataset_observed() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 0.6, 1, 0, 2, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.0, 0.0);
if let SemanticDisposition::Named(motif) = got {
let entry = bank.entry_for(motif).expect("matched motif should be in bank");
assert_ne!(entry.provenance, Provenance::FieldValidated,
"no FieldValidated entries are populated yet");
} else {
panic!("expected a Named motif, got Unknown");
}
}
#[test]
fn lookup_admissible_silent() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let got = bank.lookup(ReasonCode::Admissible, 0.0, 0.0);
assert_eq!(got, SemanticDisposition::Unknown,
"Admissible reason code has no motif; must be Unknown");
}
#[test]
fn count_after_canonical_init() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
assert_eq!(bank.count(), 32);
}
#[test]
fn entry_for_returns_metadata() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let entry = bank.entry_for(MotifClass::CascadingTimeoutSlew)
.expect("CascadingTimeoutSlew must be in the canonical bank");
assert_eq!(entry.provenance, Provenance::DatasetObserved);
assert!(!entry.dashboard_hint.is_empty());
assert!(!entry.taxonomy_ref.is_empty());
assert_eq!(entry.evidence_dataset, "tadbench_trainticket_F04");
}
#[test]
fn db_lock_wants_multiple_services() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::SustainedOutwardDrift, 0.3, 1, 0, 10, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.6, 0.5);
if let SemanticDisposition::Named(MotifClass::DatabaseLockContention) = got {
panic!("DatabaseLockContention should not match single-service episodes");
}
}
#[test]
fn transient_spike_excludes_long_duration() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 0.9, 2, 0, 49, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.0, 0.0);
if let SemanticDisposition::Named(MotifClass::EpisodicTransientSpike) = got {
panic!("EpisodicTransientSpike must not match long-duration episodes");
}
}
#[test]
fn jvm_gc_pause_caps_correlation() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 0.6, 8, 0, 5, DriftDirection::Positive);
let got = bank.match_episode(&ep, 0.1, 0.4);
if let SemanticDisposition::Named(MotifClass::JvmGcPause) = got {
panic!("JvmGcPause must not match wide multi-service episodes");
}
}
#[test]
fn high_dim_cluster_requires_many_signals() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep_few = blank_episode_with(
ReasonCode::SustainedOutwardDrift, 0.3, 3, 0, 10, DriftDirection::Positive);
let got = bank.match_episode(&ep_few, 0.5, 0.4);
if let SemanticDisposition::Named(MotifClass::HighDimAnomalyCluster) = got {
panic!("HighDimAnomalyCluster must not match low-correlation episodes");
}
}
#[test]
fn taxonomy_ref_present_on_every_entry() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let mut i = 0;
while i < bank.count {
if let Some(entry) = &bank.entries[i] {
assert!(!entry.taxonomy_ref.is_empty(),
"entry index {} has empty taxonomy_ref", i);
assert!(!entry.dashboard_hint.is_empty(),
"entry index {} has empty dashboard_hint", i);
assert!(!entry.evidence_dataset.is_empty(),
"entry index {} has empty evidence_dataset", i);
}
i += 1;
}
}
#[test]
fn confidence_margin_in_unit_interval() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::AbruptSlewViolation, 0.9, 4, 10, 14, DriftDirection::Positive);
let conf = bank.match_episode_with_confidence(&ep, 0.3, 0.1);
assert!(matches!(conf.disposition, SemanticDisposition::Named(_)),
"high-slew multi-service episode should produce a named disposition");
assert!(conf.margin >= 0.0 && conf.margin <= 1.0,
"margin must be in [0, 1]; got {}", conf.margin);
assert!(conf.top_score > 0.0, "top_score must be positive when match is Named");
}
#[test]
fn confidence_zero_when_unknown() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::SingleCrossing, 0.0, 1, 0, 1, DriftDirection::None);
let conf = bank.match_episode_with_confidence(&ep, 0.0, 0.0);
assert_eq!(conf.disposition, SemanticDisposition::Unknown);
assert_eq!(conf.top_score, 0.0);
assert_eq!(conf.margin, 0.0);
}
#[test]
fn confidence_runner_up_distinct_from_top_when_present() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let ep = blank_episode_with(
ReasonCode::SustainedOutwardDrift, 0.05, 3, 0, 30, DriftDirection::Positive);
let conf = bank.match_episode_with_confidence(&ep, 0.7, 0.5);
if let SemanticDisposition::Named(top) = conf.disposition {
if let Some(runner_up) = conf.runner_up_motif {
assert_ne!(top, runner_up,
"runner-up must differ from top when both are present");
}
}
}
#[test]
fn dataset_observed_entries_have_doi() {
let bank = HeuristicsBank::<64>::with_canonical_motifs();
let mut i = 0;
while i < bank.count {
if let Some(entry) = &bank.entries[i] {
if entry.provenance == Provenance::DatasetObserved {
assert!(!entry.evidence_dataset_doi.is_empty(),
"DatasetObserved entry index {} missing DOI", i);
}
}
i += 1;
}
}
}