Adding a second coding agent feels like free velocity. Adding a tenth feels like a production incident. The difference is not the agents — it is whether you have a **loop**: a repeatable unit of work that an agent enters, is governed inside, and exits with provable output. Once you have that loop at the project level, the interesting move is realizing it is *fractal*: the same shape scales from one repository up to an entire organization without a second control system.
This is an end-to-end guide to implementing that loop properly in Nool, from a single project to a multi-level workspace.
## The unit of work is a loop, not a prompt
Most "multi-agent" setups are really just several chat sessions running in parallel against the same repository. They produce velocity and coordination debt at the same rate. The fix is to stop thinking in prompts and start thinking in a closed loop with four properties:
1. Each agent has a durable identity and mandate. 2. Agents declare what they will touch *before* they touch it. 3. No change enters the repository until it passes review. 4. Every step is recorded so the output can be measured later.
flowchart TD
G[Goal or intent] --> W[nool work start --parallel --teams]
W --> P[Souls pick up tasks]
P --> A[announce intent and lease region]
A --> C[Agent proposes a Knot]
C --> R{Review council}
R -- quorum, no veto --> S[solidify into the DAG]
R -- fail or veto --> X[replan with rationale]
X --> C
S --> M{More tasks?}
M -- yes --> P
M -- no --> K[checkpoint and measure]
Everything below is an expansion of this diagram, first at the project level, then lifted to the workspace.
## Step 1 — Give each agent a soul
A **soul** is a durable persona defined in `nool.toml`. It is not a system prompt you paste each session; it is a standing identity with a model, a charter, optional memory, and a budget. Souls are what turn "five identical agents racing on the same file" into "a small team with distinct mandates."
```toml [soul.architect] model = "opus" charter = "policies/souls/architect.md" memory = true budget_usd_per_day = 5.0
[soul.security] model = "opus" charter = "policies/souls/security.md" memory = true budget_usd_per_day = 3.0
[soul.implementer] model = "sonnet" charter = "policies/souls/implementer.md" memory = true budget_usd_per_day = 8.0 ```
The **charter** is a markdown file that fixes the persona's mandate, taste, and limits. It is injected on every run, so the architect keeps caring about boundaries and the security soul keeps looking for injection and secret leakage even after a hundred tasks. Setting `memory = true` lets a soul recall relevant findings and decisions from the project's vector index, so persona knowledge survives across sessions instead of resetting each prompt. The daily budget is a hard ceiling enforced by the telemetry ledger — a soul cannot quietly burn your month in an afternoon.
The rule of thumb: assign **complementary** souls. An architect, a security reviewer, and an implementer divide the work and, crucially, form a natural review board for each other's changes.
## Step 2 — Compose souls into a review council
Distinct souls only prevent collisions. To prevent *bad changes*, compose them into a council. A `[consortium]` is a multi-model review gate: several souls (or bare models) review a candidate change in parallel and vote.
```toml [consortium.review-board] members = ["soul:architect", "soul:security", "gpt"] quorum = 0.66 veto = ["soul:security"] budget_usd = 0.50 on_failure = "block" ```
Three properties make this trustworthy rather than theater:
- **Quorum** — a configurable fraction of members must approve. No single rubber stamp. - **Veto** — named members can block regardless of quorum. The security soul's "no" is final, even if everyone else approves. - **Budget** — a hard per-invocation cap. The council pre-flights its own cost and refuses to run if it would exceed the cap.
Every verdict is recorded as signed **Council Evidence**: each member's verdict, rationale, cost, and latency, plus the overall outcome (pass, fail, veto-deny, or budget-exceeded). That evidence is the difference between "the AI said it was fine" and "here is the proof, signed, of who approved this and why."
## Step 3 — Let agents coordinate before they write
The most expensive multi-agent failure is two agents silently editing the same surface and producing a clean Git merge that is semantically broken. Nool kills this at declaration time with **first-to-lease region locking**.
```bash # Each soul declares its target before generating code nool announce intent --intent "refactor auth middleware" --thread auth-hardening
# Check for overlap before committing tokens to the work nool discover conflicts ```
`announce intent` takes a **lease** over the paths an agent is about to touch. A second agent proposing into an overlapping region is rejected (Locked) *before it generates a single token*. This is arbitration at declaration time, not merge time — which means you stop paying for redundant work instead of discovering it during review.
To start a coordinated batch, use `work start`, which creates the intent and fans tasks out to parallel agents or teams:
```bash nool work start --intent "harden the billing subsystem" --parallel 3 --teams backend,security ```
## Step 4 — The loop: propose, review, solidify or replan
With souls defined, a council composed, and leases in place, the loop itself is mechanical:
```bash # An agent stages its change as a candidate Knot nool propose --all --intent "validate webhook signatures" --fast
# The council reviews before anything is sealed; on pass it solidifies nool solidify --full ```
On approval, the Knot enters the DAG and becomes part of the causal history. On failure, the agent receives the council's rationale and replans — the same loop runs again with better information. Bad changes never reach your repository state; they bounce off the gate with feedback attached. That self-correcting property is what makes the loop safe to run unattended across many agents.
A useful discipline: use `--fast` proposals for cheap, low-risk iteration and `solidify --full` when a change touches a high-stakes subsystem and you want the full semantic guarantees before it is sealed.
## Step 5 — Measure the output from the ledger
Because every step is recorded as a signed Knot, you can measure output without bolting on a separate analytics system. Three commands cover quality, cost, and reliability:
```bash nool insights justifications # why agents made each decision, preserved as rationale nool insights loops # detect thrashing where an agent is stuck retrying nool usage agent # token spend broken down per agent nool usage thread # cost per thread, against budget nool doctor --strict # first-pass success and release readiness ```
`nool insights` turns a pile of agent runs into an architectural ROI report: it surfaces the reasoning behind accepted changes, detects friction loops where a soul is thrashing on the same failing test, and quantifies time saved. `nool usage` breaks token spend down per agent and per thread and enforces budgets, so a runaway soul is caught early rather than on the invoice. And signed Council Evidence plus `nool doctor`'s first-pass success rate give you a verifiable answer to "did the output meet the bar?" — proof, not a vibe check.
If you only take one measurement habit from this: track **first-pass success rate** per soul over time. A soul whose charter is well-tuned trends upward; one that keeps getting vetoed is telling you its mandate or model is wrong.
## Scaling the loop: from project to workspace
Everything so far lives inside one project ledger. Real organizations have dozens of them, with dependencies between teams. The mistake is to build a second control plane — a management dashboard that drifts from delivery within a week. Nool instead scales the *same* loop fractally with a **workspace**.
A workspace is a read-down lens over a tree of independent Nool projects, discovered by convention from the folder layout: Org → Department → Team → Project. It has **no authoritative DAG of its own**. Child projects remain the single source of truth; the workspace only coordinates and reads downward.
flowchart TD ORG[Org workspace] --> D1[Department] ORG --> D2[Department] D1 --> T1[Team] D2 --> T2[Team] T1 --> P1[(Project ledger)] T1 --> P2[(Project ledger)] T2 --> P3[(Project ledger)] P1 --> L1[Souls loop here] P2 --> L2[Souls loop here] P3 --> L3[Souls loop here]
The key insight: the loop does not change shape as you go up a level. Each leaf project still runs souls, councils, leases, propose, and solidify exactly as before. The workspace adds three things on top — top-down actuation, bottom-up rollup, and dependency-ordered sync — without ever owning the work.
Top-down: decompose a goal into real child tasks
A workspace goal is not a ticket in a separate tool. `workspace goal --decompose` creates **real tasks inside the leaf ledgers that own delivery**. Aim it at a group target and the task fans out to every project beneath that node.
```bash # Fan a strategic goal into concrete tasks across every project under "backend" nool workspace goal --decompose backend="adopt signed webhooks everywhere"
# Roll completion of that goal back up the hierarchy nool workspace goal-status ```
Each project then runs its own loop against its new task. Managers see the exact work engineers execute, because it *is* the same work — there is no translation layer that can drift.
flowchart LR GOAL[workspace goal --decompose] --> FAN[Tasks created in child ledgers] FAN --> RUN[Each project runs its own soul loop] RUN --> UP[goal-status and doctor --recursive] UP --> ROLL[Worst-of health, summed insights]
Bottom-up: roll up health and insight honestly
The workspace reads downward to aggregate, and it does so *conservatively*:
```bash nool workspace doctor --recursive # release health, worst-of across all children nool workspace insights # operating insights, summed across projects nool workspace status # health plus git-branch drift at every level ```
`workspace doctor --recursive` rolls release health up with **worst-of** semantics: one blocked child keeps the parent view honest rather than averaging the problem away. `workspace insights` sums the per-project ROI and friction signals so leadership sees the whole fleet's output in one place — built from the same `nool insights` data each project already produces, not a parallel metric.
Dependency-ordered sync
Because projects depend on each other, naive parallel pulls break builds. The workspace pulls in dependency order:
```bash nool workspace pull --remote ```
Dependency edges, tracking branches, and aliases are declared durably in `workspace.toml`, kept separate from `nool.toml` so configuration round-trips never wipe them. The workspace knows that the billing project must reconcile before the storefront that consumes it.
## Putting it together
The discipline is the same at every altitude:
- **At the project level**, the loop is souls → lease → propose → council → solidify → measure. The output is signed Knots and Council Evidence. - **At the workspace level**, the loop is decompose → run child loops → roll up → sync. The output is goal completion and conservative release health, assembled from the children without a competing ledger.
You implement it once and inherit it everywhere. A new project that adopts the soul-and-council pattern automatically participates in workspace goals, health rollups, and insight aggregation, because the workspace reads the same ledger the project already keeps. There is no second system to keep in sync, and nothing that can quietly drift from what the agents actually did.
That is the whole point of doing the multi-agent loop properly: parallelism stops being a coordination tax and becomes leverage you can prove — from a single repository all the way up to the org.