bullswarm 0.10.8 → 0.11.1

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/AGENTS.md CHANGED
@@ -37,7 +37,7 @@ content. Published as `bullswarm` on npm.
37
37
  ## Development
38
38
 
39
39
  ```bash
40
- npm test # 261 tests, no network needed (meters read from cache)
40
+ npm test # full suite, no network needed (meters read from cache)
41
41
  node bin/bullswarm.js doctor --json # readiness report
42
42
  node bin/bullswarm.js workflow list # discover workflows
43
43
  node bin/bullswarm.js workflow runs # ongoing workflow instances
package/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # bullswarm changelog
2
2
 
3
+ ## 0.11.1 — reliable `--watch` handoff
4
+
5
+ - `workflow goal --watch` no longer races the detached child: the watcher now
6
+ waits up to 30 s for the run's `state.json` to appear before attaching, and
7
+ `runWorkflowWatch` accepts `waitForRunMs`. The 0.11.0 tag failed to publish
8
+ because this race made the release-gate test fail on the CI runner; 0.11.1
9
+ carries the full 0.11.0 change set below.
10
+
11
+ ## 0.11.0 — plan the whole graph, run it wide
12
+
13
+ Adopts the driving mechanics of Claude Code's dynamic workflow (documented in
14
+ `docs/claude-dynamic-workflow-mechanics.md`) into the autonomous loop.
15
+
16
+ - Ready-set scheduler: every planner action whose dependencies have succeeded
17
+ starts immediately, and a dependent action starts the moment its own inputs
18
+ finish rather than when the whole round finishes. The global
19
+ `settings.concurrency` limiter caps real parallelism. Verify-B now overlaps
20
+ fix-C exactly like a Claude `pipeline()` stage.
21
+ - Planning doctrine: the planner is told to propose the complete dependency
22
+ graph in one decision (per-item fix→verify chains plus one whole-system
23
+ verify), to declare file ownership per action and order same-file edits with
24
+ `dependsOn`, to write self-contained worker prompts, and what a planning round
25
+ trip costs. The goal orchestrator prompt no longer asks for "the smallest
26
+ useful set" of actions. `executionConstraints.concurrency` is exposed.
27
+ - `workflow goal` default `--concurrency` is 8 (was 3; max 16).
28
+ - The planner prompt's shared-working-tree caution now says what is actually
29
+ unsafe (whole-tree mutation, running the full suite while others edit) and
30
+ states that concurrent workers editing disjoint files is the expected mode;
31
+ the 0.10.9 orchestrator had cited the old wording as its reason not to fan
32
+ out ("Implementation is deliberately NOT fanned out … shared-target mutation
33
+ policy").
34
+ - `verify.review` is recovered when a planner puts instructions or a filesystem
35
+ path there: instructions move to `prompt`, the single dependency's artifact is
36
+ inferred, and any `review` that is not `outputs.<actionId>.outFile` is
37
+ rejected at validation (feeding the corrective turn) instead of failing a
38
+ dispatch after a full planning round trip.
39
+
40
+ ## 0.10.9 — planner self-correction and honest silence
41
+
42
+ - An invalid or non-JSON orchestrator decision no longer fails the run. The
43
+ runtime feeds the exact validator issues, the rejected proposal, and a
44
+ response excerpt back to the same orchestrator thread for bounded corrective
45
+ turns (`settings.maxPlannerCorrections`, default 2, emits
46
+ `decision.correction_requested`), then benches that pool as orchestrator for
47
+ the rest of the run and escalates to one other eligible pool
48
+ (`decision.orchestrator_escalated`), and only then settles on a qualified
49
+ `completed_with_concerns`/`blocked` outcome. The decide action's ledger status
50
+ now agrees with that outcome (`failed_retryable` while correcting,
51
+ `failed_terminal` on exhaustion) instead of reporting `succeeded`.
52
+ - The planner prompt now shows complete `run`, `fanout` (`items` +
53
+ `stepTemplate.prompt` with `{{item}}`), and `verify` (`review`) skeletons, so
54
+ the first proposal can match what `decision.js` validates.
55
+ - `workflow watch` separates two silences: `quiet` counts durable workflow
56
+ events, and a new `agent output … ago` figure (JSONL `transportQuietForSec`)
57
+ counts raw output from live agents, so a thinking agent and a dead one look
58
+ different on the same heartbeat line.
59
+ - Removed the dead thin-leaf help renderer left over from the help unification
60
+ and stopped hard-coding the test count in AGENTS.md.
61
+
3
62
  ## 0.10.8 — quieter monitoring and resilient orchestration
4
63
 
5
64
  - Made `workflow watch` aggregate low-level activity into compact interval
@@ -0,0 +1,231 @@
1
+ # How Claude Code drives a dynamic workflow — mechanics, and what bullswarm adopts
2
+
3
+ Written 2026-08-29 from inside a Claude Code session that has the `Workflow`
4
+ tool ("ultracode") loaded, by the model that authors those workflows.
5
+
6
+ Every statement is tagged:
7
+
8
+ - **[SPEC]** — quoted or closely paraphrased from the `Workflow` tool contract
9
+ and the `workflow-authoring` reference as loaded into the session on
10
+ 2026-08-29. This is the documented behaviour the orchestrating model is
11
+ told to rely on.
12
+ - **[OBSERVED]** — measured in this repository's experiments
13
+ (`docs/experiments/2026-08-29-ultracode-vs-bullswarm.md`).
14
+ - **[INFERRED]** — my reading of how the harness must behave to satisfy the
15
+ spec. Not confirmed by source; treat as a hypothesis.
16
+
17
+ ## 0. The one-paragraph shape
18
+
19
+ Claude's orchestrator (the main-loop model) does **not** decide step-by-step at
20
+ runtime. It writes a *program* — a small JavaScript script — that declares the
21
+ phases and calls `agent(prompt, opts)` once per worker, wired together with
22
+ `pipeline()` / `parallel()` / plain loops. The harness executes that program
23
+ deterministically, spawning subagents concurrently up to a cap, and the model
24
+ reads the aggregate return value when the program finishes. Planning is
25
+ front-loaded into one authoring act; parallelism is explicit in the code; every
26
+ worker receives an individually written, self-contained prompt and (usually) a
27
+ JSON schema its answer must satisfy. Re-planning happens either as ordinary
28
+ code (loops, conditionals) inside the script, or between scripts when the model
29
+ reads a result and authors the next one. **[SPEC]**
30
+
31
+ bullswarm's `workflow goal`, by contrast, runs an LLM *at every checkpoint*: a
32
+ `decide` step proposes JSON actions, the runtime validates and executes them,
33
+ then asks the LLM again. **[SPEC — bullswarm source]** The rest of this document
34
+ is about which of Claude's mechanics that loop can adopt without giving up its
35
+ one advantage — the user supplies a goal, never a graph.
36
+
37
+ ## 1. Mechanics, one at a time
38
+
39
+ ### 1.1 The control plane is code, authored once **[SPEC]**
40
+
41
+ - A script must begin with a pure-literal `export const meta = { name,
42
+ description, phases: [{ title, detail }] }`; the body uses `phase()`,
43
+ `agent()`, `pipeline()`, `parallel()`, `log()`, `args`, `budget`, and
44
+ `workflow()` (one level of nesting).
45
+ - The tool is explicitly for "multi-step orchestration where control flow
46
+ should be deterministic (loops, conditionals, fan-out) rather than
47
+ model-driven."
48
+ - The recommended pattern is **hybrid**: "scout inline first (list the files,
49
+ find the channels, scope the diff) to discover the work-list, then call
50
+ Workflow to pipeline over it. You don't need to know the shape before the
51
+ *task* — only before the *orchestration step*."
52
+ - Multi-phase work is several workflows in sequence: "run several in sequence —
53
+ read each result before deciding the next phase. You stay in the loop; each
54
+ workflow is one well-scoped fan-out."
55
+
56
+ Consequence: between two agents inside one workflow there is **no model
57
+ round-trip**. The next agent starts the instant its inputs exist. **[INFERRED
58
+ from spec; consistent with OBSERVED timings]**
59
+
60
+ ### 1.2 Phases are progress groups, not barriers **[SPEC]**
61
+
62
+ - `phase(title)` starts a new phase; "subsequent agent() calls are grouped
63
+ under this title in the progress display". `opts.phase` on `agent()` assigns
64
+ the group explicitly and exists precisely "to avoid races on the global
65
+ phase() state" inside `pipeline()`/`parallel()` stages.
66
+ - Titles are matched exactly against `meta.phases`; an unmatched title "just
67
+ gets its own progress group".
68
+ - Nothing about a phase synchronises work. The **only** barrier is
69
+ `parallel()`.
70
+
71
+ ### 1.3 Parallelism primitives **[SPEC]**
72
+
73
+ - `pipeline(items, stage1, stage2, …)`: "run each item through all stages
74
+ independently, NO barrier between stages. Item A can be in stage 3 while item
75
+ B is still in stage 1. This is the DEFAULT for multi-stage work. Wall-clock =
76
+ slowest single-item chain, not sum-of-slowest-per-stage." A stage that throws
77
+ drops that item to `null` and skips its remaining stages.
78
+ - `parallel(thunks)`: "run tasks concurrently. This is a BARRIER … Use ONLY
79
+ when you genuinely need all results together." A barrier is justified only
80
+ when stage N needs cross-item context from all of stage N−1 (dedup/merge,
81
+ early exit on zero count, "compare with the other findings").
82
+ - Anti-barrier guidance is explicit: "'I need to flatten/map/filter first' —
83
+ do it inside a pipeline stage"; "'The stages are conceptually separate' —
84
+ that's what pipeline() models. Separate stages ≠ synchronized stages."
85
+ - Caps: "Concurrent agent() calls are capped at min(16, available CPUs − 2) per
86
+ workflow — excess calls queue and run as slots free up." Lifetime cap 1000
87
+ agents; ≤ 4096 items per `parallel()`/`pipeline()` call.
88
+
89
+ ### 1.4 One prompt per agent, structured return **[SPEC]**
90
+
91
+ - `agent(prompt, opts)`; "Subagents are told their final text IS the return
92
+ value (not a human-facing message), so they return raw data."
93
+ - `opts.schema` (JSON Schema): "the subagent is forced to call a
94
+ StructuredOutput tool and agent() returns the validated object — no parsing
95
+ needed … validation happens at the tool-call layer so the model retries on
96
+ mismatch."
97
+ - Other per-agent knobs: `label` (display), `phase` (group), `model`
98
+ (override; "default to omitting it"), `effort` (`low`…`max`), `isolation:
99
+ 'worktree'` ("EXPENSIVE … use ONLY when agents mutate files in parallel and
100
+ would otherwise conflict"), `agentType` (custom subagent definition).
101
+ - `agent()` "returns null if the user skips the agent mid-run or the subagent
102
+ dies on a terminal API error after retries (filter with .filter(Boolean))."
103
+
104
+ ### 1.5 Failure semantics live in code **[SPEC]**
105
+
106
+ - `parallel()` "never rejects" — a failing thunk becomes `null`.
107
+ - Retry, repair and convergence are ordinary loops: *loop-until-count*,
108
+ *loop-until-budget*, *loop-until-dry* ("keep spawning finders until K
109
+ consecutive rounds return nothing new"). No planner turn is spent deciding to
110
+ retry; the script already says so.
111
+
112
+ ### 1.6 Determinism and resume **[SPEC]**
113
+
114
+ - `Date.now()`, `Math.random()`, argless `new Date()` throw in scripts ("they
115
+ would break resume"); timestamps come in via `args`.
116
+ - Every invocation persists its script; the run's `journal.jsonl` "records each
117
+ agent's actual return value". Resume = same script + `resumeFromRunId`: "the
118
+ longest unchanged prefix of agent() calls returns cached results instantly;
119
+ the first edited/new call and everything after it runs live."
120
+
121
+ ### 1.7 Budget is a hard ceiling **[SPEC]**
122
+
123
+ - `budget.total / spent() / remaining()`; "The target is a HARD ceiling, not
124
+ advisory: once spent() reaches total, further agent() calls throw."
125
+ - bullswarm deliberately does the opposite (advisory targets, by user
126
+ decision: "a failed run is more costly than a slightly over budget run").
127
+ This difference is kept on purpose.
128
+
129
+ ### 1.8 Observability **[SPEC]**
130
+
131
+ - Progress is rendered as a tree grouped by phase, with per-agent labels;
132
+ `log()` "emit[s] a progress message to the user (shown as a narrator line
133
+ above the progress tree)"; `/workflows` shows live progress; the tool result
134
+ carries the `runId` and transcript directory.
135
+
136
+ ### 1.9 Quality patterns the orchestrator is told to compose **[SPEC]**
137
+
138
+ Adversarial verify (N skeptics prompted to refute; kill on majority),
139
+ perspective-diverse verify (distinct lenses instead of N identical refuters),
140
+ judge panel (N independent attempts → parallel judges → synthesis), loop-until-
141
+ dry, multi-modal sweep, completeness critic, and "no silent caps: if a workflow
142
+ bounds coverage (top-N, no-retry, sampling), log() what was dropped."
143
+
144
+ ### 1.10 Ultracode **[SPEC]**
145
+
146
+ "When a system-reminder confirms ultracode is on, that opt-in is standing:
147
+ author and run a workflow for every substantive task by default … For
148
+ multi-phase work (understand → design → implement → review), that often means
149
+ several workflows in sequence — one per phase — so you stay in the loop between
150
+ them."
151
+
152
+ ## 2. What that looks like from the outside **[OBSERVED]**
153
+
154
+ Filled from the experiment report as runs complete. Numbers here are copied
155
+ from `docs/experiments/2026-08-29-ultracode-vs-bullswarm.md`, never projected.
156
+
157
+ - bullswarm 0.10.9 smoke goal ("create hello.txt and verify it"), single pool
158
+ `claude-code` pinned to `claude-opus-5`: wall 553 s; **4 orchestrator turns =
159
+ 438 s (79 % of wall)**; 2 worker attempts = 114 s; max concurrency 1; 6
160
+ dispatches; ~29 k tokens (bullswarm's byte/4 estimate). Two of the four turns
161
+ were spent recovering from a `verify` proposal whose `review` field carried
162
+ instructions instead of an artifact path — a shape the 0.10.9 planner
163
+ skeleton itself had suggested.
164
+ - bullswarm 0.10.9 on the 7-module fixture (baseline, `--concurrency 8`): the
165
+ orchestrator's first decision (152 s) proposed one serial chain
166
+ discover → implement → verify and wrote: "Implementation is deliberately NOT
167
+ fanned out: all fixes land in one shared working tree and converge on
168
+ src/index.js, so concurrent workers would violate the shared-target mutation
169
+ policy and race on the barrel file." The policy it cites was a caution line
170
+ in the planner prompt; a concurrency cap of 8 was available and unused.
171
+ Discovery alone then ran 458 s. (Final numbers: experiment report.)
172
+ - (comparison fixture final results: pending)
173
+
174
+ ## 3. bullswarm today, mechanic by mechanic
175
+
176
+ | Mechanic | Claude `Workflow` | bullswarm ≤ 0.10.9 | Gap |
177
+ | --- | --- | --- | --- |
178
+ | Control plane | Code, authored once; no model call between agents | LLM `decide` turn at every checkpoint; each turn is a fresh `claude -p --resume` process reading the full durable context | Structural. Reachable target: **one planning turn per replan-worthy event** (initial DAG; then only on failure/completion), not per action |
179
+ | Phases | Labels for grouping; never synchronise | Forward-only kebab-case names per action; also just labels | None |
180
+ | Parallelism | `pipeline` default, `parallel` barrier; cap min(16, CPUs−2) | `executeActions` ran dependency-ready siblings **serially** (`runner.js:558`); only `fanout` items ran concurrently; goal default concurrency 3 | **Fixed in 0.11.0** — ready-set scheduler + default 8 |
181
+ | Planner bias | Script author is told to fan out and default to pipeline | Goal prompt said "return needs_more_work with the **smallest useful set** of bounded … actions" (`goal.js:18`) and planner prompt said "keep actions cohesive" | **Fixed in 0.11.0** — "propose the COMPLETE dependency graph", per-item fix→verify chains, file ownership, self-contained prompts |
182
+ | Per-agent prompt | Self-contained, plus JSON schema enforced at tool layer | Planner-authored prompt; free-text answer, content-verified by heuristics; `verify` returns JSON verdict | Partial. Schema-enforced worker output is a candidate, not adopted yet |
183
+ | Failure handling | Loops in code; `null` on agent death | Planner replans (costly); 0.10.9 added corrective turns for invalid decisions and 0.11.0 recovers mis-shaped `verify.review` before dispatch | Improved; retry-in-code per action still absent |
184
+ | Determinism / resume | Journal of return values; prefix cache | Durable `state.json` + `events.jsonl` + action ledger; resume skips durable outputs | Equivalent |
185
+ | Budget | Hard ceiling | Advisory targets (user decision) | Intentional difference |
186
+ | Observability | Progress tree, narrator, `/workflows` | `watch` heartbeat (semantic quiet + agent-output quiet since 0.10.9), `tui`, events | Comparable |
187
+ | Isolation | `isolation: 'worktree'` per agent | Shared `addDir`; planner-declared file ownership | Candidate |
188
+
189
+ ## 4. Adopted into bullswarm 0.11.0
190
+
191
+ 1. **Ready-set scheduler** (`src/workflow/runner.js`, `executeActions`).
192
+ Every action whose `dependsOn` have all succeeded is launched immediately;
193
+ a dependent action starts the moment *its own* dependencies finish, not when
194
+ the whole round finishes. The global dispatch limiter
195
+ (`settings.concurrency`) caps real concurrency. This gives `pipeline`
196
+ semantics to any DAG the planner proposes: verify-B overlaps fix-C.
197
+ Test: `dependency-ready sibling actions run concurrently and dependents
198
+ start as soon as their own inputs finish` (`tests/workflow-adaptive.test.js`).
199
+ 2. **Planning doctrine in the planner prompt** (`src/workflow/runtime.js`) and
200
+ goal orchestrator prompt (`src/workflow/goal.js`): propose the complete graph
201
+ in one decision; independent actions run concurrently; per-item fix→verify
202
+ chains plus one final whole-system verify; explicit file ownership per
203
+ action and `dependsOn` for any same-file edits; self-contained worker
204
+ prompts with absolute paths and the exact acceptance command. The prompt
205
+ also states the cost of a planning turn so the model can weigh it. The
206
+ context exposes `executionConstraints.concurrency` and
207
+ `readySiblingsRunConcurrently: true`.
208
+ 3. **Default `--concurrency` 8** for `workflow goal` (was 3; max 16).
209
+ 4. **`verify.review` contract made survivable**: instructions placed in
210
+ `review` are moved to `prompt` and the single dependency's artifact is
211
+ inferred; a `review` that is not `outputs.<actionId>.outFile` is rejected at
212
+ validation (so the 0.10.9 corrective turn fixes it) instead of failing a
213
+ dispatch after a planning round trip.
214
+
215
+ ## 5. Not adopted (yet), and why
216
+
217
+ - **Schema-enforced worker output.** bullswarm's content verification and the
218
+ JSON `verify` verdict cover the failure mode today; adding per-action
219
+ `outputSchema` is the next step if planners keep re-asking workers for
220
+ structure.
221
+ - **Per-action worktree isolation.** File ownership declared by the planner is
222
+ cheaper and matches Claude's own guidance ("EXPENSIVE … use ONLY when agents
223
+ mutate files in parallel and would otherwise conflict").
224
+ - **Hard budgets.** Deliberately advisory; see 1.7.
225
+ - **Retry policy in the graph.** Claude writes repair loops as code. A per-
226
+ action `retryPolicy` proposed by the planner would remove one planner turn per
227
+ transient failure; not yet built.
228
+ - **Identical control plane.** bullswarm's orchestrator remains an LLM per
229
+ checkpoint because the user supplies only a goal. The convergence target is
230
+ therefore "few planning turns, full width between them", measured as
231
+ `plannerSec / wallSec` and `maxConcurrentAttempts` in the experiment report.
@@ -0,0 +1,240 @@
1
+ # Claude ultracode dynamic workflow vs bullswarm `workflow goal` — side-by-side (2026-08-29)
2
+
3
+ Status: IN PROGRESS — results sections are filled in as runs complete. Numbers
4
+ below are observed, not projected; anything estimated is labelled.
5
+
6
+ ## Question
7
+
8
+ Same goal, same models, same fixture: how does bullswarm's autonomous
9
+ `workflow goal` loop behave next to Claude Code's native dynamic workflow
10
+ (the `Workflow` tool, "ultracode"), and what has to change in bullswarm to make
11
+ it feel as smooth?
12
+
13
+ ## Setup (identical on both sides)
14
+
15
+ - **Fixture**: `swarmbench`, a zero-dependency Node ESM project with six small
16
+ pure-logic modules under `src/`, one `node:test` file per module under
17
+ `tests/`, plus an intentionally incomplete `src/index.js` barrel with its own
18
+ test. Bugs were planted by a separate subagent; the orchestrator of the Claude
19
+ side (this session) was told only file names and baseline counts, never the
20
+ bugs, so both orchestrators had to discover them.
21
+ - **Two byte-identical copies**, each `git init`-ed so the diff can be audited
22
+ afterwards. Test files are SHA-256 fingerprinted before and after each run to
23
+ prove neither side edited a test.
24
+ - **Goal text (verbatim, both sides)**: see "Goal" below.
25
+ - **Models locked to one account**: workers on both sides are `claude-opus-5`.
26
+ Orchestrator: Claude side = this Claude Code session (Fable 5, authoring and
27
+ running the workflow script); bullswarm side = `claude-code` connector pinned
28
+ with `--orchestrator claude-code` and `strategy assign high|medium|low --pool
29
+ claude-code --model claude-opus-5`, `claude-fable-5` excluded.
30
+ - **bullswarm** 0.10.9 (released and installed from npm for this experiment),
31
+ run from the installed binary with an isolated `BULLSWARM_HOME` in which every
32
+ pool except `claude-code` is disabled and the connector meter is `none` so a
33
+ 5-hour burst gate cannot bench the only pool mid-run.
34
+ - **Sequential runs** on the same laptop so wall-clock numbers are not skewed
35
+ by contention for the same subscription and CPUs.
36
+
37
+ ## Goal
38
+
39
+ Verbatim, passed as the single goal argument to bullswarm and as the task
40
+ statement the Claude-side workflow script decomposes:
41
+
42
+ ```text
43
+ Make `npm test` pass in this repository. Every failing test is caused by a bug in a file under src/ (including an incomplete src/index.js barrel that must re-export every module's public API). Fix the source so all tests pass. You must not modify, delete, or add any file under tests/. Finish by running the full `npm test` and report, per file changed, what was wrong and what you changed.
44
+ ```
45
+
46
+ ## Fixture baseline
47
+
48
+ `swarmbench`: `src/{csv,duration,index,intervals,lru,semver,slugify}.js`,
49
+ `tests/{csv,duration,index,intervals,lru,semver,slugify}.test.js`, `npm test`
50
+ = `node --test tests/*.test.js`. Baseline on both copies (Node v22.23.2):
51
+
52
+ ```text
53
+ # tests 52
54
+ # pass 37
55
+ # fail 15 (failures present in all 7 test files)
56
+ ```
57
+
58
+ Test-file SHA-256 before any run (identical in both copies):
59
+
60
+ ```text
61
+ 5106804683c7f6cef0dbc3629c4c14f4ab2e88319d934626d79bb18a080ae707 tests/csv.test.js
62
+ b7c7d77daad8bfe03dd0b755574cf59faaf677df7d2701a717cf8e44138bd108 tests/duration.test.js
63
+ 27aa49f0f601d22748f36e161c963ac77bd9861fe84028a69e02a456a9b38302 tests/index.test.js
64
+ 80abeecbea763df43b2260a7ee03df211db5b092ea87e418e0be740c59383cbd tests/intervals.test.js
65
+ 2aa610981dcb9a6c65a0dd9b7b3362f23c95ef5c6436a43cb3d969fc0cc3dded tests/lru.test.js
66
+ d62bc016abe37f4400499f4a77b94c7b60cc8f473bac6b63f5a461f9a18b6a20 tests/semver.test.js
67
+ d611fd789caed8820eaf03b08aeb2799f564e5f0d320485f886fd9c5cf6ac112 tests/slugify.test.js
68
+ ```
69
+
70
+ Both bullswarm runs and the Claude workflow use a concurrency cap of 8
71
+ (Claude's own cap on this 10-CPU laptop is min(16, CPUs − 2) = 8; bullswarm
72
+ gets `--concurrency 8`), so parallelism differences come from the driving
73
+ loop, not the cap.
74
+
75
+ ## Warm-up: bullswarm 0.10.9 smoke goal (observed)
76
+
77
+ Goal: "Create a file named hello.txt in this directory containing exactly the
78
+ single line 'hello from smoke test', then verify it exists with that exact
79
+ content." Run `srdwjs`, single pool `claude-code` pinned to `claude-opus-5`,
80
+ `--max-agents 5 --max-expansion-rounds 2 --concurrency 2`.
81
+
82
+ | metric | value |
83
+ | --- | --- |
84
+ | wall | 553 s (9m13s) |
85
+ | dispatches | 6 (4 orchestrator turns, 1 worker, 1 verifier) |
86
+ | orchestrator time | 438 s = 79 % of wall (turns: 108 s, 102 s, 151 s, 77 s) |
87
+ | worker time | 38 s (create) + 76 s (verify) |
88
+ | max concurrent attempts | 1 |
89
+ | tokens | ≈29.1 k (bullswarm estimate: utf8 bytes / 4) |
90
+ | outcome | `completed`, verified |
91
+
92
+ Two of the four planning turns were spent on `verify` actions that died inside
93
+ the runtime without dispatching: the planner put reviewer instructions (turn 2)
94
+ and then a filesystem path (turn 3) into `verify.review`, which the runtime
95
+ resolves as a dotted scope path to an artifact. The 0.10.9 planner skeleton had
96
+ shown exactly that wrong shape. Fixed in the 0.11.0 work (see below).
97
+
98
+ ## Results
99
+
100
+ ### bullswarm 0.10.9 `workflow goal` (baseline, installed binary)
101
+
102
+ Run `wf-mtd6lxfn-3912fd`, started 2026-08-28T16:44:18Z, `--orchestrator
103
+ claude-code --concurrency 8 --max-agents 30 --max-expansion-rounds 8`.
104
+
105
+ First planning turn: 152 s. It proposed one strictly serial chain —
106
+ `discover-failures` → `implement-fixes` → `verify-suite` — and explained why
107
+ (verbatim from `state.json` `decisions[0].reason`):
108
+
109
+ > Implementation is deliberately NOT fanned out: all fixes land in one shared
110
+ > working tree and converge on src/index.js, so concurrent workers would violate
111
+ > the shared-target mutation policy and race on the barrel file.
112
+
113
+ The "shared-target mutation policy" it cites is a caution line in the 0.10.9
114
+ planner prompt. `discover-failures` (read-only diagnosis of 7 test files) then
115
+ ran alone for 458 s before the single `implement-fixes` worker started.
116
+
117
+ Final numbers (run `xnujua`, 16:44:18Z → 17:19:26Z):
118
+
119
+ | metric | value |
120
+ | --- | --- |
121
+ | outcome | `completed`, verified; **52/52 tests pass**; `tests/` byte-identical (SHA-256 unchanged); 7 `src/` files changed, +20/−14 lines |
122
+ | wall | **2108 s (35m08s)** |
123
+ | dispatches | 7 = 3 orchestrator turns + 4 workers |
124
+ | orchestrator time | 431 s (20 %) — turns of 152 s, 189 s, 90 s |
125
+ | worker time | 1677 s: discover 458 s → implement 356 s → (verify-suite: runtime failure, 0 s) → independent-audit 618 s → final-verification 245 s |
126
+ | max concurrent attempts | **1** (parallelism 1.00 — every second of the run had exactly one process working) |
127
+ | planning rounds | 2 expansion rounds + 1 completion |
128
+ | tokens | ≈35.8 k by bullswarm's utf8-bytes/4 estimate of outputs only — **not comparable** to real provider usage (the Claude session's own status line showed ~49 k input tokens after its first four tool calls) |
129
+
130
+ Timeline of attempts (UTC):
131
+
132
+ ```text
133
+ 16:44:18 → 16:46:50 orchestrator turn 1 (152 s) → serial chain of 3
134
+ 16:46:50 → 16:54:28 discover-failures (458 s) read-only diagnosis
135
+ 16:54:28 → 17:00:24 implement-fixes (356 s) one worker fixes all 7 files; 52/52
136
+ verify-suite failed inside runtime (review = instructions), never dispatched
137
+ 17:00:24 → 17:03:33 orchestrator turn 2 (189 s) → independent-audit + final-verification
138
+ 17:03:33 → 17:13:51 independent-audit (618 s) re-tests, fuzzes (3 000 interval sets, 2 000 LRU trials, …)
139
+ 17:13:51 → 17:17:57 final-verification (245 s) verify verdict ok
140
+ 17:17:57 → 17:19:26 orchestrator turn 3 (90 s) complete
141
+ ```
142
+
143
+ What the orchestrator did well: it delivered a correct result with an honest,
144
+ detailed completion report, refused to fan out for a stated reason, and
145
+ recovered from its own `verify.review` mistake ("it was my control-plane
146
+ error") in one extra turn. What cost time: a strictly serial plan under a
147
+ concurrency cap of 8 that was never used; a 458 s read-only discovery step
148
+ before any fix; a 618 s audit that fuzzed every module; and one wasted
149
+ planning round on the `verify.review` shape.
150
+
151
+ ### Claude Code, fresh interactive session (Opus, ultracode)
152
+
153
+ Set-up: a brand-new Claude Code 2.1.250 session started by `herdr` in its own
154
+ tab (`claude-compare`), `--model claude-opus-5 --dangerously-skip-permissions`
155
+ (the same permission posture bullswarm's connector uses), `/effort ultracode`
156
+ confirmed on screen, cwd = the twin fixture copy, no prior context. The goal
157
+ text above was submitted as the first and only prompt at 17:19:16 Z. The
158
+ bullswarm baseline's final single-process planning turn was still running
159
+ until 17:19:26 Z — a 10-second overlap, noted for honesty.
160
+
161
+ Result (session `309931b0…`, goal turn 17:19:16 Z → 17:22:45 Z):
162
+
163
+ | metric | value |
164
+ | --- | --- |
165
+ | outcome | **52/52 tests pass**; `tests/` byte-identical (SHA-256 unchanged); 7 `src/` files changed, +21/−14 lines; per-file bug explanation printed |
166
+ | wall | **209 s (3m29s)** — the session's own footer: "Baked for 3m 29s" |
167
+ | agents spawned | **0** — no `Workflow`, no `Agent` call; 11 `Bash` calls in total |
168
+ | planning round trips | 0 |
169
+ | how it decided | 17:19:19–17:20:34 read every `src/` and `tests/` file and ran `npm test` inline; 17:20:34 "I have a complete picture: 15 failures across 7 modules. Let me get a review before I start editing." → advisor: apply directly, "the ultracode carve-out for trivial mechanical edits applies now that diagnosis is done"; 17:22:09 one Python patch script over all 7 files; 17:22:14–17:22:29 `npm test`, `git diff -- tests/` (empty), `git diff -- src/` |
170
+ | tokens (real provider usage from the transcript) | output 41 042; cache-read 1 939 219; cache-write 119 052; uncached input 62 — 24 assistant messages |
171
+
172
+ **The finding that matters for this experiment:** a fresh Opus session with
173
+ ultracode on judged this goal *too small to orchestrate* and did it solo. The
174
+ "smoothness" here is a fast inline scout (75 s to a complete diagnosis) and
175
+ zero planning round trips — not parallel agents. bullswarm's orchestrator, by
176
+ contrast, cannot do anything inline: every observation costs a worker
177
+ dispatch and every decision a full planning turn, so the same 15 bugs took
178
+ 7 dispatches and 35 minutes.
179
+
180
+ Goal #1 therefore cannot show the fan-out mechanic the user asked about. Goal
181
+ #2 below is sized to require it.
182
+
183
+ ### Correctness audit — goal #1
184
+
185
+ Independent `npm test` after each run: bullswarm copy 52/52, Claude copy
186
+ 52/52. All seven `tests/*.test.js` SHA-256 values identical to the pre-run
187
+ fingerprints on both copies. Diff stat: bullswarm +20/−14 across the same 7
188
+ `src/` files; Claude +21/−14. Both fixed the same bugs the same way.
189
+
190
+ ## Goal #2 — sized to require fan-out
191
+
192
+ Base: the fixed fixture (52/52) committed clean, copied three times
193
+ (`g2-claude`, `g2-bs-v2`, `g2-bs-base`). Goal text (verbatim, both sides):
194
+
195
+ ```text
196
+ This repository (swarmbench) has six modules under src/ — csv, duration, intervals, lru, semver, slugify — and a passing test suite. For EACH of the six modules deliver three things: (1) a new test file tests/<module>.edge.test.js using node:test and node:assert/strict with at least 6 additional edge-case tests that are not already covered by tests/<module>.test.js, all passing against the current implementation; (2) complete JSDoc on every exported function or class in src/<module>.js (parameter types, return type, thrown errors, one @example) without changing any behaviour; (3) a documentation page docs/<module>.md describing the public API, edge-case behaviour, and examples. Then write docs/README.md as an index table linking all six pages with a one-line summary each. Do not modify the existing tests/<module>.test.js files. Finish with the full `npm test` passing (existing plus new tests) and report what you created.
197
+ ```
198
+
199
+ Six independent items, three deliverables each, one cross-cutting index — the
200
+ shape `pipeline()` exists for.
201
+
202
+ ### Claude Code, fresh interactive session #2 (Opus, ultracode)
203
+
204
+ New session (`0754e69f…`) in herdr tab `claude-compare-2`, same set-up as
205
+ session #1; goal submitted 17:24:4x Z. Observed driving sequence:
206
+
207
+ - 17:24:50–17:25:20 inline scouting (ls, `cat -n` every source and test file,
208
+ `npm test` → 52 passing). First words: "I'll start by orienting myself in the
209
+ repo before authoring the workflow."
210
+ - 17:25:20–17:26:44 advisor consultation ("Advisor sharpened the plan"); then
211
+ `mkdir docs` and a baseline snapshot of `src/` for behaviour-preservation
212
+ checks.
213
+ - 17:28:42 `Workflow` call: script `swarmbench-docs-and-edge-tests`, phases
214
+ Probe → Author → Verify, five `agent()` call sites in a per-module
215
+ `pipeline()` ("probe → author → adversarially verify → fix-loop, pipelined
216
+ per module"), ≈23 k characters, workers inherit the session model (Opus).
217
+ - The first call was **rejected by the harness**: "Invalid workflow script:
218
+ Script parse error: Unexpected token (48:15) … Workflow scripts must be plain
219
+ JavaScript". The session replied "Backtick inside a template literal in the
220
+ lru hints. Fixing and resending." — the same validate-and-correct loop
221
+ bullswarm 0.10.9 now has for planner decisions, except here the correction is
222
+ an inline retry inside one turn rather than a fresh planning process.
223
+
224
+ (execution numbers pending)
225
+
226
+ ### bullswarm 0.11.0 code (`node bin/bullswarm.js` from the repo checkout)
227
+
228
+ (pending)
229
+
230
+ ### bullswarm 0.10.9 (installed binary), if time allows
231
+
232
+ (pending)
233
+
234
+ ## Behaviour differences observed
235
+
236
+ (pending)
237
+
238
+ ## What to change in bullswarm
239
+
240
+ (pending)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bullswarm",
3
- "version": "0.10.8",
3
+ "version": "0.11.1",
4
4
  "description": "Route work across coding-agent CLI subscriptions — paced by live quota meters, verified by content, never trusting exit codes.",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -332,9 +332,25 @@ optional work. The loop is durable:
332
332
  execute -> observe -> decide -> validate proposal -> append -> execute -> observe
333
333
  ```
334
334
 
335
+ Execution of an accepted proposal is a ready-set scheduler: every action whose
336
+ `dependsOn` have succeeded starts at once (up to `settings.concurrency`, default
337
+ 8 for `workflow goal`), and a dependent action starts the moment its own inputs
338
+ finish. The planner is instructed to propose the complete graph per decision —
339
+ per-item fix→verify chains plus one whole-system verify — because each planning
340
+ turn is a full orchestrator round trip. See
341
+ `docs/claude-dynamic-workflow-mechanics.md` for the model this follows.
342
+
335
343
  Allowed planner decisions are `proceed`, `complete`, `needs_more_work`,
336
344
  `retry`, `escalate`, `wait_for_approval`, and `stop`. Expansion decisions must
337
345
  contain bounded actions; malformed or over-budget output executes nothing.
346
+ A malformed or non-JSON decision does not fail the run: the runtime returns
347
+ the exact validator issues to the same orchestrator thread for up to
348
+ `settings.maxPlannerCorrections` (default 2) corrective turns
349
+ (`decision.correction_requested` events), then benches that pool as
350
+ orchestrator for the rest of the run and tries one other eligible pool
351
+ (`decision.orchestrator_escalated`), and only then settles on a qualified
352
+ `completed_with_concerns`/`blocked` outcome. Safety bounds (`maxActions`,
353
+ `maxItemsPerExpansion`) still terminate immediately with a qualified outcome.
338
354
  `complete` still requires successful delivery and verification. `stop` returns
339
355
  `completed_with_concerns` with a ready best-effort artifact when useful work
340
356
  exists, or `blocked` when it does not; neither hides failed verification.
package/src/cli.js CHANGED
@@ -17,7 +17,7 @@ import { release } from './lib/release.js';
17
17
  import { cmdWorkflow } from './workflow/cli.js';
18
18
  import { cmdStrategy, maybeRefreshStrategy } from './strategy-cli.js';
19
19
  import { cmdIntegrate, installIntegration } from './integrate.js';
20
- import { helpForArgs } from './help.js';
20
+ import { helpForArgs, usageLine } from './help.js';
21
21
  import { resolveDispatchModel } from './lib/strategy.js';
22
22
 
23
23
  export function getBullswarmDir() {
@@ -474,9 +474,7 @@ export async function main(argv) {
474
474
  case 'release':
475
475
  return cmdRelease(opts);
476
476
  default:
477
- console.error(
478
- `unknown verb "${verb}". try: setup | integrate | run | health | pools | strategy | doctor | workflow | runs | version | release`,
479
- );
477
+ console.error(`unknown verb "${verb}". Run "bullswarm --help" for the list of commands.`);
480
478
  return 2;
481
479
  }
482
480
  }
@@ -486,7 +484,7 @@ export async function main(argv) {
486
484
  function cmdRelease(opts) {
487
485
  const kind = opts.rest[0];
488
486
  if (!['patch', 'minor', 'major'].includes(kind)) {
489
- console.error('usage: bullswarm release patch|minor|major [--dry-run]');
487
+ console.error(`usage: ${usageLine(['release'])}`);
490
488
  return 2;
491
489
  }
492
490
  try {