Programmable Agent Orchestration: the h5i-orchestra eDSL
One topology was never going to be enough.
h5i team ships a single shape: N agents on one task, a review round, a
neutral verdict. h5i-orchestra makes the shape yours to write. A run is a
plain async Rust program. Every agent turn is journaled on a Git ref, so a killed run
resumes without re-paying a single turn.
- A score is define-by-run: the orchestration graph is a trace of what executed, not a graph you build up front.
if,for, andtokio::join!are the control flow. - Every effectful step is journaled on the team event log (
refs/h5i/team/<run>). Resuming replays completed steps from the journal, so agent turns are never re-run. - The journal is a Git ref, so a run is shareable and resumable across clones with
h5i share push. The handoff is agit push, not a hosted runtime. - Agents stay in sandboxed
h5i envboxes. The score is host-side code. The eDSL adds orchestration, not a new trust surface.
An agent ensemble has one fixed answer to "what should happen next": everyone attempts the
task, the round is sealed, peers review, a verifier decides. That is a good default, and
h5i team ships it. But the real
space of useful topologies is larger. Sometimes you want a designer agent to hand a plan to
two implementers. Sometimes you want three independent attempts ranked in an arena. Sometimes
you want one integrator to merge the work of many. Each of those is a different program, and a
fixed protocol cannot express them.
h5i-orchestra is that program layer. It is an embedded DSL in Rust: you write an
ordinary async function, you are handed a Conductor, and you drive the run by
calling methods on it. There is no graph builder. There is no compile() step. The
orchestration is whatever your code did, recorded as it ran.
Define-by-run: the graph is a trace, not a prerequisite
This is the one design decision everything else follows from, and it is not a new idea. It is the decision that settled the deep-learning framework wars a decade ago. Theano and early TensorFlow asked you to describe a computation to a smarter executor, then hand it over. PyTorch let you perform the computation and quietly recorded it. The recorded-execution side won on the things that actually matter day to day: errors surface where you wrote the code, the host language is the control flow, and you can drop a breakpoint anywhere.
Agent orchestration is more dynamic and more failure-prone than a training loop, so the lesson
transfers with force. In h5i-orchestra, the classic ensemble is a loop you can
read:
// hire two agents, each into its own sandboxed env let claude = c.agent("claude").runtime("claude").hire().await?; let codex = c.agent("codex").runtime("codex").hire().await?; // fan-out is just host-language concurrency let (a, b) = tokio::try_join!(claude.work(&task), codex.work(&task))?; c.freeze().await?; // seal the independent attempts // cross review, a plain loop let (ra, rb) = tokio::try_join!(codex.review(&a), claude.review(&b))?; // neutral verification in a fresh sandbox, then a pluggable verdict let verified = c.verify([&a, &b]).command("cargo test").await?; let verdict = c.judge(verified, policy::tests_then_smallest_diff()).await?;
There is no framework vocabulary here. No executors, no edges, no supersteps. To add a
condition, you write an if. To retry, you write a loop. To run two agents at once,
you use the concurrency your language already has. The DAG that a reviewer will later want to
see is derived from the trace after the fact, with h5i team trace --dot. It is a
view over what happened, never a thing you had to construct before anything could happen.
Durability without a server: journaling on a Git ref
A coding-agent turn costs minutes and real money. So the property that matters most in an orchestrator is not speed, it is this: if the process dies at step forty, restarting must not re-run the first thirty-nine. That is durable execution, and the industry has three designs for it. Temporal replays your workflow code against an event history. Restate journals each named step's result and skips it on recovery. Graph frameworks checkpoint at a barrier and re-execute forward, which can re-pay LLM calls.
h5i-orchestra takes the journaling design, because for agent turns it is the only
one where a resume is free. Every effectful operation, an agent().hire(), a
work, a verify, an arbitrary step, executes once and
appends its serialized result to the run's event log at refs/h5i/team/<run>.
Each step carries a stable key, label#seq. Re-running the score consults the
journal by key: a hit returns the recorded result instantly, a miss executes live and records.
Kill the run at any point, run the same binary again, and it fast-forwards through everything
already done and picks up exactly where it stopped.
Because the journal is a Git ref, durability costs nothing to operate and it travels. There is
no database to run and no checkpoint store to configure. h5i share push moves a
live run to another clone. One machine can drive a score up to a human approval gate, a
reviewer on a different machine can answer, and either side can resume. The cross-machine
handoff is a git push. None of the general-purpose orchestration frameworks can do
that without a hosted runtime, because their state does not live in your repo.
$ h5i team run fix-auth --task-file task.md --verify-cmd "cargo test" ➜ driving team 'fix-auth': 2 agents, 1 review round # ...process killed at the verify step... $ h5i team run fix-auth --task-file task.md --verify-cmd "cargo test" ➜ resuming: 6 journaled steps replay without re-execution # the two agent attempts and the reviews are NOT re-run: they replay from the ref
The primitives
The surface is deliberately small. A score composes a handful of methods on the
Conductor and on the Agent handles it hands you. Everything else is a
pattern built from these.
c.agent(name)...hire()creates or binds a sandboxedh5i envand enrolls the agent on the roster.agent.work(task)dispatches a work turn and resolves to the frozen candidate.with_materials(&parts)grants it sight of teammates' diffs for integration work.agent.ask::<T>(prompt)returns typed data instead of code: the reply must deserialize asT, so judges and routers are ordinary functions.agent.review/agent.revisedrive the peer-review loop.c.verify,c.judge,c.applyrun the neutral verifier, decide a verdict through a pluggableVerdictPolicy, and land the winner. Apply stays mediated and human-gated.c.gate(question)is durable human-in-the-loop: it asks over the i5h message bus, and a score can exit and resume after the answer arrives, even the next day.c.step(label, f)is the universal escape hatch: run any side effect exactly once, journaled.
The one discipline the eDSL asks of you is stable step identity: parallel steps must carry
distinct labels, so a resume pairs the right recorded result with the right call. Agent
operations get this for free, since their labels embed the agent id. For your own parallel
loops, Conductor::scope gives each branch its own label namespace, and the journal
fails closed if two concurrent steps ever collide on a label. That is the analog of the one
constraint PyTorch borrowed from JAX: demand discipline only where it buys something users
cannot get otherwise. Here it buys a free resume.
Where this sits
It would be a mistake to sell h5i-orchestra as a better LangGraph or a better
Microsoft Agent Framework. Those orchestrate arbitrary in-process LLM calls, they have hosted
observability, and they have ecosystems this does not. If you are building a general
multi-agent application, use one of them.
What h5i-orchestra owns is the layer none of them touch: the agents are
semi-trusted coding agents that need kernel-enforced isolation, their outputs need provenance,
their cross-influence needs to be auditable, and their merged result needs a mediated,
evidence-gated path back to the main branch. That is not a feature you bolt onto a general
framework. It is the substrate underneath, and h5i already had it. The orchestrator is the
thin part. The isolated envs, the capture evidence, the neutral verifier, and the Git-native
event log are the parts that are hard to build and easy to depend on.
The result is an orchestrator with a different center of gravity. Not "describe a graph to a runtime," but "write a program, and the runtime records it into a Git ref you can share, resume, and audit." The score is yours. The proof travels with it.
FAQ
Do I have to write Rust to use this? No. The CLI drives the common cases: h5i team run executes the full classic cycle over the ensemble pattern, with --rounds, --verify-cmd, --gate, and --manifest team.toml to parameterize it. You reach for a hand-written score when you want a topology the built-in patterns do not cover.
How does the score talk to the agents? Through the same wire an h5i team uses: an i5h message plus a per-env inbox, delivered to a warm resident session that the Stop hook keeps alive between turns. The score is a coordinator, not an LLM client. It holds no model connection and no agent state.
What runs the agents, then? A RuntimeLauncher. The default, Attach, expects a resident interactive session (started by team-launch.sh) to pick the turn out of its inbox. LaunchResident spawns that same warm session itself in tmux. A headless per-turn claude -p is deliberately not the default, because cold boots and stateless turns defeat the resident-session model.
Is the run really reproducible? The journal is. Re-running the same score against the same run replays every recorded step. A changed binary is detected by a recorded score digest and warns loudly, and a patched("change-id") marker lets you upgrade a live run deliberately.
Where is the code? The eDSL is the h5i-orchestra crate; the design and rationale live in roadmap/orchestra-design.md in the repo.
Write your run as a program
Drive a full agent cycle with h5i team run, or write a score for a topology of your own. Every turn journaled, every run resumable.