vinta-ai-workflows 0.7.0-alpha31 → 0.7.0-alpha32

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
@@ -32,6 +32,35 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
32
32
 
33
33
  ### Added
34
34
 
35
+ - **`vinta-ai-maestro`: a busy crew member is replaced, not borrowed
36
+ (`defaults.max_spare_crew`, default 2).** When the member a phase is planned
37
+ for — or the one an operator picked with `retry with <member>` — is busy, or
38
+ is planned for a phase still to come, the run takes on a spare in that
39
+ member's image (same tier, model and harness) with a worktree provisioned for
40
+ it there and then, instead of borrowing a member another pending phase is
41
+ waiting for, or waiting. A member nobody is planned for is still borrowed, an
42
+ idle spare is reused first, and a member busy only with a finished phase's PR
43
+ chores is waited for. Journalled as `node_crew` `reason: spare_crew` and shown
44
+ in the post-mortem's `crew_substitutions`. Set `0` in the plan or under
45
+ `maestro.defaults` to keep the roster to the plan's. One observed run left a
46
+ phase on its critical path idle for 17 minutes while a retry held the member
47
+ it was planned for.
48
+ - **`vinta-ai-maestro`: `defaults.pull_requests: plan` opens one PR for the
49
+ run.** The default, `phase`, is unchanged: a PR per phase, per integration
50
+ branch, and the plan PR. `plan` opens the plan PR alone, from the last wave
51
+ branch, and skips the phases' `after_pr` chores (there is no phase PR for
52
+ them). Also accepted under `maestro.defaults` in `.vinta-ai-workflows.yaml`.
53
+ - **`vinta-ai-maestro doctor` warns when a phase gate will run its full command
54
+ every time** (`gate-scope:<gate>`), and every run prints it at start: a
55
+ `test` or `lint` gate a phase uses has no `scoped_cmd` under
56
+ `gate_scope: scoped`. It says whether `commands.test_unit_scoped` /
57
+ `lint_scoped` was passed over for having no `{changed_files}` / `{touches}`
58
+ placeholder — a skill-era line such as `pytest <app>/tests/` is not inherited
59
+ — or nothing is configured, and names the key to set (e.g.
60
+ `pytest --testmon {changed_files}`), or `maestro.defaults.gate_scope: full`
61
+ to accept it. One observed run spent two of its five hours running a
62
+ 6,500-test suite on every phase gate and fix round.
63
+
35
64
  - **`vinta-ai-maestro`: `run` now shows what a slow start is doing.** Until
36
65
  now `run` printed "starting in the background…" and nothing else until the
37
66
  run was up, which can take minutes (`prepare_cmd`, preflight, database
@@ -523,6 +552,70 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
523
552
 
524
553
  ### Changed
525
554
 
555
+ - **`vinta-ai-maestro`: agents wait for background work the way a normal
556
+ Claude Code session does.** A headless session no longer ends at the first
557
+ turn that leaves background work running. It stays open, the agent is woken
558
+ in the same session when that work reports, and the session ends on the
559
+ first turn that leaves nothing running. A held session ends after 30 minutes
560
+ with no output, and any work still running is cleaned up. Harnesses declare
561
+ this as a new `backgroundWork` capability:
562
+ - **claude-code:** the CLI wakes the agent itself. A resumed session also no
563
+ longer ends on the empty turn the CLI replays first.
564
+ - **codex:** the adapter wakes the agent itself (see below).
565
+ - **opencode:** on servers the adapter starts, background `task` sub-agents
566
+ are enabled through `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`. A
567
+ configured `baseUrl` reports `false`.
568
+
569
+ This fixes the slow review loop. The reviewer, resumed with a message, was
570
+ killed with the turn. Every round after the first ended "waiting on the
571
+ reviewer" with no verdict, went to the operator as `unapproved`, and
572
+ restarted with a cold reviewer. Every turn's prompt now says how to wait on
573
+ its harness (end the turn, never sleep or poll), naming that harness's own
574
+ tools. The review starts each round's gates alongside the reviewer's next
575
+ pass; on opencode, which has no background shell, the reviewer runs in the
576
+ background and the gates run beside it.
577
+
578
+ - **`vinta-ai-maestro`: codex runs on `codex app-server` instead of
579
+ `codex exec`, and needs codex-cli 0.147.0 or later.** `exec` killed
580
+ background work when the turn ended. App-server keeps it, and the adapter
581
+ starts the next turn naming, by id, what finished. Codex now also supports
582
+ `inject`, so steering a running codex node is a message, no longer an
583
+ interrupt and resume. Each thread is started and resumed with an explicit
584
+ sandbox, approval policy and reviewer, so `~/.codex/config.toml` can no
585
+ longer widen `--permission`. Under `--permission ask`, a request for approval
586
+ is now recorded as a `permission_request` and declined; before, the model
587
+ could not ask at all. A logged-out codex is refused at spawn instead of
588
+ failing mid-turn.
589
+
590
+ - **`vinta-ai-maestro`: an opencode review can no longer pass on its own
591
+ prompt.** The adapter echoed the user's prompt back as the agent's text. The
592
+ review prompt contains the line `VERDICT: pass`, so a review that stated no
593
+ verdict could read as approved. opencode 1.19's `permission.asked` event is
594
+ now mapped too.
595
+ - **`vinta-ai-maestro`: an unattended run answers `exhausted` and `unapproved`
596
+ with `continue` once, then `stop`.** It answered `stop` straight away, which
597
+ failed the phase; the unattended retry then re-drove it from `implement` on
598
+ a cold session, gates and review from scratch, to reach the same question —
599
+ twelve failed attempts in one observed run. A plan question's
600
+ `unattended_answer` may now be a list, one answer per unattended answer
601
+ within an attempt, the last repeating.
602
+ - **`vinta-ai-maestro`: a phase's dependents start once it has merged, pushed
603
+ and opened its PR**, when only `after_pr` chores that cannot fail it are left
604
+ (a review canvas, a PR comment). They used to wait for those too — up to four
605
+ minutes a phase on the critical path.
606
+ - **`vinta-ai-maestro`: an agent's `gate <id>` is refused while an earlier gate
607
+ of the phase is red on the same tree**, naming that gate, and runs once the
608
+ tree moves. A `for g in lint typecheck test` loop no longer runs the suite
609
+ against a tree its typecheck already failed.
610
+ - **`vinta-ai-maestro`: a crew member woken on a new phase is told the
611
+ objective changed** before anything else — the previous phase is finished
612
+ and nothing in flight for it is owed — ahead of the brief and the note about
613
+ what moved in its worktree.
614
+ - **`vinta-ai-maestro`: the coordinator's gate-cost wake scales with the run**:
615
+ more than 15 minutes of uncached gate time per phase that ran it, never less
616
+ than 30 minutes, instead of every 30 minutes; it says how many phases the
617
+ time is spread over and whether the gate lacks a scoped form.
618
+
526
619
  - **`vinta-ai-maestro`: lane database names stay within Postgres's 63-character
527
620
  identifier limit, with room for test-runner affixes.** A forked lane database
528
621
  or template whose `<name>_wt_<lane>` form was too long was silently truncated
@@ -798,6 +891,24 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
798
891
 
799
892
  ### Fixed
800
893
 
894
+ - **`vinta-ai-maestro`: a turn the agent never took part in is no longer read
895
+ as work.** A session that ended with no text and no tool call — observed when
896
+ a resumed claude-code session closed a replay of killed background tasks
897
+ before reading the prompt — counted as a failed review with no reviewer
898
+ behind it, or, on a crew member's first turn of a new phase, swallowed the
899
+ new phase's brief so every later turn carried on with the old phase. Such a
900
+ turn is now retried once, cold, with the whole brief (`node_session`
901
+ `reason: empty_turn`), and a cold one that is empty too fails the turn.
902
+
903
+ - **Maestro: an amended gate re-runs instead of returning its old result.**
904
+ The gate cache matched only the gate's command, so changing a gate's
905
+ `timeout_s` (the monitor's `retime_gate`) or its `requires` partway through
906
+ a run reused the result from the old settings on an unchanged tree. The
907
+ cache now matches every setting that affects the run. Edits to
908
+ `description` or `tuning` still reuse the result, and so does a
909
+ `scoped_cmd` edit for runs that use the full `cmd`. Existing
910
+ `gate-cache.db` entries are dropped once, at the first start after
911
+ upgrading.
801
912
  - **Maestro: PRs say how the plan lands.** Every phase and integration PR is
802
913
  marked as a review unit that lands through the plan PR, and the plan PR says
803
914
  to merge it alone; it used to offer two landing paths, and a 36-PR landing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinta-ai-workflows",
3
- "version": "0.7.0-alpha31",
3
+ "version": "0.7.0-alpha32",
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": {
@@ -593,9 +593,10 @@
593
593
  "type": "string",
594
594
  "enum": [
595
595
  "peer_busy",
596
- "warm_session"
596
+ "warm_session",
597
+ "spare_crew"
597
598
  ],
598
- "description": "`peer_busy`: the planned member was working. `warm_session`: promoted to reuse an open session."
599
+ "description": "`peer_busy`: the planned member was working. `warm_session`: promoted to reuse an open session. `spare_crew`: the planned member was working, and a spare was taken on in their image."
599
600
  },
600
601
  "attempts": {
601
602
  "type": "integer",
@@ -339,7 +339,7 @@
339
339
  "additionalProperties": false,
340
340
  "properties": {
341
341
  "defaults": {
342
- "description": "Run defaults a plan's `defaults` overrides field by field: `harness`, `model`, `pipeline`, `chores`, `gates` (the gate ids every phase runs unless it names its own), `gate_scope` (`scoped` | `full`), `wave_gates` (`off` | `final` | `every`), `substitution` (`same_tier` | `up_one_tier` | `any`), `allow_ungated_phases`, `model_fallbacks`, `max_session_turns`.",
342
+ "description": "Run defaults a plan's `defaults` overrides field by field: `harness`, `model`, `pipeline`, `chores`, `gates` (the gate ids every phase runs unless it names its own), `gate_scope` (`scoped` | `full`), `wave_gates` (`off` | `final` | `every`), `substitution` (`same_tier` | `up_one_tier` | `any`), `allow_ungated_phases`, `model_fallbacks`, `max_session_turns`, `max_spare_crew`, `pull_requests` (`phase` | `plan`).",
343
343
  "type": "object",
344
344
  "properties": {
345
345
  "harness": {
@@ -412,6 +412,19 @@
412
412
  "max_session_turns": {
413
413
  "type": "integer",
414
414
  "minimum": 1
415
+ },
416
+ "pull_requests": {
417
+ "type": "string",
418
+ "enum": [
419
+ "phase",
420
+ "plan"
421
+ ],
422
+ "description": "Which pull requests a run opens. `phase` (the default): one per phase, one per integration branch, and the plan PR that lands them. `plan`: the plan PR alone, from the last wave branch; phases' `after_pr` chores are then skipped."
423
+ },
424
+ "max_spare_crew": {
425
+ "type": "integer",
426
+ "minimum": 0,
427
+ "description": "Crew members a run may take on beyond the plan's roster (default 2). When the member a phase was planned for is busy, the phase gets a new member at that member's tier and model, with its own worktree, instead of borrowing a member another pending phase is planned for or waiting. `0` keeps the roster to the plan's."
415
428
  }
416
429
  },
417
430
  "additionalProperties": false
@@ -423,6 +423,22 @@
423
423
  "any"
424
424
  ]
425
425
  },
426
+ "pull_requests": {
427
+ "default": "phase",
428
+ "description": "Which pull requests a run opens. `phase` — the default — one per phase, one per integration branch, and the plan PR that lands them all: the phase PRs are review units stacked in merge order. `plan` opens the plan PR alone, from the last wave branch, which already holds every phase and every conflict resolution; review happens there. One observed ten-phase refactor opened fifteen PRs that nobody was going to merge. Under `plan` a phase has no PR of its own, so its `after_pr` chores are skipped.",
429
+ "type": "string",
430
+ "enum": [
431
+ "phase",
432
+ "plan"
433
+ ]
434
+ },
435
+ "max_spare_crew": {
436
+ "default": 2,
437
+ "description": "Crew members a run may take on beyond the roster. When the member a phase was planned for is busy, the phase does not borrow a member another pending phase is planned for, and does not wait: it gets a new member at the planned member’s tier and model, with a worktree of its own provisioned there and then. A member nobody plans for is borrowed as before. The spare stays on the roster for the rest of the run, is never planned for, and covers later phases at its tier. `retry with <member>` takes a spare in that member’s image the same way when the member is needed elsewhere. A worktree, its databases and its compose stack each, so this is bounded; `0` keeps the roster to the plan’s and restores waiting and covering. One observed run left a phase on the critical path idle for 17 minutes while a retry held the member it was planned for.",
438
+ "type": "integer",
439
+ "minimum": 0,
440
+ "maximum": 9007199254740991
441
+ },
426
442
  "wave_gates": {
427
443
  "default": "final",
428
444
  "description": "When the phases’ gates run in full on a merged wave, in the integration worktree. Each phase passes its gates on its own branch, and two parallel phases can each be green and disagree with each other — a sender and a receiver of one signal, say — which only a merged tree shows. `final` — the default — runs every gate of the plan once on the last wave before the plan PR opens, and the wave’s phases’ gates on any wave whose merge needed a conflict resolution. `every` runs the wave’s phases’ gates on every wave as well, which costs one full suite per wave and catches a disagreement at the wave that introduced it. `off` runs none of these. Under `gate_scope: scoped` the full form of a narrowed gate still runs on each wave whatever this says: that is where the suite the phases skipped is paid back.",