vinta-ai-workflows 0.7.0-alpha13 → 0.7.0-alpha15
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 +131 -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-alpha15 on 2026-09-16 -->
|
|
11
|
+
<!-- pre-release: 0.7.0-alpha14 on 2026-09-16 -->
|
|
10
12
|
<!-- pre-release: 0.7.0-alpha13 on 2026-09-16 -->
|
|
11
13
|
<!-- pre-release: 0.7.0-alpha12 on 2026-09-15 -->
|
|
12
14
|
<!-- pre-release: 0.7.0-alpha11 on 2026-09-15 -->
|
|
@@ -23,6 +25,18 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
23
25
|
|
|
24
26
|
### Added
|
|
25
27
|
|
|
28
|
+
- **`vinta-ai-maestro gate <gate-id>` runs a declared gate from inside an agent
|
|
29
|
+
turn.** A phase used to run its full gate suite five or six times a round —
|
|
30
|
+
implementer, reviewer, fixer, reviewer again, then the authoritative `gate`
|
|
31
|
+
node — and only the last of those consulted the gate cache or the resource
|
|
32
|
+
pool. Agents now ask the daemon for a gate by id: the result is cached on the
|
|
33
|
+
same `(gate id, lane tree hash)` key the `gate` node reads, the gate's
|
|
34
|
+
resources are held by the daemon rather than by whichever agent remembered to
|
|
35
|
+
wrap the command, and the id resolves to the plan's own command so a scoped
|
|
36
|
+
approximation cannot be reported as the gate. Each run is journalled as a
|
|
37
|
+
`gate_result` event and a `gate_run` transcript entry under the GATE band,
|
|
38
|
+
whoever asked for it.
|
|
39
|
+
|
|
26
40
|
- **A project declares what makes a lane runnable.** `project.env_files` copies
|
|
27
41
|
ignored configuration into each worktree, `project.setup_cmd` runs an
|
|
28
42
|
idempotent setup hook on provisioning and recycle, and the fixed
|
|
@@ -185,10 +199,33 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
185
199
|
claimed per review turn rather than per phase — it has one session ledger and
|
|
186
200
|
two concurrent reviews would collide over it — so one reviewer on a three-lane
|
|
187
201
|
plan is a queue at the review step and not a serialised run.
|
|
202
|
+
- **"Cheapest qualified" has one exception, and it is about sessions.** A
|
|
203
|
+
member who already holds a session this phase would genuinely resume takes
|
|
204
|
+
it ahead of a cheaper member who would have to start cold — even when they
|
|
205
|
+
are more senior than the phase needs, and even when the member the plan
|
|
206
|
+
named is free. What it buys is the context a cold session spends its first
|
|
207
|
+
turn rebuilding; what it costs is the dearer model for that phase, which is
|
|
208
|
+
the smaller of the two bills. **The tier floor is untouched**: warmth
|
|
209
|
+
reorders the members who already qualify and never widens them, so a warm
|
|
210
|
+
junior still cannot take a Tier 3 phase.
|
|
211
|
+
- **Only when a session is genuinely already up.** "Warm" means the scheduler
|
|
212
|
+
asked its own session-reuse rules and got a yes — same pinned lane, same
|
|
213
|
+
harness, under the turn ceiling, not poisoned by a failed phase. A member
|
|
214
|
+
who has merely run before is not warm, because a promotion bought on a
|
|
215
|
+
resume that is then refused pays the senior's rate *and* cold-starts
|
|
216
|
+
anyway. With nobody warm, staffing is exactly what it was: a session is
|
|
217
|
+
being opened either way, and the plan's own level wins.
|
|
218
|
+
- An operator who picks the member by hand on a retry gets that member. A
|
|
219
|
+
saving nobody asked for does not overrule an explicit answer.
|
|
188
220
|
- The run view reports who actually worked against who the plan said would.
|
|
189
221
|
`substituted` is the figure to read beside a cost that overran: every
|
|
190
222
|
substitution ran at or above the budgeted tier, so a run can be entirely green
|
|
191
|
-
and still have been staffed dearer than planned.
|
|
223
|
+
and still have been staffed dearer than planned. The phases promoted to reuse
|
|
224
|
+
a warm session are counted **within** that figure and reported apart from it —
|
|
225
|
+
a peer covering for a busy member costs what the plan budgeted, while a warm
|
|
226
|
+
promotion is the scheduler deliberately trading model rate against cold
|
|
227
|
+
starts, and folded together a run that promoted everything to the top tier
|
|
228
|
+
would read as an ordinary busy wave.
|
|
192
229
|
|
|
193
230
|
- **`implement-plan` runs independent phases in parallel.** The plan now carries a
|
|
194
231
|
dependency graph and the conductor schedules against it: a phase starts as soon as
|
|
@@ -303,10 +340,43 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
303
340
|
it rather than only in a log beside it. The operator's own steering stays the
|
|
304
341
|
operator's (§7), even though the adapter echoes it back on the agent's event
|
|
305
342
|
stream. Transcripts written before this render exactly as they did, with no
|
|
306
|
-
bands.
|
|
343
|
+
bands. The band stays pinned to the top of the scroller while its own run of
|
|
344
|
+
rows goes past, so the agent you are reading is named without scrolling back
|
|
345
|
+
to where it started — in the phase transcript and in the monitor conversation
|
|
346
|
+
alike.
|
|
307
347
|
|
|
308
348
|
### Changed
|
|
309
349
|
|
|
350
|
+
- **A session cannot inherit a machine's decision to stop compacting.** All
|
|
351
|
+
three harnesses compact automatically when their context fills — there was
|
|
352
|
+
never a flag to turn on — but each honours an environment variable that turns
|
|
353
|
+
it off, and an agent spawned on a machine carrying one would run a long phase
|
|
354
|
+
straight into a wall it was supposed to be able to survive. Those variables
|
|
355
|
+
are now stripped from every child the daemon spawns, and claude-code's own
|
|
356
|
+
setting is reasserted in the per-lane file beside its permission rules. The
|
|
357
|
+
window itself is left alone: an operator who narrowed theirs compacts earlier,
|
|
358
|
+
which is not the failure this guards against. A harness that compacts declares
|
|
359
|
+
it, and a compaction the harness reports is a transcript row like any other.
|
|
360
|
+
|
|
361
|
+
One consequence worth knowing: `max_session_turns` used to be a proxy for "this
|
|
362
|
+
session is nearing a context window it will die on". That death no longer
|
|
363
|
+
happens, so the ceiling now guards a different thing — a session whose memory
|
|
364
|
+
was replaced by a summary of itself, and which answers just as confidently.
|
|
365
|
+
|
|
366
|
+
- **Agent prompts name gates by id rather than printing their commands.** The
|
|
367
|
+
implementer's step 4 was descriptive — "Outer gate… These run against your
|
|
368
|
+
lane:" followed by the gate commands — and implementers were observed reading
|
|
369
|
+
it as a note about what would run later, finishing the scoped inner loop and
|
|
370
|
+
never running a gate at all. It is imperative now, and every role that runs a
|
|
371
|
+
gate (implementer, reviewer, fixer, and the continuation of each) is pointed
|
|
372
|
+
at `vinta-ai-maestro gate <id>`. The fixer prompts no longer say to re-run the
|
|
373
|
+
gates "until they are green", which contradicted the commit protocol directly
|
|
374
|
+
below them and left fixers running out the turn with the work uncommitted;
|
|
375
|
+
they now say to commit and report FAILURE when a gate cannot be turned green.
|
|
376
|
+
Implementers and fixers report which gates they ran and what each returned —
|
|
377
|
+
a record for the reviewer to check against, explicitly not a licence for the
|
|
378
|
+
reviewer to skip running them itself.
|
|
379
|
+
|
|
310
380
|
- **Phase branches base on their dependencies, not on the previous phase.** A phase
|
|
311
381
|
with no dependencies cuts from the default branch; one with a single dependency
|
|
312
382
|
cuts from that phase's branch; one with several cuts from a
|
|
@@ -360,8 +430,67 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
360
430
|
is only as good as the `healthcheck` a service declares: without one, compose
|
|
361
431
|
returns as soon as the container starts and `createdb` races the server.
|
|
362
432
|
|
|
433
|
+
- **A merge conflict goes to the most senior member who wrote one of its
|
|
434
|
+
sides.** The conflict fixer was staffed from `defaults` and ignored the roster
|
|
435
|
+
entirely, so on a tiered crew a collision between two Tier 4 phases was handed
|
|
436
|
+
to a default-tier agent that had read neither of them — the hardest merge in
|
|
437
|
+
the run going to the least contextualised agent available, and resolved by
|
|
438
|
+
guessing which side looked more finished.
|
|
439
|
+
|
|
440
|
+
`Integrator` already knows which nodes own the contested paths, and `node_crew`
|
|
441
|
+
already records who took each one; this is the join. Highest tier among them
|
|
442
|
+
wins, on that member's own `model` and `harness`, and a tier tie breaks on
|
|
443
|
+
member id the way roster substitution breaks its own. An unstaffed workflow
|
|
444
|
+
still resolves through `defaults`, unchanged, and so does a conflict whose
|
|
445
|
+
nodes cannot be resolved to a member.
|
|
446
|
+
|
|
447
|
+
What is carried is the member's **capability, not their session**. A phase's
|
|
448
|
+
session ran in that phase's lane; the fixer runs in the integration worktree,
|
|
449
|
+
where the files in dispute are half-merged and unlike anything that session
|
|
450
|
+
saw — resuming there would give the agent confident, false memories of exactly
|
|
451
|
+
the files it is merging. The prompt tells the fixer which side is its own, and
|
|
452
|
+
in the same breath that it is a fresh session and must read rather than
|
|
453
|
+
recall.
|
|
454
|
+
|
|
363
455
|
### Fixed
|
|
364
456
|
|
|
457
|
+
- **A member's reviews were counted as phases they took.** `node_crew` is
|
|
458
|
+
written for both seats — the reviewer's claim carries `role: 'reviewer'`, the
|
|
459
|
+
implementer's omits it — and the staffing rollup read neither, so every phase
|
|
460
|
+
a member reviewed was folded into the count of phases they implemented. On any
|
|
461
|
+
run with reviewers that inflated the number the rollup exists to put beside
|
|
462
|
+
the plan's estimate, and inflated `asPlanned`, which the run view divides by.
|
|
463
|
+
The two seats are counted apart now rather than one of them dropped: a review
|
|
464
|
+
is real work by a real member, and a member who only reviewed is neither a
|
|
465
|
+
phase-taker nor idle.
|
|
466
|
+
|
|
467
|
+
- **An agent taking a lease left the resource panel showing the one before it.**
|
|
468
|
+
The scheduler's own gate-pool transitions are journalled as `gate_pool`
|
|
469
|
+
events, so the browser re-reads the snapshot when one lands. An agent's lease,
|
|
470
|
+
taken through `vinta-ai-maestro with`, was only a row in the `leases` table —
|
|
471
|
+
correct in the daemon's projection and announced to nobody. A projection
|
|
472
|
+
nobody is told changed is a projection nobody sees change, so the panel showed
|
|
473
|
+
whatever holders it had last time some unrelated event happened to arrive, and
|
|
474
|
+
the waits it was stalest about were the agent ones, which are the long ones.
|
|
475
|
+
The broker now journals `agent_lease` on both edges. A renewal writes nothing:
|
|
476
|
+
it is a heartbeat on a lease already reported, and journalling it would bury
|
|
477
|
+
the two rows that are transitions under a liveness log.
|
|
478
|
+
|
|
479
|
+
- **An agent waiting for a semaphore could be overtaken without bound.** A
|
|
480
|
+
`vinta-ai-maestro with` wait is served in fifteen-second hops so no client
|
|
481
|
+
timeout has an opinion about it, and each hop used to abort the underlying
|
|
482
|
+
`pools.acquire`. Aborting leaves the pool queue, and the next POST re-entered
|
|
483
|
+
it — at the tail, behind every waiter that had arrived in the meantime. Since
|
|
484
|
+
a gate run holds a capacity-1 semaphore for minutes, an agent lost that race
|
|
485
|
+
on hop after hop while the scheduler's own in-process waiters, which never
|
|
486
|
+
leave the queue, did not. The pool's aging rule could not help: aging reserves
|
|
487
|
+
against waiters *behind* the aged one, and the waiters that overtook it are in
|
|
488
|
+
front of it. A `202` now carries a `waitToken` naming the wait, the client
|
|
489
|
+
sends it back, and the daemon keeps the one queue entry — one arrival time,
|
|
490
|
+
one place in line — across the whole wait. A parked wait whose client stops
|
|
491
|
+
hopping is reaped after a minute, releasing the slot if one was granted to it
|
|
492
|
+
while nobody was watching.
|
|
493
|
+
|
|
365
494
|
- **The monitor's conversation could never be written on Windows.** It is kept
|
|
366
495
|
under a reserved node id that becomes a directory, and that id was
|
|
367
496
|
`monitor:conversation` — a colon, chosen because no phase id may contain one,
|
package/package.json
CHANGED