The learning loop
Decomposition without feedback is scattering. Forge's work tree is a call stack: every level returns a judged result to whatever called it.
The return path
A plan-mode agent turns an ask into a batch of tasks with dependency edges — and every batch ends in a continuation: a supervise-mode work, blocked until every member reaches a terminal state, failure included. Failure especially: repair is the point.
The supervisor's prompt was frozen before the batch ran, so it learns what actually happened at call time, through a tool that returns every child's state, result summary, and cost. It inspects the repository the batch produced — the code, not the reports — and returns a required verdict:
"assessment": {
"outcome": "done" | "revise",
"scores": { "correctness": 4, "completeness": 3, "quality": 4,
"effort_fit": 5, "overall": 4 },
"weakness": "the map view bundles no offline tiles"
}
revise ships corrective tasks — narrow deltas, not batch re-runs —
which fan out as the next round with a fresh continuation. The supervisor never edits
code itself: a reviewer that patches what it scores isn't a reviewer.
verify · merge · one fails
outcome=revise
weakness named
in facts, forever
Bounded recursion
- Rounds: revisions per subtree are capped (default 3); the final round's prompt forbids revising — report honestly and name what remains.
- Nesting: a planned task can itself be a plan, capped in depth (default 2); past the cap it demotes to a plain task instead of failing anything.
- Release ordering: when a nested batch spawns, everything waiting on its parent is re-chained onto the new continuation — an outer supervisor cannot release before the inner subtree settles, transitively, across rounds.
- Settlement: a task blocked on a failed sibling would otherwise wedge open forever and silently starve the supervisor. A settlement cascade cancels wedged members so the repair path always fires. (Caught in design review, not production — the best place to catch a deadlock.)
Outcomes are data
Every attempt already lands a facts row: phases, tokens, turns, dollar cost,
verification outcome. The loop added its dimensions: the tree's root (so any ask
rolls up in one query), the size bucket (S/M/L, assigned by the
submitter or the planner and trusted), and the five scores. Two readouts fall
out:
- Size calibration — per bucket: runs, verified rate, cost per run, turns, mean score. Whether "S actually costs S" is a table, not an argument.
- The per-ask rollup — one view per tree: total cost, tokens, outcome census, wall time, and the newest supervisor verdict with its named weakness.
Memory at three levels
| Level | Write-back |
|---|---|
| Repository | Supervisors and retros record knowledge-base notes — versioned with the code they describe — when a round exposes a systemic lesson. |
| Library | A standing reflection routine reads the stats and the recent failures, picks the single worst-performing directive, and sharpens it — through the ordinary merge pipeline, with the evidence in the commit message. |
| Calibration | Scores × size × cost accumulate into the tables the next triage reads. |
The benchmark is the eval
The standard: recursively learn, or it's an expensive abstraction layer. A standing spec — rebuild a full application from an empty repository, hard constraints included — runs through the entire loop:
$ forge bench run rebuild-equitizr $ forge bench list rebuild-equitizr STARTED WORK STATE SCORE COST WALL WORKS WEAKNESS 2026-09-04 20:35 248e988b running - $… - … …
Each run is one row: overall score, dollar cost, wall time, task count, the biggest remaining weakness. The chart over rows is the honest answer to the only question that matters about a self-improving system: is it actually getting better, per dollar, over time?