grant manage_root_agent by stored permission only, not by the name ruth
Permission::default_for and hive-sh4re's MANAGER_AGENT are gone: every agent with no permissions key holds Permission::DEFAULT, ruth included. The agent-name parameters that only fed default_for go with it (permissions::effective, HiveCopy::before_read/after_read and their callers). swarm-controller seeds the grant ruth held by name: at start, a declared ruth whose permissions are unset gets DEFAULT plus manage_root_agent written into her key, once, logged. A set key is never touched. It is a one-shot to delete once every swarm has passed it. The swarm UI setup assistant opens the create form with manage_root_agent checked, so a new ruth is created with the grant. permissions_import no longer adds ruth to the import or splits her default. Docs and help text no longer say ruth holds the grant by default; swarmctl-cli.md regenerated.
This commit is contained in:
parent
9258563f9c
commit
7b5400ecb8
26 changed files with 380 additions and 216 deletions
|
|
@ -4,9 +4,6 @@
|
|||
use hive_types::Ident;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Logical name the broker uses for the manager.
|
||||
pub const MANAGER_AGENT: &str = "ruth";
|
||||
|
||||
/// Logical name of the human operator: the sender of operator-authored
|
||||
/// messages. Not a recipient — `send` to it fails with
|
||||
/// [`OPERATOR_SEND_REFUSAL`].
|
||||
|
|
@ -57,7 +54,7 @@ pub enum HelperEvent {
|
|||
|
||||
#[cfg(test)]
|
||||
mod reserved_name_tests {
|
||||
use super::{MANAGER_AGENT, OPERATOR_RECIPIENT, SYSTEM_SENDER};
|
||||
use super::{OPERATOR_RECIPIENT, SYSTEM_SENDER};
|
||||
use hive_types::{Ident, RESERVED_NAMES_ENV, is_reserved_name};
|
||||
|
||||
/// The blacklist as nix rendered it for this test run.
|
||||
|
|
@ -132,7 +129,7 @@ mod reserved_name_tests {
|
|||
fn manager_name_is_taken_not_reserved() {
|
||||
let owned = reserved();
|
||||
let reserved: Vec<&str> = owned.iter().map(String::as_str).collect();
|
||||
assert!(Ident::parse(MANAGER_AGENT).is_ok());
|
||||
assert!(!is_reserved_name(MANAGER_AGENT, &reserved));
|
||||
assert!(Ident::parse("ruth").is_ok());
|
||||
assert!(!is_reserved_name("ruth", &reserved));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! is enforced where it applies: the MCP-tool and built-in-tool members by
|
||||
//! the processes that spawn `claude` (the agent harness and the subagent
|
||||
//! daemon), [`Permission::ManageRootAgent`] by hive-c0re's bind mounts. An
|
||||
//! agent the swarm sets nothing for holds [`Permission::default_for`].
|
||||
//! agent the swarm sets nothing for holds [`Permission::DEFAULT`].
|
||||
//!
|
||||
//! It also resolves the *built-in* claude tool list ([`builtin_tools_for`]):
|
||||
//! that set is permission-gated too (`web_tools`), so it belongs here rather
|
||||
|
|
@ -135,22 +135,10 @@ impl Permission {
|
|||
Self::ManageRootAgent,
|
||||
];
|
||||
|
||||
/// What an agent the swarm sets nothing for holds.
|
||||
/// What an agent the swarm sets nothing for holds, whatever its name.
|
||||
pub const DEFAULT: &'static [Self] =
|
||||
&[Self::Messaging, Self::Meta, Self::Inbox, Self::Execution];
|
||||
|
||||
/// What `agent` holds when the swarm sets nothing for it: [`Self::DEFAULT`],
|
||||
/// plus [`Self::ManageRootAgent`] for the root agent
|
||||
/// ([`crate::manager::MANAGER_AGENT`]), which recovers the others.
|
||||
#[must_use]
|
||||
pub fn default_for(agent: &str) -> Vec<Self> {
|
||||
let mut perms = Self::DEFAULT.to_vec();
|
||||
if agent == crate::manager::MANAGER_AGENT {
|
||||
perms.push(Self::ManageRootAgent);
|
||||
}
|
||||
perms
|
||||
}
|
||||
|
||||
/// The MCP tool names (without the `mcp__hyperhive__` prefix) this
|
||||
/// permission grants. Empty for the permissions that grant something
|
||||
/// else; see each variant's doc comment.
|
||||
|
|
@ -227,17 +215,6 @@ mod tests {
|
|||
assert!(serde_json::from_str::<Permission>("\"lifecycle\"").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_root_agent_defaults_to_managing_the_others() {
|
||||
assert_eq!(
|
||||
Permission::default_for("atlas"),
|
||||
Permission::DEFAULT.to_vec()
|
||||
);
|
||||
let ruth = Permission::default_for(crate::manager::MANAGER_AGENT);
|
||||
assert!(ruth.contains(&Permission::ManageRootAgent));
|
||||
assert_eq!(&ruth[..Permission::DEFAULT.len()], Permission::DEFAULT);
|
||||
}
|
||||
|
||||
/// Adding permissions only ever adds built-ins — the base set is in every
|
||||
/// resolution, and no permission can remove one. Both `--tools` callers (the
|
||||
/// harness and the subagent daemon) rely on this to reason about "the
|
||||
|
|
|
|||
Loading…
Reference in a new issue