Skip to main content

mur_common/
commander.rs

1use serde::{Deserialize, Serialize};
2
3/// Key under which a governance directive is placed in a `ChannelEvent.payload`.
4pub const COMMANDER_DIRECTIVE_KEY: &str = "commander_directive";
5
6/// A cryptographically-signed instruction from the Commander governance plane.
7///
8/// Serialised as a JSON object and placed under [`COMMANDER_DIRECTIVE_KEY`]
9/// in a `ChannelEvent.payload` appended to the `fleet-<name>` channel. The
10/// signing layer lives in `mur-core`; this crate only owns the payload shape.
11/// Cross-network A2A-envelope delivery is Phase 2.
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct CommanderDirective {
14    /// Directive kind, e.g. `"kill"`, `"budget_ceiling"`.
15    pub kind: String,
16
17    /// Target fleet name.
18    pub fleet: String,
19
20    /// Optional budget ceiling in USD; `>=0` is honoured, `<0`/`NaN` ignored.
21    pub budget_usd: Option<f64>,
22
23    /// Replay-prevention nonce (UUID v4 recommended).
24    pub nonce: String,
25
26    /// Wall-clock milliseconds since Unix epoch when the directive was issued.
27    pub issued_at_ms: u64,
28}
29
30/// Derived governance state for a running fleet loop.
31///
32/// Built by reducing the stream of [`CommanderDirective`]s received so far.
33#[derive(Debug, Clone, Default, PartialEq)]
34pub struct GovernanceState {
35    /// If `true`, the loop must stop immediately.
36    pub killed: bool,
37
38    /// Active budget ceiling (USD), if any has been applied.
39    pub budget_ceiling: Option<f64>,
40
41    /// Nonce of the directive that produced `killed == true` (audit binding).
42    /// `None` when not killed, or after a resume clears the kill.
43    pub kill_nonce: Option<String>,
44
45    /// Nonce of the directive that set `budget_ceiling` (audit binding).
46    pub budget_nonce: Option<String>,
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn directive_roundtrips_under_the_marker_key() {
55        let d = CommanderDirective {
56            kind: "kill".into(),
57            fleet: "dev".into(),
58            budget_usd: None,
59            nonce: "n1".into(),
60            issued_at_ms: 1_750_000_000_000,
61        };
62        let wrapped = serde_json::json!({ COMMANDER_DIRECTIVE_KEY: &d });
63        let got: CommanderDirective =
64            serde_json::from_value(wrapped[COMMANDER_DIRECTIVE_KEY].clone()).unwrap();
65        assert_eq!(got.kind, "kill");
66        assert_eq!(got.fleet, "dev");
67        assert_eq!(got.issued_at_ms, 1_750_000_000_000);
68    }
69
70    #[test]
71    fn governance_state_default_is_inert() {
72        let g = GovernanceState::default();
73        assert!(!g.killed && g.budget_ceiling.is_none());
74    }
75}