@bluewombat/runtime 0.10.0 → 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/README.md +202 -94
- package/SPECS.md +151 -25
- package/dist/cli.js +95 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/agent-shorthand.d.ts +3 -4
- package/dist/config/agent-shorthand.d.ts.map +1 -1
- package/dist/config/agent-shorthand.js +3 -4
- package/dist/config/agent-shorthand.js.map +1 -1
- package/dist/config/fixture-yaml.d.ts +8 -0
- package/dist/config/fixture-yaml.d.ts.map +1 -0
- package/dist/config/fixture-yaml.js +30 -0
- package/dist/config/fixture-yaml.js.map +1 -0
- package/dist/config/load-config.d.ts.map +1 -1
- package/dist/config/load-config.js +90 -102
- package/dist/config/load-config.js.map +1 -1
- package/dist/config/parse-args.d.ts.map +1 -1
- package/dist/config/parse-args.js +3 -4
- package/dist/config/parse-args.js.map +1 -1
- package/dist/config/types.d.ts +15 -16
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/loop/authority.d.ts +2 -0
- package/dist/loop/authority.d.ts.map +1 -1
- package/dist/loop/authority.js +11 -1
- package/dist/loop/authority.js.map +1 -1
- package/dist/loop/deliveries.d.ts.map +1 -1
- package/dist/loop/deliveries.js +19 -0
- package/dist/loop/deliveries.js.map +1 -1
- package/dist/loop/features.d.ts +30 -0
- package/dist/loop/features.d.ts.map +1 -1
- package/dist/loop/features.js +195 -73
- package/dist/loop/features.js.map +1 -1
- package/dist/loop/journal.d.ts +7 -0
- package/dist/loop/journal.d.ts.map +1 -1
- package/dist/loop/journal.js +23 -1
- package/dist/loop/journal.js.map +1 -1
- package/dist/loop/lock.d.ts +2 -0
- package/dist/loop/lock.d.ts.map +1 -1
- package/dist/loop/lock.js +10 -0
- package/dist/loop/lock.js.map +1 -1
- package/dist/loop/open-host.d.ts +10 -0
- package/dist/loop/open-host.d.ts.map +1 -1
- package/dist/loop/open-host.js +8 -8
- package/dist/loop/open-host.js.map +1 -1
- package/dist/loop/run-slot.d.ts +14 -0
- package/dist/loop/run-slot.d.ts.map +1 -1
- package/dist/loop/run-slot.js +53 -5
- package/dist/loop/run-slot.js.map +1 -1
- package/dist/loop/streams.d.ts +3 -0
- package/dist/loop/streams.d.ts.map +1 -1
- package/dist/loop/streams.js +4 -3
- package/dist/loop/streams.js.map +1 -1
- package/dist/loop/tick.d.ts +3 -0
- package/dist/loop/tick.d.ts.map +1 -1
- package/dist/loop/tick.js +2 -1
- package/dist/loop/tick.js.map +1 -1
- package/dist/loop/trace.d.ts +1 -0
- package/dist/loop/trace.d.ts.map +1 -1
- package/dist/loop/trace.js +1 -1
- package/dist/loop/trace.js.map +1 -1
- package/dist/loop/transformers.d.ts +4 -4
- package/dist/loop/transformers.d.ts.map +1 -1
- package/dist/loop/transformers.js +16 -25
- package/dist/loop/transformers.js.map +1 -1
- package/dist/operator/board.d.ts +43 -4
- package/dist/operator/board.d.ts.map +1 -1
- package/dist/operator/board.js +153 -10
- package/dist/operator/board.js.map +1 -1
- package/dist/operator/dash-model.d.ts +65 -0
- package/dist/operator/dash-model.d.ts.map +1 -0
- package/dist/operator/dash-model.js +209 -0
- package/dist/operator/dash-model.js.map +1 -0
- package/dist/operator/dash-view.d.ts +16 -0
- package/dist/operator/dash-view.d.ts.map +1 -0
- package/dist/operator/dash-view.js +226 -0
- package/dist/operator/dash-view.js.map +1 -0
- package/dist/operator/dash.d.ts +34 -0
- package/dist/operator/dash.d.ts.map +1 -0
- package/dist/operator/dash.js +141 -0
- package/dist/operator/dash.js.map +1 -0
- package/dist/operator/doctor.d.ts.map +1 -1
- package/dist/operator/doctor.js +48 -8
- package/dist/operator/doctor.js.map +1 -1
- package/dist/operator/explain.d.ts.map +1 -1
- package/dist/operator/explain.js +6 -11
- package/dist/operator/explain.js.map +1 -1
- package/dist/operator/incident-dossier.d.ts +154 -0
- package/dist/operator/incident-dossier.d.ts.map +1 -0
- package/dist/operator/incident-dossier.js +692 -0
- package/dist/operator/incident-dossier.js.map +1 -0
- package/dist/operator/incident-system.d.ts +57 -0
- package/dist/operator/incident-system.d.ts.map +1 -0
- package/dist/operator/incident-system.js +214 -0
- package/dist/operator/incident-system.js.map +1 -0
- package/dist/operator/incident.d.ts +27 -0
- package/dist/operator/incident.d.ts.map +1 -0
- package/dist/operator/incident.js +220 -0
- package/dist/operator/incident.js.map +1 -0
- package/dist/operator/init.js +24 -24
- package/dist/operator/init.js.map +1 -1
- package/dist/operator/install.d.ts +9 -0
- package/dist/operator/install.d.ts.map +1 -0
- package/dist/operator/install.js +14 -0
- package/dist/operator/install.js.map +1 -0
- package/dist/operator/live.js +4 -4
- package/dist/operator/live.js.map +1 -1
- package/dist/operator/log.d.ts +2 -0
- package/dist/operator/log.d.ts.map +1 -1
- package/dist/operator/log.js +2 -5
- package/dist/operator/log.js.map +1 -1
- package/dist/operator/restart.d.ts +20 -0
- package/dist/operator/restart.d.ts.map +1 -0
- package/dist/operator/restart.js +63 -0
- package/dist/operator/restart.js.map +1 -0
- package/dist/operator/supervise/act.d.ts +37 -0
- package/dist/operator/supervise/act.d.ts.map +1 -0
- package/dist/operator/supervise/act.js +354 -0
- package/dist/operator/supervise/act.js.map +1 -0
- package/dist/operator/supervise/config.d.ts +74 -0
- package/dist/operator/supervise/config.d.ts.map +1 -0
- package/dist/operator/supervise/config.js +175 -0
- package/dist/operator/supervise/config.js.map +1 -0
- package/dist/operator/supervise/council.d.ts +45 -0
- package/dist/operator/supervise/council.d.ts.map +1 -0
- package/dist/operator/supervise/council.js +97 -0
- package/dist/operator/supervise/council.js.map +1 -0
- package/dist/operator/supervise/decision.d.ts +46 -0
- package/dist/operator/supervise/decision.d.ts.map +1 -0
- package/dist/operator/supervise/decision.js +137 -0
- package/dist/operator/supervise/decision.js.map +1 -0
- package/dist/operator/supervise/exec.d.ts +28 -0
- package/dist/operator/supervise/exec.d.ts.map +1 -0
- package/dist/operator/supervise/exec.js +47 -0
- package/dist/operator/supervise/exec.js.map +1 -0
- package/dist/operator/supervise/memory.d.ts +33 -0
- package/dist/operator/supervise/memory.d.ts.map +1 -0
- package/dist/operator/supervise/memory.js +73 -0
- package/dist/operator/supervise/memory.js.map +1 -0
- package/dist/operator/supervise/observe.d.ts +47 -0
- package/dist/operator/supervise/observe.d.ts.map +1 -0
- package/dist/operator/supervise/observe.js +73 -0
- package/dist/operator/supervise/observe.js.map +1 -0
- package/dist/operator/supervise/prompts.d.ts +60 -0
- package/dist/operator/supervise/prompts.d.ts.map +1 -0
- package/dist/operator/supervise/prompts.js +338 -0
- package/dist/operator/supervise/prompts.js.map +1 -0
- package/dist/operator/supervise/review.d.ts +24 -0
- package/dist/operator/supervise/review.d.ts.map +1 -0
- package/dist/operator/supervise/review.js +51 -0
- package/dist/operator/supervise/review.js.map +1 -0
- package/dist/operator/supervise/supervise.d.ts +26 -0
- package/dist/operator/supervise/supervise.d.ts.map +1 -0
- package/dist/operator/supervise/supervise.js +484 -0
- package/dist/operator/supervise/supervise.js.map +1 -0
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -53,19 +53,23 @@ package's README — Host does not grow a `--repo` flag for it.
|
|
|
53
53
|
| --------------- | -------------------------------------------------------------------------------------- |
|
|
54
54
|
| `mason run` | Drain the TicketTracker, run Conductor, report |
|
|
55
55
|
| `mason watch` | Snapshot the ledger and follow the journal. Ctrl-C leaves the worker running |
|
|
56
|
-
| `mason
|
|
56
|
+
| `mason dash` | A dashboard that redraws itself: version (npm or local copy), worker running or stopped, active / upcoming / past. `t` flips to a dependency tree, `q` quits; `--tree`, `--once`, `--interval-ms N`, `--no-color` |
|
|
57
|
+
| `mason status` | Snapshot the ledger as a Feature / Ticket tree, then exit. `--json` for scripts |
|
|
57
58
|
| `mason log` | Replay the journal, with the reason a phase stopped. `--problems` for just those |
|
|
58
59
|
| `mason init` | Set this directory up: asks, writes the config and the Builder stub, then runs `setup` |
|
|
59
60
|
| `mason setup` | Prepare the tracker. A plan by default; `--apply` writes. Exit 1 on a blocked step |
|
|
60
61
|
| `mason doctor` | Check the config, the slots, and the manager. Exit 1 on any failure |
|
|
62
|
+
| `mason incident` | Gather what is known about one stop (ledger, journal, streams, transcripts) into one page, for a person to read or hand on. Without a key, list what waits. `--json`, `--out DIR`, `--with-config`. Read-only |
|
|
63
|
+
| `mason supervise` | Watch a run from another process and do what a person would: find the cause of a stop with a council of agents, resume the run, and offer the change that stops it coming back. `--once`, `--dry-run`, `--supervisor-config FILE` |
|
|
61
64
|
| `mason explain` | Say what an agent role starts with: prompt, plugins, skills, the project's profile. Read-only, offline. Exit 1 on a setup every attempt would fail on |
|
|
62
65
|
| `mason cancel` | Abandon a Ticket by key, when the tracker cannot say it is gone |
|
|
66
|
+
| `mason restart` | Resume an escalated Feature with a fresh workspace, made again from its line. Offline: the tracker is not told |
|
|
63
67
|
| `mason release` | Drop a held unit without waiting for the bail clock |
|
|
64
68
|
|
|
65
69
|
### What an agent role starts with
|
|
66
70
|
|
|
67
71
|
```bash
|
|
68
|
-
mason explain
|
|
72
|
+
mason explain ticket.build.producer # one role; no role names all of them
|
|
69
73
|
mason explain --json # the same facts for a script
|
|
70
74
|
```
|
|
71
75
|
|
|
@@ -128,7 +132,9 @@ mason watch
|
|
|
128
132
|
That is the operator surface: the durable state of every Ticket, then the film
|
|
129
133
|
of the current step (`submitted` included). It does not talk to the tracker, and
|
|
130
134
|
it cannot `resume` or claim. `status` is the same snapshot without the
|
|
131
|
-
follow
|
|
135
|
+
follow: a header with the counts, each Feature with its Tickets under it
|
|
136
|
+
(escalated Features first, finished ones last, keys in numeric order), then the
|
|
137
|
+
Tickets of no Feature. `--json` prints that tree as JSON.
|
|
132
138
|
|
|
133
139
|
When a run only printed `unavailable:<key>`, the reason is already in the
|
|
134
140
|
journal and `log` is how you read it:
|
|
@@ -144,6 +150,100 @@ distinct failure. If a slot was started with `--transcript-dir`, the newest
|
|
|
144
150
|
transcript files are named underneath, with the agent exit and duration read
|
|
145
151
|
from the start of each file.
|
|
146
152
|
|
|
153
|
+
When a Ticket or a Feature is waiting for a person, `incident` gathers what the
|
|
154
|
+
run knows of it:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
mason incident # what waits for a person
|
|
158
|
+
mason incident 106 # one stop: the key, or the tracker's number
|
|
159
|
+
mason incident 106 --out ./inc # incident.md and incident.json, to hand on
|
|
160
|
+
mason incident 106 --with-config # also the config file, parsed as YAML and blanked by path and by shape
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The page says what the run was made of (versions, what the configuration switched
|
|
164
|
+
on), who refused the work and what they said, the Tickets of the
|
|
165
|
+
Feature with their pull requests, the Gate's configuration, the journal's events
|
|
166
|
+
of the last round and where the streams and transcripts are. What it could not
|
|
167
|
+
find is listed under "Not found". Streams exist only if the Project turned them
|
|
168
|
+
on (`--streams-dir`); without them the page has the Gate's report but not what
|
|
169
|
+
the Gate printed.
|
|
170
|
+
|
|
171
|
+
### Supervising a run
|
|
172
|
+
|
|
173
|
+
> **In test, and meant to leave mason.** Do not rely on it unattended. Its move out of Host is issue #55.
|
|
174
|
+
|
|
175
|
+
A run stops: a Gate blocks, two Tickets conflict, the disk fills, the worker dies. Each time, a person
|
|
176
|
+
reads the page above, finds the cause, resumes, and fixes whatever made it happen. `supervise` does that
|
|
177
|
+
by hand-work, in a loop, in a process of its own on the same ledger:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
mason supervise --dry-run # deliberate and write what it would do; do none of it
|
|
181
|
+
mason supervise # watch, decide, act
|
|
182
|
+
mason supervise --once # one pass, then exit
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
It watches for four things: an escalated Ticket or Feature, a journal silent while work is active, a
|
|
186
|
+
worker gone, a disk running low. The last two it answers directly. For the first two it gathers the
|
|
187
|
+
`incident` page, then three agents read it from different seats and cannot see each other (the
|
|
188
|
+
*root cause*: the chain, with the code read; the *remedies*: every way to resume and to prevent; the
|
|
189
|
+
*skeptic*: how the obvious answer fails). An *arbiter* reads the three and the code and decides: a
|
|
190
|
+
cause with evidence, the moves that put the run back in motion, and the changes to make so it does not
|
|
191
|
+
return. A decision it is not sure of (`confidence: low`) is not acted on: it is asked again with that
|
|
192
|
+
on record. An arbiter answer refused for its shape is not rerun: a small agent (`agents.reformatter`,
|
|
193
|
+
Haiku by default) rewrites the answer's own text into the required form, without the reports or the code.
|
|
194
|
+
A stop only a person can settle is not resumed, but the changes that keep it from
|
|
195
|
+
returning are still made.
|
|
196
|
+
|
|
197
|
+
Every member reads the target's *rules* first (`targets.<name>.rules`, default `AGENTS.md`, which routes to
|
|
198
|
+
the decisions and the product): the code says what mason does, the rules say what it must be. The arbiter
|
|
199
|
+
states each fix as a result, its constraints and its proof, not as a design, and adds its *conformity* (the
|
|
200
|
+
rules and decisions it touches). A fix that cannot reach its result without revisiting an accepted decision
|
|
201
|
+
carries `revises`: it is not made and a person decides. The guard also reads the proposal before the fixer
|
|
202
|
+
writes anything, and a change whose same guard point is rejected twice in a row is stopped for a person.
|
|
203
|
+
|
|
204
|
+
The changes are made by a *fixer* agent in a worktree of its own, checked by the repository's own
|
|
205
|
+
commands, then read by a *guard* agent that is shown the change and the repository but not the stop (does it
|
|
206
|
+
fit the product, its decisions, its callers, its documents, and is it proved?) and refused without evidence.
|
|
207
|
+
A change the guard approves is offered as a pull request, and left for a person to fold unless the target has
|
|
208
|
+
`autoMerge: true`; nothing that waits on it moves meanwhile. Then whatever follows the fold
|
|
209
|
+
(a release, an upgrade) runs. A Project declares which repositories may be changed; with none, the
|
|
210
|
+
supervisor only moves the run. The moves are `resume`, `restart` (a Feature), `cancel`, `start-worker`,
|
|
211
|
+
`restart-worker` and `cleanup-disk`; a bound is never one of them.
|
|
212
|
+
|
|
213
|
+
It is configured in `mason.supervisor.yaml`, next to the main configuration. Nothing in it is required
|
|
214
|
+
but what the Project wants done:
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
pollIntervalMs: 15000
|
|
218
|
+
maxAttempts: 3 # per ticket, before a person is asked
|
|
219
|
+
cooldownMs: 300000 # after acting, the same finding is left this long to take effect
|
|
220
|
+
worker:
|
|
221
|
+
start: { cmd: [mason, run], timeoutMs: 30000 }
|
|
222
|
+
resume: # how a resume reaches the tracker: {key} and {number} are filled
|
|
223
|
+
cmd: [gh, issue, edit, "{number}", --repo, owner/name, --add-label, to-resume]
|
|
224
|
+
diskCleanup:
|
|
225
|
+
- cmd: [sh, -c, "rm -rf .mason/workspaces/*done*"]
|
|
226
|
+
notify: { cmd: [say, "mason needs you: {subject}"] } # optional; needs-human.md is written either way
|
|
227
|
+
agents: # defaults to Claude Code: plan mode for the council, a worktree for the fixer
|
|
228
|
+
arbiter: { cmd: [claude, -p, "{prompt}", --model, opus, --permission-mode, plan, "{addDirArgs}"] }
|
|
229
|
+
targets:
|
|
230
|
+
mason:
|
|
231
|
+
repo: ../bluewombat # a checkout; the supervisor never edits it, it adds worktrees
|
|
232
|
+
base: main
|
|
233
|
+
checks:
|
|
234
|
+
- { cmd: [npm, test] }
|
|
235
|
+
- { cmd: [npm, run, lint] }
|
|
236
|
+
deny: [.github/workflows/] # paths the fixer may not change
|
|
237
|
+
after: # once folded: cut and install the release, restart the worker
|
|
238
|
+
- { cmd: [sh, -c, "./scripts/ship-and-restart.sh"] }
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`open`, `merge` and `merged` default to `gh pr create`, `gh pr merge --squash --auto` and a `gh pr view`
|
|
242
|
+
state check; a repository that folds otherwise names its own. Everything the supervisor does is filed
|
|
243
|
+
under `<home>/supervisor/`: `events.jsonl` (the film), `memory.json` (attempts per subject),
|
|
244
|
+
`incidents/<stamp>-<key>/` (the dossier, each member's prompt and answer, `decision.json`,
|
|
245
|
+
`outcomes.json`) and `needs-human.md`, the one page a person needs to read: what only they can decide.
|
|
246
|
+
|
|
147
247
|
To abandon a Ticket the tracker can no longer mention (a deleted issue, a
|
|
148
248
|
manager with no `probe`), stop `run` if it holds the ledger lock, then:
|
|
149
249
|
|
|
@@ -162,6 +262,18 @@ mason release github:owner/name#19
|
|
|
162
262
|
|
|
163
263
|
`release` destroys the workspace and forgets it; the next `run` isolates a fresh one.
|
|
164
264
|
|
|
265
|
+
A Feature that escalated keeps its work. When a person then changes its line and resumes it,
|
|
266
|
+
the work takes in what the line gained, merged into it, before it is judged again; a conflict
|
|
267
|
+
escalates the Feature again, naming it. To throw the work away instead, stop `run` if it holds
|
|
268
|
+
the lock, then:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
mason restart github:owner/name#106 # or just 106
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The Feature must be escalated. Its workspace is removed and it is resumed; the next `run`
|
|
275
|
+
cuts the work again from the line. The tracker is not told.
|
|
276
|
+
|
|
165
277
|
The film is `<ledger>/events.jsonl`, next to `cursor`. It is not truth after a
|
|
166
278
|
crash — the ledger is. The file can grow; there is no rotation yet.
|
|
167
279
|
|
|
@@ -178,43 +290,48 @@ value that starts with `@`, like every `@bluewombat/...` package name, needs
|
|
|
178
290
|
quotes: `@` is a reserved YAML indicator at the start of a plain scalar.
|
|
179
291
|
|
|
180
292
|
```yaml
|
|
181
|
-
featureTracker:
|
|
182
|
-
package: "@bluewombat/manager-fake"
|
|
183
|
-
options:
|
|
184
|
-
source: ./.mason/source
|
|
185
|
-
target: ./.mason/threads
|
|
186
|
-
ticketTracker:
|
|
187
|
-
package: "@bluewombat/manager-fake"
|
|
188
|
-
options:
|
|
189
|
-
source: ./.mason/source
|
|
190
|
-
target: ./.mason/threads
|
|
191
293
|
workLine:
|
|
192
294
|
stable: ./work-line-stable
|
|
193
295
|
isolation: "@bluewombat/isolation-copy"
|
|
194
296
|
warm:
|
|
195
297
|
cmd: [pnpm, install, --frozen-lockfile]
|
|
196
298
|
timeoutMs: 600000
|
|
197
|
-
builder:
|
|
198
|
-
producer:
|
|
199
|
-
cmd: [node, ./mason-builder.mjs]
|
|
200
|
-
timeoutMs: 600000
|
|
201
|
-
repair:
|
|
202
|
-
cmd: [node, ./mason-repair.mjs]
|
|
203
|
-
timeoutMs: 600000
|
|
204
|
-
gates: { defaultTimeoutMs: 120000, gates: [] }
|
|
205
|
-
maxAttempts: 3
|
|
206
299
|
ticket:
|
|
300
|
+
tracker:
|
|
301
|
+
package: "@bluewombat/manager-fake"
|
|
302
|
+
options:
|
|
303
|
+
source: ./.mason/source
|
|
304
|
+
target: ./.mason/threads
|
|
305
|
+
build:
|
|
306
|
+
producer:
|
|
307
|
+
cmd: [node, ./mason-builder.mjs]
|
|
308
|
+
timeoutMs: 600000
|
|
309
|
+
repair:
|
|
310
|
+
cmd: [node, ./mason-repair.mjs]
|
|
311
|
+
timeoutMs: 600000
|
|
312
|
+
gates: { defaultTimeoutMs: 120000, gates: [] }
|
|
313
|
+
maxAttempts: 3
|
|
314
|
+
validate: { agent: false }
|
|
207
315
|
gates: { defaultTimeoutMs: 900000, gates: [] }
|
|
316
|
+
feature:
|
|
317
|
+
tracker:
|
|
318
|
+
package: "@bluewombat/manager-fake"
|
|
319
|
+
options:
|
|
320
|
+
source: ./.mason/source
|
|
321
|
+
target: ./.mason/threads
|
|
208
322
|
authority:
|
|
209
323
|
enabled: false
|
|
210
324
|
timeoutMs: 600000
|
|
211
325
|
pollIntervalMs: 10000
|
|
212
326
|
```
|
|
213
327
|
|
|
328
|
+
The file reads by level, and each level in the order it is used: `ticket` and `feature` both hold
|
|
329
|
+
`tracker`, then what makes the work (`build` for a Ticket, `test` for a Feature), `validate`, `gates`, `fix`.
|
|
330
|
+
|
|
214
331
|
| Field | Meaning |
|
|
215
332
|
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
333
|
+
| `feature.tracker` | `package` (a name or path that exports `createManager`) and `options`: where Features are written and signalled |
|
|
334
|
+
| `ticket.tracker` | Same shape: where the Tickets of a Feature are, and the Authority that holds the lines. May name the same package as `feature.tracker`, or another |
|
|
218
335
|
| `workLine.stable` | Existing directory the work folds into. Optional when the manager names a reference work line: Host then keeps its own copy at `<home>/work-line` |
|
|
219
336
|
| `workLine.targetStable` | A second copy of the work line, on the branch a Feature is folded into: what is merged into the Feature when it is brought up to date. Host keeps its own at `<home>/target-line` when the manager names a reference; required with an Authority otherwise |
|
|
220
337
|
| `workLine.isolation` | Package name or path exporting `strategy` — required. Host loads it like a manager; Isolator/Integrator do not sniff |
|
|
@@ -227,26 +344,25 @@ pollIntervalMs: 10000
|
|
|
227
344
|
| `ledger` | WorkLedger root (created). The Cursor is a file inside it. Default `<home>/ledger` |
|
|
228
345
|
| `persist` | Package name or path exporting `openPersist({ ledgerRoot })`: how the ledger is stored under that root. Default `@bluewombat/persist-fs`; `@bluewombat/persist-sqlite` ships too. `watch` reads through the same backend |
|
|
229
346
|
| `agents` | Agents a pass names instead of writing `cmd` (§ Agent passes written short) |
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `builder.maxAttempts` | Attempts one Task may start. Default 3 |
|
|
347
|
+
| `feature.test` | Optional. Declares the feature-test acceptance check (see § The feature-test acceptance check): the pass that makes the Ticket holding the suite, `{ agent, prompt, homePlugins, appDeckProfile, timeoutMs, gates }` or `{ cmd, timeoutMs, gates }`. One command for the first pass and for a pass a Gate refused; its own `gates` (they check the form: compiles, one test per manifest id, no application file touched). Nothing is borrowed from `ticket.build.producer`, `ticket.build.repair` or `ticket.build.gates`. Needs a Feature Gate with `skipWhen: skip-feature-test`, and the other way round |
|
|
348
|
+
| `ticket.build.producer` | `cmd` / `timeoutMs`. Spawned in the workspace. Exit 0 runs `ticket.build.gates` |
|
|
349
|
+
| `ticket.build.repair` | The same, for a pass a Gate refused. **Required**, and never inherited: it names its own command. It is judged by the same `ticket.build.gates` as the producer |
|
|
350
|
+
| `ticket.build.gates` | `defaultTimeoutMs` and `gates`: the one Gate list for `ticket.build.producer` and `ticket.build.repair`. `gates` written on either pass is refused, naming this key. Empty: the unit runs ungated |
|
|
351
|
+
| `ticket.build.maxAttempts` | Attempts one Task may start. Default 3 |
|
|
236
352
|
| `ticket.fix` | `cmd` / `timeoutMs` / `gates`. Spawned when a judgement of the whole refused it — by an Authority, by `ticket.validate`, or by both. Judged by its own `gates` |
|
|
237
|
-
| `ticket.validate` | `cmd` / `timeoutMs` / `gates`. A local, read-only judge of the assembled ticket, run after align, with or without an Authority. A refusal is repaired by `ticket.fix` the same as an Authority's. Judged by its own `gates`, never `
|
|
353
|
+
| `ticket.validate` | `cmd` / `timeoutMs` / `gates`. A local, read-only judge of the assembled ticket, run after align, with or without an Authority. A refusal is repaired by `ticket.fix` the same as an Authority's. Judged by its own `gates`, never `ticket.build`'s or `ticket.fix`'s. `validate` is required when a `ticket` block is written, and so is `feature.validate`: `{ agent: false, gates }` turns the agent off and keeps the gates, and `{ agent: false }` with no gates turns the review off — a bare `gates:` counts as none. |
|
|
238
354
|
| `ticket.maxAttempts` | Attempts that one fix may start. Default 3 |
|
|
239
355
|
| `ticket.gates` | `defaultTimeoutMs` and `gates`: the produce:false judgement-only pass — no producer of its own, re-checking the assembled ticket. Empty: the Authority judges alone. Independent of `ticket.fix.gates` / `ticket.validate.gates` — never shared with either |
|
|
240
356
|
| `timeoutMs` | How long a child **outside** a Task may run: manager, isolations |
|
|
241
357
|
| `ticket.maxRefusals` | Times the work may be sent back before it escalates, by an Authority, by `ticket.validate`, or by both — one shared budget. Default 3 |
|
|
242
|
-
| `ticket.maxParallel` | How many Tickets of a Project are worked on at once. Default 1: one after the other. Above 1 needs `authority.enabled`; a Ticket waiting on a check or a person holds a place, the others go on. Tickets that wait on another (*Blocked by*) still start only once it is `done
|
|
243
|
-
| `feature` | Required with `authority.enabled`. How a Feature's line is delivered to the line it is folded into, by the same steps as a Ticket: the final line is merged into the Feature's work (`workLine.targetStable`, or Host's own copy of the reference), `validate` (same shape as `ticket.validate`) reviews the whole against the Feature's request and its `gates` (at least one) are where the project's checks on the whole line run, a refusal goes to `fix`, the line is published, and `gates` (at least one, the CI) judge it. `maxAttempts` and `maxRefusals` (default 3, same rule as a Ticket's) are its own. Nothing falls back to `ticket`. The merge itself is automatic only with `merge-when-ready`; the delivery to the Authority is for every Feature |
|
|
358
|
+
| `ticket.maxParallel` | How many Tickets of a Project are worked on at once. Default 1: one after the other. Above 1 needs `authority.enabled`; a Ticket waiting on a check or a person holds a place, the others go on. Tickets that wait on another (*Blocked by*) still start only once it is `done`. A resumed Ticket takes a place too: while all of them are held by Tickets being worked on, the resume waits and its signal is kept |
|
|
359
|
+
| `feature` | Required with `authority.enabled`. How a Feature's line is delivered to the line it is folded into, by the same steps as a Ticket: the final line is merged into the Feature's work (`workLine.targetStable`, or Host's own copy of the reference), `validate` (same shape as `ticket.validate`) reviews the whole against the Feature's request and its `gates` (at least one) are where the project's checks on the whole line run, a refusal goes to `fix`, the line is published, and `gates` (at least one, the CI) judge it. `maxAttempts` and `maxRefusals` (default 3, same rule as a Ticket's) are its own. `settledTimeoutMs` (default 14400000, 4 hours) is how long a submitted Feature waits while the manager cannot say whether its Submission was folded (no answer, or no `settled`) before it escalates; a Submission the tracker says is still open waits for its reviewer. Nothing falls back to `ticket`. The merge itself is automatic only with `merge-when-ready`; the delivery to the Authority is for every Feature |
|
|
244
360
|
| `pollIntervalMs` | Set it and the process keeps draining until SIGINT. Absent: one tick. Pick it with § Choosing `pollIntervalMs` — 10000 is the floor worth having |
|
|
245
361
|
| `observability.streams` | `enabled`, `dir`, `keep`. Films what every child says, as it says it — one file per child under `<dir>/<ticket>/`, named in the journal by a `stream-opened` line. `dir` resolves against the config file; default `<home>/streams`. `keep` is `stdout` / `stderr`, default both. Off unless `enabled` is true: a stream is the Project's own code and prompts in the clear |
|
|
246
362
|
|
|
247
363
|
Every `cmd` is named where it is used, and nothing falls back to a neighbour: a
|
|
248
364
|
`repair` with no command of its own is refused rather than quietly running the
|
|
249
|
-
first-pass agent again. `
|
|
365
|
+
first-pass agent again. `ticket.build` makes the unit of work. `ticket` judges the whole
|
|
250
366
|
and, when that judgement refuses it, runs `ticket.fix` — not a second first-pass.
|
|
251
367
|
`ticket.validate`, when declared, is a second, local judge of the whole: it
|
|
252
368
|
runs after align, before the Authority sees the work (or before the final
|
|
@@ -275,23 +391,23 @@ agents:
|
|
|
275
391
|
denyShell: [] # commands refused to the agent, e.g. [gh, vercel]; ssh and scp always are
|
|
276
392
|
transcripts: ./.mason/transcripts
|
|
277
393
|
pluginsOut: ./.mason/plugins # where deck profiles are built
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
394
|
+
ticket:
|
|
395
|
+
build:
|
|
396
|
+
producer:
|
|
397
|
+
agent: cursor
|
|
398
|
+
prompt: ./prompts/builder.md
|
|
399
|
+
homePlugins: [./ai-plugins/core/builder]
|
|
400
|
+
appDeckProfile: .ai/profiles/builder
|
|
401
|
+
args: [--mode, agent] # the vendor CLI's own flags
|
|
402
|
+
timeoutMs: 1200000
|
|
287
403
|
```
|
|
288
404
|
|
|
289
405
|
Host writes the `cmd` it stands for — `node <slots>/<role>.mjs --prompt-file …
|
|
290
406
|
-- node <slots>/agents/cursor.mjs --transcript-dir … --plugin-dir … --app-deck-profile
|
|
291
407
|
… --plugins-out … --agent-arg …` — with `<slots>` the
|
|
292
408
|
`node_modules/@bluewombat/slots` next to the config. Nothing past the config
|
|
293
|
-
knows a pass was written short. The roles: `
|
|
294
|
-
`
|
|
409
|
+
knows a pass was written short. The roles: `ticket.build.producer`,
|
|
410
|
+
`ticket.build.repair`, `ticket.fix`, `ticket.validate`, and `authority.describe`
|
|
295
411
|
(an argv, or `{ agent, prompt?, homePlugins?, appDeckProfile?, args? }`).
|
|
296
412
|
|
|
297
413
|
| Key | Meaning |
|
|
@@ -387,20 +503,20 @@ Examples ship in [`@bluewombat/slots`](../../plugins/slots/README.md), installed
|
|
|
387
503
|
the shipped roles and agents (Cursor CLI, Claude Code), the shipped Gates, and
|
|
388
504
|
their options. What follows is what Host guarantees them.
|
|
389
505
|
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
506
|
+
```yaml
|
|
507
|
+
ticket:
|
|
508
|
+
build:
|
|
509
|
+
producer:
|
|
510
|
+
cmd:
|
|
511
|
+
- node
|
|
512
|
+
- ./node_modules/@bluewombat/slots/builders/producer.mjs
|
|
513
|
+
- --
|
|
514
|
+
- node
|
|
515
|
+
- ./node_modules/@bluewombat/slots/agents/claude.mjs
|
|
516
|
+
timeoutMs: 600000
|
|
401
517
|
```
|
|
402
518
|
|
|
403
|
-
`
|
|
519
|
+
`ticket.build.repair` is required too, with its own `cmd` and `timeoutMs`. The
|
|
404
520
|
composition with `--` is the same.
|
|
405
521
|
|
|
406
522
|
### Builder
|
|
@@ -437,16 +553,16 @@ that completes cleanly but finds a problem is still a refusal.
|
|
|
437
553
|
|
|
438
554
|
### Gates
|
|
439
555
|
|
|
440
|
-
Four sequences: `
|
|
441
|
-
`
|
|
556
|
+
Four sequences: `ticket.build.gates`, shared by `ticket.build.producer` and
|
|
557
|
+
`ticket.build.repair` (a repair is judged by exactly what judged the first try),
|
|
442
558
|
then one per assembly role — `ticket.fix`, `ticket.validate` — plus
|
|
443
559
|
`ticket.gates` for the one judgement-only pass with no producer of its own
|
|
444
560
|
(re-checking the assembled ticket; `ci-green` reads the checks the work line
|
|
445
561
|
ran on what the Publisher placed, so it belongs there). The assembly lists are
|
|
446
562
|
not shared with each other or with the builder, and no dispatch chooses between
|
|
447
563
|
two buckets: a check right for one pass is not necessarily right for another,
|
|
448
|
-
even within the same stage. `gates` written on `
|
|
449
|
-
`
|
|
564
|
+
even within the same stage. `gates` written on `ticket.build.producer` or
|
|
565
|
+
`ticket.build.repair` is refused, and the message names `ticket.build.gates`.
|
|
450
566
|
|
|
451
567
|
Implementer's Gates are told `--stage unit` or `--stage ticket` alongside
|
|
452
568
|
`--id`, `--attempt`, `--gate-id` and `--intention`.
|
|
@@ -454,7 +570,7 @@ Most have no use for it. `workspace-changed` does: "nothing changed" means
|
|
|
454
570
|
nothing was done in a unit, and is the correct outcome for an assembly that
|
|
455
571
|
needed nothing.
|
|
456
572
|
|
|
457
|
-
`init` writes an empty list on `
|
|
573
|
+
`init` writes an empty list on `ticket.build.producer` and `ticket.build.repair`; add a
|
|
458
574
|
Gate when the Project has a check.
|
|
459
575
|
|
|
460
576
|
Every child carries its own ceiling and no other. A Gate entry may name a
|
|
@@ -464,7 +580,7 @@ from what another child left behind, so a slow producer cannot leave its
|
|
|
464
580
|
Gates a millisecond to answer in.
|
|
465
581
|
|
|
466
582
|
What bounds the unit is therefore what you wrote:
|
|
467
|
-
`
|
|
583
|
+
`ticket.build.maxAttempts × (ticket.build.producer.timeoutMs + Σ ticket.build.gates timeouts)`
|
|
468
584
|
for a first pass, and the same shape with `repair` in place of `producer` once
|
|
469
585
|
a Gate has refused one.
|
|
470
586
|
A repair and an assembly fix each carry their own `timeoutMs`. An assembly fix
|
|
@@ -484,59 +600,51 @@ is bounded the same way from `ticket.maxAttempts` and `ticket.fix`.
|
|
|
484
600
|
}
|
|
485
601
|
```
|
|
486
602
|
|
|
487
|
-
### The
|
|
603
|
+
### The feature-test acceptance check
|
|
488
604
|
|
|
489
|
-
A Project proves each Feature by
|
|
605
|
+
A Project proves each Feature by its own tests, run once on the assembled Feature
|
|
490
606
|
line, when its config says so, by a pair:
|
|
491
607
|
|
|
492
608
|
```yaml
|
|
493
|
-
|
|
494
|
-
|
|
609
|
+
feature:
|
|
610
|
+
test: # makes the Ticket that holds the suite (label `feature-test` on the tracker)
|
|
495
611
|
agent: claude
|
|
496
|
-
prompt: ./prompts/
|
|
612
|
+
prompt: ./prompts/feature-test.md # {{task}} is the scenarios; {{report}}, {{refused_by}}, {{refusal}} on a refused pass
|
|
497
613
|
timeoutMs: 1800000
|
|
498
614
|
gates: # form only: it compiles, one test per manifest id, no application file touched
|
|
499
615
|
gates:
|
|
500
|
-
- { id:
|
|
501
|
-
feature:
|
|
616
|
+
- { id: feature-tests-form, argv: [...], timeoutMs: 120000 }
|
|
502
617
|
validate:
|
|
503
618
|
agent: false
|
|
504
619
|
gates:
|
|
505
620
|
defaultTimeoutMs: 120000
|
|
506
621
|
gates:
|
|
507
|
-
- id:
|
|
508
|
-
skipWhen: skip-test
|
|
622
|
+
- id: feature-tests
|
|
623
|
+
skipWhen: skip-feature-test # Host does not run this Gate for a Feature that carries the label
|
|
509
624
|
timeoutMs: 720000
|
|
510
625
|
argv:
|
|
511
626
|
- node
|
|
512
|
-
- ./node_modules/@bluewombat/slots/gates/
|
|
513
|
-
- --up
|
|
514
|
-
- pnpm e2e:up
|
|
515
|
-
- --ready
|
|
516
|
-
- pnpm e2e:ready
|
|
627
|
+
- ./node_modules/@bluewombat/slots/gates/feature-tests.mjs
|
|
517
628
|
- --run
|
|
518
|
-
- pnpm
|
|
519
|
-
- --down
|
|
520
|
-
- pnpm e2e:down
|
|
629
|
+
- pnpm test --grep "{ids}" --reporter=junit --outputFile={junit}
|
|
521
630
|
```
|
|
522
631
|
|
|
523
632
|
`skipWhen` is the one condition Host reads for a Gate, on a Feature's Gates only
|
|
524
633
|
(`feature.validate`, `feature.gates`, `feature.fix`); its one value today is
|
|
525
|
-
`skip-test
|
|
526
|
-
passed: the journal gets `gate-skipped` and the trace says "`
|
|
527
|
-
skip-test
|
|
634
|
+
`skip-feature-test`, whatever the tracker calls that label. A Gate left out is not a Gate that
|
|
635
|
+
passed: the journal gets `gate-skipped` and the trace says "`feature-test` not run: the Feature carries
|
|
636
|
+
skip-feature-test". The Gate knows nothing of the skip and no environment variable carries it.
|
|
528
637
|
|
|
529
|
-
The tracker side: a Ticket carrying the
|
|
530
|
-
made by `
|
|
531
|
-
(`
|
|
638
|
+
The tracker side: a Ticket carrying the feature-test label (`featureTestLabel` of the TicketTracker manager) is
|
|
639
|
+
made by `feature.test`; a Feature carrying the skip label
|
|
640
|
+
(`skipFeatureTestLabel` of the FeatureTracker manager) has that Ticket cancelled, with a line in
|
|
532
641
|
the trace. The label is read again until the Feature is submitted. A Project that declares the
|
|
533
|
-
check refuses a Feature that holds neither
|
|
642
|
+
check refuses a Feature that holds neither a feature-test Ticket nor the skip label, or whose feature-test
|
|
534
643
|
Ticket is not blocked by every other Ticket, as `invalid`, with the reason as a comment.
|
|
535
644
|
|
|
536
645
|
What the Gate proves is a red/green verdict; what the operator reads is its text in the
|
|
537
646
|
journal (`gate-finished`) — a red suite is also in the reason of the refusal the fix is
|
|
538
|
-
given and in the escalation. A passing suite's text is not shown on the pull request
|
|
539
|
-
RESERVATIONS I17.
|
|
647
|
+
given and in the escalation. A passing suite's text is not shown on the pull request (issue #62).
|
|
540
648
|
|
|
541
649
|
### Describer
|
|
542
650
|
|
|
@@ -610,7 +718,7 @@ it each stop `mason run` at boot, and `mason doctor` reports the same offline
|
|
|
610
718
|
and is the operator's directory.
|
|
611
719
|
|
|
612
720
|
```
|
|
613
|
-
assembled → align → recheck (
|
|
721
|
+
assembled → align → recheck (ticket.build.gates again, on the aligned work)
|
|
614
722
|
conflict or failed → ticket.fix, then align again
|
|
615
723
|
→ ticket.validate (optional, local)
|
|
616
724
|
refused → ticket.fix, then align again, recheck, validate again
|
|
@@ -631,13 +739,13 @@ fast-forward — the work line moved where the Authority is, and the next
|
|
|
631
739
|
Isolation must start from that version, not from a copy left behind.
|
|
632
740
|
|
|
633
741
|
A refusal is not a second failure path: its report reaches `ticket.fix`
|
|
634
|
-
exactly as a Gate's report reaches `
|
|
742
|
+
exactly as a Gate's report reaches `ticket.build.repair`. After `ticket.maxRefusals`
|
|
635
743
|
refusals the ticket escalates with that report as its Trace, and `done` names
|
|
636
744
|
the Submission rather than a directory on the machine that ran.
|
|
637
745
|
|
|
638
746
|
## Write another manager
|
|
639
747
|
|
|
640
|
-
Host loads whatever `
|
|
748
|
+
Host loads whatever `feature.tracker.package` and `ticket.tracker.package` name, as long as it exports `createManager`. The
|
|
641
749
|
contract and a worked example are in
|
|
642
750
|
[`@bluewombat/manager-kit`](../manager-kit/README.md). Nothing in Host changes.
|
|
643
751
|
|
|
@@ -706,7 +814,7 @@ change that breaks it does not merge.
|
|
|
706
814
|
| `config/` | What a Project wrote: argv, `mason.config.yaml`, discovery, `HostOptions`. Pure: YAML in, options out | A runtime value (a Journal, a Port, a process handle) | Node, `manager-kit` types |
|
|
707
815
|
| `plugins/` | Load a package by name and check its shape: a manager, an isolation strategy | What to do with the loaded thing | `config` |
|
|
708
816
|
| `loop/` | The process: listen → probe → adapt → admit → run → report; composition (`open-host`, `transformers`, Authority, work line, lock, Cursor, journal, trace) | A command a human types; a string a human reads about the config | `plugins`, `config`, the kernel, Transformers |
|
|
709
|
-
| `operator/` | Tools for a human: `init`, `setup`, `doctor`, `watch` / `status`, `log`, `cancel`, `release`, `board`, the terminal prompt | Anything the loop needs — the loop cannot see this layer | everything below |
|
|
817
|
+
| `operator/` | Tools for a human: `init`, `setup`, `doctor`, `watch` / `status`, `log`, `incident`, `supervise/`, `cancel`, `restart`, `release`, `board`, the terminal prompt | Anything the loop needs — the loop cannot see this layer | everything below |
|
|
710
818
|
| `index.ts`, `cli.ts` | The package surface and the dispatch. Nothing else sits at the top of `src/` | Logic | everything |
|
|
711
819
|
|
|
712
820
|
Host's code names no plugin. A manager, an isolation strategy, a persistence
|
|
@@ -761,6 +869,6 @@ src/
|
|
|
761
869
|
config/ argv, JSON config, config discovery → HostOptions; defaults: the plugin names Host ships with
|
|
762
870
|
plugins/ load: the one loader; manager, isolation, persist: one shape each; discover
|
|
763
871
|
loop/ open-host (composition), tick (the loop), deliveries, drive, probe, report, authority, work-line, cursor, lock, journal (the film), streams (what a child said), trace, transformers
|
|
764
|
-
operator/ run, init, setup, doctor, explain, live (watch / status), log, film, cancel, release, board, prompt, git-remote
|
|
872
|
+
operator/ run, init, setup, doctor, explain, live (watch / status), log, incident, supervise/ (observe, council, act), film, cancel, release, board, prompt, git-remote
|
|
765
873
|
fixtures/ stand-in slots for the loop's tests
|
|
766
874
|
```
|