Files
clawmates/crates/cm-topology/src/builders.rs
T
Omar SobhandClaude Opus 4.8 3554a3aaf2
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 23s
ci / rust (push) Failing after 27s
ci / e2e (push) Has been skipped
CI: remove k8s stages, fix the Docker-level pipeline green
Survey + fixes so the pipeline passes at the Docker level (no k8s).

- Remove k8s: drop the `sandbox-k8s` job (kind/Calico/--features k8s-tests) and the
  "Helm chart lints" gate step. release.yml was already k8s-clean.
- Rust job:
  - `cargo fmt --all` — fix pre-existing formatting drift (fmt --check was failing).
  - clippy -D warnings: fix 3 lib warnings (cm-brain sort_by_key→Reverse, cm-api
    fleet.rs doc list indentation, node_rules map_or→is_none_or).
  - Regenerate the .sqlx offline cache (was missing the cm-runtime run_loop test
    query → offline compile failed). DB-backed tests use testcontainers at runtime.
  - Set SQLX_OFFLINE=true on the rust + e2e jobs so query! macros compile against
    the committed cache deterministically (no DB needed at compile time).
- Frontend job:
  - Fix the 1 ESLint error (useAgentTelemetry: no setState-synchronously-in-effect;
    tag the slice with agentId + derive null on mismatch).
  - Fix 2 stale panel-params tests (`terminal` is a valid app id now; assert the
    current APP_IDS + use a genuinely-unknown id for the reject case).

Verified locally: fmt clean, clippy --all-targets -D warnings clean (offline),
frontend lint 0 errors, tsc clean, 86/86 frontend tests pass, build OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-26 18:15:31 -07:00

128 lines
3.5 KiB
Rust

//! Canonical topology builders: instantiate a [`TopologyGraph`] of any
//! [`TopologyKind`] from a list of roles.
//!
//! This is the "template catalog" — it turns a kind + roles into a runnable
//! graph, and it is the candidate generator for the evolution/search layer.
//! Node ids are `n0..nk`; `roles[i]` is the role of `ni`.
use crate::graph::{Edge, EdgeKind, Node, TopologyGraph};
use crate::kind::TopologyKind;
use crate::TopologyError;
fn id(i: usize) -> String {
format!("n{i}")
}
fn star(n: usize, kind: EdgeKind) -> Vec<Edge> {
(1..n)
.map(|i| Edge {
from: id(0),
to: id(i),
kind,
})
.collect()
}
fn chain(n: usize, close: bool) -> Vec<Edge> {
let mut edges: Vec<Edge> = (0..n.saturating_sub(1))
.map(|i| Edge {
from: id(i),
to: id(i + 1),
kind: EdgeKind::PipesTo,
})
.collect();
if close && n > 1 {
edges.push(Edge {
from: id(n - 1),
to: id(0),
kind: EdgeKind::PipesTo,
});
}
edges
}
fn complete(n: usize, kind: EdgeKind) -> Vec<Edge> {
let mut edges = Vec::new();
for i in 0..n {
for j in (i + 1)..n {
edges.push(Edge {
from: id(i),
to: id(j),
kind,
});
}
}
edges
}
/// Build a canonical graph of `kind` over `roles` (node `ni` plays `roles[i]`).
pub fn build(kind: TopologyKind, roles: &[&str]) -> Result<TopologyGraph, TopologyError> {
if roles.is_empty() {
return Err(TopologyError::Empty);
}
let n = roles.len();
let nodes: Vec<Node> = roles
.iter()
.enumerate()
.map(|(i, r)| Node::new(id(i), *r))
.collect();
use TopologyKind::*;
let edges = match kind {
Hierarchical => star(n, EdgeKind::DelegatesTo),
HubSpoke | StarMoe => star(n, EdgeKind::RoutesTo),
Market => star(n, EdgeKind::BidsTo),
Pipeline => chain(n, false),
Ring => chain(n, true),
Mesh => complete(n, EdgeKind::PeersWith),
Blackboard => complete(n, EdgeKind::ReadsWrites),
// Peer/parallel kinds wire structure at run time, not via edges.
Flat | Holacratic | Swarm | Debate => Vec::new(),
};
TopologyGraph::new(kind, nodes, edges)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::classify;
#[test]
fn builds_every_kind() {
let roles = ["coordinator", "researcher", "writer"];
for kind in TopologyKind::ALL {
let g = build(kind, &roles).expect("build");
assert_eq!(g.order(), 3);
assert_eq!(g.kind, kind);
g.validate().expect("valid");
}
}
#[test]
fn canonical_shapes_classify_back() {
let roles = ["a", "b", "c", "d"];
assert_eq!(
classify(&build(TopologyKind::Hierarchical, &roles).unwrap()).primary,
TopologyKind::HubSpoke // a 1→(n-1) star reads as hub/star structurally
);
assert_eq!(
classify(&build(TopologyKind::Pipeline, &roles).unwrap()).primary,
TopologyKind::Pipeline
);
assert_eq!(
classify(&build(TopologyKind::Ring, &roles).unwrap()).primary,
TopologyKind::Ring
);
assert_eq!(
classify(&build(TopologyKind::Mesh, &roles).unwrap()).primary,
TopologyKind::Mesh
);
}
#[test]
fn empty_roles_error() {
assert_eq!(build(TopologyKind::Flat, &[]), Err(TopologyError::Empty));
}
}