@giamme/forge 0.0.0-stage → 0.8.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 (127) hide show
  1. package/CHANGELOG.md +181 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1854 -2
  4. package/SKILL.md +122 -0
  5. package/VERSION +1 -0
  6. package/forge +74 -0
  7. package/package.json +26 -4
  8. package/references/dataset.md +329 -0
  9. package/references/decompose.md +667 -0
  10. package/references/fractal-dashboard.md +21 -0
  11. package/references/fractal.md +307 -0
  12. package/references/harnesses.md +353 -0
  13. package/references/jev.md +865 -0
  14. package/references/memory.md +228 -0
  15. package/references/planning.md +42 -0
  16. package/references/ripwire.md +47 -0
  17. package/references/runtime.md +64 -0
  18. package/references/solo.md +113 -0
  19. package/references/ui.md +112 -0
  20. package/registry.tsv +81 -0
  21. package/scripts/forge-artifact.sh +78 -0
  22. package/scripts/forge-contract.py +430 -0
  23. package/scripts/forge-dataset.py +6 -0
  24. package/scripts/forge-dispatch.sh +665 -0
  25. package/scripts/forge-fail-first.py +195 -0
  26. package/scripts/forge-fractal-dispatch.sh +4 -0
  27. package/scripts/forge-fractal-options.sh +54 -0
  28. package/scripts/forge-fractal.py +6 -0
  29. package/scripts/forge-guard.py +266 -0
  30. package/scripts/forge-install-fractal.sh +4 -0
  31. package/scripts/forge-install-ripwire.sh +52 -0
  32. package/scripts/forge-install.sh +135 -0
  33. package/scripts/forge-jev-options.sh +259 -0
  34. package/scripts/forge-jev.py +6 -0
  35. package/scripts/forge-lib-plans.sh +785 -0
  36. package/scripts/forge-lib-review.sh +494 -0
  37. package/scripts/forge-lib-task.sh +302 -0
  38. package/scripts/forge-lib-verify.sh +30 -0
  39. package/scripts/forge-memory.sh +575 -0
  40. package/scripts/forge-metrics.sh +39 -0
  41. package/scripts/forge-parallel-options.sh +76 -0
  42. package/scripts/forge-parallel.sh +1717 -0
  43. package/scripts/forge-plan-check.py +118 -0
  44. package/scripts/forge-prompt.py +45 -0
  45. package/scripts/forge-ripwire.py +136 -0
  46. package/scripts/forge-route.py +344 -0
  47. package/scripts/forge-runtime.py +533 -0
  48. package/scripts/forge-schedule.py +312 -0
  49. package/scripts/forge-shadow.py +305 -0
  50. package/scripts/forge-solo.sh +347 -0
  51. package/scripts/forge-ui-options.sh +42 -0
  52. package/scripts/forge-ui.py +435 -0
  53. package/scripts/forge-update.sh +71 -0
  54. package/scripts/forge-verify-coverage.py +956 -0
  55. package/scripts/forge-version.py +198 -0
  56. package/scripts/forge_dataset/__init__.py +439 -0
  57. package/scripts/forge_dataset/capture.py +330 -0
  58. package/scripts/forge_dataset/checks.py +109 -0
  59. package/scripts/forge_dataset/cli.py +233 -0
  60. package/scripts/forge_dataset/export.py +1009 -0
  61. package/scripts/forge_dataset/harvest.py +716 -0
  62. package/scripts/forge_dataset/importer.py +412 -0
  63. package/scripts/forge_dataset/outcomes.py +275 -0
  64. package/scripts/forge_dataset/routing.py +278 -0
  65. package/scripts/forge_fractal/__init__.py +83 -0
  66. package/scripts/forge_fractal/bridge.py +46 -0
  67. package/scripts/forge_fractal/cli.py +202 -0
  68. package/scripts/forge_fractal/dashboard.css +138 -0
  69. package/scripts/forge_fractal/dashboard.html +41 -0
  70. package/scripts/forge_fractal/dashboard.js +687 -0
  71. package/scripts/forge_fractal/decomposition.py +262 -0
  72. package/scripts/forge_fractal/execution.py +659 -0
  73. package/scripts/forge_fractal/inspection.py +261 -0
  74. package/scripts/forge_fractal/install.py +158 -0
  75. package/scripts/forge_fractal/invocation.py +94 -0
  76. package/scripts/forge_fractal/pipeline.py +124 -0
  77. package/scripts/forge_fractal/selection.py +179 -0
  78. package/scripts/forge_jev/__init__.py +376 -0
  79. package/scripts/forge_jev/backtest.py +322 -0
  80. package/scripts/forge_jev/cli.py +980 -0
  81. package/scripts/forge_jev/client.py +418 -0
  82. package/scripts/forge_jev/corpus.py +156 -0
  83. package/scripts/forge_jev/coupling.py +244 -0
  84. package/scripts/forge_jev/features.py +24 -0
  85. package/scripts/forge_jev/gates.py +100 -0
  86. package/scripts/forge_jev/memory.py +163 -0
  87. package/scripts/forge_jev/outcome.py +49 -0
  88. package/scripts/forge_jev/predict.py +204 -0
  89. package/scripts/forge_jev/questions.py +661 -0
  90. package/scripts/forge_jev/review.py +73 -0
  91. package/scripts/forge_jev/routing.py +271 -0
  92. package/scripts/forge_jev/subset.py +123 -0
  93. package/scripts/forge_jev/verify.py +453 -0
  94. package/scripts/forge_registry.py +140 -0
  95. package/ui/README.md +287 -0
  96. package/ui/THIRD_PARTY_NOTICES.md +69 -0
  97. package/ui/docs/api.md +888 -0
  98. package/ui/forge_ui/__init__.py +20 -0
  99. package/ui/forge_ui/__main__.py +135 -0
  100. package/ui/forge_ui/diffs.py +454 -0
  101. package/ui/forge_ui/executions.py +436 -0
  102. package/ui/forge_ui/export.py +204 -0
  103. package/ui/forge_ui/findings.py +132 -0
  104. package/ui/forge_ui/fractal.py +583 -0
  105. package/ui/forge_ui/models.py +216 -0
  106. package/ui/forge_ui/reader.py +1336 -0
  107. package/ui/forge_ui/server.py +303 -0
  108. package/ui/forge_ui/static/app.js +924 -0
  109. package/ui/forge_ui/static/cloud3d.js +646 -0
  110. package/ui/forge_ui/static/cloudmodel.js +375 -0
  111. package/ui/forge_ui/static/cloudui.js +394 -0
  112. package/ui/forge_ui/static/diffview.js +216 -0
  113. package/ui/forge_ui/static/favicon.svg +1 -0
  114. package/ui/forge_ui/static/files.js +776 -0
  115. package/ui/forge_ui/static/gantt.js +97 -0
  116. package/ui/forge_ui/static/graph.js +266 -0
  117. package/ui/forge_ui/static/index.html +55 -0
  118. package/ui/forge_ui/static/iso.js +765 -0
  119. package/ui/forge_ui/static/leaderboard.js +315 -0
  120. package/ui/forge_ui/static/map3d.js +45 -0
  121. package/ui/forge_ui/static/modelstats.js +249 -0
  122. package/ui/forge_ui/static/notify.js +179 -0
  123. package/ui/forge_ui/static/panel.js +526 -0
  124. package/ui/forge_ui/static/runmodel.js +651 -0
  125. package/ui/forge_ui/static/scene3d.js +996 -0
  126. package/ui/forge_ui/static/search.js +119 -0
  127. package/ui/forge_ui/static/style.css +618 -0
package/SKILL.md ADDED
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: forge
3
+ description: Dispatch implementation and independent diff review to user-selected models through local agent CLIs. Use for /forge or explicit requests to delegate coding or review; explanation-only questions do not authorize dispatch.
4
+ argument-hint: '"<goal>" --dwarf <alias>[:effort[:harness]] [--qa <alias>] [--planner <alias>] [--yolo-dwarf] [--yolo-qa] [--decompose-level low|medium|high] [--fractal|--no-fractal|--fractal-auto-decompose] [--fractal-planner <spec>] [--jev|--no-jev] [--jev-shadow] [--no-memory] [--no-ripwire] [--ui|--no-ui] [--timeout <seconds>] [--retry-failed <n>] [--escalate-after <n>] [--qa-threshold P0|P1|P2|P3] [--shadow-qa <spec>[,<spec>]] [--shadow-rate <0..1>] [--explore <0..0.5>] [--fail-first]'
5
+ allowed-tools: [Bash, Read]
6
+ ---
7
+
8
+ # Forge
9
+
10
+ Run an implementer (**dwarf**) followed by an independent **QA** review of its actual
11
+ changes. Use the scripts for dispatch and capture; do not reconstruct harness commands.
12
+ For explanation requests, explain the workflow without spending model quota.
13
+
14
+ ## Choices and boundaries
15
+
16
+ - A dwarf model must be chosen by the user. Ask when it is missing; never invent one.
17
+ - QA defaults to `opus`; effort defaults to `medium` for dwarf and `xhigh` for QA/planner.
18
+ - Specs are `alias[:effort[:harness]]`, e.g. `sol:xhigh:openclaude`. Resolve using
19
+ [registry.tsv](registry.tsv); report any effort clamp. Literal model IDs require a harness.
20
+ - Bypass flags apply only when explicitly requested for that role (`--yolo-dwarf`,
21
+ `--yolo-qa`). Bash access can mutate files despite disabled editing tools.
22
+ - Ask **“Use Fractal for this run? [y/N]”** once before an interactive run, unless
23
+ the user supplied `--fractal`, `--no-fractal`, or `--fractal-auto-decompose`. Pass the answer explicitly to
24
+ the runner. Unattended runs default off. Installation never activates Fractal.
25
+ Resume and explicit retry retain the recorded choice. Read [Fractal](references/fractal.md)
26
+ when selected; Fractal completion never substitutes for Forge QA acceptance.
27
+ - Ask **“Open forge-ui to watch this run? [y/N]”** once at the start of every new
28
+ interactive run, unless the user supplied `--ui` or `--no-ui`. Pass the answer to `plan`
29
+ or `forge-solo.sh` and relay the `forge-ui:` URL they print. Never ask on resume or retry;
30
+ unattended runs leave it off. A UI failure never changes the run. Read [forge-ui](references/ui.md).
31
+ - A finished solo run, `plan` or `run` may end with one stderr line, `forge X is available
32
+ (you have Y): forge update` (checked at most once a day). Relay it once; run the update only
33
+ when the user asks (`forge update`, then restart the harness session). `FORGE_UPDATE_CHECK=off` silences it.
34
+ - `--fractal-auto-decompose` enables automatic split-or-atomic decisions before each
35
+ eligible node implements. Include the effective planner and bounds in the approval
36
+ preview; child decisions need no additional user approval inside those bounds.
37
+ Forward `--fractal-planner <spec>` to the runner. When omitted, forward a selected
38
+ top-level `--planner` as `--fractal-planner`; otherwise each task's initial root model
39
+ plans all its descendants. Ordinary `--fractal` keeps worker-requested decomposition.
40
+ - One implementer per tree. Solo edits remain uncommitted. Decomposed runs may commit
41
+ and merge on Forge task and integration branches. Updating the user's branch requires
42
+ separate authorization and `integrate --approved`. Never push automatically.
43
+ - Show the decomposed task/model table and wait for approval before dispatch. Retry only
44
+ when requested; preserve failed task branches and worktrees. `run --retry-failed N`,
45
+ `--infra-retries M`, `--qa-threshold Pn` and `plan --shadow-qa` (a second review of a
46
+ sampled share of attempts, never read, to measure reviewer agreement) are opt-in: offer
47
+ them, never add them unprompted. So is `--fail-first` (solo or `plan`): the change's new
48
+ tests also run without its non-test changes, and the reviewer is asked to report any that
49
+ still pass as P2; it needs `.forge/verify-subset` and never changes a result by itself. `plan` explores by default (`--explore 0.2`: now and then
50
+ a row left to forge gets its neighbouring tier's model from the user's pools, odds
51
+ recorded for `forge dataset routing`); say so with the table and pass `--explore 0` when
52
+ the user declines. Retries follow a policy: after three failed attempts on one model the
53
+ next goes a tier up in the user's pools, or the task stops with a split suggestion
54
+ (`--escalate-after N`, `0` = off); an explicit `retry --dwarf` overrides it. `accept`
55
+ overrides a reviewer, so run it (with the user's `--reason` and
56
+ `--approved`) only on the user's explicit instruction; never self-accept.
57
+ - Exit `8` / status `INFRA` is a quota, auth or network stop, not a task failure and no
58
+ attempt was spent: report it, say what to fix, and re-run only when told. For a multi-task
59
+ plan, offer the opt-in whole-run review (`review`, or `integrate --final-review <qa-spec>`);
60
+ it spends QA quota, so never run it unprompted.
61
+ - Jev is optional, off unless configured and enabled, and always advisory: it never
62
+ changes a task's tier, never selects a model, and never substitutes for QA acceptance
63
+ or repository verification (`--jev-act` is still accepted and changes nothing). Read
64
+ [Jev](references/jev.md) when selected.
65
+
66
+ ## Common workflow
67
+
68
+ 1. Inspect the repository and existing work. State the goal, selected models and any bypass
69
+ flags. Keep complete requirements in `prompt.md`; short titles are insufficient for QA.
70
+ 2. Preflight every selected role before the first model call, including an optional planner:
71
+ `bash <skill_dir>/scripts/forge-dispatch.sh doctor --spec <spec> --role <role>`.
72
+ This checks resolution, executable and advertised flags without model quota. Authentication,
73
+ quota and live model availability remain unverified; a live probe must be explicitly requested.
74
+ 3. Use [solo runs](references/solo.md) for one implementation. For `--decompose-level
75
+ low|medium|high`, read [decomposition](references/decompose.md), prepare the task table,
76
+ obtain approval, then use `forge-parallel.sh plan/run`. Write the user's request verbatim
77
+ to `request.md` in the run or plan dir, and pass `--decompose-level` to `plan` with the
78
+ level you actually split at, even when the user named none. Keep each task small and end
79
+ its `prompt.md` with an `## Acceptance criteria` section of checks the reviewer can verify;
80
+ `plan` names tasks over 7K characters or 9 files, where first attempts mostly fail review.
81
+ Pass the recorded Fractal choice and requested limits to the execution runner; tier
82
+ pools go to solo directly or decomposed `plan`. Fractal does not replace planner dispatch.
83
+ `--timeout` applies to decomposed runs too (kept in the plan).
84
+ 4. Runners default to summary output. Read each `dwarf.last` and `qa.last` once; retrieve raw
85
+ logs only when needed. Report findings faithfully, and identify verification
86
+ limits. Source or review mutations invalidate acceptance. Missing verdicts are UNKNOWN.
87
+ An absent repository verification command means UNVERIFIED, even when QA passes.
88
+
89
+ For a Fractal run, report its managed run ID with the Forge result. Use
90
+ `<skill_dir>/forge fractal runs --repo <repo> --json` and `tree RUN_ID --json` to discover
91
+ run/task/node IDs; use `status`, `logs` or `open` for inspection. Run-level `resume`
92
+ recovers the recorded pipeline; scoped controls affect only the selected execution scope.
93
+ Preserve failed work and state the distinction between execution completion, QA acceptance
94
+ and verification. Read the Fractal reference before retrying, changing limits or exporting
95
+ a portable report.
96
+
97
+ Ripwire context is automatic when compatible; `--no-ripwire` or `FORGE_RIPWIRE=off`
98
+ disables it. Pass opt-out to every role, including a separately dispatched planner.
99
+ Runners offer installation once before agents; dispatch never prompts. See
100
+ [Ripwire](references/ripwire.md) for installation, limits and fallback behavior.
101
+
102
+ ## Conditional references
103
+
104
+ - [Planning](references/planning.md): read for `--planner` or an agreed approach.
105
+ - [Solo](references/solo.md): command, snapshots, artifacts, exit codes and native-review limits.
106
+ - [Decomposition](references/decompose.md): task schema, routing, waves, retries and integration.
107
+ - [Harnesses](references/harnesses.md): invocation troubleshooting or adding a harness.
108
+ - [Fractal](references/fractal.md): opt-in nested execution, installation, limits,
109
+ recovery, managed run commands, read-only dashboard and portable reports.
110
+ - [Memory](references/memory.md): `.forge/` learning and spend records; `--no-memory` disables
111
+ them. Memory is written to the user's repository but excluded from implementation diffs.
112
+ - [forge-ui](references/ui.md): the opt-in local run viewer; `forge ui open|status|stop`,
113
+ `FORGE_UI=off`, one shared server, privacy.
114
+ - [Dataset](references/dataset.md): every run's inputs and outcomes, kept outside the repo
115
+ for training decision models; `forge dataset status|import|export|outcomes`, `FORGE_DATASET=off`.
116
+ - [Jev](references/jev.md): optional TypeSafe System One judgments for routing, test
117
+ selection and memory curation; off by default. Chosen sites can go to Cloudflare's hosted
118
+ Clef instead (`forge jev setup --provider cloudflare`, `forge jev sites`, `FORGE_CLEF=off`).
119
+
120
+ The common invocation is `/forge "<goal>" --dwarf <spec> [--qa <spec>]`. Advanced flags and
121
+ commands live with their mode reference. Use [offline tests and evaluation cases](tests/README.md)
122
+ when maintaining this skill.
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8
package/forge ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # npm runs this file through a symlink in its bin dir, usually a relative one. Follow the
4
+ # links so ROOT is the package and not the bin dir; older macOS has no readlink -f.
5
+ SELF="${BASH_SOURCE[0]}"
6
+ while [ -L "$SELF" ]; do
7
+ LINK="$(readlink "$SELF")"
8
+ case "$LINK" in
9
+ /*) SELF="$LINK" ;;
10
+ *) SELF="$(dirname "$SELF")/$LINK" ;;
11
+ esac
12
+ done
13
+ ROOT="$(cd -P "$(dirname "$SELF")" && pwd)"
14
+
15
+ usage() {
16
+ printf '%s\n' \
17
+ 'usage: forge <command> [args]' \
18
+ '' \
19
+ 'commands:' \
20
+ ' install link forge into each agent harness on this machine; --dry-run previews' \
21
+ ' fractal opt-in Fractal execution: install, doctor, runs, status, tree, logs, open, report, ...' \
22
+ ' jev optional Jev advice: setup, status, doctor, enable, disable, backtest' \
23
+ ' dataset every run'"'"'s inputs and outcomes: status, dump, import, export, outcomes, ...' \
24
+ ' ui forge-ui, the run viewer: open [DIR], status, stop' \
25
+ ' update fast-forward this checkout and refresh every harness installation' \
26
+ ' version the installed version, and whether a newer release is out' \
27
+ '' \
28
+ 'Run `forge <command> --help` for its options.' \
29
+ 'Runs start from your agent harness (/forge "<goal>" --dwarf <model>); see README.md.'
30
+ }
31
+
32
+ case "${1:-}" in
33
+ install)
34
+ shift
35
+ exec /bin/bash "$ROOT/scripts/forge-install.sh" "$@"
36
+ ;;
37
+ fractal)
38
+ shift
39
+ exec python3 "$ROOT/scripts/forge-fractal.py" "$@"
40
+ ;;
41
+ jev)
42
+ shift
43
+ exec python3 "$ROOT/scripts/forge-jev.py" "$@"
44
+ ;;
45
+ dataset)
46
+ shift
47
+ exec python3 "$ROOT/scripts/forge-dataset.py" "$@"
48
+ ;;
49
+ ui)
50
+ shift
51
+ exec python3 "$ROOT/scripts/forge-ui.py" "$@"
52
+ ;;
53
+ update)
54
+ shift
55
+ exec /bin/bash "$ROOT/scripts/forge-update.sh" "$@"
56
+ ;;
57
+ version)
58
+ shift
59
+ exec python3 "$ROOT/scripts/forge-version.py" "$@"
60
+ ;;
61
+ -h|--help|help)
62
+ usage
63
+ exit 0
64
+ ;;
65
+ '')
66
+ usage >&2
67
+ exit 2
68
+ ;;
69
+ *)
70
+ printf 'forge: unknown command: %s\n\n' "$1" >&2
71
+ usage >&2
72
+ exit 2
73
+ ;;
74
+ esac
package/package.json CHANGED
@@ -1,6 +1,28 @@
1
1
  {
2
2
  "name": "@giamme/forge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.8.0",
4
+ "description": "One model builds it. A different model reviews what it actually built.",
5
+ "keywords": ["claude-code", "codex", "opencode", "agent-skill", "code-review", "multi-model"],
6
+ "license": "MIT",
7
+ "author": "Giamme",
8
+ "repository": { "type": "git", "url": "git+https://github.com/Giamme/forge.git" },
9
+ "bin": { "forge": "forge" },
10
+ "os": ["darwin", "linux"],
11
+ "files": [
12
+ "forge",
13
+ "SKILL.md",
14
+ "VERSION",
15
+ "CHANGELOG.md",
16
+ "README.md",
17
+ "LICENSE",
18
+ "registry.tsv",
19
+ "references/",
20
+ "scripts/",
21
+ "ui/forge_ui/",
22
+ "ui/README.md",
23
+ "ui/THIRD_PARTY_NOTICES.md",
24
+ "ui/docs/api.md",
25
+ "!**/__pycache__"
26
+ ],
27
+ "publishConfig": { "access": "public" }
28
+ }
@@ -0,0 +1,329 @@
1
+ # Run dataset
2
+
3
+ Every forge run records its inputs and outcomes in one global store outside every repo,
4
+ so a decision model (Jev, Laya or any other) can later be trained or calibrated on what
5
+ forge actually did. Run and plan dirs hold nearly all of this, but they live in `/tmp`
6
+ and scratchpads and get deleted. `.forge/ledger.tsv` survives, but it keeps only spec
7
+ strings and verdicts. It has no inputs, no resolved models, no attempt numbers and no
8
+ tokens.
9
+
10
+ ```
11
+ ${FORGE_DATA_HOME:-${XDG_DATA_HOME:-~/.local/share}/forge}/dataset/
12
+ dataset.sqlite events (rid-keyed, append-only) and repos
13
+ blobs/<aa>/<sha>.gz prompts, diffs, messages, logs: capped, gzipped, deduplicated
14
+ errors.log capture failures
15
+ ```
16
+
17
+ Capture is on by default and costs a few milliseconds per execution. `FORGE_DATASET=off`
18
+ turns it off for one invocation.
19
+
20
+ ## Raw events, not training examples
21
+
22
+ The store holds **what happened, when it happened**: the state each decision saw and the
23
+ outcomes that followed. It never holds (state, question, label) rows. Those are derived
24
+ per decision by an exporter, so a question nobody has asked yet can still be answered
25
+ from old runs without re-running anything. Labels come from folding a run's events in
26
+ time order: an attempt that PASSed, then a merge that CONFLICTed, then a human `accept`.
27
+
28
+ | kind | one per | what it holds |
29
+ |---|---|---|
30
+ | `start` | execution | a receipt written by `begin()`. A start with no matching end is an execution that was killed or is still running |
31
+ | `dispatch` | model call | role, alias, harness, **resolved** model, effort after clamping, timeout and its source, delivery (custom or native review), phase timings, exit, timeout, infra class, tokens, parsed verdict and findings, `FORGE_LEARNING` lines. Blobs: the delivered prompt and the final message |
32
+ | `attempt` | task attempt or solo run | status, attempt number (with refund and resume flags), trigger (`initial`, `auto`, `human`), dwarf and qa spec, declared vs changed files, numstat, drift, guard notes, gate line, patch key, the files this execution wrote and the ones it only carried. Blobs: the diff, retry findings, memory slices, capsule, Jev task files |
33
+ | `verification` / `verify_run` | verify, and each command execution | command and its source (flag, `.forge/verify`, Jev), status, flaky, Jev triage. One `verify_run` per execution, each with its own log |
34
+ | `plan` | `plan` | goal, original request, decompose level, exploratory routing decisions (`tasks[id].route`, with their odds), author (orchestrator or planner), per-task declared vs executed difficulty and who set it, pools, waves, Jev routing and coupling. Blobs: both task tables, prompts, approaches, excerpts of the declared files at plan time, the file inventory, `memory.md` |
35
+ | `run_end` | `run` | results per task, the accepted integration commit, `memory.md` |
36
+ | `invocation` | user-facing `forge-parallel.sh` subcommand | argv, exit, duration, plus final review, accept, integrate and split/combine payloads |
37
+ | `event` | point event | `merge` (MERGED, CONFLICT with the files, INVALIDATED and why), `integrate_advanced`, `integrate_conflict`, `combine_conflict`, `sched_terminal`, `subset_run` (every outcome), `memory_record`, `memory_prune` |
38
+ | `event` `escalation` | `retry --dwarf` | the model a retry escalated from and to, recorded at the rewrite, before the retried attempt begins |
39
+ | `event` `shadow_review` | shadow review | how an opt-in second review ended (`done`, `invalid`, `overtime`, `orphaned`, ...), with the reviewed commit and both specs. Its dispatch is recorded with role `qa_shadow` |
40
+ | `jev` | Jev request | site, answers, usage, latency, mode. Blob: the **full request** (state and questions). Its original sha is `jev.jsonl`'s `key` |
41
+ | `fractal_step` / `fractal_node` | Fractal step, node state | decomposition decision, iteration, resolved model, cost, tokens, candidate diff |
42
+ | `ledger` | ledger row | imported `.forge/ledger.tsv` rows |
43
+ | `outcome` | integrated task, per horizon | written by `forge dataset outcomes`, never by a run: how many of the lines a task added survive N days after `integrate`, and what reverted it |
44
+
45
+ Each blob reference carries `sha` (the stored bytes), `original_sha`, `n` (the original
46
+ size), `kept`, and `cut`. A cut keeps the head and the tail, because a QA prompt ends with
47
+ the diff and a log ends with the failure. Caps: prompt 1 MiB, diff 2 MiB, message 256 KiB,
48
+ log 272 KiB. Transcripts (`*.log`) and `.cmd` (which some harnesses use to carry the
49
+ prompt in argv) are never stored. A harness without structured output has its final
50
+ message copied from the whole log, so for those harnesses only the tail is kept, flagged
51
+ `final_source: log_copy`.
52
+
53
+ ## How it is captured
54
+
55
+ All capture goes through a handful of hooks. Every one of them is fail-open: errors go
56
+ to `errors.log`, nothing is printed, and nothing changes an exit code.
57
+
58
+ - **`forge-runtime.py begin()/finish()`.** Every dispatch, task attempt, solo run,
59
+ verification and parallel run already passes through these. `finish()` captures from a
60
+ `finally:` block, so an extraction error cannot lose the record. This is the only
61
+ hook that sees every exit path: an empty diff, INVALIDATED, a timeout, an infra stop
62
+ or a `die`.
63
+ - **Provenance travels with the execution.** `begin()` writes
64
+ `attempts/<role>-<uuid>/dataset.json` with the run id, task, starting attempt counter,
65
+ trigger and a signature (inode, mtime, size) of every task file. `finish()` credits a
66
+ file to this execution only when its signature changed. A dwarf that fails returns
67
+ before `changes.diff` is refreshed, and without this check it would inherit the
68
+ previous attempt's diff and verdict.
69
+ - **Point events** (`forge_dataset_event`) record facts that disappear before any EXIT
70
+ trap could read them: a merge that gets aborted, a combine that rolls back, a user
71
+ branch that moves just before a cleanup that may still fail.
72
+ - **An EXIT trap in `forge-parallel.sh`** records every user-facing subcommand, `die`
73
+ included.
74
+ - **`forge_jev/client.py`** keeps the canonical request bytes of every live Jev call,
75
+ including calls made without a run dir. It skips fixture replays, the setup probe and
76
+ backtests.
77
+
78
+ Run identity is a uuid in `<plan-or-run-dir>/dataset.uid`, created once (`O_EXCL`) by
79
+ `plan` or by the first `begin()`. Split and combine copy attempt dirs unchanged, so a
80
+ copied execution keeps its original identity and collapses to one event by rid.
81
+
82
+ ## Which forge produced an event
83
+
84
+ Outcomes depend on the prompts, contracts and registry forge ran with, and the skill is
85
+ often a live dev checkout that changes between releases. So every event carries four
86
+ provenance columns:
87
+
88
+ | column | what it identifies |
89
+ |---|---|
90
+ | `forge_version` | the release in `VERSION` |
91
+ | `registry_sha` | the effective `registry.tsv` (or `FORGE_REGISTRY`) |
92
+ | `code_sha` | the content of every file that shapes a run: `scripts/`, `references/`, `SKILL.md`, `VERSION`, `forge` and the effective registry. Two runs share it only if they ran the same code, uncommitted edits included |
93
+ | `forge_rev` | the skill checkout's `HEAD` (`git rev-parse`, read-only), or null for a copied skill dir |
94
+
95
+ An execution is stamped with what `begin()` saw, because its prompt was built then.
96
+ `finish()` runs in another process; if the code changed in between, the event carries
97
+ `code_changed: true` and can be left out of any per-regime analysis.
98
+
99
+ An imported run was produced by whatever forge ran it, so the importer stamps nothing,
100
+ unless a live `begin()` left its provenance in the attempt dir. Rows imported before
101
+ schema 2 carry the importer's version by mistake, so readers treat `forge_version` and
102
+ `registry_sha` on `src = 'import'` rows without a `code_sha` as unknown. The exporter
103
+ gives those a `forge_rev_estimate`: the last forge commit before the run started, which is a
104
+ guess and is labeled as one. Schema 2 added `code_sha` and `forge_rev`. The first
105
+ capture after an upgrade adds them in one `BEGIN IMMEDIATE` transaction, and an older
106
+ forge still running keeps writing.
107
+
108
+ ## Commands
109
+
110
+ ```sh
111
+ ./forge dataset status [-v] [--json] # where, how much, and what looks incomplete
112
+ ./forge dataset dump [--kind K] [--run R] [--task T] [--since UNIX] # events as JSON lines
113
+ ./forge dataset blob <sha> # print one stored blob
114
+ ./forge dataset import <dir|archive|legacy-dir>... [--registry] [--fractal] [--ledger REPO] [--dry-run]
115
+ ./forge dataset export [--slice S]... [--limit N] [--seed N] [--out DIR] # labeled decision cases
116
+ ./forge dataset outcomes [--horizon-days 14] [--repo PATH] [--dry-run] # what integrated work became
117
+ ./forge dataset routing [--repo PATH] [--since DAYS] [--json] # pass rate and runtime per tier and model
118
+ ```
119
+
120
+ `status` runs a different completeness check for each mode, because each mode
121
+ legitimately lacks different things. A solo run has no separate verification, and a
122
+ final review's QA has no task attempt. The checks are:
123
+
124
+ - executions that started but never ended
125
+ - task QA dispatches with no task attempt
126
+ - parallel PASSes with no merge outcome in a finished run
127
+ - parallel runs with no end
128
+ - blob references with no stored blob
129
+
130
+ ## Exporting labeled decisions
131
+
132
+ `export` folds the events into cases in the System One wire format (one JSON object per
133
+ line with `id`, `state`, `questions` and `expected`), the format decision-model
134
+ benchmarks and fine-tuning recipes take. Each case is a decision Jev makes in forge,
135
+ with its state built the way `forge_jev` builds it for that site and its label taken
136
+ from what happened afterwards:
137
+
138
+ | slice | question | label |
139
+ |---|---|---|
140
+ | `routing` | will the first attempt at this task pass review? (state adds the dwarf and reviewer models) | the final status of the first real attempt (see below) |
141
+ | `routing` | which difficulty tier is this task? | the tier the orchestrator or planner declared. This is agreement, not an outcome |
142
+ | `retry` | will this retry pass, given the review findings it was handed? (state adds the attempt number and the previous review) | the final status of that attempt (see below) |
143
+ | `review` | will the independent reviewer pass this diff? (state names the reviewer's model, effort and harness) | the QA verdict. A diff whose reviews disagreed is dropped |
144
+ | `review` | will most of the lines this diff adds stay in the code, unrewritten and unreverted? | from `outcomes`: at least 80% of the added lines still blamed to the task's commits at the horizon, and no revert. Only the review of the diff that landed gets it |
145
+ | `review` | what is the most severe problem the reviewer will find? (`none`, `P3` to `P0`, the contract's scale) | the worst CONFIRMED finding, only when every CONFIRMED finding carries a severity and none is UNSTATED. A review whose findings cannot be fully read gets no label rather than a wrong `none` |
146
+ | `drift` | is this undeclared file likely to need editing? | whether the passing attempt changed it. Negatives are undeclared files near the task, plus random ones |
147
+ | `citation` | do the places this review cites appear in this diff? | the review paired with its own diff, or with another task's diff from the same repo that shares no file |
148
+ | `memory` | is this recorded fact worth including for this task? | the fact's anchor is a file the task edited (true), or lies in an area it never touched (false). Anything in between is left out |
149
+
150
+ The reviewer is part of the review state because strictness differs by reviewer: the
151
+ same kind of diff passes far more often under one reviewer than another, so a label
152
+ means little without knowing who gave it. Routing and retry states also carry a `size`
153
+ block (`prompt_chars`, `declared_files`, `plan_tasks`, `deps`, `decompose_level`, from
154
+ `forge_jev/features.py`), since task size moves the first-pass rate more than anything
155
+ else in the history.
156
+
157
+ An attempt is labeled by the last execution that carries its number, because a QA-only
158
+ rerun keeps the number. Refunded and infra-stopped executions spent no attempt. PASS is
159
+ true and FAIL is false. TIMEOUT is false only when the implementer's own dispatch ran out
160
+ of time. Everything else says nothing about the implementer and is left out, counted
161
+ under `censored` in `manifest.json`:
162
+ - a reviewer's TIMEOUT or ERROR
163
+ - a harness crash
164
+ - forge's own failures between dispatches
165
+ - UNKNOWN, INVALIDATED, INTERRUPTED
166
+
167
+ A later success is never labeled as the first attempt's.
168
+
169
+ Decisions with no outcome to label them are left out rather than labeled by opinion.
170
+ These are QA effort sizing (recorded until 2026-10-17), the five routing rubrics, failure triage, and coupling, since
171
+ no merge has conflicted yet.
172
+
173
+ Routing cases of tasks routed with `plan --explore` add `route` to their metadata: the
174
+ candidates, the pool's own pick, the choice, its propensity, and `followed`, which says
175
+ whether attempt 1 really ran the chosen spec (an approval-gate edit or `retry --dwarf` can
176
+ replace it). `assignments.jsonl` lists every such decision with its first attempt's outcome
177
+ or censoring reason, including tasks no case was exported for. It is the randomized cohort,
178
+ free of the export's own filters.
179
+
180
+ Review cases whose attempt also had a shadow review (`plan --shadow-qa`) add `shadow` (its
181
+ verdict, reviewer and worst finding) and `consensus` to their metadata. Only a shadow that
182
+ finished on an untouched snapshot and reviewed the same commit counts. The primary
183
+ labels never change, because `qa_shadow` dispatches are not reviews of record.
184
+ `./forge dataset status` prints the agreement rate, overall and per model pair.
185
+
186
+ `meta.jsonl` records each case's slice, run, repo and label source. It also holds hosted
187
+ Jev's own answers where forge recorded them, as baselines that are never part of a case.
188
+ Routing and retry cases add:
189
+ - `decision_provenance` (the plan's) and `outcome_provenance` (the attempt's)
190
+ - `code_changed`
191
+ - the plan `options`
192
+ - `started`
193
+
194
+ Routing cases also add how the task went:
195
+ - `attempts_observed`
196
+ - `raw_verdict`
197
+ - `gate_pass`, a reviewer FAIL the severity gate accepted
198
+ - `first_raw_pass_attempt`
199
+ - `human_accept`
200
+ - `stop_reason`: `passed`, `accepted`, `noretry`, `interrupted` or `no_pass_observed`
201
+
202
+ A final FAIL is not proof a task cannot pass: the retry budget ended the observation.
203
+ The output defaults to `<data>/exports/forge-decisions/` (`0700`, files `0600`). It holds
204
+ prompts, diffs and reviews from your repos, so keep it out of public repos and pass it
205
+ only to local models. Builds are deterministic for a given store and `--seed`.
206
+
207
+ ## Routing
208
+
209
+ `forge dataset routing` answers "which model should this tier use?" from the store, in two
210
+ tables kept apart because they answer different questions.
211
+
212
+ **Observational.** Every task's first attempt, by declared tier and the model and effort that
213
+ ran it (resolved, so `sol` and `gpt-6.1-sol:medium:codex` share a row):
214
+ - first-pass rate with a Wilson 95% interval
215
+ - the share that passed review at some attempt, or that a human accepted
216
+ - unfinished tasks
217
+ - attempts per task
218
+ - the implementer's median runtime
219
+
220
+ Routing chose who ran what, so a model sent the hard tasks looks worse here than it is.
221
+ Fractal runs are left out, since their dispatches have identities of their own.
222
+
223
+ **Randomized.** The `plan --explore` draws, one row per (tier, pool pick, neighbour) pair.
224
+ Round-robin pools make the pair differ between rows, and a model has no chance at all
225
+ outside its own pairs, so pairs are never pooled. For each arm:
226
+ - assigned tasks
227
+ - first-pass as a Hajek (self-normalized inverse-propensity) mean
228
+ - Kish's effective n
229
+ - attrition: tasks not run as assigned, and tasks with no outcome
230
+ - runtime
231
+
232
+ Every task stays in the arm it was *assigned* (intent to treat), whatever the approval gate
233
+ or a retry did afterwards. A task is bound to the last draw recorded before its first attempt
234
+ started, so a later re-plan never relabels it. Draws from before the two-candidate rule
235
+ (a pick among every spec of the plan) have no pair and are counted, not compared.
236
+
237
+ **The decision rule** is fixed in advance and only advises. A pair is judged once, at the
238
+ first of 30, 60 or 120 neighbour assignments it reaches, on exactly the assignments up to
239
+ that point. The interval is a run-cluster bootstrap, shown only with at least 10 neighbour
240
+ assignments across 3 runs.
241
+ - For a **low** pair the neighbour is the stronger model: switching is advised only when the
242
+ interval is above 0.
243
+ - For **medium** and **high** pairs it is the cheaper one: it is called non-inferior when the
244
+ interval's lower end is above -0.10 and its runtime is lower.
245
+
246
+ At the default `--explore 0.2` a row changes model 10% of the time, so a pair needs weeks of
247
+ normal use to reach its first window.
248
+
249
+ ## Outcomes after merge
250
+
251
+ Every other label comes from a reviewer. `forge dataset outcomes` adds one that does not,
252
+ read from the repo's history after the fact. `integrate` merges the run branch with
253
+ `--no-ff` and fast-forwards your branch to that merge, never squashing, so each task's
254
+ attempt commits land as they are and `git blame` still attributes a task's lines to them.
255
+ For every integration, and every task it brought in, the harvester records:
256
+
257
+ - **Survival:** of the text lines the task added (`base_ref..reviewed_commit`), how many
258
+ are still attributed to the task's commits at the horizon, per file. A deleted file
259
+ counts as none surviving. Binary files are skipped, and a task that only deleted lines
260
+ is `no_additions`.
261
+ - **Reverts:** later commits whose message says `This reverts commit <sha>` for one of the
262
+ task's commits, its merge or the integration.
263
+ - **The horizon:** the last commit on your branch's first-parent chain, after the
264
+ integration, dated before `integrate` + N days. The branch is resolved once, and the
265
+ chain is walked by position, so commit dates out of order cannot skip a commit.
266
+
267
+ Two cases leave a task without a survival count:
268
+ - **Pending:** the horizon has not been reached and no later commit has passed it yet.
269
+ Nothing is written, and the next harvest tries again.
270
+ - **Lost:** a rebase, squash or reset took the integration off the branch. This means
271
+ unobservable, not failed.
272
+
273
+ The rid names the integration, the reviewed commit and the horizon, so harvesting again
274
+ adds nothing, and two horizons are two outcomes. Survival describes the diff that landed,
275
+ with every retry in it, not the first model the task was routed to. That is why the
276
+ exporter attaches it to that diff's review.
277
+
278
+ Each verification event also records the commit it verified (`verification.commit`, named
279
+ before any early return), so a FAIL or UNVERIFIED result can be joined to its code too.
280
+
281
+ ## Importing old runs
282
+
283
+ `import` reads run dirs that predate live capture through the same readers. An execution
284
+ seen both live and imported collapses to one event. Old dirs have no begin-time snapshot,
285
+ so the importer will not guess which execution wrote a mutable file:
286
+
287
+ - The **newest** task attempt gets the task dir's current files, flagged `legacy`.
288
+ - **Earlier** attempts get only what is immutable:
289
+ - their own dispatch dirs
290
+ - the diff QA was shown (the last `` ```diff `` block of the QA prompt)
291
+ - the status that `attempts.tsv` logged, or failing that, the QA verdict, with
292
+ `status_source` saying which
293
+ - Unfinished executions (still running, or killed) are skipped. The first write of a rid
294
+ wins, so importing one now would block the real outcome that live capture records when
295
+ it ends. Re-import the dir afterwards to fill anything live capture missed.
296
+ - Repos under temp dirs (test runs) are skipped unless `--include-temp` is given.
297
+ - Archives are extracted to a temp dir and mapped back to their original path. The legacy
298
+ run id hashes that original path, so the archive and the original dir agree on it.
299
+
300
+ `~/.local/share/forge/legacy/` holds archives of run dirs taken before live capture, with a
301
+ `MANIFEST.tsv`. Passing that dir to `import` reads every archive, plus the ledger copies
302
+ listed in `ledgers/REPOS.tsv`.
303
+
304
+ ## Invariants
305
+
306
+ - **Never inside the repo.** Capture refuses, silently and before any write, when the store
307
+ resolves inside the run's repo or a forge worktree. `integrate` refuses a dirty tree.
308
+ - **Never read by a prompt builder.** Memory inject, the capsule and Ripwire must not read
309
+ the store. A reviewer that could see earlier runs' verdicts would not be independent.
310
+ `tests/test_dataset.py` enforces that outside its own package, the dataset is only
311
+ imported for capture.
312
+ - **Read-only in your repos.** Capture and the harvester run only object-store git queries
313
+ in a repo: `rev-parse`, `rev-list`, `log`, `show`, `ls-tree`, `cat-file`, `diff` between
314
+ commits and `blame` at a commit. All run with `GIT_OPTIONAL_LOCKS=0`. None takes the
315
+ working tree or refreshes the index. `outcomes` reads the store through a read-only
316
+ connection and writes only what it found.
317
+ - **Private.** Prompts, diffs and logs can contain secrets. The dir is `0700` and its files
318
+ `0600`. Do not sync it. Redaction belongs to whatever exports a training set.
319
+
320
+ ## Not captured
321
+
322
+ - **Token counts for openclaude, opencode and antigravity.** Those harnesses expose none.
323
+ They are recorded as null.
324
+ - **Dispatches refused before `begin()`** (an unknown alias, bad flags). No model ran.
325
+ - **The orchestrator's own reasoning.** Only its outputs are recorded: the task table,
326
+ `request.md` and the approval edits visible between the planner's message and the
327
+ declared table.
328
+ - **Counterfactuals.** Each task runs on one model, so nothing here says whether another
329
+ model would have done better. That needs deliberate head-to-head runs.