@giamme/forge 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +106 -1766
  3. package/SKILL.md +3 -2
  4. package/VERSION +1 -1
  5. package/docs/README.md +26 -0
  6. package/docs/add-ons.md +33 -0
  7. package/docs/artifacts.md +69 -0
  8. package/docs/commands.md +315 -0
  9. package/docs/dataset.md +20 -0
  10. package/docs/decomposed-runs.md +268 -0
  11. package/docs/development.md +95 -0
  12. package/docs/forge-ui.md +92 -0
  13. package/docs/fractal.md +181 -0
  14. package/docs/install.md +160 -0
  15. package/docs/memory.md +156 -0
  16. package/docs/models.md +118 -0
  17. package/docs/reviews.md +231 -0
  18. package/docs/safety.md +55 -0
  19. package/docs/troubleshooting.md +54 -0
  20. package/forge +5 -0
  21. package/package.json +3 -2
  22. package/references/decompose.md +1 -1
  23. package/references/fractal.md +1 -1
  24. package/references/harnesses.md +18 -6
  25. package/references/solo.md +1 -1
  26. package/scripts/forge-dispatch.sh +7 -1
  27. package/scripts/forge-runs.py +121 -0
  28. package/scripts/forge-runtime.py +59 -6
  29. package/scripts/forge-version.py +54 -12
  30. package/scripts/forge_dataset/harvest.py +1 -1
  31. package/scripts/forge_dataset/importer.py +1 -1
  32. package/scripts/forge_fractal/__init__.py +2 -1
  33. package/ui/README.md +80 -20
  34. package/ui/THIRD_PARTY_NOTICES.md +108 -0
  35. package/ui/docs/api.md +99 -17
  36. package/ui/forge_ui/export.py +16 -6
  37. package/ui/forge_ui/fractal.py +1 -2
  38. package/ui/forge_ui/reader.py +95 -7
  39. package/ui/forge_ui/server.py +10 -1
  40. package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
  41. package/ui/forge_ui/static/app.js +279 -124
  42. package/ui/forge_ui/static/cloud3d.js +3 -2
  43. package/ui/forge_ui/static/cloudui.js +8 -5
  44. package/ui/forge_ui/static/convo.js +295 -0
  45. package/ui/forge_ui/static/datatable.js +232 -0
  46. package/ui/forge_ui/static/diffview.js +29 -10
  47. package/ui/forge_ui/static/findings3d.js +134 -0
  48. package/ui/forge_ui/static/graph.js +3 -3
  49. package/ui/forge_ui/static/icons.js +94 -0
  50. package/ui/forge_ui/static/index.html +25 -13
  51. package/ui/forge_ui/static/iso.js +70 -19
  52. package/ui/forge_ui/static/leaderboard.js +44 -48
  53. package/ui/forge_ui/static/panel.js +68 -32
  54. package/ui/forge_ui/static/runmodel.js +17 -1
  55. package/ui/forge_ui/static/scene3d.js +518 -43
  56. package/ui/forge_ui/static/search.js +2 -1
  57. package/ui/forge_ui/static/shell.js +137 -0
  58. package/ui/forge_ui/static/stagectl.js +150 -0
  59. package/ui/forge_ui/static/style.css +924 -469
  60. package/ui/forge_ui/static/theme.js +25 -0
  61. package/ui/forge_ui/transcripts.py +608 -0
package/README.md CHANGED
@@ -1,910 +1,144 @@
1
- # forge
2
-
3
- **One model builds it. A different model reviews what it actually built.**
4
-
5
- [![Latest release](https://img.shields.io/github/v/release/Giamme/forge?label=release)](https://github.com/Giamme/forge/releases)
6
- [![Changelog](https://img.shields.io/badge/changelog-what's%20new-blue)](CHANGELOG.md)
7
- [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
1
+ <h1 align="center">forge</h1>
8
2
 
9
3
  <p align="center">
10
- <img src="ui/docs/media/hero.webp" width="880" alt="A decomposed run in forge-ui's 3D view: eight tasks on a plate in their planned waves, five merged in green and two running in blue, then the same run on wall-clock time, where two first attempts that failed review show in red beside the retries that passed.">
4
+ <b>Multi-model coding with a reviewer that reads the code: plan, build in parallel worktrees, review every real diff, merge only what passes.</b>
11
5
  </p>
12
- <p align="center"><sub>The run from <a href="#quick-start">Quick start</a> in <a href="#forge-ui">forge-ui</a>: its tasks in the waves the plan gave them, then as they really ran, the failed attempts and their retries included. Demo data.</sub></p>
13
-
14
- `forge` is a skill for agent CLIs. You hand it a coding task and two models: a **dwarf**
15
- that implements the change, and a **qa** reviewer that reads the dwarf's real diff — not
16
- the dwarf's summary of its own work. A model grading its own homework isn't a check; the
17
- entire value of the second stage is that it sees the code rather than the claim.
18
-
19
- Either role can be any model, at any reasoning effort, through any of six agent CLIs:
20
-
21
- | | codex | claude | openclaude | opencode | antigravity | grok |
22
- |---|---|---|---|---|---|---|
23
- | binary | `codex` | `claude` | `openclaude` | `opencode` | `agy` | `grok` |
24
-
25
- So `--dwarf sol:xhigh --qa opus` runs GPT through Codex and Claude Opus through Claude
26
- Code, in one command, from whichever CLI you happen to be sitting in.
27
6
 
28
- With `--decompose-level` it goes wider: the goal is split into tasks, several dwarves work
29
- **concurrently in isolated git worktrees**, each task is reviewed on its own diff, and only
30
- the tasks that pass get merged.
31
-
32
- ```mermaid
33
- flowchart LR
34
- goal(["your goal"]) --> plan["plan: tasks and models,<br/>then you read the table"] --> dwarf["each task: a dwarf<br/>implements it in<br/>its own worktree"] --> qa["qa reads<br/>the real diff"]
35
- qa -. "FAIL: retry with<br/>the findings" .-> dwarf
36
- qa -- PASS --> merge["merged into<br/>integration"] --> deliver(["verification, final review,<br/>integrate --approved"])
37
- ```
7
+ <p align="center">A skill for Claude Code, Codex, OpenCode, OpenClaude, Antigravity and Grok.</p>
38
8
 
39
- Without `--decompose-level` the whole goal is one task: one dwarf, one review of its diff.
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@giamme/forge"><img src="https://img.shields.io/npm/v/@giamme/forge?label=npm" alt="npm version"></a>
11
+ <a href="https://github.com/Giamme/forge/releases"><img src="https://img.shields.io/github/v/release/Giamme/forge?label=release" alt="Latest release"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License: MIT"></a>
13
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-what's%20new-blue" alt="Changelog"></a>
14
+ </p>
40
15
 
41
- With optional [Fractal execution](#fractal-execution), an implementer can delegate bounded
42
- child tasks inside its owned paths. Forge still chooses the models, reviews the combined
43
- diff, verifies the result and controls integration. A local dashboard shows the task tree,
44
- logs and separate execution/acceptance states; HTML reports capture them for later review.
16
+ <p align="center">
17
+ <a href="#install">Install</a> ·
18
+ <a href="#quick-start">Quick start</a> ·
19
+ <a href="#how-it-works">How it works</a> ·
20
+ <a href="#forge-ui">forge-ui</a> ·
21
+ <a href="docs/README.md">Docs</a> ·
22
+ <a href="CHANGELOG.md">Changelog</a>
23
+ </p>
45
24
 
46
- [forge-ui](#forge-ui), its opt-in companion, is a read-only local page that watches any run
47
- live, drawn as a 3D map in archivision's style: every run on one plate, tasks in their waves
48
- and as they really ran, the final review as a node, and the prompt, model, timings and P0–P3
49
- findings behind every step. It also floats every run in a full-screen 3D cloud, shows diffs
50
- with each review finding under its line, and ranks the models on your own runs. forge offers
51
- it at the start of every new run and never opens it without a yes.
25
+ <p align="center">
26
+ <img src="ui/docs/media/hero.webp" width="880" alt="A decomposed run in forge-ui's 3D view: eight tasks on a plate in their planned waves, five merged in green and two running in amber, with a mark over each block for every review finding, then the same run on wall-clock time, where two first attempts that failed review show in red beside the retries that passed.">
27
+ </p>
28
+ <p align="center"><sub>The run from <a href="#quick-start">Quick start</a> in <a href="#forge-ui">forge-ui</a>: its tasks in the waves the plan gave them, then as they really ran, the failed attempts and their retries included. Demo data.</sub></p>
52
29
 
53
30
  ---
54
31
 
55
- ## Contents
56
-
57
- - [Install](#install)
58
- - [Quick start](#quick-start)
59
- - [Fractal execution](#fractal-execution)
60
- - [The spec: `alias:effort:harness`](#the-spec-aliaseffortharness)
61
- - [Effort, ceilings and clamping](#effort-ceilings-and-clamping)
62
- - [Yolo mode](#yolo-mode)
63
- - [Decomposed runs](#decomposed-runs)
64
- - [Planning](#planning)
65
- - [Project memory](#project-memory)
66
- - [Run dataset](#run-dataset)
67
- - [forge-ui](#forge-ui)
68
- - [Full command reference](#full-command-reference)
69
- - [Model registry](#model-registry)
70
- - [Run artifacts](#run-artifacts)
71
- - [Safety guarantees](#safety-guarantees)
72
- - [Troubleshooting](#troubleshooting)
73
- - [Repository layout](#repository-layout)
74
- - [License](#license)
75
-
76
- ---
32
+ You hand forge a coding goal and pick the models. **Dwarves** implement it. A **qa** reviewer
33
+ reads each dwarf's real diff, never the dwarf's summary of its own work. A model grading its own
34
+ homework isn't a check: the second stage is worth having because it sees the code, not the claim.
35
+
36
+ - **Any model, any CLI.** `--dwarf sol:xhigh --qa opus` runs GPT through Codex and Claude Opus
37
+ through Claude Code in one command, from whichever CLI you are in. → [Models](docs/models.md)
38
+ - **Parallel when you want it.** `--decompose-level` splits the goal into tasks, routes each to a
39
+ model by difficulty, and runs them concurrently in isolated git worktrees.
40
+ → [Decomposed runs](docs/decomposed-runs.md)
41
+ - **Only reviewed work merges.** Every task is reviewed on its own diff. A failed task keeps its
42
+ branch for a retry with the reviewer's findings, and only passing tasks merge.
43
+ → [Reviews](docs/reviews.md)
44
+ - **You decide what gets spent and what lands.** A decomposed run shows you the task table and
45
+ stops before any dispatch. Solo changes stay uncommitted, decomposed work lands on your branch
46
+ only through `integrate --approved`, and forge never pushes. → [Safety](docs/safety.md)
47
+ - **It learns your repo.** What a run discovers goes into a small `.forge/memory.md`, and every
48
+ later run is given it. → [Project memory](docs/memory.md)
49
+ - **You can watch it live.** forge-ui draws every run in 3D, with diffs, findings and a model
50
+ leaderboard. → [forge-ui](docs/forge-ui.md)
77
51
 
78
52
  ## Install
79
53
 
80
- forge runs on macOS and Linux. It needs bash, git and Python 3.9 or later, plus the agent
81
- CLIs you route models through; the doctor below shows which ones it finds. Installing from
82
- npm brings none of these with it.
83
-
84
- ### From npm
85
-
86
54
  ```bash
87
55
  npm install -g @giamme/forge
88
- forge install
89
- ```
90
-
91
- `forge install` links the installed package into each harness, as the installer does for a
92
- checkout below. It is a step of its own rather than an npm install script, so it works where
93
- scripts are turned off (`--ignore-scripts`). To update:
94
-
95
- ```bash
96
- npm install -g @giamme/forge@latest && forge install
56
+ forge install # links forge into every agent CLI it finds
97
57
  ```
98
58
 
99
- The links point into npm's global package directory, so they keep working across updates;
100
- `forge install` refreshes antigravity's copied plugin. Under nvm, that directory belongs to
101
- one Node version: after switching Node, install forge on the new one and run `forge install`
102
- again.
103
-
104
- > **Foundry users:** Foundry's `forge` has the same name, and whichever directory comes first
105
- > on your `PATH` wins; npm's global bin usually comes first. Runs start from your harness
106
- > (`/forge`), so this command is only for `forge install` and the optional tools. If you keep
107
- > Foundry's `forge` first, run ours as `"$(npm root -g)/@giamme/forge/forge"`.
108
-
109
- ### From a git checkout
59
+ Update with `npm install -g @giamme/forge@latest && forge install`. Or install from a git
60
+ checkout:
110
61
 
111
62
  ```bash
112
63
  git clone https://github.com/Giamme/forge.git ~/.claude/skills/forge
113
64
  bash ~/.claude/skills/forge/scripts/forge-install.sh
114
65
  ```
115
66
 
116
- Clone anywhere you like — the installer works out its own location and links from there.
117
- `~/.claude/skills/forge` is just the tidiest home: claude reads that path directly, and
118
- opencode and grok scan it on their own.
119
-
120
- The installer also offers optional Fractal support in an interactive terminal. Declining
121
- or a Fractal installation failure does not prevent ordinary Forge setup. See
122
- [Fractal installation](#install-the-optional-runtime) for prerequisites and a separate install.
123
-
124
- Then check what's actually reachable on your machine:
125
-
126
- ```bash
127
- bash ~/.claude/skills/forge/scripts/forge-dispatch.sh doctor
128
- ```
129
-
130
- ```
131
- HARNESS STATUS DETAIL
132
- codex found /Users/you/.nvm/versions/node/v24.18.0/bin/codex
133
- claude found /Users/you/.local/bin/claude
134
- openclaude found /Users/you/.nvm/versions/node/v24.18.0/bin/openclaude
135
- opencode found /Users/you/.opencode/bin/opencode
136
- antigravity MISSING agy not on PATH
137
- grok found /Users/you/.grok/bin/grok
138
- ```
139
-
140
- A `MISSING` harness is fine — forge only needs the ones you actually route models through.
141
-
142
- **Binary presence is not reachability.** A harness can be installed and still fail on auth or
143
- billing (opencode in particular fails this way), and that failure only surfaces mid-dispatch.
144
- To prove a pairing end to end before spending a real run:
145
-
146
- ```bash
147
- bash scripts/forge-dispatch.sh dwarf <spec> --prompt-file /dev/stdin --repo <scratch dir> \
148
- <<< 'Reply with exactly: FORGE_OK'
149
- ```
150
-
151
- ### What the installer does per harness
152
-
153
- Each CLI discovers skills differently, and the differences are not guessable — so verify
154
- with the listed check rather than trusting file placement.
155
-
156
- | Harness | Mechanism | Verify with |
157
- |---|---|---|
158
- | claude | `~/.claude/skills/forge/` | `/forge` appears in the skill list |
159
- | openclaude | symlink into `~/.openclaude/skills/` | `openclaude skills list --json` |
160
- | codex | symlink into **`~/.codex/skills/`** | `codex exec "do you have a skill named forge?"` |
161
- | opencode | **nothing** — it scans `~/.claude/` itself | `opencode debug skill` |
162
- | antigravity | `agy plugin install` of a generated wrapper | `agy --print='/forge'` |
163
-
164
- > **The one ongoing caveat:** `agy plugin install` *copies* the files instead of referencing
165
- > them. The other four track the source directory live, so an edit reaches them instantly —
166
- > antigravity keeps running whatever it copied. **Re-run `forge-install.sh` after any edit**,
167
- > or agy silently runs a stale version.
168
-
169
- `forge-install.sh --dry-run` prints what it would do and touches nothing. It never clobbers
170
- a real directory that isn't a symlink.
171
-
172
- ### Updating all harness installations
173
-
174
- Run the updater from your Forge checkout (or through one of its skill symlinks):
175
-
176
- ```bash
177
- ./forge update # same as: bash ~/.claude/skills/forge/scripts/forge-update.sh
178
- ./forge version # VERSION, checkout commit, tracked upstream, last known release
179
- ./forge version --check # ask the upstream for its newest release now
180
- ```
181
-
182
- It fetches the current branch's configured upstream, fast-forwards the checkout, and
183
- runs Forge's installer to refresh Claude, OpenClaude and Codex links, OpenCode discovery,
184
- and Antigravity's copied plugin. Harnesses that are not present are skipped. Restart
185
- active harness sessions afterward to load the updated skill.
186
-
187
- ```bash
188
- # Preview without fetching or changing files:
189
- bash <forge-checkout>/scripts/forge-update.sh --dry-run
190
-
191
- # Refresh harness installations after local edits, without fetching:
192
- bash <forge-checkout>/scripts/forge-update.sh --local
193
-
194
- # Point a copied installation's updater at the original checkout:
195
- bash <path>/scripts/forge-update.sh --source <forge-checkout>
196
- ```
197
-
198
- When a solo run, `plan` or `run` finishes, forge checks the checkout's upstream for a newer
199
- release tag at most once a day (`git ls-remote`, 5-second timeout, cached in
200
- `${XDG_STATE_HOME:-~/.local/state}/forge/update-check.json`) and, if there is one, prints a
201
- single line on stderr: `forge 0.8 is available (you have 0.7.1): forge update`. The check is
202
- silent offline, in a copied installation without Git, in CI (`CI` set) and with
203
- `FORGE_UPDATE_CHECK=off`, and it never changes a run's outcome or exit code.
204
-
205
- The default update requires a clean Git checkout and a branch with an upstream. Local
206
- changes, detached HEAD, fetch failures and diverged branches stop the update without
207
- resetting or stashing your work. `--local` deliberately allows current local edits.
208
- Real skill directories are preserved; a conflicting directory or plugin installation
209
- failure produces a nonzero exit and an explanation. After fixing an installation
210
- failure, use `--local` to retry from the updated source. Updating Forge does not prompt
211
- for optional Ripwire installation.
67
+ forge runs on macOS and Linux. It needs bash, git, Python 3.9 or later, and the agent CLIs
68
+ you route models through; [the doctor](docs/install.md#check-what-forge-can-reach) shows which
69
+ ones it can reach. Foundry's `forge` shares the name: see
70
+ [the Foundry note](docs/install.md#from-npm).
212
71
 
213
- An npm installation is not a Git checkout, so the updater stops there and prints the npm
214
- command [above](#from-npm) instead. `--local` still refreshes its harness installations.
215
-
216
- ---
72
+ → [Install and update](docs/install.md)
217
73
 
218
74
  ## Quick start
219
75
 
220
- Inside any installed harness, invoke the skill:
76
+ Inside any agent CLI where forge is installed:
221
77
 
222
- ```
78
+ ```text
223
79
  /forge "add retry with exponential backoff to the HTTP client" --dwarf sol:high
224
80
  ```
225
81
 
226
- That's the whole minimum. `sol` builds it at `high` effort on Codex, `opus` reviews the
227
- resulting diff at `xhigh` (the default reviewer), and you get back a summary of the change
228
- plus qa's findings.
229
- Before an interactive run, Forge asks **“Use Fractal for this run? [y/N]”** once.
230
- Pass `--fractal` or `--no-fractal` to answer explicitly; unattended runs default off.
231
- It also asks **“Open forge-ui to watch this run? [y/N]”** (`--ui` / `--no-ui`); on a yes,
232
- the [forge-ui](#forge-ui) page opens as soon as the run exists. Resume and retry never ask.
82
+ That's the whole minimum. `sol` builds it at `high` effort on Codex, `opus` reviews the diff at
83
+ `xhigh` (the default reviewer), and you get back a summary of the change plus qa's findings.
233
84
 
234
- **Pick both sides explicitly:**
235
-
236
- ```
85
+ ```text
86
+ # pick both sides
237
87
  /forge "fix the off-by-one in the pagination cursor" --dwarf luna:max --qa sol:xhigh
238
- ```
239
88
 
240
- **Send a model through a different CLI** — same model, different harness:
241
-
242
- ```
89
+ # the same model, through a different CLI
243
90
  /forge "port the config loader to TOML" --dwarf sol:high:openclaude --qa opus
244
- ```
245
-
246
- **Let the dwarf off the leash** so it can install deps and run the suite:
247
91
 
248
- ```
92
+ # let the dwarf leave the sandbox to install deps and run the suite
249
93
  /forge "add integration tests against a real Postgres container" --dwarf sol:xhigh --yolo-dwarf
250
- ```
251
94
 
252
- **Split it across several dwarves working in parallel:**
253
-
254
- ```
95
+ # several dwarves in parallel, routed by difficulty
255
96
  /forge "add auth: middleware, token model, login route, and docs" \
256
97
  --decompose-level medium \
257
98
  --dwarf-high sol:xhigh --dwarf-low gemini:low:antigravity --dwarf luna:high
258
99
  ```
259
100
 
260
- forge decomposes the goal, rates each task's difficulty, routes it, then **shows you the
261
- table and stops** before spending anything.
262
-
263
- ---
264
-
265
- ## Fractal execution
266
-
267
- Fractal is an optional implementation backend for both solo and decomposed runs. It uses
268
- the existing Forge dispatcher for Codex, Claude, OpenClaude, OpenCode and Antigravity.
269
- Each work step gets a fresh model invocation; preserved files and child results provide
270
- continuity when a parent resumes. Independent QA reviews the complete Forge task diff.
271
-
272
- `--decompose-level` plans top-level Forge tasks before execution. `--fractal` lets an
273
- implementer request nested children during execution. They can be used together, and
274
- solo runs can use Fractal without a top-level decomposition.
275
-
276
- ### Install the optional runtime
277
-
278
- From the Forge checkout:
279
-
280
- ```bash
281
- ./forge fractal install --dry-run
282
- ./forge fractal install --yes --with-prerequisites
283
- ./forge fractal doctor --spec sol --spec opus --json
284
- ```
285
-
286
- The managed runtime pins **Fractal 1.2.0** at revision
287
- `18793200c0d7e8cdb2db369ea3abe5647a1e15e4`, checks the source archive checksum,
288
- and includes the required `wiki` executable. It prefers an existing Python 3.12–3.14;
289
- with prerequisite installation selected, it can provision uv, Python 3.13 and tmux.
290
- tmux installation uses an existing Homebrew on macOS or apt on Debian/Ubuntu.
291
- The [installation reference](references/fractal.md#installation-and-provenance) covers
292
- consent, supported bootstrap platforms, provenance and failure diagnostics.
293
-
294
- Installation never enables Fractal for a run. `./forge` is the repository-local launcher,
295
- the same one npm puts on your `PATH` as `forge`; installing Fractal does not add a command
296
- or change your shell startup files.
297
- `./forge --help` lists its commands.
298
- Doctor checks the runtime and advertised harness capabilities without calling a provider.
299
-
300
- ### Start and inspect a run
301
-
302
- Through the skill:
303
-
304
- ```text
305
- /forge "add retry with exponential backoff to the HTTP client" --dwarf sol:high --fractal
306
- ```
307
-
308
- Or prepare a solo run directory outside the product repository and invoke the runner:
309
-
310
- ```bash
311
- FORGE_RUN="$HOME/.local/state/forge/examples/http-client"
312
- mkdir -p "$FORGE_RUN"
313
- printf '%s\n' 'Add bounded exponential backoff to the HTTP client and run its tests.' > "$FORGE_RUN/prompt.md"
314
- bash scripts/forge-solo.sh "$FORGE_RUN" --repo /absolute/path/to/product \
315
- --dwarf sol:high --qa opus --fractal --fractal-concurrency 3
316
- ```
317
-
318
- Choose a new run directory for unrelated work. Backend selection, routing pools and limits
319
- are stored with the run; resume and explicit retry retain them. For a prepared, approved
320
- decomposed plan, select the backend on `run`:
321
-
322
- ```bash
323
- bash scripts/forge-parallel.sh run /absolute/path/to/plan --fractal --dry-run
324
- bash scripts/forge-parallel.sh run /absolute/path/to/plan --fractal
325
- ```
326
-
327
- Dry runs show resolved model pools, limits and proposed workspaces without installation
328
- or provider calls. When a selected runtime is unavailable, interactive runs offer
329
- installation; unattended runs return an actionable error instead of changing backends.
330
-
331
- Discover the managed run ID, then inspect it from another terminal:
332
-
333
- ```bash
334
- ./forge fractal runs --repo /absolute/path/to/product --json
335
- ./forge fractal open # live runs index
336
- ./forge fractal status RUN_ID --json
337
- ./forge fractal tree RUN_ID --json
338
- ./forge fractal logs RUN_ID --follow
339
- ./forge fractal report RUN_ID --html /absolute/path/to/report.html
340
- ```
341
-
342
- `RUN_ID` is the `run-…` identifier returned by `runs`, not the solo directory or plan name.
343
- The [run inspector](references/fractal-dashboard.md) opens on progress and problems, then lets
344
- readers inspect task QA/history or node decisions, logs and changes. It refreshes about every
345
- two seconds without replacing the workspace; search, selection, focus and scrolling remain in
346
- place. “Pause live updates” affects the browser only. Browser access is read-only, bound to
347
- `127.0.0.1` with an ephemeral access token. Closing its server does not stop execution.
348
- Portable HTML embeds complete captured history and logs, works offline and needs no Fractal
349
- runtime to read.
350
-
351
- ### Automatic recursive decomposition
352
-
353
- `--fractal` permits workers to request children. To require a planning decision before
354
- every eligible node implements, use `--fractal-auto-decompose`:
355
-
356
- ```text
357
- /forge "<goal>" --dwarf luna:high --qa sol:high --fractal-auto-decompose --fractal-planner sol:xhigh --fractal-depth 3 --fractal-nodes 24
358
- ```
359
-
360
- After the top-level Forge approval, the planner automatically splits useful work or
361
- records why it is atomic. Children repeat this decision within the approved ownership
362
- and frozen limits. `--fractal-planner` selects a separate decision model; when omitted,
363
- Forge reuses the configured top-level planner, then the task's initial root model.
364
- Decisions use disposable snapshots, persist for resume, and appear in tree/status/HTML
365
- reports. Ordinary `--fractal` and existing runs retain their current behavior. Start a
366
- new run to change policy or planner. See the [full decision contract](references/fractal.md#automatic-decomposition-decisions).
367
-
368
- ### Bounds, recovery and acceptance
369
-
370
- Defaults per Forge task are two nesting levels below implementation, three unsettled
371
- direct children per node, twelve lifetime nodes, six iterations per node per attempt,
372
- and a 45-minute attempt deadline excluding explicit pauses. Three model slots are shared
373
- across the whole Forge run, including QA. `--max-parallel` bounds top-level task pipelines;
374
- `--fractal-concurrency` bounds model invocations across their nested trees.
375
-
376
- Child difficulty selects a frozen `--dwarf-low`, `--dwarf-medium` or `--dwarf-high` pool,
377
- falling back to the configured general dwarf, then the parent. Dependencies and owned
378
- paths constrain admission; overlapping ownership is serialized. Fractal never implies
379
- `--yolo-dwarf`. Costs are observational; unavailable usage stays unknown and dollar-cap
380
- requests are rejected.
381
-
382
- ```bash
383
- ./forge fractal pause RUN_ID
384
- ./forge fractal resume RUN_ID
385
- ./forge fractal stop RUN_ID
386
- ```
387
-
388
- Pause and stop preserve work and diagnostics. Run-level resume recovers recorded execution
389
- and the Forge pipeline without repeating accepted stages. Use explicit runner retry for
390
- a new attempt, retaining work and the original Forge QA baseline. Controls also accept
391
- `--task TASK_ID` and `--task TASK_ID --node NODE_ID`; those IDs come from `tree` output.
392
-
393
- Managed data lives in `${XDG_STATE_HOME:-$HOME/.local/state}/forge/fractal/`. Solo execution
394
- uses an isolated snapshot and imports changes only after checking source drift, preserving
395
- staging and leaving changes uncommitted. Decomposed candidates return to Forge's existing
396
- review/integration pipeline. **Fractal completion is not Forge acceptance**: QA, repository
397
- verification and authorization to update the user's branch remain distinct.
398
-
399
- See the [full Fractal reference](references/fractal.md) for limits, child requests and
400
- recovery details, and [verification evidence](tests/fractal-verification.md) for the tested
401
- platforms and browser checks. No paid-provider or speed/cost claim follows from fake-CLI tests.
402
-
403
- ## The spec: `alias:effort:harness`
404
-
405
- Everywhere forge takes a model, it takes the same three-field spec. Each field after the
406
- first is optional:
407
-
408
- | Spec | Resolves to |
409
- |---|---|
410
- | `sol` | alias `sol`, default effort for the role, its default harness (codex) |
411
- | `sol:xhigh` | `sol` at `xhigh` effort on its default harness |
412
- | `sol:xhigh:openclaude` | same model and effort, run through the `openclaude` CLI |
413
- | `opus::claude` | explicit harness, effort left to the role default — the middle field may be empty |
414
- | `gpt-5.6-sol::codex` | not in the registry? passed through as a literal model id — but only with an explicit harness |
415
-
416
- An alias with no registry row still works, provided you name the harness. forge won't guess
417
- a default harness for a model it has never seen; that guess would silently spend quota on
418
- the wrong provider.
419
-
420
- **Role defaults when you leave things out:**
421
-
422
- - `--dwarf` missing → forge **asks**. Every dispatch spends real quota on your account, so
423
- picking for you isn't its call.
424
- - `--qa` missing → `opus`, which runs locally through `claude` and adds no cost surprise.
425
- - effort missing → dwarf builds at `medium`, qa reviews at `xhigh`. A reviewer thinking
426
- *less* than the implementer tends to wave things through, which wastes the stage entirely.
427
-
428
- ---
429
-
430
- ## Effort, ceilings and clamping
431
-
432
- Every harness has its own effort ladder:
433
-
434
- | Harness | Ladder |
435
- |---|---|
436
- | codex | `low medium high xhigh max ultra` |
437
- | claude | `low medium high xhigh max` |
438
- | openclaude | `low medium high xhigh max ultracode` |
439
- | antigravity | `low medium high` |
440
- | grok | `low medium high xhigh max` (grok-4.7 offers up to `xhigh`) |
441
- | opencode | provider-specific; forwarded verbatim |
442
-
443
- And support is more irregular than one ceiling per backend:
444
-
445
- - **Ceilings** — `luna` tops out at `max` on codex, so `luna:ultra` clamps down.
446
- - **Gaps** — `gemini-pro` offers `low` and `high` but *not* `medium`, so `gemini-pro:medium`
447
- clamps to `low`.
448
- - **None at all** — Claude models on antigravity take no effort flag; passing one errors, so
449
- forge reports it and drops it.
450
-
451
- When forge clamps, it says so (`clamped=ultra -> max`) and the orchestrator passes that on
452
- to you. Getting less thinking than you paid for, silently, is exactly the failure worth ten
453
- lines of validation to prevent — and it's a real one: **codex accepts an invalid effort
454
- without complaint and quietly runs at some other level.** forge validates first.
455
-
456
- A word that isn't a recognized effort at all is an error, not a clamp.
457
-
458
- ---
459
-
460
- ## Yolo mode
461
-
462
- `--yolo-dwarf` and `--yolo-qa` drop that role's sandbox and approval gates:
463
-
464
- | Harness | Flag used |
465
- |---|---|
466
- | codex | `--dangerously-bypass-approvals-and-sandbox` |
467
- | claude / openclaude / antigravity | `--dangerously-skip-permissions` |
468
- | opencode | `--auto` |
469
- | grok | `--always-approve` |
470
-
471
- Each flag applies to **one role only**, so `--yolo-dwarf` alone is the normal combination:
472
- the implementer gets a free hand, the reviewer stays fenced in and still can't edit the code
473
- it's judging.
474
-
475
- You mostly don't need it. On codex, claude and openclaude the sandboxed roles are already
476
- useful — the dwarf edits files and runs tests, qa reads and runs commands but cannot write.
477
- Reach for yolo when a role genuinely must escape the sandbox: installing dependencies,
478
- writing outside the repo, touching the network.
479
-
480
- > **Antigravity is the exception worth knowing.** Without yolo it cannot run *any* shell
481
- > command, and a single denied command **aborts the whole run** rather than degrading — a
482
- > dwarf that tries to run the test suite ends with an error and an empty diff. forge detects
483
- > this and appends a note telling that role not to attempt commands, which is why an
484
- > antigravity dwarf reports its work as unverified by execution. If you want a Gemini dwarf
485
- > that can actually run the tests it writes, it needs `--yolo-dwarf`.
486
-
487
- ---
488
-
489
- ## Decomposed runs
490
-
491
- `--decompose-level low|medium|high` turns one dwarf into many. Without the flag, nothing
492
- changes — one dwarf, one QA pass.
493
-
494
- ### Two independent axes
495
-
496
- Easy to conflate, so: **decompose-level** is how finely the *goal* is split. **difficulty**
497
- is how hard each resulting *task* is.
498
-
499
- | `--decompose-level` | Split |
500
- |---|---|
501
- | `low` | only where the goal has obviously independent pieces; 2–3 coarse tasks |
502
- | `medium` | one task per coherent unit (module, feature slice); typically 4–6 |
503
- | `high` | finest split where each task is still independently reviewable *and* independently correct; typically 7–12 |
504
-
505
- | `difficulty` | Means |
506
- |---|---|
507
- | `low` | mechanical and local: rename, move, docs, a test for behaviour that already exists |
508
- | `medium` | self-contained implementation against a clear spec; bounded blast radius |
509
- | `high` | needs design judgment, cross-cutting behaviour, subtle edge cases, state or concurrency |
510
-
511
- Difficulty is about **judgment required, not diff size**. A five-hundred-line mechanical
512
- rename is `low`. A ten-line concurrency fix is `high`. Getting this backwards sends the cheap
513
- model at the subtle problem and the expensive one at the boilerplate — worse than not routing
514
- at all.
515
-
516
- ### Routing dwarves to tasks
517
-
518
- Per-task assignment can't be expressed when you type the command, because the tasks don't
519
- exist yet. Naming a dwarf per task would mean guessing how the goal will split. So you map
520
- **model tiers to difficulty tiers**, once, in advance — three layers, each optional:
521
-
522
- ```bash
523
- # 1. one dwarf for everything (today's syntax, unchanged)
524
- --dwarf sol:xhigh
525
-
526
- # 2. per-tier routing, with plain --dwarf as the fallback for any tier not named
527
- --dwarf-high sol:xhigh --dwarf-medium luna:high --dwarf-low gemini:low:antigravity
528
- --dwarf sonnet:medium
529
-
530
- # 3. pool several models within one tier — round-robined across tasks
531
- --dwarf-high sol:xhigh,terra:xhigh
532
- ```
533
-
534
- `--qa`, `--qa-high`, `--qa-medium`, `--qa-low` mirror it exactly. QA has no tier rules by
535
- default and falls back to `opus`.
536
-
537
- Resolution order per task: **explicit column value → the tier's rule → plain `--dwarf` →
538
- `UNASSIGNED`**. An `UNASSIGNED` task makes `plan` exit `2` and `run` refuse outright. forge
539
- does not invent a model when you haven't said which one should spend your quota.
540
-
541
- Layer three is **the approval gate**: after decomposition, before any dispatch, you get the
542
- task table with each task's difficulty and resolved dwarf/qa. Retier a task ("2 is actually
543
- medium") and it re-routes per your rules, or override a single row's dwarf outright. This is
544
- also the cost gate — `high` on a large goal is 20+ dispatches, which should be visible before
545
- it's spent.
546
-
547
- **Exploration is on by default** (`--explore 0.2`):
548
- - **Which rows:** only rows left to forge, never one whose dwarf column you filled.
549
- - **What changes:** now and then such a row gets its *neighbouring* tier's spec from your own
550
- pools: the next cheaper tier for medium and high rows, the next stronger for low rows.
551
- Pools that differ only in effort (`--dwarf-low sol --dwarf-high sol:xhigh`) compare the
552
- two efforts.
553
- - **How often:** about one row in ten at the default.
554
- - **What you see:** each explored row is marked `*` in the table, before anything is spent.
555
- - **What it's for:** `forge dataset routing` reads the draws back as pass rates per model and
556
- effort. Pass `--explore 0` to turn it off.
557
-
558
- See [decompose.md](references/decompose.md#exploratory-routing-on-by-default).
559
-
560
- ### Waves, and what actually bounds parallelism
561
-
562
- A **wave** is the largest set of tasks whose dependencies are all satisfied by earlier waves
563
- **and** whose declared `files` sets are pairwise disjoint.
564
-
565
- Disjointness — not the task count — is the real bound. Two dwarves editing one file produce a
566
- conflict no reviewer can untangle and no merge can resolve honestly, so they land in different
567
- waves however the goal was decomposed. When that happens forge says so:
568
-
569
- ```
570
- deferrals (serialized to protect the merge):
571
- wave 1: lint deferred (file overlap with a task already in this wave)
572
- ```
573
-
574
- That line matters. A decomposition that looks parallel but serializes at run time should
575
- announce it, rather than leaving you wondering why eight tasks took eight rounds. When most
576
- tasks overlap, the fix is a better split — not more parallelism.
577
-
578
- Waves are a planning preview. A Python 3 standard-library coordinator starts eligible tasks
579
- as prerequisites merge and capacity becomes available, while excluding overlapping active
580
- paths. Merges are serialized. Each task pins the integration commit at dispatch; retries keep
581
- that baseline, and QA sees only baseline membership recorded for that task.
582
-
583
- ### Fresh session per task, plus a capsule
584
-
585
- Every dispatch is a **clean slate** — forge never passes `--continue`, `--resume`, or
586
- `codex exec resume`. One task's confused turn can't poison another, and each task's context
587
- stays small.
588
-
589
- Clean slates alone would leave every dwarf blind to the wider run, so a short `capsule.md` is
590
- generated from the task's pinned baseline and preserved for both implementation and QA:
591
-
592
- ```
593
- # forge run <run-id>
594
- Goal: <overall goal>
595
- You are doing ONE task of <N>. Do not do the others' work.
596
-
597
- ## Your task
598
- tokens — Add the token model difficulty: medium
599
- Files you own: src/auth/token.py
600
-
601
- ## Run status
602
- id diff status files
603
- middleware high MERGED src/auth/middleware.py
604
- tokens medium THIS ONE src/auth/token.py
605
- docs low NOT IN BASE README.md
606
-
607
- ## Ground rules
608
- - Your branch already contains every MERGED task's work — do not reimplement it.
609
- - Files owned by other tasks are off limits; another dwarf is editing them now.
610
- - If your task truly needs a change in someone else's file, say so in your final
611
- message instead of making it.
612
- ```
613
-
614
- It's written **per task**, not once per run — tasks in a wave execute concurrently, so a
615
- single shared capsule file is a race in which each task overwrites it and reads back whichever
616
- sibling wrote last, handing a dwarf someone else's task description.
617
-
618
- For QA the capsule additionally scopes the review: code from `MERGED` tasks is already in the
619
- base and is not this task's bug. Without that, a wave-2 reviewer reports wave-1 code as
620
- defects in the diff it was handed.
621
-
622
- ### Verdicts and failure
623
-
624
- Decompose-mode QA must end with a sentinel line:
625
-
626
- ```
627
- FORGE_VERDICT: PASS no confirmed correctness bug (style nits are not failures)
628
- FORGE_VERDICT: FAIL at least one CONFIRMED correctness bug
629
- ```
630
-
631
- Only a standalone final line counts, and the parser is shared by every runner (solo,
632
- decomposed, Fractal's QA checkpoint). The prompt shows each verdict with a dash and its
633
- meaning, and some reviewers copy that whole line, so `FORGE_VERDICT: PASS — meaning` is
634
- accepted, as are trailing whitespace and CRLF line endings. Prose or a quoted marker earlier
635
- in the reply is never a verdict. A missing verdict is `UNKNOWN` and is treated exactly like a
636
- failure — excluded from integration and flagged. Merging a diff whose reviewer never reached
637
- a conclusion would defeat the point of reviewing it, so the ambiguous case fails safe.
638
-
639
- A failing task is excluded and **its branch and worktree are preserved**; the rest of the run
640
- proceeds. forge never loops a dwarf against its reviewer on its own: a retry happens when you
641
- type `retry`, or when you opt in for a run with `--retry-failed N` (next section).
642
-
643
- An `UNKNOWN` task is different from a rejected one: the reviewer never judged the code, so
644
- `retry` (or `--retry-failed`) asks **QA again on the unchanged work** instead of paying an
645
- implementer to rewrite something nobody rejected. No attempt is spent and no dwarf runs. If
646
- the worktree no longer matches what was reviewed, the full task runs instead.
647
-
648
- #### Retrying inside a run: `--retry-failed N`
649
-
650
- ```bash
651
- forge-parallel.sh run <plan-dir> --retry-failed 2
652
- ```
653
-
654
- A task whose reviewer says `FAIL` (or `UNKNOWN`) is re-queued at once, **inside the same
655
- run**, up to `N` more times — with the reviewer's findings carried into the next prompt exactly
656
- as `retry` does. Other tasks keep running meanwhile; dependents still wait for the task to
657
- *merge*, so nothing downstream starts early. Without this, a failed task waited for the whole
658
- run (verification included) to finish before a human could retry it.
659
-
660
- It is opt-in and per invocation: it is not remembered by the plan, and the orchestrator never
661
- adds it unprompted. Only reviewer rejections are retried automatically — `TIMEOUT`, `ERROR`,
662
- `INVALIDATED` and `CONFLICT` still need a human. A retry that changes nothing (empty diff, or
663
- a diff byte-identical to the previous attempt) writes a `noretry` marker and is **not**
664
- retried again, so a stuck task cannot burn its whole budget repeating itself.
665
- `tasks/<id>/attempts.tsv` records every entry (epoch, attempt number, resulting status, infra
666
- class).
667
-
668
- #### Infrastructure failures are not task failures
669
-
670
- A quota or usage limit, an auth problem, a rate limit, a network error, or a harness that
671
- returns nothing at all says nothing about the work. forge classifies those from the dispatch
672
- log (`forge-runtime.py classify`) and `forge-dispatch.sh` exits **8** instead of 4. A
673
- decomposed task then ends as **`INFRA`**, not `ERROR`/`FAIL`, and:
674
-
675
- - **no attempt is spent** — a dwarf-stage stop refunds the counter; a QA-stage stop keeps the
676
- dwarf's committed work and re-runs **QA only** next time (`tasks/<id>/resume`, revalidated
677
- against the branch tip and fingerprint before it is trusted);
678
- - the scheduler stops dispatching, lets running tasks drain, leaves unstarted ones `PENDING`
679
- (never `BLOCKED`) and `run` exits **8**: fix the cause, then run the same command again;
680
- - `run --infra-retries M` waits instead — up to `M` times, using the provider's retry-after
681
- hint when it gave one (capped at 6 hours) and otherwise 60 s doubling to 30 min
682
- (`FORGE_INFRA_BACKOFF` sets the base) — and resumes in the same run;
683
- - verification is skipped for a paused run (it is unfinished); the next `run` verifies at its end.
684
-
685
- `.infra` (`dwarf.infra`/`qa.infra`) holds `class=`, `retry_after=`, `detail=`, `rc=`,
686
- `role=`. Detection is deliberately strict: strong phrases only, and ordinary text that merely
687
- mentions "rate limit" in a reply that otherwise succeeded is never classified. A false
688
- positive costs a pause, not a spent attempt. Under Fractal a dwarf-side infrastructure stop
689
- still collapses to Fractal's own "failed" handling; QA-side stops propagate as above.
690
-
691
- #### Severity, thresholds and known issues
692
-
693
- QA is asked to label every finding `- [P0..P3][CONFIRMED|PLAUSIBLE] file:line — what breaks`
694
- (P0 data loss/security/crash on the main path, P1 wrong behaviour on realistic input, P2
695
- edge-case bug, P3 minor). By default the verdict rule is unchanged — any CONFIRMED bug fails.
696
-
697
- `--qa-threshold Pn` (on `run` or `retry`; remembered in the plan; `none` clears it) names the
698
- **least severe level that still blocks**: `P2` means P0–P2 block and only P3 is tolerated.
699
- A task-specific `tasks/<id>/qa_threshold` file beats the plan-level value, which is how a
700
- dev-tooling or investigation task gets a looser bar than production code. With a threshold, a
701
- reviewer `FAIL` made only of CONFIRMED findings that are all labelled and strictly milder than
702
- the threshold is accepted as **PASS with known issues**: the task merges, `tasks/<id>/known_issues.md`
703
- keeps the findings, `qa.gate` records the decision, and every run/retry/integrate summary
704
- lists them. The literal status stays `PASS`, so nothing downstream needed a new vocabulary.
705
- It fails safe: an unlabelled CONFIRMED finding, or a FAIL with no CONFIRMED finding at all,
706
- is never tolerated, and without a threshold nothing changes.
707
-
708
- #### Shadow reviews: how much of a verdict is the reviewer?
709
-
710
- `plan --shadow-qa <spec>[,<spec>...] [--shadow-rate R]` (default R = 0.1) sends a sampled
711
- share of reviewed attempts to a second reviewer, the first listed one of another model
712
- family than the task's. Its verdict goes to the run dataset and nothing in the run reads it,
713
- so it never changes a status, merge, memory or retry. `forge dataset status` then reports how
714
- often the two reviewers agree, overall and per model pair. The table shows the extra reviews
715
- before you approve. The shadow runs beside the main review and is stopped soon after that
716
- finishes, so the most it costs is reviews and a little latency. See
717
- [decomposition](references/decompose.md#shadow-reviews-opt-in).
718
-
719
- #### Implementer guards
720
-
721
- The dwarf is told not to commit, switch branches, start background processes, or claim checks
722
- it did not run. forge then **observes** whether it did, rather than trusting its report:
723
-
724
- - **self-commit / branch switch** — HEAD or the checked-out ref moved during the dispatch;
725
- - **orphan processes** — the harness runs in its own session; anything still alive in it after
726
- the harness exits is listed in `<role>.orphans` and stopped (`FORGE_ORPHAN_GUARD=off`
727
- disables this; it is also skipped under Fractal);
728
- - **promised later work** — "running in the background", "will report back once it
729
- finishes" and similar in the final message.
730
-
731
- These are warnings recorded in `tasks/<id>/guard.txt`, shown in the QA prompt ("Implementer
732
- guard notes") and in the run summary. They never change a status: the diff and the reviewer
733
- still decide. In solo mode a self-commit prints the `git reset --soft <old HEAD>` that would
734
- undo it; forge never resets anything itself.
735
-
736
- #### Verification coverage and flaky reruns
737
-
738
- `--verify` is whatever you hand it, and a gate that skips a suite the project defines lets a
739
- regression there reach your branch silently. `forge-verify-coverage.py` reads the project's own
740
- test configuration (`package.json` scripts, Makefile targets, pytest/tox, Cargo, Go, Ruby,
741
- Elixir, `scripts/test*.sh`), follows aggregate scripts, and **warns** — before any model is
742
- paid for, again in the run summary and at integrate — about suites the verify command never
743
- runs, e.g. `suite 'test:contract' (npm run test:contract) is not run by the verify command`.
744
- It is advisory: it never fails a run, and `verification.coverage.txt` keeps the lines. It is a
745
- text heuristic (root of the repo only; a mentioned command counts as run).
746
-
747
- A failing verify command is rerun **once** (`--verify-retries N`; `0` restores the single strict
748
- run) when the source fingerprint is unchanged. If the rerun passes the status is `PASS` but
749
- `verification.flaky` is written, `verification.log` keeps the first (failing) run,
750
- `verification.retry.log` the second, and `run`/`integrate` print a loud `FLAKY` line — an
751
- intermittent failure can be a real bug, so it is never passed off as a clean pass. A command
752
- that changes the tree is never retried. This deterministic rerun comes before Jev's optional
753
- model-assisted triage, which then does not run.
754
-
755
- ---
756
-
757
- ## Planning
758
-
759
- A dwarf that is handed only an objective plans its own approach, silently, inside its own
760
- run. Usually fine — but in a decomposed run the capsule prevents two dwarves editing one
761
- *file* and does nothing about them inventing incompatible *interfaces* at a shared seam.
762
- Both stay in their lane, both pass their own QA, and the mismatch surfaces at integration.
763
-
764
- So forge plans before it dispatches. By default the orchestrator does it and writes the
765
- approach into each task's prompt — no extra dispatch. `--planner <spec>` hands that stage to
766
- a named model instead:
767
-
768
- ```bash
769
- /forge "migrate the API layer to gRPC" --decompose-level medium \
770
- --planner sol:xhigh --dwarf-high sol:high --dwarf luna:high
771
- ```
772
-
773
- **One planner dispatch per run, never one per task.** That is the whole point: a single mind
774
- designs both sides of every seam, where N independent planners would recreate the problem.
775
- Effort defaults to `xhigh`, since a bad plan is executed at full price by every dwarf
776
- downstream of it. The planner runs with QA's permission profile — it reads the repo and
777
- writes nothing to it.
778
-
779
- The approach shows up in the approval table under each task, which is the last moment it can
780
- be changed for free, and it is handed to QA as the *intended* approach — so a review can
781
- finally say "this works, but it abandons the planned shape" rather than only judging against
782
- the objective.
783
-
784
- ## Project memory
785
-
786
- Every forge dispatch is a clean slate, so without help the tenth run in a repo rediscovers
787
- what the first one learned. forge keeps that in the repo, in two files:
788
-
789
- ```
790
- .forge/memory.md small, capped, injected into every prompt
791
- .forge/ledger.tsv append-only, one row per dispatch, never injected
792
- ```
793
-
794
- The split is the design. Injected context is paid for on every dispatch of every future run,
795
- so "remember everything" and "stay small" pull against each other — the ledger remembers at
796
- zero context cost and is read only when asked, while `memory.md` is capped at 40 lines / 4 KB.
797
-
798
- ```markdown
799
- ## Verify
800
- - pytest -q runs the suite; make test also lints and is 4x slower
801
-
802
- ## Known traps
803
- - tests/test_api.py::test_timeout is flaky under -n auto [tests/test_api.py]
101
+ A decomposed run plans the tasks, rates and routes each one, then **shows you the table and
102
+ stops** before spending anything. Before an interactive run, forge also asks whether to use
103
+ [Fractal](docs/fractal.md) and whether to open [forge-ui](docs/forge-ui.md).
804
104
 
805
- ## Recurring QA findings
806
- - 3x: route added without updating openapi.yaml [src/routes]
807
- ```
808
-
809
- ### How a fact gets in
105
+ → [Every flag, with more examples](docs/commands.md)
810
106
 
811
- The role that just did the work writes it down, in its final message, next to the verdict:
107
+ ## How it works
812
108
 
109
+ ```mermaid
110
+ flowchart LR
111
+ goal(["your goal"]) --> plan["plan: tasks and models,<br/>then you read the table"] --> dwarf["each task: a dwarf<br/>implements it in<br/>its own worktree"] --> qa["qa reads<br/>the real diff"]
112
+ qa -. "FAIL: retry with<br/>the findings" .-> dwarf
113
+ qa -- PASS --> merge["merged into<br/>integration"] --> deliver(["verification, final review,<br/>integrate --approved"])
813
114
  ```
814
- FORGE_LEARNING: trap | src/proto/*_pb2.py is generated, never hand-edit [src/proto]
815
- ```
816
-
817
- No extra dispatch and no separate model guessing what mattered — the one that hit the
818
- landmine is the one that records it. Everything after that is deterministic bash.
819
-
820
- **What stops it becoming a junk drawer:**
821
-
822
- | Guard | Effect |
823
- |---|---|
824
- | recurrence threshold | a `finding` needs **two distinct runs** before it is injected. One occurrence is an incident, not a pattern |
825
- | stale anchors | an entry ending in `[path]` is dropped once that path stops existing, so memory stays true as code moves |
826
- | hard cap | over 40 lines / 4 KB, lowest-count and least-recent facts are trimmed; one entry is clamped to 180 chars |
827
- | bias to silence | the prompt says most runs teach nothing durable and emitting nothing is correct. A false fact costs every future run; a missing one costs a single rediscovery |
828
115
 
829
- Only `verify` and `trap` are kept on first sighting — those are facts, true the first time.
116
+ Without `--decompose-level`, the whole goal is one task: one dwarf, and one review of its diff.
117
+ With optional [Fractal execution](docs/fractal.md), an implementer can delegate bounded child
118
+ tasks inside the paths it owns. forge still reviews the combined diff.
830
119
 
831
- ### Slices, and what forge writes
120
+ Every model is named by one spec, `alias:effort:harness`. Each field after the alias is optional:
832
121
 
833
- | Role | Gets |
122
+ | Spec | Means |
834
123
  |---|---|
835
- | planner, dwarf | Verify + Known traps + Recurring findings |
836
- | qa | Known traps + Recurring findings |
124
+ | `sol` | `sol` at the role's default effort, on its default CLI (codex) |
125
+ | `sol:xhigh` | the same model at `xhigh` effort |
126
+ | `sol:xhigh:openclaude` | the same model and effort, run through `openclaude` |
837
127
 
838
- QA is not told the build command; it is not building anything. It *is* told the recurring
839
- findings, so it can notice this is the fourth time the same bug shipped.
128
+ Either role can run through any of six agent CLIs:
840
129
 
841
- `.forge/` is the **only** thing forge writes into your working tree rather than onto a branch
842
- of its own. It is excluded from every captured diff, so a reviewer never sees it as the
843
- dwarf's work — but it *will* show up as untracked in `git status` after your first run.
844
-
845
- ### What to do with `.forge/` when it appears
846
-
847
- forge never commits it. That decision is yours, and there are three sane answers:
848
-
849
- | You want | Do this |
850
- |---|---|
851
- | memory shared with your team, and with future you | **commit `.forge/`** — the intended case; every teammate's runs then build on the same facts |
852
- | memory kept to your machine | `echo '.forge/' >> .gitignore` |
853
- | no memory at all | `--no-memory`, or `FORGE_MEMORY=off` in your environment |
854
-
855
- Two things that are easy to get wrong:
856
-
857
- **Gitignoring is not the same as turning it off.** An ignored `.forge/` is still written and
858
- still injected into every prompt — it just stops travelling. Only `--no-memory` /
859
- `FORGE_MEMORY=off` actually disables the feature.
860
-
861
- **Never commit one file and ignore the other.** `memory.md` is *rebuilt from* `ledger.tsv` on
862
- every write, so a clone that has the memory but not the ledger loses everything the moment
863
- anyone runs forge there — the rebuild regenerates `memory.md` from that machine's own short
864
- ledger and the shared facts are gone, silently. Verified behaviour, not a theoretical risk:
865
-
866
- ```
867
- teammate A commits memory.md only, ledger.tsv gitignored
868
- memory.md: - pytest -q runs the suite
869
- - src/a.py is generated [src/a.py]
870
-
871
- teammate B clones, runs forge once, learns one unrelated thing
872
- memory.md: - b learned something new ← A's two facts destroyed
873
- ```
874
-
875
- Commit both, or neither. If you commit them, expect `ledger.tsv` to gain a line per dispatch
876
- — small, but it will appear in your diffs; that growth is the price of the recurrence
877
- counting that keeps `memory.md` honest.
878
-
879
- If you contribute to someone else's repo, gitignore it. A pull request containing forge's
880
- notes to itself is noise to a reviewer who does not use forge.
881
-
882
- ## Run dataset
883
-
884
- Run dirs get deleted, and the ledger keeps spec strings and verdicts but no inputs. So
885
- every run also records its raw inputs and outcomes outside every repo, in
886
- `~/.local/share/forge/dataset/` (`FORGE_DATA_HOME` moves it, `FORGE_DATASET=off` turns
887
- it off). That covers resolved models, prompts, per-attempt diffs, verdicts and findings,
888
- tokens, merge and verification outcomes, and the full request behind every Jev judgment.
889
- It exists to train and calibrate decision models on what forge actually did.
890
-
891
- ```sh
892
- ./forge dataset status # size, counts, and anything that looks incomplete
893
- ./forge dataset import ~/.local/share/forge/legacy # runs that predate live capture
894
- ./forge dataset export # labeled decision cases for benchmarks and fine-tuning
895
- ./forge dataset outcomes # which integrated lines survived, read from git history
896
- ```
130
+ | | codex | claude | openclaude | opencode | antigravity | grok |
131
+ |---|---|---|---|---|---|---|
132
+ | binary | `codex` | `claude` | `openclaude` | `opencode` | `agy` | `grok` |
897
133
 
898
- Capture is fail-open and never read by anything that builds a prompt. See
899
- [references/dataset.md](references/dataset.md) for the event schema and its invariants.
134
+ → [Effort ladders, clamping, sandboxes and adding a model](docs/models.md)
900
135
 
901
136
  ## forge-ui
902
137
 
903
- forge-ui is a small read-only local web page that shows runs live, as a 3D isometric map
904
- ported from [archivision](https://github.com/Giamme/archivision). Its **run cloud** puts every
905
- run in one full-screen view, grouped by project, in five layouts (helix, sphere, galaxy,
906
- timeline, constellation), filtered down to exactly the runs you want; drag any run anywhere
907
- and the cloud remembers it.
138
+ forge-ui is an opt-in, read-only local page that watches any run live, drawn as a 3D map. It
139
+ shows every run on one plate, tasks in their waves and as they really ran, and the prompt, model,
140
+ timings and P0–P3 findings behind every step. forge offers it at the start of every new run and
141
+ never opens it without a yes.
908
142
 
909
143
  <p align="center">
910
144
  <img src="ui/docs/media/cloud.webp" width="880" alt="The run cloud: about seventy runs as blocks in five project spirals, orbited, then laid out as galaxies and as a timeline with one lane per project.">
@@ -912,8 +146,8 @@ and the cloud remembers it.
912
146
 
913
147
  <table>
914
148
  <tr>
915
- <td width="50%" valign="top"><img src="ui/docs/media/run.png" alt="A run's eight tasks on a 3D plate in their planned waves, with dependency cables and the finish nodes, beside the run's inspector."><br><b>A run, as planned.</b> Tasks in their waves with dependency cables, ending in integration, verification and the final review. Click a task to open its attempts, dwarves and reviews.</td>
916
- <td width="50%" valign="top"><img src="ui/docs/media/execution.png" alt="The same run on wall-clock time: one block per attempt, failed attempts in red, a plane marking now, and the inspector's timeline and critical path."><br><b>And as it ran.</b> One block per attempt in the lane it ran in, failed ones in red, against <code>--max-parallel</code>; the inspector names the critical path.</td>
149
+ <td width="50%" valign="top"><img src="ui/docs/media/run.png" alt="forge-ui's window: the runs list, a run's eight tasks on a 3D plate in their planned waves with dependency cables, finding marks and the finish nodes, and the run's inspector."><br><b>A run, as planned.</b> Tasks in their waves with dependency cables, ending in integration, verification and the final review. Click a task to open its attempts, dwarves and reviews.</td>
150
+ <td width="50%" valign="top"><img src="ui/docs/media/execution.png" alt="The same run on wall-clock time: one block per attempt, failed attempts in red with their finding marks, a plane marking now, the concurrency strip, and the inspector's timeline and critical path."><br><b>And as it ran.</b> One block per attempt in the lane it ran in, failed ones in red, against <code>--max-parallel</code>; the inspector names the critical path.</td>
917
151
  </tr>
918
152
  <tr>
919
153
  <td valign="top"><img src="ui/docs/media/diff.png" alt="The diff a reviewer read, with a P1 finding pinned under the line comparing token digests and a P2 finding under the revoke function."><br><b>Findings on their lines.</b> Any diff of a task, or exactly the diff one reviewer read, with each P0–P3 finding under the line it names.</td>
@@ -921,932 +155,38 @@ and the cloud remembers it.
921
155
  </tr>
922
156
  <tr>
923
157
  <td valign="top"><img src="ui/docs/media/files.png" alt="The files heatmap: files grouped by directory against tasks grouped by wave, with planned, touched, drift and same-wave overlap marks."><br><b>Who touched which file.</b> Planned, changed, drifted into or conflicted on, wave by wave; a cell opens that file's diff.</td>
924
- <td valign="top"><img src="ui/docs/media/map.png" alt="The map of every run: one block per run in a district per repo, green when done, blue while running, red with failures."><br><b>Every run, by repo.</b> One block per run with a lamp per task, filtered by state, repo, model, age or review verdict.</td>
158
+ <td valign="top"><img src="ui/docs/media/map.png" alt="The map of every run: one block per run in a district per repo, green when done, amber while running, red with failures."><br><b>Every run, by repo.</b> One block per run with a lamp per task, filtered by state, repo, model, age or review verdict.</td>
925
159
  </tr>
926
160
  </table>
927
161
 
928
- Also: replay, critical path, Fractal child-task trees, quick jump (Ctrl K, ⌘K on a Mac, or
929
- `/`), a bell for runs that finish or fail, failed tasks and final-review verdicts (with opt-in
930
- browser notifications), and a run exported as one HTML file that opens without forge-ui, with
931
- prompts, diffs and local paths left out unless you choose them.
932
-
933
- Offline (it loads three.js from a CDN for the 3D view) the same page works in 2D. It is opt-in.
934
- The `/forge` skill asks "Open forge-ui to watch this run? [y/N]" at the start of every new
935
- interactive run, or you pass it yourself:
936
-
937
- ```sh
938
- bash scripts/forge-solo.sh "$RUN" --repo "$REPO" --dwarf sol --ui
939
- bash scripts/forge-parallel.sh plan "$PLAN" --repo "$REPO" --decompose-level medium --ui
940
- ./forge ui open [DIR] # an existing run dir, or the run list
941
- ```
942
-
943
- All runs share one detached server on `127.0.0.1:8765` (`FORGE_UI_PORT`) that exits after
944
- four idle hours (`FORGE_UI_IDLE_S`). A UI that cannot start never fails the run. It has no
945
- token: any local process can read the prompts and diffs of the runs it shows. See
946
- [references/ui.md](references/ui.md) and [ui/README.md](ui/README.md).
947
-
948
- To look around before your first run, point it at made-up runs (the ones in these pictures):
162
+ To look around before your first run, open it on made-up runs (the ones pictured here) from a
163
+ git checkout:
949
164
 
950
165
  ```sh
951
166
  python3 ui/tools/demo_runs.py /tmp/forge-demo
952
167
  (cd ui && python3 -m forge_ui --no-registry --root /tmp/forge-demo --open)
953
168
  ```
954
169
 
955
- ## Full command reference
956
-
957
- ### The `/forge` slash command
958
-
959
- ```
960
- /forge "<goal>" --dwarf <alias>[:<effort>[:<harness>]]
961
- [--qa <alias>[:<effort>[:<harness>]]]
962
- [--planner <alias>[:<effort>[:<harness>]]]
963
- [--yolo-dwarf] [--yolo-qa] [--repo <dir>] [--native-review]
964
- [--no-memory] [--timeout <seconds>] [--ui | --no-ui] [--fractal | --no-fractal]
965
- [--fractal-auto-decompose] [--fractal-planner <spec>]
966
- [--fractal-depth <n>] [--fractal-children <n>] [--fractal-nodes <n>]
967
- [--fractal-iterations <n>] [--fractal-concurrency <n>] [--fractal-deadline <s>]
968
- [--decompose-level low|medium|high] [--max-parallel <n>]
969
- [--dwarf-high <spec>] [--dwarf-medium <spec>] [--dwarf-low <spec>]
970
- [--qa-high <spec>] [--qa-medium <spec>] [--qa-low <spec>]
971
- ```
972
-
973
- | Flag | Default | Meaning |
974
- |---|---|---|
975
- | `--dwarf <spec>` | *asks* | the model that implements the change |
976
- | `--qa <spec>` | `opus` at `xhigh` | the model that reviews the dwarf's diff |
977
- | `--planner <spec>` | orchestrator plans | dispatch planning to a model; one dispatch per run, effort defaults to `xhigh` |
978
- | `--no-memory` | memory on | write and inject nothing in `.forge/` |
979
- | `--timeout <s>` | `2700` (45m) | kill any single dispatch that runs longer. `0` disables |
980
- | `--yolo-dwarf` | off | drop the dwarf's sandbox and approval gates |
981
- | `--yolo-qa` | off | drop the reviewer's sandbox |
982
- | `--repo <dir>` | cwd | repository to work in |
983
- | `--native-review` | off | use native Codex review against the recorded baseline instead of an inlined diff |
984
- | `--decompose-level <l>` | off | split the goal into parallel tasks |
985
- | `--max-parallel <n>` | `3` | concurrent task pipelines |
986
- | `--dwarf-high/-medium/-low` | — | per-difficulty routing; comma-list pools models |
987
- | `--qa-high/-medium/-low` | — | same, for reviewers |
988
- | `--fractal` / `--no-fractal` | ask interactively; off unattended | mutually exclusive, per-run backend choice |
989
- | `--fractal-auto-decompose` | off | enables Fractal with automatic recursive split-or-atomic decisions |
990
- | `--fractal-planner <spec>` | top-level planner, then task root spec | frozen decision model; requires automatic mode |
991
- | `--fractal-depth <n>` | `2` | nested levels below each Forge implementation node |
992
- | `--fractal-children <n>` | `3` | unsettled direct children per node |
993
- | `--fractal-nodes <n>` | `12` | lifetime nodes per Forge task, including implementation |
994
- | `--fractal-iterations <n>` | `6` | per node per execution attempt |
995
- | `--fractal-concurrency <n>` | `3` | shared model slots across nested trees and QA |
996
- | `--fractal-deadline <s>` | `2700` | task attempt deadline, excluding explicit pauses |
997
- | `--fractal-max-cost <amount>` | unsupported | returns an error; the bridge cannot enforce dollar caps |
998
-
999
- Fractal's task deadline still applies when `--timeout 0` disables the ordinary
1000
- per-dispatch timeout. Child routing uses the dwarf tier flags; QA stays at the Forge
1001
- task boundary. Pools and Fractal limits are frozen for an existing run.
1002
-
1003
- **On `--native-review`:** for a diff too large to inline, this switches codex to its
1004
- purpose-built reviewer. Know the trade — codex refuses a custom prompt alongside its scope
1005
- flags, so the reviewer never learns what the dwarf was *asked* to do. It can still judge
1006
- whether the code is correct, but not whether it's the right change.
1007
-
1008
- #### Examples
1009
-
1010
- ```bash
1011
- # minimum
1012
- /forge "make the cache key include the tenant id" --dwarf sol:high
1013
-
1014
- # both roles named, reviewer thinking harder than the implementer
1015
- /forge "rewrite the rate limiter as a token bucket" --dwarf luna:max --qa sol:ultra
1016
-
1017
- # same model, different CLI
1018
- /forge "extract the retry helper into its own module" --dwarf sol:high:openclaude
1019
-
1020
- # Gemini as the dwarf, unsandboxed so it can actually run the tests it writes
1021
- /forge "add table-driven tests for the parser" --dwarf gemini:high:antigravity --yolo-dwarf
1022
-
1023
- # a cheap dwarf with an expensive reviewer
1024
- /forge "update all the docstrings to match the new signatures" --dwarf haiku --qa opus:max
1025
-
1026
- # work in another repo
1027
- /forge "bump the pinned deps and fix the fallout" --dwarf sol:xhigh --repo ~/dev/other-project
1028
-
1029
- # large refactor, reviewed by codex's native reviewer
1030
- /forge "split the god object into three services" --dwarf sol:ultra --qa sol:ultra --native-review
1031
-
1032
- # parallel, one dwarf for every task
1033
- /forge "add CRUD endpoints for projects, users and teams" \
1034
- --decompose-level medium --dwarf sol:high
1035
-
1036
- # parallel, routed by difficulty, wider fan-out
1037
- /forge "migrate the whole API layer from REST to gRPC" \
1038
- --decompose-level high --max-parallel 5 \
1039
- --dwarf-high sol:ultra --dwarf-medium luna:high --dwarf-low haiku:medium \
1040
- --qa-high opus:max --qa sonnet:high
1041
-
1042
- # pool two providers on the hard tier so neither quota carries it alone
1043
- /forge "implement the new billing rules across the codebase" \
1044
- --decompose-level medium \
1045
- --dwarf-high sol:xhigh,terra:xhigh --dwarf luna:high
1046
-
1047
- # a model that thinks for a long time; raise the per-dispatch backstop
1048
- /forge "prove the scheduler cannot deadlock, and fix it if it can" \
1049
- --dwarf sol:ultra --timeout 7200
1050
- ```
1051
-
1052
- After a decomposed run, a task that failed review can be re-run on its own — with the
1053
- findings that failed it, on a stronger model:
1054
-
1055
- ```bash
1056
- bash scripts/forge-parallel.sh retry /tmp/forge-billing pricing --dwarf sol:ultra
1057
- ```
1058
-
1059
- ### `forge fractal` command group
1060
-
1061
- Use `./forge fractal` from this checkout, or `<forge-checkout>/forge fractal` elsewhere.
1062
- This group manages Fractal runs; it does not replace the `/forge` skill or runner scripts.
1063
-
1064
- | Command | Purpose |
1065
- | --- | --- |
1066
- | `install [--dry-run] [--yes] [--with-prerequisites]` | preview or install the isolated pinned runtime |
1067
- | `doctor [--spec SPEC] [--json]` | check runtime/hooks/tmux and optional repeated harness specs without provider calls |
1068
- | `runs [--repo DIR] [--json] [--html PATH]` | discover managed runs, optionally filtered by repository |
1069
- | `status\|tree\|activity RUN_ID` | inspect progress, hierarchy and history |
1070
- | `logs\|costs\|messages\|config RUN_ID` | inspect diagnostics, observed accounting, radio traffic and effective settings |
1071
- | `pause\|resume\|stop RUN_ID` | request a control and record its observed state |
1072
- | `open [RUN_ID] [--port PORT]` | open the read-only live dashboard or runs index |
1073
- | `report RUN_ID [--html PATH]` | capture a self-contained HTML report; default filename is `RUN_ID.html` |
1074
-
1075
- Inspection commands accept `--task TASK_ID`, `--node NODE_ID`, `--json`, `--html PATH`,
1076
- `--offset N` and `--limit N` (1–1000). They currently return a common JSON run projection;
1077
- the command names do not imply separate output schemas. `logs --follow` follows changes.
1078
- HTML exports capture full history and logs. Controls require an explicit run ID, and a
1079
- node control also requires its task ID; they never guess the latest run. Use each command's
1080
- `--help` for its accepted options. Installation is also available through
1081
- `bash scripts/forge-install-fractal.sh`.
1082
-
1083
- ### `forge ui` command group
1084
-
1085
- | Command | Purpose |
1086
- | --- | --- |
1087
- | `open [DIR] [--no-browser] [--port N]` | start or reuse the shared forge-ui server; print `forge-ui: <url>` for the plan or solo run dir `DIR` (the run list without it) and open a browser the first time |
1088
- | `status [--json]` | exit 0 and describe the server when it runs, 1 when not |
1089
- | `stop` | stop the server |
1090
-
1091
- `FORGE_UI=off` makes `--ui` on a runner start nothing (it does not stop a running server or block `forge ui open`; use `forge ui stop`), `FORGE_UI_BROWSER=off` never opens a browser,
1092
- `FORGE_UI_PORT` (8765) and `FORGE_UI_IDLE_S` (14400, `0` = never) tune the server. State and
1093
- `server.log` are in `${XDG_STATE_HOME:-~/.local/state}/forge/ui/`.
1094
-
1095
- ### `scripts/forge-dispatch.sh`
1096
-
1097
- Resolves a spec into a real CLI invocation and runs it. Needs only bash and coreutils, which
1098
- is the point: it behaves identically no matter which harness is orchestrating.
1099
-
1100
- ```
1101
- forge-dispatch.sh doctor
1102
- forge-dispatch.sh dwarf <spec> --prompt-file <f> [--repo <dir>] [--run-dir <d>] [--yolo] [--dry-run]
1103
- forge-dispatch.sh qa <spec> --prompt-file <f> [--repo <dir>] [--run-dir <d>] [--yolo] [--dry-run]
1104
- [--native-review [--review-base <ref>]]
1105
- forge-dispatch.sh planner <spec> --prompt-file <f> [--repo <dir>] [--run-dir <d>] [--dry-run]
1106
- ```
1107
-
1108
- | Option | Meaning |
1109
- |---|---|
1110
- | `--prompt-file <f>` | file holding the prompt (required for `dwarf`/`qa`) |
1111
- | `--repo <dir>` | repository the role operates in |
1112
- | `--run-dir <d>` | where prompts, logs and results are written |
1113
- | `--yolo` | bypass sandbox and approvals for this role |
1114
- | `--dry-run` | resolve and print the command; run nothing |
1115
- | `--native-review` | qa only; use codex's built-in reviewer |
1116
- | `--review-base <ref>` | qa only; base ref for the native review |
1117
- | `--agy-timeout <s>` | antigravity `--print-timeout` (long runs need this raised) |
1118
- | `--timeout <s>` | kill this dispatch after `<s>` seconds; `0` disables. See the precedence below |
1119
-
1120
- **Exit codes:** `0` ok · `2` spec or usage error · `3` harness missing or unusable ·
1121
- `4` the backend ran and failed · `7` the backend hit its timeout and was killed ·
1122
- `8` an **infrastructure failure** (quota or usage limit, auth, rate limit, network, or an
1123
- empty answer) — nothing about the work was judged, so callers pause instead of counting a
1124
- failure. On `8`, `<run-dir>/<role>.infra` holds `class=`, `retry_after=`, `detail=`, `rc=`,
1125
- `role=`.
1126
-
1127
- **Timeout precedence.** An explicit `--timeout`, then `FORGE_TIMEOUT` (an explicit `0` wins
1128
- and disables the watchdog), then the optional fifth `timeout` column of the model's
1129
- [registry](#model-registry) row, then the built-in `2700` (45 min). A value that was *not*
1130
- explicit is scaled by the resolved (post-clamp) effort: up to `high` ×1, `xhigh` ×4/3,
1131
- `max`/`ultra`/`ultracode` ×2. QA and planner default to `xhigh`, so their built-in limit is
1132
- 3600 s; dwarves default to `medium` and keep 2700 s. `<role>.resolved` records `timeout=` and
1133
- `timeout_source=` (`explicit`, `env`, `registry` or `default`, plus `+effort-scaled`).
1134
- `forge-parallel.sh run`/`retry --timeout S` sets it for a decomposed run (kept in the plan).
1135
- `FORGE_REGISTRY` points the dispatcher at an alternative registry file.
1136
-
1137
- Only antigravity bounds its own runtime, and stock macOS has no `timeout` binary to wrap the
1138
- others in — so a hung backend used to block forever, and under a decomposed run one hung task
1139
- silently stalled the others. The default is deliberately generous: this is a backstop against
1140
- a wedged process, not a budget. A role killed by it has usually left a partial edit behind, so
1141
- `7` is worth reporting as such rather than as a clean failure.
1142
-
1143
- On `3` or `4`, forge shows you the actual error rather than substituting a different model —
1144
- quietly swapping backends hides the fact that the one you picked is broken.
1145
-
1146
- **Process hygiene.** Unless `FORGE_ORPHAN_GUARD=off` (or the run is a Fractal one), the harness
1147
- is started in its own session, and after it exits any process still alive in that session is
1148
- listed in `<role>.orphans` and stopped, so a backgrounded measurement cannot outlive the
1149
- dispatch. The timeout watchdog and the exit trap signal the whole session too.
1150
-
1151
- ```bash
1152
- # what would this actually run?
1153
- bash scripts/forge-dispatch.sh dwarf sol:ultra --prompt-file /tmp/p --dry-run
1154
-
1155
- # check a clamp before committing to a long run
1156
- bash scripts/forge-dispatch.sh qa gemini-pro:medium --prompt-file /tmp/p --dry-run
1157
- # effort=low
1158
- # clamped=medium -> low (gemini-pro offers low and high, but no medium)
1159
- ```
1160
-
1161
- ### `scripts/forge-solo.sh`
1162
-
1163
- The whole single-task run: memory injection, prompt assembly, the dwarf dispatch, the diff
1164
- capture, the QA dispatch, and the ledger writes.
1165
-
1166
- ```
1167
- forge-solo.sh <run-dir> --repo <dir> --dwarf <spec> [--qa <spec>]
1168
- [--approach <file>] [--yolo-dwarf] [--yolo-qa]
1169
- [--native-review] [--no-memory] [--timeout <s>] [--dry-run] [--ui | --no-ui]
1170
- [--fail-first]
1171
- [--fractal | --no-fractal] [--fractal-<limit> <n>]
1172
- [--dwarf-low <pool>] [--dwarf-medium <pool>] [--dwarf-high <pool>] [--retry]
1173
- ```
1174
-
1175
- You write two files into the run directory; forge does the rest:
1176
-
1177
- | You write | Is |
1178
- |---|---|
1179
- | `prompt.md` | the implementation instruction (required) |
1180
- | `goal.txt` | one line, what was asked — given to the reviewer as intent (optional) |
1181
-
1182
- | It writes | Is |
1183
- |---|---|
1184
- | `dwarf.last` / `qa.last` | each role's final message |
1185
- | `changes.diff` | exactly what the reviewer read |
1186
- | `verdict` | `PASS`, `FAIL`, `UNKNOWN` or `NOCHANGES` |
1187
- | `<role>.{input,log,resolved,cmd}` | the prompt, the transcript, the resolution, the command |
1188
- | `fractal-selection.json` | persisted backend choice; enabled runs include their managed run ID |
1189
-
1190
- With Fractal, per-step dispatch artifacts live in the managed run while the solo directory
1191
- retains the aggregate diff and Forge QA result. `--retry` starts a new Fractal attempt;
1192
- `./forge fractal resume RUN_ID` continues preserved execution and pipeline checkpoints.
1193
-
1194
- ```bash
1195
- RUN="$(mktemp -d /tmp/forge-XXXXXX)"
1196
- echo "add retry with backoff to the HTTP client" > "$RUN/goal.txt"
1197
- echo "Add exponential backoff to src/http.py, capped at 5 attempts." > "$RUN/prompt.md"
1198
-
1199
- bash scripts/forge-solo.sh "$RUN" --repo ~/dev/api --dwarf sol:high --qa opus
1200
- ```
1201
-
1202
- **Exit codes:** `0` reviewed · `2` usage · `3` not a git repository · `4` a dispatch failed ·
1203
- `5` **no changes or review invalidated** · `7` a dispatch timed out · `8` a dispatch hit an
1204
- **infrastructure failure** (quota, auth, rate limit, network, empty output): nothing was
1205
- judged, fix the cause and run again. A dwarf that exits 0 with an empty answer *and* changes
1206
- nothing is reported as `8`, not `5` — it never really ran.
1207
-
1208
- The dwarf is given the same implementer rules as in a decomposed run (no commits, no
1209
- background processes, finish measurements before the last message). If it commits anyway, the
1210
- run still reviews its changes, says so, and prints the `git reset --soft <old HEAD>` that would
1211
- turn them back into uncommitted changes; forge never resets anything itself. Orphan processes
1212
- and promised-later-work notes appear under "implementer guard notes".
1213
-
1214
- `5` is the one worth recognising on sight. Headless dwarves genuinely stop to ask a
1215
- clarifying question that nothing can answer; the run ends having spent the quota and changed
1216
- nothing, with the question sitting in `dwarf.last`.
1217
-
1218
- Two things it gets right that are easy to get wrong by hand, and that fail *silently* rather
1219
- than loudly:
1220
-
1221
- - **`.forge/` is excluded from the capture.** Forge rewrites its own memory in the working
1222
- tree at the end of every run, so left in, it reaches the reviewer as a file the dwarf
1223
- appears to have touched — which a good reviewer correctly flags as scope creep.
1224
- - **New files are diffed against `/dev/null`.** A `git status` line says only `?? calc.py`.
1225
- A dwarf whose entire task was to add a file would have had its actual code reviewed by
1226
- nobody, while the run still reported a clean QA pass.
1227
-
1228
- The run directory must live outside the repository — anything forge writes inside the working
1229
- tree shows up in the dwarf's own diff. `forge-solo.sh` refuses rather than letting that happen
1230
- quietly.
1231
-
1232
- ### `scripts/forge-parallel.sh`
1233
-
1234
- Decomposed runs: many dwarves in isolated worktrees, per-task QA, only passing work merged.
1235
- Bash 3.2 entrypoints use a Python standard-library coordinator to schedule eligible tasks.
1236
-
1237
- ```
1238
- forge-parallel.sh plan <plan-dir> --repo <dir> [routing flags] [--planner <spec>] [--no-memory]
1239
- [--decompose-level L] [--shadow-qa <spec>[,..]|none] [--shadow-rate R]
1240
- [--explore E] [--ui | --no-ui] [--fail-first | --no-fail-first]
1241
- forge-parallel.sh run <plan-dir> [--max-parallel N] [--yolo-dwarf] [--yolo-qa] [--dry-run]
1242
- [--verify <cmd>] [--setup <cmd>] [--timeout S]
1243
- [--retry-failed N] [--infra-retries M]
1244
- [--qa-threshold P0|P1|P2|P3|none] [--verify-retries N]
1245
- [--fractal | --no-fractal] [--fractal-<limit> <n>]
1246
- forge-parallel.sh retry <plan-dir> <task-id> [--dwarf <spec>] [--timeout S]
1247
- [--qa-threshold Pn|none] [--verify-retries N]
1248
- forge-parallel.sh accept <plan-dir> <task-id> --reason "<why>" --approved
1249
- forge-parallel.sh review <plan-dir> [--qa <spec>] [--yolo-qa] [--qa-threshold Pn]
1250
- forge-parallel.sh integrate <plan-dir> --approved [--final-review <qa-spec>] [--verify-retries N]
1251
- forge-parallel.sh split <plan-dir> <new-plan-dir> --tasks <id,id> [--dry-run]
1252
- forge-parallel.sh combine <new-plan-dir> <plan-dir> <plan-dir>... [--verify <cmd>] [--dry-run]
1253
- ```
1254
-
1255
- | Subcommand | Does |
1256
- |---|---|
1257
- | `plan` | validate `tasks.tsv`, resolve difficulty → concrete dwarf/qa specs, compute waves, render the approval table |
1258
- | `run` | schedule ready tasks; per task: worktree → dwarf → diff → QA → status; merge passing tasks onto the integration branch |
1259
- | `retry` | re-run **one** failed task in the worktree it already has, with its reviewer's findings in the prompt |
1260
- | `integrate` | **never automatic** — merge the integration branch into your branch |
1261
- | `accept` | **only on your explicit instruction** — merge a `FAIL`/`UNKNOWN` task's reviewed work anyway, recording why |
1262
- | `review` | **opt-in, spends QA quota** — one reviewer reads the whole combined result; read-only |
1263
- | `split` / `combine` | move tasks into a plan of their own / join fully merged plans into one |
1264
-
1265
- | Flag | Subcommands | Remembered in the plan | Means |
1266
- |---|---|---|---|
1267
- | `--timeout S` | run, retry | `timeout` | per-dispatch timeout in seconds (`0` disables); wins over `FORGE_TIMEOUT` |
1268
- | `--retry-failed N` | run | no — this run only | re-queue a task the reviewer rejected up to `N` more times inside the run |
1269
- | `--infra-retries M` | run | no — this run only | wait out up to `M` infrastructure stops instead of pausing |
1270
- | `--qa-threshold Pn` | run, retry | `qa_threshold` | least severe finding that still blocks; milder ones become known issues; `none` clears |
1271
- | `--verify-retries N` | run, retry, integrate | `verify_retries` | reruns of a failed verify command (default `1`, `0` = strict single run) |
1272
-
1273
- **Exit codes:** `0` ok · `2` usage or validation · `3` precondition (not a git repo, …) ·
1274
- `5` some task failed QA, or the combined verification failed · `6` integration conflict ·
1275
- `8` **paused on an infrastructure failure** (quota, auth, rate limit, network): no attempt was
1276
- spent and nothing was judged — fix the cause and run the same command again.
1277
-
1278
- ```bash
1279
- PLAN=/tmp/forge-auth
1280
-
1281
- # 1. plan and inspect the routing
1282
- bash scripts/forge-parallel.sh plan "$PLAN" --repo ~/dev/api \
1283
- --dwarf-high sol:xhigh --dwarf-low gemini:low:antigravity --dwarf luna:high
1284
-
1285
- # 2. see exactly what will be dispatched, without dispatching
1286
- bash scripts/forge-parallel.sh run "$PLAN" --dry-run
1287
-
1288
- # 3. run it, four dwarves at a time, implementers unsandboxed
1289
- bash scripts/forge-parallel.sh run "$PLAN" --max-parallel 4 --yolo-dwarf
1290
-
1291
- # 4. after reading the results, deliver the passing work
1292
- bash scripts/forge-parallel.sh integrate "$PLAN" --approved
1293
- ```
1294
-
1295
- `integrate` refuses without `--approved`, and refuses on a dirty working tree.
1296
- For Fractal execution, pass tier pools to `plan` and backend/limit flags to `run`.
1297
- `--max-parallel` limits task pipelines; the shared `--fractal-concurrency` limit includes
1298
- all their child invocations and QA. See [decomposed Fractal execution](references/decompose.md#fractal-within-decomposed-tasks).
1299
-
1300
- #### Retrying a failed task
1301
-
1302
- ```bash
1303
- # re-run one task with the findings that failed it, on a stronger model
1304
- bash scripts/forge-parallel.sh retry "$PLAN" middleware --dwarf sol:xhigh
1305
- ```
1306
-
1307
- The findings are the point. `retry` prepends the previous reviewer's report to the dwarf's
1308
- prompt — "your last attempt was reviewed and these were the findings; fix them" — and reuses
1309
- the worktree, so the dwarf continues from its own code rather than starting over. If the
1310
- verdict comes back `PASS` the task merges like any other.
1311
-
1312
- `--dwarf` is written back into `tasks.tsv`, so the table shows the model that will actually
1313
- be spent and the escalation survives a re-plan.
1314
-
1315
- Two things it deliberately does:
1316
-
1317
- - **Reviews the task's cumulative diff**, against the commit the task was originally branched
1318
- from — not just the fix. Reviewing the fix alone would let the first attempt's code through
1319
- unread.
1320
- - **Refuses to re-review identical code.** If the retry produces a byte-identical diff, the
1321
- task fails again without spending a QA dispatch.
1322
-
1323
- `retry` is something you run. Forge never loops a dwarf against its own reviewer on its own
1324
- initiative — unless you ask a *run* to with `--retry-failed N`, see
1325
- [retrying inside a run](#retrying-inside-a-run---retry-failed-n). A task whose reviewer
1326
- never gave a verdict (`UNKNOWN`) or whose run stopped on an infrastructure failure (`INFRA`)
1327
- resumes at the review stage instead: QA runs again on the unchanged work, no dwarf runs, and
1328
- no attempt is spent.
1329
-
1330
- #### Resuming an interrupted run
1331
-
1332
- Run `run` again to skip MERGED tasks, merge saved PASS results after fingerprint checks,
1333
- and dispatch eligible pending tasks. Tasks paused on an infrastructure failure (`INFRA`) are
1334
- dispatched again without any flag, since nothing was wrong with the work. Failed and
1335
- interrupted tasks need an explicit `retry` (or `run --retry-failed N` for rejected ones).
1336
- Their worktrees and pinned baselines remain available. Concurrent run/retry/integrate
1337
- operations for the same plan are excluded by an advisory lock.
1338
-
1339
- For a Fractal-backed run, use `./forge fractal resume RUN_ID` to recover recorded execution
1340
- and pipeline checkpoints. A task/subtree selector resumes only that execution scope.
1341
- Explicit `retry` retains the backend and frozen pools; a replacement dwarf must already
1342
- belong to that pool. See [recovery](references/fractal.md#limits-and-lifecycle).
1343
-
1344
- #### Accepting a task despite its reviewer
1345
-
1346
- ```bash
1347
- bash scripts/forge-parallel.sh accept "$PLAN" tooling \
1348
- --reason "only an unreachable option left; tracked in the backlog" --approved
1349
- ```
1350
-
1351
- When a reviewer keeps finding new corners of something that should ship (the report's
1352
- investigation harness got 12 findings over five rounds, and only a human decision ended it),
1353
- `accept` is that decision made explicit instead of done with git plumbing. It needs **both**
1354
- `--reason` and `--approved`, because it overrides a reviewer; an orchestrating model never runs
1355
- it on its own. It accepts only a `FAIL` or `UNKNOWN` task whose reviewed work is **intact** —
1356
- the branch is still at the reviewed commit and the worktree still has the saved fingerprint —
1357
- otherwise it exits `3` and says to `retry` for a fresh review (accepting would sign off on code
1358
- nobody reviewed). It writes `tasks/<id>/known_issues.md` (your reason, who and when, and the
1359
- reviewer's reply copied verbatim) and `accepted.by`, merges through the same checks as any other
1360
- task, and puts everything back if the merge conflicts (exit `6`). Dependents are not touched;
1361
- `run` continues them.
1362
-
1363
- #### Whole-run review (opt-in)
1364
-
1365
- Per-task QA only sees one slice. In the report, a holistic review of the combined result still
1366
- found two confirmed P2 defects in code every per-task review had passed. It is opt-in because it
1367
- spends QA quota and `integrate` made no model calls before:
1368
-
1369
- ```bash
1370
- bash scripts/forge-parallel.sh review "$PLAN" --qa opus # read-only look at the combined result
1371
- bash scripts/forge-parallel.sh integrate "$PLAN" --approved --final-review opus # and gate the merge on it
1372
- ```
1373
-
1374
- The reviewer gets the overall goal, every task's complete requirements (bounded to about 80 KB,
1375
- truncated visibly), what forge observed that no per-task reviewer saw (accepted known issues,
1376
- scope drift, guard notes, the verification result including a `FLAKY` pass and coverage
1377
- warnings), the combined diff (inline up to 400 KB), and the same labelled-findings contract —
1378
- with an emphasis on cross-task interactions, interface mismatches and ordering races. `review`
1379
- reads `integration.base..integration tip` from a disposable checkout, writes
1380
- `<plan>/final-review-<epoch>-<pid>/` (`qa.input`, `qa.last`, `verdict`, `qa.gate`, …), keeps
1381
- `<plan>/final-review` and `final-review.verdict` pointing at the newest, and never touches your
1382
- branch or the integration branch. With `integrate --final-review`, it runs after verification
1383
- and before your branch can move: `PASS` (or a `FAIL` accepted by `--qa-threshold`) continues,
1384
- anything else stops with exit `5`; an infrastructure stop exits `8`. The QA spec is `--qa`, else
1385
- the one remembered in `final_qa`, else the single spec every task shares (otherwise it asks).
1386
- Tasks that are not merged are not in the result, and it says so. `integrate` also names unmerged
1387
- tasks even without a review.
1388
-
1389
- #### Splitting and combining plans
1390
-
1391
- `run --retry-failed N` removes most of the reason to split a plan (a failed task no longer holds
1392
- the rest of a run). When you still want independent work in separate plans — separate locks,
1393
- separate integration branches, one verified at a time — these two make it a supported operation
1394
- instead of hand-built git plumbing:
1395
-
1396
- - `split <plan> <new-plan> --tasks a,b` moves tasks **with their saved state** (attempts,
1397
- findings, reviewed commit, status) into a new plan: their branches and worktrees are renamed to
1398
- the new run (`forge/<new>/<id>`), the new integration branch starts at the source's accepted
1399
- tip, dependencies on already-merged tasks are dropped, and the source forgets the moved rows
1400
- (`split-<new>.txt` records them). It refuses — changing nothing — for a Fractal-backed plan, a
1401
- MERGED or RUNNING task, a selection that leaves a dependent behind, or a dependency that is
1402
- neither moving nor merged. Copy first, delete last; an interrupted split rolls back.
1403
- - `combine <new-plan> <plan-a> <plan-b>…` joins plans whose tasks are **all `MERGED`** into one
1404
- plan: a new integration branch merges each source's, the per-task records are copied, and
1405
- `run <new-plan>` then only verifies (no model call) so `integrate` works unchanged. It refuses
1406
- different repositories, locked or dirty sources, duplicate task ids, and any task left behind;
1407
- a merge conflict exits `6` after removing everything it created. `--verify` sets the combined
1408
- plan's check; `--dry-run` previews and predicts conflicts. Task branches keep their original
1409
- names.
1410
-
1411
- #### Scope drift
1412
-
1413
- After each dwarf, the runner compares what the task **declared** in `files` against what it
1414
- actually touched (a declared directory covers the paths beneath it). Undeclared paths appear
1415
- in the summary and in the QA prompt:
1416
-
1417
- ```
1418
- scope drift (touched files the task did not declare):
1419
- auth src/db.py
1420
- ```
1421
-
1422
- Never a failure — a dwarf that genuinely needed one more file did the right thing. What it
1423
- buys is that a merge `CONFLICT` two waves later arrives with its cause already named.
1424
-
1425
- #### `tasks.tsv`
1426
-
1427
- Tab-separated, one row per task, written during decomposition:
1428
-
1429
- ```
1430
- # id deps difficulty files dwarf qa title
1431
- middleware - high src/auth/middleware.py - - Auth middleware
1432
- tokens - medium src/auth/token.py - - Token model
1433
- route middleware,tokens medium src/routes/login.py - - Login route
1434
- docs - low README.md - - Document the auth flow
1435
- ```
1436
-
1437
- | Column | Meaning |
1438
- |---|---|
1439
- | `id` | short unique slug; also the worktree and branch name |
1440
- | `deps` | comma-list of task ids that must land first, or `-` |
1441
- | `difficulty` | `low` \| `medium` \| `high` |
1442
- | `files` | expected touch set, comma-separated — **drives disjointness** |
1443
- | `dwarf` / `qa` | `-` to resolve from the routing rules; an explicit spec always wins |
1444
- | `title` | one line, shown in the approval table and the capsule |
1445
-
1446
- Each task also needs `tasks/<id>/prompt.md`, written the same way a single-task forge goal is.
1447
-
1448
- `files` is a **promise, not a prediction**: it's what the wave planner trusts when deciding
1449
- what may run concurrently, and what the capsule tells other dwarves not to touch. An
1450
- understated `files` is how two dwarves end up in the same file.
1451
-
1452
- `plan` writes resolved specs back into the `dwarf`/`qa` columns, so `run` never consults the
1453
- routing rules — and an explicit value survives a re-plan, which is what makes a gate override
1454
- durable. (`UNASSIGNED` is treated as a leftover marker and re-resolved.)
1455
-
1456
- ### `scripts/forge-memory.sh`
1457
-
1458
- ```
1459
- forge-memory.sh inject <repo> <planner|dwarf|qa> # the block to prepend to a prompt
1460
- forge-memory.sh note <planner|dwarf|qa> # the instruction to append
1461
- forge-memory.sh record <repo> --last <file> --role <r> [--run-id X] [--task T]
1462
- [--model M] [--verdict V] [--duration S]
1463
- forge-memory.sh show <repo>
1464
- forge-memory.sh prune <repo> # re-apply staleness and the cap now
1465
- forge-memory.sh spend <repo> [--run <run-id>] # what forge has cost this repo
1466
- ```
1467
-
1468
- `record` appends to the ledger and then rebuilds `memory.md` from the whole ledger — the
1469
- rebuild is idempotent, so a crashed run can leave the ledger a row short but can never leave
1470
- memory half-written. Editing `memory.md` by hand therefore does not stick; remove the ledger
1471
- rows instead.
1472
-
1473
- ```bash
1474
- # what would a dwarf actually be told about this repo?
1475
- bash scripts/forge-memory.sh inject ~/dev/api dwarf
1476
-
1477
- # start over
1478
- rm -rf ~/dev/api/.forge
1479
- ```
1480
-
1481
- #### Spend
1482
-
1483
- The ledger already holds a row per dispatch and is never injected into a prompt, so
1484
- accounting from it costs nothing:
1485
-
1486
- ```bash
1487
- bash scripts/forge-memory.sh spend ~/dev/api
1488
- ```
1489
- ```
1490
- 14 dispatch(es) across 3 run(s), 1h12m of model time
1491
- Durations sum concurrent work, so they exceed the wall-clock of a parallel run.
1492
-
1493
- by model disp total mean
1494
- sol:xhigh 4 38m20s 9m35s
1495
- opus 7 22m10s 3m10s
1496
- gemini:low:antigravity 3 11m30s 3m50s
1497
-
1498
- by role disp total
1499
- dwarf 7 49m50s
1500
- qa 7 22m10s
1501
- ```
1502
-
1503
- **Time and dispatch counts, never tokens or money.** The six CLIs expose usage differently
1504
- or not at all, and a number forge cannot actually measure would be worse than no number.
1505
-
1506
- `duration_s` is the ledger's tenth column, appended at the end on purpose: a ledger written
1507
- by an older forge — possibly already committed and shared — has nine fields, and awk yields
1508
- an empty string for the missing one instead of shifting every column. Those rows are counted
1509
- but not timed, and the output says so.
1510
-
1511
- ### `scripts/forge-install.sh`
1512
-
1513
- ```
1514
- forge-install.sh [--dry-run]
1515
- ```
1516
-
1517
- Links this directory into every installed harness. Skips harnesses that aren't present,
1518
- skips a destination that exists and isn't a symlink (it will never clobber a directory you
1519
- may have edited in place), and reports opencode as auto-detected.
1520
-
1521
- ---
1522
-
1523
- ## Model registry
1524
-
1525
- `registry.tsv` is the single source of truth for alias resolution. Four tab-separated
1526
- columns, plus an optional fifth:
1527
-
1528
- ```
1529
- # alias harness model effort [timeout]
1530
- sol codex gpt-6.1-sol <=ultra
1531
- sol openclaude gpt-6.1-sol <=max
1532
- gemini-pro antigravity gemini-3.1-pro low,high
1533
- opus antigravity claude-opus-4-6-thinking none
1534
- grok grok grok-4.7 <=xhigh
1535
- grok opencode github-copilot/grok-4.6 -
1536
- ```
1537
-
1538
- | `effort` form | Means |
1539
- |---|---|
1540
- | `<=X` | any effort on that harness's ladder up to and including `X` |
1541
- | `a,b,c` | exactly these efforts — gaps are real, and this is how they're expressed |
1542
- | `none` | takes no effort flag at all; passing one errors |
1543
- | `-` | no validated ladder; forwarded verbatim |
1544
-
1545
- The optional fifth column, `timeout`, is a per-model default dispatch limit in seconds (`-`
1546
- or absent = the built-in default). It applies only when neither `--timeout` nor
1547
- `FORGE_TIMEOUT` was given and is scaled by the resolved effort (see
1548
- [timeout precedence](#scriptsforge-dispatchsh)). Rows without it behave exactly as before.
1549
-
1550
- **Row order is meaningful:** the *first* row for an alias is that alias's default harness.
1551
- `--dwarf sol` means sol on codex; `--dwarf sol:high:openclaude` moves the same model
1552
- elsewhere.
1553
-
1554
- Adding a model is one line in this file. Nothing else needs to change.
1555
-
1556
- ---
1557
-
1558
- ## Run artifacts
1559
-
1560
- Fractal adds a managed workspace alongside the runner artifacts described here:
1561
-
1562
- ```text
1563
- ${XDG_STATE_HOME:-$HOME/.local/state}/forge/fractal/
1564
- runtime/ isolated pinned Fractal and wiki environment
1565
- provenance.json installed versions and source checksum
1566
- installs/ installation attempts and diagnostics
1567
- runs/<run-id>/run.json effective routing, limits and runner binding
1568
- runs/<run-id>/controls/ requested controls and observed results
1569
- runs/<run-id>/tasks/<task-id>/
1570
- request.json / state.json task request and execution checkpoint
1571
- initialization.json control repository and Fractal ledger provenance
1572
- control/ Fractal metadata, separate from product changes
1573
- nodes/<node-id>/product/ isolated product candidate
1574
- nodes/<node-id>/steps/ fresh dispatcher attempts and raw logs
1575
- outcome.json captured Forge acceptance and verification
1576
- ```
1577
-
1578
- The original solo/plan directory stores `fractal-selection.json`; decomposed plans also
1579
- store `fractal-routing.json`. Prefer `./forge fractal runs`, `tree`, `logs` and `report`
1580
- to direct ledger access. Inspection and stop never delete managed work.
1581
-
1582
- Each stage writes into the run directory, which lives **outside** the repo — anything forge
1583
- writes inside the working tree would show up in the dwarf's own diff and land in front of qa
1584
- as if the dwarf had written it.
1585
-
1586
- ```
1587
- <run-dir>/
1588
- <role>.resolved what the spec resolved to (model, effort, harness, any clamp)
1589
- <role>.prompt the exact prompt sent
1590
- <role>.cmd the exact command line executed
1591
- <role>.log full transcript
1592
- <role>.last the final message — read this first
1593
- <role>.infra only after an infrastructure failure: class, retry_after, detail
1594
- <role>.orphans only when processes were left running after the harness exited
1595
- changes.diff the dwarf's real diff, which is qa's input
1596
- ```
1597
-
1598
- A solo run also records, for forge-ui and for you:
1599
-
1600
- ```
1601
- <run-dir>/
1602
- status RUNNING, then PASS|FAIL|UNKNOWN|NOCHANGES|INVALIDATED|TIMEOUT|INFRA|INTERRUPTED|ERROR
1603
- dwarf.spec, qa.spec the specs the run was given
1604
- attempts.tsv epoch, attempt number, status, infrastructure class, dwarf spec; one row per invocation
1605
- ```
1606
-
1607
- Plus, in the repo itself and surviving the run:
1608
-
1609
- ```
1610
- .forge/memory.md what forge learned here, injected into future prompts
1611
- .forge/ledger.tsv one row per dispatch, ever; never injected
1612
- ```
1613
-
1614
- A decomposed run adds:
1615
-
1616
- ```
1617
- <plan-dir>/
1618
- goal.txt
1619
- tasks.tsv
1620
- waves.tsv, deferrals.txt
1621
- tasks/<id>/{capsule.md,prompt.md,dwarf.*,qa.*,changes.diff,status}
1622
- tasks/<id>/attempt, attempts.tsv attempts spent; one audit row per entry (incl. refunded stops)
1623
- tasks/<id>/known_issues.md, qa.gate findings tolerated by --qa-threshold, and the gate's decision
1624
- tasks/<id>/guard.txt what forge observed about the implementer (self-commit, orphans, promises)
1625
- tasks/<id>/infra.txt, resume, noretry infrastructure stop record; QA-only resume marker; no-progress marker
1626
- tasks/<id>/qa_threshold optional per-task blocking threshold
1627
- results.tsv id status branch
1628
- timeout, qa_threshold, verify_retries options remembered from run/retry flags
1629
- integration.base commit the integration branch started from
1630
- verification.{status,exit,log,command,fingerprint} the combined verification
1631
- verification.{flaky,retry.log} only when a failing command passed on rerun
1632
- verification.coverage.txt suites the verify command does not run (advisory)
1633
- ```
1634
-
1635
- with worktrees and branches at:
1636
-
1637
- ```
1638
- <repo>/../.forge-worktrees/<run-id>/<task-id> worktree
1639
- forge/<run-id>/<task-id> task branch
1640
- forge/<run-id>-integration integration branch
1641
- ```
1642
-
1643
- The worktree root is a **sibling of the repo, never `TMPDIR`**. Isolated worktrees are never
1644
- pushed, so a temp root that gets cleaned takes the only copy of that work with it.
1645
-
1646
- ---
1647
-
1648
- ## Safety guarantees
1649
-
1650
- - Solo runs leave source changes uncommitted. Forge creates private review commits, but
1651
- does not commit on the solo source branch, push, or reset the user tree.
1652
- - In a decomposed run, commits and merges happen **only** on the `forge/<run-id>/*` task
1653
- branches and the `forge/<run-id>-integration` branch — branches forge created for itself.
1654
- Never your branch, never a push. Your branch and working tree are untouched for the whole
1655
- run.
1656
- - Bypass flags are never added on forge's own initiative. `--yolo-dwarf` / `--yolo-qa` are the
1657
- only route to an unsandboxed run, so that choice is always explicit and visible.
1658
- - **One dwarf per working tree, always.** Two agents editing one tree interleave their edits
1659
- into a diff neither of them wrote and qa cannot untangle. Parallelism comes from separate
1660
- worktrees, which is why worktrees are mandatory there rather than an optimization.
1661
- - A failed task's branch and worktree are **never** deleted — they're the only record of what
1662
- went wrong and the only thing to retry from. A successful task's worktree is removed only
1663
- after `results.tsv` is written and read back.
1664
- - QA runs in a disposable repository. Mutations to the source or review snapshot invalidate
1665
- acceptance. Disabled editing tools alone do not prevent writes through Bash.
1666
-
1667
- ### Preconditions
170
+ → [forge-ui](docs/forge-ui.md)
1668
171
 
1669
- - Not a git repo → decomposed runs refuse. Worktrees are impossible and there's no honest
1670
- fallback.
1671
- - Uncommitted changes → **warned prominently.** Worktrees branch from a commit, so your local
1672
- edits are invisible to every dwarf. This is the likeliest route to a confusingly wrong
1673
- result.
172
+ ## Documentation
1674
173
 
1675
- ---
1676
-
1677
- ## Troubleshooting
1678
-
1679
- | Symptom | Cause / fix |
174
+ | Page | What's in it |
1680
175
  |---|---|
1681
- | skill missing in **codex** only | frontmatter isn't strict YAML. Codex refuses to load and logs `invalid YAML`; the other four are lenient and load it fine. Note codex's line numbers exclude the opening `---`, so its "line 3" is the file's line 4 |
1682
- | skill missing in **antigravity** | `agy` ignores `~/.gemini/skills` entirely — skills load only inside a plugin. Re-run `forge-install.sh` |
1683
- | antigravity runs an **old version** | `agy plugin install` copies. Re-run `forge-install.sh` after every edit |
1684
- | duplicate entry in **opencode** | opencode auto-scans `~/.claude/`; a `~/.config/opencode/command/forge.md` on top is redundant |
1685
- | agy: `Find command timed out` | missing `--add-dir <repo>` — forge passes it; a hand-built command must too |
1686
- | agy dwarf ends with an error and an empty diff | it tried to run a shell command without yolo. A single denial aborts the run — use `--yolo-dwarf` |
1687
- | dwarf produced no changes | usually an ambiguous prompt it couldn't resolve headlessly. Nobody can answer a question mid-run |
1688
- | task status `ERROR` | worktree creation or a dispatch failed — read `tasks/<id>/dwarf.out` / `qa.out` |
1689
- | task status `UNKNOWN` | QA never emitted a verdict line; read `tasks/<id>/qa.last`. `retry` (or `run --retry-failed N`) asks QA again on the unchanged work — no dwarf runs, no attempt is spent |
1690
- | task status `INFRA`, `run` exits `8` | a quota/usage limit, auth problem, rate limit, network error or empty answer stopped a dispatch — not a task failure, and no attempt was spent. Read `tasks/<id>/infra.txt` (`class=`, `retry_after=`), fix the cause, run the same command again. `run --infra-retries M` waits it out instead. Dependents stay `PENDING`, never `BLOCKED` |
1691
- | a task merged although its reviewer said FAIL | `--qa-threshold` accepted it: every CONFIRMED finding was labelled and milder than the threshold. See `tasks/<id>/known_issues.md` and `qa.gate`; nothing is accepted without an explicit threshold |
1692
- | `accept` exits `3` "changed since it was reviewed" | the branch or worktree moved after QA, so accepting would sign off on unreviewed code. `retry` for a fresh review |
1693
- | `combine` / `split` refuses | the message names why: unmerged task left behind, duplicate ids, locked or dirty plan, a Fractal-backed plan, or a selection that strands a dependent. Nothing was changed |
1694
- | `integrate --final-review` stops with exit `5` | the whole-run reviewer found a blocking defect (or never reached a verdict); findings are in `<plan>/…/final-review/qa.last`. Your branch is untouched. Exit `8` means the reviewer hit a quota/auth stop: run it again |
1695
- | `FLAKY` in the summary, verification `PASS` | the verify command failed once and passed on rerun. Read `verification.log` (the first run) — an intermittent failure can be a real bug. `--verify-retries 0` restores the strict single run |
1696
- | `verify coverage: suite '…' is not run by the verify command` | the project defines a suite your `--verify` skips. Advisory only; add it to the command (or `.forge/verify`) if it should gate the merge |
1697
- | implementer guard notes in the summary | the dwarf committed, switched branch, left processes running, or promised later work. Recorded in `tasks/<id>/guard.txt`; the diff and the reviewer still decide. Leftover processes were stopped |
1698
- | task status `CONFLICT` | QA passed but the merge conflicted — `files` was understated somewhere. Check the run's scope-drift list: it usually names the file |
1699
- | task status `TIMEOUT` | that role exceeded the dispatch timeout and was killed. Raise `--timeout`, or `retry` — the partial edit is still in the worktree |
1700
- | a dispatch never returns | it can't any more: every backend is killed at its timeout (`--timeout`, else `FORGE_TIMEOUT`, else the registry's `timeout` column, else 45 min — 60 min for the default `xhigh` QA/planner effort) |
1701
- | a task won't re-run: "already exists" | a worktree directory was deleted by hand. `run` and `retry` prune stale registrations first, so re-run rather than deleting the branch |
1702
- | a failed task is expensive to redo | it isn't — `forge-parallel.sh retry <dir> <id> [--dwarf <spec>]` re-runs that one task with its reviewer's findings, in the worktree it already has |
1703
- | an interrupted run seems lost | run `run` again; merged tasks are skipped, not re-dispatched |
1704
- | every wave has exactly one task | `files` sets overlap across most tasks; the decomposition isn't actually parallel |
1705
- | dwarves reimplement already-merged work | `goal.txt` or `files` missing, so the capsule carries no useful status |
1706
- | several tasks report "produced no changes" | the decomposition made tasks that weren't independently actionable — use a coarser `--decompose-level`, not more retries |
1707
- | `retry` says "produced no new changes" | the dwarf returned a byte-identical diff, so no reviewer was paid to read the same code twice. Escalate with `--dwarf`, or fix the prompt |
1708
- | a new file's code never reached QA | fixed: untracked files are diffed against `/dev/null`. A hand-built capture using only `git status --porcelain` shows the reviewer a filename and no content |
1709
- | claude dwarf ends "I need your permission to edit" | a permission mode that prompts for Edit. forge uses `acceptEdits` plus an explicit Bash allowance, because `auto` allows shell but prompts for Edit, and `acceptEdits` alone allows Edit but prompts for real shell commands |
1710
- | `.forge/` turns up in a review | a diff capture is missing `':(exclude).forge'` |
1711
- | memory stays empty | nothing durable was learned — the common, correct case. The ledger still has a row per dispatch |
1712
- | a wrong fact keeps reappearing | `memory.md` is rebuilt from the ledger; delete the ledger rows, not the memory line |
1713
- | dwarves build incompatible interfaces | the seam was never agreed — plan the approach, or use `--planner` |
1714
- | Fractal was installed but a new unattended run uses ordinary execution | installation never activates it; pass `--fractal` explicitly |
1715
- | `Fractal selected but unavailable` | run `./forge fractal doctor`, then the suggested managed install; resume retains the selected backend |
1716
- | existing run rejects a new backend, pool or limit | selection and bounds are recorded; create a new run directory for changed settings |
1717
- | Fractal reports completion but Forge has not accepted the work | inspect QA, verdict and verification; execution completion does not establish acceptance |
1718
- | Fractal import stops with `source_drift` | the source changed after its snapshot; preserve both versions and inspect the candidate instead of forcing import |
1719
- | Fractal work was paused or its coordinator exited | inspect `status` and `logs`, then use explicit `resume RUN_ID`; retry starts a new attempt |
1720
- | browser dashboard closed while work is running | execution is independent; reopen with `./forge fractal open RUN_ID` |
1721
- | costs are unknown or a dollar cap is rejected | the bridge only reports available usage; v1 cannot enforce a spending cap |
1722
-
1723
- For per-harness invocation details, effort ladders and known CLI failure modes, see
1724
- [`references/harnesses.md`](references/harnesses.md). For decomposition mechanics, see
1725
- [`references/decompose.md`](references/decompose.md). For memory internals, see
1726
- [`references/memory.md`](references/memory.md).
1727
-
1728
- ---
1729
-
1730
- ## Repository layout
1731
-
1732
- ```
1733
- forge/
1734
- ├── SKILL.md the skill itself — what the orchestrating model reads
1735
- ├── registry.tsv alias → (harness, model, effort). Add a row to teach forge a model
1736
- ├── README.md this file
1737
- ├── LICENSE MIT
1738
- ├── package.json the npm package: its name, version and the files it ships
1739
- ├── forge launcher, on PATH from npm: `forge install`, `fractal`, `jev`, `dataset` and `ui`
1740
- ├── scripts/
1741
- │ ├── forge-dispatch.sh resolve a spec → run one role on one harness
1742
- │ ├── forge-solo.sh the whole single-task run: dwarf → diff → qa
1743
- │ ├── forge-parallel.sh plan / run / retry / integrate for decomposed runs
1744
- │ ├── forge-parallel-options.sh flags shared by several forge-parallel.sh subcommands
1745
- │ ├── forge-lib-task.sh attempt log, infra stops, QA-only resume, guards, severity gate, report blocks
1746
- │ ├── forge-lib-verify.sh verify command lookup and the coverage report
1747
- │ ├── forge-lib-plans.sh accept / split / combine
1748
- │ ├── forge-lib-review.sh the opt-in whole-run review (review, integrate --final-review)
1749
- │ ├── forge-schedule.py readiness scheduler: in-run retries, infrastructure pauses
1750
- │ ├── forge-contract.py the shared QA/dwarf prompt contract and the severity gate
1751
- │ ├── forge-guard.py implementer guards: session wrapper, orphan reaping, promise scan
1752
- │ ├── forge-verify-coverage.py suites the project defines that the verify command skips
1753
- │ ├── forge-memory.sh inject / note / record / spend project memory
1754
- │ ├── forge-fractal.py Fractal CLI and internal adapter entrypoint
1755
- │ ├── forge-fractal-options.sh runner selection, limit flags and help
1756
- │ ├── forge-fractal-dispatch.sh bridge entrypoint for existing pipelines
1757
- │ ├── forge-install-fractal.sh isolated optional runtime installer
1758
- │ ├── forge_fractal/ nested execution, recovery, inspection and dashboard
1759
- │ ├── forge-ui.py `forge ui open|status|stop`: the one shared forge-ui server
1760
- │ ├── forge-ui-options.sh `--ui|--no-ui` and the fail-open launch, sourced by the runners
1761
- │ ├── forge_registry.py the run registry: where run dirs are noted, read by forge-ui
1762
- │ ├── forge-dataset.py `forge dataset` entry point
1763
- │ ├── forge_dataset/ the run dataset: capture hooks, readers, importer, status checks
1764
- │ └── forge-install.sh install into every harness that needs it (opencode and grok find it themselves)
1765
- ├── ui/ forge-ui: read-only local run viewer (`forge_ui` package, tests, API contract)
1766
- └── references/
1767
- ├── ui.md forge-ui: commands, env vars, shared server, state, privacy
1768
- ├── harnesses.md per-harness invocation, ladders, failure modes
1769
- ├── decompose.md tasks.tsv schema, difficulty criteria, wave algorithm
1770
- ├── fractal.md opt-in backend, limits, recovery and observability
1771
- ├── memory.md FORGE_LEARNING grammar, promotion and pruning rules
1772
- └── dataset.md the run dataset: what is captured, where, and its invariants
1773
- ```
1774
-
1775
- ## Execution integrity and offline checks
1776
-
1777
- Forge now captures solo changes against a recorded starting snapshot, including staged edits,
1778
- without altering the user's index. Existing work is recorded separately in `existing.diff`.
1779
- QA receives the full implementation requirements and reviews a disposable repository; source
1780
- or snapshot changes invalidate the result. Only the recorded reviewed task commit can merge.
1781
-
1782
- Parallel execution rechecks dependencies before each task and retry. Failed prerequisites leave
1783
- consumers BLOCKED. Directory and child file declarations are serialized.
1784
-
1785
- `forge-dispatch.sh doctor --spec sol --role dwarf` validates a selected model recipe using local
1786
- CLI help. Solo and parallel runners preflight the whole selected pipeline before implementation.
1787
- These checks do not establish authentication, billing or live model availability.
1788
-
1789
- Use `forge-parallel.sh run <plan> --verify 'your check command'` or put the command in
1790
- `.forge/verify` to verify the combined result. Output and exit status are saved with the plan.
1791
- Integration checks a candidate containing the current user branch before updating it. A missing
1792
- command is reported as UNVERIFIED; a failing configured check blocks integration. The project's
1793
- own test suites that the command does not run are named as warnings (advisory), and a failing
1794
- command is rerun once so a flaky lane is reported rather than failing the run — see
1795
- [verification coverage and flaky reruns](#verification-coverage-and-flaky-reruns).
1796
- `integrate` also names any task that is not merged and therefore not part of what it delivers.
1797
-
1798
- Run `bash tests/check.sh` for offline regression checks. See [tests](tests/README.md) for skill
1799
- invocation scenarios and verification limits. There is currently no CI workflow.
1800
-
1801
-
1802
- ## Runtime efficiency and measurement
1803
-
1804
- Shell entrypoints require Python 3 (standard library only) in addition to Bash and Git.
1805
- The [runtime helper decision](references/runtime.md) records the module boundaries and tradeoffs.
1806
- Solo and parallel runners default to `--output summary`; use `--output full` to include raw
1807
- backend output. Direct dispatch keeps its full-output default. Read each `dwarf.last` and
1808
- `qa.last` once, and retrieve `.log` or `.out` only when investigating a problem.
1809
-
1810
- QA receives complete requirements, approach, ownership, retry findings and distinct memory
1811
- facts once, with review instructions separate from implementation instructions. Prompts and
1812
- full binary diffs remain on disk. Review repositories receive self-contained tree object packs,
1813
- create baseline/final commits, and check out the final tree once. Exact tree comparisons and
1814
- all source, review and integration fingerprint checks remain acceptance boundaries.
1815
-
1816
- `FORGE_CAPABILITY_CACHE` optionally selects an offline help cache. Runners share a run-local
1817
- `capabilities/` cache across preflights and dispatches. Keys cover the executable's resolved
1818
- path, device/inode, size and modification/change timestamps, help command mode, registry
1819
- contents and dispatch recipe. Every selected spec, effort and permission recipe is still
1820
- validated; cached help never proves authentication or live model availability.
1821
-
1822
- Run `bash scripts/forge-dispatch.sh report <run-dir>` for JSON attempt records, including
1823
- failures and timeouts, with project memory enabled or disabled. `attempts/<role>-<id>/`
1824
- retains prompts, final responses, raw logs, resolution, command and `metrics.json` per dispatch.
1825
- Pipeline records capture preparation, preflight, snapshot, verification and total time;
1826
- dispatch records capture preparation, preflight, model and total time. Nested pipeline
1827
- `dispatch` time includes dispatch overhead: do not add nested totals together. Timing uses
1828
- monotonic seconds. A still-running or forcibly killed record has a null exit code and may
1829
- have incomplete timings. A dispatch that stopped on an infrastructure failure records its class
1830
- (`quota`, `auth`, `rate_limit`, `network`, `empty`) as `infra_class`, and keeps the `.infra` and
1831
- `.orphans` files beside the other attempt artifacts.
1832
-
1833
- Prompt bytes are separate from native input, cached-input and output token counts. Codex
1834
- uses `turn.completed` usage; Claude uses result usage (input includes uncached, cache-read
1835
- and cache-creation tokens). Unsupported or absent usage stays null, never estimated from
1836
- bytes. See [measurement and live evaluation](tests/README.md).
1837
-
1838
- Measured results, including the live comparison's higher total token use and latency, are
1839
- recorded in [the September 7 evaluation](tests/benchmark-2026-09-07.md).
1840
-
1841
- ## Ripwire context
1842
-
1843
- Forge automatically adds optional Ripwire 0.4.0 repository evidence to planner, dwarf,
1844
- and QA prompts across all six harnesses. Setup and interactive runners offer a pinned,
1845
- verified installation; headless runs continue when it is missing. Use `--no-ripwire`
1846
- or `FORGE_RIPWIRE=off` to disable it. Decomposed plans preserve opt-out across resumes
1847
- and retries. See [installation, controls and attempt artifacts](references/ripwire.md).
1848
-
1849
- ---
176
+ | [Install and update](docs/install.md) | npm or a git checkout, the doctor, what the installer does per CLI, updating |
177
+ | [Command reference](docs/commands.md) | every `/forge` flag with examples, the `forge` command, the runner scripts and their exit codes |
178
+ | [Models, efforts and harnesses](docs/models.md) | the `alias:effort:harness` spec, effort ladders and clamping, yolo mode, the model registry |
179
+ | [Decomposed runs](docs/decomposed-runs.md) | decompose levels and difficulty, routing, waves, capsules, planning, resuming, split and combine, `tasks.tsv` |
180
+ | [Reviews, retries and recovery](docs/reviews.md) | verdicts, retries, infrastructure stops, severity thresholds, accepting, whole-run and shadow reviews, guards, verification, fail-first |
181
+ | [Fractal execution](docs/fractal.md) | the optional nested-execution backend: install, run, inspect, bounds and recovery |
182
+ | [Project memory](docs/memory.md) | `.forge/memory.md` and the ledger, what each role is told, whether to commit it, spend |
183
+ | [forge-ui](docs/forge-ui.md) | the run viewer: commands, the shared server, environment variables, privacy |
184
+ | [Run dataset](docs/dataset.md) | every run's inputs and outcomes, kept for training decision models |
185
+ | [Run artifacts](docs/artifacts.md) | the run directory, a plan's files, worktrees and branches |
186
+ | [Safety](docs/safety.md) | what forge never does to your branch, preconditions, execution integrity |
187
+ | [Ripwire and Jev](docs/add-ons.md) | optional repository context and advisory judgments |
188
+ | [Troubleshooting](docs/troubleshooting.md) | symptoms, causes and fixes |
189
+ | [Development](docs/development.md) | repository layout, offline checks, runtime measurement |
1850
190
 
1851
191
  ## License
1852
192