Meliputi: - File-level //! doc comment: tujuan file, alur kerja, komponen utama - Function-level /// doc comment: apa, parameter, return, flow, edge cases - Struct/enum/trait /// doc comment: peran, field docs - Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi - Inline comments untuk variable dan branching logic penting - Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities, zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils - Build: 0 errors, 242/242 tests passed
169 lines
5.5 KiB
Rust
169 lines
5.5 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.
|
|
//!
|
|
//! Tiers (least → most privileged): `read` < `write` < `full`.
|
|
|
|
/// The three tool-access tiers a hive-mind node can be granted.
|
|
pub mod tool_scope {
|
|
use tracing;
|
|
/// 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";
|
|
|
|
/// The read-only tool set — reused by `context::dedup` as the
|
|
/// authoritative "safe to deduplicate" classification, so there's a
|
|
/// single list of read-only tool names in the codebase instead of two.
|
|
pub 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.
|
|
///
|
|
/// Flow: match `scope` against the three known constants → return the
|
|
/// corresponding static slice → collect into owned `Vec<String>`.
|
|
///
|
|
/// Return: an owned `Vec<String>` suitable for `AgentDefinition::with_allowed_tools`.
|
|
pub fn tools_for(scope: &str) -> Vec<String> {
|
|
// Select the tool list matching the requested access tier.
|
|
// Unknown scope names are treated as "read" (least privilege).
|
|
let tools: &[&str] = match scope {
|
|
FULL => FULL_TOOLS,
|
|
WRITE => WRITE_TOOLS,
|
|
_ => {
|
|
tracing::debug!(
|
|
"[division] unknown scope '{scope}' — falling back to READ",
|
|
);
|
|
READ_TOOLS
|
|
}
|
|
};
|
|
tools.iter().map(|s| (*s).to_string()).collect()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::tool_scope::{tools_for, FULL, READ, WRITE};
|
|
|
|
/// Verify the READ tier does not contain write or bash tools.
|
|
#[test]
|
|
fn read_tier_excludes_write_tools() {
|
|
let tools = tools_for(READ);
|
|
assert!(!tools.contains(&"write".to_string()));
|
|
assert!(!tools.contains(&"bash".to_string()));
|
|
}
|
|
|
|
/// Verify the WRITE tier includes bash and write but not delete or git.
|
|
#[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()));
|
|
}
|
|
|
|
/// Verify the FULL tier includes delete and git tools.
|
|
#[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()));
|
|
}
|
|
|
|
/// Verify that an unrecognized scope name falls back to the READ tier.
|
|
#[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()));
|
|
}
|
|
|
|
/// Verify the tier hierarchy: READ ⊂ WRITE ⊂ FULL (each is a strict superset).
|
|
#[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"
|
|
);
|
|
}
|
|
}
|