swarm-orchestrator 13.1.8 → 14.0.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 (299) hide show
  1. package/CHANGELOG.md +311 -0
  2. package/README.md +329 -27
  3. package/dist/adapters/external-agent.js +141 -0
  4. package/dist/adapters/external-agent.js.map +1 -0
  5. package/dist/agent-run.js +388 -22
  6. package/dist/agent-run.js.map +1 -1
  7. package/dist/build-version.js +9 -0
  8. package/dist/build-version.js.map +1 -0
  9. package/dist/cli-local-backend.js +23 -0
  10. package/dist/cli-local-backend.js.map +1 -0
  11. package/dist/cli-options.js +161 -43
  12. package/dist/cli-options.js.map +1 -1
  13. package/dist/cli-run-commands.js +142 -0
  14. package/dist/cli-run-commands.js.map +1 -0
  15. package/dist/cli-select.js +109 -0
  16. package/dist/cli-select.js.map +1 -0
  17. package/dist/cli.js +681 -93
  18. package/dist/cli.js.map +1 -1
  19. package/dist/config/init.js +105 -0
  20. package/dist/config/init.js.map +1 -0
  21. package/dist/config/settings.js +28 -7
  22. package/dist/config/settings.js.map +1 -1
  23. package/dist/config/swarm-toml.js +64 -15
  24. package/dist/config/swarm-toml.js.map +1 -1
  25. package/dist/core/compaction.js +64 -0
  26. package/dist/core/compaction.js.map +1 -0
  27. package/dist/core/loop.js +127 -10
  28. package/dist/core/loop.js.map +1 -1
  29. package/dist/core/model-client.js +23 -3
  30. package/dist/core/model-client.js.map +1 -1
  31. package/dist/core/termination.js.map +1 -1
  32. package/dist/durable/run-store.js +215 -0
  33. package/dist/durable/run-store.js.map +1 -0
  34. package/dist/edit-distance.js +39 -0
  35. package/dist/edit-distance.js.map +1 -0
  36. package/dist/eval/arms.js +93 -0
  37. package/dist/eval/arms.js.map +1 -0
  38. package/dist/eval/campaign-run.js +262 -0
  39. package/dist/eval/campaign-run.js.map +1 -0
  40. package/dist/eval/statistics.js +102 -0
  41. package/dist/eval/statistics.js.map +1 -0
  42. package/dist/evidence/attestation.js +111 -0
  43. package/dist/evidence/attestation.js.map +1 -0
  44. package/dist/evidence/blob-manifest.js +170 -0
  45. package/dist/evidence/blob-manifest.js.map +1 -0
  46. package/dist/evidence/blob-restore.js +100 -0
  47. package/dist/evidence/blob-restore.js.map +1 -0
  48. package/dist/evidence/blob-store.js +8 -3
  49. package/dist/evidence/blob-store.js.map +1 -1
  50. package/dist/evidence/bundle-manifest.js +17 -2
  51. package/dist/evidence/bundle-manifest.js.map +1 -1
  52. package/dist/evidence/bundle.js +23 -8
  53. package/dist/evidence/bundle.js.map +1 -1
  54. package/dist/evidence/dag.js +12 -3
  55. package/dist/evidence/dag.js.map +1 -1
  56. package/dist/evidence/fixtures/empty-assistant-turns.js +13 -0
  57. package/dist/evidence/fixtures/empty-assistant-turns.js.map +1 -0
  58. package/dist/evidence/ledger-record.js +12 -1
  59. package/dist/evidence/ledger-record.js.map +1 -1
  60. package/dist/evidence/ledger.js +4 -3
  61. package/dist/evidence/ledger.js.map +1 -1
  62. package/dist/evidence/model-call-recording.js +20 -1
  63. package/dist/evidence/model-call-recording.js.map +1 -1
  64. package/dist/evidence/predicate-catalogue.js +507 -0
  65. package/dist/evidence/predicate-catalogue.js.map +1 -0
  66. package/dist/evidence/record-kind.js +1 -0
  67. package/dist/evidence/record-kind.js.map +1 -1
  68. package/dist/evidence/retention.js +93 -0
  69. package/dist/evidence/retention.js.map +1 -0
  70. package/dist/evidence/review-page.js +160 -23
  71. package/dist/evidence/review-page.js.map +1 -1
  72. package/dist/evidence/run-spec.js +117 -0
  73. package/dist/evidence/run-spec.js.map +1 -0
  74. package/dist/evidence/signer-trust.js +60 -0
  75. package/dist/evidence/signer-trust.js.map +1 -0
  76. package/dist/evidence/signing.js +7 -2
  77. package/dist/evidence/signing.js.map +1 -1
  78. package/dist/evidence/store-mode.js +17 -0
  79. package/dist/evidence/store-mode.js.map +1 -0
  80. package/dist/evidence/turn-content.js +32 -0
  81. package/dist/evidence/turn-content.js.map +1 -0
  82. package/dist/evidence/verdict.js +111 -0
  83. package/dist/evidence/verdict.js.map +1 -0
  84. package/dist/evidence/verifier/rederive.d.mts +21 -0
  85. package/dist/evidence/verifier/rederive.mjs +331 -0
  86. package/dist/evidence/verifier/verify.d.mts +18 -0
  87. package/dist/evidence/verifier/verify.mjs +191 -4
  88. package/dist/evidence/verify-report.js +87 -0
  89. package/dist/evidence/verify-report.js.map +1 -0
  90. package/dist/exec/child-environment.js +120 -0
  91. package/dist/exec/child-environment.js.map +1 -0
  92. package/dist/exec/container-backend.js +58 -0
  93. package/dist/exec/container-backend.js.map +1 -0
  94. package/dist/exec/execution-envelope-record.js +31 -0
  95. package/dist/exec/execution-envelope-record.js.map +1 -0
  96. package/dist/exec/execution-mode.js +164 -0
  97. package/dist/exec/execution-mode.js.map +1 -0
  98. package/dist/exec/isolation-option.js +38 -0
  99. package/dist/exec/isolation-option.js.map +1 -0
  100. package/dist/exec/run-cancellation.js +33 -0
  101. package/dist/exec/run-cancellation.js.map +1 -0
  102. package/dist/exec/run-envelope.js +48 -0
  103. package/dist/exec/run-envelope.js.map +1 -0
  104. package/dist/exec/run-process.js +108 -0
  105. package/dist/exec/run-process.js.map +1 -0
  106. package/dist/gates/approval.js +59 -0
  107. package/dist/gates/approval.js.map +1 -0
  108. package/dist/gates/auto-resolve.js +60 -12
  109. package/dist/gates/auto-resolve.js.map +1 -1
  110. package/dist/gates/base-control.js +23 -39
  111. package/dist/gates/base-control.js.map +1 -1
  112. package/dist/gates/behaviour-probe.js +178 -0
  113. package/dist/gates/behaviour-probe.js.map +1 -0
  114. package/dist/gates/bond-runner.js +169 -0
  115. package/dist/gates/bond-runner.js.map +1 -0
  116. package/dist/gates/bonds.js +133 -0
  117. package/dist/gates/bonds.js.map +1 -0
  118. package/dist/gates/default-gates.js +115 -26
  119. package/dist/gates/default-gates.js.map +1 -1
  120. package/dist/gates/engine.js +110 -20
  121. package/dist/gates/engine.js.map +1 -1
  122. package/dist/gates/escalation.js +9 -0
  123. package/dist/gates/escalation.js.map +1 -1
  124. package/dist/gates/failure-signature.js +9 -0
  125. package/dist/gates/failure-signature.js.map +1 -0
  126. package/dist/gates/file-set.js +22 -0
  127. package/dist/gates/file-set.js.map +1 -1
  128. package/dist/gates/gate-capability.js +37 -0
  129. package/dist/gates/gate-capability.js.map +1 -0
  130. package/dist/gates/gate-definition.js.map +1 -1
  131. package/dist/gates/gate-runner.js +133 -19
  132. package/dist/gates/gate-runner.js.map +1 -1
  133. package/dist/gates/gate-set-seal.js +63 -0
  134. package/dist/gates/gate-set-seal.js.map +1 -0
  135. package/dist/gates/git-workspace.js +53 -4
  136. package/dist/gates/git-workspace.js.map +1 -1
  137. package/dist/gates/harness-reporting.js +67 -0
  138. package/dist/gates/harness-reporting.js.map +1 -0
  139. package/dist/gates/independent-verification.js +207 -0
  140. package/dist/gates/independent-verification.js.map +1 -0
  141. package/dist/gates/inherited-changes.js +26 -0
  142. package/dist/gates/inherited-changes.js.map +1 -0
  143. package/dist/gates/inspection-gates.js +140 -2
  144. package/dist/gates/inspection-gates.js.map +1 -1
  145. package/dist/gates/measure-snapshot.js +28 -3
  146. package/dist/gates/measure-snapshot.js.map +1 -1
  147. package/dist/gates/measures.js +78 -4
  148. package/dist/gates/measures.js.map +1 -1
  149. package/dist/gates/node-command-runner.js +57 -32
  150. package/dist/gates/node-command-runner.js.map +1 -1
  151. package/dist/gates/node-test-command.js +0 -34
  152. package/dist/gates/node-test-command.js.map +1 -1
  153. package/dist/gates/parsers.js +69 -2
  154. package/dist/gates/parsers.js.map +1 -1
  155. package/dist/gates/patch-paths.js +25 -0
  156. package/dist/gates/patch-paths.js.map +1 -0
  157. package/dist/gates/value-flow.js +335 -0
  158. package/dist/gates/value-flow.js.map +1 -0
  159. package/dist/install/health.js +1 -1
  160. package/dist/install/health.js.map +1 -1
  161. package/dist/machine-output.js +44 -0
  162. package/dist/machine-output.js.map +1 -0
  163. package/dist/node-floor.js +21 -0
  164. package/dist/node-floor.js.map +1 -0
  165. package/dist/providers/ai-sdk-model-client.js +85 -40
  166. package/dist/providers/ai-sdk-model-client.js.map +1 -1
  167. package/dist/providers/fixture-provider.js +18 -1
  168. package/dist/providers/fixture-provider.js.map +1 -1
  169. package/dist/providers/local-fetch.js +28 -0
  170. package/dist/providers/local-fetch.js.map +1 -0
  171. package/dist/providers/registry.js +48 -2
  172. package/dist/providers/registry.js.map +1 -1
  173. package/dist/providers/transport-trace-file.js +24 -0
  174. package/dist/providers/transport-trace-file.js.map +1 -0
  175. package/dist/providers/transport-trace.js +148 -0
  176. package/dist/providers/transport-trace.js.map +1 -0
  177. package/dist/select/calibrate.js +3 -0
  178. package/dist/select/calibrate.js.map +1 -1
  179. package/dist/select/calibration-measures.js +26 -0
  180. package/dist/select/calibration-measures.js.map +1 -1
  181. package/dist/select/calibration-report.js +15 -2
  182. package/dist/select/calibration-report.js.map +1 -1
  183. package/dist/select/calibration-run.js +61 -15
  184. package/dist/select/calibration-run.js.map +1 -1
  185. package/dist/select/calibration-summary.js +15 -3
  186. package/dist/select/calibration-summary.js.map +1 -1
  187. package/dist/select/competency-table.js +164 -0
  188. package/dist/select/competency-table.js.map +1 -0
  189. package/dist/select/reward.js +11 -0
  190. package/dist/select/reward.js.map +1 -1
  191. package/dist/select/routing-log.js +2 -2
  192. package/dist/select/routing-log.js.map +1 -1
  193. package/dist/select/routing-mode.js +73 -0
  194. package/dist/select/routing-mode.js.map +1 -0
  195. package/dist/select/routing-record.js +16 -0
  196. package/dist/select/routing-record.js.map +1 -1
  197. package/dist/select/servable-candidates.js +30 -0
  198. package/dist/select/servable-candidates.js.map +1 -0
  199. package/dist/select/task-class.js +8 -0
  200. package/dist/select/task-class.js.map +1 -1
  201. package/dist/select/ucb.js +21 -5
  202. package/dist/select/ucb.js.map +1 -1
  203. package/dist/telemetry/otel.js +130 -0
  204. package/dist/telemetry/otel.js.map +1 -0
  205. package/dist/tools/chokepoint-record.js +8 -7
  206. package/dist/tools/chokepoint-record.js.map +1 -1
  207. package/dist/tools/chokepoint.js +4 -4
  208. package/dist/tools/chokepoint.js.map +1 -1
  209. package/dist/tools/derivation.js +44 -4
  210. package/dist/tools/derivation.js.map +1 -1
  211. package/dist/tools/file-tools.js +26 -10
  212. package/dist/tools/file-tools.js.map +1 -1
  213. package/dist/tools/policy-guard.js +165 -0
  214. package/dist/tools/policy-guard.js.map +1 -0
  215. package/dist/tools/search-tool.js +16 -9
  216. package/dist/tools/search-tool.js.map +1 -1
  217. package/dist/tools/shell-canonical.js +104 -0
  218. package/dist/tools/shell-canonical.js.map +1 -0
  219. package/dist/tools/shell-command.js +154 -0
  220. package/dist/tools/shell-command.js.map +1 -0
  221. package/dist/tools/shell-tool.js +22 -17
  222. package/dist/tools/shell-tool.js.map +1 -1
  223. package/dist/tools/tool-argument-decoding.js +3 -0
  224. package/dist/tools/tool-argument-decoding.js.map +1 -1
  225. package/dist/tools/workspace-path.js +4 -4
  226. package/dist/tools/workspace-path.js.map +1 -1
  227. package/dist/tools/workspace-tools.js +13 -9
  228. package/dist/tools/workspace-tools.js.map +1 -1
  229. package/dist/tui/calibrate-interface.js +59 -0
  230. package/dist/tui/calibrate-interface.js.map +1 -0
  231. package/dist/tui/calibrate-screen-model.js +118 -0
  232. package/dist/tui/calibrate-screen-model.js.map +1 -0
  233. package/dist/tui/calibrate-screen.js +44 -0
  234. package/dist/tui/calibrate-screen.js.map +1 -0
  235. package/dist/tui/calibrate-store.js +22 -0
  236. package/dist/tui/calibrate-store.js.map +1 -0
  237. package/dist/tui/calibrate-view.js +92 -0
  238. package/dist/tui/calibrate-view.js.map +1 -0
  239. package/dist/tui/confirmation-queue.js +31 -18
  240. package/dist/tui/confirmation-queue.js.map +1 -1
  241. package/dist/tui/plain-lines.js +15 -0
  242. package/dist/tui/plain-lines.js.map +1 -1
  243. package/dist/tui/render-timings.js +22 -0
  244. package/dist/tui/render-timings.js.map +1 -0
  245. package/dist/tui/screen-model.js +86 -5
  246. package/dist/tui/screen-model.js.map +1 -1
  247. package/dist/tui/screen.js +4 -0
  248. package/dist/tui/screen.js.map +1 -1
  249. package/dist/tui/session-interface.js +31 -4
  250. package/dist/tui/session-interface.js.map +1 -1
  251. package/dist/tui/session-view.js +45 -0
  252. package/dist/tui/session-view.js.map +1 -1
  253. package/dist/tui/verify-bundle.js +2 -2
  254. package/dist/tui/verify-bundle.js.map +1 -1
  255. package/dist/tui/view-state.js +10 -2
  256. package/dist/tui/view-state.js.map +1 -1
  257. package/dist/workers/attempt-selector.js +126 -0
  258. package/dist/workers/attempt-selector.js.map +1 -0
  259. package/dist/workers/attempt-universe.js +50 -0
  260. package/dist/workers/attempt-universe.js.map +1 -0
  261. package/dist/workers/fan-out.js +35 -0
  262. package/dist/workers/fan-out.js.map +1 -0
  263. package/dist/workers/graph-record.js +109 -0
  264. package/dist/workers/graph-record.js.map +1 -0
  265. package/dist/workers/graph-tool.js +65 -0
  266. package/dist/workers/graph-tool.js.map +1 -0
  267. package/dist/workers/merge-queue.js +66 -31
  268. package/dist/workers/merge-queue.js.map +1 -1
  269. package/dist/workers/parallel-report.js +27 -0
  270. package/dist/workers/parallel-report.js.map +1 -1
  271. package/dist/workers/parallel-run.js +274 -25
  272. package/dist/workers/parallel-run.js.map +1 -1
  273. package/dist/workers/planner-run.js +80 -0
  274. package/dist/workers/planner-run.js.map +1 -0
  275. package/dist/workers/pool.js +52 -0
  276. package/dist/workers/pool.js.map +1 -0
  277. package/dist/workers/schedule.js +64 -0
  278. package/dist/workers/schedule.js.map +1 -0
  279. package/dist/workers/selection-record.js +61 -0
  280. package/dist/workers/selection-record.js.map +1 -0
  281. package/dist/workers/task-contract.js +117 -0
  282. package/dist/workers/task-contract.js.map +1 -0
  283. package/dist/workers/task-graph.js +156 -0
  284. package/dist/workers/task-graph.js.map +1 -0
  285. package/dist/workers/trail-tool.js +27 -0
  286. package/dist/workers/trail-tool.js.map +1 -0
  287. package/dist/workers/trail.js +175 -0
  288. package/dist/workers/trail.js.map +1 -0
  289. package/dist/workers/worktree.js +30 -0
  290. package/dist/workers/worktree.js.map +1 -1
  291. package/docs/README.md +46 -0
  292. package/docs/build-guide.md +25 -11
  293. package/docs/claims.md +23 -2
  294. package/docs/security-coverage.md +58 -0
  295. package/package.json +5 -1
  296. package/dist/gates/coverage-artifact.js +0 -55
  297. package/dist/gates/coverage-artifact.js.map +0 -1
  298. package/dist/tools/sandbox.js +0 -108
  299. package/dist/tools/sandbox.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,316 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 14.0.0 - 2026-09-06
6
+
7
+ ### Breaking
8
+
9
+ - **`swarm ci` no longer reports a patch verified because the project's suite passed.** A suite
10
+ says nothing broke; it does not say the task was done, because it tests the behaviour the
11
+ project already had and a task adds behaviour it did not. `verified` now requires both a
12
+ passing suite and a task oracle that accepts, so a run without `--oracle <command>` exits
13
+ non-zero with `task: unjudged` and the reason printed. Anything scripted against `swarm ci`
14
+ exiting 0 needs an oracle passed to it. This is the fix for a measured 22.2% false-green rate,
15
+ not a tightening for its own sake.
16
+ - **The major version moves because of that one change.** Everything else below is additive.
17
+
18
+ ### Added
19
+
20
+ - **`swarm ci` reports whether anything broke and whether the task was done, separately.**
21
+ `regression` says the project's own suite still passes; `task` says a trusted oracle agrees the
22
+ work was done, and reads `unjudged` where none was given. `verified` requires both, so a run
23
+ with no oracle is unverified with the reason printed rather than verified by omission. This
24
+ closed a real 22.2% false-green rate: a repository's suite tests the behaviour the project
25
+ already had, and a task adds behaviour it did not, so a patch that adds a feature badly still
26
+ passes it.
27
+ - **`--oracle <command>` supplies that task check.** Nothing infers one. It runs in the fresh
28
+ checkout after the suite, whether or not the suite passed, so its verdict is readable either
29
+ way.
30
+ - **`--install` installs the fresh checkout's dependencies from its lockfile**, with install
31
+ scripts off. Off by default, because installing runs whatever the registry serves. Without it a
32
+ real project has no test runner in a fresh clone, every check stands down, and the honest
33
+ answer is that nothing was measured rather than that nothing passed. Reporting the second was a
34
+ defect, and it is why this exists.
35
+ - **`--isolation <runtime[:image]>` runs commands behind a kernel-enforced boundary**, on docker,
36
+ podman or nerdctl, with no network, a read-only root, a tmpfs, dropped capabilities, no new
37
+ privileges, and memory and pid ceilings.
38
+ - **A run measures what it actually executes under, before the first tool call.** The containment
39
+ self-test runs the escapes rather than reasoning about them: read a host file outside the
40
+ workspace, write outside it, open a socket. Whatever gets through is named in an
41
+ `execution-envelope` record and printed. With no kernel-enforced backend the answer is
42
+ `restricted`, and that is the word used. A backend that refused every escape and could not
43
+ reach its own workspace is `unknown` rather than `isolated`, since a command that sees nothing
44
+ is contained and cannot work.
45
+ - **A bundle carries a DSSE envelope over an in-toto statement**, signed over the
46
+ pre-authentication encoding rather than the payload bytes, so what a run produced can be checked
47
+ by tooling that never heard of this project.
48
+ - **Durable run state, and the commands to work with it.** `swarm list-runs`, `swarm inspect
49
+ <run-id>`, `swarm resume`, `swarm retry-step`, `swarm abort` and `swarm repair`, over a
50
+ `node:sqlite` store in WAL mode with intent recorded before effect and an idempotency key per
51
+ step, so an interrupted run is taken up rather than started again and a dead run's leases are
52
+ released rather than held forever.
53
+ - **`swarm gc [--older-than 30d] [--remove]`** says what stored evidence would be removed before
54
+ removing any of it.
55
+ - **`--json` on a run and on `swarm ci` and `swarm inspect`**: line-delimited JSON, one line per
56
+ event and one result at the end, each naming its schema.
57
+ - **`confirm_timeout_minutes` under `[interface]`**, so a confirmation nobody answers is refused
58
+ after thirty minutes rather than holding a run open indefinitely.
59
+ - **The false-green rate is measured against an oracle the tool is never given.** Each
60
+ real-repository task carries two: one sealed before the runs and handed to the tool, one written
61
+ from the task text and held back from it. 0 of 11, 95% CI [0.0, 25.9], counting the eleven
62
+ patches the tool certified. `node scripts/second-oracle-pass.mjs` reproduces it from recorded
63
+ diffs with no model calls. A single-oracle version of this number was published and withdrawn:
64
+ the same test on both sides of a comparison agrees with itself.
65
+ - **`scripts/restore-bundle-blobs.mjs` puts offloaded artifacts back**, writing a file only where
66
+ its content hashes to the digest the bundle names.
67
+
68
+ - **A run has a wall budget of its own.** `--max-wall-minutes <n>`, or `max_wall_minutes`
69
+ under `[budgets]` in swarm.toml, bounds the whole run: the first loop and every auto-resolve
70
+ attempt draw from it, the attempts stop when it is spent, and the run goes on to its final
71
+ gates and its bundle with the budget recorded on the chain as a `session-budget` record.
72
+ Before this each loop had its own half hour and the run as a whole had no bound, which is how
73
+ a run measured inside a forty-five minute container could outlast it and leave nothing: the
74
+ campaign's first arm lost seven runs that way. Unset, nothing changes: each loop keeps its
75
+ half hour.
76
+ - **The criteria a run is measured by are sealed on the chain before the model is asked for
77
+ anything.** Every gate with its severity and the rule that reads it, the budgets, the attempt
78
+ cap and the ratchet arms, as one record written before the loop, and the embedded verifier
79
+ holds every gate run to it: a gate the seal does not name, a severity or rule it did not
80
+ declare, or a sealed gate missing from the final attempt fails verification. A bundle of the
81
+ new format that carries gate runs and no seal is refused, since that is the one way to dodge
82
+ the check; a bundle that ran no gates has nothing to be held to.
83
+ - **Every passing gate is bonded.** After the fixed point, each gate that passed is handed one
84
+ added file it has to refuse, a failing test, a TODO, a credential-shaped token, a file outside
85
+ the declared set, a change over the budget, and the result is a `gate-bond` record carrying
86
+ the observation. A check that refused it held. A check that passed over a bond it demonstrably
87
+ saw is vacuous, which makes a blocking gate's run not green whatever the cycle said, and the
88
+ exit code says so. A command that passed where nothing shows it read the bond is unshown,
89
+ named as such and never promoted to held. A gate with no bond is recorded as not bonded. The
90
+ review page carries the bond beside every gate and paints a vacuous pass red.
91
+ - **A bundle carries `rederive.mjs` beside `verify.mjs`.** Where the verifier asks whether a
92
+ bundle is what it says it is, the re-deriver asks whether a third party applying this tool's
93
+ rules would reach the verdicts it records: every gate status from the recorded exit code and
94
+ output under the rule the record now names, every ratchet decision from its recorded
95
+ measures, every bond from its observation, every claim from its predicate, and the gate runs
96
+ from the seal. What it cannot re-derive from the bundle alone, it names rather than agrees
97
+ with. Dependency-free, like the verifier, and parity-tested against the parsers it mirrors.
98
+ - **Routing by a competency table, class by class, and never by interpolation.** `swarm
99
+ calibrate` now writes, beside its pick, what each model was measured to do on each class of
100
+ task: executed repeats and the ones whose gate passed, per sweep, read off the sweep's own
101
+ `calibration-run` records and folded across sweeps of the same golden set. A task's router
102
+ asks the table for its class before the reward log has enough samples to say anything, and
103
+ where a candidate has at least six executed runs on that class the best gate share stands
104
+ as the default, recorded as a `competency` assignment with the counts it was chosen on. Where
105
+ no candidate clears the floor, or none has an entry for the class at all, the table abstains
106
+ by name and the calibration pick stands as before; a model's showing on one class is never
107
+ read as evidence about another.
108
+ - **A fifty-repository campaign harness, with its criteria sealed before anything was
109
+ selected.** `campaign/criteria.md` was committed before the first GitHub query, the method in
110
+ `campaign/methodology.md` before the first run, and `campaign/harness/` sequences the rest:
111
+ the search saved raw, the candidate walk judged by rules that name the first one failed, one
112
+ seeded defect per repository accepted only where the repository's own suite passes before and
113
+ fails after, and one run per arm inside a pinned container on an internal network whose only
114
+ other member is a forwarder to the model backend. Results are read off the bundle and the
115
+ tree, never off the run's exit code.
116
+ - **The campaign was run: two local arms over fifty repositories, one hundred runs, ninety-three
117
+ bundles that verify.** Fifty seeds in five languages, each a repository whose own suite passed
118
+ before the seeded line and failed after it, run once per arm under a forty-five minute
119
+ budget. On qwen3.8:27b over rapid-mlx, forty-three runs executed and every bundle verifies,
120
+ twenty-six of them fixing the seed; seven ran out of budget without a bundle. On
121
+ qwen3.6:35b-a3b over Ollama, all fifty executed and verify, thirty-nine fixing the seed, at a
122
+ median of nine minutes against sixteen. The frontier arm was not run, because the key it
123
+ needs has no balance, and the report carries it with zero runs rather than leaving it out.
124
+ `campaign/results/report.md` is generated from the result records alone, the bundles are
125
+ under `campaign/corpus/`, and the method with its dated amendments is in
126
+ `campaign/methodology.md`. The campaign measured the CLI packed on 2026-09-02 and stayed on
127
+ it while two defects it exposed were fixed in the tree: a shell allowlist that refused every
128
+ toolchain but node's, and a model call with no deadline.
129
+ - **Two scheduled proofs on GitHub Actions.** A nightly one runs the suite and the fuzz smoke
130
+ from a clean clone, then verifies the committed reference bundle with its own verifier and
131
+ refuses a one-byte copy of it, uploading the transcript. A weekly one installs a pinned
132
+ Ollama on the runner, seeds a workspace whose test fails on a real defect, runs one task
133
+ through the built CLI, and verifies the fresh bundle in the same job; the task outcome is
134
+ recorded and never judged. Neither continues on error, and a scheduled failure of either
135
+ opens an issue with the run link.
136
+
137
+ - **A debug copy of what a local backend was actually sent and actually said.**
138
+ `SWARM_TRANSPORT_TRACE=<path>` writes the raw request body and every raw response frame of
139
+ every local call to a JSONL artifact, before anything parses them. Off with no path, which is
140
+ the default: the artifact holds whole prompts and whole completions unscrubbed, so it is not
141
+ the ledger and not a thing to leave running. It exists for one question the assembled response
142
+ cannot answer, which is whether a turn that arrived empty was empty on the wire, dropped
143
+ during stream assembly, or genuinely empty from the model. The response body is teed rather
144
+ than buffered, so streaming timing is unchanged and the first-token measurement stays real.
145
+ - **A screen for `swarm calibrate`.** A three-model sweep is 180 runs over roughly three hours,
146
+ the longest thing this tool does, and it wrote plain progress lines and nothing else. It has a
147
+ view of its own now, because a sweep has none of the things the run screen is built around: it
148
+ has a grid. Runs finished out of runs planned, the run in flight, a row per model in plan
149
+ order with green counted over executed runs rather than attempted ones, abstentions named by
150
+ the reason code the harness recorded, and the last few outcomes. No estimate of time
151
+ remaining, which would be a prediction people plan around, and no keys, because a sweep has
152
+ nothing to scroll. Off a terminal it writes one line per finished run in the same words, so
153
+ the log and the screen stay one account of the run.
154
+ - **An index over the evidence column of the review page.** Records are grouped by turn and
155
+ folded by default, the per-record digest moved into the expanded payload where the reviewer
156
+ resolving a claim through it is already looking, and the column opens with how many records
157
+ there are, how many of each type, a button per type and a search box. The 08-23 calibration
158
+ bundle is 3,716 records; before this there was no way to find anything in it. A rendering
159
+ change and nothing else: no record changed, and four existing bundles verify unaltered.
160
+ - **A behaviour probe over changed functions.** A function that answered several ways at the
161
+ base commit and answers one way now is reported by a blocking gate. Reading the text will
162
+ never tell a stub from a correct constant, and running it does not have to: the comparison is
163
+ between two measured variances rather than a judgement about what a function is for. Quiet on
164
+ a function that was always constant, one that takes no arguments, and one that now refuses
165
+ every input, which is a tighter signature rather than a missing body.
166
+
167
+ - **Workers that read what the others have already tried.** They ran side by side and saw
168
+ nothing of each other, so two could declare the same file and one could spend its whole
169
+ attempt cap on a gate that had already refused the same fix on the chain next door. They
170
+ coordinate now through the thing they were already writing: a `read_trail` tool projects
171
+ typed signals off the peers' ledgers, which files are claimed, which gates failed and how
172
+ often, which attempts the ratchet rejected, which approaches are spent. No daemon, no bus,
173
+ nothing anyone writes through, and no read of a chain but the peers' own. Nothing it returns
174
+ can render green: every signal comes from a record rather than from model text, every line
175
+ names the peer it is about, and no signal kind reports a success. A run that is not parallel
176
+ is offered none of it and sends the same tools and the same prompt, byte for byte.
177
+ - **`--redundancy <n>`: try each task several ways and land the best of them.** Attempts
178
+ diverge because each carries a seed derived from the task, the model and the attempt number,
179
+ so a report can re-derive them. The winner is chosen by a comparator over harness-measured
180
+ numbers with the precedence written down, never by asking a model which answer it likes: four
181
+ earn-it dimensions above every do-less one, a dimension nothing measured abstained on by
182
+ name, and a dimension only one side measured won by the side that measured it. Counting
183
+ happens over a file universe fixed across the attempts first, so opening a large test file
184
+ gains the opener nothing. The whole ranking goes on the chain, losers and their reasons
185
+ included. A refused winner falls to the next attempt only where the integrated gates were
186
+ what refused it.
187
+ - **`--goal <text>`: the tool breaks the goal into tasks itself.** A planner run reads the
188
+ workspace with read-only tools and declares a task graph, which is checked before anything
189
+ runs for unique ids, resolving dependencies, no cycle, and files that two unordered nodes
190
+ do not share. The graph is a ledger record written before the first worker starts. Nodes
191
+ land layer by layer, each layer branching from the tree the one before it left, and a node
192
+ whose parent did not land is recorded as blocked rather than run against a tree that lacks
193
+ it. `--tasks` also reads a JSON graph now, so a hand-written one and a declared one are the
194
+ same artifact. The claim says every declared node ran and landed and says nothing about
195
+ whether they satisfy the goal, which this tool does not check and says so.
196
+ - **`--concurrency <n>`,** capping how many workers hold a worktree at once. It defaults to
197
+ one against a local model server, because every worker in the run is asking one resident
198
+ model for tokens, so the parallelism buys nothing and costs the memory the model lives in.
199
+ Against a model served elsewhere it defaults to four and deliberately does not grow with the
200
+ hardware: what the machine is being asked for is N copies of the project's test suite, and a
201
+ bigger machine does not make those cheaper. The fan-out was unbounded before this, at any
202
+ redundancy.
203
+
204
+ ### Changed
205
+
206
+ - **Bundle format 2.** A format 2 bundle carries the sealed criteria, a bond per passing gate,
207
+ and the re-derivation script; every gate-run record names the rule that read it. Format 1
208
+ bundles are still read, replayed and verified, and the four committed ones verify unchanged
209
+ under the new verifier, which reports their lack of a seal as predating sealing rather than
210
+ as a defect.
211
+ - **The corpus replay reads the v12 falsification corpus from the `v12-final` tag.** It named
212
+ `main`, which held the v12 tree until `main` was moved onto this lineage; from then on the
213
+ three replay tests skipped under a green run, on CI and on any clone that had moved its
214
+ `main`. A tag is the one name that does not move, and the checkout test now pins that the
215
+ corpus was reached rather than allowing a skip.
216
+ - **The reports the ratchet reads come off streams the harness owns, not files.** The first fix
217
+ for the counter forgery below had the runner write reports to paths the harness named under
218
+ the session store. An adversarial pass took that apart: a destination is an argument of the
219
+ spawned process, so `ps -p $PPID -o command=` hands it to any test that asks, and the file is
220
+ writable by anything running as the same user, since the writer is the same uid. Getting the
221
+ harness to read a forgery did not work, because it reads as soon as the runner exits and won
222
+ that race every time; what did work, four times out of four, was destroying both measures, and
223
+ an abstention is not a violation. So there is no file: tap goes to stdout and lcov to stderr,
224
+ and under process isolation a test's own output is captured by the parent and folded into the
225
+ reporters' streams as escaped comments. The base-control result had the identical hole and
226
+ gets the identical fix.
227
+ - **The base a run is measured against is resolved to a commit before anything reads it.** HEAD
228
+ is a symbolic ref spent at the moment each base-side question is asked, and `git` is on the
229
+ default shell allowlist, so one unconfirmed tool call moved it. Measured on a scratch
230
+ repository with two of three tests deleted in the tree: after a `git commit -am` the base
231
+ declared 1 test instead of 3 and the change set held 0 files instead of 1, so the deletion the
232
+ ratchet exists to catch stopped being a deletion. The parallel path already resolved its base
233
+ before handing it to any worker; this is the single-run path held to the same thing.
234
+ - **The ratchet's collected and skipped test counts come from a report the runner wrote, not
235
+ from what it printed.** Node's default reporter passes a test's own `console.log("# tests
236
+ 999")` through ahead of its own counters and the counter reader took the first match, so four
237
+ print statements in one test file reported 999 collected for a suite of one. That number fed a
238
+ blocking arm. Where the harness can vouch for the invocation it now asks node for TAP at a
239
+ path of its own and counts result points there, which a test cannot print into existence.
240
+ Where it cannot vouch for the invocation both measures are null and the ratchet abstains on
241
+ them by name, which is stricter than the reading it replaces.
242
+ - **An empty assistant turn is recorded as an abstention rather than as a run of the model.**
243
+ The harness classifies every turn as it crosses into the ledger and stamps the verdict on the
244
+ record, so a repeat whose only turn arrived empty reports `abstained` with a machine-readable
245
+ reason instead of being scored against the model as a wrong answer. Two calibration bundles
246
+ had been scored with those folded in. Calibration now reads `executed` off the records rather
247
+ than off the loop's own counter, because a reviewer re-deriving that number has the records
248
+ and not the loop.
249
+ - **A seed is recorded with whether the backend took it.** Ollama and rapid-mlx both accept a
250
+ seed field and neither promises to sample from it, and the SDK reports a refused setting as a
251
+ warning. Those warnings now reach the record, so a seed in a bundle is never read as a seed
252
+ that made the run re-derivable.
253
+ - **The derivation heuristic reads a shell command as a command.** Its threshold is unchanged,
254
+ because lowering it flags ordinary commands that mention a filename someone read. What changed
255
+ is that where an argument parses as a shell command it is also compared as one, with flags
256
+ dropped and interpreters folded together, so inserting `-fsSL` and swapping `sh` for `bash`
257
+ no longer rewrites it past the match.
258
+ - **An assertion comparing a value with itself stops counting as an assertion.** The rule was a
259
+ literal against the identical literal; it now substitutes the file's own bindings first, so
260
+ `expect(v0.a).toBe(v0.a)` and the spelling that binds one side to a name are both seen. This
261
+ is arithmetic over expressions and not a judgement about meaning: deciding that two
262
+ *different* expressions mean the same thing is still refused.
263
+ - **The secret-scan gate reads a credential written in pieces.** Where a change rejoins them,
264
+ by concatenation or by a template literal, the gate rebuilds the value those lines make and
265
+ hands it to the detector that already decides about credentials, under the name the change
266
+ gave it. No second detector and no new threshold. Halves that are never rejoined build no
267
+ value and are still not caught, which build guide 7.1 says in those words.
268
+
269
+ ### Fixed
270
+
271
+ - **Every evidence bundle verifies from a clone again.** An earlier weight reduction moved record
272
+ payloads out of the tracked tree, which stopped 47 of 51 bundles passing their own verifier,
273
+ four of them cited in the README and `claims.md` in words like "verify.mjs exit 0". Payloads are
274
+ never offloaded now; only derived artifacts are, and those are regenerated from the records.
275
+ - **The README no longer cites a number the documents behind it withdrew**, and its gate tally is
276
+ counted from the table rather than from memory.
277
+
278
+ - **A parallel run now sweeps up the branches it created.** Worker branches outlive their
279
+ worktrees on purpose, because the merge queue merges from them after the working copy is
280
+ gone, and nothing outlived the queue: a repository gained a branch per worker per run, for
281
+ ever. The integration branch is never swept, since that is the result. It prunes worktrees
282
+ first, which is what stops a run killed part-way from making the next one fail on a path git
283
+ still believes in.
284
+ - **`--goal` says how the planner stopped, not just that it declared nothing.** Running out of
285
+ steps, answering in prose, and returning nothing at all want three different responses, and
286
+ the message treated them as one. It now names the stop reason and the step count and says
287
+ what to try for each.
288
+ - **Two review-page summaries that trailed off after the colon.** `worker-finished` and
289
+ `merge-attempt` both asked for a field neither writer emits, so they rendered as
290
+ `worker-1 finished:` with nothing after it. They read what is actually recorded now.
291
+ - **The weekly scan stopped filing the same 21 findings every Monday.** Data is excluded by
292
+ path, meaning the secret scrubber's own fuzz corpus and the captured shakedown logs and
293
+ nothing else; the nine findings in real source carry a suppression naming the one rule and the
294
+ reason, at the line that carries the finding. Under the workflow's own command it now reports
295
+ zero and exits 0, so the weekly issue means something arrived that nobody has looked at.
296
+
297
+ ## 13.1.9
298
+
299
+ ### Added
300
+
301
+ - **A line that says the run is alive, and what it is doing.** A spinner that turns, the current
302
+ activity, how long it has been going, and while the model is talking, the tail of what it is
303
+ saying. Nothing on the screen moved before this: the status said `thinking (step 3)` and
304
+ stayed there, and the only thing that changed was a seconds counter the layout hides below 80
305
+ columns or 12 rows, so a run taking a minute looked exactly like one that had hung.
306
+ - **The model's words as they arrive.** The stream was already being drained to time the first
307
+ token and every piece of text was discarded. It is handed on now. One line of it: the whole
308
+ response lands in the action stream when it arrives and in the ledger for ever, and repeating
309
+ it as it streams would be the same text three times.
310
+ - **The tool that is running, named while it runs.** Between a tool starting and finishing
311
+ nothing was emitted, which for a shell command is a screen sitting still for a minute and a
312
+ half. A tool that finishes inside a frame still draws nothing, which is the right amount.
313
+
3
314
  ## 13.1.8
4
315
 
5
316
  ### Added