@bluewombat/runtime 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +126 -100
  2. package/SPECS.md +103 -84
  3. package/dist/cli.js +32 -34
  4. package/dist/cli.js.map +1 -1
  5. package/dist/config/agent-shorthand.d.ts +2 -1
  6. package/dist/config/agent-shorthand.d.ts.map +1 -1
  7. package/dist/config/agent-shorthand.js +30 -9
  8. package/dist/config/agent-shorthand.js.map +1 -1
  9. package/dist/config/defaults.d.ts +0 -2
  10. package/dist/config/defaults.d.ts.map +1 -1
  11. package/dist/config/defaults.js +0 -2
  12. package/dist/config/defaults.js.map +1 -1
  13. package/dist/config/find-config.d.ts +3 -2
  14. package/dist/config/find-config.d.ts.map +1 -1
  15. package/dist/config/find-config.js +4 -4
  16. package/dist/config/find-config.js.map +1 -1
  17. package/dist/config/home.js +2 -2
  18. package/dist/config/home.js.map +1 -1
  19. package/dist/config/load-config.d.ts +2 -2
  20. package/dist/config/load-config.d.ts.map +1 -1
  21. package/dist/config/load-config.js +46 -29
  22. package/dist/config/load-config.js.map +1 -1
  23. package/dist/config/parse-args.d.ts.map +1 -1
  24. package/dist/config/parse-args.js +50 -48
  25. package/dist/config/parse-args.js.map +1 -1
  26. package/dist/config/types.d.ts +40 -24
  27. package/dist/config/types.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/loop/authority.d.ts +3 -4
  33. package/dist/loop/authority.d.ts.map +1 -1
  34. package/dist/loop/authority.js +27 -22
  35. package/dist/loop/authority.js.map +1 -1
  36. package/dist/loop/context.d.ts +8 -3
  37. package/dist/loop/context.d.ts.map +1 -1
  38. package/dist/loop/deliveries.d.ts +16 -16
  39. package/dist/loop/deliveries.d.ts.map +1 -1
  40. package/dist/loop/deliveries.js +35 -187
  41. package/dist/loop/deliveries.js.map +1 -1
  42. package/dist/loop/describe.d.ts +4 -6
  43. package/dist/loop/describe.d.ts.map +1 -1
  44. package/dist/loop/describe.js +2 -2
  45. package/dist/loop/describe.js.map +1 -1
  46. package/dist/loop/drive.d.ts +4 -6
  47. package/dist/loop/drive.d.ts.map +1 -1
  48. package/dist/loop/drive.js +9 -33
  49. package/dist/loop/drive.js.map +1 -1
  50. package/dist/loop/features.d.ts +23 -0
  51. package/dist/loop/features.d.ts.map +1 -0
  52. package/dist/loop/features.js +571 -0
  53. package/dist/loop/features.js.map +1 -0
  54. package/dist/loop/journal.d.ts +1 -1
  55. package/dist/loop/journal.js +2 -2
  56. package/dist/loop/journal.js.map +1 -1
  57. package/dist/loop/lock.d.ts +1 -1
  58. package/dist/loop/lock.js +1 -1
  59. package/dist/loop/open-host.d.ts.map +1 -1
  60. package/dist/loop/open-host.js +23 -24
  61. package/dist/loop/open-host.js.map +1 -1
  62. package/dist/loop/probe.d.ts +1 -1
  63. package/dist/loop/probe.d.ts.map +1 -1
  64. package/dist/loop/probe.js +51 -22
  65. package/dist/loop/probe.js.map +1 -1
  66. package/dist/loop/process-tree.d.ts +1 -1
  67. package/dist/loop/process-tree.js +1 -1
  68. package/dist/loop/report.d.ts +9 -7
  69. package/dist/loop/report.d.ts.map +1 -1
  70. package/dist/loop/report.js +30 -99
  71. package/dist/loop/report.js.map +1 -1
  72. package/dist/loop/streams.d.ts +5 -5
  73. package/dist/loop/streams.d.ts.map +1 -1
  74. package/dist/loop/streams.js +3 -3
  75. package/dist/loop/streams.js.map +1 -1
  76. package/dist/loop/tick.d.ts +6 -4
  77. package/dist/loop/tick.d.ts.map +1 -1
  78. package/dist/loop/tick.js +50 -78
  79. package/dist/loop/tick.js.map +1 -1
  80. package/dist/loop/trace.d.ts +5 -5
  81. package/dist/loop/trace.d.ts.map +1 -1
  82. package/dist/loop/trace.js +5 -14
  83. package/dist/loop/trace.js.map +1 -1
  84. package/dist/loop/transformers.d.ts +3 -13
  85. package/dist/loop/transformers.d.ts.map +1 -1
  86. package/dist/loop/transformers.js +3 -53
  87. package/dist/loop/transformers.js.map +1 -1
  88. package/dist/loop/warm.d.ts +3 -3
  89. package/dist/loop/warm.js +3 -3
  90. package/dist/loop/work-line.d.ts +1 -1
  91. package/dist/operator/agent-plugins.d.ts +11 -0
  92. package/dist/operator/agent-plugins.d.ts.map +1 -1
  93. package/dist/operator/agent-plugins.js +3 -3
  94. package/dist/operator/agent-plugins.js.map +1 -1
  95. package/dist/operator/board.d.ts +3 -8
  96. package/dist/operator/board.d.ts.map +1 -1
  97. package/dist/operator/board.js +2 -11
  98. package/dist/operator/board.js.map +1 -1
  99. package/dist/operator/cancel.d.ts +2 -2
  100. package/dist/operator/cancel.js +6 -6
  101. package/dist/operator/cancel.js.map +1 -1
  102. package/dist/operator/doctor.d.ts +9 -0
  103. package/dist/operator/doctor.d.ts.map +1 -1
  104. package/dist/operator/doctor.js +32 -25
  105. package/dist/operator/doctor.js.map +1 -1
  106. package/dist/operator/explain.d.ts +76 -0
  107. package/dist/operator/explain.d.ts.map +1 -0
  108. package/dist/operator/explain.js +556 -0
  109. package/dist/operator/explain.js.map +1 -0
  110. package/dist/operator/film.d.ts +3 -11
  111. package/dist/operator/film.d.ts.map +1 -1
  112. package/dist/operator/film.js +8 -90
  113. package/dist/operator/film.js.map +1 -1
  114. package/dist/operator/init.d.ts.map +1 -1
  115. package/dist/operator/init.js +41 -49
  116. package/dist/operator/init.js.map +1 -1
  117. package/dist/operator/live.js +1 -1
  118. package/dist/operator/live.js.map +1 -1
  119. package/dist/operator/log.js +3 -4
  120. package/dist/operator/log.js.map +1 -1
  121. package/dist/operator/release.d.ts +2 -2
  122. package/dist/operator/release.js +7 -7
  123. package/dist/operator/release.js.map +1 -1
  124. package/dist/operator/run.d.ts +25 -0
  125. package/dist/operator/run.d.ts.map +1 -0
  126. package/dist/operator/run.js +70 -0
  127. package/dist/operator/run.js.map +1 -0
  128. package/dist/operator/setup.d.ts.map +1 -1
  129. package/dist/operator/setup.js +20 -16
  130. package/dist/operator/setup.js.map +1 -1
  131. package/dist/plugins/manager.d.ts +33 -2
  132. package/dist/plugins/manager.d.ts.map +1 -1
  133. package/dist/plugins/manager.js +67 -0
  134. package/dist/plugins/manager.js.map +1 -1
  135. package/package.json +15 -16
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Host: the process you run, `@bluewombat/runtime`, binary `mason`. It listens,
4
4
  admits, calls Conductor, and reports. It does not know a tracker: it loads one
5
- FeatureManager package and hands it the options it never reads. Contract:
5
+ TicketTracker package and hands it the options it never reads. Contract:
6
6
  [SPECS.md](./SPECS.md).
7
7
 
8
8
  ## Install
@@ -24,12 +24,11 @@ npx mason run
24
24
  npx mason watch
25
25
  ```
26
26
 
27
- A script gets the same result with no questions — `--manager` turns them off:
27
+ A script gets the same result with no questions — `--manager` turns them off (it names the one package that answers both sides):
28
28
 
29
29
  ```bash
30
30
  mason init --manager @bluewombat/manager-github \
31
31
  --manager-option repo=owner/name \
32
- --manager-option labels=mason \
33
32
  --work-line-stable ./work-line-stable \
34
33
  --clone git@github.com:owner/name.git --branch dev
35
34
  mason setup --apply
@@ -52,15 +51,43 @@ package's README — Host does not grow a `--repo` flag for it.
52
51
 
53
52
  | Command | Job |
54
53
  | --------------- | -------------------------------------------------------------------------------------- |
55
- | `mason run` | Drain the FeatureManager, run Conductor, report |
54
+ | `mason run` | Drain the TicketTracker, run Conductor, report |
56
55
  | `mason watch` | Snapshot the ledger and follow the journal. Ctrl-C leaves the worker running |
57
56
  | `mason status` | Snapshot the ledger, then exit. `--json` for scripts |
58
57
  | `mason log` | Replay the journal, with the reason a phase stopped. `--problems` for just those |
59
58
  | `mason init` | Set this directory up: asks, writes the config and the Builder stub, then runs `setup` |
60
59
  | `mason setup` | Prepare the tracker. A plan by default; `--apply` writes. Exit 1 on a blocked step |
61
60
  | `mason doctor` | Check the config, the slots, and the manager. Exit 1 on any failure |
62
- | `mason cancel` | Abandon a Feature by key, when the tracker cannot say it is gone |
63
- | `mason release` | Drop a held Subtask without waiting for the bail clock |
61
+ | `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
+ | `mason cancel` | Abandon a Ticket by key, when the tracker cannot say it is gone |
63
+ | `mason release` | Drop a held unit without waiting for the bail clock |
64
+
65
+ ### What an agent role starts with
66
+
67
+ ```bash
68
+ mason explain builder.producer # one role; no role names all of them
69
+ mason explain --json # the same facts for a script
70
+ ```
71
+
72
+ It reads the config, the role's prompt file, the plugin folders of the home, and — for the
73
+ project's side — the deck requests and locks of the work line as it was last refreshed. It runs
74
+ no agent and fetches nothing. Each line says where it comes from: `[home]` for the role you wrote
75
+ in the Mason home, `[base]` and `[profile]` for the project's deck setup, read from the
76
+ `ai-plugin.lock.json` the project committed (which lists the skills, agents, hooks and MCP servers
77
+ of each plugin, so the plugin does not have to be fetched to be described).
78
+
79
+ What cannot be known is said, not guessed:
80
+
81
+ - before the work line exists, the project's side is "not known yet";
82
+ - a request no committed lock pins, a prompt that does not place a required `{{placeholder}}` or
83
+ uses one the role does not fill, an unreadable prompt file, a missing plugin folder: `FAIL`
84
+ (exit 1), because every attempt of that role would fail the same way;
85
+ - a lock of a version it does not read, a missing profile, two plugins bringing a skill of the
86
+ same name: `warn`;
87
+ - the key of the agent is never printed, only whether its variable is set;
88
+ - a role written as a plain command is said to be one;
89
+ - the final prompt of a ticket, and what the agent's own tool reads from the checkout, are listed
90
+ as not covered.
64
91
 
65
92
  One `run` per ledger: `run` writes `<ledger>/lock` with its pid and a second
66
93
  `run` on the same home refuses to start while that process lives (two runs
@@ -74,15 +101,33 @@ steps it is handed. A manager without `setupManager` says it has nothing to do.
74
101
  `init` takes `--manager NAME` (which turns the questions off), `--manager-option KEY=VALUE`,
75
102
  `--work-line-stable DIR`, `--clone REMOTE`, `--branch NAME`, `--force` and `--interactive`.
76
103
 
104
+ `mason run` speaks before it does anything, so a terminal is never blank: it says that it
105
+ is starting, then the config, the trackers, the ledger and the poll interval, then that it is
106
+ opening the host and that it has started. A tick with work prints one line per step; a loop
107
+ with nothing to do says once a minute that it is polling; a tracker that cannot be read is
108
+ named on every poll. The last line says that it stopped and with which exit code, after the
109
+ error message on stderr when there was one.
110
+
111
+ ```
112
+ 10:28:50 run starting
113
+ 10:28:50 config /path/to/home
114
+ 10:28:50 trackers features @bluewombat/manager-github, tickets @bluewombat/manager-github
115
+ 10:28:50 ledger /path/to/home/.mason/ledger
116
+ 10:28:50 poll every 10s
117
+ 10:28:50 opening ledger, lock, work line and trackers
118
+ 10:28:51 started pid 4242, Ctrl-C to stop
119
+ 10:29:51 waiting nothing to do: 6 poll(s) in 60s
120
+ ```
121
+
77
122
  Leave `mason run` going in one terminal. In another:
78
123
 
79
124
  ```bash
80
125
  mason watch
81
126
  ```
82
127
 
83
- That is the operator surface: the durable state of every Feature, then the film
128
+ That is the operator surface: the durable state of every Ticket, then the film
84
129
  of the current step (`submitted` included). It does not talk to the tracker, and
85
- it cannot `ready` or claim. `status` is the same snapshot without the
130
+ it cannot `resume` or claim. `status` is the same snapshot without the
86
131
  follow. `--json` prints that snapshot as JSON.
87
132
 
88
133
  When a run only printed `unavailable:<key>`, the reason is already in the
@@ -95,19 +140,18 @@ mason log --problems
95
140
  `--all` keeps the idle polls. `--tail N` limits the text film (`0` shows every
96
141
  kept line). `--json` prints every kept line; with `--tail`, a truncated reply
97
142
  names how many were kept and how many are shown. The closing tally counts each
98
- distinct failure. A pause with no planner exit code is counted apart from a
99
- non-zero exit. If a slot was started with `--transcript-dir`, the newest
143
+ distinct failure. If a slot was started with `--transcript-dir`, the newest
100
144
  transcript files are named underneath, with the agent exit and duration read
101
145
  from the start of each file.
102
146
 
103
- To abandon a Feature the tracker can no longer mention (a deleted issue, a
147
+ To abandon a Ticket the tracker can no longer mention (a deleted issue, a
104
148
  manager with no `probe`), stop `run` if it holds the ledger lock, then:
105
149
 
106
150
  ```bash
107
151
  mason cancel github:owner/name#19
108
152
  ```
109
153
 
110
- After a crash left a Subtask `running` with nobody on it, stop `run` if it
154
+ After a crash left a unit `running` with nobody on it, stop `run` if it
111
155
  holds the lock, then:
112
156
 
113
157
  ```bash
@@ -122,7 +166,7 @@ crash — the ledger is. The file can grow; there is no rotation yet.
122
166
  `mason.config.yaml`, found by walking up from the working directory. `--config
123
167
  FILE` names another one. Relative paths resolve against the file; relative
124
168
  flag paths resolve against the working directory. Flags override the file, and
125
- `--manager-option` overrides one manager option without dropping the others.
169
+ `--feature-tracker-option` and `--ticket-tracker-option` override one option of that side without dropping the others.
126
170
  Parsed with the `yaml` package (YAML 1.2 core schema), so a repeated key is
127
171
  refused rather than silently keeping the last one, and `no` / `on` / `off`
128
172
  stay plain strings — never coerced to a boolean the way YAML 1.1 would. A
@@ -130,22 +174,22 @@ value that starts with `@`, like every `@bluewombat/...` package name, needs
130
174
  quotes: `@` is a reserved YAML indicator at the start of a plain scalar.
131
175
 
132
176
  ```yaml
133
- manager: "@bluewombat/manager-fake"
134
- managerOptions:
135
- source: ./.mason/source
136
- target: ./.mason/threads
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
137
187
  workLine:
138
188
  stable: ./work-line-stable
139
189
  isolation: "@bluewombat/isolation-copy"
140
190
  warm:
141
191
  cmd: [pnpm, install, --frozen-lockfile]
142
192
  timeoutMs: 600000
143
- planner:
144
- cmd:
145
- - node
146
- - ./node_modules/@bluewombat/slots/planners/one-subtask.mjs
147
- timeoutMs: 600000
148
- gates: { defaultTimeoutMs: 120000, gates: [] }
149
193
  builder:
150
194
  producer:
151
195
  cmd: [node, ./mason-builder.mjs]
@@ -166,35 +210,34 @@ pollIntervalMs: 10000
166
210
 
167
211
  | Field | Meaning |
168
212
  | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
169
- | `manager` | Package name or path that exports `createManager` |
170
- | `managerOptions` | Opaque to Host. What it holds is the manager's own documentation |
213
+ | `featureTracker` | `package` (a name or path that exports `createManager`) and `options`: where Features are written and signalled |
214
+ | `ticketTracker` | Same shape: where the Tickets of a Feature are, and the Authority that holds the lines. May name the same package as `featureTracker`, or another |
171
215
  | `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` |
172
216
  | `workLine.isolation` | Package name or path exporting `strategy` — required. Host loads it like a manager; Isolator/Integrator do not sniff |
173
- | `workLine.isolationOptions` | What that strategy is told, unread by Host — like `managerOptions`. `isolation-git` reads `exclude`, the paths a Child never gets (default `.env`, `.env.*`). A strategy without `createStrategy` refuses any |
174
- | `workLine.warm` | Optional `cmd` / `timeoutMs`. Runs once in the Feature workspace after Isolation, before any Subtask is copied from it — typically `pnpm install --frozen-lockfile`. Absent: Subtasks inherit a cold Feature |
217
+ | `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
+ | `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 |
175
219
  | `workLine.branch` | The work line a fold lands on. Without it, what the reference names, or else whatever is checked out |
176
220
  | `authority` | `enabled`, the `publish` / `refresh` slots, and the optional `describe` slot (§ Describer). Absent: the fold is local |
177
221
  | `home` | Where Host keeps what is its own — default `./.mason` |
178
- | `workspaceRoot` | Isolated feature / Subtask directories (created). Default `<home>/workspaces` |
222
+ | `workspaceRoot` | Isolated directories (created). Default `<home>/workspaces` |
179
223
  | `ledger` | WorkLedger root (created). The Cursor is a file inside it. Default `<home>/ledger` |
180
224
  | `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 |
181
225
  | `agents` | Agents a pass names instead of writing `cmd` (§ Agent passes written short) |
182
- | `planner` | `cmd` / `timeoutMs` / `gates`. FeatureStandard in, a Plan out. Its own `gates` judges `workLineStable` once a Plan is otherwise accepted — not where the Planner agent itself reads from |
183
- | `builder.producer` | `cmd` / `timeoutMs` / `gates`. Spawned in the Subtask workspace. Exit 0 runs its own `gates` |
226
+ | `builder.producer` | `cmd` / `timeoutMs` / `gates`. Spawned in the workspace. Exit 0 runs its own `gates` |
184
227
  | `builder.repair` | The same, for a pass a Gate refused. **Required**, and never inherited — including its `gates`: a repair with none of its own runs ungated, even when `builder.producer.gates` is not empty |
185
228
  | `builder.maxAttempts` | Attempts one Task may start. Default 3 |
186
229
  | `assembly.fix` | `cmd` / `timeoutMs` / `gates`. Spawned when a judgement of the whole refused it — by an Authority, by `assembly.validate`, or by both. Judged by its own `gates` |
187
- | `assembly.validate` | `cmd` / `timeoutMs` / `gates`. Optional. A local, read-only judge of the assembled feature, run after align, with or without an Authority. A refusal is repaired by `assembly.fix` the same as an Authority's. Judged by its own `gates`, never `builder`'s or `assembly.fix`'s |
230
+ | `assembly.validate` | `cmd` / `timeoutMs` / `gates`. Optional. A local, read-only judge of the assembled ticket, run after align, with or without an Authority. A refusal is repaired by `assembly.fix` the same as an Authority's. Judged by its own `gates`, never `builder`'s or `assembly.fix`'s |
188
231
  | `assembly.maxAttempts` | Attempts that one fix may start. Default 3 |
189
- | `assembly.gates` | `defaultTimeoutMs` and `gates`: the produce:false judgement-only pass — no producer of its own, re-checking the assembled feature. Empty: the Authority judges alone. Independent of `assembly.fix.gates` / `assembly.validate.gates` — never shared with either |
232
+ | `assembly.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 `assembly.fix.gates` / `assembly.validate.gates` — never shared with either |
190
233
  | `timeoutMs` | How long a child **outside** a Task may run: manager, isolations |
191
234
  | `maxRefusals` | Times the work may be sent back before it escalates, by an Authority, by `assembly.validate`, or by both — one shared budget. Default 3 |
192
235
  | `pollIntervalMs` | Set it and the process keeps draining until SIGINT. Absent: one tick. Pick it with § Choosing `pollIntervalMs` — 10000 is the floor worth having |
193
- | `observability.streams` | `enabled`, `dir`, `keep`. Films what every child says, as it says it — one file per child under `<dir>/<feature>/`, 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 |
236
+ | `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 |
194
237
 
195
238
  Every `cmd` is named where it is used, and nothing falls back to a neighbour: a
196
239
  `repair` with no command of its own is refused rather than quietly running the
197
- first-pass agent again. `builder` makes a Subtask. `assembly` judges the whole
240
+ first-pass agent again. `builder` makes the unit of work. `assembly` judges the whole
198
241
  and, when that judgement refuses it, runs `assembly.fix` — not a second first-pass.
199
242
  `assembly.validate`, when declared, is a second, local judge of the whole: it
200
243
  runs after align, before the Authority sees the work (or before the final
@@ -219,22 +262,17 @@ agents:
219
262
  adapter: cursor # or claude, or the path of an agent wrapper
220
263
  home: ./.mason/agent-home # with apiKeyEnv: isolate the vendor (Cursor only)
221
264
  apiKeyEnv: CURSOR_API_KEY
265
+ passEnv: [] # operator variables the agent also gets; it otherwise has a short list
266
+ denyShell: [] # commands refused to the agent, e.g. [gh, vercel]; ssh and scp always are
222
267
  transcripts: ./.mason/transcripts
223
268
  pluginsOut: ./.mason/plugins # where deck profiles are built
224
- planner:
225
- agent: cursor
226
- prompt: ./prompts/planner.md
227
- read: ./.mason/work-line # planner only
228
- plugins: [./ai-plugins/core/planner]
229
- profile: planner
230
- args: [--plan] # the vendor CLI's own flags
231
- timeoutMs: 600000
232
269
  builder:
233
270
  producer:
234
271
  agent: cursor
235
272
  prompt: ./prompts/builder.md
236
273
  plugins: [./ai-plugins/core/builder]
237
274
  profile: builder
275
+ args: [--mode, agent] # the vendor CLI's own flags
238
276
  timeoutMs: 1200000
239
277
  gates: { defaultTimeoutMs: 120000, gates: [] }
240
278
  ```
@@ -243,16 +281,15 @@ Host writes the `cmd` it stands for — `node <slots>/<role>.mjs --prompt-file
243
281
  -- node <slots>/agents/cursor.mjs --transcript-dir … --plugin-dir … --profile
244
282
  … --plugins-out … --agent-arg …` — with `<slots>` the
245
283
  `node_modules/@bluewombat/slots` next to the config. Nothing past the config
246
- knows a pass was written short. The roles: `planner`, `builder.producer`,
284
+ knows a pass was written short. The roles: `builder.producer`,
247
285
  `builder.repair`, `assembly.fix`, `assembly.validate`, and `authority.describe`
248
- (an argv as before, or `{ agent, prompt?, plugins?, profile?, args? }`).
286
+ (an argv, or `{ agent, prompt?, plugins?, profile?, args? }`).
249
287
 
250
288
  | Key | Meaning |
251
289
  | --------- | --------------------------------------------------------------------------------------------------------- |
252
290
  | `agent` | A name `agents` declares. With `cmd` beside it, the pass is refused |
253
291
  | `prompt` | `--prompt-file` of the role slot. Absent: the role's built-in template |
254
292
  | `rules` | `--rules-file` of the role slot |
255
- | `read` | Planner only: the directory the Planner agent reads (`--read`) |
256
293
  | `plugins` | Plugin folders of the Mason home, each a `--plugin-dir` |
257
294
  | `profile` | The project's deck profile for this role. Needs the agent's `pluginsOut`. See the slots' § Agents/Plugins |
258
295
  | `args` | Appended to the agent's own `args`, each an `--agent-arg` |
@@ -261,12 +298,12 @@ A `profile` is read in the project, not in the Mason home: the role gets the
261
298
  base plugins and that profile, as the project's committed deck locks pin them.
262
299
  `mason doctor` checks those locks in the work line without the network.
263
300
 
264
- Every tracker field lives in `managerOptions`. Host does not read them.
301
+ Every tracker field lives in the `options` of its side. Host does not read them.
265
302
 
266
303
  ### Choosing `pollIntervalMs`
267
304
 
268
305
  The sleep between two ticks, and nothing else. While a tick drives work — a
269
- Planner, a Builder, a validate — it runs to the end; the interval does not
306
+ Builder, a validate — it runs to the end; the interval does not
270
307
  touch it. It only sets how long `mason run` takes to notice:
271
308
 
272
309
  - a new issue, or an edit to one;
@@ -274,7 +311,7 @@ touch it. It only sets how long `mason run` takes to notice:
274
311
  that submitted (`docs/DECISIONS.md`, the Authority re-drive row), so on a
275
312
  work line without CI the whole interval is waited for a Gate that answers in
276
313
  two seconds;
277
- - a `ready`, or an issue closed to cancel.
314
+ - a `resume`, or an issue closed to cancel.
278
315
 
279
316
  **What one tick costs**, with `@bluewombat/manager-github` (read in
280
317
  `src/loop/tick.ts` `runOnce`, `src/loop/probe.ts` `abandonGone`, the manager's
@@ -285,8 +322,8 @@ against the token's budget.
285
322
  | The tick | GitHub REST calls | Counted when nothing moved | Besides |
286
323
  | --------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | ---------------------------------------------- |
287
324
  | Nothing to do | 1: the issues updated since the Cursor, 100 per page | 0 (304) | none: no fetch |
288
- | A Feature in flight | + 1 per Feature that can still be abandoned: is its issue still there | 0 (304) | a `git fetch` of the work line copy (not REST) |
289
- | A Feature `submitted` | + the judgement: `ci-green` asks for the pull request, its check runs and its statuses (about 3) | about 3: `ci-green` is its own process and keeps no `etag` | |
325
+ | A Ticket in flight | + 1 per Ticket that can still be abandoned: is its issue still there | 0 (304) | a `git fetch` of the work line copy (not REST) |
326
+ | A Ticket `submitted` | + the judgement: `ci-green` asks for the pull request, its check runs and its statuses (about 3) | about 3: `ci-green` is its own process and keeps no `etag` | |
290
327
 
291
328
  Reports to the tracker (a comment, a label move: 2 to 4 calls each) happen
292
329
  when the work moves, not per tick, and each changes the listing: the read
@@ -296,7 +333,7 @@ after a report is a full one.
296
333
  (GitHub's primary rate limit). It is shared by every caller using that token:
297
334
  the reports, `ci-green`, `gh`, and every other Mason home pointed at it.
298
335
 
299
- | `pollIntervalMs` | Idle, nothing moved | A Feature `submitted` (about 3 counted a tick) | Worst case, every read changed (about 5) |
336
+ | `pollIntervalMs` | Idle, nothing moved | A Ticket `submitted` (about 3 counted a tick) | Worst case, every read changed (about 5) |
300
337
  | ---------------- | ------------------- | ---------------------------------------------- | ---------------------------------------- |
301
338
  | 30000 | ~0 /h | 360 /h (7 %) | 600 /h (12 %) |
302
339
  | 15000 | ~0 /h | 720 /h (14 %) | 1,200 /h (24 %) |
@@ -310,14 +347,14 @@ listing. Without conditional reads an idle tick counted one call (360 /h at
310
347
  **The floor.** An idle tick takes 1 to 1.5 s, almost all of it the `listen`
311
348
  call (measured on `tilap/orangemonkey-site`, October 2026). Below 2 to 3 s
312
349
  the loop never sleeps. Below 10 s nothing a person notices is gained: a
313
- Feature takes minutes, and 10 s against 30 s saves about 20 s per Feature,
350
+ Ticket takes minutes, and 10 s against 30 s saves about 20 s per Ticket,
314
351
  mostly the post-Submission wait (30 s of a 5 min 40 s run, measured). Going
315
- lower spends the token while a Feature is submitted, for seconds nobody
352
+ lower spends the token while a Ticket is submitted, for seconds nobody
316
353
  notices.
317
354
 
318
- **So:** 10000 for one Mason home on a token. Idle no longer spends the
355
+ **So:** 10000 for one Mason home on a token. Idle does not spend the
319
356
  budget, so the quota is not what holds the floor: usefulness is, and a
320
- submitted Feature still is (its judgement counts). Several homes on the same
357
+ submitted Ticket still is (its judgement counts). Several homes on the same
321
358
  token add up: multiply the `submitted` and worst-case columns by their count
322
359
  and stay under half the budget. The journal is not a reason either way: quiet
323
360
  ticks are folded into one `idle` line a minute (`src/loop/journal.ts`).
@@ -328,7 +365,7 @@ settle or its timeout ends the attempt.
328
365
 
329
366
  ## Slots
330
367
 
331
- Commands mason spawns and never imports: the Planner, the Builder, each Gate,
368
+ Commands mason spawns and never imports: the Builder, each Gate,
332
369
  and — when an Authority is enabled — the Publisher and the Refresher. Every one
333
370
  reads argv and answers with **one JSON object on stdout**; an exit code alone is
334
371
  not the contract. mason does not pick a producer, and a slot may be written in
@@ -339,10 +376,6 @@ the shipped roles and agents (Cursor CLI, Claude Code), the shipped Gates, and
339
376
  their options. What follows is what Host guarantees them.
340
377
 
341
378
  ```json
342
- "planner": {
343
- "cmd": ["node", "./node_modules/@bluewombat/slots/planners/one-subtask.mjs"],
344
- "timeoutMs": 600000
345
- },
346
379
  "builder": {
347
380
  "producer": {
348
381
  "cmd": [
@@ -360,8 +393,8 @@ composition with `--` is the same.
360
393
 
361
394
  ### Builder
362
395
 
363
- Spawned in the Subtask workspace with `--intention`, `--definition-of-done`,
364
- `--id`, `--attempt`, and `--context`. On a retry it also gets `--report` and
396
+ Spawned in the workspace with `--intention`, `--id`, `--attempt`, and
397
+ `--context`. On a retry it also gets `--report` and
365
398
  `--report-from`: what refused the last pass, and which Gate said it. Exit 0
366
399
  means the Gates may run.
367
400
 
@@ -371,11 +404,11 @@ hides the change from them.
371
404
 
372
405
  The config always names a producer (and a repair). Implementer can still run a
373
406
  Task as its Gate sequence alone — that is the first judgement of the assembled
374
- feature, which makes nothing.
407
+ ticket, which makes nothing.
375
408
 
376
409
  ### Assembly fix
377
410
 
378
- Spawned in the feature workspace when a judgement of the whole refused it
411
+ Spawned in the ticket workspace when a judgement of the whole refused it
379
412
  (`--report`, `--report-from`). Same argv as a Builder otherwise. Host hands
380
413
  Implementer `assembly.fix` as that pass's only agent: there is no first-pass
381
414
  producer at this moment. The refusal it repairs may have come from an
@@ -383,7 +416,7 @@ Authority or from `assembly.validate`.
383
416
 
384
417
  ### Assembly validate
385
418
 
386
- Spawned in the feature workspace after align, with or without an Authority —
419
+ Spawned in the ticket workspace after align, with or without an Authority —
387
420
  `--intention`, `--id`, `--attempt`, `--context`. No `--report`: it is a
388
421
  read-only review, not a repair, and takes nothing a producer would. Optional.
389
422
  Its verdict is not the exit code alone: the slot reads what the agent itself
@@ -392,10 +425,10 @@ that completes cleanly but finds a problem is still a refusal.
392
425
 
393
426
  ### Gates
394
427
 
395
- Five independent sequences, one per role — `planner`, `builder.producer`,
428
+ Four independent sequences, one per role — `builder.producer`,
396
429
  `builder.repair`, `assembly.fix`, `assembly.validate` — plus `assembly.gates`
397
430
  for the one judgement-only pass with no producer of its own (re-checking the
398
- assembled feature; `ci-green` reads the checks the work line ran on what the
431
+ assembled ticket; `ci-green` reads the checks the work line ran on what the
399
432
  Publisher placed, so it belongs there). No sharing, no dispatch choosing
400
433
  between two buckets: a check right for one pass is not necessarily right for
401
434
  another, even within the same stage. `builder.repair` is the sharpest edge —
@@ -403,13 +436,10 @@ it does not inherit `builder.producer.gates`, so a repair Attempt with no
403
436
  Gate of its own runs ungated.
404
437
 
405
438
  Implementer's Gates are told `--stage unit` or `--stage assembly` alongside
406
- `--id`, `--attempt`, `--gate-id`, `--intention` and `--definition-of-done`.
439
+ `--id`, `--attempt`, `--gate-id` and `--intention`.
407
440
  Most have no use for it. `workspace-changed` does: "nothing changed" means
408
441
  nothing was done in a unit, and is the correct outcome for an assembly that
409
- needed nothing. FeatureBreakdown's Gates (`planner.gates`) are told only
410
- `--key` and `--gate-id` — no Attempt counter, no `--stage`, no
411
- `--definition-of-done` — so a Gate written for Implementer's shape is not a
412
- drop-in for `planner.gates`.
442
+ needed nothing.
413
443
 
414
444
  `init` writes an empty list on `builder.producer` and `builder.repair`; add a
415
445
  Gate when the Project has a check.
@@ -417,10 +447,10 @@ Gate when the Project has a check.
417
447
  Every child carries its own ceiling and no other. A Gate entry may name a
418
448
  `timeoutMs`; without one it takes its sequence's `defaultTimeoutMs`, and a Gate
419
449
  that ends up with neither is refused when the config loads. Nothing is derived
420
- from what another child left behind, so a slow producer can no longer leave its
450
+ from what another child left behind, so a slow producer cannot leave its
421
451
  Gates a millisecond to answer in.
422
452
 
423
- What bounds a Subtask is therefore what you wrote:
453
+ What bounds the unit is therefore what you wrote:
424
454
  `builder.maxAttempts × (builder.producer.timeoutMs + Σ builder.producer.gates timeouts)`
425
455
  for a first pass, and the same shape with `repair` in place of `producer` once
426
456
  a Gate has refused one.
@@ -441,16 +471,9 @@ is bounded the same way from `assembly.maxAttempts` and `assembly.fix`.
441
471
  }
442
472
  ```
443
473
 
444
- ### Planner
445
-
446
- FeatureStandard in, a Plan out. It is given `--key`, `--intention`,
447
- `--title` and `--max-units`. `init` writes the
448
- bootstrap Planner — one Subtask that is the intention — from
449
- `@bluewombat/slots/planners/one-subtask.mjs`.
450
-
451
474
  ### Describer
452
475
 
453
- How a delivered feature describes itself — the words the manager puts on the
476
+ How a delivered ticket describes itself — the words the manager puts on the
454
477
  Submission and on the fold — is the Project's, not Host's. Three levels, one
455
478
  key, `authority.describe`:
456
479
 
@@ -458,10 +481,10 @@ key, `authority.describe`:
458
481
  | -------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------- |
459
482
  | not set | the manager's own default: the issue's title, its body as the human wrote it | none |
460
483
  | `messages/conventional.mjs` | `<type>: <title>` (`--scope`, `--type`), the body as written | none |
461
- | `messages/git-producer.mjs -- <agent>` | an agent reads the git diff and writes both under `--rules-file` — the team's guideline | one agent run per feature |
484
+ | `messages/git-producer.mjs -- <agent>` | an agent reads the git diff and writes both under `--rules-file` — the team's guideline | one agent run per ticket |
462
485
 
463
486
  It is an argv like `publish` and `refresh`, bounded by the global `timeoutMs`.
464
- Host spawns it once per feature, in the feature workspace, when the work is
487
+ Host spawns it once per ticket, in the ticket workspace, when the work is
465
488
  offered — with `--id`, `--title`, `--intention` and `--target` (the work line
466
489
  it lands on) — and takes one line back: `{ "subject": "…", "body": "…" }`.
467
490
  Host hands that description to the manager as is; what it becomes is the
@@ -469,12 +492,9 @@ manager's business (for GitHub: the squash's title and message, the pull
469
492
  request's first paragraph). A slot that fails is skipped and the journal says
470
493
  so.
471
494
 
472
- Subtasks are never described: their folds are folded again into the
473
- feature's, so their message is the Subtask's intention — its headline when
474
- the Planner wrote one (a line, a blank line, the rest: the shape of a commit),
475
- else its first sentence cut at a word, no markdown. That headline is also what
476
- the tracker's Plan, the progress comments and the Submission's change list
477
- show, so a Planner is asked to write one.
495
+ The commit that takes up what the Builder made is worded from the title of the
496
+ issue (or the headline of its intention: a line, a blank line, the rest — the
497
+ shape of a commit), else its first sentence cut at a word, no markdown.
478
498
 
479
499
  ### Write another slot
480
500
 
@@ -484,8 +504,8 @@ otherwise repeat is [`@bluewombat/slot-kit`](../slot-kit/README.md).
484
504
 
485
505
  ## Submissions
486
506
 
487
- Without an Authority, a finished feature is folded straight into WorkLineStable
488
- and that is the end of it. With one, the assembled feature is **offered** to an
507
+ Without an Authority, a finished ticket is folded straight into WorkLineStable
508
+ and that is the end of it. With one, the assembled ticket is **offered** to an
489
509
  outside judge first, and only an `accepted` verdict lets it into the work line.
490
510
 
491
511
  It is **declared**, never inferred:
@@ -520,7 +540,7 @@ strategy does not know, a branch the remote does not have, a `workLine.branch`
520
540
  that disagrees with the reference, or a `workLine.stable` that is not a copy of
521
541
  it each stop `mason run` at boot, and `mason doctor` reports the same offline
522
542
  (`reference`, `work line`). Without a reference, `workLine.stable` is required
523
- and is the operator's directory, as before.
543
+ and is the operator's directory.
524
544
 
525
545
  ```
526
546
  assembled → align → assembly.validate (optional, local)
@@ -542,12 +562,12 @@ Isolation must start from that version, not from a copy left behind.
542
562
 
543
563
  A refusal is not a second failure path: its report reaches `assembly.fix`
544
564
  exactly as a Gate's report reaches `builder.repair`. After `maxRefusals`
545
- refusals the feature escalates with that report as its Trace, and `done` names
565
+ refusals the ticket escalates with that report as its Trace, and `done` names
546
566
  the Submission rather than a directory on the machine that ran.
547
567
 
548
568
  ## Write another manager
549
569
 
550
- Host loads whatever `manager` names, as long as it exports `createManager`. The
570
+ Host loads whatever `featureTracker.package` and `ticketTracker.package` name, as long as it exports `createManager`. The
551
571
  contract and a worked example are in
552
572
  [`@bluewombat/manager-kit`](../manager-kit/README.md). Nothing in Host changes.
553
573
 
@@ -557,14 +577,20 @@ contract and a worked example are in
557
577
  import { openHost } from "@bluewombat/runtime";
558
578
 
559
579
  const host = await openHost({
560
- manager: "@bluewombat/manager-fake",
561
- managerOptions: { source: "./.mason/source", target: "./.mason/threads" },
580
+ featureTracker: {
581
+ package: "@bluewombat/manager-fake",
582
+ options: { source: "./.mason/source", target: "./.mason/threads" },
583
+ },
584
+ ticketTracker: {
585
+ package: "@bluewombat/manager-fake",
586
+ options: { source: "./.mason/source", target: "./.mason/threads" },
587
+ },
562
588
  // …paths and slots
563
589
  });
564
590
  await host.runOnce();
565
591
  ```
566
592
 
567
- `manager` also takes an already-loaded module, which is how a test drives Host
593
+ Each `package` also takes an already-loaded module, which is how a test drives Host
568
594
  without resolving a package.
569
595
 
570
596
  ## Stack
@@ -616,7 +642,7 @@ change that breaks it does not merge.
616
642
  Host's code names no plugin. A manager, an isolation strategy, a persistence
617
643
  backend, the shipped slots: each is loaded by the name a config gives, through
618
644
  `plugins/`, never imported. The names Host falls back to — the two isolation
619
- strategies `init` writes, the default `persist`, the bootstrap Planner — are
645
+ strategies `init` writes, the default `persist` — are
620
646
  strings in `config/defaults.ts` and nowhere else; `check-host.mjs` refuses an
621
647
  import of any of them and a spelled-out name anywhere else. Host's
622
648
  `package.json` declares exactly those defaults, so a config `init` wrote runs
@@ -665,6 +691,6 @@ src/
665
691
  config/ argv, JSON config, config discovery → HostOptions; defaults: the plugin names Host ships with
666
692
  plugins/ load: the one loader; manager, isolation, persist: one shape each; discover
667
693
  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
668
- operator/ init, setup, doctor, live (watch / status), log, film, cancel, release, board, prompt, git-remote
694
+ operator/ run, init, setup, doctor, explain, live (watch / status), log, film, cancel, release, board, prompt, git-remote
669
695
  fixtures/ stand-in slots for the loop's tests
670
696
  ```