Skip to main content

bb_runtime/errors/
bootstrap.rs

1//! `BootstrapError`
2//!
3//! Returned by [`crate::node::Node::run_bootstrap`] when input
4//! staging or target selection violates the contract per
5//! `docs/internal/superpowers/specs/2026-06-25-host-driven-bootstrap.md`
6//! §3.2.
7//!
8//! Each variant carries every datum a host needs to recover (the
9//! offending name plus the declared set, the unknown slot id plus
10//! the available ids, …) so callers can surface human-readable
11//! prompts without re-introspecting the engine.
12
13/// Errors surfaced by host-facing bootstrap staging methods on
14/// `Node`. F3 lands the Node API + validation logic; this commit
15/// defines the error taxonomy + display only.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum BootstrapError {
18    /// `run_bootstrap(BootstrapTarget::ModuleRequests|ModuleNames|Slots)`
19    /// named a target the engine has no bootstrap registration for.
20    /// Carries the queue snapshot so callers can present the legal set.
21    UnknownTarget {
22        /// Target name the caller supplied.
23        target_name: String,
24        /// Target names currently queued, in install order.
25        available: Vec<String>,
26    },
27
28    /// `run_bootstrap(BootstrapTarget::Slots(...))` named a slot id
29    /// that does not exist on the target's input site map. Carries
30    /// the legal ids so callers can correct the mapping.
31    UnknownSlot {
32        /// Slot id the caller supplied.
33        slot_id: u32,
34        /// Slot ids declared on the target's input site map.
35        available: Vec<u32>,
36    },
37
38    /// The host called `run_bootstrap` with a target whose inputs are
39    /// already staged + queued. The host must drive the pending
40    /// request through to completion (or cancel it) before
41    /// re-targeting the same name.
42    AlreadyTransitivelyQueued {
43        /// Target name that was already queued.
44        target_name: String,
45    },
46
47    /// `run_bootstrap(BootstrapTarget::ModuleRequests(...))` named an
48    /// input the target does not declare as a formal. Carries the
49    /// declared set so callers can correct the request.
50    UnknownInput {
51        /// Target name the request targeted.
52        target_name: String,
53        /// Input name the caller supplied.
54        input_name: String,
55        /// Input names declared as formals on the target.
56        declared: Vec<String>,
57    },
58
59    /// `run_bootstrap(BootstrapTarget::ModuleRequests(...))` is
60    /// missing a required formal input. Validation fails atomically —
61    /// no inputs stage when this fires.
62    MissingInput {
63        /// Target name the request targeted.
64        target_name: String,
65        /// Input name declared as a formal but not supplied.
66        input_name: String,
67    },
68
69    /// Bootstrap input staging hit the engine's `ingress_byte_budget`
70    /// cap or the `try_reserve_exact` seam returned `TryReserveError`.
71    /// Carries the offending input's byte count + the remaining
72    /// budget at the point of rejection so the host can decide
73    /// whether to back off, shrink the payload, or raise the cap.
74    /// Any per-input charges that landed earlier in the same
75    /// request are released before the engine surfaces this error —
76    /// the bootstrap state stays untouched.
77    AllocationFailed {
78        /// Target name the request targeted.
79        target_name: String,
80        /// Bytes the staging step tried to admit when the cap or
81        /// allocator rejected the request.
82        byte_count: usize,
83        /// Bytes still available under `ingress_byte_budget` at the
84        /// point of the rejection.
85        budget_remaining: usize,
86    },
87}
88
89impl std::fmt::Display for BootstrapError {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Self::UnknownTarget {
93                target_name,
94                available,
95            } => write!(
96                f,
97                "unknown bootstrap target '{target_name}'; available: {available:?}",
98            ),
99            Self::UnknownSlot { slot_id, available } => write!(
100                f,
101                "unknown bootstrap slot id {slot_id}; available: {available:?}",
102            ),
103            Self::AlreadyTransitivelyQueued { target_name } => write!(
104                f,
105                "bootstrap target '{target_name}' already has a queued input request",
106            ),
107            Self::UnknownInput {
108                target_name,
109                input_name,
110                declared,
111            } => write!(
112                f,
113                "target '{target_name}' has no input '{input_name}'; declared: {declared:?}",
114            ),
115            Self::MissingInput {
116                target_name,
117                input_name,
118            } => write!(
119                f,
120                "target '{target_name}' missing required input '{input_name}'",
121            ),
122            Self::AllocationFailed {
123                target_name,
124                byte_count,
125                budget_remaining,
126            } => write!(
127                f,
128                "target '{target_name}' input staging refused {byte_count}B (budget remaining {budget_remaining}B)",
129            ),
130        }
131    }
132}
133
134impl std::error::Error for BootstrapError {}
135