vinta-ai-workflows 0.7.0-alpha7 → 0.7.0-alpha9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,8 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [0.7.0] — YYYY-MM-DD
9
9
 
10
+ <!-- pre-release: 0.7.0-alpha9 on 2026-09-14 -->
11
+ <!-- pre-release: 0.7.0-alpha8 on 2026-09-14 -->
10
12
  <!-- pre-release: 0.7.0-alpha7 on 2026-09-14 -->
11
13
  <!-- pre-release: 0.7.0-alpha6 on 2026-09-13 -->
12
14
  <!-- pre-release: 0.7.0-alpha5 on 2026-09-13 -->
@@ -17,6 +19,69 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
17
19
 
18
20
  ### Added
19
21
 
22
+ - **A project declares what makes a lane runnable.** `project.env_files` copies
23
+ ignored configuration into each worktree, `project.setup_cmd` runs an
24
+ idempotent setup hook on provisioning and recycle, and the fixed
25
+ `project.commands` vocabulary tells implementers, reviewers and fixers the
26
+ project's real lint, typecheck, test and migration entry points.
27
+
28
+ - **Compose-backed lanes are isolated past `COMPOSE_PROJECT_NAME`.** The daemon
29
+ now generates an out-of-tree Compose override per lane, strips fixed host
30
+ ports, re-pins fixed and external volumes, and carries the complete lane
31
+ environment into agent and takeover processes as well as gates. Explicitly
32
+ published services receive probed per-lane ports recorded in the teardown
33
+ summary.
34
+
35
+ - **Shared services get one namespace per lane.** `project.services` models a
36
+ fixed index or lane-derived name inside one Redis, RabbitMQ, object-storage,
37
+ or similar server, with project-owned create/reset commands and capacity
38
+ validation before provisioning creates templates or worktrees.
39
+
40
+ - **Agents can take the semaphore a workflow declares.**
41
+ `vinta-ai-maestro with <resource> -- <cmd>` waits on the run's existing
42
+ `ResourcePools`, runs the heavy inner-loop command, and releases on exit.
43
+ Renewable leases expire when their client disappears, are visible in the
44
+ live journal, and are surfaced in implementer, reviewer and fixer prompts.
45
+
46
+ - **A failed phase retries itself, and then asks.** `--on-failure` defaults to
47
+ `retry`: one cold re-attempt (`--retries <n>`, 0–5), and then the node parks
48
+ on a question — retry, retry with another member of the crew, or stop. The
49
+ failures this system produces are overwhelmingly environmental and are gone by
50
+ the second attempt; the ones that survive it are the ones worth a person.
51
+ `ask` skips the automatic attempt. `stop` is the old behaviour and remains the
52
+ right choice for CI, because everything else eventually **waits**.
53
+
54
+ A failure that survives its retries says so: the reason carries
55
+ `(after N attempts)`, which no event otherwise records.
56
+
57
+ - **A failed phase can be retried by hand.** A **Retry phase** button, and
58
+ `POST /api/runs/:runId/nodes/:nodeId/retry`. It returns the node to the ready
59
+ set and unblocks the subtree its failure blocked — a phase that died of
60
+ something environmental holds up work that was never broken. It needs the run
61
+ to still be in flight, which with the new default it usually is; a run that
62
+ has genuinely ended is re-run, not retried, and says so rather than doing
63
+ nothing.
64
+
65
+ - **The monitor can go and look.** It was given a digest and nothing else, and
66
+ read exactly as thin as that sounds — it could say a phase had failed and
67
+ never say what the phase had written. It is briefed as a technical project
68
+ manager now, with the map: the plan document, each phase's brief, each lane's
69
+ worktree path, each branch and its base, and where the journal and transcripts
70
+ live. It has a shell and its working directory is the repository, so it can
71
+ read the diff before drawing a conclusion. The digest stays as the index —
72
+ bounded, cheap, enough to know which phase is worth a closer look.
73
+
74
+ - **The monitor conversation is kept.** It lived in component state, so a reload
75
+ threw away every question and every answer — a worse record than the run it
76
+ describes. It is written to the transcript store under a reserved node id and
77
+ read back on load, so it outlives the tab, the daemon and the run.
78
+
79
+ - **Panels expand.** Anything holding content that is only nominally
80
+ summarisable — an agent's transcript, a gate log, the monitor conversation,
81
+ the steering box — takes the full container width and about a screen of
82
+ height on demand. The height travels as a CSS variable, because the element
83
+ that has to grow is a scroller several components below the panel.
84
+
20
85
  - **A run has a spokesperson you can ask.** `serve` grows a **Monitor** panel on
21
86
  the run view: one agent that reads the journal and answers in prose — what is
22
87
  blocked, why a phase failed, what it would take to move on. It runs on the
@@ -254,6 +319,13 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
254
319
 
255
320
  ### Fixed
256
321
 
322
+ - **`serve --host 0.0.0.0` prints a URL another machine can open.** Binding
323
+ every interface made the server report the wildcard back, so the line printed
324
+ for the operator to open — and share — was `http://0.0.0.0:<port>`, which
325
+ resolves for nobody. The bind is unchanged; the printed host is now this
326
+ machine's LAN address. The daemon still warns, because the token in that URL
327
+ is the only thing between the run and anyone who can reach the port.
328
+
257
329
  - **An agent can run a shell command.** `auto` mapped to the vendor's
258
330
  `acceptEdits`, which accepts file *edits* and nothing else — a shell command
259
331
  still goes to a permission prompt, and in `-p` there is nobody to answer one.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinta-ai-workflows",
3
- "version": "0.7.0-alpha7",
3
+ "version": "0.7.0-alpha9",
4
4
  "description": "Bootstrap AI tooling (AGENTS.md, skills, sub-agents) across Claude Code, Codex, Cursor, and VS Code + GitHub Copilot.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -191,6 +191,141 @@
191
191
  }
192
192
  },
193
193
  "additionalProperties": false
194
+ },
195
+ "env_files": {
196
+ "default": [],
197
+ "description": "Repo-relative ignored files every lane needs its own COPY of — `.env`, `.env.docker`, `.envrc`. Copied, never symlinked: lane provisioning appends to them (a connection string, a compose override path), and a symlink would write those lane-specific lines back into the main checkout. A declared file that is missing fails provisioning rather than producing a lane whose stack cannot boot.",
198
+ "type": "array",
199
+ "items": {
200
+ "type": "string",
201
+ "minLength": 1
202
+ }
203
+ },
204
+ "commands": {
205
+ "default": {},
206
+ "type": "object",
207
+ "properties": {
208
+ "lint": {
209
+ "type": "string",
210
+ "minLength": 1
211
+ },
212
+ "typecheck": {
213
+ "type": "string",
214
+ "minLength": 1
215
+ },
216
+ "test": {
217
+ "description": "The whole suite.",
218
+ "type": "string",
219
+ "minLength": 1
220
+ },
221
+ "test_one": {
222
+ "description": "A single test or a subtree. The agent appends the target, so end it accordingly.",
223
+ "type": "string",
224
+ "minLength": 1
225
+ },
226
+ "migrate": {
227
+ "type": "string",
228
+ "minLength": 1
229
+ }
230
+ },
231
+ "additionalProperties": false,
232
+ "description": "The project’s own command lines, handed to every agent verbatim. A fixed vocabulary rather than free-form, because the prompt has to be able to say which one step 3 of the working instructions means. Declaring these is how an agent stops guessing: a project whose suite only runs inside a container, or only against a lane-specific database, has no way of telling one otherwise, and `pytest` is a plausible guess that is wrong in exactly that project."
233
+ },
234
+ "services": {
235
+ "default": {},
236
+ "description": "Shared servers each lane gets its own namespace inside, by id. One postgres, one redis, one rabbit on the machine, with a database / db index / vhost per lane — which is the difference between six lanes being viable on a laptop and not. The alternative a project falls into without this is booting a server per lane, or sharing one with no isolation at all.",
237
+ "type": "object",
238
+ "propertyNames": {
239
+ "type": "string",
240
+ "minLength": 1,
241
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
242
+ },
243
+ "additionalProperties": {
244
+ "type": "object",
245
+ "properties": {
246
+ "namespace": {
247
+ "type": "string",
248
+ "enum": [
249
+ "index",
250
+ "name"
251
+ ],
252
+ "description": "How this lane’s namespace inside the shared server is derived. `index` is a small integer, for a server with a fixed number of slots — redis’s sixteen databases are the case it exists for. `name` is a token derived from the lane, for a server that names things freely: a vhost, a bucket prefix, a schema."
253
+ },
254
+ "url": {
255
+ "description": "The shared server, without the per-lane segment — `redis://localhost:6379`. Omit it where the namespace *is* the value: an object-storage prefix has no URL to hang off, and the variable then carries the bare token.",
256
+ "type": "string",
257
+ "minLength": 1
258
+ },
259
+ "url_var": {
260
+ "type": "string",
261
+ "minLength": 1,
262
+ "description": "Env var the lane reads this service’s address from."
263
+ },
264
+ "capacity": {
265
+ "default": 16,
266
+ "description": "How many distinct namespaces the server has. Only meaningful for `index`, where a pool larger than this is refused before anything is created — two lanes on one redis database is the bug this exists to prevent, and reaching it by arithmetic is no better than reaching it by neglect.",
267
+ "type": "integer",
268
+ "minimum": 1,
269
+ "maximum": 9007199254740991
270
+ },
271
+ "create_cmd": {
272
+ "description": "Run once per lane, in the lane. `{namespace}`, `{url}` and `{lane}` are substituted. Declaring a vhost is a `rabbitmqadmin` invocation this package has no business knowing; a project that needs none declares none.",
273
+ "type": "string",
274
+ "minLength": 1
275
+ },
276
+ "reset_cmd": {
277
+ "description": "Empties this lane’s namespace when the lane is handed to the next phase. Same substitutions. A service without one keeps whatever the last phase left in it, which for a cache is usually right and for a queue usually is not — declaring this is how a project says which of those it has.",
278
+ "type": "string",
279
+ "minLength": 1
280
+ }
281
+ },
282
+ "required": [
283
+ "namespace",
284
+ "url_var"
285
+ ],
286
+ "additionalProperties": false,
287
+ "description": "A shared server each lane gets its own namespace inside."
288
+ }
289
+ },
290
+ "compose": {
291
+ "default": {
292
+ "enabled": true,
293
+ "publish": [],
294
+ "shared_volumes": []
295
+ },
296
+ "type": "object",
297
+ "properties": {
298
+ "enabled": {
299
+ "default": true,
300
+ "description": "Whether lane provisioning generates a compose override. On by default, and it does nothing at all in a project with no compose file. Where there is one and docker cannot answer, provisioning fails rather than quietly producing lanes that share a data volume — set this to false to say that is genuinely what you want.",
301
+ "type": "boolean"
302
+ },
303
+ "publish": {
304
+ "default": [],
305
+ "description": "Services that must keep a reachable host port, republished on one this lane was granted instead of the one the project pinned. Empty is the default and is usually right outright: where the project’s own test command runs inside compose, services reach each other by container DNS on the lane’s own network and a published port buys nothing but a collision. The granted port reaches the lane as `LANE_PORT_<SERVICE>_<CONTAINER_PORT>`.",
306
+ "type": "array",
307
+ "items": {
308
+ "type": "string",
309
+ "minLength": 1
310
+ }
311
+ },
312
+ "shared_volumes": {
313
+ "default": [],
314
+ "description": "Volume keys that leak past the project name and are to be left leaking. The escape hatch for the one decision this cannot make for you: re-pinning a shared dependency volume is correct, and it costs every lane the install that sharing it was avoiding. A read-only cache with no dependency churn in the plan is safe here. A data volume never is — that is two servers on one data directory, which is the bug this exists to prevent.",
315
+ "type": "array",
316
+ "items": {
317
+ "type": "string",
318
+ "minLength": 1
319
+ }
320
+ }
321
+ },
322
+ "additionalProperties": false,
323
+ "description": "How a lane’s compose stack is isolated past `COMPOSE_PROJECT_NAME`."
324
+ },
325
+ "setup_cmd": {
326
+ "description": "The project’s own lane-setup command, run inside each freshly provisioned lane with that lane’s environment applied. The escape hatch for whatever a project needs that no field here describes. It MUST be idempotent: it runs again every time the lane is recycled for another phase.",
327
+ "type": "string",
328
+ "minLength": 1
194
329
  }
195
330
  },
196
331
  "required": [