Skip to main content

sim_lib_agent_runner_core/
ceiling.rs

1use sim_kernel::{CapabilityName, CapabilitySet, Result, Symbol, diminish};
2
3/// Resolves capability symbols and intersects them with the active set.
4pub fn effective_ceiling(current: &CapabilitySet, names: &[Symbol]) -> Result<CapabilitySet> {
5    let allowed = names
6        .iter()
7        .map(capability_name)
8        .fold(CapabilitySet::new(), CapabilitySet::grant);
9    Ok(diminish(current, &allowed))
10}
11
12fn capability_name(symbol: &Symbol) -> CapabilityName {
13    match symbol.namespace.as_deref() {
14        Some("capability") => CapabilityName::new(symbol.name.to_string()),
15        _ => CapabilityName::new(symbol.as_qualified_str()),
16    }
17}
18
19#[cfg(test)]
20mod tests {
21    use super::effective_ceiling;
22    use sim_kernel::{CapabilityName, CapabilitySet, Symbol};
23
24    #[test]
25    fn effective_ceiling_never_grows() {
26        let ai_run = CapabilityName::new("ai/run");
27        let fs_read = CapabilityName::new("fs/read");
28        let current = CapabilitySet::new()
29            .grant(ai_run.clone())
30            .grant(fs_read.clone());
31
32        let narrowed = effective_ceiling(
33            &current,
34            &[
35                Symbol::qualified("ai", "run"),
36                Symbol::qualified("net", "write"),
37            ],
38        )
39        .unwrap();
40
41        assert!(narrowed.contains(&ai_run));
42        assert!(!narrowed.contains(&fs_read));
43        assert!(!narrowed.contains(&CapabilityName::new("net/write")));
44    }
45
46    #[test]
47    fn capability_namespace_resolves_to_bare_name() {
48        let read_eval = CapabilityName::new("read-eval");
49        let current = CapabilitySet::new().grant(read_eval.clone());
50
51        let narrowed =
52            effective_ceiling(&current, &[Symbol::qualified("capability", "read-eval")]).unwrap();
53
54        assert!(narrowed.contains(&read_eval));
55    }
56}