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