Skip to main content

u280_hardware_promoter/
failure.rs

1//! Stable fail-closed error taxonomy for promotion evidence.
2
3use serde::Serialize;
4use std::fmt;
5
6/// Machine-readable reason that a promotion transaction did not complete.
7#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
8#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
9pub enum FailureCode {
10    /// An input could not be opened through the required filesystem boundary.
11    InputFs,
12    /// A retained input or pathname changed during validation.
13    InputChanged,
14    /// A typed evidence envelope had the wrong schema.
15    Schema,
16    /// JSON, JSONL, a manifest, a digest, or a path was non-canonical.
17    Noncanonical,
18    /// A caller attempted to supply a terminal success claim.
19    PrematureClaim,
20    /// No accepted route parser exists for the observed Vitis evidence.
21    RouteUnaccepted,
22    /// Accepted route evidence failed setup, hold, or clock policy.
23    RouteTiming,
24    /// Accepted route evidence exceeded a resource ceiling.
25    RouteResource,
26    /// The sealed Phase-B transport archive failed validation.
27    TransportInvalid,
28    /// The independent oracle receipt was absent or did not bind its result.
29    OracleReceipt,
30    /// The independent oracle result archive failed validation.
31    OracleResult,
32    /// The independent oracle reported a cryptographic mismatch.
33    OracleNegative,
34    /// Independently valid evidence objects did not identify the same run/card.
35    IdentityJoin,
36    /// The complete coordination history was not one canonical genesis chain.
37    CoordinationChain,
38    /// A run, intent, artifact, or grant binding was already consumed.
39    CoordinationReplay,
40    /// The measured population did not meet the fixed batch/sample policy.
41    PerformancePopulation,
42    /// An exact integer performance threshold was not met.
43    PerformanceThreshold,
44    /// Durable promotion-state validation or append failed.
45    StateBurn,
46    /// Exclusive result construction or atomic publication failed.
47    Publication,
48}
49
50impl FailureCode {
51    /// Stable uppercase spelling stored in failure evidence.
52    #[must_use]
53    pub const fn as_str(self) -> &'static str {
54        match self {
55            Self::InputFs => "INPUT_FS",
56            Self::InputChanged => "INPUT_CHANGED",
57            Self::Schema => "SCHEMA",
58            Self::Noncanonical => "NONCANONICAL",
59            Self::PrematureClaim => "PREMATURE_CLAIM",
60            Self::RouteUnaccepted => "ROUTE_UNACCEPTED",
61            Self::RouteTiming => "ROUTE_TIMING",
62            Self::RouteResource => "ROUTE_RESOURCE",
63            Self::TransportInvalid => "TRANSPORT_INVALID",
64            Self::OracleReceipt => "ORACLE_RECEIPT",
65            Self::OracleResult => "ORACLE_RESULT",
66            Self::OracleNegative => "ORACLE_NEGATIVE",
67            Self::IdentityJoin => "IDENTITY_JOIN",
68            Self::CoordinationChain => "COORDINATION_CHAIN",
69            Self::CoordinationReplay => "COORDINATION_REPLAY",
70            Self::PerformancePopulation => "PERFORMANCE_POPULATION",
71            Self::PerformanceThreshold => "PERFORMANCE_THRESHOLD",
72            Self::StateBurn => "STATE_BURN",
73            Self::Publication => "PUBLICATION",
74        }
75    }
76}
77
78impl fmt::Display for FailureCode {
79    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80        formatter.write_str(self.as_str())
81    }
82}
83
84/// One promotion failure with a stable code and contextual explanation.
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub struct PromotionError {
87    code: FailureCode,
88    message: String,
89}
90
91impl PromotionError {
92    /// Creates a failure at a specific evidence boundary.
93    #[must_use]
94    pub fn new(code: FailureCode, message: impl Into<String>) -> Self {
95        Self {
96            code,
97            message: message.into(),
98        }
99    }
100
101    /// Returns the stable failure code.
102    #[must_use]
103    pub const fn code(&self) -> FailureCode {
104        self.code
105    }
106
107    /// Returns the human-readable context without changing its stable code.
108    #[must_use]
109    pub fn message(&self) -> &str {
110        &self.message
111    }
112}
113
114impl fmt::Display for PromotionError {
115    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(formatter, "{}: {}", self.code, self.message)
117    }
118}
119
120impl std::error::Error for PromotionError {}
121
122/// Result type used throughout the promoter core.
123pub type PromotionResult<T> = std::result::Result<T, PromotionError>;