made_core/value_objects/ceremony_outcome.rs
1//! [`CeremonyOutcome`] — the terminal result of a ceremony run.
2//!
3//! A ceremony driven to a stop ends in exactly one of these. Unlike a
4//! step's [`StepStatus`](super::StepStatus), this names *why the whole
5//! ceremony stopped*: it completed, a step failed, no transition could
6//! fire, the transition safety cap was hit, a step exhausted its semantic
7//! repeat limit, or the instance already existed. Adapter-shaped aborts
8//! (persistence/transport errors) are not
9//! outcomes — they surface as errors, not as a recorded end-state.
10
11/// How a ceremony run terminated.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum CeremonyOutcome {
14 /// Reached a terminal state — the ceremony finished.
15 Completed,
16 /// A step did not complete successfully and aborted the run.
17 StepFailed,
18 /// No transition out of the current state was satisfiable (a guard
19 /// deadlock or a missing event).
20 NoTransition,
21 /// The transition safety cap was hit without reaching a terminal
22 /// state.
23 IterationLimit,
24 /// A bounded semantic step repeat used every permitted iteration without
25 /// satisfying its declared stop condition.
26 RepeatLimit,
27 /// An instance with the same id already existed; the run was rejected.
28 AlreadyExists,
29}
30
31impl CeremonyOutcome {
32 /// Stable, low-cardinality label value for metrics exposition. Part of
33 /// the metric contract; dashboards and alerts match on it.
34 #[must_use]
35 pub const fn as_label(self) -> &'static str {
36 match self {
37 Self::Completed => "completed",
38 Self::StepFailed => "step_failed",
39 Self::NoTransition => "no_transition",
40 Self::IterationLimit => "iteration_limit",
41 Self::RepeatLimit => "repeat_limit",
42 Self::AlreadyExists => "already_exists",
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn labels_are_distinct_and_stable() {
53 let all = [
54 CeremonyOutcome::Completed,
55 CeremonyOutcome::StepFailed,
56 CeremonyOutcome::NoTransition,
57 CeremonyOutcome::IterationLimit,
58 CeremonyOutcome::RepeatLimit,
59 CeremonyOutcome::AlreadyExists,
60 ];
61 let labels: std::collections::BTreeSet<&str> =
62 all.iter().map(|outcome| outcome.as_label()).collect();
63 assert_eq!(labels.len(), all.len());
64 }
65}