Skip to main content

kernel/
delegation.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! Delegation: two delegate kinds, one level deep.
7//! The guard is two-layered on purpose: statically, a `Delegate` value
8//! has no delegate method — minting a grandchild cannot be spelled;
9//! dynamically, `admit` re-checks depth at every spawn so a hand-rolled
10//! path cannot sneak past the type. One level is an invariant, not a
11//! constant — a constant would imply it is tunable.
12//! The part `crates/kernel/spec/Delegation.lean` specifies this module.
13
14use serde::{Deserialize, Serialize};
15
16/// Resident delegates join society and answer for output; Ephemerals are
17/// tools — one clarification channel up, nothing else.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum DelegateKind {
21    Resident,
22    Ephemeral,
23}
24
25impl DelegateKind {
26    /// The one spelling. Both the tool that parses these words and the
27    /// status line that prints them read it here, so a rename cannot
28    /// half-happen.
29    #[must_use]
30    pub fn as_str(self) -> &'static str {
31        match self {
32            DelegateKind::Resident => "resident",
33            DelegateKind::Ephemeral => "ephemeral",
34        }
35    }
36}
37
38/// The depth-zero position: the only type with a `delegate` method
39/// (15.3-10). Who holds a `Delegator` is assembly's discipline; what a
40/// `Delegate` value can mint is this module's — nothing.
41#[derive(Debug)]
42pub struct Delegator(());
43
44impl Delegator {
45    /// Minting point: assembly and citysim only.
46    pub fn root() -> Delegator {
47        Delegator(())
48    }
49
50    pub fn delegate(&self, kind: DelegateKind) -> Delegate {
51        Delegate { kind }
52    }
53}
54
55/// A delegated position. No delegate method — the compile error is the
56/// design (trybuild counterexample).
57#[derive(Debug)]
58pub struct Delegate {
59    kind: DelegateKind,
60}
61
62impl Delegate {
63    pub fn kind(&self) -> &DelegateKind {
64        &self.kind
65    }
66}
67
68/// Where a spawner stands.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum Depth {
71    Root,
72    Delegated,
73}
74
75/// Deliberately exhaustive verdict; the refusal (E_DELEGATION_DEPTH,
76/// three parts) is shaped by gate::spawn — the sole gate-code producer.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum DelegationVerdict {
79    Allow,
80    Deny,
81}
82
83/// The dynamic half of the two-layer guard: a delegated position spawns
84/// nothing, whatever kind it asks for.
85pub fn admit(parent: Depth, kind: &DelegateKind) -> DelegationVerdict {
86    match parent {
87        Depth::Root => {
88            // Exhaustive on purpose: a new kind must decide its depth rule.
89            match kind {
90                DelegateKind::Resident | DelegateKind::Ephemeral => DelegationVerdict::Allow,
91            }
92        }
93        Depth::Delegated => DelegationVerdict::Deny,
94    }
95}
96
97#[cfg(test)]
98#[allow(
99    clippy::unwrap_used,
100    clippy::expect_used,
101    clippy::panic,
102    clippy::indexing_slicing,
103    reason = "test code"
104)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn root_spawns_both_kinds_delegated_spawns_none() {
110        assert_eq!(
111            admit(Depth::Root, &DelegateKind::Resident),
112            DelegationVerdict::Allow
113        );
114        assert_eq!(
115            admit(Depth::Root, &DelegateKind::Ephemeral),
116            DelegationVerdict::Allow
117        );
118        assert_eq!(
119            admit(Depth::Delegated, &DelegateKind::Resident),
120            DelegationVerdict::Deny
121        );
122        assert_eq!(
123            admit(Depth::Delegated, &DelegateKind::Ephemeral),
124            DelegationVerdict::Deny
125        );
126    }
127
128    #[test]
129    fn the_static_path_mints_one_level_only() {
130        let root = Delegator::root();
131        let child = root.delegate(DelegateKind::Ephemeral);
132        assert_eq!(child.kind(), &DelegateKind::Ephemeral);
133        // `child.delegate(...)` does not compile — pinned by the
134        // trybuild case; this test documents the runtime-visible half.
135    }
136}