A reference implementation of agentic coalitions in Rust.
koalisi provides a layered architecture for building agent coalition systems —
from temporal hypergraph topology to coalition formation algorithms to
tokio::sync task-based runtime orchestration.
┌─────────────────────────────────────────────────┐
│ Runtime Layer (tokio tasks) │
│ broadcast buses, mpsc/oneshot handles, │
│ TaskTracker + CancellationToken three-step │
│ shutdown, task-restart supervision, │
│ CoalitionService seam, optional durable log │
├─────────────────────────────────────────────────┤
│ Algorithm Layer │
│ DCVC workload distribution, AIPA partition │
│ search, pluggable value calculators │
├─────────────────────────────────────────────────┤
│ Topology Layer (catgraph-applied Hypergraph) │
│ Temporal hypergraph, event sourcing, │
│ CoalitionManager, time-travel queries, │
│ analytics │
├─────────────────────────────────────────────────┤
│ Core │
│ CoalitionRuntime, config, logging │
└─────────────────────────────────────────────────┘
| Module | Description |
|---|---|
core |
CoalitionRuntime (lifecycle), settings, logging |
topology |
Temporal hypergraph with event sourcing, CoalitionManager, time-travel queries, analytics (incl. magnitude_history coalition-diversity trajectories behind the magnitude feature) |
algorithms |
ValueCalculator trait + 4 base calculators + a feedback-weighting FeedbackCalculator wrapper (history/failure signals from a shared FeedbackStore), DCVCDistributor, AIPA partition search, population coalition-structure search (search/record_trajectory, #42) |
ingest |
Domain-neutral ingestion (K5): Sample/DataSource traits, generic SampleMonitor<S>, Pacing + pump_source, synthetic NEST-shaped multi-resolution and tauhokohoko-shaped sensor-event fixture sources (seeded, no credentials) |
decision |
CoalitionDecisionPolicy trait + always-available ThresholdPolicy; optional Active Inference strategy (EfeValueCalculator, AifDecisionPolicy) behind the decision feature; optional categorical-magnitude strategy (MagnitudeValueCalculator, MagnitudePolicy) behind the magnitude feature |
persistence |
Append-only event store (feature persistence): hash-chained streams, CBOR frame log (FileEventStore), crash-tail recovery, writer task; topology events tap in and replay back into a fresh EventLog all queries run on unchanged (P7.1 + P7.2); join/leave decisions land on a Decisions stream, each with one causal parent into Topology, and with durable also on, a tee writes each decision to the log and then to the bus (P7.4, minus belief snapshots) — see .claude/docs/phase7-persistence-design.md |
harness |
The K7 A/B harness (feature harness): seeded instance generation, the battery loop (bootstrap join, policy-gated arrivals, one leave sweep), per-seed metrics, report helpers — the plumbing every K7 registration shares; with process, the workflow world (WorkflowSpec: per-agent roles, per-task declared Demand, role-matched step coverage, an optional performance draw and, where an instance carries one, a performance-scored outcome beside the coverage-scored one) and the per-task lifecycle hook every policy can observe (begin_task / observe_outcome, the latter with the task's per-bit signal and its final members, each with whether it performed) |
subsystems |
CoalitionService — the policy-gated coalition-membership seam (join/leave consult a CoalitionDecisionPolicy before mutating the hypergraph) — plus a decision-tap tee (spawn_decision_tee), an optional durable decision log (durable), and an optional libp2p remote coalition-event gateway (remote: bounded buffer, cursor polling, stable RemoteCoalitionEventV1 wire schema) |
use koalisi::topology::{CoalitionManager, TemporalQueries, Timestamp};
use std::fmt::{Display, Formatter};
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
struct Agent(&'static str);
impl Display for Agent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) }
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
struct Team(&'static str, usize);
impl Display for Team {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) }
}
impl From<Team> for usize { fn from(t: Team) -> usize { t.1 } }
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mgr = CoalitionManager::<Agent, Team>::empty();
let a = mgr.add_agent(Agent("alice")).await?;
let b = mgr.add_agent(Agent("bob")).await?;
let c = mgr.form_coalition(vec![a, b], Team("alpha", 10)).await?;
let members = mgr.coalition_members(c).await?;
assert_eq!(members.len(), 2);
// Time-travel: how many agents existed at T=0?
let count = TemporalQueries::count_vertices_at(
mgr.graph().events_ref(), Timestamp::new(0)
).await;
assert_eq!(count, 1); // only alice
Ok(())
}# Coalition topology — form, join, merge, time-travel
cargo run --example topology_coalition
# Algorithm values — calculators, DCVC, AIPA
cargo run --example algorithm_values
# Flagship: synthetic ingestion → coalition formation (domain-neutral, no credentials)
cargo run --example synthetic_ingestion
# Task-restart supervision (spawn_supervised over a synthetic monitor)
cargo run --example supervised_monitor
# Population search over coalition structures atop AIPA (P5.2, #42)
cargo run --example population_search
# Feature-gated
cargo run --release --features decision,magnitude,process --example strategy_comparison # the frozen K4 archive battery (Parts 1–11, serial)
cargo run --features harness --example gauntlet # K7 harness skeleton
K7_1_SEEDS=5000..5003 cargo run --release --features harness,decision,process --example k7_1 # K7-1 off-block smoke (no verdict; the run of record is docs/runs/K7-1.log)
K7_2_SEEDS=6000..6003 cargo run --release --features harness,decision,process --example k7_2 # K7-2 smoke: gate lines only, no cell value (the run of record is docs/runs/K7-2.log)
cargo run --features decision --example population_reliability # learned-reliability fitness for the structure search (#57)
cargo run --features durable --example durable_decisions # durable decision log (needs Docker)
cargo run --features remote --example remote_coalition_consumer # remote coalition-event gateway + client, one process (#38)
cargo run --features metrics --example metrics_scrape # Prometheus counters/histograms over the decision, outcome and topology-event taps, self-scraped and asserted (#25)Value models for structure search (population_search).
searchmaximisesΣ over blocks of ValueCalculator(block). The built-in calculators are degenerate for this:AdditiveCalculatoris constant across every set-partition (its size / capability / trust terms sum to the same total for any grouping), andSynergisticCalculator/MultiplicativeCalculatorfavour all-singletons — so with those the answer is trivial and the improvement lineage is one epoch. Thepopulation_searchexample therefore uses aTaskCoveragevalue model (reward full coverage of a required capability set, penalise redundant members) whose optimum is a genuine non-trivial partition. Real structure search needs an interior-optimum value model — coverage-style, or themagnitude/ EFE /FeedbackCalculatorarms.
The showcase is a head-to-head battery of coalition-decision strategies —
Active Inference arms built on aif vs a
categorical (magnitude-based) baseline built on
catgraph — run as pre-registered
A/B experiments. Criteria are fixed and committed before each run
(docs/prereg-*.md), verdicts are recorded against them
(docs/ab-report-*.md), and falsified arms stay falsified — the reports are
never rewritten. docs/README.md indexes every run and the
seed ledger; docs/PROTOCOL.md is the protocol; the raw
output of each run of record is committed under
docs/runs/.
Two binaries carry it. examples/strategy_comparison.rs is the K4 lineage
(Parts 1–11, the table below), frozen as the archive: it changes only through
src/, and a fresh serial run diffed against docs/runs/K4-archive.log is the
drift gate at every dependency re-pin. The K7 lineage runs on
examples/gauntlet.rs and one example per registration under examples/k7/,
sharing its plumbing through src/harness/ (feature harness); its
pre-registrations and reports are under docs/k7/.
The run history is deliberately adversarial:
| Run | Challenger arm | Verdict |
|---|---|---|
| K4 v1 (#7) | scalar AIF bridge | FALSIFIED (latency) under v1 criteria; VALIDATED (B) under the pre-posted v2 amendment — magnitude superior on quality 30/30 seeds |
| K4 v3 | multimodal AIF (one modality per capability bit) | FALSIFIED (multimodality) — proved decision-equivalent to the scalar bridge, all 30 seeds |
| K4 v4 (#44) | persistent AIF (learning + precision dynamics) | FALSIFIED (persistence) — genuinely escapes the v3 equivalence theorem, but loses on quality |
| K4 v5 (#53) | E1-only persistent AIF (learned precisions + novelty, fixed γ) | VALIDATED (gap closed) — first arm to beat magnitude on out-of-sample quality (0.4406 vs 0.2720), at a churn + latency cost |
| K4 v6 (#56) | v5 + never-evict state damping | FALSIFIED (never-evict) — the eviction churn is the winning mechanism (monotone cap series) |
| EQ1 (#61) | de-saturated query regime (γ sweep) | FALSIFIED (de-saturation) — γ frees only the leave stream; the join rail is margin-proof at p = 1.0 |
| EQ1-corrected (#63) | block-level coverage routing | FALSIFIED (block-routing) — the value window barely exceeds the competing singleton lattice |
| EQ3 (#69) | incremental-magnitude latency levers | FALSIFIED (latency re-match) — bit-parity held, but the 2.5× gap never closed |
| EQ4 (#72) | typed roles (ρ-modulated coupling) | VALIDATED (typed roles) — 3.61× on 30/30 seeds; the lever is retained role-diverse redundancy, not coverage routing |
| EQ5a (#76) | process-structured tasks (string-diagram rewriting) | FALSIFIED (process structure) — rewriting converted 100 % of a low ceiling; the signal was valuation, not rewriting |
| EQ5a follow-up (#80) | is the residual lever process-specific? | FALSIFIED (coverage proxy) — it replicates at 1.34× and then behaves identically without the process structure |
| EQ5b (#78) | GroupAgent of role-slotted AIF internals over the v5 world model |
VALIDATED (two-engine) — 1.2567× on 22/30, by 0.7 %; but role specialisation measured negative and the win rides members structurally blind to the candidate |
| K7-1 (#90) | EQ5b's group with the candidate's own role internal routed to every voter (aif ext-6 topology, λ = ½) vs the EQ5b group |
VALIDATED (topology-routed group) — 2.41× on 30/30; but the routed arm's outcome is that of an engine-free redundancy prune (598 of 600 tasks), one voter decides every read it can see, and the control loses by evicting needed members on every three-role task. The effect was seen off-block before the run |
| K7-2 (#97) | the inherited novelty term on against off on K7-1's routed group | FALSIFIED (novelty moves the outcome) — the registered expectation was dead at the outcome; novelty on is 3.00× on 30/30. But the better cell is the engine-free prune (600 of 600 tasks): without the term a join that changes no coverage is decided by whether the role's last tasks succeeded, and a success declines it |
Two feedback-calculator arms ran the same gauntlet
(#46 FALSIFIED,
#48 PARTIAL (mechanism only)). The arm-choice decision is recorded in
docs/k4-arm-choice-memo.md: magnitude remains the demonstrated default;
the E1 arm stands as capability evidence, arm selection being a cost–quality
tradeoff. The competitive pressure also flowed upstream — several aif
engine features (seed API, novelty EFE term, Dirichlet-count injection) were
cut specifically for these arms; tira's README tells the same story from the
upstream side.
cargo test # 107 tests (core + topology + algorithms + population search + decision + ingestion + decision-tap tee + decision trace tap)
cargo test --features decision # 163 tests (+ Active Inference decision strategies: scalar, multimodal, persistent + the derived ReliabilityCoverage calculator)
cargo test --features magnitude # 136 tests (+ categorical-magnitude decision strategy + typed-role modulation + trajectory analytics)
cargo test --features decision,magnitude # 192 tests (both decision arms)
cargo test --features magnitude-fast # 144 tests (+ EQ3 opt-in levers + read-only probes)
cargo test --features persistence # 131 tests (+ chained event store + topology replay + the Decisions stream)
cargo test --features persistence,magnitude # 161 tests (incl. the live-vs-replayed parity gate)
cargo test --features persistence,durable # 133 tests (+ the log-then-bus decision tee; needs Docker)
cargo test --features remote # 113 tests (+ gateway event buffer + loopback round-trip)
cargo test --features process # 162 tests (+ process-structured workflows + the unstaffable-residual policy)
cargo test --features decision,magnitude,process # 250 tests (the full A/B battery surface)
cargo test --features durable # 108 tests (+ container-backed restart-durability test; needs Docker)
cargo test --features harness # 132 tests (+ the K7 harness: rng, instance generation, battery loop, lifecycle hook, decision trace, report helpers)
cargo test --features harness,process # 235 tests (+ the K7 workflow world and its identity gate against docs/runs/K4-archive.log; trace reconstruction and the engine-free reference policies; the performance-scored outcome)
cargo test --features harness,decision,process # 330 tests (+ the group arm hosted on the harness, its identity gates against docs/runs/K4-archive.log and docs/runs/K7-1.log, the novelty fixture)
cargo test --features metrics # 107 tests (the default suite; the feature gates two optional deps and the metrics_scrape example)- catgraph-applied (tag
v0.23.0, kept in lockstep with catgraph-magnitude and catgraph-syntax — one repo, one checkout, all three move together) — CRUD hypergraph container backing the topology layer - tokio + tokio-util — async runtime + lifecycle primitives
- rayon + tokio-rayon — CPU-bound graph operations bridge
- surrealdb-live-message (tag
v0.2.2, optional, featuredurable) — two-tier restart-durable message bus for the coalition decision log - aif (tag
aif-v0.14.0, optional, featuredecision) — active-inference engine for the AIF decision strategies (scalar, multimodal, persistent);nalgebrais only compiled when the feature is enabled - catgraph-magnitude (tag
v0.23.0, optional, featuremagnitude) — enriched-category coalition magnitude for the categorical decision strategy - catgraph-syntax (tag
v0.23.0, optional, featureprocess) — colored-syntax layer over the free-prop term surface for process-structured tasks; depends on catgraph + catgraph-applied + thiserror only, so no DeepCausality crate is in the dependency graph under any feature - ciborium 0.2 + sha2 0.11 (optional, feature
persistence) — CBOR frames and SHA-256 chaining for the append-only event store - libp2p 0.57 (optional, feature
remote) — TCP+noise+yamuxrequest-responsetransport for the remote coalition-event gateway - metrics 0.24 + metrics-exporter-prometheus 0.18 (
http-listeneronly) (optional, featuremetrics) — the metrics facade and the Prometheus scrape listener used byexamples/metrics_scrape.rs
- Zhang, Y., Lin, C., Tang, S., Chen, H., Zhou, S., Ma, Y., Tresp, V. (2025).
SwarmAgentic: Towards Fully Automated Agentic System Generation via Swarm
Intelligence. arXiv:2506.15672 — the
design inspiration for the population-based coalition-structure search
(
algorithms::population) and the feedback-weighted value calculator (FeedbackCalculator). A working digest (and a CC0 copy of the paper) lives in.claude/docs/. - The Active Inference side of the A/B battery builds on the aif engine — see tira's README for its own paper-reproduction lineage (Waade et al.).
MIT OR Apache-2.0