nixfleet_state_machine/rollout/
event.rs

1//! Rollout-level event vocabulary (RFC-0008 §4). All CP-internal: the
2//! applier synthesizes these from per-host events (RFC-0005 §4.2) and
3//! channel-refs poll outcomes — agents never emit `RolloutEvent`s on the
4//! wire.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use crate::HostState;
10use crate::rollout::state::{ChannelId, RolloutId};
11use nixfleet_proto::ChannelRef;
12
13pub type HostId = String;
14
15/// Per-host state representation as seen by the rollout reducer.
16///
17/// The rollout reducer reasons about per-host transitions at a coarser
18/// granularity than RFC-0005 §3's six states; this alias names the input
19/// shape it accepts on `HostStateChanged`. Today it carries the full
20/// RFC-0005 state (re-exported from the host reducer); future projection
21/// to a coarser enum is a v0.3 optimisation.
22pub type HostRolloutState = HostState;
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub enum RolloutEvent {
26    /// Channel-refs poll detected a new ref; planner opened the rollout.
27    /// First event in any rollout's lifecycle.
28    RolloutOpened {
29        rollout_id: RolloutId,
30        channel: ChannelId,
31        target_ref: ChannelRef,
32        at: DateTime<Utc>,
33    },
34    /// A host has been dispatched into this rollout (RFC-0005 §4.1
35    /// `Dispatch` queued). First `HostJoined` moves `Opening → Active`.
36    HostJoined {
37        rollout_id: RolloutId,
38        host_id: HostId,
39        wave: u32,
40        at: DateTime<Utc>,
41    },
42    /// Per-host state transitioned (RFC-0005 §3). The rollout reducer
43    /// aggregates these to drive the rollout-level state machine.
44    HostStateChanged {
45        rollout_id: RolloutId,
46        host_id: HostId,
47        from: HostRolloutState,
48        to: HostRolloutState,
49        at: DateTime<Utc>,
50    },
51    /// A wave's hosts all reached Soaked; planner is dispatching the next
52    /// wave. Drives `Converging → Active`.
53    WaveAdvanced {
54        rollout_id: RolloutId,
55        from_wave: u32,
56        to_wave: u32,
57        at: DateTime<Utc>,
58    },
59    /// Aggregate signal: all hosts in all waves Converged. Drives
60    /// `Converging → Terminal`. Emitted by the reducer itself (not by an
61    /// upstream worker) as a consequence of the last per-host `Converged`.
62    RolloutTerminal {
63        rollout_id: RolloutId,
64        at: DateTime<Utc>,
65    },
66    /// Channel-refs poll detected a successor ref. Both rollouts are
67    /// named in one event so the reducer can transition the predecessor
68    /// (→ Superseded) and open the successor in lockstep.
69    SuccessorOpened {
70        superseded_rollout_id: RolloutId,
71        successor_rollout_id: RolloutId,
72        at: DateTime<Utc>,
73    },
74    /// Retention threshold elapsed for a `Terminal | Superseded | Failed |
75    /// Reverted` rollout. Drives the terminal-set → `Pruned` transition.
76    RetentionExpired {
77        rollout_id: RolloutId,
78        at: DateTime<Utc>,
79    },
80    /// Operator-issued state override (currently rare; CLI wiring is a
81    /// future v0.2.x follow-up — RFC-0008 §13 + Phase 10 brief §9.3).
82    OperatorClearance {
83        rollout_id: RolloutId,
84        operator: String,
85        reason: String,
86        at: DateTime<Utc>,
87    },
88}
89
90impl RolloutEvent {
91    /// Static event-kind name for logging / error reporting.
92    pub fn kind(&self) -> &'static str {
93        match self {
94            RolloutEvent::RolloutOpened { .. } => "RolloutOpened",
95            RolloutEvent::HostJoined { .. } => "HostJoined",
96            RolloutEvent::HostStateChanged { .. } => "HostStateChanged",
97            RolloutEvent::WaveAdvanced { .. } => "WaveAdvanced",
98            RolloutEvent::RolloutTerminal { .. } => "RolloutTerminal",
99            RolloutEvent::SuccessorOpened { .. } => "SuccessorOpened",
100            RolloutEvent::RetentionExpired { .. } => "RetentionExpired",
101            RolloutEvent::OperatorClearance { .. } => "OperatorClearance",
102        }
103    }
104}