Skip to main content

kernel/gate/
spawn.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//! The spawn admission: who may hand work down, and how far.
7//! The part `crates/kernel/spec/Gate.lean` specifies this module with the
8//! rest of `kernel::gate`.
9//!
10//! Handing work to a second agent needs no person's answer: the depth
11//! rule bounds it, one level and no further, and a rule the type system
12//! already carries is not a question.
13
14use crate::delegation::{self, DelegateKind, DelegationVerdict, Depth};
15use crate::error::{AxCode, AxError, GateRefusal};
16
17use super::GateOutcome;
18
19/// Delegates do not delegate (10.1). The refusal teaches the
20/// alternative instead of hiding the tool.
21pub fn spawn(parent: Depth, kind: &DelegateKind) -> GateOutcome {
22    match delegation::admit(parent, kind) {
23        DelegationVerdict::Allow => GateOutcome::Allow,
24        DelegationVerdict::Deny => GateOutcome::Deny {
25            refusal: Box::new(
26                AxError::refusal(
27                    AxCode::DelegationDepth,
28                    "spawn delegate",
29                    format!("{kind:?}"),
30                    GateRefusal::new(
31                        "delegates do not delegate: one level deep",
32                        "a delegated position requested a spawn",
33                        "return this subtask to the resident who spawned you; \
34                         that resident can delegate it",
35                    ),
36                )
37                .with_recovery(
38                    "finish this subtask yourself, or end the turn with what you have so \
39                     the resident who spawned you can delegate the rest",
40                ),
41            ),
42        },
43    }
44}
45
46#[cfg(test)]
47#[allow(
48    clippy::unwrap_used,
49    clippy::expect_used,
50    clippy::panic,
51    reason = "test code"
52)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn the_spawn_door_teaches_the_way_back_up() {
58        assert!(matches!(
59            spawn(Depth::Root, &DelegateKind::Ephemeral),
60            GateOutcome::Allow
61        ));
62        let GateOutcome::Deny { refusal } = spawn(Depth::Delegated, &DelegateKind::Ephemeral)
63        else {
64            panic!("a delegate may not delegate")
65        };
66        assert_eq!(refusal.code(), &AxCode::DelegationDepth);
67        let parts = refusal.gate().expect("gate refusals carry three parts");
68        assert!(!parts.rule().is_empty());
69        assert!(!parts.violation().is_empty());
70        assert!(parts.alternative().contains("spawned you"));
71    }
72}