Skip to main content

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 safety iteration cap was hit, or the instance already
7//! existed. Adapter-shaped aborts (persistence/transport errors) are not
8//! outcomes — they surface as errors, not as a recorded end-state.
9
10/// How a ceremony run terminated.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum CeremonyOutcome {
13    /// Reached a terminal state — the ceremony finished.
14    Completed,
15    /// A step did not complete successfully and aborted the run.
16    StepFailed,
17    /// No transition out of the current state was satisfiable (a guard
18    /// deadlock or a missing event).
19    NoTransition,
20    /// The transition safety cap was hit without reaching a terminal
21    /// state.
22    IterationLimit,
23    /// An instance with the same id already existed; the run was rejected.
24    AlreadyExists,
25}
26
27impl CeremonyOutcome {
28    /// Stable, low-cardinality label value for metrics exposition. Part of
29    /// the metric contract; dashboards and alerts match on it.
30    #[must_use]
31    pub const fn as_label(self) -> &'static str {
32        match self {
33            Self::Completed => "completed",
34            Self::StepFailed => "step_failed",
35            Self::NoTransition => "no_transition",
36            Self::IterationLimit => "iteration_limit",
37            Self::AlreadyExists => "already_exists",
38        }
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn labels_are_distinct_and_stable() {
48        let all = [
49            CeremonyOutcome::Completed,
50            CeremonyOutcome::StepFailed,
51            CeremonyOutcome::NoTransition,
52            CeremonyOutcome::IterationLimit,
53            CeremonyOutcome::AlreadyExists,
54        ];
55        let labels: std::collections::BTreeSet<&str> =
56            all.iter().map(|outcome| outcome.as_label()).collect();
57        assert_eq!(labels.len(), all.len());
58    }
59}