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`. Returned by [`crate::node::Node::run_bootstrap`].
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum BootstrapError {
17 /// `run_bootstrap(&[BootstrapInput])` named a target the engine
18 /// has no bootstrap registration for. Carries the queue snapshot
19 /// so callers can present the legal set.
20 UnknownTarget {
21 /// Target name the caller supplied.
22 target_name: String,
23 /// Target names currently registered as Module bootstrap
24 /// targets, in install order.
25 available: Vec<String>,
26 },
27
28 /// `run_bootstrap(&[BootstrapInput])` named an input the target
29 /// does not declare as a formal. Carries the declared set so
30 /// callers can correct the request.
31 UnknownInput {
32 /// Target name the request targeted.
33 target_name: String,
34 /// Input name the caller supplied.
35 input_name: String,
36 /// Input names declared as formals on the target.
37 declared: Vec<String>,
38 },
39
40 /// `run_bootstrap(&[BootstrapInput])` is missing a required
41 /// formal input. Validation fails atomically — no inputs stage
42 /// when this fires.
43 MissingInput {
44 /// Target name the request targeted.
45 target_name: String,
46 /// Input name declared as a formal but not supplied.
47 input_name: String,
48 },
49
50 /// Bootstrap input staging hit the engine's `ingress_byte_budget`
51 /// cap or the `try_reserve_exact` seam returned `TryReserveError`.
52 /// Carries the offending input's byte count + the remaining
53 /// budget at the point of rejection so the host can decide
54 /// whether to back off, shrink the payload, or raise the cap.
55 /// Any per-input charges that landed earlier in the same
56 /// request are released before the engine surfaces this error —
57 /// the bootstrap state stays untouched.
58 AllocationFailed {
59 /// Target name the request targeted.
60 target_name: String,
61 /// Bytes the staging step tried to admit when the cap or
62 /// allocator rejected the request.
63 byte_count: usize,
64 /// Bytes still available under `ingress_byte_budget` at the
65 /// point of the rejection.
66 budget_remaining: usize,
67 },
68}
69
70impl std::fmt::Display for BootstrapError {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 match self {
73 Self::UnknownTarget {
74 target_name,
75 available,
76 } => write!(
77 f,
78 "unknown bootstrap target '{target_name}'; available: {available:?}",
79 ),
80 Self::UnknownInput {
81 target_name,
82 input_name,
83 declared,
84 } => write!(
85 f,
86 "target '{target_name}' has no input '{input_name}'; declared: {declared:?}",
87 ),
88 Self::MissingInput {
89 target_name,
90 input_name,
91 } => write!(
92 f,
93 "target '{target_name}' missing required input '{input_name}'",
94 ),
95 Self::AllocationFailed {
96 target_name,
97 byte_count,
98 budget_remaining,
99 } => write!(
100 f,
101 "target '{target_name}' input staging refused {byte_count}B (budget remaining {budget_remaining}B)",
102 ),
103 }
104 }
105}
106
107impl std::error::Error for BootstrapError {}
108