@tangle-network/agent-bench 0.3.7 → 0.3.8

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 (191) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/HARNESS.md +43 -0
  3. package/dist/adapters.js +23 -23
  4. package/dist/benchmarks/_harness.d.ts +1 -1
  5. package/dist/benchmarks/_harness.js +1 -1
  6. package/dist/benchmarks/aec-bench.js +2 -2
  7. package/dist/benchmarks/agentbench.js +2 -2
  8. package/dist/benchmarks/appworld.js +2 -2
  9. package/dist/benchmarks/bfcl.js +2 -2
  10. package/dist/benchmarks/commit0.js +2 -2
  11. package/dist/benchmarks/crag.js +2 -2
  12. package/dist/benchmarks/dabstep.js +2 -2
  13. package/dist/benchmarks/enterpriseops-gym.js +2 -2
  14. package/dist/benchmarks/finresearchbench.js +2 -2
  15. package/dist/benchmarks/nomiracl.js +2 -2
  16. package/dist/benchmarks/open-rag-bench.js +2 -2
  17. package/dist/benchmarks/programbench.js +2 -2
  18. package/dist/benchmarks/ragbench.js +2 -2
  19. package/dist/benchmarks/swe-bench.js +2 -2
  20. package/dist/benchmarks/t2-ragbench.js +2 -2
  21. package/dist/benchmarks/tau-bench-shared.js +2 -2
  22. package/dist/benchmarks/tau2-bench.js +3 -3
  23. package/dist/benchmarks/tau3-banking.js +3 -3
  24. package/dist/benchmarks/terminal-bench.js +2 -2
  25. package/dist/benchmarks/toollm.js +2 -2
  26. package/dist/benchmarks/webarena-verified.js +2 -2
  27. package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
  28. package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
  29. package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
  30. package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
  31. package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
  32. package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
  33. package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
  34. package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
  35. package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
  36. package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
  37. package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
  38. package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
  39. package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
  40. package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
  41. package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
  42. package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
  43. package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
  44. package/dist/{chunk-PWQVGAJB.js → chunk-V7AEBY6U.js} +21 -21
  45. package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
  46. package/dist/chunk-WSKWVEQB.js.map +1 -0
  47. package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
  48. package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
  49. package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
  50. package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
  51. package/dist/index.js +23 -23
  52. package/package.json +4 -4
  53. package/scripts/verify-packed-consumer.mjs +1 -1
  54. package/src/benchmarks/_harness.ts +20 -2
  55. package/src/benchmarks/humaneval.test.mts +2 -2
  56. package/src/quant-arena/README.md +144 -0
  57. package/src/quant-arena/backtest.test.mts +135 -0
  58. package/src/quant-arena/backtest.ts +218 -0
  59. package/src/quant-arena/data.test.mts +44 -0
  60. package/src/quant-arena/data.ts +141 -0
  61. package/src/quant-arena/driver.test.mts +253 -0
  62. package/src/quant-arena/driver.ts +219 -0
  63. package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
  64. package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
  65. package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
  66. package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
  67. package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
  68. package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
  69. package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
  70. package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
  71. package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
  72. package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
  73. package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
  74. package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
  75. package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
  76. package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
  77. package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
  78. package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
  79. package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
  80. package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
  81. package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
  82. package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
  83. package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
  84. package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
  85. package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
  86. package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
  87. package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
  88. package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
  89. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
  90. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
  91. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
  92. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
  93. package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
  94. package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
  95. package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
  96. package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
  97. package/src/quant-arena/holdout-certify.mts +206 -0
  98. package/src/quant-arena/holdout-certify.test.mts +82 -0
  99. package/src/quant-arena/leak-audit.test.mts +79 -0
  100. package/src/quant-arena/leak-audit.ts +95 -0
  101. package/src/quant-arena/make-fixtures.mts +161 -0
  102. package/src/quant-arena/multiplicity.test.mts +68 -0
  103. package/src/quant-arena/multiplicity.ts +87 -0
  104. package/src/quant-arena/nautilus-certify.ts +31 -0
  105. package/src/quant-arena/oms.ts +90 -0
  106. package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
  107. package/src/quant-arena/python/pyproject.toml +8 -0
  108. package/src/quant-arena/python/uv.lock +1297 -0
  109. package/src/quant-arena/python/vbt-worker.py +192 -0
  110. package/src/quant-arena/quant-loop.mts +813 -0
  111. package/src/quant-arena/quant-loop.test.mts +75 -0
  112. package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
  113. package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
  114. package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
  115. package/src/quant-arena/types.ts +133 -0
  116. package/src/quant-arena/vbt-client.ts +321 -0
  117. package/src/quant-arena/vbt-parity.test.mts +183 -0
  118. package/src/quant-arena/windows.test.mts +45 -0
  119. package/src/quant-arena/windows.ts +54 -0
  120. package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
  121. package/src/rollout-ledger/backfill-swe-arena.test.mts +338 -0
  122. package/src/rollout-ledger/settle-capture.mts +442 -0
  123. package/src/rollout-ledger/settle-capture.test.mts +270 -0
  124. package/src/swe-arena/activation.mts +228 -0
  125. package/src/swe-arena/activation.test.mts +303 -0
  126. package/src/swe-arena/arms.ts +30 -14
  127. package/src/swe-arena/briefing.mts +217 -0
  128. package/src/swe-arena/briefing.test.mts +178 -0
  129. package/src/swe-arena/calibrate.ts +102 -1
  130. package/src/swe-arena/cell-evidence.mts +41 -9
  131. package/src/swe-arena/factory-command-container.ts +284 -0
  132. package/src/swe-arena/factory-judge-child.mts +228 -0
  133. package/src/swe-arena/factory.test.mts +643 -0
  134. package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
  135. package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
  136. package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
  137. package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
  138. package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
  139. package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
  140. package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
  141. package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
  142. package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
  143. package/src/swe-arena/fixtures/run-report/README.md +43 -0
  144. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
  145. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
  146. package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
  147. package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
  148. package/src/swe-arena/fixtures.ts +135 -2
  149. package/src/swe-arena/gepa-seat.mts +583 -0
  150. package/src/swe-arena/gepa-seat.test.mts +635 -0
  151. package/src/swe-arena/ledger-orphans.mts +77 -0
  152. package/src/swe-arena/ledger-orphans.test.mts +147 -0
  153. package/src/swe-arena/lineage-record.mts +164 -0
  154. package/src/swe-arena/lineage-record.test.mts +115 -0
  155. package/src/swe-arena/outer-loop.mts +677 -27
  156. package/src/swe-arena/premeasured-from-cells.mts +281 -0
  157. package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
  158. package/src/swe-arena/proposer-fanout.mts +295 -14
  159. package/src/swe-arena/proposer-fanout.test.mts +247 -0
  160. package/src/swe-arena/proposer-provenance.mts +177 -0
  161. package/src/swe-arena/proposer-provenance.test.mts +106 -0
  162. package/src/swe-arena/run-experiment.mts +367 -1
  163. package/src/swe-arena/run-report.mts +75 -0
  164. package/src/swe-arena/run-supervisor.test.mts +5 -3
  165. package/src/swe-arena/score-split.mts +140 -0
  166. package/src/swe-arena/score-split.test.mts +123 -0
  167. package/src/swe-arena/types.ts +52 -0
  168. package/src/swe-structural.mts +245 -837
  169. package/dist/chunk-LRRD7NAG.js.map +0 -1
  170. /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
  171. /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
  172. /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
  173. /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
  174. /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
  175. /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
  176. /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
  177. /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
  178. /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
  179. /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
  180. /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
  181. /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
  182. /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
  183. /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
  184. /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
  185. /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
  186. /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
  187. /package/dist/{chunk-PWQVGAJB.js.map → chunk-V7AEBY6U.js.map} +0 -0
  188. /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
  189. /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
  190. /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
  191. /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "factory.agent-eval.309",
3
+ "repo": "tangle-network/agent-eval",
4
+ "repo_local_mirror": "/home/drew/code/agent-eval",
5
+ "base_commit": "5fe8d0c83a7e38f2893b2d7a0f6cd62dd5430521",
6
+ "judge_ref": "9b0a4f82257e07ac96d90d4af24364bc964ed178",
7
+ "spec_md": "spec.md",
8
+ "judge_tests": [
9
+ "src/paired-arms.test.ts",
10
+ "src/capability-headroom.test.ts"
11
+ ],
12
+ "excluded_tests": [],
13
+ "worker_visible_paths_note": "worker gets the base tree only (git archive of base_commit, no refs); judge tests overlaid at judge time",
14
+ "command_image": "node:24-bookworm@sha256:5711a0d445a1af54af9589066c646df387d1831a608226f4cd694fc59e745059",
15
+ "setup_cmds": [
16
+ "pnpm install"
17
+ ],
18
+ "judge_cmds": [
19
+ "npx vitest run src/paired-arms.test.ts src/capability-headroom.test.ts"
20
+ ],
21
+ "resolved_criterion": "all 30 judge tests pass; partial score = passed/30",
22
+ "timeout_s": 600,
23
+ "runtime": "node>=20, pnpm 11, vitest 3 (repo devDependency)",
24
+ "calibration": {
25
+ "gold": "PASS 30/30 (twice, deterministic)",
26
+ "base": "FAIL 2/2 test files (unresolvable imports: modules do not exist at base)",
27
+ "receipts": "calibration.md"
28
+ }
29
+ }
@@ -0,0 +1,64 @@
1
+ # Add matched-pair arm comparison and a capability-headroom gate to the statistics layer
2
+
3
+ ## Background
4
+
5
+ agent-eval ships a statistics module (`src/statistics.ts`) with paired estimators — `mcnemar`, paired risk difference, paired bootstrap, `wilcoxonSignedRank` — but every consumer that runs a two-arm A/B over run records hand-rolls the same two things on top of it:
6
+
7
+ 1. Matching baseline rows to treatment rows into pairs before feeding the paired estimators.
8
+ 2. A calibrate-before-measure gate that checks the benchmark can even see the capability being measured (if the capability-absent baseline already passes a task, that task carries no signal).
9
+
10
+ Lift both into the library as new modules. Compose the existing statistics exports; do not add new math. No changes to any existing export.
11
+
12
+ ## Deliverable 1 — matched-pair arm comparison (`src/paired-arms.ts`)
13
+
14
+ Input rows are arm-labeled records the caller projects from its own run data:
15
+
16
+ - `PairedArmRow`: `{ pairKey: string; repKey?: string; arm: string; pass?: boolean; metrics?: Record<string, number> }`.
17
+
18
+ ### `pairArms(rows, { baselineArm, treatmentArm })`
19
+
20
+ Builds matched pairs. Hard requirements:
21
+
22
+ - **Pairing keys on row identity, never on outcome content.** A pair is an exact `(pairKey, repKey)` match across the two arms. Outcome-keyed matching is forbidden — it deflates discordant counts and manufactures fake lifts on null A/Bs.
23
+ - Single-rep tasks (one row per arm for a `pairKey`) pair directly without `repKey`.
24
+ - If any `pairKey` has multiple reps in either arm, **every** row of that `pairKey` must carry `repKey`; otherwise throw a validation error whose message names the offending pairKey and says a repKey is missing (e.g. matches `pairKey 't1' has multiple reps ... missing repKey`).
25
+ - Throw on a duplicate `repKey` within one `(pairKey, arm)` group; message names the repKey, pairKey, and arm (matches `duplicate repKey 'r1' for pairKey 't1' in arm 'off'`).
26
+ - Throw on an arm name with no rows; message lists the arms actually present (matches `no rows for arm 'onn' ... arms present: off, on`). Throw when baseline and treatment name the same arm (message contains `cannot be compared to itself`).
27
+ - Rows without a counterpart are **reported, never dropped**: result carries `unpairedBaseline` and `unpairedTreatment` arrays holding the leftover rows.
28
+ - Result shape: `{ pairs, unpairedBaseline, unpairedTreatment }`; each pair carries `pairKey`, a `repIndex` (0-based per-pair ordinal within its pairKey), and the full `baseline` / `treatment` rows.
29
+ - Deterministic under input reordering: `pairArms(shuffled)` deep-equals `pairArms(original)`.
30
+
31
+ ### `comparePairedArms(rows, options)`
32
+
33
+ Runs the full comparison over the pairs:
34
+
35
+ - **Correctness**: derive discordant counts (baseline-pass/treatment-fail and the reverse) from `pass` and report exact McNemar (`pValue`) plus the paired risk difference under a `correctness` result section, with `nPairs`, `nUnpairedBaseline`, `nUnpairedTreatment` visible.
36
+ - **Efficiency**: for every metric name present, per-pair deltas (treatment − baseline) summarized with the paired bootstrap and Wilcoxon signed-rank, reported as `metricDeltas` (one entry per metric).
37
+ - A metric with zero complete pairs reports `null` for its bootstrap CI and Wilcoxon — the bootstrap's all-zero sentinel must never read as a measured tight null.
38
+ - Bootstrap must be seeded/deterministic so repeated runs agree.
39
+
40
+ ## Deliverable 2 — capability-headroom gate (`src/capability-headroom.ts`)
41
+
42
+ ### `capabilityHeadroom(rows, options)`
43
+
44
+ Given per-task baseline outcomes for the capability-absent arm, report which tasks have headroom (baseline fails → task can show the capability) with fail-closed semantics:
45
+
46
+ - Outcomes are per-rep and may be pass / fail / unknown; **unknown never counts as headroom** and never counts as a known rep.
47
+ - Reject an unrecognized baseline outcome value loudly (message names the bad value, e.g. `unrecognized baselineOutcome 'passed'`); reject an empty baseline (`no baseline rows`).
48
+ - Per-task output includes `nKnown`; a `summary` includes `tasksWithGap` and `repsUnknown` so thin evidence stays visible.
49
+ - Numeric guards validate loudly: minimum reps must be an integer ≥ 1 (message contains `integer ≥ 1`); where two of something are required the message says `need ≥ 2`.
50
+ - Result exposes `tasks` (per-task rows) and `summary`.
51
+
52
+ ### `assertCapabilityHeadroom(rows, options)`
53
+
54
+ Throws an actionable error when the benchmark cannot see the capability it claims to measure (no headroom); otherwise returns the headroom report.
55
+
56
+ ## Wiring
57
+
58
+ - Export both modules' public symbols from `src/index.ts`.
59
+ - Match repo conventions: strict TS, colocated `*.test.ts` allowed, biome-clean.
60
+
61
+ ## Acceptance
62
+
63
+ - `pnpm typecheck` and `pnpm exec biome check` clean.
64
+ - Hidden acceptance tests import `{ pairArms, comparePairedArms, type PairedArmRow }` from `src/paired-arms` and `{ capabilityHeadroom, assertCapabilityHeadroom }` from `src/capability-headroom`, plus `mcnemar` from `src/statistics`, and exercise every behavior above, including: identity-based multi-rep pairing on a both-reps-discordant scenario, leftover reporting, all the fail-loud validation messages, null CI on zero-pair metrics, determinism under reordering, and fail-closed unknown handling in the headroom gate.
@@ -0,0 +1,48 @@
1
+ # Calibration — factory.agent-runtime.232
2
+
3
+ Run 2026-07-23, node v24.13.0, pnpm 11.15.1, local mirror `/home/drew/code/agent-runtime` (read-only; work in throwaway clones).
4
+
5
+ ## Gold run — base + real PR impl (tests excluded) must PASS
6
+
7
+ ```
8
+ git clone /home/drew/code/agent-runtime work/ar-232-gold
9
+ git -C work/ar-232-gold checkout ce6194a8e4e8468fb47d7a040f9df7366c0dda9b
10
+ git -C /home/drew/code/agent-runtime diff ce6194a8 690a8fd0 -- ':(exclude)tests/' > ar232-impl.patch
11
+ git -C work/ar-232-gold apply ar232-impl.patch
12
+ mkdir -p work/ar-232-gold/tests/mcp
13
+ git -C /home/drew/code/agent-runtime show 690a8fd0:tests/mcp/delegation-store.test.ts > work/ar-232-gold/tests/mcp/delegation-store.test.ts
14
+ git -C /home/drew/code/agent-runtime show 690a8fd0:tests/mcp/task-queue-durable.test.ts > work/ar-232-gold/tests/mcp/task-queue-durable.test.ts
15
+ cd work/ar-232-gold && pnpm install
16
+ npx vitest run tests/mcp/delegation-store.test.ts tests/mcp/task-queue-durable.test.ts
17
+ ```
18
+
19
+ Output:
20
+
21
+ ```
22
+ ✓ tests/mcp/delegation-store.test.ts (10 tests) 12ms
23
+ ✓ tests/mcp/task-queue-durable.test.ts (11 tests) 82ms
24
+ Test Files 2 passed (2)
25
+ Tests 21 passed (21)
26
+ ```
27
+
28
+ Re-run (flakiness check): `PASS (21) FAIL (0)`.
29
+
30
+ ## Base run — judge tests on bare base must FAIL
31
+
32
+ Same clone/checkout of `ce6194a8`, overlay only the two test files, `pnpm install`, same vitest command.
33
+
34
+ Output:
35
+
36
+ ```
37
+ Test Files 2 failed (2)
38
+ Tests no tests
39
+ ```
40
+
41
+ Both files fail at collection: `src/mcp/delegation-store` does not exist and `task-queue.ts` lacks the durable exports (`DelegationResumeDriver`, store option). Producing them is the task.
42
+
43
+ ## Judge-set notes
44
+
45
+ - Test bodies use temporary-directory round trips and fake in-process drivers. The judge runs in the pinned factory container with network disabled and no operator environment. One test uses deterministic fake timers (`vi`). **Excluded: none.**
46
+ - The judge asserts **typed error classes** (`DelegationPersistenceError`, `DelegationStateCorruptError`) and one error `kind` string (`'DriverRestartError'`), not message prose — clean behavioral contract; all named in `spec.md`.
47
+ - `DelegationRecord`, `DelegationTaskQueue`, `hashIdempotencyInput`, and `DelegateCodeArgs` pre-exist at base; the judge extends them rather than asserting incidental internals.
48
+ - The PR also touched `README.md` — excluded from the impl patch definition of "the feature" for grading purposes (docs churn, no behavioral content). It applies cleanly either way since the impl patch used `':(exclude)tests/'` only.
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "factory.agent-runtime.232",
3
+ "repo": "tangle-network/agent-runtime",
4
+ "repo_local_mirror": "/home/drew/code/agent-runtime",
5
+ "base_commit": "ce6194a8e4e8468fb47d7a040f9df7366c0dda9b",
6
+ "judge_ref": "690a8fd0e0ed5eec3a2b1c19031717a087aabd5d",
7
+ "spec_md": "spec.md",
8
+ "judge_tests": [
9
+ "tests/mcp/delegation-store.test.ts",
10
+ "tests/mcp/task-queue-durable.test.ts"
11
+ ],
12
+ "excluded_tests": [],
13
+ "worker_visible_paths_note": "worker gets the base tree only; judge tests overlaid at judge time",
14
+ "command_image": "node:24-bookworm@sha256:5711a0d445a1af54af9589066c646df387d1831a608226f4cd694fc59e745059",
15
+ "setup_cmds": [
16
+ "pnpm install"
17
+ ],
18
+ "judge_cmds": [
19
+ "npx vitest run tests/mcp/delegation-store.test.ts tests/mcp/task-queue-durable.test.ts"
20
+ ],
21
+ "resolved_criterion": "all 21 judge tests pass; partial score = passed/21",
22
+ "timeout_s": 600,
23
+ "runtime": "node>=20, pnpm 11, vitest (repo devDependency)",
24
+ "calibration": {
25
+ "gold": "PASS 21/21 (twice, deterministic)",
26
+ "base": "FAIL 2/2 test files (src/mcp/delegation-store does not exist; task-queue lacks durable exports)",
27
+ "receipts": "calibration.md"
28
+ }
29
+ }
@@ -0,0 +1,48 @@
1
+ # Durable delegation queue — persistence port, file store, restart-honest rehydration, resume seam
2
+
3
+ ## Background
4
+
5
+ The MCP delegation server keeps its task registry (`DelegationTaskQueue` in `src/mcp/task-queue.ts`, records typed as `DelegationRecord`) purely in memory: when the MCP process restarts, delegation status/history vanish and an idempotently re-submitted task re-runs. `task-queue.ts` already names a Phase-2 follow-up: make the registry survive the process. Sandbox filesystems are snapshot-persisted by products, so a file-backed store makes delegation state genuinely span conversations.
6
+
7
+ Build that follow-up. Additive only: zero behavior change for existing consumers who configure nothing; existing tests must stay untouched and green.
8
+
9
+ ## Deliverable 1 — persistence port + stores (new module `src/mcp/delegation-store.ts`)
10
+
11
+ Public surface (also re-exported from `src/mcp/index.ts`):
12
+
13
+ - `DelegationStore` — async port with the operations the queue needs: load all persisted records, upsert one record, remove one record, and resolve/persist idempotency-key → taskId mappings.
14
+ - `InMemoryDelegationStore` — the default; semantics identical to today. Round-trips records, isolates stored state from caller mutation (no shared references), supports removal and idempotency resolution.
15
+ - `FileDelegationStore` — single-file JSON snapshot store:
16
+ - A missing file is an empty store, not an error.
17
+ - Writes are atomic (write tmp file, then rename); concurrent upserts serialize into one parseable snapshot; no tmp litter left behind.
18
+ - Writing before an initial load is a programming error → typed `DelegationPersistenceError`.
19
+ - Upserts and removals persist across store instances (new instance over the same path sees them).
20
+ - Corrupt state (unparseable JSON, or valid JSON with the wrong shape) **refuses to load** with a typed `DelegationStateCorruptError` — never silently starts fresh.
21
+ - Explicit opt-in recovery (`recoverCorrupt` behavior, wired to env `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` in the bin): archives the corrupt file (kept on disk under a recognizable archived name) and starts empty.
22
+ - Typed errors `DelegationPersistenceError` and `DelegationStateCorruptError` exported from the same module.
23
+
24
+ ## Deliverable 2 — durable mode in `DelegationTaskQueue` (`src/mcp/task-queue.ts`)
25
+
26
+ Queue options grow a `store?: DelegationStore` (default in-memory, unchanged behavior) plus:
27
+
28
+ - **Restore/rehydration** (a `restore()` step a fresh queue instance runs over the store):
29
+ - Terminal records (completed/failed/cancelled) become queryable again: status and history visible to the fresh instance.
30
+ - The idempotency index rebuilds: re-submitting a previously-seen idempotency key returns the prior taskId and terminal state **without re-running** the delegate (`hashIdempotencyInput` unchanged).
31
+ - Records that were in-flight when the previous driver died must not pretend to be running: settle them as failed with a truthful error whose `error.kind` is `'DriverRestartError'` — **unless** the record carries a `detachedSessionRef` (see resume seam).
32
+ - `restore()` rejects with `DelegationStateCorruptError` over a corrupt state file.
33
+ - **Resume seam**: a `DelegationResumeDriver` interface (a `tick`-style driver the queue polls to re-drive a detached session to completion, designed to map 1:1 onto the sandbox SDK's turn-drive result). Behavior:
34
+ - A restored in-flight record with a `detachedSessionRef` and a configured resume delegate is resumed through the driver (the driver sees the ref) and settles with the driver's outcome.
35
+ - With a `detachedSessionRef` but **no** resume delegate configured, the record settles failed truthfully (no resurrection).
36
+ - A driver tick that throws settles the record as failed.
37
+ - `cancel()` aborts an in-progress resume loop.
38
+ - **Retention**: a `maxTerminalRecords` cap evicts the oldest terminal records beyond the cap, both in memory and in the persisted file. A non-positive cap is rejected loudly at construction.
39
+ - **Fail loud on persistence loss**: after a store write failure the queue refuses further submissions (surfacing `DelegationPersistenceError`) rather than degrading to memory-only.
40
+
41
+ ## Deliverable 3 — wiring (`src/mcp/bin.ts`)
42
+
43
+ - Opt-in via env `AGENT_RUNTIME_DELEGATION_STATE_FILE=<path>` → the bin constructs the queue over a `FileDelegationStore` at that path; `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` enables corrupt-state recovery. Nothing set → in-memory, exactly today's behavior.
44
+
45
+ ## Acceptance
46
+
47
+ - `pnpm typecheck`, lint, and the full existing test suite stay green.
48
+ - Hidden acceptance tests import `{ DelegationPersistenceError, DelegationStateCorruptError, FileDelegationStore, InMemoryDelegationStore, type DelegationStore }` from `src/mcp/delegation-store` and `{ DelegationTaskQueue, hashIdempotencyInput, type DelegationResumeDriver }` plus `type DelegationRecord` from `src/mcp/task-queue`, and exercise every behavior above with real tmpdir files (atomicity, cross-instance persistence, corrupt-state refusal and recovery, restart-honest settlement, detached resume, dedupe-across-restart, eviction, write-failure lockout).
@@ -0,0 +1,47 @@
1
+ # Calibration — factory.loops.28
2
+
3
+ Run 2026-07-23, node v24.13.0, pnpm 11.15.1, local mirror `/home/drew/code/loops` (read-only; work in throwaway clones).
4
+
5
+ ## Gold run — base + real PR impl (tests excluded) must PASS
6
+
7
+ ```
8
+ git clone /home/drew/code/loops work/lp-28-gold
9
+ git -C work/lp-28-gold checkout 3ca55b9eb24adae9d5c8383dc785bd610e1db5b0
10
+ git -C /home/drew/code/loops diff 3ca55b9e 7805270f -- ':(exclude)tests/' > lp28-impl.patch
11
+ git -C work/lp-28-gold apply lp28-impl.patch
12
+ for t in best-effort worker-clone worker-evidence; do
13
+ git -C /home/drew/code/loops show 7805270f:tests/$t.test.ts > work/lp-28-gold/tests/$t.test.ts
14
+ done
15
+ cd work/lp-28-gold && pnpm install
16
+ npx vitest run tests/best-effort.test.ts tests/worker-clone.test.ts tests/worker-evidence.test.ts
17
+ ```
18
+
19
+ Output:
20
+
21
+ ```
22
+ Test Files 3 passed (3)
23
+ Tests 20 passed (20)
24
+ ```
25
+
26
+ Re-run (flakiness check): `PASS (20) FAIL (0)`.
27
+
28
+ ## Base run — judge tests on bare base must FAIL
29
+
30
+ Same clone/checkout of `3ca55b9e`, overlay only the three test files, `pnpm install`, same vitest command.
31
+
32
+ Output:
33
+
34
+ ```
35
+ Test Files 3 failed (3)
36
+ Tests no tests
37
+ ```
38
+
39
+ All three fail at collection: `src/best-effort.ts`, `src/worker-clone.ts`, `src/worker-evidence.ts` do not exist at base.
40
+
41
+ ## Judge-set notes
42
+
43
+ - Tests spawn `git` in temporary directories with identity passed inline, and use symlink/chmod, so they are POSIX-only. The judge runs in the pinned factory container with network disabled and no operator environment. **Excluded: none.**
44
+ - `tests/worker-evidence.test.ts` imports the bound constants (`EVIDENCE_MAX_CHARS` etc.) and asserts **relative to them** (`length ≤ EVIDENCE_MAX_CHARS`, tail contains `'v'.repeat(VERIFY_TAIL_CHARS)`) — exact values are the builder's choice; the names and semantics are contract and are in `spec.md`. Not trimmed: self-referencing bounds have behavioral content (bounding + tail retention).
45
+ - One pinned prose contract: the best-effort delivery commit subject must contain `best-effort delivery from <label>` and `no worker passed the verify gate` — promoted into `spec.md` as acceptance criteria.
46
+ - Judge imports `@tangle-network/agent-runtime/loops` (`gitWorkspace`, `runInWorkspace`) — a published npm dependency already in the base lockfile, not sibling unmerged work.
47
+ - Scope note: the merged PR also tuned worker budgets and prompts (`extensions/pi/`, `src/top-model.ts`); the judge does not cover those, so this instance grades the three library modules only. The spec describes the modules as the deliverable.
@@ -0,0 +1,30 @@
1
+ {
2
+ "id": "factory.loops.28",
3
+ "repo": "drewstone/loops",
4
+ "repo_local_mirror": "/home/drew/code/loops",
5
+ "base_commit": "3ca55b9eb24adae9d5c8383dc785bd610e1db5b0",
6
+ "judge_ref": "7805270fda9f82869028b197e9424292ec611245",
7
+ "spec_md": "spec.md",
8
+ "judge_tests": [
9
+ "tests/best-effort.test.ts",
10
+ "tests/worker-clone.test.ts",
11
+ "tests/worker-evidence.test.ts"
12
+ ],
13
+ "excluded_tests": [],
14
+ "worker_visible_paths_note": "worker gets the base tree only; judge tests overlaid at judge time",
15
+ "command_image": "node:24-bookworm@sha256:5711a0d445a1af54af9589066c646df387d1831a608226f4cd694fc59e745059",
16
+ "setup_cmds": [
17
+ "pnpm install"
18
+ ],
19
+ "judge_cmds": [
20
+ "npx vitest run tests/best-effort.test.ts tests/worker-clone.test.ts tests/worker-evidence.test.ts"
21
+ ],
22
+ "resolved_criterion": "all 20 judge tests pass; partial score = passed/20",
23
+ "timeout_s": 600,
24
+ "runtime": "node>=20, pnpm 11, vitest 4 (repo devDependency); requires local git binary; POSIX (symlink/chmod)",
25
+ "calibration": {
26
+ "gold": "PASS 20/20 (twice, deterministic)",
27
+ "base": "FAIL 3/3 test files (src/best-effort.ts, src/worker-clone.ts, src/worker-evidence.ts do not exist at base)",
28
+ "receipts": "calibration.md"
29
+ }
30
+ }
@@ -0,0 +1,50 @@
1
+ # Supervisor delivery robustness — best-effort patch delivery, artifact-inheriting worker clones, evidence-rich settlement
2
+
3
+ ## Background
4
+
5
+ The loops supervisor fans a coding task out to workers, each in a fresh `git clone` of the shared workspace, and settles each worker with a verify verdict. A paired SWE-bench round exposed three delivery defects, all reproduced from run artifacts:
6
+
7
+ 1. **All-or-nothing delivery.** A worker whose patch applied but whose verify failed delivered *nothing* — several losses were literal 0-byte deliveries of real work.
8
+ 2. **`git clone` drops untracked compiled artifacts.** Fresh worker clones were missing untracked/ignored build outputs present in the workspace (the compiled-tree shape), producing wholesale import crashes in workers.
9
+ 3. **Evidence starvation at settlement.** The supervisor rated workers on a one-line verdict; failed patches, the verify tail, and the worker's own closing note never survived to the brain, so "close — refine it" and "dead end" looked identical.
10
+
11
+ Build three library modules fixing these, with the event-dir conventions below. (The supervisor loop wiring in `extensions/pi/` consumes them; the modules themselves are the deliverable.)
12
+
13
+ Worker event-dir convention used throughout: each worker `<label>` writes its patch to `<eventDir>/workers/<label>.patch` and appends lifecycle events to `<eventDir>/workers/<label>.ndjson` — a `finished` event carries `{ kind: 'finished', passed, testPassed, typecheckPassed, at }` (ISO timestamp).
14
+
15
+ ## Deliverable 1 — best-effort delivery (`src/best-effort.ts`)
16
+
17
+ - `rankBestEffortCandidates(candidates)` — orders failed-verify candidates `{ label, patch, testPassed, typecheckPassed, finishedAt }` by: verify progression first (typecheck passed beats not), then larger patch, then more recent `finishedAt`, then label. Deterministic: ranking a shuffled copy yields the identical order.
18
+ - `deliverBestEffortPatch(eventDir, workspace)` — when no worker passed the gate, scan the event dir for worker patches and deliver the best applicable one into the workspace:
19
+ - Skip blank/whitespace-only patches; if only those exist, deliver nothing and leave the workspace untouched (return `undefined`).
20
+ - Try candidates in ranked order; a patch that does not apply is skipped and the next one is tried.
21
+ - A delivered patch is **applied and committed** in the workspace; the commit subject marks it as a best-effort delivery, names the worker (`best-effort delivery from <label>`), and states that no worker passed the verify gate.
22
+ - Returns `{ worker, patchBytes }` for the delivered candidate.
23
+
24
+ ## Deliverable 2 — artifact-inheriting worker clones (`src/worker-clone.ts`)
25
+
26
+ - `copyUntrackedIntoClone(sourceWorkspace, cloneDir)` — copies files present in the source workspace but absent from a fresh clone: untracked files AND git-ignored build outputs. Returns stats including `copied` (count). Requirements:
27
+ - Preserve the executable bit; rewrite symlinks whose targets point inside the source workspace to point inside the clone.
28
+ - Skip loop-infrastructure directories and nested git repos.
29
+ - A payload above a sanity bound warns but still copies (never silently truncates the workspace).
30
+ - `withUntrackedArtifacts(...)` — wraps worker-clone materialization so a worker clone contains both the tracked files and the untracked artifacts, while a valid delivery (worker pushing back to the shared ref) **never commits the copied artifacts** back to the shared repository.
31
+
32
+ ## Deliverable 3 — evidence-rich settlement (`src/worker-evidence.ts`)
33
+
34
+ Exported bounds (constants, also exported): `EVIDENCE_MAX_CHARS`, `VERIFY_TAIL_CHARS`, `NOTE_MAX_CHARS` (order-of-magnitude guidance: a few thousand / ~1k / a few hundred chars).
35
+
36
+ - `composeWorkerEvidence(input)` — one bounded evidence block for the brain from a worker's patch, verify log, and note:
37
+ - Always ≤ `EVIDENCE_MAX_CHARS`, even when every input is oversized.
38
+ - Keeps the **tail** of the verify log (at least the last `VERIFY_TAIL_CHARS` chars) — the failing assertion lives at the END of the log.
39
+ - Labels an empty patch explicitly; surfaces the worker note; a passing verify is reported without inventing a failure section.
40
+ - `settledWorkerOut(input: { passed, patch, evidence })` — what settlement hands the brain:
41
+ - Passing worker with real edits → its patch (the deliverable).
42
+ - Failing worker → its evidence block, never the raw patch.
43
+ - Passing NO-EDIT worker (e.g. a post-delivery reviewer) → its evidence, not an empty patch.
44
+ - No evidence at all → fall back to the patch; nothing at all → empty string.
45
+ - `closingWorkerNote(stdout, stderr)` — the worker's closing self-report: keep the **tail** of stdout (≤ `NOTE_MAX_CHARS`) so a final verdict line survives; fall back to stderr when stdout is blank; `undefined` when both are.
46
+
47
+ ## Acceptance
48
+
49
+ - Strict TS, repo lint clean, existing tests untouched and green.
50
+ - Hidden acceptance tests import `{ deliverBestEffortPatch, rankBestEffortCandidates }` from `src/best-effort.js`, `{ copyUntrackedIntoClone, withUntrackedArtifacts }` from `src/worker-clone.js`, and `{ closingWorkerNote, composeWorkerEvidence, settledWorkerOut, EVIDENCE_MAX_CHARS, NOTE_MAX_CHARS, VERIFY_TAIL_CHARS }` from `src/worker-evidence.js`, driving real local git repos in tmpdirs through every behavior above.
@@ -0,0 +1,43 @@
1
+ # run-report evidence
2
+
3
+ Reports over completed runs, committed as the measured record behind the supervisor-behavior
4
+ claims. Both source run directories were read READ-ONLY (`--report-dir` wrote the reports
5
+ here, never into the run).
6
+
7
+ The reader is `@tangle-network/agent-eval/supervisor-run` — a supervision tree is a rollout
8
+ trace with one more dimension, so it lives in the trace-analysis layer next to single-rollout
9
+ analysis. `src/swe-arena/run-report.mts` is only the CLI over it.
10
+
11
+ These files were captured before that move, so they are the pre-move record, not current
12
+ library output. Every metric key and value below was reproduced from the same run bytes with
13
+ zero diffs when the reader moved (tangle-network/agent-eval#417); what a re-run today would
14
+ change is the envelope and the metric set, never these numbers:
15
+
16
+ - `schema` is `swe-arena/run-report@1` / `swe-arena/run-report-rollup@1` here; the library
17
+ now emits `tangle.supervisor-run@1` / `tangle.supervisor-run-rollup@1`.
18
+ - the per-run envelope names the source `cellDir`; the library names it `runRef`, because the
19
+ input contract is a reader over bytes rather than a directory.
20
+ - `economics.brainTruncations` did not exist at capture time and is absent here.
21
+
22
+ Re-capturing them means re-reading the source run directories, which are not part of this
23
+ repo — so they stay as committed evidence rather than being regenerated in place.
24
+
25
+ | File | Source run | Command |
26
+ |---|---|---|
27
+ | `gen3-rollup.{json,md}` | gen-3 arena, 51 cells (`…/scratchpad/hh/gen3`) | `tsx src/swe-arena/run-report.mts --round <gen3> --report-dir <out>` |
28
+ | `factory-agent-eval-309-FSUP0.{json,md}` | factory gen-0 live, cell `factory.agent-eval.309` rep 0 (`/tmp/factory-gen0-live`) | `tsx src/swe-arena/run-report.mts <cellDir> --patch <p> --ledger <ledger.jsonl> --report-dir <out>` |
29
+
30
+ Headline facts these files carry:
31
+
32
+ - **gen-3: 0 steers across all 51 cells.** 42 cells measured a real `0`; 9 cells report
33
+ `unavailable` (no `workers/` directory — the supervisor never started a worker there).
34
+ 161 workers spawned, 35 accepted, mean 3.1 waves per cell, mean worker utilization 0.47,
35
+ mean idle share 52.6%, $3.26 brain spend, 17/51 judged resolved.
36
+ - **factory.agent-eval.309 (FSUP0, rep 0): 0 steers, 4 waves `[2,1,1,1]`, 5 workers,
37
+ utilization 0.663, idle 15.6min of a 32.2min run (48.3%).** 3 accepted / 1 rejected /
38
+ 1 empty-pass; every respawn was preceded by settled evidence (3/3); delegation depth 1;
39
+ judge 13/30 (score 0.4333, unresolved).
40
+
41
+ Read together: the supervisor is a dispatcher with a retry loop — spawn → wait → read
42
+ settled evidence → respawn — not a lead steering contributors mid-task. Half the wall clock
43
+ has no worker running at all.
@@ -0,0 +1,173 @@
1
+ {
2
+ "schema": "swe-arena/run-report@1",
3
+ "cellDir": "/tmp/factory-gen0-live/rep-0/runs/factory.agent-eval.309/FSUP0",
4
+ "instanceId": "factory.agent-eval.309",
5
+ "arm": "FSUP0",
6
+ "supervisorId": "sup-1-8f75ee",
7
+ "generatedAt": "2026-07-24T00:32:17.691Z",
8
+ "orchestration": {
9
+ "workersSpawned": 5,
10
+ "workersSettled": 5,
11
+ "workersCancelled": 0,
12
+ "steers": 0,
13
+ "steersDelivered": 0,
14
+ "steersByWorker": [
15
+ {
16
+ "worker": "w-0",
17
+ "queued": 0,
18
+ "delivered": 0
19
+ },
20
+ {
21
+ "worker": "w-1",
22
+ "queued": 0,
23
+ "delivered": 0
24
+ },
25
+ {
26
+ "worker": "w-2",
27
+ "queued": 0,
28
+ "delivered": 0
29
+ },
30
+ {
31
+ "worker": "w-3",
32
+ "queued": 0,
33
+ "delivered": 0
34
+ },
35
+ {
36
+ "worker": "w-4",
37
+ "queued": 0,
38
+ "delivered": 0
39
+ }
40
+ ],
41
+ "driverSteerCalls": 0,
42
+ "waves": 4,
43
+ "waveSizes": [
44
+ 2,
45
+ 1,
46
+ 1,
47
+ 1
48
+ ],
49
+ "maxConcurrency": 2,
50
+ "respawns": 3,
51
+ "repeatedLabels": [],
52
+ "delegationDepth": 1,
53
+ "timeToFirstSpawnMs": 831016,
54
+ "supervisorWallMs": 1934608,
55
+ "idleMs": 934563,
56
+ "idlePct": 48.3,
57
+ "workerUtilization": 0.663
58
+ },
59
+ "decision": {
60
+ "settledByStatus": {
61
+ "done": 5
62
+ },
63
+ "settledVerdicts": {},
64
+ "accepted": 3,
65
+ "rejected": 1,
66
+ "emptyPass": 1,
67
+ "observeThenRespawn": 3,
68
+ "respawnWithoutEvidence": 0,
69
+ "reviewActions": 0,
70
+ "workerEvidenceBytes": 12153
71
+ },
72
+ "economics": {
73
+ "brain": {
74
+ "tokensIn": 228169,
75
+ "tokensOut": 19398,
76
+ "usd": 0.179577,
77
+ "source": "journal metered events (n=17)"
78
+ },
79
+ "workers": {
80
+ "tokensIn": 286763,
81
+ "tokensOut": 69794,
82
+ "usd": 0,
83
+ "source": "journal settled spend + opencode sessions (n=5)"
84
+ },
85
+ "totalUsd": 0.179577,
86
+ "totalUsdSource": "state.json result.spentUsd — brain-priced only; worker CLI inference is unpriced (see worker token counts)",
87
+ "costPerAcceptedPatchUsd": 0.059859,
88
+ "workerWallMsDistribution": {
89
+ "n": 5,
90
+ "min": 70445,
91
+ "p50": 235872,
92
+ "p90": 556457,
93
+ "max": 556457,
94
+ "sum": 1274117
95
+ },
96
+ "perWorker": [
97
+ {
98
+ "worker": "w-0",
99
+ "wallMs": 556457,
100
+ "tokensIn": 0,
101
+ "tokensOut": 0,
102
+ "usd": 0,
103
+ "patchBytes": 21183,
104
+ "passed": false
105
+ },
106
+ {
107
+ "worker": "w-1",
108
+ "wallMs": 279898,
109
+ "tokensIn": 0,
110
+ "tokensOut": 0,
111
+ "usd": 0,
112
+ "patchBytes": 14573,
113
+ "passed": true
114
+ },
115
+ {
116
+ "worker": "w-2",
117
+ "wallMs": 70445,
118
+ "tokensIn": 0,
119
+ "tokensOut": 0,
120
+ "usd": 0,
121
+ "patchBytes": 0,
122
+ "passed": true
123
+ },
124
+ {
125
+ "worker": "w-3",
126
+ "wallMs": 235872,
127
+ "tokensIn": 0,
128
+ "tokensOut": 0,
129
+ "usd": 0,
130
+ "patchBytes": 21325,
131
+ "passed": true
132
+ },
133
+ {
134
+ "worker": "w-4",
135
+ "wallMs": 131445,
136
+ "tokensIn": 0,
137
+ "tokensOut": 0,
138
+ "usd": 0,
139
+ "patchBytes": 1016,
140
+ "passed": true
141
+ }
142
+ ]
143
+ },
144
+ "outcome": {
145
+ "supStatus": "completed",
146
+ "supVerdict": "delivered",
147
+ "delivered": true,
148
+ "judgeResolved": false,
149
+ "judgeScore": 0.4333,
150
+ "judgePassed": 13,
151
+ "judgeTotal": 30,
152
+ "verifyPass": {
153
+ "unavailable": "result.json absent or has no verify_pass"
154
+ },
155
+ "verifyRc": {
156
+ "unavailable": "result.json absent or has no verify_rc"
157
+ },
158
+ "patch": {
159
+ "files": 5,
160
+ "linesAdded": 939,
161
+ "linesRemoved": 0,
162
+ "testFilesTouched": [
163
+ "src/capability-headroom.test.ts",
164
+ "src/paired-arms.test.ts"
165
+ ]
166
+ },
167
+ "judgeSource": "/tmp/factory-gen0-live/ledger.jsonl (ledger row)"
168
+ },
169
+ "gaps": [
170
+ "verifyPass: result.json absent or has no verify_pass"
171
+ ],
172
+ "traceCommand": "npx --yes @tangle-network/traces@latest analyze --harness opencode --cwd <worker-clone-cwd>"
173
+ }