diff --git a/CHANGELOG.md b/CHANGELOG.md index 94d45c486..59645f2b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Credential-free TLS evidence containing canonical origin, TCP peers, reference identity, TLS version, cipher-suite identifier, selected ALPN or explicit absence, leaf certificate and SPKI hashes, server-presented certificate hashes and bounds, trust-bundle identity and hash, validity interval, fixed verification time, revocation configuration, and measured handshake duration. - Credential-free connection and redirect evidence containing canonical addresses, destination classes, target digests, hop numbers, and approved-address counts. - Credential-free verified TCP evidence containing the logical origin, requested socket, observed peer, destination class, successful attempt number, and per-attempt timeout. -- Standard `Display` and `std::error::Error` contracts for destination, redirect, digest, direct-network, and TLS failures, including preserved destination-policy, rustls, and operating-system sources where applicable. +- Standard `Display` and `std::error::Error` contracts for destination, redirect, digest, direct-network, TLS, and resource-budget failures, including preserved destination-policy, rustls, and operating-system sources where applicable. - Real loopback TCP integration proof plus deterministic timeout, refusal, retry, peer-inspection, peer-mismatch, canonicalization, IPv6 metadata, and single-use replay tests. - Real loopback rustls integration covering trusted DNS SAN, Common-Name fallback rejection, wrong-name and untrusted-root rejection, fixed-time expiry and not-yet-valid failures, exact IPv4 and IPv6 SANs, TLS 1.2/TLS 1.3, required and optional ALPN, and transport-origin binding. - Cumulative interactive-first RAM, VRAM, batch, local-model, admission, pause, and compositor-pressure mitigation plans, including active-consumer reduction at exact hard limits. diff --git a/crates/originweave-resource/src/lib.rs b/crates/originweave-resource/src/lib.rs index 8c77aa3d0..35a30789a 100644 --- a/crates/originweave-resource/src/lib.rs +++ b/crates/originweave-resource/src/lib.rs @@ -9,6 +9,8 @@ #![forbid(unsafe_code)] #![deny(missing_docs)] +use std::fmt; + /// A validation error in a resource budget. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BudgetError { @@ -18,6 +20,19 @@ pub enum BudgetError { SoftExceedsHard, } +impl fmt::Display for BudgetError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ZeroLimit => formatter.write_str("resource budget limits must be nonzero"), + Self::SoftExceedsHard => { + formatter.write_str("resource budget soft limits must not exceed hard limits") + } + } + } +} + +impl std::error::Error for BudgetError {} + /// Validated resource limits for one agent task. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ResourceBudget { diff --git a/crates/originweave-resource/tests/error_contract.rs b/crates/originweave-resource/tests/error_contract.rs new file mode 100644 index 000000000..cc8b88dfb --- /dev/null +++ b/crates/originweave-resource/tests/error_contract.rs @@ -0,0 +1,21 @@ +use originweave_resource::BudgetError; +use std::error::Error as _; + +#[test] +fn budget_errors_expose_stable_standard_error_contract() { + let cases = [ + ( + BudgetError::ZeroLimit, + "resource budget limits must be nonzero", + ), + ( + BudgetError::SoftExceedsHard, + "resource budget soft limits must not exceed hard limits", + ), + ]; + + for (error, expected_message) in cases { + assert_eq!(error.to_string(), expected_message); + assert!(error.source().is_none()); + } +}