vinta-ai-workflows 0.7.0-alpha12 → 0.7.0-alpha14

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 (2) hide show
  1. package/CHANGELOG.md +195 -3
  2. package/package.json +1 -1
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-alpha14 on 2026-09-16 -->
11
+ <!-- pre-release: 0.7.0-alpha13 on 2026-09-16 -->
10
12
  <!-- pre-release: 0.7.0-alpha12 on 2026-09-15 -->
11
13
  <!-- pre-release: 0.7.0-alpha11 on 2026-09-15 -->
12
14
  <!-- pre-release: 0.7.0-alpha10 on 2026-09-15 -->
@@ -22,6 +24,18 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
22
24
 
23
25
  ### Added
24
26
 
27
+ - **`vinta-ai-maestro gate <gate-id>` runs a declared gate from inside an agent
28
+ turn.** A phase used to run its full gate suite five or six times a round —
29
+ implementer, reviewer, fixer, reviewer again, then the authoritative `gate`
30
+ node — and only the last of those consulted the gate cache or the resource
31
+ pool. Agents now ask the daemon for a gate by id: the result is cached on the
32
+ same `(gate id, lane tree hash)` key the `gate` node reads, the gate's
33
+ resources are held by the daemon rather than by whichever agent remembered to
34
+ wrap the command, and the id resolves to the plan's own command so a scoped
35
+ approximation cannot be reported as the gate. Each run is journalled as a
36
+ `gate_result` event and a `gate_run` transcript entry under the GATE band,
37
+ whoever asked for it.
38
+
25
39
  - **A project declares what makes a lane runnable.** `project.env_files` copies
26
40
  ignored configuration into each worktree, `project.setup_cmd` runs an
27
41
  idempotent setup hook on provisioning and recycle, and the fixed
@@ -184,10 +198,33 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
184
198
  claimed per review turn rather than per phase — it has one session ledger and
185
199
  two concurrent reviews would collide over it — so one reviewer on a three-lane
186
200
  plan is a queue at the review step and not a serialised run.
201
+ - **"Cheapest qualified" has one exception, and it is about sessions.** A
202
+ member who already holds a session this phase would genuinely resume takes
203
+ it ahead of a cheaper member who would have to start cold — even when they
204
+ are more senior than the phase needs, and even when the member the plan
205
+ named is free. What it buys is the context a cold session spends its first
206
+ turn rebuilding; what it costs is the dearer model for that phase, which is
207
+ the smaller of the two bills. **The tier floor is untouched**: warmth
208
+ reorders the members who already qualify and never widens them, so a warm
209
+ junior still cannot take a Tier 3 phase.
210
+ - **Only when a session is genuinely already up.** "Warm" means the scheduler
211
+ asked its own session-reuse rules and got a yes — same pinned lane, same
212
+ harness, under the turn ceiling, not poisoned by a failed phase. A member
213
+ who has merely run before is not warm, because a promotion bought on a
214
+ resume that is then refused pays the senior's rate *and* cold-starts
215
+ anyway. With nobody warm, staffing is exactly what it was: a session is
216
+ being opened either way, and the plan's own level wins.
217
+ - An operator who picks the member by hand on a retry gets that member. A
218
+ saving nobody asked for does not overrule an explicit answer.
187
219
  - The run view reports who actually worked against who the plan said would.
188
220
  `substituted` is the figure to read beside a cost that overran: every
189
221
  substitution ran at or above the budgeted tier, so a run can be entirely green
190
- and still have been staffed dearer than planned.
222
+ and still have been staffed dearer than planned. The phases promoted to reuse
223
+ a warm session are counted **within** that figure and reported apart from it —
224
+ a peer covering for a busy member costs what the plan budgeted, while a warm
225
+ promotion is the scheduler deliberately trading model rate against cold
226
+ starts, and folded together a run that promoted everything to the top tier
227
+ would read as an ordinary busy wave.
191
228
 
192
229
  - **`implement-plan` runs independent phases in parallel.** The plan now carries a
193
230
  dependency graph and the conductor schedules against it: a phase starts as soon as
@@ -282,8 +319,63 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
282
319
  **generated** from a zod source rather than hand-written — see
283
320
  [`schemas/README.md`](schemas/README.md).
284
321
 
322
+ - **The monitor keeps thinking when you look away, and thinks out loud.**
323
+ Asking used to be one long HTTP request that produced the whole answer inside
324
+ it, which made the turn's lifetime the browser's connection: switching view,
325
+ reloading or letting a laptop sleep killed the monitor mid-thought and left a
326
+ question with no answer and no record that one had been attempted. The daemon
327
+ owns the turn now — `POST` answers `202`, and the monitor journals each
328
+ thought and each sentence as it produces them, so the conversation is
329
+ readable while it is being written and by any tab that opens it. The panel
330
+ renders through the same rows a phase's transcript uses, which is what the
331
+ API always claimed and the UI never did.
332
+
333
+ - **A phase's transcript says which agent wrote each line, and holds the gates.**
334
+ Every spawn on a node appends to one file whatever its role, so a phase that
335
+ took two fix rounds held five agents' output in one undifferentiated stream —
336
+ the role was in scope at the append and simply not written down. Each line now
337
+ carries the role and the session slot that produced it, the view bands the
338
+ list where one agent stops and the next starts, and gate runs are entries in
339
+ it rather than only in a log beside it. The operator's own steering stays the
340
+ operator's (§7), even though the adapter echoes it back on the agent's event
341
+ stream. Transcripts written before this render exactly as they did, with no
342
+ bands. The band stays pinned to the top of the scroller while its own run of
343
+ rows goes past, so the agent you are reading is named without scrolling back
344
+ to where it started — in the phase transcript and in the monitor conversation
345
+ alike.
346
+
285
347
  ### Changed
286
348
 
349
+ - **A session cannot inherit a machine's decision to stop compacting.** All
350
+ three harnesses compact automatically when their context fills — there was
351
+ never a flag to turn on — but each honours an environment variable that turns
352
+ it off, and an agent spawned on a machine carrying one would run a long phase
353
+ straight into a wall it was supposed to be able to survive. Those variables
354
+ are now stripped from every child the daemon spawns, and claude-code's own
355
+ setting is reasserted in the per-lane file beside its permission rules. The
356
+ window itself is left alone: an operator who narrowed theirs compacts earlier,
357
+ which is not the failure this guards against. A harness that compacts declares
358
+ it, and a compaction the harness reports is a transcript row like any other.
359
+
360
+ One consequence worth knowing: `max_session_turns` used to be a proxy for "this
361
+ session is nearing a context window it will die on". That death no longer
362
+ happens, so the ceiling now guards a different thing — a session whose memory
363
+ was replaced by a summary of itself, and which answers just as confidently.
364
+
365
+ - **Agent prompts name gates by id rather than printing their commands.** The
366
+ implementer's step 4 was descriptive — "Outer gate… These run against your
367
+ lane:" followed by the gate commands — and implementers were observed reading
368
+ it as a note about what would run later, finishing the scoped inner loop and
369
+ never running a gate at all. It is imperative now, and every role that runs a
370
+ gate (implementer, reviewer, fixer, and the continuation of each) is pointed
371
+ at `vinta-ai-maestro gate <id>`. The fixer prompts no longer say to re-run the
372
+ gates "until they are green", which contradicted the commit protocol directly
373
+ below them and left fixers running out the turn with the work uncommitted;
374
+ they now say to commit and report FAILURE when a gate cannot be turned green.
375
+ Implementers and fixers report which gates they ran and what each returned —
376
+ a record for the reviewer to check against, explicitly not a licence for the
377
+ reviewer to skip running them itself.
378
+
287
379
  - **Phase branches base on their dependencies, not on the previous phase.** A phase
288
380
  with no dependencies cuts from the default branch; one with a single dependency
289
381
  cuts from that phase's branch; one with several cuts from a
@@ -337,8 +429,110 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
337
429
  is only as good as the `healthcheck` a service declares: without one, compose
338
430
  returns as soon as the container starts and `createdb` races the server.
339
431
 
432
+ - **A merge conflict goes to the most senior member who wrote one of its
433
+ sides.** The conflict fixer was staffed from `defaults` and ignored the roster
434
+ entirely, so on a tiered crew a collision between two Tier 4 phases was handed
435
+ to a default-tier agent that had read neither of them — the hardest merge in
436
+ the run going to the least contextualised agent available, and resolved by
437
+ guessing which side looked more finished.
438
+
439
+ `Integrator` already knows which nodes own the contested paths, and `node_crew`
440
+ already records who took each one; this is the join. Highest tier among them
441
+ wins, on that member's own `model` and `harness`, and a tier tie breaks on
442
+ member id the way roster substitution breaks its own. An unstaffed workflow
443
+ still resolves through `defaults`, unchanged, and so does a conflict whose
444
+ nodes cannot be resolved to a member.
445
+
446
+ What is carried is the member's **capability, not their session**. A phase's
447
+ session ran in that phase's lane; the fixer runs in the integration worktree,
448
+ where the files in dispute are half-merged and unlike anything that session
449
+ saw — resuming there would give the agent confident, false memories of exactly
450
+ the files it is merging. The prompt tells the fixer which side is its own, and
451
+ in the same breath that it is a fresh session and must read rather than
452
+ recall.
453
+
340
454
  ### Fixed
341
455
 
456
+ - **An agent taking a lease left the resource panel showing the one before it.**
457
+ The scheduler's own gate-pool transitions are journalled as `gate_pool`
458
+ events, so the browser re-reads the snapshot when one lands. An agent's lease,
459
+ taken through `vinta-ai-maestro with`, was only a row in the `leases` table —
460
+ correct in the daemon's projection and announced to nobody. A projection
461
+ nobody is told changed is a projection nobody sees change, so the panel showed
462
+ whatever holders it had last time some unrelated event happened to arrive, and
463
+ the waits it was stalest about were the agent ones, which are the long ones.
464
+ The broker now journals `agent_lease` on both edges. A renewal writes nothing:
465
+ it is a heartbeat on a lease already reported, and journalling it would bury
466
+ the two rows that are transitions under a liveness log.
467
+
468
+ - **An agent waiting for a semaphore could be overtaken without bound.** A
469
+ `vinta-ai-maestro with` wait is served in fifteen-second hops so no client
470
+ timeout has an opinion about it, and each hop used to abort the underlying
471
+ `pools.acquire`. Aborting leaves the pool queue, and the next POST re-entered
472
+ it — at the tail, behind every waiter that had arrived in the meantime. Since
473
+ a gate run holds a capacity-1 semaphore for minutes, an agent lost that race
474
+ on hop after hop while the scheduler's own in-process waiters, which never
475
+ leave the queue, did not. The pool's aging rule could not help: aging reserves
476
+ against waiters *behind* the aged one, and the waiters that overtook it are in
477
+ front of it. A `202` now carries a `waitToken` naming the wait, the client
478
+ sends it back, and the daemon keeps the one queue entry — one arrival time,
479
+ one place in line — across the whole wait. A parked wait whose client stops
480
+ hopping is reaped after a minute, releasing the slot if one was granted to it
481
+ while nobody was watching.
482
+
483
+ - **The monitor's conversation could never be written on Windows.** It is kept
484
+ under a reserved node id that becomes a directory, and that id was
485
+ `monitor:conversation` — a colon, chosen because no phase id may contain one,
486
+ and the drive separator on Windows. `mkdir` failed with `ENOENT`, so every
487
+ question threw on its first append and the endpoint reported the monitor
488
+ unavailable. A colon is legal in a macOS or Linux filename, which is why no
489
+ machine the feature was built on ever saw it. The id is now
490
+ `_monitor-conversation`: still unrepresentable as a phase id, and legal
491
+ everywhere. Conversations recorded under the old id are not migrated.
492
+
493
+ - **An implementer was told both to commit and never to commit.** The prompt
494
+ carried "never commit while a gate is red" beside "the turn is not complete
495
+ until `git status --porcelain` is empty of your work" — a contradiction
496
+ whenever the gate could not be turned green inside the turn, which is most of
497
+ why fix rounds exist. Agents obeyed the `never`, and the reviewer one node
498
+ later raised the BLOCKER it is told to raise for uncommitted work, spending a
499
+ fix round re-implementing code that was already on disk. A red gate now
500
+ changes the report and never the decision to commit: commit, and report
501
+ FAILURE saying what is still red.
502
+
503
+ The reviewer's side was a false BLOCKER independently of that. Any unclean
504
+ tree was a finding, and a lane's tree is never clean — the pool copies
505
+ configuration in, links dependency trees, and the gates leave build output
506
+ behind, none of which the implementer is allowed to stage. It is scoped to
507
+ the failure it was written for now: work that is in the tree and missing from
508
+ the diff.
509
+ - **A live transcript stops following at entry sixty.** The effect that kept the
510
+ box on the newest row was keyed on how many rows were *shown*, which is
511
+ `min(entries, window)` — so it stopped changing the moment a transcript
512
+ outgrew one window, and the following died silently and permanently, including
513
+ after the operator scrolled back to the bottom to ask for it. Which is roughly
514
+ minute two of any real phase. It follows on the entry count now, growing the
515
+ window keeps the reader on the row they were reading instead of teleporting
516
+ them, and a **Jump to latest** button says so out loud rather than leaving a
517
+ 60px band at the bottom of a scroller as the only way back in.
518
+
519
+ - **The transcript spends its space on the noisiest rows.** An agent's answer,
520
+ its private reasoning and four hundred characters of tool-call JSON all
521
+ rendered as the same two lines at the same weight. Thinking is now small and
522
+ quiet, and a streamed thought is one row rather than the dozen events it
523
+ arrived as; tool calls and their results fold to the argument that says what
524
+ they did — a command, a path — and open individually or a whole kind at a time
525
+ from the panel header. Prose is untouched and never folds.
526
+
527
+ - **A panel's expand control did nothing on the node view.** It expanded by
528
+ taking `col-span-full`, which on the run view bought a strip a third wider and
529
+ on the node view matched nothing at all — those panels sit in a flex column,
530
+ not a grid, so the only thing the button changed was the height of the
531
+ scroller inside it. It now opens the panel over the whole page through a
532
+ portal, so no scrolling ancestor can clip it, with Escape to leave and the
533
+ page behind it held still. A transcript that was following stays on its newest
534
+ row across the change.
535
+
342
536
  - **`doctor` checks that the shared servers answer.** It verified that binaries
343
537
  existed and disk fitted, and never looked at a `server_url` — so a project
344
538
  whose Postgres lives in another checkout's compose stack passed the preflight
@@ -1178,7 +1372,6 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1178
1372
  **Consumers**: re-sync to pick up the opt-in questions. E2E-disabled projects
1179
1373
  are unaffected (the e2e regions strip out as before).
1180
1374
 
1181
-
1182
1375
  <!-- pre-release: 0.2.0-alpha6 on 2026-07-13 -->
1183
1376
 
1184
1377
  ### Changed
@@ -1215,7 +1408,6 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1215
1408
 
1216
1409
  ### Added
1217
1410
 
1218
-
1219
1411
  - **`handoff` foundation skill — session-continuation handoff docs between
1220
1412
  agents.** Write mode captures the current task (goal, verified-vs-unverified
1221
1413
  state, decisions + rejected alternatives, landmines, single concrete next
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinta-ai-workflows",
3
- "version": "0.7.0-alpha12",
3
+ "version": "0.7.0-alpha14",
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": {