open-standards/jcp/jcp-rs/crates/jcp-orchestrate/examples/fleet.rs
Transaction Science 3e3d0779f8 jcp-orchestrate: a cross-harness orchestration profile
The agent-work ecosystem converged on a shape — a tracker of work items, a
reconcile loop that routes each to a worker, dispatches it, retries on failure
— over a heterogeneous fleet. The open meta-harness composes those workers but
leaves who-may-run-what, for-how-many-joules, and what-happened to the platform.

jcp-orchestrate keeps the loop and makes each dispatch a JCP decision:
- A Harness is any backend that commits a joule cost (its quote) and reports an
  outcome — the orchestrator is blind to what runs inside. Registered with a
  capability (the task kinds it may handle) and a joule budget.
- run() is the reconcile loop: poll pending → route → gate → seal → run →
  reconcile → retry. route() is capability-and-budget-aware, so an exhausted
  harness fails over to the next one with capacity.
- Every dispatch is framed, decided against the harness grant OUTSIDE the
  harness, and sealed as a JCR-1 receipt before it runs. A task whose kind no
  harness is granted is denied; a harness past its joule budget is denied; a
  flaky harness retries up to max_attempts. The run is an ordered, verifiable
  ledger of sealed decisions — the audit a bare meta-harness can't produce.

Built directly on jcp-runtime + jcp-grant + jcp-receipt (frames carry the
harness's multi-action capability resource) — no new crypto. 6 tests green,
clippy clean; the fleet example routes a mixed batch with budget failover, a
retrying flaky harness, and an ungranted-kind denial — 5 done / 1 denied,
7/7 sealed decisions verify. Whole workspace test + clippy --all-targets clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-27 21:11:36 -04:00

107 lines
3.8 KiB
Rust

//! Orchestrate a heterogeneous fleet: route work across harnesses, fail over on
//! budget exhaustion, retry a flaky one, deny the ungranted — every dispatch
//! capability-gated, joule-budgeted, and sealed.
//!
//! ```text
//! cargo run -p jcp-orchestrate --example fleet
//! ```
use std::cell::Cell;
use ed25519_dalek::SigningKey;
use jcp_orchestrate::{Harness, HarnessOutcome, Orchestrator, Task};
/// A harness that completes at a fixed quoted cost (stands in for any backend —
/// a different agent framework, a local model, a remote worker).
struct Worker {
id: String,
kinds: Vec<String>,
cost: u64,
}
impl Harness for Worker {
fn id(&self) -> &str {
&self.id
}
fn handles(&self, kind: &str) -> bool {
self.kinds.iter().any(|k| k == kind)
}
fn estimate(&self, _task: &Task) -> u64 {
self.cost
}
fn run(&self, _task: &Task) -> HarnessOutcome {
HarnessOutcome::done(self.cost)
}
}
/// A harness that fails transiently `n` times before completing.
struct Flaky {
id: String,
cost: u64,
fails: Cell<u32>,
}
impl Harness for Flaky {
fn id(&self) -> &str {
&self.id
}
fn handles(&self, kind: &str) -> bool {
kind == "scrape"
}
fn estimate(&self, _task: &Task) -> u64 {
self.cost
}
fn run(&self, _task: &Task) -> HarnessOutcome {
let left = self.fails.get();
if left > 0 {
self.fails.set(left - 1);
HarnessOutcome::retry(self.cost, "endpoint timeout")
} else {
HarnessOutcome::done(self.cost)
}
}
}
fn main() {
let mut fleet = Orchestrator::new(
SigningKey::from_bytes(&[1u8; 32]), // the principal authorising the fleet
SigningKey::from_bytes(&[7u8; 32]), // the sealing steward
"did:example:fleet",
3, // max attempts per item
);
// Two harnesses can summarize; the primary runs out of budget first and the
// backup picks up the overflow. A third harness scrapes (flaky). Nothing is
// granted "translate".
fleet.register(Box::new(Worker { id: "summarizer-A".into(), kinds: vec!["summarize".into()], cost: 100 }), &["summarize"], 250).unwrap();
fleet.register(Box::new(Worker { id: "summarizer-B".into(), kinds: vec!["summarize".into()], cost: 100 }), &["summarize"], 1_000).unwrap();
fleet.register(Box::new(Flaky { id: "scraper".into(), cost: 80, fails: Cell::new(2) }), &["scrape"], 1_000).unwrap();
// A batch of mixed work.
for n in 0..4 {
fleet.submit(Task::new(format!("sum-{n}"), "summarize", vec![n]));
}
fleet.submit(Task::new("scrape-1", "scrape", b"https://example".to_vec()));
fleet.submit(Task::new("xlate-1", "translate", b"bonjour".to_vec())); // ungranted kind
let report = fleet.run();
println!("── run report ──");
println!(" done : {}", report.done);
println!(" denied : {}", report.denied);
println!(" failed : {}", report.failed);
println!(" joules : {} µJ authorised across the fleet", report.total_uj);
println!(" sealed : {} decision receipts", report.receipts);
println!("\n── per-item ──");
for id in ["sum-0", "sum-1", "sum-2", "sum-3", "scrape-1", "xlate-1"] {
println!(" {id:10} → {:?}", fleet.state_of(id).unwrap());
}
println!("\n── per-harness joules ──");
for h in ["summarizer-A", "summarizer-B", "scraper"] {
println!(" {h:14} {} µJ", fleet.spent_uj(h));
}
// The whole run is one ordered, verifiable ledger.
let steward = fleet.steward().to_string();
let pk = fleet.steward_pubkey();
let verified = fleet.receipts().iter().filter(|r| jcp_receipt::extract(r, &steward, &pk).is_ok()).count();
println!("\naudit: {verified}/{} sealed decisions verify under the steward key", fleet.receipts().len());
}