dsfb_debug/error.rs
1//! DSFB-Debug: error types — additive, Copy, no-panic.
2//!
3//! Every fallible operation in the crate returns `Result<T, DsfbError>`
4//! rather than panicking. The crate's top-level lints
5//! (`#![deny(clippy::unwrap_used)]`) enforce this at compile time.
6//!
7//! `DsfbError` is `Copy + Clone + Debug + PartialEq + Eq` — every
8//! variant is a stack-only enum, no heap allocation. Variants are
9//! additive across versions: existing variants are never removed or
10//! renumbered, so cross-version `match` arms remain valid.
11//!
12//! # Variant taxonomy
13//!
14//! - **DimensionMismatch / BufferTooSmall** — caller-shape errors
15//! (operator passed wrong slice lengths or undersized output buffer)
16//! - **MissingRealData / HashMismatch** — `paper-lock` integrity
17//! gates (sentinel fixture / SHA-256 drift)
18//! - **ParseError** — adapter/fixture-format errors (invalid TSV,
19//! missing header, etc.)
20//! - **InvalidConfig** — paper-lock config-construction errors
21//!
22//! Operators reading these errors at runtime can map each variant to
23//! a specific corrective action; the crate documents the action in
24//! each variant's per-variant doc-comment.
25
26/// All errors the DSFB engine can produce.
27/// No heap allocation — all variants are Copy.
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub enum DsfbError {
30 /// Input slice length does not match expected signal count
31 DimensionMismatch { expected: usize, got: usize },
32 /// Baseline has not been established (healthy window not computed)
33 BaselineNotEstablished,
34 /// Envelope radius is zero or negative for a signal
35 InvalidEnvelopeRadius { signal_index: usize },
36 /// Window index is out of range for history buffer
37 WindowOutOfRange { index: u64 },
38 /// Episode buffer is full — increase MAX_EPISODES
39 EpisodeBufferFull,
40 /// Signal buffer is full — increase MAX_SIGNALS
41 SignalBufferFull,
42 /// History buffer is full — increase MAX_WINDOWS
43 HistoryBufferFull,
44 /// Heuristics bank is full — increase MAX_MOTIFS
45 HeuristicsBankFull,
46 /// Configuration parameter is invalid
47 InvalidConfig(&'static str),
48 /// Dataset parse error at a specific record
49 ParseError { record: u64, field: u16 },
50 /// Insufficient healthy-window data for baseline
51 InsufficientBaselineData { available: usize, required: usize },
52 /// Internal flat-aggregation buffer too small for the requested
53 /// `num_signals * num_windows`. Returned at `run_evaluation` entry.
54 BufferTooSmall { needed: usize, available: usize },
55 /// `paper-lock` real-data evaluation requested but the supplied fixture
56 /// bytes are empty. Never falls back to synthetic data.
57 MissingRealData,
58 /// SHA-256 of supplied fixture bytes does not match the manifest's
59 /// `fixture_sha256`. Indicates tampering or upstream drift; the
60 /// engine refuses to proceed.
61 HashMismatch,
62}
63
64impl core::fmt::Display for DsfbError {
65 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
66 match self {
67 Self::DimensionMismatch { expected, got } => {
68 write!(f, "dimension mismatch: expected {}, got {}", expected, got)
69 }
70 Self::BaselineNotEstablished => write!(f, "baseline not established"),
71 Self::InvalidEnvelopeRadius { signal_index } => {
72 write!(f, "invalid envelope radius at signal {}", signal_index)
73 }
74 Self::WindowOutOfRange { index } => {
75 write!(f, "window index {} out of range", index)
76 }
77 Self::EpisodeBufferFull => write!(f, "episode buffer full"),
78 Self::SignalBufferFull => write!(f, "signal buffer full"),
79 Self::HistoryBufferFull => write!(f, "history buffer full"),
80 Self::HeuristicsBankFull => write!(f, "heuristics bank full"),
81 Self::InvalidConfig(msg) => write!(f, "invalid config: {}", msg),
82 Self::ParseError { record, field } => {
83 write!(f, "parse error at record {}, field {}", record, field)
84 }
85 Self::InsufficientBaselineData { available, required } => {
86 write!(f, "insufficient baseline data: {} of {} required", available, required)
87 }
88 Self::BufferTooSmall { needed, available } => {
89 write!(f, "internal flat buffer too small: needed {}, available {}", needed, available)
90 }
91 Self::MissingRealData => write!(f, "real-data fixture missing or empty (paper-lock refuses to fall back to synthetic)"),
92 Self::HashMismatch => write!(f, "fixture SHA-256 does not match manifest"),
93 }
94 }
95}
96
97pub type Result<T> = core::result::Result<T, DsfbError>;