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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
//! Errors produced by runtime planning, scheduling, and rendering.
use std::io;
use thiserror::Error;
use crate::configuration::ConfigurationError;
/// Failure while planning, executing, or displaying a workflow runtime.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RuntimeError {
/// A selected phase failed after producing a structured runtime snapshot.
#[error("workflow phase execution failed: {source}")]
PhaseExecutionFailed {
/// All phase outcomes observed through the failed phase.
summary: super::RuntimeSummary,
/// Exact scheduling, cancellation, panic, or workload cause.
#[source]
source: Box<RuntimeError>,
},
/// Project configuration could not supply a required task or identity value.
#[error(transparent)]
Configuration(#[from] ConfigurationError),
/// Another live reporter already owns process terminal rendering.
#[error("another progress reporter already owns the process terminal")]
TerminalAlreadyOwned,
/// The validated task count cannot be represented by this platform.
#[error("task count {task_count} exceeds this platform's addressable progress slots")]
TaskCountTooLarge {
/// Validated project task count.
task_count: u64,
},
/// One phase contains no tasks.
#[error("phase {phase} must contain at least one task")]
EmptyPhase { phase: u64 },
/// A first-class reporter/runtime plan contains no phase.
#[error("at least one phase is required")]
EmptyPhaseSet,
/// A phase label is empty or whitespace-only.
#[error("phase {phase} must have a nonempty label")]
InvalidPhaseLabel { phase: u64 },
/// A phase active-workload limit is zero.
#[error("phase {phase} max_concurrent_workloads must be greater than zero")]
InvalidPhaseWorkloadLimit { phase: u64 },
/// A phase prepared-work queue capacity is zero.
#[error("phase {phase} queue_capacity must be greater than zero")]
InvalidPhaseQueueCapacity { phase: u64 },
/// A reporter phase list repeats one phase ID.
#[error("phase ID {phase} appears more than once")]
DuplicatePhaseId { phase: u64 },
/// One phase dependency is not registered in the runtime plan.
#[error("phase {phase} depends on unknown phase {dependency}")]
UnknownPhaseDependency { phase: u64, dependency: u64 },
/// The phase dependency graph contains a cycle.
#[error("phase dependency graph contains a cycle involving phase {phase}")]
PhaseDependencyCycle { phase: u64 },
/// A selected phase ID is absent from the runtime plan.
#[error("selected phase {phase} is not registered")]
UnknownSelectedPhase { phase: u64 },
/// Exact selection omitted a dependency that was not externally verified.
#[error("selected phase {phase} requires unsatisfied phase {dependency}")]
UnsatisfiedPhaseDependency { phase: u64, dependency: u64 },
/// Standard input ended before a required phase transition was confirmed.
#[error("confirmation input ended after phase {phase} before the next phase could start")]
PhaseConfirmationEof { phase: u64 },
/// A required phase-transition confirmation could not read standard input.
#[error("failed to read confirmation after phase {phase}")]
PhaseConfirmationInput {
/// Successfully completed phase awaiting permission to advance.
phase: u64,
/// Underlying standard-input or prompt-output failure.
#[source]
source: io::Error,
},
/// One declared task has no executable workload.
#[error("task `{task}` has no workload")]
MissingTaskWorkload { task: String },
/// A task-owned workload returned an error.
#[error("task `{task}` failed: {source}")]
TaskWorkload {
task: String,
#[source]
source: Box<dyn std::error::Error + Send + Sync + 'static>,
},
/// A scheduler worker panicked.
#[error("a runtime scheduler worker panicked")]
SchedulerPanicked,
/// Cooperative cancellation stopped the selected runtime plan.
#[error("workflow runtime was cancelled")]
Cancelled,
/// One task has an empty phase-local ID.
#[error("phase {phase} contains an empty task ID")]
InvalidManagedTaskId { phase: u64 },
/// One task has an empty kind/namespace.
#[error("task `{task}` must have a nonempty kind")]
InvalidManagedTaskKind { task: String },
/// One phase repeats the same phase-local task ID.
#[error("phase {phase} repeats task ID `{task}`")]
DuplicateManagedTaskId { phase: u64, task: String },
/// Tasks of one kind do not expose one consistent parameter-key set.
#[error("task kind `{kind}` has inconsistent parameter keys between `{first}` and `{second}`")]
InconsistentManagedTaskParameters {
kind: String,
first: String,
second: String,
},
/// A display projection names a task kind absent from the phase.
#[error("task kind `{kind}` is not declared by the phase")]
UnknownManagedTaskKind { kind: String },
/// Two tasks receive the same requested generated label.
#[error("generated task label `{label}` collides between `{first}` and `{second}`")]
ManagedTaskDisplayCollision {
label: String,
first: String,
second: String,
},
/// A partial selector matched no managed task.
#[error("task selector `{selector}` matched no task")]
ManagedTaskNotFound { selector: String },
/// A partial selector matched more than one managed task.
#[error("task selector `{selector}` is ambiguous between `{first}` and `{second}`")]
ManagedTaskSelectorAmbiguous {
selector: String,
first: String,
second: String,
},
/// A managed task does not contain one required parameter.
#[error("task `{task}` does not contain parameter `{key}`")]
UnknownManagedTaskParameter { task: String, key: String },
/// One managed task parameter could not be decoded.
#[error("task `{task}` parameter `{key}` could not be decoded")]
DecodeManagedTaskParameter {
task: String,
key: String,
#[source]
source: serde_json::Error,
},
/// An explicit parameter key is empty.
#[error("task parameter key `{key}` is invalid")]
InvalidTaskParameter { key: String },
/// Configuration-derived parameters cannot be mutated.
#[error("configuration-derived task `{task}` has immutable parameters")]
ConfiguredTaskParametersImmutable { task: String },
/// The reporter does not contain one exact first-class task key.
#[error("managed task `{task}` does not exist in this reporter")]
UnknownManagedTask { task: String },
/// A requested handle does not match the task's declared display kind.
#[error("task `{task}` is declared as {actual}, not {requested}")]
ManagedTaskKindMismatch {
task: String,
requested: &'static str,
actual: &'static str,
},
/// One identity key was supplied more than once.
#[error("task identity repeats parameter key `{key}`")]
DuplicateIdentityParameter {
/// Repeated exact parameter name.
key: String,
},
/// One requested identity key is absent from project parameters.
#[error("task identity parameter `{key}` is not declared by the project")]
UnknownIdentityParameter {
/// Missing exact parameter name.
key: String,
},
/// Two generated tasks have the same selected parameter identity.
#[error(
"task identity `{identity}` is shared by ordinals {first_ordinal} and {second_ordinal}"
)]
NonUniqueTaskIdentity {
/// Deterministic rendered parameter identity.
identity: String,
/// First colliding automatically assigned ordinal.
first_ordinal: u64,
/// Second colliding automatically assigned ordinal.
second_ordinal: u64,
},
/// A task handle does not belong to the reporter's configured task space.
#[error("task ordinal {task_ordinal} is outside the reporter's task registry")]
UnknownTaskOrdinal {
/// Automatically assigned ordinal obtained from `TaskConfig`.
task_ordinal: u64,
},
/// A directly registered application task name was not found.
#[error("registered task `{identity}` does not exist")]
UnknownRegisteredTask { identity: String },
/// The same directly registered application task name appeared twice.
#[error("registered task `{identity}` appears more than once")]
DuplicateRegisteredTask { identity: String },
/// A task handle's selected identity differs from the registered project.
#[error("task ordinal {task_ordinal} does not match its registered parameter identity")]
TaskIdentityMismatch {
/// Automatically assigned task ordinal.
task_ordinal: u64,
},
/// The same task was started more than once.
#[error("task `{identity}` has already started or reached a terminal status")]
TaskAlreadyStarted {
/// Human-readable parameter identity.
identity: String,
},
/// Initial progress lies beyond a known target.
#[error("task `{identity}` starts at iteration {initial}, beyond target {target}")]
InitialIterationBeyondTarget {
/// Human-readable parameter identity.
identity: String,
/// Initial absolute simulation iteration.
initial: u64,
/// Target absolute simulation iteration.
target: u64,
},
/// A progress update attempted to move scientific iteration backward.
#[error("task `{identity}` cannot move progress from iteration {current} back to {attempted}")]
IterationRegressed {
/// Human-readable parameter identity.
identity: String,
/// Previously reported iteration.
current: u64,
/// Rejected iteration.
attempted: u64,
},
/// A progress update exceeded a known target.
#[error("task `{identity}` reported iteration {iteration}, beyond target {target}")]
IterationBeyondTarget {
/// Human-readable parameter identity.
identity: String,
/// Rejected absolute simulation iteration.
iteration: u64,
/// Configured absolute target iteration.
target: u64,
},
/// Completion was requested before a known target was reached.
#[error("task `{identity}` completed at iteration {current}, before target {target}")]
TargetIterationNotReached {
/// Human-readable parameter identity.
identity: String,
/// Last reported absolute simulation iteration.
current: u64,
/// Configured absolute target iteration.
target: u64,
},
/// The sole renderer thread could not be created.
#[error("failed to start the centralized terminal reporter")]
StartRenderer {
/// Underlying thread-creation failure.
#[source]
source: io::Error,
},
/// Interactive terminal isolation could not be established.
#[error("failed to {operation} for the isolated progress screen")]
TerminalSetup {
operation: &'static str,
#[source]
source: io::Error,
},
/// The renderer stopped before accepting a requested message.
#[error("the centralized terminal reporter is no longer available")]
RendererUnavailable,
/// The renderer thread panicked while the reporter was active.
#[error("the centralized terminal reporter panicked")]
RendererPanicked,
/// Successful finalization was requested before every task completed.
#[error(
"cannot report success with {pending} pending, {running} running, and {failed} failed tasks"
)]
IncompleteProgress {
/// Tasks that never started.
pending: u64,
/// Tasks that have not reached a terminal status.
running: u64,
/// Tasks that failed or dropped before completion.
failed: u64,
},
}
impl RuntimeError {
/// Returns structured outcomes when execution reached a failing phase.
pub fn runtime_summary(&self) -> Option<&super::RuntimeSummary> {
match self {
Self::PhaseExecutionFailed { summary, .. } => Some(summary),
_ => None,
}
}
/// Returns the underlying execution cause when a phase failed.
pub fn execution_cause(&self) -> Option<&RuntimeError> {
match self {
Self::PhaseExecutionFailed { source, .. } => Some(source),
_ => None,
}
}
}
pub(crate) use RuntimeError as ReportingError;