bullswarm 0.27.1 → 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.
- package/CHANGELOG.md +158 -0
- package/README.md +107 -5
- package/connectors/opencode2.json +6 -0
- package/data/openrouter-benchmarks.json +9933 -9854
- package/docs/studies/portal-token-diet.md +10 -0
- package/package.json +1 -1
- package/skill/SKILL.md +15 -3
- package/skill/references/operations.md +21 -1
- package/src/help.js +10 -4
- package/src/lib/cli-flags.js +1 -1
- package/src/lib/opencode-kaihk.js +46 -0
- package/src/lib/strategy.js +10 -1
- package/src/workflow/action-validator.js +32 -3
- package/src/workflow/cli.js +4 -1
- package/src/workflow/dashboard.js +2 -2
- package/src/workflow/runs-cli.js +31 -5
- package/src/workflow/v2-outcome.js +224 -3
- package/src/workflow/v2-planner.js +11 -1
- package/src/workflow/v2-runtime.js +130 -5
- package/src/workflow/v2-state.js +27 -2
- package/src/workflow/watch-cli.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,163 @@
|
|
|
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
|
+
|
|
3
161
|
## 0.27.1 — audit cleanup
|
|
4
162
|
|
|
5
163
|
- Deleted the remaining dead symbols the 2026-09-09 audit listed as Tier A:
|
package/README.md
CHANGED
|
@@ -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
|
|
@@ -390,12 +409,25 @@ names that nature once and derives them:
|
|
|
390
409
|
| --- | --- | --- |
|
|
391
410
|
| `mechanical` | chore | low |
|
|
392
411
|
| `io-read` | analyze | low |
|
|
412
|
+
| `digest` | analyze | low |
|
|
393
413
|
| `check` | analyze | medium |
|
|
394
414
|
| `implement` | build | medium |
|
|
395
415
|
| `integration` | build | high |
|
|
396
416
|
| `architecture` | analyze | high |
|
|
397
417
|
| `adversarial-acceptance` | analyze | high |
|
|
398
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
|
+
|
|
399
431
|
Resolution is per field: an explicit `lane` or `effort` on the action wins,
|
|
400
432
|
then the kind table, then an optional program-level `defaults` object — which
|
|
401
433
|
may set only `effort` and `reasoning`, because lane follows the individual
|
|
@@ -439,6 +471,7 @@ bullswarm workflow runs show <shortId>
|
|
|
439
471
|
bullswarm workflow watch <shortId> # V2: attach, then one line per notable event
|
|
440
472
|
bullswarm workflow watch <shortId> --next # print the next notable event and exit
|
|
441
473
|
# relaunch with the --after/--since it prints
|
|
474
|
+
bullswarm workflow runs result <shortId> --json --summary # compact status-loop envelope once terminal
|
|
442
475
|
bullswarm workflow # unified human workflow home
|
|
443
476
|
bullswarm workflow tui <shortId> # jump directly to one run timeline
|
|
444
477
|
bullswarm workflow tui --json <shortId>
|
|
@@ -574,7 +607,8 @@ bullswarm workflow runs --historical --since yesterday --until today
|
|
|
574
607
|
bullswarm workflow runs --all --from 2026-08-20 --to 2026-08-27
|
|
575
608
|
bullswarm workflow runs --limit 20 # cap the result count
|
|
576
609
|
bullswarm workflow runs show <shortId> # state + report + summary
|
|
577
|
-
bullswarm workflow runs result <shortId> --json #
|
|
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)
|
|
578
612
|
bullswarm runs show <shortId> # top-level shorthand
|
|
579
613
|
bullswarm workflow runs delete <shortId> --yes # remove the run dir
|
|
580
614
|
```
|
|
@@ -591,8 +625,10 @@ Values accept ISO timestamps, local `YYYY-MM-DD` dates, `today`, `yesterday`,
|
|
|
591
625
|
`tomorrow`, `now`, or relative durations such as `30m`, `24h`, `7d`, and `2w`.
|
|
592
626
|
|
|
593
627
|
After a workflow reaches a terminal state, agents should consume
|
|
594
|
-
`workflow runs result <id> --json`
|
|
595
|
-
or provider-specific output.
|
|
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
|
|
596
632
|
`bullswarm.workflow.result.v2` envelope with kernel-computed status, fresh
|
|
597
633
|
requirement evidence, per-action status/failure/output files, explicit gaps,
|
|
598
634
|
usage, and verification qualification. New programs include `executionMode:
|
|
@@ -608,6 +644,71 @@ otherwise the command returns after printing this handoff.
|
|
|
608
644
|
Time filters preserve the existing scope, so use `--all` or `--historical` when
|
|
609
645
|
auditing completed runs.
|
|
610
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
|
+
|
|
611
712
|
### Live workflow dashboard
|
|
612
713
|
|
|
613
714
|
For ordinary observation, use the non-interactive watcher. For V2 runs it
|
|
@@ -615,8 +716,9 @@ prints one attach line, then one line per notable event as it happens
|
|
|
615
716
|
(action finished/failed/blocked/cancelled, evidence, stage completion,
|
|
616
717
|
planner turn, stall/recovery, cancellation, and the existing pause and
|
|
617
718
|
terminal `outcome:` / `next:` lines) and stays silent while work is merely
|
|
618
|
-
in progress.
|
|
619
|
-
|
|
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
|
|
620
722
|
prints, verbose or not: `⚠ <actionId> usage limit on <pool> · paused until
|
|
621
723
|
<deadline> · retrying on another pool`, followed once the mechanical retry
|
|
622
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 },
|