crucible
Deterministic agent orchestration
Agents do the work. The engine selects the next task.
Crucible is a deterministic orchestrator for agent workflows. It runs agent turns in sandboxes. You declare the graph, and the engine schedules the tasks, isolates them, and records their cost.
A workflow is a graph of agent turns, commands, and checks. The executor selects the next task, not the agent. The selection rules are fixed, and you can read them before the run starts.
Deterministic
The engine runs the plan that you review
- Workflows compile to a static plan. Loops and functions unroll at compile time.
- The executor advances the plan. The dispatch order is stable, and the event stream is reproducible.
- Outcomes follow fixed rules. Transport failures retry. Measured failures do not retry. If a plan cannot run in full, the engine dispatches nothing.
- Every run has a cost limit and a time limit.
Sandboxed
The host keeps the credentials
- Agent turns run in OpenShell sandboxes with deny-by-default egress.
- Privileged operations go through a broker. The agent sends a request over MCP, and the host does the operation.
- A pack declares its egress and credentials.
crucible checklists them before the run starts. - Launch parameters go into prompts. They do not go into a command line.
Two lanes
The same engine runs two kinds of workflow.
Playbooks →
Run the graph once and ship the result, with a verdict that tells if every required task passed. The engine decides branches, review rounds, and fan-out at run time, and each has a limit.
Autoresearch →
Run the graph in a keep-or-discard loop against a frozen judge. The engine keeps a change only if it scores better than the best result so far. Git stores the accepted states.
A playbook, end to end
A pack is a directory that holds a crucible.toml, a workflow.star, and the scripts that
the tasks run. This pack reads a ticket, routes it to one queue, and files it:
read = command(name = "read", run = "./classify.sh", emits = ["bucket"])
gate = route(
name = "gate",
depends_on = [read],
source = read,
questions = {
"bucket": choice(
ask = "Which queue owns this ticket?",
options = ["outage", "billing", "feature"],
),
},
)
oncall = command(name = "oncall", run = "./file.sh oncall", depends_on = [gate],
when = gate.bucket, answers = "outage")
finance = command(name = "finance", run = "./file.sh finance", depends_on = [gate],
when = gate.bucket, answers = "billing")
backlog = command(name = "backlog", run = "./file.sh backlog", depends_on = [gate],
when = gate.bucket, otherwise = True)
filed = command(name = "filed", run = "./file.sh filed",
depends_on = [oncall, finance, backlog], join = "passed")
workflow(type = "playbook", tasks = [read, gate, oncall, finance, backlog, filed], result = filed)
$ crucible plan run --manifest crucible.toml --max-cost 1 --max-time 5m
read pass attempts=1 cost=$0.0000 out={"bucket":"billing"}
gate pass attempts=1 cost=$0.0000 out={"bucket":{"confidence":1.0,"label":"billing",...}}
oncall not_taken attempts=0 cost=$0.0000 (gate.bucket resolved to billing)
finance pass attempts=1 cost=$0.0000 out={}
backlog not_taken attempts=0 cost=$0.0000 (gate.bucket resolved to billing)
filed pass attempts=1 cost=$0.0000 out={}
plan v1: completed — spent $0.0000 of $1
verdict: valid
The branches that no answer selects settle not_taken. They do not dispatch and do not
spend. If you replace source = read with min_confidence = 0.8, a decision model answers
the question. The engine records a low-confidence answer as uncertain, and the otherwise
task takes it.
Where to go next
Your first playbook →
Write a pack, run it with no model, then point it at a real agent.
Branching and review →
Routes, revise loops, fan-out, sessions, joins, and advisory tasks.
DSL reference →
Every constructor and argument, generated from the compiler.
The autoresearch loop →
Propose, apply, measure, keep or discard, against a frozen judge.
The control plane →
A UI, an API, and a CLI to launch and monitor runs. One command starts it on a laptop.
The contract →
The frozen interface between the engine and a domain.
The pieces
| Crate | What it is |
|---|---|
crucible/ | The engine: the Starlark compiler, the plan executor, the autoresearch loop, agent backends, and crucible deploy. |
crucible-controller/ | The control plane: a Postgres ledger, the reconciling daemon, the HTTP API, and the embedded UI. |
crux/ | The CLI and MCP tools over the controller API. |
crucible-broker/ | The broker. The host keeps the credentials, and the sandboxed agent sends requests over MCP. |
forge/ | Image builds, registry, and deployment support. |