@giamme/forge 0.0.0-stage → 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 (152) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/LICENSE +21 -0
  3. package/README.md +194 -2
  4. package/SKILL.md +123 -0
  5. package/VERSION +1 -0
  6. package/docs/README.md +26 -0
  7. package/docs/add-ons.md +33 -0
  8. package/docs/artifacts.md +69 -0
  9. package/docs/commands.md +315 -0
  10. package/docs/dataset.md +20 -0
  11. package/docs/decomposed-runs.md +268 -0
  12. package/docs/development.md +95 -0
  13. package/docs/forge-ui.md +92 -0
  14. package/docs/fractal.md +181 -0
  15. package/docs/install.md +160 -0
  16. package/docs/memory.md +156 -0
  17. package/docs/models.md +118 -0
  18. package/docs/reviews.md +231 -0
  19. package/docs/safety.md +55 -0
  20. package/docs/troubleshooting.md +54 -0
  21. package/forge +79 -0
  22. package/package.json +27 -4
  23. package/references/dataset.md +329 -0
  24. package/references/decompose.md +667 -0
  25. package/references/fractal-dashboard.md +21 -0
  26. package/references/fractal.md +307 -0
  27. package/references/harnesses.md +365 -0
  28. package/references/jev.md +865 -0
  29. package/references/memory.md +228 -0
  30. package/references/planning.md +42 -0
  31. package/references/ripwire.md +47 -0
  32. package/references/runtime.md +64 -0
  33. package/references/solo.md +113 -0
  34. package/references/ui.md +112 -0
  35. package/registry.tsv +81 -0
  36. package/scripts/forge-artifact.sh +78 -0
  37. package/scripts/forge-contract.py +430 -0
  38. package/scripts/forge-dataset.py +6 -0
  39. package/scripts/forge-dispatch.sh +671 -0
  40. package/scripts/forge-fail-first.py +195 -0
  41. package/scripts/forge-fractal-dispatch.sh +4 -0
  42. package/scripts/forge-fractal-options.sh +54 -0
  43. package/scripts/forge-fractal.py +6 -0
  44. package/scripts/forge-guard.py +266 -0
  45. package/scripts/forge-install-fractal.sh +4 -0
  46. package/scripts/forge-install-ripwire.sh +52 -0
  47. package/scripts/forge-install.sh +135 -0
  48. package/scripts/forge-jev-options.sh +259 -0
  49. package/scripts/forge-jev.py +6 -0
  50. package/scripts/forge-lib-plans.sh +785 -0
  51. package/scripts/forge-lib-review.sh +494 -0
  52. package/scripts/forge-lib-task.sh +302 -0
  53. package/scripts/forge-lib-verify.sh +30 -0
  54. package/scripts/forge-memory.sh +575 -0
  55. package/scripts/forge-metrics.sh +39 -0
  56. package/scripts/forge-parallel-options.sh +76 -0
  57. package/scripts/forge-parallel.sh +1717 -0
  58. package/scripts/forge-plan-check.py +118 -0
  59. package/scripts/forge-prompt.py +45 -0
  60. package/scripts/forge-ripwire.py +136 -0
  61. package/scripts/forge-route.py +344 -0
  62. package/scripts/forge-runs.py +121 -0
  63. package/scripts/forge-runtime.py +586 -0
  64. package/scripts/forge-schedule.py +312 -0
  65. package/scripts/forge-shadow.py +305 -0
  66. package/scripts/forge-solo.sh +347 -0
  67. package/scripts/forge-ui-options.sh +42 -0
  68. package/scripts/forge-ui.py +435 -0
  69. package/scripts/forge-update.sh +71 -0
  70. package/scripts/forge-verify-coverage.py +956 -0
  71. package/scripts/forge-version.py +240 -0
  72. package/scripts/forge_dataset/__init__.py +439 -0
  73. package/scripts/forge_dataset/capture.py +330 -0
  74. package/scripts/forge_dataset/checks.py +109 -0
  75. package/scripts/forge_dataset/cli.py +233 -0
  76. package/scripts/forge_dataset/export.py +1009 -0
  77. package/scripts/forge_dataset/harvest.py +716 -0
  78. package/scripts/forge_dataset/importer.py +412 -0
  79. package/scripts/forge_dataset/outcomes.py +275 -0
  80. package/scripts/forge_dataset/routing.py +278 -0
  81. package/scripts/forge_fractal/__init__.py +84 -0
  82. package/scripts/forge_fractal/bridge.py +46 -0
  83. package/scripts/forge_fractal/cli.py +202 -0
  84. package/scripts/forge_fractal/dashboard.css +138 -0
  85. package/scripts/forge_fractal/dashboard.html +41 -0
  86. package/scripts/forge_fractal/dashboard.js +687 -0
  87. package/scripts/forge_fractal/decomposition.py +262 -0
  88. package/scripts/forge_fractal/execution.py +659 -0
  89. package/scripts/forge_fractal/inspection.py +261 -0
  90. package/scripts/forge_fractal/install.py +158 -0
  91. package/scripts/forge_fractal/invocation.py +94 -0
  92. package/scripts/forge_fractal/pipeline.py +124 -0
  93. package/scripts/forge_fractal/selection.py +179 -0
  94. package/scripts/forge_jev/__init__.py +376 -0
  95. package/scripts/forge_jev/backtest.py +322 -0
  96. package/scripts/forge_jev/cli.py +980 -0
  97. package/scripts/forge_jev/client.py +418 -0
  98. package/scripts/forge_jev/corpus.py +156 -0
  99. package/scripts/forge_jev/coupling.py +244 -0
  100. package/scripts/forge_jev/features.py +24 -0
  101. package/scripts/forge_jev/gates.py +100 -0
  102. package/scripts/forge_jev/memory.py +163 -0
  103. package/scripts/forge_jev/outcome.py +49 -0
  104. package/scripts/forge_jev/predict.py +204 -0
  105. package/scripts/forge_jev/questions.py +661 -0
  106. package/scripts/forge_jev/review.py +73 -0
  107. package/scripts/forge_jev/routing.py +271 -0
  108. package/scripts/forge_jev/subset.py +123 -0
  109. package/scripts/forge_jev/verify.py +453 -0
  110. package/scripts/forge_registry.py +140 -0
  111. package/ui/README.md +347 -0
  112. package/ui/THIRD_PARTY_NOTICES.md +177 -0
  113. package/ui/docs/api.md +970 -0
  114. package/ui/forge_ui/__init__.py +20 -0
  115. package/ui/forge_ui/__main__.py +135 -0
  116. package/ui/forge_ui/diffs.py +454 -0
  117. package/ui/forge_ui/executions.py +436 -0
  118. package/ui/forge_ui/export.py +214 -0
  119. package/ui/forge_ui/findings.py +132 -0
  120. package/ui/forge_ui/fractal.py +582 -0
  121. package/ui/forge_ui/models.py +216 -0
  122. package/ui/forge_ui/reader.py +1424 -0
  123. package/ui/forge_ui/server.py +312 -0
  124. package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
  125. package/ui/forge_ui/static/app.js +1079 -0
  126. package/ui/forge_ui/static/cloud3d.js +647 -0
  127. package/ui/forge_ui/static/cloudmodel.js +375 -0
  128. package/ui/forge_ui/static/cloudui.js +397 -0
  129. package/ui/forge_ui/static/convo.js +295 -0
  130. package/ui/forge_ui/static/datatable.js +232 -0
  131. package/ui/forge_ui/static/diffview.js +235 -0
  132. package/ui/forge_ui/static/favicon.svg +1 -0
  133. package/ui/forge_ui/static/files.js +776 -0
  134. package/ui/forge_ui/static/findings3d.js +134 -0
  135. package/ui/forge_ui/static/gantt.js +97 -0
  136. package/ui/forge_ui/static/graph.js +266 -0
  137. package/ui/forge_ui/static/icons.js +94 -0
  138. package/ui/forge_ui/static/index.html +67 -0
  139. package/ui/forge_ui/static/iso.js +816 -0
  140. package/ui/forge_ui/static/leaderboard.js +311 -0
  141. package/ui/forge_ui/static/map3d.js +45 -0
  142. package/ui/forge_ui/static/modelstats.js +249 -0
  143. package/ui/forge_ui/static/notify.js +179 -0
  144. package/ui/forge_ui/static/panel.js +562 -0
  145. package/ui/forge_ui/static/runmodel.js +667 -0
  146. package/ui/forge_ui/static/scene3d.js +1471 -0
  147. package/ui/forge_ui/static/search.js +120 -0
  148. package/ui/forge_ui/static/shell.js +137 -0
  149. package/ui/forge_ui/static/stagectl.js +150 -0
  150. package/ui/forge_ui/static/style.css +1073 -0
  151. package/ui/forge_ui/static/theme.js +25 -0
  152. package/ui/forge_ui/transcripts.py +608 -0
package/ui/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # forge-ui
2
+
3
+ forge-ui is a small, read-only local web UI for forge runs. It draws them the way
4
+ [archivision](https://github.com/Giamme/archivision) draws a codebase: a 3D isometric map of
5
+ blocks on a plate, with cables between them and lamps that light up as things happen.
6
+
7
+ - **A map of every run**, one district per repo and one block per run, with a lamp per task.
8
+ Filter it by state, repo, kind, model, age or review verdict; what does not match fades
9
+ instead of disappearing.
10
+ - **A run, as planned and as it ran.** *Plan* puts each task in its planned wave, with
11
+ dependency cables, and ends in a *finish* line of three nodes: integration, verification
12
+ and the whole-run **final review**. *Execution* lays the same work out on wall-clock time:
13
+ one block per attempt, in the lane it really ran in, with its dwarf and review inside, the
14
+ attempts that have not started projected by forge-schedule's own rules, and a strip of how
15
+ many slots were busy against `--max-parallel`. Switching between the two moves each task
16
+ from its wave to where it ran.
17
+ - **Click to open.** A task opens to show its attempts, each with its dwarf, its reviewer and
18
+ any shadow reviews, linked; an escalation is a labelled cable between attempts. The
19
+ inspector beside the stage shows what launched each step and what came of it: the exact
20
+ prompt, the model (alias, harness, model, effort, clamping), the timings by phase, tokens,
21
+ the verdict, and every **P0–P3 finding** of every review, the final review included.
22
+ - **Live.** Statuses change in place while a run works (every 2 s while something runs, 5 s
23
+ otherwise); running nodes pulse and show their current phase.
24
+ - **Extras:** replay a run from its recorded times, light its critical path, set block height
25
+ and colour by duration, tokens, attempts or model, and mark shadow reviews that disagreed
26
+ with the primary and how sure Clef was that each task would pass.
27
+ - **Beyond one run:** a full-screen **run cloud** of every run, grouped by project, in five
28
+ layouts with filters and runs you can drag anywhere; **diffs** with each finding under the
29
+ line it names; each dwarf's and reviewer's **conversation**, read from its CLI's log; a
30
+ **model leaderboard**; a **files heatmap**; **Fractal** child-task trees;
31
+ **quick jump** and **notifications**; and **export** of a run as one HTML file. See
32
+ [What you see](#what-you-see).
33
+
34
+ <p align="center">
35
+ <img src="docs/media/hero.webp" width="860" alt="A decomposed run on its 3D plate: eight tasks in their planned waves with a mark for each 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.">
36
+ </p>
37
+
38
+ The 3D view loads three.js from a CDN the first time it is used (see [Privacy](#privacy)).
39
+ Without it (offline, an old browser, no WebGL) the same page shows the 2D dependency graph,
40
+ an execution chart and the same inspector. The server is Python's standard library; there
41
+ is no install or build step.
42
+
43
+ It lives inside forge, in `ui/`, and is forge's companion: runs open it with `--ui`.
44
+
45
+ ## Run it
46
+
47
+ Normally forge launches it. `forge ui open [DIR]` (from the forge checkout) starts or
48
+ reuses one shared detached server and prints the link to a run dir, `forge ui status`
49
+ shows it and `forge ui stop` ends it; see [references/ui.md](../references/ui.md). A run
50
+ starts it itself with `forge-solo.sh --ui` or `forge-parallel.sh plan --ui`.
51
+
52
+ To run the server directly, from `ui/`:
53
+
54
+ ```sh
55
+ python3 -m forge_ui
56
+ ```
57
+
58
+ Open the printed URL (default `http://127.0.0.1:8765/`). Stop with Ctrl-C.
59
+
60
+ - `--plan DIR`: add one plan directory; repeatable, default none.
61
+ - `--root DIR`: scan for plan directories at depth 0–2; repeatable, default none.
62
+ - `--no-registry`: disable the registry, which is read by default.
63
+ - `--registry-dir DIR`: override the registry directory described below.
64
+ - `--port N`: use port N, default `8765`; `0` selects a free port.
65
+ - `--quiet`: suppress request logging; off by default (errors can still be logged).
66
+ - `--open`: open the browser after startup; off by default.
67
+ - `--idle-exit SECONDS`: exit after that many idle seconds; default `0` (never). `forge ui` sets it from `FORGE_UI_IDLE_S`.
68
+ - `--version`: print the version and exit.
69
+ - `-h`, `--help`: print usage and exit.
70
+
71
+ Three examples (replace the example paths with your own):
72
+
73
+ ```sh
74
+ # Registry only
75
+ python3 -m forge_ui
76
+ # One plan directory only
77
+ python3 -m forge_ui --no-registry --plan /path/to/plan
78
+ # A root containing many runs, without registry entries
79
+ python3 -m forge_ui --no-registry --root /path/to/runs
80
+ ```
81
+
82
+ No runs yet? `tools/demo_runs.py` writes made-up ones, the same that fill the pictures here: a
83
+ live run and a few weeks of history across five projects.
84
+
85
+ ```sh
86
+ python3 tools/demo_runs.py /tmp/forge-demo
87
+ python3 -m forge_ui --no-registry --root /tmp/forge-demo --open
88
+ ```
89
+
90
+ ## Where runs come from
91
+
92
+ Sources are checked in this order:
93
+
94
+ 1. Explicit `--plan DIR` directories.
95
+ 2. `--root DIR` itself, its children and grandchildren. Scanning stops when a plan
96
+ directory is found; hidden children and symlinked child directories are skipped.
97
+ 3. Forge's registry: `*.txt` files in `$XDG_CONFIG_HOME/forge/runs/`, defaulting
98
+ to `~/.config/forge/runs/` when XDG_CONFIG_HOME is unset or empty. Each file
99
+ supplies absolute paths, one per line; blank lines and `#` comments are ignored.
100
+ `--registry-dir` replaces this location; `--no-registry` disables it.
101
+
102
+ Plan directories (containing a `tasks.tsv` file) and solo run directories (a `prompt.md`
103
+ and a `repo` file) appear through `--plan` and the registry; `--root` finds plan directories
104
+ only. Stale or invalid paths are skipped. Duplicate real paths appear once,
105
+ with the first source winning. Discovery and disk reads repeat on each API request.
106
+
107
+ ## What you see
108
+
109
+ forge-ui looks like a signal box's route-setting desk: the run is drawn on a panel, every heading
110
+ is an engraved plate, every switch is a push button whose lamp lights when it is on, and lamps
111
+ keep one meaning everywhere (amber running, green merged or passed, red failed, unlit pending).
112
+
113
+ - **Layout:** the *Runs*, *Inspector* and *Tasks* modules sit around the stage. *Immersive* (the
114
+ default) floats them over a stage that fills the window; *Workbench* docks them beside it, and
115
+ the runs list folds to a rail of lamps. Switch with the two layout keys in the header; the
116
+ module keys beside them, or `1`, `2` and `3`, fold and open the runs, the inspector and the
117
+ tasks. On your first visit the inspector and the tasks start folded, so a run opens on its plate. Drag a module's inner edge, or focus it and use the arrow keys, to resize it. The header
118
+ also switches the theme (system, light or dark). All of this is remembered in this browser.
119
+ - **Map (`#/`):** every discovered run, newest first, grouped by repo. By default the last
120
+ 60 runs from the past 14 days; *older runs too* shows the rest. The runs list follows the same
121
+ filter, and sorts by latest activity, state or repo.
122
+
123
+ <img src="docs/media/map.png" width="760" alt="The map: one block per run, in a district per repo, green when done, amber while running, red with failures.">
124
+ - **Run cloud (`#/cloud`):** every run at once, full screen, floating in 3D and grouped by project
125
+ (repo). Pick a layout: *Helix* (one spiral per project, oldest at the bottom), *Sphere*,
126
+ *Galaxy* (a disc per project, newest at the centre), *Timeline* (left to right by start time,
127
+ one lane per project) or *Constellation* (pulled together by project and shared models, with
128
+ lines joining each project's nearest runs). Runs are *Blocks* (a lamp per task on the roof) or
129
+ *Orbs* (a ring of task segments); size is the number of tasks, colour the run's state. The
130
+ filter panel narrows the cloud to exactly the runs wanted: projects, state, kind, difficulty
131
+ (has a task of, or mostly), tasks per run, models (any role, implementer or reviewer), recent
132
+ activity, final review, verification, decompose level and text (goals, ids, task titles).
133
+ Orbit, pan and zoom anywhere; drag a run to put it where you want (Shift: nearer or further,
134
+ Alt: up or down). Dropped positions are kept in this browser per layout; *Positions* resets them
135
+ (with undo) or exports and imports them as a JSON file. Click a run for its card, double-click
136
+ to open it; `#/cloud/runs/<key>` and `#/cloud/projects/<id>` link to a selection. Keys:
137
+ arrows orbit, + − zoom, F frames, P steps through projects, [ ] through runs, L changes the
138
+ layout, B the shape, 0 resets the view, Esc steps back. With *2D*, the cloud lists the same
139
+ filtered runs and offers *Load 3D*.
140
+
141
+ <img src="docs/media/cloud.webp" width="760" alt="The run cloud orbited, then laid out as galaxies and as a timeline with one lane per project.">
142
+ - **Run (`#/runs/<key>`):** a header with the goal, the repo, a describer (each state's count,
143
+ the wall time, how long since anything moved), a bank of push buttons (*Plan* /
144
+ *Execution* / *Files*, *3D* / *2D*, and *View*: height and colour overlays, the *Layers* that
145
+ switch each part of the 3D drawing on or off, and the *Marks*: *Critical path*, *Review marks*,
146
+ *Projected*) and the replay's time base; then the stage, the inspector and the
147
+ task table. The stage frames the run in the part of the window the modules leave free. The
148
+ inspector opens when you choose a task, an attempt or a finish node; with
149
+ nothing chosen it shows the run's timeline, finish and record (*Run details* in the header:
150
+ settings, request, waves, verification output).
151
+ - **Stage (3D):** each review finding stands over its task's block as a mark shaped by its
152
+ severity (P0 spike, P1 diamond, P2 cone, P3 pin, a ring when it has none; a plausible one smaller
153
+ and paler); on an opened task it moves onto the review that raised it, and the final review's sit
154
+ on *Final review*. Hover one for its location and summary; click it to open the diff that
155
+ review read at that finding. Choosing a task keeps its route lit, what it waits on and what
156
+ waits on it (`I` switches this off). Hovering a task shows its card: status, attempts, busy
157
+ time, tokens, models, its worst finding and what waits on it. A block whose roof text is too
158
+ small to read gets its name on a tag that faces you. A merge sends a pulse to *Integration*, a
159
+ failed attempt a red ring across the plate, an escalation a hop with the two models, a final
160
+ review a ring in its verdict's colour, live and during a replay (with reduced motion the block
161
+ lights instead). The keys at the stage's edge fit what is shown (`F`), zoom (`+` `−`), change
162
+ the projection (`V`: perspective, true isometric, top-down), reset the view (`0`), open the
163
+ legend of what the stage draws now (`L`), go full screen, and list every key (`?`). The layers,
164
+ the projection and the route are remembered.
165
+ - **Lamps:** green merged or passed, amber running, red failed, unlit pending. A hollow lamp is
166
+ that family's other case: passed or accepted without a merge (green), interrupted (amber), an
167
+ infrastructure failure, a timeout or blocked behind a failure (red). Review findings are never
168
+ a lamp: their severity is a shape (P0 spike, P1 diamond, P2 cone, P3 pin).
169
+ - **Tables:** the task table and the leaderboard's tables sort by any column (click its header:
170
+ ascending, descending, off), reorder by dragging a header (or Alt+← → on it), and show or hide
171
+ columns from *Columns*, which also resets them. Each table remembers its columns.
172
+
173
+ <img src="docs/media/run.png" width="49%" alt="Planned waves: a run's tasks on a 3D plate with dependency cables, finding marks and the finish nodes, between the runs list and the run's inspector."> <img src="docs/media/execution.png" width="49%" alt="Execution: one block per attempt on wall-clock time, failed attempts in red, with the timeline and critical path in the inspector.">
174
+ - **Inspector:** for the run, its timeline, critical path, peak concurrency and finish line;
175
+ for a task, its models and routing, escalations, timings (busy, model time, overhead,
176
+ queue wait after its dependencies merged), attempts and their executions, review findings
177
+ with the gate decision, Clef predictions against outcomes, and its prompts; for one
178
+ execution, its model, phases, tokens, exact prompt and final message; for the final
179
+ review, its verdict, findings, report, prompt and history.
180
+ - **Files (the run toolbar's third mode):** a heatmap of files (grouped and collapsible by
181
+ directory) against tasks (grouped by wave): planned files outlined, touched files filled by how
182
+ many attempts changed them, drift hatched, merge conflicts ringed, and a dot where another task
183
+ of the same wave planned or touched the file too. Filter by path, sort by path, most touched or
184
+ most tasks, and hide any kind of mark; a cell selects its task and offers that file's diff.
185
+ Below: overlaps, drift, merge conflicts and deferrals as lists.
186
+
187
+ <img src="docs/media/files.png" width="760" alt="The files heatmap: files by directory against tasks by wave, with planned, touched, drift and same-wave overlap marks.">
188
+ - **Models (`#/models`):** a leaderboard of the models forge used. Implementers: tasks, first-attempt
189
+ pass rate with its 95% Wilson interval, censored attempts, retries, escalations away from the
190
+ model, timeouts, gate and human accepts, median model time and tokens. Reviewers: primary,
191
+ shadow and final reviews, the PASS/FAIL split, findings per review by severity, and how often a
192
+ shadow review agreed with the primary. Filter by project, difficulty, kind and period, or keep
193
+ only exploratory picks; open a difficulty for its own table. Routing is not random, so the
194
+ numbers compare what happened, not what would have: the page says so. A model's name opens the
195
+ run cloud filtered to its runs.
196
+
197
+ <img src="docs/media/models.png" width="760" alt="The implementers table: tasks, first-attempt pass rate with its 95% interval, retries, escalations, timeouts, accepts, median model time and tokens per model.">
198
+ - **Diffs (`…/tasks/<id>/diff[/<source>][/file/<path> | /finding/<id>]`, `…/final-review/<name>/diff[/finding/<id>]`):** a task's
199
+ `changes.diff`, its previous or committed diff, or exactly the diff one reviewer read (the last
200
+ diff in that review's prompt), picked from a row of sources with each review's verdict; a final
201
+ review's combined diff. Files are listed with their +/− counts and how many findings point
202
+ into them; each finding is shown under the line it names, those on a line outside the changed
203
+ hunks at the top of their file, and those whose file is not in the diff (or that name no
204
+ location) in a section of their own. n and p move between findings, j and k between files.
205
+ Open it from a task, an attempt's review, an execution or the final review in the inspector, or
206
+ from a finding's mark on the stage (`/finding/<id>` scrolls to that finding and focuses it).
207
+
208
+ <img src="docs/media/diff.png" width="760" alt="The diff a reviewer read, with a P1 finding under the line that compares token digests and a P2 finding under the revoke function.">
209
+ - **Conversations (`…/tasks/<id>/executions/<dwarf-or-qa>/conversation`, `…/executions/<id>/conversation`):**
210
+ what one dwarf or reviewer said and did, read from its agent CLI's `<role>.log`, in the diff's
211
+ place: its messages, its thinking folded, each command with its exit and output, each edit with
212
+ its patch and a link to the file in the diff its review read, other tool calls, and the final
213
+ answer with the tokens and cost. Search it (`/`), keep only what it said, ran or changed, and
214
+ step through its errors (`E`). While the agent works the log is read again every few seconds and
215
+ *Follow* keeps the newest event in view. codex, grok, claude, openclaude and opencode logs are
216
+ read event by event; when a claude run logged only its final answer, the conversation comes from
217
+ Claude's own session file for that run, if it is still on this machine; any other log is shown
218
+ as it is. Open it from an attempt's dwarf or reviewer in the inspector.
219
+
220
+ <img src="docs/media/conversation.png" width="760" alt="A dwarf's conversation: its thinking, the files it wrote, a test run that failed with exit 1 and the run that passed after the fix.">
221
+ - **Fractal child tasks:** when a run used Fractal, a task's inspector shows the tree it was split
222
+ into (status, difficulty, model, phase, cost, the split decision), and the opened task in 3D
223
+ carries the same tree floating above it. A node (`…/tasks/<id>/fractal/<node>`) shows its goal,
224
+ paths, timings, cost and tokens, its decomposition and the children it admitted, its last
225
+ messages, and its candidate diff. Fractal's state is read under `$XDG_STATE_HOME/forge/fractal`
226
+ (default `~/.local/state`), read-only, without following symlinks.
227
+ - **Export:** *Export…* in a run's header downloads the run as one HTML file that opens in any
228
+ browser without forge-ui, offline included (in 2D; in 3D when cdn.jsdelivr.net is reachable).
229
+ It asks each time: by default prompts, reviewer reports, logs, finding details and diffs are
230
+ left out (each finding's headline stays) and local paths are reduced to their last component.
231
+ The file states what it left out, and fetches nothing but three.js: its own policy allows only
232
+ its inline scripts, by hash.
233
+ - **Quick jump:** Ctrl K (⌘K on a Mac), `/` or *Search* opens one search over every run (goal,
234
+ run id, repo, models), task (id and title), project, model and command. Enter opens the pick;
235
+ Shift+Enter shows a run in the cloud; a model shows its runs in the cloud. With nothing typed,
236
+ recent picks and running runs come first.
237
+ - **Notifications:** the bell lists what changed while the page was open: a run that finished or
238
+ ended with failures, a task that failed (FAIL, ERROR, CONFLICT, TIMEOUT, INVALIDATED,
239
+ INTERRUPTED or BLOCKED; INFRA retries are not failures) and a final review's verdict, each
240
+ linked to it. Unread ones show as a count in the tab title and a dot on its icon. *Enable
241
+ browser notifications* asks the browser once; then changes also show as system notifications
242
+ while the tab is in the background, and a hidden tab checks the run list every 30 seconds
243
+ (browsers may check less often after a while). Open tabs share one log and notify once. Runs
244
+ that have not changed for two hours never notify.
245
+ - **Keys on the stage:** with the stage focused, ← → move between tasks (on the map, between
246
+ runs; ↑ ↓ between repos, Enter opens the run), Esc closes, `e` switches plan and execution.
247
+ Anywhere on the page while the stage shows: `F`, `+` `−`, `V`, `0`, `L`, `I`, Shift+F (full
248
+ screen) and `?`. Drag to orbit, right-drag to pan, scroll to zoom once the stage is clicked.
249
+
250
+ URLs such as `http://127.0.0.1:8765/#/runs/<key>/tasks/<id>` are shareable within the
251
+ machine while this server is running and discovering that run dir. *Live refresh* pauses
252
+ polling; a hidden tab polls only the run list, every 30 seconds, and only with browser
253
+ notifications on. Failed requests keep the last good data and show a banner.
254
+
255
+ ## Layout of the code
256
+
257
+ - `forge_ui/reader.py`: discover runs, parse their files and produce summaries and details.
258
+ - `forge_ui/executions.py`: the per-dispatch records (`attempts/<role>-<uuid>/`), nested and
259
+ matched to attempts, through a cache keyed by each file's identity and mtime.
260
+ - `forge_ui/findings.py`: P0–P3 findings and the gate, parsed by forge's own `scripts/forge-contract.py`.
261
+ - `forge_ui/server.py`: serve the JSON API and static assets, with GET/HEAD only.
262
+ - `forge_ui/__main__.py`: CLI options, local startup and optional browser opening.
263
+ - `forge_ui/__init__.py`: package version and the build id `forge ui` compares to find a stale server.
264
+ - `forge_ui/static/index.html`, `style.css`, `app.js`: the page shell, styles, routes, views and polling.
265
+ - `forge_ui/static/theme.js`, `shell.js`, `icons.js`, `datatable.js`: the theme switch, the Immersive and
266
+ Workbench layouts with their modules, keys and splitters, the icon set, and the sortable tables
267
+ (their pure parts are node-tested). `D-DINCondensed-Bold.woff2` is the engraved labels' face.
268
+ - `forge_ui/static/runmodel.js`: the pure run model (layouts, schedule projection, critical path, replay).
269
+ - `forge_ui/static/panel.js`, `gantt.js`, `graph.js`: the inspector, the execution chart and strip, the 2D graph.
270
+ - `forge_ui/static/map3d.js`, `scene3d.js`, `iso.js`: the 3D loader, scenes and engine (ported from archivision);
271
+ `findings3d.js` the findings' marks, `stagectl.js` the stage's keys, legend and keys sheet.
272
+ - `forge_ui/static/cloudmodel.js`, `cloud3d.js`, `cloudui.js`: the run cloud's pure model (projects,
273
+ filters, layouts, layout files), its scene on the shared stage, and its controls.
274
+ - `forge_ui/static/diffview.js`: the diff viewer, with findings pinned to their lines.
275
+ - `forge_ui/static/convo.js`, `forge_ui/transcripts.py`: an execution's conversation, and the
276
+ reader of each agent CLI's log format behind it.
277
+ - `forge_ui/static/files.js`: the files heatmap (`matrix()` is pure and node-tested).
278
+ - `forge_ui/export.py`: a run as one HTML file (the page's scripts and styles inlined, the API
279
+ responses it reads, the 3D stage's modules as one module text, a hash-based policy).
280
+ - `forge_ui/static/modelstats.js`, `leaderboard.js`: the leaderboard's aggregation (outcomes as
281
+ `scripts/forge_dataset/export.py` labels them, the interval of its `routing.py`) and its page.
282
+ - `forge_ui/diffs.py`, `fractal.py`, `models.py`: diff parsing and finding pins, the Fractal state
283
+ reader, and the facts behind the model leaderboard.
284
+ - `forge_ui/static/search.js`, `notify.js`, `favicon.svg`: the quick jump and the notifications
285
+ (ranking and change detection are in `runmodel.js`).
286
+ - `tests/`: reader, executions, endpoints, server, static-script and browser checks; `run_fixture.py`
287
+ builds realistic runs in a temp dir (`python3 tests/run_fixture.py DIR` writes two you can open),
288
+ and `many_fixture.py` many small ones across repos for the cloud (`python3 tests/many_fixture.py DIR 300`).
289
+ - `tools/demo_runs.py`, `tools/readme_media.py`: made-up runs to look around with (one live run
290
+ and five projects of history), and the script that turns them into the READMEs' pictures in
291
+ `docs/media/`.
292
+ - [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md): the licences of the ported and loaded code;
293
+ forge-ui itself is under forge's [MIT License](../LICENSE).
294
+
295
+ [docs/api.md](docs/api.md) is the data contract between the reader, server and page.
296
+
297
+ ### Known differences from docs/api.md
298
+
299
+ - Root discovery stops at a discovered plan and skips symlinked child directories;
300
+ it does not visit every nested plan within depth 2.
301
+ - The reader accepts wave numbers only from 1 to 10,000 and warns about other values.
302
+ - List `updated` timestamps cover the directory and summary inputs; detail timestamps
303
+ additionally cover files read for the task panels and other run details.
304
+ - The server rejects Host headers other than `127.0.0.1` or `localhost` (optional
305
+ port) with `403`; an absent Host is accepted. This also precedes method rejection.
306
+ - Static filenames must contain only letters, digits, `_`, `-` and `.` and must
307
+ not contain `..`; resolved files must be directly inside the static directory.
308
+
309
+ ## Develop
310
+
311
+ Use Python 3.9+ to run the server and tests. Node.js 18+ runs the JavaScript checks; use a
312
+ modern browser for the page (import maps: Chrome 89, Firefox 108, Safari 16.4). No Python or
313
+ Node packages need installing.
314
+
315
+ Run these from `ui/`:
316
+
317
+ ```sh
318
+ python3 -m unittest discover -s tests # includes the syntax checks and node --test
319
+ node --test tests/js/*.test.js # the run and cloud models alone
320
+ ```
321
+
322
+ `tests/test_browser.py` drives the page in Chromium when Python Playwright is installed, and
323
+ skips otherwise. Its 3D tests need three.js: the CDN, or a local copy of the npm package
324
+ (`npm pack three@0.160.0`, unpacked) named by `FORGE_UI_TEST_THREE=<dir>/package`.
325
+
326
+ The pictures in this README and forge's come from `tools/readme_media.py`. After a change you
327
+ can see, run it again: it builds the demo runs, serves them, and captures each view in
328
+ Chromium with Playwright (three.js as for the browser tests; libwebp's `img2webp` and ImageMagick
329
+ to encode the animations and PNGs). `--only cloud diff` retakes some of them.
330
+
331
+ ```sh
332
+ python3 tools/readme_media.py # writes docs/media/
333
+ ```
334
+
335
+ ## Privacy
336
+
337
+ Run dirs can contain prompts, diffs and review text from private repos. The server binds
338
+ only `127.0.0.1`, makes no network requests, and writes nothing to run dirs, repositories or
339
+ forge's config. Outside the runs it reads one kind of file: for a claude execution whose log
340
+ holds only its final answer, that run's session file, `<session id>.jsonl` under
341
+ `~/.claude/projects/` (or `$CLAUDE_CONFIG_DIR/projects/`). Anyone with access to the local server can read the runs it discovers, and
342
+ forge now registers every run, so that is every run on the machine.
343
+
344
+ The 3D view loads three.js 0.160.0 from `cdn.jsdelivr.net` the first time it is shown in a
345
+ tab: the browser asks the CDN for those script files and sends nothing about your runs
346
+ (`Referrer-Policy: no-referrer`, and the page's CSP allows only that one package). Choose
347
+ **2D** in the toolbar and the browser remembers it and never contacts the CDN.
@@ -0,0 +1,177 @@
1
+ # Third-party notices
2
+
3
+ forge-ui's 3D view is ported from archivision and draws in the iso-glow style. The served
4
+ copies of that code (`forge_ui/static/iso.js`, `scene3d.js` and `runmodel.js`) carry these
5
+ notices in their head comments; this file keeps them with the source.
6
+
7
+ ## archivision
8
+
9
+ `forge_ui/static/iso.js` and `scene3d.js` port `assets/shell-3d.html` and `assets/figure.js`;
10
+ `forge_ui/static/runmodel.js` ports `lib/layout.js` (https://github.com/Giamme/archivision).
11
+
12
+ ```
13
+ MIT License
14
+
15
+ Copyright (c) 2026 Giamme
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ of this software and associated documentation files (the "Software"), to deal
19
+ in the Software without restriction, including without limitation the rights
20
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ copies of the Software, and to permit persons to whom the Software is
22
+ furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
34
+ ```
35
+
36
+ ## iso-glow and iso-figure
37
+
38
+ archivision's shell and the drawing style of its figures come from iso-glow, which is based
39
+ on iso-figure. The same code is in `forge_ui/static/iso.js` and `scene3d.js`.
40
+
41
+ ```
42
+ MIT License
43
+
44
+ Copyright (c) 2026 April Zhu (iso-glow: lighting, palettes, gallery shell, examples)
45
+ Copyright (c) 2026 Tolga Cohce (iso-figure, on which iso-glow is based)
46
+
47
+ Permission is hereby granted, free of charge, to any person obtaining a copy
48
+ of this software and associated documentation files (the "Software"), to deal
49
+ in the Software without restriction, including without limitation the rights
50
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51
+ copies of the Software, and to permit persons to whom the Software is
52
+ furnished to do so, subject to the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be included in all
55
+ copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
63
+ SOFTWARE.
64
+ ```
65
+
66
+ ## three.js
67
+
68
+ three.js 0.160.0 (MIT, Copyright © 2010-2023 three.js authors) is not part of forge. The page
69
+ loads it, with its `examples/jsm` add-ons, from `cdn.jsdelivr.net` when the 3D view is used.
70
+
71
+ ## D-DIN Condensed (font)
72
+
73
+ `forge_ui/static/D-DINCondensed-Bold.woff2`, unmodified, is the D-DIN Condensed Bold face by Datto Inc.
74
+ (https://www.datto.com/fonts/d-din), used for forge-ui's engraved labels. It is licensed under the SIL Open
75
+ Font License 1.1; "D-DIN" and "D-DIN Condensed" are Reserved Font Names.
76
+
77
+ ```
78
+ Copyright (C) 2017 Datto Inc. https://www.datto.com/fonts/d-din,
79
+ with Reserved Font Names "D-DIN".
80
+
81
+ Copyright (C) 2017 Datto Inc. https://www.datto.com/fonts/d-din,
82
+ with Reserved Font Names "D-DIN Condensed".
83
+
84
+ Copyright (C) 2017 Datto Inc. https://www.datto.com/fonts/d-din,
85
+ with Reserved Font Names "D-DIN Expanded".
86
+
87
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
88
+ This license is copied below, and is also available with a FAQ at:
89
+ http://scripts.sil.org/OFL
90
+
91
+ -----------------------------------------------------------
92
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
93
+ -----------------------------------------------------------
94
+
95
+ PREAMBLE
96
+ The goals of the Open Font License (OFL) are to stimulate worldwide
97
+ development of collaborative font projects, to support the font creation
98
+ efforts of academic and linguistic communities, and to provide a free and
99
+ open framework in which fonts may be shared and improved in partnership
100
+ with others.
101
+
102
+ The OFL allows the licensed fonts to be used, studied, modified and
103
+ redistributed freely as long as they are not sold by themselves. The
104
+ fonts, including any derivative works, can be bundled, embedded,
105
+ redistributed and/or sold with any software provided that any reserved
106
+ names are not used by derivative works. The fonts and derivatives,
107
+ however, cannot be released under any other type of license. The
108
+ requirement for fonts to remain under this license does not apply
109
+ to any document created using the fonts or their derivatives.
110
+
111
+ DEFINITIONS
112
+ "Font Software" refers to the set of files released by the Copyright
113
+ Holder(s) under this license and clearly marked as such. This may
114
+ include source files, build scripts and documentation.
115
+
116
+ "Reserved Font Name" refers to any names specified as such after the
117
+ copyright statement(s).
118
+
119
+ "Original Version" refers to the collection of Font Software components as
120
+ distributed by the Copyright Holder(s).
121
+
122
+ "Modified Version" refers to any derivative made by adding to, deleting,
123
+ or substituting -- in part or in whole -- any of the components of the
124
+ Original Version, by changing formats or by porting the Font Software to a
125
+ new environment.
126
+
127
+ "Author" refers to any designer, engineer, programmer, technical
128
+ writer or other person who contributed to the Font Software.
129
+
130
+ PERMISSION & CONDITIONS
131
+ Permission is hereby granted, free of charge, to any person obtaining
132
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
133
+ redistribute, and sell modified and unmodified copies of the Font
134
+ Software, subject to the following conditions:
135
+
136
+ 1) Neither the Font Software nor any of its individual components,
137
+ in Original or Modified Versions, may be sold by itself.
138
+
139
+ 2) Original or Modified Versions of the Font Software may be bundled,
140
+ redistributed and/or sold with any software, provided that each copy
141
+ contains the above copyright notice and this license. These can be
142
+ included either as stand-alone text files, human-readable headers or
143
+ in the appropriate machine-readable metadata fields within text or
144
+ binary files as long as those fields can be easily viewed by the user.
145
+
146
+ 3) No Modified Version of the Font Software may use the Reserved Font
147
+ Name(s) unless explicit written permission is granted by the corresponding
148
+ Copyright Holder. This restriction only applies to the primary font name as
149
+ presented to the users.
150
+
151
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
152
+ Software shall not be used to promote, endorse or advertise any
153
+ Modified Version, except to acknowledge the contribution(s) of the
154
+ Copyright Holder(s) and the Author(s) or with their explicit written
155
+ permission.
156
+
157
+ 5) The Font Software, modified or unmodified, in part or in whole,
158
+ must be distributed entirely under this license, and must not be
159
+ distributed under any other license. The requirement for fonts to
160
+ remain under this license does not apply to any document created
161
+ using the Font Software.
162
+
163
+ TERMINATION
164
+ This license becomes null and void if any of the above conditions are
165
+ not met.
166
+
167
+ DISCLAIMER
168
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
169
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
170
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
171
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
172
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
173
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
174
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
175
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
176
+ OTHER DEALINGS IN THE FONT SOFTWARE.
177
+ ```