diff --git a/README.md b/README.md
index 1a341580..57c3c8ce 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,19 @@
# crucible
-Crucible is an engine for running goal-directed optimization loops against a codebase or
-other reversible system. An agent proposes a change, a domain-provided judge measures it,
-and the engine either keeps the candidate or restores the last accepted state.
+Crucible runs agent work as a declared graph of tasks. Agent turns, commands, evaluations
+and engine-owned decisions are written in Starlark, compiled into a static plan, and
+executed against one Git workspace under a cost budget.
+
+The graph is not a DAG at runtime. A `route()` task answers typed questions about its
+dependencies' output and the tasks downstream of it branch on the answers; a reviewer can
+send its proposer back with a verdict for a bounded number of rounds until the work stops
+failing. Neither is decided at compile time.
+
+There are two lanes. A **playbook** runs the graph once and ships what it produced. An
+**autoresearch** run wraps the graph in an optimization loop: an agent proposes a change, a
+domain-provided judge measures it, and the engine either keeps the candidate or restores
+the last accepted state.
@@ -17,7 +27,13 @@ use any language; no Rust integration is required.
## Execution model
-For each run, Crucible:
+A playbook run compiles the plan, admits it against the capabilities the substrate offers,
+and executes its tasks in dependency order in one workspace: branches settle `not_taken`
+without spending, revise pairs run their rounds, and the run ends when the result task
+settles or the budget is gone.
+
+A scored (autoresearch) run puts that graph inside the keep-or-discard loop. For each
+iteration, Crucible:
1. prepares a Git workspace from `[repo]` and `[workspace]`;
2. records a baseline measurement unless `[judge].skip_baseline` is enabled;
@@ -54,17 +70,19 @@ on top of the runtime image.
-## Why not a general workflow engine
+## Why not Argo or Tekton
Crucible executes its own graph rather than delegating to Argo Workflows, Tekton, or a
similar system. The reason is not the graph, which is ordinary, but the loop around it and
the state under it.
-The compiled plan is deliberately loop-free. Starlark control flow unrolls at compile time
-and the resulting plan is static; the only loop is the engine's iteration over that plan,
-which carries the accepted state, the best score so far, and the remaining budget across
-rounds. A workflow engine would have to host that loop, and neither hosts it well: Tekton
-has no loop construct at all, since a pipeline cannot reference itself and `matrix` covers
+The task set is fixed at compile time: Starlark control flow unrolls into tasks, and
+nothing creates a task during a run. Execution over that fixed set is cyclic. A `revise`
+pair re-dispatches a settled task with the reviewer's verdict under `revision`, round after
+round, until it stops failing or `max_rounds` is spent; a scored run repeats the whole graph
+across iterations, carrying the accepted state, the best score so far, and the remaining
+budget. A workflow engine would have to host both, and neither hosts them well: Tekton has
+no loop construct at all, since a pipeline cannot reference itself and `matrix` covers
fan-out only, and Argo expresses iteration as template recursion, which accumulates every
round's nodes in a single workflow object.
@@ -221,9 +239,10 @@ is a task: unsupervised general-purpose work where every completed turn is kept
### Workflow DSL
-A domain can define its iteration graph in a `workflow.star` file beside
-`crucible.toml`. The file uses a declarative subset of Starlark to create a directed acyclic
-graph of agent, command, evaluation, and engine tasks.
+A domain defines its graph in a `workflow.star` file beside `crucible.toml`. The file uses
+a declarative subset of Starlark to create a graph of agent, command, evaluation, and engine
+tasks. The dependency edges are acyclic; execution over them is not, since `revise` sends a
+task back for another round and `when` decides at runtime whether a branch runs at all.
`workflow.star` is authoring syntax. Crucible compiles it into the manifest's generated
`[workflow]` and `[[workflow.task]]` tables; the generated manifest data is the runtime
@@ -263,6 +282,55 @@ workflow(
)
```
+A playbook has no judge. It branches on a `route()` instead, and a task that settles failing
+can send a dependency back:
+
+```python
+read = command(name = "read", run = "./read.sh", emits = ["ticket"])
+
+gate = route(
+ name = "gate",
+ depends_on = [read],
+ min_confidence = 0.8,
+ questions = {
+ "bucket": choice(
+ ask = "Which queue owns this ticket?",
+ options = {
+ "outage": "the product is down or unusable",
+ "billing": "charges, invoices, refunds",
+ "feature": "a request for something new",
+ },
+ ),
+ },
+)
+
+oncall = command(name = "oncall", run = "./act.sh oncall", depends_on = [gate],
+ when = gate.bucket, answers = "outage")
+backlog = command(name = "backlog", run = "./act.sh backlog", depends_on = [gate],
+ when = gate.bucket, otherwise = True)
+
+filed = command(name = "filed", run = "./act.sh filed",
+ depends_on = [oncall, backlog], join = "passed")
+
+workflow(type = "playbook", tasks = [read, gate, oncall, backlog, filed], result = filed)
+```
+
+A branch no answer selected settles `not_taken`: it never dispatches, spends nothing, and
+does not invalidate the run, which is why `filed` rejoins on `join = "passed"`. `otherwise`
+covers every answer no other task claimed, so adding an option to the question cannot
+silently strand a ticket.
+
+The revise pair is the other runtime cycle. `review` re-runs `author` with its verdict under
+`revision` until it stops failing or the rounds are spent:
+
+```python
+author = agent(name = "author", prompt = "Write PROBE.md.", emits_files = ["PROBE.md"])
+review = command(name = "review", run = "./check.sh", depends_on = [author],
+ revise = author, max_rounds = 3)
+
+workflow(type = "playbook", tasks = [author, review])
+```
+
Every constructor, the lane it belongs to, and its keyword arguments are in
[docs/dsl-reference.md](docs/dsl-reference.md), which is generated from the compiler's own
tables. Print the same thing for the binary in hand:
@@ -294,9 +362,10 @@ Edit `workflow.star`, not the generated manifest block. `crucible scope` automat
recompiles a sibling `workflow.star` before validation and freeze. A materialized engine
workflow automatically selects graph execution when the loop runs.
-See [docs/work-graphs.md](docs/work-graphs.md) for plan execution and task output semantics,
-and [examples/counter/workflow.star](examples/counter/workflow.star) for a complete executable
-example.
+See [docs/work-graphs.md](docs/work-graphs.md) for plan execution and task output semantics.
+Complete executable examples: [examples/counter](examples/counter) (scored),
+[examples/route](examples/route) (branching) and [examples/revise-loop](examples/revise-loop)
+(bounded rounds).
### Measurement protocol
@@ -368,7 +437,10 @@ Common loop controls include `--iterations`, `--wide`, `--wide-keep`, `--max-cos
| Path | Contents |
| --- | --- |
-| `crucible/` | CLI, loop engine, manifest loading, agent backends, deployment rendering, and reporting. |
+| `crucible/` | CLI, plan executor, loop engine, manifest loading, agent backends, deployment rendering, and reporting. |
+| `crucible-controller/` | The control plane: Postgres ledger, reconciling daemon, HTTP API, and the embedded React UI. |
+| `crux/` | CLI and MCP tool library over the controller API. |
+| `capability/` | The image capability vocabulary the controller and the image feedstock share. |
| `crucible-contract/` | Shared wire types for events, identities, sessions, and artifacts. |
| `crucible-vcs/` | Git-backed workspace and history operations. |
| `crucible-harness/` | Harness stream processing and telemetry support. |