feat(#704): add <children> recipient sentinel — fan-out to direct descendants
This commit is contained in:
parent
7cc2690717
commit
36c683138d
5 changed files with 128 additions and 15 deletions
|
|
@ -313,11 +313,38 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc<Coordinator>) ->
|
|||
}
|
||||
}
|
||||
|
||||
/// Fan out one message to each recipient in `targets`. Skips the sender
|
||||
/// itself. Returns a list of `"<agent>: <error>"` strings for any delivery
|
||||
/// failures (empty = all good).
|
||||
fn fan_out_send(
|
||||
coord: &Arc<Coordinator>,
|
||||
from: &str,
|
||||
body: &str,
|
||||
in_reply_to: Option<i64>,
|
||||
targets: &[String],
|
||||
) -> Vec<String> {
|
||||
let mut errors = Vec::new();
|
||||
for target in targets {
|
||||
if target == from {
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = coord.broker.send(&Message {
|
||||
from: from.to_owned(),
|
||||
to: target.clone(),
|
||||
body: body.to_owned(),
|
||||
in_reply_to,
|
||||
}) {
|
||||
errors.push(format!("{target}: {e}"));
|
||||
}
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
||||
/// Common Send handler shared between dispatch arms. Applies the
|
||||
/// 4 KiB body cap, then routes broadcast (`to == "*"`) vs unicast
|
||||
/// through their respective broker calls. Pulled out of `dispatch`
|
||||
/// to keep that function under the clippy too-many-lines limit; the
|
||||
/// behaviour is identical to inlining.
|
||||
/// 4 KiB body cap, then routes broadcast (`to == "*"`) / children fan-out
|
||||
/// (`to == "<children>"`) / unicast through their respective broker calls.
|
||||
/// Pulled out of `dispatch` to keep that function under the clippy
|
||||
/// too-many-lines limit; the behaviour is identical to inlining.
|
||||
fn handle_send(
|
||||
coord: &Arc<Coordinator>,
|
||||
agent: &str,
|
||||
|
|
@ -338,11 +365,25 @@ fn handle_send(
|
|||
}
|
||||
};
|
||||
}
|
||||
// Resolve magic-recipient sentinels (currently `<parent>`) against
|
||||
// topology.json; no-op for ordinary names. Lets agents address
|
||||
// structural roles without learning the label — runtime
|
||||
// reparenting propagates for free. See `docs/conventions.md::
|
||||
// Recipient sentinels`.
|
||||
// `<children>`: fan out to every direct descendant of the sender per
|
||||
// topology.json. Bypasses the allow-list check — structural fan-out
|
||||
// targets are never user-listed peers. No-op (returns Ok) for leaf
|
||||
// agents that have no children.
|
||||
if to == hive_sh4re::CHILDREN_RECIPIENT {
|
||||
let children = crate::topology::children_of(agent);
|
||||
let errors = fan_out_send(coord, agent, body, in_reply_to, &children);
|
||||
return if errors.is_empty() {
|
||||
AgentResponse::Ok
|
||||
} else {
|
||||
AgentResponse::Err {
|
||||
message: format!("children fan-out failed for agents: {}", errors.join(", ")),
|
||||
}
|
||||
};
|
||||
}
|
||||
// Resolve magic-recipient sentinels (`<parent>`) against topology.json;
|
||||
// no-op for ordinary names. Lets agents address structural roles without
|
||||
// learning the label — runtime reparenting propagates for free. See
|
||||
// `docs/conventions.md::Recipient sentinels`.
|
||||
let resolved = crate::topology::resolve_recipient(agent, to);
|
||||
match coord.broker.send(&Message {
|
||||
from: agent.to_owned(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue