topology: drop manager-root special case + notify three agents on reparent (#743)
This commit is contained in:
parent
9884ac013d
commit
2c3b62be55
4 changed files with 139 additions and 20 deletions
|
|
@ -182,18 +182,23 @@ pub fn default_seed(agent_names: &[String]) -> BTreeMap<String, Option<String>>
|
|||
}
|
||||
|
||||
/// Pure validation + apply for [`set_parent`]. Splits off so tests
|
||||
/// can exercise the rules (cycle / unknown / manager-protect) on
|
||||
/// an in-memory `BTreeMap` without touching the on-disk
|
||||
/// `topology.json`. Returns either the post-move map (caller
|
||||
/// writes it back) or a user-readable error string.
|
||||
/// can exercise the rules (cycle / unknown) on an in-memory
|
||||
/// `BTreeMap` without touching the on-disk `topology.json`. Returns
|
||||
/// either the post-move map (caller writes it back) or a
|
||||
/// user-readable error string.
|
||||
///
|
||||
/// Pre-#743 this also refused to reparent the manager
|
||||
/// ("cannot reparent the manager — it is structurally root") —
|
||||
/// argus-paranoia from #361 that we dropped per mara's
|
||||
/// `#9512` / `#9557`: the manager's special powers come from its
|
||||
/// privileged MCP socket, not its tree position. The cycle walk
|
||||
/// below covers "moving X under its own descendant" for the
|
||||
/// manager as much as any other agent.
|
||||
pub fn apply_set_parent(
|
||||
topo: &BTreeMap<String, Option<String>>,
|
||||
child: &str,
|
||||
new_parent: Option<&str>,
|
||||
) -> Result<BTreeMap<String, Option<String>>, String> {
|
||||
if child == crate::lifecycle::MANAGER_NAME {
|
||||
return Err("cannot reparent the manager — it is structurally root".to_owned());
|
||||
}
|
||||
if !topo.contains_key(child) {
|
||||
return Err(format!("unknown agent: {child}"));
|
||||
}
|
||||
|
|
@ -342,10 +347,34 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn apply_set_parent_refuses_manager_move() {
|
||||
let err = apply_set_parent(&topo_three_level(), crate::lifecycle::MANAGER_NAME, None)
|
||||
fn apply_set_parent_allows_manager_move() {
|
||||
// Post-#743: the manager is reparentable like any other agent
|
||||
// (its privileges live on the MCP socket, not its tree
|
||||
// position). Build a topo with an unrelated root-level agent
|
||||
// `peer` so moving the manager under it doesn't trip the
|
||||
// cycle walk (every non-manager agent in topo_three_level
|
||||
// descends from the manager, so that fixture can't exercise
|
||||
// a legal manager move).
|
||||
let mut topo = BTreeMap::new();
|
||||
topo.insert(crate::lifecycle::MANAGER_NAME.to_owned(), None);
|
||||
topo.insert("peer".to_owned(), None);
|
||||
let next = apply_set_parent(&topo, crate::lifecycle::MANAGER_NAME, Some("peer"))
|
||||
.expect("manager move should succeed post-#743");
|
||||
assert_eq!(
|
||||
next.get(crate::lifecycle::MANAGER_NAME),
|
||||
Some(&Some("peer".to_owned()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_set_parent_refuses_manager_under_own_descendant() {
|
||||
// Moving the manager under `bob` (who already lives under
|
||||
// `alice` who lives under the manager) would close the loop.
|
||||
// The general cycle walk catches this; no separate manager
|
||||
// guard needed.
|
||||
let err = apply_set_parent(&topo_three_level(), crate::lifecycle::MANAGER_NAME, Some("bob"))
|
||||
.unwrap_err();
|
||||
assert!(err.contains("manager"), "err = {err}");
|
||||
assert!(err.contains("cycle"), "err = {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue