nixfleet_state_machine/error.rs
1//! Reducer error path. Distinct from runtime errors — these mean the input
2//! event is structurally inapplicable to the current state, which is a
3//! runtime invariant violation (out-of-order event, stale seq, etc.). The
4//! runtime layer decides whether to log + drop, request replay, or panic.
5
6use thiserror::Error;
7
8use crate::state::{HostState, RolloutId};
9
10#[derive(Debug, Error, Clone, PartialEq)]
11pub enum TransitionError {
12 /// The event is not legal from the current `HostState`. The runtime
13 /// layer should typically log + drop (lost-ordering noise) and rely on
14 /// heartbeat drift-detection to recover via Replay-From (RFC-0005 §4.3).
15 #[error("event {event} not legal from state {from:?} (rollout {rollout_id}, host {hostname})")]
16 IllegalForState {
17 from: HostState,
18 event: &'static str,
19 rollout_id: RolloutId,
20 hostname: String,
21 },
22
23 /// Event `seq` is not strictly greater than `last_event_seq`. Could be
24 /// a retransmit (idempotent, runtime dedupes on `(host, rollout, seq)`)
25 /// or out-of-order arrival.
26 #[error(
27 "event seq {got} is not > last_event_seq {last} (rollout {rollout_id}, host {hostname})"
28 )]
29 SeqRegression {
30 got: u64,
31 last: u64,
32 rollout_id: RolloutId,
33 hostname: String,
34 },
35
36 /// Invariant from RFC-0005 §3 violated by the event's payload (e.g.
37 /// `Converged` claimed but `current != target`). CP rejects the event
38 /// with `409 Conflict`; agent retries after re-verifying.
39 #[error("invariant violation: {0}")]
40 Invariant(&'static str),
41
42 /// Reducer arm not yet implemented. Returned by the Phase 3a skeleton;
43 /// every variant gets a real arm in Phase 3b. Should never be reached
44 /// after Phase 3 closes — if it is, that's a code defect.
45 #[error("transition not yet implemented for event {event} from state {from:?}")]
46 Unimplemented {
47 from: HostState,
48 event: &'static str,
49 },
50}