102 lines
4.0 KiB
Rust
102 lines
4.0 KiB
Rust
//! Access tiers for the anonymous processing nodes spawned by the
|
|
//! hive-mind orchestrator (`app::workflow::hive_mind`).
|
|
//!
|
|
//! Nodes have no persistent identity of their own — the Core Intelligence
|
|
//! addresses each one only by directive and access tier. Since node
|
|
//! designations are system-assigned coordinates rather than named roles,
|
|
//! tool access can't be a lookup table keyed by role name. Instead the
|
|
//! Core Intelligence picks one of these three tiers per node, matched to
|
|
//! what that node's specific directive needs — this keeps the Harness
|
|
//! gate meaningful while the node roster itself stays fully dynamic.
|
|
|
|
/// The three tool-access tiers a hive-mind node can be granted.
|
|
pub mod tool_scope {
|
|
/// Read-only investigation: no file mutation, no shell, no VCS.
|
|
pub const READ: &str = "read";
|
|
/// Read-tier plus file mutation and non-destructive shell (tests/builds).
|
|
pub const WRITE: &str = "write";
|
|
/// Write-tier plus delete, git, and the remaining LSP actions.
|
|
pub const FULL: &str = "full";
|
|
|
|
const READ_TOOLS: &[&str] = &[
|
|
"read", "grep", "glob", "search", "seqthink", "recall",
|
|
"lsp_connect", "lsp_diagnostics", "lsp_hover", "lsp_definition",
|
|
"lsp_references", "read_findings",
|
|
];
|
|
|
|
const WRITE_TOOLS: &[&str] = &[
|
|
"read", "grep", "glob", "search", "seqthink", "recall",
|
|
"lsp_connect", "lsp_diagnostics", "lsp_hover", "lsp_definition",
|
|
"lsp_references", "read_findings",
|
|
"write", "edit", "bash", "todowrite", "todofinish", "remember",
|
|
];
|
|
|
|
const FULL_TOOLS: &[&str] = &[
|
|
"read", "grep", "glob", "search", "seqthink", "recall",
|
|
"lsp_connect", "lsp_diagnostics", "lsp_hover", "lsp_definition",
|
|
"lsp_references", "read_findings",
|
|
"write", "edit", "bash", "todowrite", "todofinish", "remember",
|
|
"delete", "git_operator", "lsp_completion", "lsp_disconnect",
|
|
];
|
|
|
|
/// Resolve a tier name to its concrete tool allowlist.
|
|
///
|
|
/// Unrecognized scope strings fall back to `READ` — the least-privileged
|
|
/// tier — rather than silently granting broader access.
|
|
///
|
|
/// Return: an owned `Vec<String>` suitable for `AgentDefinition::with_allowed_tools`.
|
|
pub fn tools_for(scope: &str) -> Vec<String> {
|
|
let tools: &[&str] = match scope {
|
|
FULL => FULL_TOOLS,
|
|
WRITE => WRITE_TOOLS,
|
|
_ => READ_TOOLS,
|
|
};
|
|
tools.iter().map(|s| (*s).to_string()).collect()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::tool_scope::{tools_for, FULL, READ, WRITE};
|
|
|
|
#[test]
|
|
fn read_tier_excludes_write_tools() {
|
|
let tools = tools_for(READ);
|
|
assert!(!tools.contains(&"write".to_string()));
|
|
assert!(!tools.contains(&"bash".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn write_tier_includes_bash_but_not_delete_or_git() {
|
|
let tools = tools_for(WRITE);
|
|
assert!(tools.contains(&"bash".to_string()));
|
|
assert!(tools.contains(&"write".to_string()));
|
|
assert!(!tools.contains(&"delete".to_string()));
|
|
assert!(!tools.contains(&"git_operator".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn full_tier_includes_delete_and_git() {
|
|
let tools = tools_for(FULL);
|
|
assert!(tools.contains(&"delete".to_string()));
|
|
assert!(tools.contains(&"git_operator".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_scope_falls_back_to_read() {
|
|
let tools = tools_for("bogus");
|
|
assert!(!tools.contains(&"write".to_string()));
|
|
assert!(!tools.contains(&"delete".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn read_tier_is_subset_of_write_tier_and_write_is_subset_of_full() {
|
|
use std::collections::HashSet;
|
|
let read: HashSet<_> = tools_for(READ).into_iter().collect();
|
|
let write: HashSet<_> = tools_for(WRITE).into_iter().collect();
|
|
let full: HashSet<_> = tools_for(FULL).into_iter().collect();
|
|
assert!(read.is_subset(&write), "read tier must be a subset of write tier");
|
|
assert!(write.is_subset(&full), "write tier must be a subset of full tier");
|
|
}
|
|
}
|