feat(topology): executors for all 12 kinds (mesh + debate; mappings)
ci / gates (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / e2e (push) Has been cancelled

Every TopologyKind now runs, mapped to five execution patterns:
- hierarchical ← hub_spoke, star_moe, market
- pipeline     ← ring
- swarm        ← flat, holacratic
- mesh (new)   ← blackboard   (two peer-exchange rounds + aggregate)
- debate (new)                (propose → critique → revise → judge)

execute()'s match is now exhaustive (adding a kind upstream forces an executor),
so the Unsupported error is gone. Benchmark spans all five distinct patterns.
14 tests with --features provider; clippy clean. Doc updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-15 20:54:12 -07:00
co-authored by Claude Opus 4.8
parent c595cebab0
commit e93a3cfb53
4 changed files with 130 additions and 18 deletions
+60
View File
@@ -171,3 +171,63 @@ pub(crate) fn swarm(g: &TopologyGraph) -> Result<Vec<PlanStep>, OrchestratorErro
});
Ok(steps)
}
/// Mesh / blackboard: two rounds of peer exchange (round 2 sees every round-1
/// output), then the first peer aggregates — models iterative convergence.
pub(crate) fn mesh(g: &TopologyGraph) -> Result<Vec<PlanStep>, OrchestratorError> {
let n = g.nodes.len();
if n == 0 {
return Err(OrchestratorError::Malformed("empty topology".into()));
}
let mut steps = Vec::new();
for i in 0..n {
steps.push(PlanStep {
node_idx: i,
phase: StepPhase::Work,
use_task: true,
ctx_from: vec![],
});
}
let round1: Vec<usize> = (0..n).collect();
for i in 0..n {
steps.push(PlanStep {
node_idx: i,
phase: StepPhase::Work,
use_task: false,
ctx_from: round1.clone(),
});
}
let round2: Vec<usize> = (n..2 * n).collect();
steps.push(PlanStep {
node_idx: 0,
phase: StepPhase::Aggregate,
use_task: false,
ctx_from: round2,
});
Ok(steps)
}
/// Debate: proposer drafts, critic critiques, proposer revises, judge decides.
pub(crate) fn debate(g: &TopologyGraph) -> Result<Vec<PlanStep>, OrchestratorError> {
let n = g.nodes.len();
if n == 0 {
return Err(OrchestratorError::Malformed("empty topology".into()));
}
if n == 1 {
return Ok(vec![PlanStep {
node_idx: 0,
phase: StepPhase::Work,
use_task: true,
ctx_from: vec![],
}]);
}
let proposer = 0;
let critic = 1;
let judge = if n >= 3 { 2 } else { 0 };
Ok(vec![
PlanStep { node_idx: proposer, phase: StepPhase::Work, use_task: true, ctx_from: vec![] },
PlanStep { node_idx: critic, phase: StepPhase::Work, use_task: true, ctx_from: vec![0] },
PlanStep { node_idx: proposer, phase: StepPhase::Synth, use_task: false, ctx_from: vec![0, 1] },
PlanStep { node_idx: judge, phase: StepPhase::Aggregate, use_task: false, ctx_from: vec![2, 1] },
])
}