dsfb_debug/types.rs
1//! DSFB-Debug: core domain types — public API contract.
2//!
3//! Every type in this module is `Copy + Clone + Debug + PartialEq`
4//! with no heap allocation. This is the load-bearing engineering
5//! claim of the no_std core: the entire residual evaluation pipeline
6//! moves Copy values across function boundaries. No Box, no Rc, no
7//! Vec on hot paths. Every public surface accepts and returns
8//! `&[T]` slices only — the observer-only contract is type-enforced.
9//!
10//! # Mapping to paper §5
11//!
12//! | Type | Paper concept | Section |
13//! |------|---------------|:------:|
14//! | `SignTuple` | residual signature σ(k) = (‖r‖, ṙ, r̈) | §5.3 |
15//! | `GrammarState` | 4-state automaton | §5.5 |
16//! | `ReasonCode` | residual policy reason | §5.5 |
17//! | `MotifClass` | typed motif (32 variants) | §5.6 |
18//! | `Provenance` | evidence ladder (3-tier) | §5.6 |
19//! | `SemanticDisposition` | bank lookup outcome | §5.6 |
20//! | `MatchConfidence` | per-episode evidence packet | §11.y |
21//! | `HeuristicEntry` | bank record (motif IP claim) | §4.x |
22//! | `DebugEpisode` | aggregated structural episode | §7 |
23//! | `BenchmarkMetrics` | RSCR / fault-recall / FP rate | §13 |
24//! | `AuditRecord` | NIST SP 800-53 AU-3 record | §15 |
25//!
26//! # Standards alignment
27//!
28//! - **NIST SP 800-53 AU-3** (audit record content): `AuditRecord`
29//! fields cover the required event_type / when / where / source /
30//! outcome content axes.
31//! - **NIST SP 800-53 AU-2** (auditable events):
32//! `HeuristicEntry.primary_witness_detectors` defines the named
33//! auditable events that must fire for typed confirmation.
34//! - **ISO/IEC 25010** (Analysability): typed `MotifClass` + per-
35//! episode evidence packet support the Analysability quality
36//! characteristic.
37//! - **IEEE 1012-2016** (V&V): the confuser-boundary mechanism
38//! provides independent validation of typed disposition.
39//!
40//! # Stability guarantee
41//!
42//! All `pub struct` fields are additive across versions; existing
43//! fields retain their type and semantics. A new field added in a
44//! later phase always carries a default-friendly meaning (e.g.
45//! `affinity_tiers: 0` falls back to the reason-code-derived mask;
46//! `primary_witness_detectors: &[]` disables Phase 8). This
47//! preserves Theorem 9 deterministic replay across upgrades.
48
49/// Residual sign tuple σ(k) = (‖r‖, ṙ, r̈)
50/// Paper §5.3
51#[derive(Copy, Clone, Debug, PartialEq)]
52pub struct SignTuple {
53 /// ‖r(k)‖ — instantaneous deviation magnitude
54 pub norm: f64,
55 /// ṙ(k) — finite-difference drift rate (window-to-window)
56 pub drift: f64,
57 /// r̈(k) — second difference / slew (curvature of trajectory)
58 pub slew: f64,
59}
60
61impl SignTuple {
62 pub const ZERO: Self = Self { norm: 0.0, drift: 0.0, slew: 0.0 };
63}
64
65/// Grammar state — Paper §5.5
66#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
67pub enum GrammarState {
68 /// Within envelope, drift inward or bounded. Normal operation.
69 Admissible = 0,
70 /// Approaching envelope with sustained outward drift. Early-warning.
71 Boundary = 1,
72 /// Exited envelope. Structural fault / actionable incident.
73 Violation = 2,
74}
75
76/// Grammar reason codes — Paper §5.5 Table
77#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78pub enum ReasonCode {
79 /// Normal operation — no structural concern
80 Admissible,
81 /// Approaching SLO boundary — monitoring recommended
82 BoundaryApproach,
83 /// Memory leak, latency creep, cache degradation, dependency slowdown
84 SustainedOutwardDrift,
85 /// Crash, spike, exception storm, deployment regression, cascading timeout
86 AbruptSlewViolation,
87 /// Periodic load saturation, GC pressure, cron-triggered spikes
88 RecurrentBoundaryGrazing,
89 /// Confirmed SLO breach — actionable incident
90 EnvelopeViolation,
91 /// Outward drift followed by self-correction
92 DriftWithRecovery,
93 /// Transient single-step boundary touch — dismissed by persistence gate
94 SingleCrossing,
95}
96
97/// Policy states — developer-facing output
98/// Paper §5: Silent / Watch / Review / Escalate
99#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
100pub enum PolicyState {
101 /// No motif activated; persistence or corroboration gate failed
102 Silent = 0,
103 /// Structural activity below escalation threshold
104 Watch = 1,
105 /// Motif confirmed; developer review warranted
106 Review = 2,
107 /// Motif confirmed + violation-class; immediate attention
108 Escalate = 3,
109}
110
111/// Motif classes for software debugging — Paper §5.6.
112///
113/// Names are anchored to IEEE 24765 vocabulary and the
114/// Avizienis–Laprie–Randell dependability tree (fault → error → failure).
115/// No ad-hoc terminology: every variant decomposes into established
116/// software-engineering vocabulary.
117#[derive(Copy, Clone, Debug, PartialEq, Eq)]
118pub enum MotifClass {
119 // ===== Tier-1: original 10 motifs (FrameworkDesign provenance) =======
120
121 /// Sustained monotonic memory-consumption drift.
122 /// IEEE 24765: "memory leak"; A-L-R: latent fault → error.
123 MemoryLeakDrift,
124 /// Step-change latency propagating across dependency chain.
125 /// IEEE 24765: "fault propagation"; A-L-R: error → service-failure.
126 CascadingTimeoutSlew,
127 /// Abrupt shift coinciding with deployment event.
128 /// IEEE 24765: "regression"; A-L-R: design fault → error.
129 DeploymentRegressionSlew,
130 /// Oscillatory approach to latency SLO boundary.
131 /// IEEE 24765: "performance degradation"; A-L-R: marginal-state error.
132 CacheDegradationGrazing,
133 /// Slow positive drift in queue depth + latency.
134 /// IEEE 24765: "resource exhaustion"; A-L-R: error build-up.
135 ConnectionPoolExhaustionDrift,
136 /// Periodic slew events from garbage collection.
137 /// IEEE 24765: "stop-the-world pause"; A-L-R: transient error.
138 GcPressureOscillation,
139 /// Sustained positive drift in error rate.
140 /// IEEE 24765: "error escalation"; A-L-R: error → multi-failure regime.
141 ErrorRateEscalation,
142 /// Gradual latency increase in upstream dependency.
143 /// IEEE 24765: "performance degradation upstream"; A-L-R: external fault.
144 DependencySlowdown,
145 /// CPU/memory/disk approaching ceiling.
146 /// IEEE 24765: "resource saturation"; A-L-R: latent → manifest fault.
147 ResourceSaturation,
148 /// Message queue depth growing monotonically.
149 /// IEEE 24765: "back-pressure accumulation"; A-L-R: error build-up.
150 QueueBackpressure,
151
152 // ===== Tier-2: TADBench / TrainTicket fault cases =====================
153
154 /// Retry-storm cascade: client retries amplify upstream load.
155 /// Evidence: TADBench retry-storm fault case.
156 /// IEEE 24765: "retry-induced amplification"; A-L-R: cascading error.
157 RetryStormCascade,
158 /// Circuit breaker open-state shift; downstream calls return immediately.
159 /// Evidence: TADBench circuit-breaker fault case.
160 /// IEEE 24765: "fault tolerance mechanism state change".
161 CircuitBreakerOpenShift,
162 /// Database lock contention with rising queue + latency.
163 /// Evidence: TADBench db-lock fault case.
164 /// IEEE 24765: "concurrency fault"; A-L-R: synchronisation error.
165 DatabaseLockContention,
166 /// Authentication failure spike (auth-backend partial outage).
167 /// Evidence: TADBench auth-fail fault case.
168 /// IEEE 24765: "authentication subsystem failure".
169 AuthenticationFailureSpike,
170 /// Step shift coinciding with version-config change.
171 /// Evidence: TrainTicket-Anomaly version-config fault class.
172 /// IEEE 24765: "configuration regression"; A-L-R: design-time fault.
173 ConfigDriftRegression,
174
175 // ===== Tier-3: AIOps Challenge categories =============================
176
177 /// Packet-loss-induced error escalation (network-layer fault).
178 /// Evidence: AIOps Challenge packet_loss category.
179 /// IEEE 24765: "communication failure (lower layer)".
180 PacketLossErrorEscalation,
181 /// Network-delay-induced upstream latency inflation.
182 /// Evidence: AIOps Challenge network_delay category.
183 /// IEEE 24765: "communication-path performance fault".
184 NetworkDelayDependencyInflation,
185 /// Disk-I/O saturation; concave-up latency drift.
186 /// Evidence: AIOps Challenge disk_exhaustion category.
187 /// IEEE 24765: "storage subsystem saturation".
188 DiskIoSaturation,
189 /// CPU saturation; latency drift with rising envelope occupancy.
190 /// Evidence: AIOps Challenge cpu_exhaustion category.
191 /// IEEE 24765: "compute resource saturation".
192 CpuSaturation,
193 /// JVM heap pressure; sustained latency drift with rising variance.
194 /// Refines `MemoryLeakDrift` for JVM-specific signatures.
195 /// Evidence: AIOps Challenge memory_exhaustion category.
196 JvmHeapPressure,
197 /// JVM GC pause: distinct stop-the-world latency spikes.
198 /// Refines `GcPressureOscillation` for JVM-specific signatures.
199 /// Evidence: AIOps Challenge jvm_resource_exhaustion category.
200 JvmGcPause,
201
202 // ===== Tier-4: MultiDimension-Localization patterns ==================
203
204 /// Drift propagating along the service-call graph (multi-hop).
205 /// Evidence: MultiDim-Localization root-cause-graph cases.
206 /// IEEE 24765: "graph-structured fault propagation".
207 ServiceGraphDriftPropagation,
208 /// Multi-metric correlated anomaly without dominant single signal.
209 /// Evidence: MultiDim-Localization high-dim cluster cases.
210 /// IEEE 24765: "compound fault signature".
211 HighDimAnomalyCluster,
212 /// Historically-correlated metrics decorrelate; structural regime shift.
213 /// Evidence: MultiDim-Localization correlation-collapse cases.
214 /// IEEE 24765: "structural model invalidation".
215 MetricCorrelationCollapse,
216
217 // ===== Tier-5: DeepTraLog log + trace fusion patterns ================
218
219 /// Log-frequency outward drift on a service.
220 /// Evidence: DeepTraLog log-volume anomalies.
221 /// IEEE 24765: "diagnostic-output anomaly".
222 LogVolumeAnomaly,
223 /// Log timing departs from trace timing pattern.
224 /// Evidence: DeepTraLog log-trace temporal-mismatch cases.
225 /// IEEE 24765: "instrumentation-temporal divergence".
226 LogTraceTemporalDecorrelation,
227 /// Log severity distribution shifts (more WARN/ERROR proportionally).
228 /// Evidence: DeepTraLog severity-shift cases.
229 /// IEEE 24765: "diagnostic severity escalation".
230 LogSeverityEscalation,
231
232 // ===== Tier-6: cross-cutting structural motifs =======================
233
234 /// Concave-up approach to a ceiling; generalises ResourceSaturation.
235 /// IEEE 24765: "asymptotic resource saturation".
236 SaturationTrending,
237 /// Short-duration high-slew event that self-resolves.
238 /// IEEE 24765: "transient-only error"; A-L-R: transient fault.
239 EpisodicTransientSpike,
240 /// Outward drift followed by return to baseline.
241 /// Refines `ReasonCode::DriftWithRecovery` into a motif class.
242 /// IEEE 24765: "self-healing transient drift".
243 RegressiveDriftWithRecovery,
244 /// First-time approach to the admissibility envelope without
245 /// recurrence or persistent drift evidence. Catches the
246 /// `ReasonCode::BoundaryApproach` reason code so it isn't an
247 /// orphan in the canonical bank (validated by
248 /// `tests::no_orphan_reason_codes_in_canonical_bank`).
249 /// IEEE 24765: "marginal-state transient"; A-L-R: dormant fault.
250 EnvelopeBoundaryApproach,
251 /// Envelope breach without abrupt slew evidence — the
252 /// "value stepped past the threshold but the trajectory shape
253 /// shows no slew" case. Catches the
254 /// `ReasonCode::EnvelopeViolation` reason code so it isn't an
255 /// orphan in the canonical bank.
256 /// IEEE 24765: "threshold breach (smooth)"; A-L-R: error → manifest.
257 EnvelopeBreach,
258}
259
260/// Semantic disposition from heuristics bank lookup
261#[derive(Copy, Clone, Debug, PartialEq, Eq)]
262pub enum SemanticDisposition {
263 /// Known motif matched in heuristics bank
264 Named(MotifClass),
265 /// Endoductive mode — structure characterized but no named match
266 Unknown,
267}
268
269/// Confidence-bearing motif match result.
270///
271/// Returned by `HeuristicsBank::match_episode_with_confidence`. The
272/// `margin` field is `(top_score - runner_up_score) / top_score`,
273/// clamped to `[0.0, 1.0]`. Operators reading the margin can calibrate
274/// trust:
275/// - margin > 0.5 → top motif clearly dominates; act on it
276/// - margin in (0.2, 0.5] → moderate confidence; surface runner-up
277/// - margin <= 0.2 → top and runner-up are competitive; surface both
278/// - top_score == 0.0 → no motif matched (`disposition == Unknown`)
279#[derive(Copy, Clone, Debug, PartialEq)]
280pub struct MatchConfidence {
281 pub disposition: SemanticDisposition,
282 pub top_score: f64,
283 pub runner_up_score: f64,
284 pub runner_up_motif: Option<MotifClass>,
285 pub margin: f64,
286 /// Phase 3 — fraction of the matched motif's affinity tiers that
287 /// actually fired in the episode range, in [0, 1]. Populated only
288 /// by `match_episode_with_tier_affinity`; the legacy
289 /// `match_episode_with_consensus` leaves this at 0.0. Used by the
290 /// adaptive margin gate (Path 3): when `tier_consensus_factor > 0.5`,
291 /// the gate is halved — strong tier evidence justifies lower
292 /// margin requirement.
293 pub tier_consensus_factor: f64,
294
295 /// Phase 5.6 — explicit confuser motif declared by the matched
296 /// motif's `HeuristicEntry`. May differ from `runner_up_motif` if
297 /// the score-based runner-up is not the declared confuser. `None`
298 /// when no confuser is declared.
299 pub confuser_motif: Option<MotifClass>,
300
301 /// Phase 5.6 — score of the declared confuser computed in the same
302 /// scoring pass as the matched motif. 0.0 if no confuser is
303 /// declared.
304 pub confuser_score: f64,
305
306 /// Phase 5.6 — margin of top motif over its declared confuser:
307 /// `(top_score - confuser_score) / top_score`, clamped to [0, 1].
308 /// Used by fusion's confuser-aware typed-confirmation gate.
309 /// 0.0 if no confuser is declared.
310 pub margin_vs_confuser: f64,
311}
312
313/// Provenance tag — where this interpretation came from
314#[derive(Copy, Clone, Debug, PartialEq, Eq)]
315pub enum Provenance {
316 /// Built into the initial heuristics bank by DSFB design
317 FrameworkDesign,
318 /// Derived from benchmark dataset observation
319 DatasetObserved,
320 /// Confirmed in production deployment
321 FieldValidated,
322}
323
324/// Heuristics bank entry — typed, provenance-aware motif record.
325///
326/// The struct is `Copy + Clone + Debug + PartialEq`; all fields are
327/// stack-only (`'static` string slices, primitives, enums). The bank is
328/// initialised at compile time as an array of `HeuristicEntry`, so
329/// every field must be `const`-friendly.
330///
331/// The seven original fields (`motif_class` … `slew_threshold`) preserve
332/// the v0.1 wire shape. The thirteen additional fields enable
333/// episode-level multi-feature scoring (`match_episode`), per-motif
334/// provenance ladders (`evidence_dataset`, `evidence_dataset_doi`),
335/// production-engineer dashboard hints (`dashboard_hint`), and
336/// taxonomy anchors (`taxonomy_ref`). All are documented in
337/// `docs/heuristics_bank.md`.
338#[derive(Copy, Clone, Debug, PartialEq)]
339pub struct HeuristicEntry {
340 pub motif_class: MotifClass,
341 pub reason_code: ReasonCode,
342 /// NOT an attribution — a candidate interpretation hypothesis
343 pub candidate_interpretation: &'static str,
344 pub provenance: Provenance,
345 pub recommended_action: PolicyState,
346 /// Minimum drift persistence (fraction of window) to trigger
347 pub drift_threshold: f64,
348 /// Minimum slew magnitude to trigger
349 pub slew_threshold: f64,
350
351 // ===== additive fields (Session 3) =====
352 //
353 // Episode-level feature thresholds. The signal-level `lookup`
354 // ignores these (so v0.1 callers see no behaviour change); the new
355 // `match_episode` consults them.
356 /// Minimum boundary-density (fraction of episode windows in
357 /// Boundary state) for this motif to trigger.
358 pub boundary_density_threshold: f64,
359 /// Minimum number of contributing signals (e.g. multi-service motifs
360 /// require ≥ 2). Use 1 for "any".
361 pub min_correlation_count: u16,
362 /// Maximum number of contributing signals (e.g. single-service
363 /// motifs require ≤ 1). Use `u16::MAX` for "unbounded".
364 pub max_correlation_count: u16,
365 /// Minimum episode duration in windows.
366 pub min_duration_windows: u16,
367 /// Maximum episode duration in windows. Use `u16::MAX` for "unbounded".
368 pub max_duration_windows: u16,
369
370 /// Per-motif scoring weights for `match_episode`. Default 1.0
371 /// reproduces the v0.1 unit-weighted behaviour; differential
372 /// weights let one motif emphasise drift while another emphasises
373 /// slew or correlation.
374 pub weight_drift: f64,
375 pub weight_slew: f64,
376 pub weight_boundary: f64,
377 pub weight_correlation: f64,
378 pub weight_duration: f64,
379
380 /// Provenance ladder — which named upstream slice motivated this
381 /// entry. `"FrameworkDesign"` for hand-coded entries with no
382 /// dataset evidence yet; `"<dataset_key>"` (e.g.
383 /// `"tadbench_F04"`, `"aiops_challenge_packet_loss"`) for entries
384 /// observed in a vendored real-data fixture.
385 pub evidence_dataset: &'static str,
386 /// DOI of the upstream archive cited in `evidence_dataset`. Empty
387 /// string when `evidence_dataset == "FrameworkDesign"`.
388 pub evidence_dataset_doi: &'static str,
389
390 /// One-line hint for a production debug engineer reading the
391 /// matched motif on a dashboard. E.g. `"Inspect jvm.memory.heap.used
392 /// + gc.duration over the past hour"`.
393 pub dashboard_hint: &'static str,
394
395 /// Taxonomy anchor — IEEE 24765 term and Avizienis–Laprie–Randell
396 /// node. E.g. `"IEEE 24765: 'fault propagation'; A-L-R: error →
397 /// service-failure"`.
398 pub taxonomy_ref: &'static str,
399
400 /// Phase 2.5 — hand-curated tier-affinity bitmask. Each bit
401 /// corresponds to one detector tier (see `TIER_BIT_*` constants in
402 /// `heuristics_bank.rs`). The bank's
403 /// `match_episode_with_tier_affinity` AND-s this mask against the
404 /// per-cell + per-window tier-fired bitmasks to compute a
405 /// motif-conditional consensus boost. A mask of `0` falls back to
406 /// the reason-code-derived default in `affinity_tiers_for(...)`,
407 /// preserving Phase-2 behaviour for entries without a curated mask.
408 pub affinity_tiers: u32,
409
410 /// Phase 5.6 — confuser-pair adjudication. Names the primary motif
411 /// expected to compete with this one for the same residual signature
412 /// (e.g., DeploymentRegressionSlew's confuser is CircuitBreakerOpenShift —
413 /// both are step-shaped single-service motifs). The bank's match
414 /// function explicitly tracks the confuser's score during scoring,
415 /// reports `margin_vs_confuser`, and fusion gates typing on whether
416 /// the candidate beats its declared confuser by `margin_vs_confuser_threshold`.
417 /// `None` means no confuser is declared — episode types-confirmed
418 /// based on runner-up margin alone (legacy semantics).
419 pub confuser_motif: Option<MotifClass>,
420
421 /// Phase 5.6 — minimum margin against the declared confuser for typed
422 /// confirmation. Episodes that beat the runner-up but not the
423 /// confuser are reported as `confuser_ambiguous` rather than typed.
424 /// Default 0.10 (10% of top score). Set to 0.0 to disable.
425 pub margin_vs_confuser_threshold: f64,
426
427 /// Phase 7 — primary witness tier gate (strict semantic
428 /// anti-hallucination). Subset of `affinity_tiers` that MUST fire
429 /// for the motif to be a valid typing candidate. Distinct from the
430 /// Phase 5.6 zero-tier filter (any affinity tier suffices); this
431 /// requires SPECIFIC tiers to fire. E.g., `DeploymentRegressionSlew`
432 /// witnesses = {A, B, N, X} — without scalar/Page-Hinkley/offline-
433 /// CPD/Pettitt step detection actually firing, the bank refuses to
434 /// type "deployment regression" even if other affinity tiers
435 /// (correlation, dispersion) fired. A mask of `0` disables the gate
436 /// (default — match behaves identically to Phase 5.6).
437 pub primary_witness_tiers: u32,
438
439 /// Phase 8 — per-detector primary witnesses (strict ensemble-methods
440 /// SOTA gate). Named-detector subset that MUST fire for the motif to
441 /// type-confirm. Distinct from `primary_witness_tiers` (any detector
442 /// in the named tiers suffices); this requires SPECIFIC detectors
443 /// (e.g., `[poisson_burst, burst_after_silence]` for
444 /// AuthenticationFailureSpike). Names match the `detector_name`
445 /// field returned by detector functions in `incumbent_baselines.rs`.
446 /// Empty slice `&[]` disables the gate (default — Phase 7 behaviour).
447 /// Applied at fusion.rs typed-confirmation, not at bank match time;
448 /// failing motifs are demoted to ambiguous rather than skipped, so
449 /// the runner-up motif can still be reported in the operator packet.
450 pub primary_witness_detectors: &'static [&'static str],
451}
452
453/// Per-signal, per-window DSFB evaluation result.
454/// The atomic unit of the traceability chain.
455#[derive(Copy, Clone, Debug, PartialEq)]
456pub struct SignalEvaluation {
457 pub window_index: u64,
458 pub signal_index: u16,
459 pub residual_value: f64,
460 pub sign_tuple: SignTuple,
461 pub raw_grammar_state: GrammarState,
462 /// After hysteresis confirmation (n_confirm=2)
463 pub confirmed_grammar_state: GrammarState,
464 pub reason_code: ReasonCode,
465 pub motif: Option<MotifClass>,
466 pub semantic_disposition: SemanticDisposition,
467 pub dsa_score: f64,
468 pub policy_state: PolicyState,
469 /// Missingness-aware flag: if true, drift=0, slew=0, grammar=Admissible
470 pub was_imputed: bool,
471 /// Drift persistence at this evaluation (fraction of last `drift_window`
472 /// windows with positive drift). Persisted so that episode-level
473 /// `match_episode` can average across an episode's window range
474 /// without recomputing norm histories.
475 pub drift_persistence: f64,
476}
477
478/// Drift direction in an episode's structural signature
479#[derive(Copy, Clone, Debug, PartialEq, Eq)]
480pub enum DriftDirection {
481 Positive,
482 Negative,
483 Oscillatory,
484 None,
485}
486
487/// Structural signature of an episode
488#[derive(Copy, Clone, Debug, PartialEq)]
489pub struct StructuralSignature {
490 pub dominant_drift_direction: DriftDirection,
491 pub peak_slew_magnitude: f64,
492 pub duration_windows: u64,
493 pub signal_correlation: f64,
494}
495
496/// A debugging episode — the Trace Event Collapse output.
497/// Paper §7: "the primary developer-facing delta"
498#[derive(Copy, Clone, Debug, PartialEq)]
499pub struct DebugEpisode {
500 pub episode_id: u32,
501 pub start_window: u64,
502 pub end_window: u64,
503 pub peak_grammar_state: GrammarState,
504 pub primary_reason_code: ReasonCode,
505 pub matched_motif: SemanticDisposition,
506 pub policy_state: PolicyState,
507 pub contributing_signal_count: u16,
508 pub structural_signature: StructuralSignature,
509 /// Most-upstream contributing signal in the service-call graph,
510 /// if a graph was supplied to `run_evaluation_with_graph` and a
511 /// unique upstream root could be determined; else `None`.
512 /// Populated by `causality::attribute_root_cause`.
513 pub root_cause_signal_index: Option<u16>,
514}
515
516/// NIST 800-53 AU-3 compliant audit record
517/// Fields: what (event_type), when (window_index), where (signal_index),
518/// source (source), outcome (outcome)
519#[derive(Copy, Clone, Debug, PartialEq, Eq)]
520pub struct AuditRecord {
521 pub event_type: AuditEventType,
522 pub window_index: u64,
523 pub signal_index: u16,
524 pub source: AuditSource,
525 pub outcome: PolicyState,
526}
527
528#[derive(Copy, Clone, Debug, PartialEq, Eq)]
529pub enum AuditEventType {
530 GrammarStateTransition,
531 EpisodeOpened,
532 EpisodeClosed,
533 PolicyEscalation,
534 MotifMatched,
535 EndoductiveUnknown,
536}
537
538#[derive(Copy, Clone, Debug, PartialEq, Eq)]
539pub enum AuditSource {
540 GrammarEvaluator,
541 EpisodeAggregator,
542 PolicyEngine,
543 HeuristicsBank,
544}
545
546/// Benchmark metrics — the paper's headline numbers
547#[derive(Copy, Clone, Debug, PartialEq)]
548pub struct BenchmarkMetrics {
549 pub dataset_name: &'static str,
550 pub total_windows: u64,
551 pub total_signals: u16,
552 pub raw_anomaly_count: u64,
553 pub dsfb_episode_count: u64,
554 /// Review Surface Compression Ratio = raw / episodes
555 pub rscr: f64,
556 /// Fraction of episodes preceding labeled faults within W_pred
557 pub episode_precision: f64,
558 /// Fraction of labeled faults captured by at least one episode
559 pub fault_recall: f64,
560 pub investigation_load_raw: u64,
561 pub investigation_load_dsfb: u64,
562 pub investigation_load_reduction_pct: f64,
563 /// Negative control: false episode rate in clean windows
564 pub clean_window_false_episode_rate: f64,
565}