bullswarm 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/GOAL.md +3 -1
  3. package/README.md +120 -19
  4. package/connectors/opencode2.json +6 -0
  5. package/data/README.md +8 -3
  6. package/data/openrouter-benchmarks.json +9933 -9854
  7. package/docs/audits/2026-09-09-codebase-audit.md +1718 -0
  8. package/docs/claude-dynamic-workflow-mechanics.md +9 -5
  9. package/docs/design/2026-09-06-caller-first-cli.md +2 -0
  10. package/docs/dynamic-workflow-qa.md +2 -0
  11. package/docs/dynamic-workflow-v2-execution-plan.md +2 -0
  12. package/docs/experiments/2026-08-28-trending-ai-autonomy.md +2 -0
  13. package/docs/experiments/2026-08-29-dogfood-bullswarm-builds-bullswarm.md +2 -0
  14. package/docs/experiments/2026-08-29-ultracode-vs-bullswarm.md +2 -0
  15. package/docs/experiments/2026-08-31-v2-component-probes.md +2 -0
  16. package/docs/experiments/2026-09-06-caller-planner-evaluation.md +2 -0
  17. package/docs/integration-audit-2026-08-31.md +2 -0
  18. package/docs/planner-prompt-audit-2026-08-29.md +2 -0
  19. package/docs/studies/portal-token-diet.md +10 -0
  20. package/docs/workflow-agent-usability-audit-2026-08-27.md +2 -0
  21. package/fixtures/failures/late-failure.json +6 -0
  22. package/fixtures/failures/structured-error.json +6 -0
  23. package/package.json +1 -1
  24. package/skill/SKILL.md +21 -4
  25. package/skill/references/operations.md +29 -4
  26. package/src/cli.js +149 -53
  27. package/src/help.js +45 -16
  28. package/src/lib/agent-events.js +0 -56
  29. package/src/lib/assignments.js +1 -1
  30. package/src/lib/cli-flags.js +212 -0
  31. package/src/lib/config.js +24 -9
  32. package/src/lib/epoch-benchmarks.js +5 -6
  33. package/src/{workflow → lib}/fsjson.js +5 -0
  34. package/src/lib/num.js +31 -0
  35. package/src/lib/opencode-kaihk.js +46 -0
  36. package/src/lib/openrouter-models.js +17 -13
  37. package/src/lib/quota.js +28 -25
  38. package/src/lib/route.js +46 -28
  39. package/src/lib/spend.js +12 -9
  40. package/src/lib/state.js +98 -3
  41. package/src/lib/strategy.js +80 -10
  42. package/src/lib/usage.js +0 -50
  43. package/src/lib/verify.js +39 -11
  44. package/src/meters/framework.js +4 -7
  45. package/src/setup.js +76 -55
  46. package/src/strategy-cli.js +232 -164
  47. package/src/strategy-dashboard.js +35 -28
  48. package/src/workflow/action-validator.js +32 -3
  49. package/src/workflow/cli.js +51 -9
  50. package/src/workflow/dashboard.js +9 -6
  51. package/src/workflow/evidence-output.js +0 -21
  52. package/src/workflow/ownership.js +0 -2
  53. package/src/workflow/runs-cli.js +97 -14
  54. package/src/workflow/short-id.js +15 -1
  55. package/src/workflow/v2-dispatch.js +30 -14
  56. package/src/workflow/v2-outcome.js +224 -3
  57. package/src/workflow/v2-planner.js +11 -1
  58. package/src/workflow/v2-runtime.js +131 -9
  59. package/src/workflow/v2-scheduler.js +0 -4
  60. package/src/workflow/v2-state.js +27 -6
  61. package/src/workflow/watch-cli.js +14 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,252 @@
1
1
  # bullswarm changelog
2
2
 
3
+ ## 0.28.0 — context diet
4
+
5
+ - `workflow runs result <id> --summary` prints a compact status-loop
6
+ envelope, `schemaVersion: "bullswarm.workflow.result-summary.v1"`. It
7
+ carries `runId`, `shortId`, `status`, `verified`, `executionMode`,
8
+ `reason`, `finishedAt`, the goal's first line trimmed to 120 characters
9
+ plus `goalBytes`, each requirement as `{ id, status, mandatory,
10
+ evidenceCount, why }` (`why` is the latest evidence's first line trimmed
11
+ to 200 characters), each action as `{ id, kind, lane, effort, status,
12
+ pool, model, reasoning, wallSec, outFile, bytes }`, `concerns: { count,
13
+ first }` (up to three one-liners), the `usage` block, and `next: { full:
14
+ "bullswarm workflow runs result <id> --json", outputs: [<outFile paths>]
15
+ }`. `--summary` implies JSON with or without `--json`; it is not
16
+ TTY-dependent. The full `bullswarm.workflow.result.v2` envelope is
17
+ unchanged and stays the default. Read the full envelope on a failed or
18
+ partial run, or before judging evidence. Flag, help, and example:
19
+ `src/lib/cli-flags.js`, `src/help.js` (`Usage: bullswarm workflow runs
20
+ result <shortId|runId> [--json] [--summary]`; `--summary` "print the
21
+ compact JSON status-loop envelope; implies --json"). A terminal `workflow
22
+ watch` prints `next: bullswarm workflow runs result <shortId> --json
23
+ --summary`.
24
+
25
+ - Byte accounting on every action attempt. `state.attempts[].bytes` is
26
+ `{ taskFile, authorPrompt, kernel, dependencyInputs, output }` — the task
27
+ file the kernel wrote, the action's own prompt text as authored, the
28
+ remainder after subtracting that prompt and any embedded requirement
29
+ text, the sum of the dependency output files the task points at (0 when
30
+ there are none; `digestOf` drill-down paths are pointers, not inputs),
31
+ and the durable out file on completion. The result envelope copies the
32
+ last attempt's `bytes` onto `actions[]` and totals `usage.bytes: {
33
+ taskFiles, dependencyInputs, outputs }`. `workflow runs show` appends
34
+ `in <taskFile>/<dependencyInputs> out <output>` per attempt (the unit
35
+ fixture prints `in 3.1K/60.8K out 14.8K`); missing values stay blank /
36
+ null, never guessed. These are UTF-8 byte counts, never tokens.
37
+
38
+ - `kind: "digest"` (analyze/low) is an extractive condensation of its
39
+ dependencies' outputs so an expensive consumer reads one artifact
40
+ instead of many raw out-files. The kernel writes the whole task: quote
41
+ verbatim (never paraphrase or judge) each source's delivered items,
42
+ validation numbers, commands and their output, unfinished work, and
43
+ every shared-file or integrator request; one section per source headed
44
+ by its absolute output path; no verdicts, no recommendations, no new
45
+ claims; target at most a quarter of the input bytes or 8 KB, whichever
46
+ is larger. The author's prompt is focus guidance only. Validation
47
+ (exit 2 with the reason at `workflow plan validate` and `workflow goal
48
+ --program`): a digest must depend on at least one action, must have
49
+ empty `evidenceFor`, owns no files, needs no `affects`. No evidence
50
+ action may list a digest in `dependsOn` — evidence reads the real
51
+ artifacts. Consumers that depend on a digest receive, in their
52
+ dependency artifacts, the digest entry plus `digestOf: [{ actionId,
53
+ outputFile }]` for each digested source. Use one when three or more
54
+ writers feed a single integrator, or when a consumer's dependency
55
+ outputs would exceed roughly 20 KB; never for evidence.
56
+
57
+ - The planning contract states the kind. `workflow plan contract --json`
58
+ (`Usage: bullswarm workflow plan contract "<goal>" [--cwd <dir>]
59
+ [--json]`) returns 16 rules; `rules[5]` lists `digest=analyze/low` in
60
+ the kind table and `rules[6]` is the extractive digest rule (when to
61
+ insert one, `digestOf`, empty `evidenceFor` / `ownedFiles`, evidence
62
+ must not depend on a digest).
63
+
64
+ - The packaged skill's status loop (`skill/SKILL.md`,
65
+ `skill/references/operations.md`) now recommends `bullswarm workflow
66
+ runs result <shortId> --json --summary`. Read the full envelope with
67
+ `--json` alone when the run is failed or partial, or before judging
68
+ evidence.
69
+
70
+ - Measured numbers, each with its command. On the real 0.27.1 build run
71
+ `ze5xz2` (files under `.diet-inputs/`): `workflow runs result ze5xz2
72
+ --json` is 60,709 bytes on disk (`wc -c .diet-inputs/real-result-ze5xz2.json`;
73
+ the same file is `tests/fixtures/real-result-ze5xz2.json`). The 0.28.0
74
+ goal recorded that envelope's `requirements` as 39,231 bytes and `goal`
75
+ as 11,009 bytes. Re-measuring the same file: `Buffer.byteLength(goal)` =
76
+ 10,996 (the summary's `goalBytes`) and `JSON.stringify(requirements)` =
77
+ 39,288; compact `JSON.stringify` of the parsed envelope is 57,141.
78
+ `summarizeV2Result` of that fixture is 3,786 bytes —
79
+ `tests/workflow-result-summary.test.js` prints `result-summary size:
80
+ full=57141 summary=3786`. The 0.28.0 goal recorded the integrator's
81
+ inputs as 60,790 bytes; `wc -c` of the seven dependency out-files under
82
+ `.diet-inputs/` sums to 46,022 (out-surface 18,659, out-routing-cleanup
83
+ 10,202, out-state-bugs 7,052, out-docs 6,831, out-dead-kernel 1,377,
84
+ out-verify-gate 974, out-dead-code 927) and the integrator task file is
85
+ 14,768 (`wc -c .diet-inputs/task-integrate-attempt-1.md`), which
86
+ together are 60,790.
87
+
88
+ - Fixture measurement, the same goal run twice under a temporary home
89
+ (`tests/workflow-context-diet-measurement.test.js`, which prints both
90
+ lines below). Three writers each padded to a few KB feed one integrator
91
+ directly, then the same three feed a `kind: "digest"` that feeds the
92
+ integrator: the integrator's `bytes.dependencyInputs` falls from
93
+ **12,477 bytes to 63 bytes** (`context diet: integrator dependencyInputs
94
+ without digest=12477 with digest=63`). The 63 is a floor, not a
95
+ condensation ratio — the deterministic fixture worker answers with a
96
+ fixed stub instead of really condensing. The ceiling is the byte target
97
+ the kernel writes into that digest's own task, **8,192 bytes** for this
98
+ input, and the test asserts the saving holds at that ceiling too
99
+ (8,192 < 12,477). The same run's envelopes print as `context diet: run B
100
+ envelope full=<n> summary=<n>` (the full envelope embeds the temporary
101
+ home's absolute paths, so its size varies a few bytes between runs; the
102
+ summary carries one `next.runDir` string plus basenames, so it does not);
103
+ the deterministic envelope comparison to quote is the real-run fixture
104
+ above (57,141 full vs the measured summary).
105
+
106
+ - `workflow capabilities` now reports the closed kind list at
107
+ `engines.autonomousV2.actionKinds`, cloned from the validator's
108
+ `KIND_DEFAULTS` rather than hand-listed, so `digest` and every future
109
+ kind are discoverable by a probing agent
110
+ (`src/workflow/cli.js`). The planning contract's
111
+ `program.actionFields.kind` description is derived from the same table
112
+ (`src/workflow/v2-planner.js`).
113
+
114
+ - `TIER_LANES` (`src/lib/strategy.js`) excludes kernel-owned kinds from the
115
+ effort-tier count, so the map stays `{ high: analyze, medium: build,
116
+ low: chore }`. A digest is a mechanism the kernel writes, not a nature of
117
+ work that should define which lane a tier routes to; counting it would
118
+ have flipped low from `chore` to `analyze` on the strength of an action
119
+ no planner has to reason about. No shipped connector's routing changes
120
+ either way — all six declare all three lanes.
121
+
122
+ - The three KaiHK-backed OpenCode pools can now run gpt-5.6-luna at a
123
+ chosen reasoning level. `connectors/opencode2.json` declares
124
+ `reasoning: { flag: "--variant", levels: [low, medium, high, xhigh,
125
+ max], defaults: { high: high, medium: medium, low: low } }` — the same
126
+ five levels as `connectors/command-code.json`, which fronts the same
127
+ backend — so rungs for these pools stop printing `— (unsupported)`.
128
+ opencode only forwards a `--variant` its config declares for that
129
+ model, so the flag alone would be silently dropped;
130
+ `expandOpenCodeKaihkConnectors` (`src/lib/opencode-kaihk.js`) therefore
131
+ sets `env.OPENCODE_CONFIG_CONTENT` on the base pool and on every clone
132
+ to the variants for that pool's OWN provider id, via the new pure
133
+ helper `kaihkVariantsConfig(providerId, model = KAIHK_OPENCODE_MODEL)`:
134
+ `{"provider":{"kaihk-2":{"models":{"gpt-5.6-luna":{"variants":{"low":{"reasoningEffort":"low"},"medium":{"reasoningEffort":"medium"},"high":{"reasoningEffort":"high"},"xhigh":{"reasoningEffort":"xhigh"},"max":{"reasoningEffort":"max"}}}}}}}`.
135
+ opencode merges that JSON string over the config file, so the API key
136
+ and everything else in `~/.config/opencode/opencode.json` stays in
137
+ force. An `OPENCODE_CONFIG_CONTENT` the operator set by hand in the
138
+ installed connector is never overwritten, on the base pool or on the
139
+ clones. A medium/max dispatch on `opencode2:kaihk-2` composes
140
+ `opencode run --auto --model kaihk-2/gpt-5.6-luna <taskFile> --variant
141
+ max --format json`; a level already pinned in the template is replaced,
142
+ not duplicated. Per pool:
143
+ `bullswarm strategy set-rung opencode2 medium --model kaihk/gpt-5.6-luna --reasoning max`,
144
+ `bullswarm strategy set-rung opencode2:kaihk-2 medium --model kaihk-2/gpt-5.6-luna --reasoning max`,
145
+ `bullswarm strategy set-rung opencode2:kaihk-3 medium --model kaihk-3/gpt-5.6-luna --reasoning max`.
146
+ Existing installations pick the block up through
147
+ `upgradeConnectorMetadata` (`src/setup.js`), which backfills a missing
148
+ `reasoning` block and leaves a customised one alone. Non-KaiHK opencode
149
+ installations get no injected variants, so `--variant` is a no-op there
150
+ rather than an error — recorded in the connector's
151
+ `$comment-reasoning`.
152
+
153
+ - Tests: 713 -> 738, 0 failures
154
+ (`env -u CLAUDE_CONFIG_DIR -u FORCE_COLOR -u NO_COLOR npm test`). Four
155
+ new files carry the new behaviour: `tests/workflow-bytes.test.js`,
156
+ `tests/workflow-digest.test.js`,
157
+ `tests/workflow-result-summary.test.js` (with the
158
+ `tests/fixtures/real-result-ze5xz2.json` envelope it measures) and
159
+ `tests/workflow-context-diet-measurement.test.js`.
160
+
161
+ ## 0.27.1 — audit cleanup
162
+
163
+ - Deleted the remaining dead symbols the 2026-09-09 audit listed as Tier A:
164
+ `recordAgentAction` and `classifyAgentProgress`, `aggregateUsage`,
165
+ `parseEvidenceOutput`, `fiveHourTier`, `REASONING_DEFAULT_TIERS`, the twelve
166
+ alias re-exports around `runAutonomousV2` /
167
+ `assertV2ResumeCompatible` / `validateEvidenceEnvelope`, and
168
+ `integrationBlock`. `'workflow-v1'` is no longer an assignment source.
169
+ `currentUsedPct` no longer reads a `weeklyUsedPct` field no producer writes,
170
+ and the identical `--json` ternary in `strategy-cli` collapsed to one
171
+ `JSON.stringify`. `fixtures/openrouter/` stays; a new
172
+ `tests/refresh-openrouter-benchmarks.test.js` runs the refresh script against
173
+ those fixtures instead of deleting them.
174
+
175
+ - `state.json` is no longer last-writer-wins (D5). EVERY state writer goes
176
+ through one locked read-modify-write — take `state.lock`, reload FRESH,
177
+ mutate, atomically replace the file, release. That is `run`, `pools`,
178
+ `health` and the fixture migration plus the twenty-two remaining
179
+ load-mutate-save sites: fifteen in `strategy-cli` (set-rung, set-model,
180
+ reset-tier, set/reset-reasoning, configure, apply, auto off, assign,
181
+ clear-assignment, exclude/include-model, set-subscription and the persisted
182
+ refresh report), three in the strategy TUI, and four in `setup`
183
+ (`setup --yes`, both writing steps of the wizard, and the reasoning step).
184
+ `grep -n 'saveState(' src/` now finds only `src/lib/state.js`, which defines
185
+ it and calls it once, inside `updateState`. Waiters retry for 10 s and a lock
186
+ older than 30 s is taken over so a killed process cannot bench the file
187
+ forever. `run --dry-run` no longer refreshes strategy or hits the network
188
+ (D3). The fixture migration no longer force-disables a pool the operator
189
+ explicitly enabled (D1). Disabled pools are no longer polled (D6). When every
190
+ lane-capable pool was dropped by an empty tier allow-list, the routing reason
191
+ now says so instead of blaming missing capabilities (D7). `pools`, `health`
192
+ and `run --dry-run` leave `state.json` byte-for-byte alone when they have
193
+ nothing to change. `src/workflow/fsjson.js` — the re-export shim that carried
194
+ the workflow importers through the move of the atomic writer into
195
+ `src/lib/fsjson.js` — is deleted; both layers import `src/lib/fsjson.js`
196
+ directly.
197
+
198
+ - The content gate no longer treats an error-shaped JSON object as an answer,
199
+ and long outputs are scanned at both the head and the tail for failure
200
+ patterns.
201
+
202
+ - An unrecognized `--flag` is a usage error on every command: Bullswarm prints
203
+ `unknown flag --name` plus that command's synopsis and exits 2, before
204
+ self-initializing, routing, or spawning anything. `--lane` is required on
205
+ `run` (omitting it, or passing anything else, exits 2). `--limit` on
206
+ `workflow runs` must be a positive integer. `health --json` now selects the
207
+ machine-readable report; the default is a human summary of the same facts.
208
+ Help for `run --no-caller`, the always-JSON verbs, and the plan-contract /
209
+ plan-validate worker flags matches what the parsers actually accept.
210
+
211
+ - A dead kernel is visible: `watch`, `runs show`, `runs result --json`, and the
212
+ TUI surface the last 20 lines of `stderr.log` instead of a silent stall.
213
+
214
+ - Four small refactors from the audit's C1–C4 list: `TIER_LANES` is derived
215
+ from the 0.26 kind/effort tables so the strategy preview cannot disagree with
216
+ the validator; `clearTierAssignment` is the one writer of a cleared tier pin;
217
+ `finiteOrNull` is the one numeric coercion (blank/null prices and scores stay
218
+ unmeasured instead of becoming 0); quota signatures live in one table.
219
+
220
+ - The OpenRouter datapack no longer pretends to have a bundled last-resort
221
+ file. Loaders try `~/.bullswarm/cache/` then the rolling GitHub Release; a
222
+ cache miss with no network yields an empty catalog. Epoch still ships
223
+ `data/epoch-benchmarks.json`.
224
+
225
+ - Documentation made true: README no longer lists `runs cleanup`; bare
226
+ `bullswarm` is a TTY wizard and non-TTY callers self-initialize; the
227
+ OpenRouter/Epoch datapack fallback is described as it actually works; the
228
+ duplicated planning-targets paragraph appears once; `--name` is an exact
229
+ goal/name filter; `--classic` is V2-only and legacy watch exits 2;
230
+ `workflow-v1` is gone from the skill; `workflow goal` is no longer described
231
+ as an LLM-at-every-checkpoint loop; `delegate` is marked historical in the
232
+ 2026-09-06 design note; GOAL.md and the ten dated `docs/` files that needed
233
+ a banner carry one.
234
+
235
+ - Tests: 661 -> 713, 0 failures. Eight new files carry the new behaviour:
236
+ `unknown-flags` (11 — one bogus flag per parser, the two typed inputs, and a
237
+ drift guard that re-extracts every documented command form), `cli-run` (6 —
238
+ the D3/D1/D7 CLI contracts), `config` (5 — D6), `num` (4 — strict
239
+ `finiteOrNull`), `workflow-dead-kernel` (4), `state-race` (1 — an operator
240
+ write during a live run), `state-lock-sites` (3 — two real `strategy`
241
+ processes racing one home, four issued at once, and a `configure` document
242
+ that throws mid-mutation, writing nothing and freeing the lock) and
243
+ `refresh-openrouter-benchmarks` (1 — the refresh script against
244
+ `fixtures/openrouter/`). Existing files lost the
245
+ cases that only covered deleted symbols (`agent-events` 19 -> 13, and the
246
+ `fiveHourTier` case in `route`, which kept its
247
+ `FIVE_HOUR_NEAR_LIMIT_PCT === 75` assertion) and gained coverage for the
248
+ locked state writers, the fixture-migration rule, and the content gate.
249
+
3
250
  ## 0.27.0 — one workflow engine
4
251
 
5
252
  - Fixed: a worker that floods its stdout could kill the kernel. Every chunk of a
package/GOAL.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # bullswarm — Goal Statement
2
2
 
3
- **Status:** PROTOTYPE · **Owner:** cowcow02 · **Created:** 2026-08-21
3
+ > Historical (2026-08-21): accurate when written; see CHANGELOG for what changed since.
4
+
5
+ **Status:** HISTORICAL PROTOTYPE CHARTER · **Owner:** cowcow02 · **Created:** 2026-08-21
4
6
 
5
7
  ## One sentence
6
8
 
package/README.md CHANGED
@@ -88,7 +88,7 @@ re-delegating and creating recursive swarms.
88
88
  ## Quick start
89
89
 
90
90
  ```bash
91
- bullswarm # first run: interactive setup wizard
91
+ bullswarm # first run: interactive setup wizard on a TTY; non-TTY callers self-initialize
92
92
  bullswarm setup # interactive provider/model configuration
93
93
  bullswarm setup --wizard # broader worktree + integration questionnaire
94
94
  bullswarm pools # meter state, pace position, quarantine status
@@ -98,7 +98,7 @@ bullswarm run --lane analyze --add-dir ~/some-repo --prompt "Inspect the parser"
98
98
  bullswarm workflow plan contract "Fix the failing tests and verify the change" --cwd ~/some-repo --json # you are the planner
99
99
  bullswarm workflow goal "Fix the failing tests and verify the change" --cwd ~/some-repo --program plan.json
100
100
  bullswarm workflow goal "Fix the failing tests and verify the change" --cwd ~/some-repo --orchestrator auto # dispatch a planner agent
101
- bullswarm health # re-judge saved outputs; catch gate failures
101
+ bullswarm health --json # re-judge saved outputs; catch gate failures (omit --json for a human summary)
102
102
  ```
103
103
 
104
104
  ## Verbs
@@ -113,7 +113,7 @@ bullswarm health # re-judge saved outputs; catch gate failures
113
113
  | `strategy` | Interactive provider/model control center with live high/medium/low route previews and an agent-facing JSON API |
114
114
  | `doctor` | Machine-readable readiness report; self-heals on first call |
115
115
  | `workflow` | Plan, execute, observe, and operate one autonomous workflow engine and its live instances. |
116
- | `runs` | Short alias for `workflow runs`, including list, show, result, delete, and cleanup operations. |
116
+ | `runs` | Short alias for `workflow runs`, including list, show, result, and delete operations. |
117
117
  | `version` / `--version` | Print the installed Bullswarm version. |
118
118
  | `release` | Run the guarded local version-bump, commit, and tag workflow used before CI publishes to npm. |
119
119
 
@@ -180,6 +180,25 @@ per pool and tier, so the tier moves off whichever model held it while that
180
180
  model keeps its other tiers. Nothing about `state.json` changed shape: rungs are
181
181
  a view over `strategy.modelTiers` and `strategy.reasoning`.
182
182
 
183
+ The KaiHK-backed OpenCode pools (`opencode2`, `opencode2:kaihk-2`,
184
+ `opencode2:kaihk-3`) express reasoning as opencode's `--variant <level>`, at the
185
+ same five levels as `command-code`. opencode only forwards a variant its own
186
+ config declares for that model, so bullswarm injects them: each pool is spawned
187
+ with `OPENCODE_CONFIG_CONTENT` declaring `low`/`medium`/`high`/`xhigh`/`max` as
188
+ `reasoningEffort` variants of `<providerId>/gpt-5.6-luna`, merged over your
189
+ `~/.config/opencode/opencode.json` (your API keys stay in force). Without that
190
+ injection opencode accepts `--variant` and silently drops it. Set a rung per
191
+ pool, using that pool's own provider prefix:
192
+
193
+ ```bash
194
+ bullswarm strategy set-rung opencode2:kaihk-2 medium \
195
+ --model kaihk-2/gpt-5.6-luna --reasoning max
196
+ ```
197
+
198
+ If you set `OPENCODE_CONFIG_CONTENT` yourself in
199
+ `~/.bullswarm/connectors/opencode2.json`, bullswarm leaves it alone and injects
200
+ nothing — you own the variants from then on.
201
+
183
202
  The benchmark evidence comes from Epoch AI's benchmarking hub, used under
184
203
  CC BY 4.0: Epoch AI, 'AI Benchmarking Hub'. Published online at epoch.ai.
185
204
  Retrieved from <https://epoch.ai/benchmarks>. `blended` is the mean of the
@@ -203,9 +222,12 @@ Installed CLIs download only those public files and never need or receive an
203
222
  OpenRouter key.
204
223
  The sources are OpenRouter's [benchmarks API](https://openrouter.ai/docs/api/api-reference/benchmarks/list-benchmarks)
205
224
  and [models API](https://openrouter.ai/docs/api/api-reference/models/list-all-models-and-their-properties).
206
- The CLI caches the datapack under `~/.bullswarm/cache/`; network failure falls
207
- back to a stale or bundled datapack, then connector metadata, without blocking
208
- setup.
225
+ The CLI caches each datapack under `~/.bullswarm/cache/`. OpenRouter is
226
+ cache-or-network only: a fresh cache is used as-is, otherwise the rolling
227
+ release is fetched, and a cache miss with no network yields an empty catalog
228
+ plus connector metadata — there is no bundled `data/openrouter-benchmarks.json`.
229
+ Epoch keeps `data/epoch-benchmarks.json` as a bundled last-resort, so a missing
230
+ network never blocks setup when that file exists.
209
231
 
210
232
  The TUI lists every detected provider/account separately so its toggle matches
211
233
  its own quota meter. Enter drills into that provider's detected models. In the
@@ -343,7 +365,9 @@ caller asks for it by name.
343
365
  planning targets. They encourage the Workflow Planner to consolidate optional
344
366
  work, but the kernel never stops or rejects essential work merely because a
345
367
  target was reached. `--concurrency` still bounds simultaneous dispatches so
346
- the scheduler can batch a wider useful program safely.
368
+ the scheduler can batch a wider useful program safely. There is no default
369
+ wall-clock timeout: fresh semantic/transport heartbeats allow a useful worker
370
+ to continue, while silence is inspected rather than blindly killed.
347
371
 
348
372
  The caller authors a complete program, or explicitly asks for a dispatched
349
373
  planner. The kernel validates the graph, executes it, and returns every action
@@ -385,12 +409,25 @@ names that nature once and derives them:
385
409
  | --- | --- | --- |
386
410
  | `mechanical` | chore | low |
387
411
  | `io-read` | analyze | low |
412
+ | `digest` | analyze | low |
388
413
  | `check` | analyze | medium |
389
414
  | `implement` | build | medium |
390
415
  | `integration` | build | high |
391
416
  | `architecture` | analyze | high |
392
417
  | `adversarial-acceptance` | analyze | high |
393
418
 
419
+ `digest` is the one kind whose instructions the kernel supplies in full — your
420
+ prompt for it is focus guidance only. It condenses the
421
+ outputs of the actions it depends on — quoting each source's delivered items,
422
+ validation numbers, commands, unfinished work, and requests verbatim, one
423
+ section per source, with no verdicts of its own — so an expensive consumer
424
+ reads one artifact instead of many raw output files, and the digest entry in
425
+ that consumer's dependency artifacts still names every digested source for
426
+ drill-down. Use one when three or more writers feed a single integrator, or
427
+ when a consumer's dependency outputs would exceed roughly 20 KB. A digest must
428
+ depend on at least one action, owns no files, needs no `affects`, and no
429
+ evidence action may depend on one: evidence reads the real artifacts.
430
+
394
431
  Resolution is per field: an explicit `lane` or `effort` on the action wins,
395
432
  then the kind table, then an optional program-level `defaults` object — which
396
433
  may set only `effort` and `reasoning`, because lane follows the individual
@@ -434,6 +471,7 @@ bullswarm workflow runs show <shortId>
434
471
  bullswarm workflow watch <shortId> # V2: attach, then one line per notable event
435
472
  bullswarm workflow watch <shortId> --next # print the next notable event and exit
436
473
  # relaunch with the --after/--since it prints
474
+ bullswarm workflow runs result <shortId> --json --summary # compact status-loop envelope once terminal
437
475
  bullswarm workflow # unified human workflow home
438
476
  bullswarm workflow tui <shortId> # jump directly to one run timeline
439
477
  bullswarm workflow tui --json <shortId>
@@ -502,12 +540,6 @@ per provider — the first as the primary `opencode2` pool, each additional one
502
540
  as its own `opencode2:<id>` pool — which is what the `--worker-model
503
541
  kaihk/gpt-5.6-luna` example above locks onto.
504
542
 
505
- `--max-agents`, `--max-actions`, and `--max-expansion-rounds` are soft V2
506
- planning targets: they guide the planner toward a small program but do not
507
- hard-stop useful work. `--concurrency` is the actual bound on simultaneous
508
- dependency-ready dispatches. There is no default wall-clock timeout: fresh
509
- semantic/transport heartbeats allow a useful worker to continue, while silence
510
- is inspected rather than blindly killed.
511
543
  New goal runs use the shared workspace regardless of the older setup
512
544
  worktree-isolation preference. Add `--isolation` to `workflow goal` when you
513
545
  explicitly want per-worker worktrees and strict ownership before integration.
@@ -569,13 +601,14 @@ no `0/1/i/l/o`). The full `wf-...` runId stays the durable handle.
569
601
  bullswarm workflow runs # ongoing only (default)
570
602
  bullswarm workflow runs --all # ongoing + historical
571
603
  bullswarm workflow runs --historical # only historical
572
- bullswarm workflow runs --name audit-code # filter by workflow
604
+ bullswarm workflow runs --name audit-code # filter by exact goal/name
573
605
  bullswarm workflow runs --all --since 7d # initiated in the last 7 days
574
606
  bullswarm workflow runs --historical --since yesterday --until today
575
607
  bullswarm workflow runs --all --from 2026-08-20 --to 2026-08-27
576
608
  bullswarm workflow runs --limit 20 # cap the result count
577
609
  bullswarm workflow runs show <shortId> # state + report + summary
578
- bullswarm workflow runs result <shortId> --json # stable result for the calling agent
610
+ bullswarm workflow runs result <shortId> --json --summary # compact status-loop envelope
611
+ bullswarm workflow runs result <shortId> --json # full envelope (failed/partial, or before judging evidence)
579
612
  bullswarm runs show <shortId> # top-level shorthand
580
613
  bullswarm workflow runs delete <shortId> --yes # remove the run dir
581
614
  ```
@@ -592,8 +625,10 @@ Values accept ISO timestamps, local `YYYY-MM-DD` dates, `today`, `yesterday`,
592
625
  `tomorrow`, `now`, or relative durations such as `30m`, `24h`, `7d`, and `2w`.
593
626
 
594
627
  After a workflow reaches a terminal state, agents should consume
595
- `workflow runs result <id> --json` instead of probing `state.json`, task files,
596
- or provider-specific output. Autonomous V2 returns the versioned
628
+ `workflow runs result <id> --json --summary` for the status loop instead of
629
+ probing `state.json`, task files, or provider-specific output. Read the full
630
+ envelope with `--json` alone when the run is failed or partial, or before
631
+ judging evidence. Autonomous V2's full document is the versioned
597
632
  `bullswarm.workflow.result.v2` envelope with kernel-computed status, fresh
598
633
  requirement evidence, per-action status/failure/output files, explicit gaps,
599
634
  usage, and verification qualification. New programs include `executionMode:
@@ -609,6 +644,71 @@ otherwise the command returns after printing this handoff.
609
644
  Time filters preserve the existing scope, so use `--all` or `--historical` when
610
645
  auditing completed runs.
611
646
 
647
+ ### Context diet
648
+
649
+ The kernel now measures — and can shrink — what it puts in front of a model.
650
+ These are UTF-8 byte counts, never tokens.
651
+
652
+ **Status loop.** Poll with `--summary`; it implies JSON (with or without
653
+ `--json`) and prints `schemaVersion: "bullswarm.workflow.result-summary.v1"`:
654
+ `runId`, `shortId`, `status`, `verified`, `executionMode`, `reason`,
655
+ `finishedAt`, the goal's first line (120 characters) plus `goalBytes`, each
656
+ requirement as `{ id, status, mandatory, evidenceCount, why }`, each action as
657
+ `{ id, kind, lane, effort, status, pool, model, reasoning, wallSec, outFile,
658
+ bytes }`, `concerns: { count, first }`, `usage`, and `next: { full, runDir, outputs }` — every output name is a basename inside `next.runDir`.
659
+ The full `bullswarm.workflow.result.v2` envelope is unchanged and remains the
660
+ default. Read it (`--json` alone) on a failed or partial run, or before judging
661
+ evidence. A terminal `workflow watch` prints the same compact command as
662
+ `next:`.
663
+
664
+ ```bash
665
+ bullswarm workflow runs result <shortId> --json --summary
666
+ bullswarm workflow runs result <shortId> --json
667
+ ```
668
+
669
+ `workflow runs result --help` states `Usage: bullswarm workflow runs result
670
+ <shortId|runId> [--json] [--summary]`; `--summary` is "print the compact JSON
671
+ status-loop envelope; implies --json".
672
+
673
+ **Bytes.** Every attempt records `bytes: { taskFile, authorPrompt, kernel,
674
+ dependencyInputs, output }` — the task file the kernel wrote, the action's own
675
+ prompt as authored, the remainder after subtracting that prompt and any
676
+ embedded requirement text, the sum of the dependency output files the task
677
+ points at (0 when there are none), and the durable out file on completion.
678
+ The result envelope copies the last attempt's `bytes` onto `actions[]` and
679
+ totals `usage.bytes: { taskFiles, dependencyInputs, outputs }`.
680
+ `workflow runs show` appends `in <taskFile>/<dependencyInputs> out <output>`
681
+ per attempt (blank when unrecorded). Missing values are null, never guessed.
682
+
683
+ **Digest.** `kind: "digest"` is analyze/low. It is an extractive condensation
684
+ of its dependencies' outputs — quoted delivered items, validation numbers,
685
+ commands, unfinished work, and integrator requests; no verdicts of its own.
686
+ The kernel writes the whole task; the author's prompt is focus guidance only.
687
+ Use one when three or more writers feed a single integrator, or when a
688
+ consumer's dependency outputs would exceed roughly 20 KB. A digest must
689
+ depend on at least one action, owns no files, has empty `evidenceFor`, and
690
+ needs no `affects`. Evidence must not depend on a digest: evidence reads the
691
+ real artifacts. Consumers that depend on a digest receive that digest plus a
692
+ `digestOf` array of `{ actionId, outputFile }` so they can drill down; those
693
+ paths are pointers, not extra `dependencyInputs`. (The kind table above
694
+ derives lane and effort.)
695
+
696
+ ```json
697
+ {
698
+ "schemaVersion": "bullswarm.workflow.program.v2",
699
+ "actions": [
700
+ { "id": "write-a", "kind": "implement", "dependsOn": [], "ownedFiles": ["a.ts"], "affects": ["requirement-1"], "evidenceFor": [], "purpose": "Write slice A", "prompt": "Implement A and report the checks you ran." },
701
+ { "id": "write-b", "kind": "implement", "dependsOn": [], "ownedFiles": ["b.ts"], "affects": ["requirement-2"], "evidenceFor": [], "purpose": "Write slice B", "prompt": "Implement B and report the checks you ran." },
702
+ { "id": "write-c", "kind": "implement", "dependsOn": [], "ownedFiles": ["c.ts"], "affects": ["requirement-3"], "evidenceFor": [], "purpose": "Write slice C", "prompt": "Implement C and report the checks you ran." },
703
+ { "id": "condense", "kind": "digest", "dependsOn": ["write-a", "write-b", "write-c"], "ownedFiles": [], "affects": [], "evidenceFor": [], "purpose": "Condense the writer outputs", "prompt": "Keep every acceptance number and every shared-file request." },
704
+ { "id": "integrate", "kind": "integration", "dependsOn": ["condense"], "ownedFiles": [], "affects": ["requirement-1", "requirement-2", "requirement-3"], "evidenceFor": [], "purpose": "Integrate and run the gates", "prompt": "Apply every request the digest carries and run the repository gates." }
705
+ ]
706
+ }
707
+ ```
708
+
709
+ An evidence action for those requirements depends on `write-a`, `write-b`, and
710
+ `write-c` — never on `condense`.
711
+
612
712
  ### Live workflow dashboard
613
713
 
614
714
  For ordinary observation, use the non-interactive watcher. For V2 runs it
@@ -616,8 +716,9 @@ prints one attach line, then one line per notable event as it happens
616
716
  (action finished/failed/blocked/cancelled, evidence, stage completion,
617
717
  planner turn, stall/recovery, cancellation, and the existing pause and
618
718
  terminal `outcome:` / `next:` lines) and stays silent while work is merely
619
- in progress. Agent starts, mechanical retries, and steering delivery print
620
- only with `--verbose`. A usage-limit failure (`failureKind: 'quota'`) always
719
+ in progress. A terminal watch's `next:` line is
720
+ `bullswarm workflow runs result <shortId> --json --summary`. Agent starts,
721
+ mechanical retries, and steering delivery print only with `--verbose`. A usage-limit failure (`failureKind: 'quota'`) always
621
722
  prints, verbose or not: `⚠ <actionId> usage limit on <pool> · paused until
622
723
  <deadline> · retrying on another pool`, followed once the mechanical retry
623
724
  lands on another pool by `↺ <actionId> now on <pool> · <model>`. The
@@ -34,6 +34,12 @@
34
34
  "capabilities": ["strong-analysis", "code-reading", "file-editing", "workflow-planning"],
35
35
  "modelDiscovery": { "cmd": ["opencode", "models"], "parse": "lines", "includePattern": "^[^\\s]+/[^\\s]+$", "timeoutMs": 20000, "maxModels": 250 },
36
36
  "modelSelection": { "flag": "--model", "mode": "replace-or-append" },
37
+ "$comment-reasoning": "verified 2026-09-09 against opencode 1.18.25: `opencode run --help` documents `--variant model variant (provider-specific reasoning effort, e.g., high, max, minimal)`. opencode only forwards a variant its CONFIG declares for that model, and the owner's opencode.json declares none, so on a bare install `--variant` is accepted and silently dropped. The level reaches the KaiHK API as `reasoning_effort` ONLY because expandOpenCodeKaihkConnectors (src/lib/opencode-kaihk.js) injects the five variants for <providerId>/gpt-5.6-luna through env.OPENCODE_CONFIG_CONTENT on every discovered KaiHK pool. Probed with that variable set: `--variant bogus` (reasoningEffort bogus-level) failed at the API with `level \"bogus-level\" not supported, valid levels: low, medium, high, xhigh, max`, and `--variant max` succeeded; probed again with the flag AFTER the positional task text, exactly where bullswarm appends it (`opencode run --auto --model <id>/gpt-5.6-luna <task> --variant <level> --format json`), with the same API rejection for the bogus level, so the position is proven too. Same five levels as connectors/command-code.json, which fronts the same backend. UNVERIFIED for non-KaiHK opencode providers: they get no injected variants, so the flag is a no-op there rather than an error.",
38
+ "reasoning": {
39
+ "flag": "--variant",
40
+ "levels": ["low", "medium", "high", "xhigh", "max"],
41
+ "defaults": { "high": "high", "medium": "medium", "low": "low" }
42
+ },
37
43
  "modelProfiles": [
38
44
  { "match": "(?:^|/)claude-fable-", "tier": "high", "qualityRank": 6, "autoRecommend": false },
39
45
  { "match": "gpt-5\\.6-sol$", "tier": "high", "qualityRank": 6, "autoRecommend": true },
package/data/README.md CHANGED
@@ -7,9 +7,14 @@ secrets and are never written here or shipped with the CLI.
7
7
  Both datapacks are published as replaceable assets on the
8
8
  `benchmark-data-latest` GitHub Release by
9
9
  `.github/workflows/refresh-benchmarks.yml`. Installed CLIs try
10
- `~/.bullswarm/cache/` first, then the copy bundled in this directory, then
11
- that release URL. A missing network never blocks setup when a bundled file
12
- exists.
10
+ `~/.bullswarm/cache/` first, then that release URL.
11
+
12
+ OpenRouter is cache-or-network only: there is no bundled
13
+ `data/openrouter-benchmarks.json`. A cache miss with no network yields an
14
+ empty catalog (and connector metadata), and never blocks setup.
15
+
16
+ Epoch keeps `data/epoch-benchmarks.json` as a bundled last-resort, so a
17
+ missing network never blocks setup when that file exists.
13
18
 
14
19
  ## `openrouter-benchmarks.json`
15
20