@bluewombat/runtime 0.9.0 → 0.11.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/README.md +200 -99
- package/SPECS.md +121 -19
- package/dist/cli.js +35 -4
- package/dist/cli.js.map +1 -1
- package/dist/config/agent-shorthand.d.ts +25 -5
- package/dist/config/agent-shorthand.d.ts.map +1 -1
- package/dist/config/agent-shorthand.js +65 -21
- 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/home.d.ts +2 -0
- package/dist/config/home.d.ts.map +1 -1
- package/dist/config/home.js +2 -0
- package/dist/config/home.js.map +1 -1
- package/dist/config/load-config.d.ts.map +1 -1
- package/dist/config/load-config.js +301 -62
- package/dist/config/load-config.js.map +1 -1
- package/dist/config/parse-args.d.ts +1 -1
- package/dist/config/parse-args.d.ts.map +1 -1
- package/dist/config/parse-args.js +84 -58
- package/dist/config/parse-args.js.map +1 -1
- package/dist/config/types.d.ts +96 -16
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +13 -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 +38 -1
- package/dist/loop/authority.d.ts.map +1 -1
- package/dist/loop/authority.js +41 -2
- package/dist/loop/authority.js.map +1 -1
- package/dist/loop/context.d.ts +16 -1
- package/dist/loop/context.d.ts.map +1 -1
- package/dist/loop/drive.d.ts +9 -3
- package/dist/loop/drive.d.ts.map +1 -1
- package/dist/loop/drive.js +17 -7
- package/dist/loop/drive.js.map +1 -1
- package/dist/loop/features.d.ts +16 -0
- package/dist/loop/features.d.ts.map +1 -1
- package/dist/loop/features.js +404 -62
- package/dist/loop/features.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 +111 -9
- package/dist/loop/open-host.js.map +1 -1
- package/dist/loop/report.d.ts.map +1 -1
- package/dist/loop/report.js +21 -9
- package/dist/loop/report.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 +2 -1
- 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.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 +2 -2
- package/dist/loop/trace.js.map +1 -1
- package/dist/loop/transformers.d.ts +16 -2
- package/dist/loop/transformers.d.ts.map +1 -1
- package/dist/loop/transformers.js +126 -22
- package/dist/loop/transformers.js.map +1 -1
- package/dist/loop/warm.js +1 -1
- package/dist/loop/warm.js.map +1 -1
- package/dist/operator/agent-plugins.d.ts.map +1 -1
- package/dist/operator/agent-plugins.js +14 -8
- package/dist/operator/agent-plugins.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/doctor.d.ts +1 -1
- package/dist/operator/doctor.d.ts.map +1 -1
- package/dist/operator/doctor.js +76 -29
- package/dist/operator/doctor.js.map +1 -1
- package/dist/operator/explain.d.ts.map +1 -1
- package/dist/operator/explain.js +36 -12
- package/dist/operator/explain.js.map +1 -1
- package/dist/operator/film.js +0 -3
- package/dist/operator/film.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 +696 -0
- package/dist/operator/incident-dossier.js.map +1 -0
- package/dist/operator/incident-system.d.ts +48 -0
- package/dist/operator/incident-system.d.ts.map +1 -0
- package/dist/operator/incident-system.js +152 -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 +27 -22
- package/dist/operator/init.js.map +1 -1
- 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 +7 -3
- 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/setup.d.ts.map +1 -1
- package/dist/operator/setup.js +2 -0
- package/dist/operator/setup.js.map +1 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -53,19 +53,21 @@ 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 status` | Snapshot the ledger, then exit. `--json` for scripts
|
|
56
|
+
| `mason status` | Snapshot the ledger as a Feature / Ticket tree, then exit. `--json` for scripts |
|
|
57
57
|
| `mason log` | Replay the journal, with the reason a phase stopped. `--problems` for just those |
|
|
58
58
|
| `mason init` | Set this directory up: asks, writes the config and the Builder stub, then runs `setup` |
|
|
59
59
|
| `mason setup` | Prepare the tracker. A plan by default; `--apply` writes. Exit 1 on a blocked step |
|
|
60
60
|
| `mason doctor` | Check the config, the slots, and the manager. Exit 1 on any failure |
|
|
61
|
+
| `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 |
|
|
61
62
|
| `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
63
|
| `mason cancel` | Abandon a Ticket by key, when the tracker cannot say it is gone |
|
|
64
|
+
| `mason restart` | Resume an escalated Feature with a fresh workspace, made again from its line. Offline: the tracker is not told |
|
|
63
65
|
| `mason release` | Drop a held unit without waiting for the bail clock |
|
|
64
66
|
|
|
65
67
|
### What an agent role starts with
|
|
66
68
|
|
|
67
69
|
```bash
|
|
68
|
-
mason explain
|
|
70
|
+
mason explain ticket.build.producer # one role; no role names all of them
|
|
69
71
|
mason explain --json # the same facts for a script
|
|
70
72
|
```
|
|
71
73
|
|
|
@@ -128,7 +130,9 @@ mason watch
|
|
|
128
130
|
That is the operator surface: the durable state of every Ticket, then the film
|
|
129
131
|
of the current step (`submitted` included). It does not talk to the tracker, and
|
|
130
132
|
it cannot `resume` or claim. `status` is the same snapshot without the
|
|
131
|
-
follow
|
|
133
|
+
follow: a header with the counts, each Feature with its Tickets under it
|
|
134
|
+
(escalated Features first, finished ones last, keys in numeric order), then the
|
|
135
|
+
Tickets of no Feature. `--json` prints that tree as JSON.
|
|
132
136
|
|
|
133
137
|
When a run only printed `unavailable:<key>`, the reason is already in the
|
|
134
138
|
journal and `log` is how you read it:
|
|
@@ -144,6 +148,24 @@ distinct failure. If a slot was started with `--transcript-dir`, the newest
|
|
|
144
148
|
transcript files are named underneath, with the agent exit and duration read
|
|
145
149
|
from the start of each file.
|
|
146
150
|
|
|
151
|
+
When a Ticket or a Feature is waiting for a person, `incident` gathers what the
|
|
152
|
+
run knows of it:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
mason incident # what waits for a person
|
|
156
|
+
mason incident 106 # one stop: the key, or the tracker's number
|
|
157
|
+
mason incident 106 --out ./inc # incident.md and incident.json, to hand on
|
|
158
|
+
mason incident 106 --with-config # also the config file, secret-looking values blanked
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The page says what the run was made of (versions, what the configuration switched
|
|
162
|
+
on), who refused the work and what they said, the Tickets of the
|
|
163
|
+
Feature with their pull requests, the Gate's configuration, the journal's events
|
|
164
|
+
of the last round and where the streams and transcripts are. What it could not
|
|
165
|
+
find is listed under "Not found". Streams exist only if the Project turned them
|
|
166
|
+
on (`--streams-dir`); without them the page has the Gate's report but not what
|
|
167
|
+
the Gate printed.
|
|
168
|
+
|
|
147
169
|
To abandon a Ticket the tracker can no longer mention (a deleted issue, a
|
|
148
170
|
manager with no `probe`), stop `run` if it holds the ledger lock, then:
|
|
149
171
|
|
|
@@ -151,13 +173,29 @@ manager with no `probe`), stop `run` if it holds the ledger lock, then:
|
|
|
151
173
|
mason cancel github:owner/name#19
|
|
152
174
|
```
|
|
153
175
|
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
A unit that was stopped (Ctrl-C, a crash) is picked up where it was by the next
|
|
177
|
+
`mason run`: its workspace is kept, the Attempts it finished still count and the
|
|
178
|
+
numbering goes on. To start a unit over instead (the line moved on, the work took
|
|
179
|
+
a wrong turn), stop `run` if it holds the lock, then:
|
|
156
180
|
|
|
157
181
|
```bash
|
|
158
182
|
mason release github:owner/name#19
|
|
159
183
|
```
|
|
160
184
|
|
|
185
|
+
`release` destroys the workspace and forgets it; the next `run` isolates a fresh one.
|
|
186
|
+
|
|
187
|
+
A Feature that escalated keeps its work. When a person then changes its line and resumes it,
|
|
188
|
+
the work takes in what the line gained, merged into it, before it is judged again; a conflict
|
|
189
|
+
escalates the Feature again, naming it. To throw the work away instead, stop `run` if it holds
|
|
190
|
+
the lock, then:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
mason restart github:owner/name#106 # or just 106
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The Feature must be escalated. Its workspace is removed and it is resumed; the next `run`
|
|
197
|
+
cuts the work again from the line. The tracker is not told.
|
|
198
|
+
|
|
161
199
|
The film is `<ledger>/events.jsonl`, next to `cursor`. It is not truth after a
|
|
162
200
|
crash — the ledger is. The file can grow; there is no rotation yet.
|
|
163
201
|
|
|
@@ -174,45 +212,50 @@ value that starts with `@`, like every `@bluewombat/...` package name, needs
|
|
|
174
212
|
quotes: `@` is a reserved YAML indicator at the start of a plain scalar.
|
|
175
213
|
|
|
176
214
|
```yaml
|
|
177
|
-
featureTracker:
|
|
178
|
-
package: "@bluewombat/manager-fake"
|
|
179
|
-
options:
|
|
180
|
-
source: ./.mason/source
|
|
181
|
-
target: ./.mason/threads
|
|
182
|
-
ticketTracker:
|
|
183
|
-
package: "@bluewombat/manager-fake"
|
|
184
|
-
options:
|
|
185
|
-
source: ./.mason/source
|
|
186
|
-
target: ./.mason/threads
|
|
187
215
|
workLine:
|
|
188
216
|
stable: ./work-line-stable
|
|
189
217
|
isolation: "@bluewombat/isolation-copy"
|
|
190
218
|
warm:
|
|
191
219
|
cmd: [pnpm, install, --frozen-lockfile]
|
|
192
220
|
timeoutMs: 600000
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
ticket:
|
|
222
|
+
tracker:
|
|
223
|
+
package: "@bluewombat/manager-fake"
|
|
224
|
+
options:
|
|
225
|
+
source: ./.mason/source
|
|
226
|
+
target: ./.mason/threads
|
|
227
|
+
build:
|
|
228
|
+
producer:
|
|
229
|
+
cmd: [node, ./mason-builder.mjs]
|
|
230
|
+
timeoutMs: 600000
|
|
231
|
+
repair:
|
|
232
|
+
cmd: [node, ./mason-repair.mjs]
|
|
233
|
+
timeoutMs: 600000
|
|
201
234
|
gates: { defaultTimeoutMs: 120000, gates: [] }
|
|
202
|
-
|
|
203
|
-
|
|
235
|
+
maxAttempts: 3
|
|
236
|
+
validate: { agent: false }
|
|
204
237
|
gates: { defaultTimeoutMs: 900000, gates: [] }
|
|
238
|
+
feature:
|
|
239
|
+
tracker:
|
|
240
|
+
package: "@bluewombat/manager-fake"
|
|
241
|
+
options:
|
|
242
|
+
source: ./.mason/source
|
|
243
|
+
target: ./.mason/threads
|
|
205
244
|
authority:
|
|
206
245
|
enabled: false
|
|
207
246
|
timeoutMs: 600000
|
|
208
247
|
pollIntervalMs: 10000
|
|
209
248
|
```
|
|
210
249
|
|
|
250
|
+
The file reads by level, and each level in the order it is used: `ticket` and `feature` both hold
|
|
251
|
+
`tracker`, then what makes the work (`build` for a Ticket, `test` for a Feature), `validate`, `gates`, `fix`.
|
|
252
|
+
|
|
211
253
|
| Field | Meaning |
|
|
212
254
|
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
255
|
+
| `feature.tracker` | `package` (a name or path that exports `createManager`) and `options`: where Features are written and signalled |
|
|
256
|
+
| `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 |
|
|
215
257
|
| `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` |
|
|
258
|
+
| `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 |
|
|
216
259
|
| `workLine.isolation` | Package name or path exporting `strategy` — required. Host loads it like a manager; Isolator/Integrator do not sniff |
|
|
217
260
|
| `workLine.isolationOptions` | What that strategy is told, unread by Host — like a manager's `options`. `isolation-git` reads `exclude`, the paths a Child never gets (default `.env`, `.env.*`). A strategy without `createStrategy` refuses any |
|
|
218
261
|
| `workLine.warm` | Optional `cmd` / `timeoutMs`. Runs in the workspace after Isolation, before the Builder — typically `pnpm install --frozen-lockfile`. Absent: the Builder gets a cold tree |
|
|
@@ -223,26 +266,30 @@ pollIntervalMs: 10000
|
|
|
223
266
|
| `ledger` | WorkLedger root (created). The Cursor is a file inside it. Default `<home>/ledger` |
|
|
224
267
|
| `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 |
|
|
225
268
|
| `agents` | Agents a pass names instead of writing `cmd` (§ Agent passes written short) |
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
269
|
+
| `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 |
|
|
270
|
+
| `ticket.build.producer` | `cmd` / `timeoutMs`. Spawned in the workspace. Exit 0 runs `ticket.build.gates` |
|
|
271
|
+
| `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 |
|
|
272
|
+
| `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 |
|
|
273
|
+
| `ticket.build.maxAttempts` | Attempts one Task may start. Default 3 |
|
|
274
|
+
| `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` |
|
|
275
|
+
| `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. |
|
|
276
|
+
| `ticket.maxAttempts` | Attempts that one fix may start. Default 3 |
|
|
277
|
+
| `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 |
|
|
233
278
|
| `timeoutMs` | How long a child **outside** a Task may run: manager, isolations |
|
|
234
|
-
| `maxRefusals` | Times the work may be sent back before it escalates, by an Authority, by `
|
|
279
|
+
| `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 |
|
|
280
|
+
| `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` |
|
|
281
|
+
| `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 |
|
|
235
282
|
| `pollIntervalMs` | Set it and the process keeps draining until SIGINT. Absent: one tick. Pick it with § Choosing `pollIntervalMs` — 10000 is the floor worth having |
|
|
236
283
|
| `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 |
|
|
237
284
|
|
|
238
285
|
Every `cmd` is named where it is used, and nothing falls back to a neighbour: a
|
|
239
286
|
`repair` with no command of its own is refused rather than quietly running the
|
|
240
|
-
first-pass agent again. `
|
|
241
|
-
and, when that judgement refuses it, runs `
|
|
242
|
-
`
|
|
287
|
+
first-pass agent again. `ticket.build` makes the unit of work. `ticket` judges the whole
|
|
288
|
+
and, when that judgement refuses it, runs `ticket.fix` — not a second first-pass.
|
|
289
|
+
`ticket.validate`, when declared, is a second, local judge of the whole: it
|
|
243
290
|
runs after align, before the Authority sees the work (or before the final
|
|
244
|
-
fold, with none). Its refusal is repaired by `
|
|
245
|
-
Authority's is, and shares the same `maxRefusals` budget.
|
|
291
|
+
fold, with none). Its refusal is repaired by `ticket.fix` exactly as an
|
|
292
|
+
Authority's is, and shares the same `ticket.maxRefusals` budget.
|
|
246
293
|
|
|
247
294
|
Paths a slot **writes** — `--transcript-dir`, `--agent-home` and
|
|
248
295
|
`--plugins-out` — always resolve against the config file's directory, even
|
|
@@ -266,36 +313,39 @@ agents:
|
|
|
266
313
|
denyShell: [] # commands refused to the agent, e.g. [gh, vercel]; ssh and scp always are
|
|
267
314
|
transcripts: ./.mason/transcripts
|
|
268
315
|
pluginsOut: ./.mason/plugins # where deck profiles are built
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
316
|
+
ticket:
|
|
317
|
+
build:
|
|
318
|
+
producer:
|
|
319
|
+
agent: cursor
|
|
320
|
+
prompt: ./prompts/builder.md
|
|
321
|
+
homePlugins: [./ai-plugins/core/builder]
|
|
322
|
+
appDeckProfile: .ai/profiles/builder
|
|
323
|
+
args: [--mode, agent] # the vendor CLI's own flags
|
|
324
|
+
timeoutMs: 1200000
|
|
278
325
|
```
|
|
279
326
|
|
|
280
327
|
Host writes the `cmd` it stands for — `node <slots>/<role>.mjs --prompt-file …
|
|
281
|
-
-- node <slots>/agents/cursor.mjs --transcript-dir … --plugin-dir … --profile
|
|
328
|
+
-- node <slots>/agents/cursor.mjs --transcript-dir … --plugin-dir … --app-deck-profile
|
|
282
329
|
… --plugins-out … --agent-arg …` — with `<slots>` the
|
|
283
330
|
`node_modules/@bluewombat/slots` next to the config. Nothing past the config
|
|
284
|
-
knows a pass was written short. The roles: `
|
|
285
|
-
`
|
|
286
|
-
(an argv, or `{ agent, prompt?,
|
|
331
|
+
knows a pass was written short. The roles: `ticket.build.producer`,
|
|
332
|
+
`ticket.build.repair`, `ticket.fix`, `ticket.validate`, and `authority.describe`
|
|
333
|
+
(an argv, or `{ agent, prompt?, homePlugins?, appDeckProfile?, args? }`).
|
|
287
334
|
|
|
288
335
|
| Key | Meaning |
|
|
289
336
|
| --------- | --------------------------------------------------------------------------------------------------------- |
|
|
290
337
|
| `agent` | A name `agents` declares. With `cmd` beside it, the pass is refused |
|
|
291
338
|
| `prompt` | `--prompt-file` of the role slot. Absent: the role's built-in template |
|
|
292
339
|
| `rules` | `--rules-file` of the role slot |
|
|
293
|
-
| `
|
|
294
|
-
| `
|
|
340
|
+
| `homePlugins` | Plugin folders of the Mason home, each a `--plugin-dir` |
|
|
341
|
+
| `appDeckProfile` | The app's deck profile for this role, as a path: `[<app dir>/].ai/profiles/<name>`. Needs the agent's `pluginsOut`. See the slots' § Agents/Plugins |
|
|
295
342
|
| `args` | Appended to the agent's own `args`, each an `--agent-arg` |
|
|
296
343
|
|
|
297
|
-
|
|
298
|
-
base plugins
|
|
344
|
+
An `appDeckProfile` is read in the app, not in the Mason home: the role gets the
|
|
345
|
+
app's base plugins, that profile's plugins and its `prompt.md`, as the app's
|
|
346
|
+
committed deck locks pin them. deck keeps profiles in `<app>/.ai/profiles/<name>`,
|
|
347
|
+
so the path has that shape; `<app dir>` is where the app's `.ai/` is in the
|
|
348
|
+
checkout (`.` by default, `apps/web` in a monorepo). It stays inside the checkout.
|
|
299
349
|
`mason doctor` checks those locks in the work line without the network.
|
|
300
350
|
|
|
301
351
|
Every tracker field lives in the `options` of its side. Host does not read them.
|
|
@@ -375,20 +425,20 @@ Examples ship in [`@bluewombat/slots`](../../plugins/slots/README.md), installed
|
|
|
375
425
|
the shipped roles and agents (Cursor CLI, Claude Code), the shipped Gates, and
|
|
376
426
|
their options. What follows is what Host guarantees them.
|
|
377
427
|
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
428
|
+
```yaml
|
|
429
|
+
ticket:
|
|
430
|
+
build:
|
|
431
|
+
producer:
|
|
432
|
+
cmd:
|
|
433
|
+
- node
|
|
434
|
+
- ./node_modules/@bluewombat/slots/builders/producer.mjs
|
|
435
|
+
- --
|
|
436
|
+
- node
|
|
437
|
+
- ./node_modules/@bluewombat/slots/agents/claude.mjs
|
|
438
|
+
timeoutMs: 600000
|
|
389
439
|
```
|
|
390
440
|
|
|
391
|
-
`
|
|
441
|
+
`ticket.build.repair` is required too, with its own `cmd` and `timeoutMs`. The
|
|
392
442
|
composition with `--` is the same.
|
|
393
443
|
|
|
394
444
|
### Builder
|
|
@@ -406,15 +456,15 @@ The config always names a producer (and a repair). Implementer can still run a
|
|
|
406
456
|
Task as its Gate sequence alone — that is the first judgement of the assembled
|
|
407
457
|
ticket, which makes nothing.
|
|
408
458
|
|
|
409
|
-
###
|
|
459
|
+
### Ticket fix
|
|
410
460
|
|
|
411
461
|
Spawned in the ticket workspace when a judgement of the whole refused it
|
|
412
462
|
(`--report`, `--report-from`). Same argv as a Builder otherwise. Host hands
|
|
413
|
-
Implementer `
|
|
463
|
+
Implementer `ticket.fix` as that pass's only agent: there is no first-pass
|
|
414
464
|
producer at this moment. The refusal it repairs may have come from an
|
|
415
|
-
Authority or from `
|
|
465
|
+
Authority or from `ticket.validate`.
|
|
416
466
|
|
|
417
|
-
###
|
|
467
|
+
### Ticket validate
|
|
418
468
|
|
|
419
469
|
Spawned in the ticket workspace after align, with or without an Authority —
|
|
420
470
|
`--intention`, `--id`, `--attempt`, `--context`. No `--report`: it is a
|
|
@@ -425,23 +475,24 @@ that completes cleanly but finds a problem is still a refusal.
|
|
|
425
475
|
|
|
426
476
|
### Gates
|
|
427
477
|
|
|
428
|
-
Four
|
|
429
|
-
`
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
478
|
+
Four sequences: `ticket.build.gates`, shared by `ticket.build.producer` and
|
|
479
|
+
`ticket.build.repair` (a repair is judged by exactly what judged the first try),
|
|
480
|
+
then one per assembly role — `ticket.fix`, `ticket.validate` — plus
|
|
481
|
+
`ticket.gates` for the one judgement-only pass with no producer of its own
|
|
482
|
+
(re-checking the assembled ticket; `ci-green` reads the checks the work line
|
|
483
|
+
ran on what the Publisher placed, so it belongs there). The assembly lists are
|
|
484
|
+
not shared with each other or with the builder, and no dispatch chooses between
|
|
485
|
+
two buckets: a check right for one pass is not necessarily right for another,
|
|
486
|
+
even within the same stage. `gates` written on `ticket.build.producer` or
|
|
487
|
+
`ticket.build.repair` is refused, and the message names `ticket.build.gates`.
|
|
488
|
+
|
|
489
|
+
Implementer's Gates are told `--stage unit` or `--stage ticket` alongside
|
|
439
490
|
`--id`, `--attempt`, `--gate-id` and `--intention`.
|
|
440
491
|
Most have no use for it. `workspace-changed` does: "nothing changed" means
|
|
441
492
|
nothing was done in a unit, and is the correct outcome for an assembly that
|
|
442
493
|
needed nothing.
|
|
443
494
|
|
|
444
|
-
`init` writes an empty list on `
|
|
495
|
+
`init` writes an empty list on `ticket.build.producer` and `ticket.build.repair`; add a
|
|
445
496
|
Gate when the Project has a check.
|
|
446
497
|
|
|
447
498
|
Every child carries its own ceiling and no other. A Gate entry may name a
|
|
@@ -451,11 +502,11 @@ from what another child left behind, so a slow producer cannot leave its
|
|
|
451
502
|
Gates a millisecond to answer in.
|
|
452
503
|
|
|
453
504
|
What bounds the unit is therefore what you wrote:
|
|
454
|
-
`
|
|
505
|
+
`ticket.build.maxAttempts × (ticket.build.producer.timeoutMs + Σ ticket.build.gates timeouts)`
|
|
455
506
|
for a first pass, and the same shape with `repair` in place of `producer` once
|
|
456
507
|
a Gate has refused one.
|
|
457
508
|
A repair and an assembly fix each carry their own `timeoutMs`. An assembly fix
|
|
458
|
-
is bounded the same way from `
|
|
509
|
+
is bounded the same way from `ticket.maxAttempts` and `ticket.fix`.
|
|
459
510
|
|
|
460
511
|
```json
|
|
461
512
|
{
|
|
@@ -471,6 +522,53 @@ is bounded the same way from `assembly.maxAttempts` and `assembly.fix`.
|
|
|
471
522
|
}
|
|
472
523
|
```
|
|
473
524
|
|
|
525
|
+
### The feature-test acceptance check
|
|
526
|
+
|
|
527
|
+
A Project proves each Feature by its own tests, run once on the assembled Feature
|
|
528
|
+
line, when its config says so, by a pair:
|
|
529
|
+
|
|
530
|
+
```yaml
|
|
531
|
+
feature:
|
|
532
|
+
test: # makes the Ticket that holds the suite (label `feature-test` on the tracker)
|
|
533
|
+
agent: claude
|
|
534
|
+
prompt: ./prompts/feature-test.md # {{task}} is the scenarios; {{report}}, {{refused_by}}, {{refusal}} on a refused pass
|
|
535
|
+
timeoutMs: 1800000
|
|
536
|
+
gates: # form only: it compiles, one test per manifest id, no application file touched
|
|
537
|
+
gates:
|
|
538
|
+
- { id: feature-tests-form, argv: [...], timeoutMs: 120000 }
|
|
539
|
+
validate:
|
|
540
|
+
agent: false
|
|
541
|
+
gates:
|
|
542
|
+
defaultTimeoutMs: 120000
|
|
543
|
+
gates:
|
|
544
|
+
- id: feature-tests
|
|
545
|
+
skipWhen: skip-feature-test # Host does not run this Gate for a Feature that carries the label
|
|
546
|
+
timeoutMs: 720000
|
|
547
|
+
argv:
|
|
548
|
+
- node
|
|
549
|
+
- ./node_modules/@bluewombat/slots/gates/feature-tests.mjs
|
|
550
|
+
- --run
|
|
551
|
+
- pnpm test --grep "{ids}" --reporter=junit --outputFile={junit}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`skipWhen` is the one condition Host reads for a Gate, on a Feature's Gates only
|
|
555
|
+
(`feature.validate`, `feature.gates`, `feature.fix`); its one value today is
|
|
556
|
+
`skip-feature-test`, whatever the tracker calls that label. A Gate left out is not a Gate that
|
|
557
|
+
passed: the journal gets `gate-skipped` and the trace says "`feature-test` not run: the Feature carries
|
|
558
|
+
skip-feature-test". The Gate knows nothing of the skip and no environment variable carries it.
|
|
559
|
+
|
|
560
|
+
The tracker side: a Ticket carrying the feature-test label (`featureTestLabel` of the TicketTracker manager) is
|
|
561
|
+
made by `feature.test`; a Feature carrying the skip label
|
|
562
|
+
(`skipFeatureTestLabel` of the FeatureTracker manager) has that Ticket cancelled, with a line in
|
|
563
|
+
the trace. The label is read again until the Feature is submitted. A Project that declares the
|
|
564
|
+
check refuses a Feature that holds neither a feature-test Ticket nor the skip label, or whose feature-test
|
|
565
|
+
Ticket is not blocked by every other Ticket, as `invalid`, with the reason as a comment.
|
|
566
|
+
|
|
567
|
+
What the Gate proves is a red/green verdict; what the operator reads is its text in the
|
|
568
|
+
journal (`gate-finished`) — a red suite is also in the reason of the refusal the fix is
|
|
569
|
+
given and in the escalation. A passing suite's text is not shown on the pull request: see
|
|
570
|
+
RESERVATIONS I17.
|
|
571
|
+
|
|
474
572
|
### Describer
|
|
475
573
|
|
|
476
574
|
How a delivered ticket describes itself — the words the manager puts on the
|
|
@@ -543,31 +641,34 @@ it each stop `mason run` at boot, and `mason doctor` reports the same offline
|
|
|
543
641
|
and is the operator's directory.
|
|
544
642
|
|
|
545
643
|
```
|
|
546
|
-
assembled → align →
|
|
547
|
-
|
|
644
|
+
assembled → align → recheck (ticket.build.gates again, on the aligned work)
|
|
645
|
+
conflict or failed → ticket.fix, then align again
|
|
646
|
+
→ ticket.validate (optional, local)
|
|
647
|
+
refused → ticket.fix, then align again, recheck, validate again
|
|
548
648
|
accepted → Publisher places the work → manager opens the Submission
|
|
549
649
|
→ submitted, and the run comes back to it on each poll
|
|
550
|
-
|
|
551
|
-
refused →
|
|
552
|
-
accepted → the
|
|
650
|
+
ticket.gates judge what was published
|
|
651
|
+
refused → ticket.fix, then the same Submission is offered again
|
|
652
|
+
accepted → has the line moved on since the align? yes: align again
|
|
653
|
+
no: the Authority folds it → done (a refused fold also aligns again)
|
|
553
654
|
```
|
|
554
655
|
|
|
555
|
-
Neither half judges: that is what `
|
|
556
|
-
and folds; a Gate says whether the fold may happen. `
|
|
656
|
+
Neither half judges: that is what `ticket.gates` are for. The Authority places
|
|
657
|
+
and folds; a Gate says whether the fold may happen. `ticket.validate`, when
|
|
557
658
|
declared, judges before either half — the same verdict has the same fate
|
|
558
659
|
whether or not there is an Authority to offer the work to afterwards. After the
|
|
559
660
|
Authority folds, Host brings its copy of the work line up to date with a
|
|
560
661
|
fast-forward — the work line moved where the Authority is, and the next
|
|
561
662
|
Isolation must start from that version, not from a copy left behind.
|
|
562
663
|
|
|
563
|
-
A refusal is not a second failure path: its report reaches `
|
|
564
|
-
exactly as a Gate's report reaches `
|
|
664
|
+
A refusal is not a second failure path: its report reaches `ticket.fix`
|
|
665
|
+
exactly as a Gate's report reaches `ticket.build.repair`. After `ticket.maxRefusals`
|
|
565
666
|
refusals the ticket escalates with that report as its Trace, and `done` names
|
|
566
667
|
the Submission rather than a directory on the machine that ran.
|
|
567
668
|
|
|
568
669
|
## Write another manager
|
|
569
670
|
|
|
570
|
-
Host loads whatever `
|
|
671
|
+
Host loads whatever `feature.tracker.package` and `ticket.tracker.package` name, as long as it exports `createManager`. The
|
|
571
672
|
contract and a worked example are in
|
|
572
673
|
[`@bluewombat/manager-kit`](../manager-kit/README.md). Nothing in Host changes.
|
|
573
674
|
|
|
@@ -636,7 +737,7 @@ change that breaks it does not merge.
|
|
|
636
737
|
| `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 |
|
|
637
738
|
| `plugins/` | Load a package by name and check its shape: a manager, an isolation strategy | What to do with the loaded thing | `config` |
|
|
638
739
|
| `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 |
|
|
639
|
-
| `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 |
|
|
740
|
+
| `operator/` | Tools for a human: `init`, `setup`, `doctor`, `watch` / `status`, `log`, `incident`, `cancel`, `restart`, `release`, `board`, the terminal prompt | Anything the loop needs — the loop cannot see this layer | everything below |
|
|
640
741
|
| `index.ts`, `cli.ts` | The package surface and the dispatch. Nothing else sits at the top of `src/` | Logic | everything |
|
|
641
742
|
|
|
642
743
|
Host's code names no plugin. A manager, an isolation strategy, a persistence
|
|
@@ -691,6 +792,6 @@ src/
|
|
|
691
792
|
config/ argv, JSON config, config discovery → HostOptions; defaults: the plugin names Host ships with
|
|
692
793
|
plugins/ load: the one loader; manager, isolation, persist: one shape each; discover
|
|
693
794
|
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
|
|
694
|
-
operator/ run, init, setup, doctor, explain, live (watch / status), log, film, cancel, release, board, prompt, git-remote
|
|
795
|
+
operator/ run, init, setup, doctor, explain, live (watch / status), log, incident, film, cancel, release, board, prompt, git-remote
|
|
695
796
|
fixtures/ stand-in slots for the loop's tests
|
|
696
797
|
```
|