1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Typed failures for every host boot, serve, and shutdown path.
use std::net::SocketAddr;
use std::path::PathBuf;
use frame_core::capability::CapabilityMutationError;
use frame_core::component::ComponentId;
use frame_core::error::RegistryError;
use frame_core::event::LifecycleState;
use thiserror::Error;
/// A typed failure from the demo host.
#[derive(Debug, Error)]
pub enum HostError {
/// Scheduler composition (BIF population + construction) failed.
#[error("scheduler composition failed")]
Composition {
/// Exact typed composition refusal.
#[source]
source: frame_core::composition::SchedulerCompositionError,
},
/// The component registry refused or failed an operation.
#[error("component registry operation failed")]
Registry(#[from] RegistryError),
/// The host capability facade refused an operation.
#[error("host capability operation failed")]
Capability(#[from] CapabilityMutationError),
/// The demo component vanished from the registry between operations.
#[error("component {id} has no status snapshot in the registry")]
StatusMissing {
/// Identity whose snapshot was absent.
id: ComponentId,
},
/// The demo component did not reach Running after start.
#[error("component {id} is {state:?} after start instead of Running")]
NotRunning {
/// Identity that failed to reach Running.
id: ComponentId,
/// Observed lifecycle state.
state: LifecycleState,
},
/// The presence child answered its liveness probe with the wrong value.
#[error("presence child answered liveness probe with {actual}, expected {expected}")]
ProbeMismatch {
/// Value the fresh incarnation must answer.
expected: i64,
/// Value actually observed.
actual: i64,
},
/// The lifecycle event logger thread could not be spawned.
#[error("failed to spawn lifecycle event logger thread: {source}")]
EventLoggerSpawn {
/// Underlying spawn failure.
#[source]
source: std::io::Error,
},
/// The lifecycle event logger thread panicked.
#[error("lifecycle event logger thread panicked")]
EventLoggerPanicked,
/// The asset directory is unusable as a shell root.
#[error("asset directory {path} is unusable: {detail}")]
AssetRoot {
/// Configured asset directory.
path: PathBuf,
/// Exact refusal detail.
detail: String,
},
/// The served config would be refused by the shell's config contract.
#[error("shell config contract violation: {detail}")]
ConfigContract {
/// Exact contract refusal and the flag that fixes it.
detail: String,
},
/// The asset directory has no `index.html` shell entry point.
#[error(
"asset directory {path} has no index.html; refusing to serve a shell with no entry point"
)]
MissingIndex {
/// Configured asset directory.
path: PathBuf,
},
/// Binding the shell listener failed.
#[error("failed to bind shell server on {addr}: {source}")]
Bind {
/// Requested socket address.
addr: SocketAddr,
/// Underlying bind failure.
#[source]
source: std::io::Error,
},
/// The shell server failed while serving.
#[error("shell server failed: {source}")]
Serve {
/// Underlying accept/serve failure.
#[source]
source: std::io::Error,
},
/// The async runtime hosting the shell server could not be built.
#[error("failed to build tokio runtime for the shell server: {source}")]
AsyncRuntime {
/// Underlying builder failure.
#[source]
source: std::io::Error,
},
/// The shutdown signal handler could not be installed or failed.
#[error("shutdown signal handling failed: {source}")]
ShutdownSignal {
/// Underlying signal failure.
#[source]
source: std::io::Error,
},
/// Ordered shutdown left live processes on the scheduler.
#[error("ordered stop leaked {count} live scheduler process(es)")]
ProcessResidue {
/// Processes still alive after the ordered drain.
count: usize,
},
/// Host-internal synchronization was poisoned by a panic.
#[error("host synchronization is poisoned")]
SynchronizationPoisoned,
/// The frame configuration file could not be read.
#[error("failed to read frame config {path}: {source}")]
ConfigRead {
/// Configured frame.toml path.
path: PathBuf,
/// Underlying read failure.
#[source]
source: std::io::Error,
},
/// The frame configuration file could not be parsed as TOML into the
/// `[frame]` + `[liminal]` schema.
#[error("failed to parse frame config {path}: {detail}")]
ConfigParse {
/// Configured frame.toml path.
path: PathBuf,
/// Exact TOML/deserialization refusal.
detail: String,
},
/// The embedded `[liminal]` config failed liminal's own validation.
#[error("embedded liminal config is invalid: {source}")]
LiminalConfig {
/// Exact typed liminal validation failure.
#[source]
source: liminal_server::ServerError,
},
/// The `[liminal]` config selects a shape the embedded frame server does
/// not faithfully orchestrate (cluster, worker-front-door, or an absent
/// WebSocket transport). Loud refusal at startup, never a silent downgrade.
#[error("embedded frame mode does not support this liminal shape: {detail}")]
EmbeddedModeUnsupported {
/// Which shape was refused and why.
detail: String,
},
/// A liminal component failed to bind or boot. Frame-host exits nonzero
/// with the component named — never a half-up stack.
#[error("embedded liminal component '{component}' failed to boot: {source}")]
LiminalComponent {
/// Which liminal component failed (health endpoint, connection
/// services, connection supervisor, TCP listener, WebSocket listener).
component: &'static str,
/// Exact typed liminal failure.
#[source]
source: liminal_server::ServerError,
},
/// Liminal's graceful shutdown sequence failed.
#[error("embedded liminal graceful shutdown failed: {source}")]
LiminalShutdown {
/// Exact typed liminal shutdown failure.
#[source]
source: liminal_server::ServerError,
},
/// The embedded liminal component stopped answering at runtime while the
/// host was still meant to be serving. A dead server behind a healthy host
/// is forbidden: frame-host tears down and exits nonzero.
#[error("embedded liminal component terminated unexpectedly at runtime: {detail}")]
LiminalExited {
/// Which liveness probe failed and against which address.
detail: String,
},
}