vinta-ai-workflows 0.7.0-alpha31 → 0.7.0-alpha33
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,7 @@ 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-alpha33 on 2026-10-16 -->
|
|
10
11
|
<!-- pre-release: 0.7.0-alpha31 on 2026-10-09 -->
|
|
11
12
|
<!-- pre-release: 0.7.0-alpha30 on 2026-10-09 -->
|
|
12
13
|
<!-- pre-release: 0.7.0-alpha20 on 2026-09-24 -->
|
|
@@ -32,6 +33,91 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
32
33
|
|
|
33
34
|
### Added
|
|
34
35
|
|
|
36
|
+
- **`vinta-ai-maestro`: ways past a stale gate verdict.** `gate <id>
|
|
37
|
+
--no-cache` runs a gate past its cached result and keeps the new one in its
|
|
38
|
+
place; a cached red now says so. `gate-cache invalidate <run-id> <gate-id>
|
|
39
|
+
[--tree <hash>]` forgets a gate's results for the operator and the run
|
|
40
|
+
coordinator (`POST /api/runs/:id/gates/:gate/invalidate`, journalled as
|
|
41
|
+
`gate_cache_invalidated`; an agent's token cannot reach it). Both are for a
|
|
42
|
+
verdict whose cause git does not track — an ignored file, a tool's cache, an
|
|
43
|
+
installed dependency — which the tree-keyed cache would otherwise replay.
|
|
44
|
+
|
|
45
|
+
- **`vinta-ai-maestro`: a phase's decisions outlive the session they were
|
|
46
|
+
made in.** Each node now keeps a list of what was settled for it: agents'
|
|
47
|
+
questions with their answers (marked as the operator's, the coordinator's,
|
|
48
|
+
or unattended), the operator's `node context` and `redirect` text, and the
|
|
49
|
+
findings the review rejected with their counter-evidence. Every later
|
|
50
|
+
prompt carries the list — implementer, fixer, chores, and the review, which
|
|
51
|
+
hands it to its reviewer — so a fresh reviewer, a replaced session or a
|
|
52
|
+
retry no longer re-asks or re-raises what was already decided. Before
|
|
53
|
+
this, an unattended run answered one question four times and a phase ran
|
|
54
|
+
some thirty rounds without converging, and operator context sent with
|
|
55
|
+
`node context` had to be re-sent after a session change. The review now
|
|
56
|
+
rejects a finding that re-opens a decision, citing it, and asks the
|
|
57
|
+
operator about it at most once, and only with new evidence. It ends its
|
|
58
|
+
report with a `REJECTED:` line per rejected finding, which the run keeps.
|
|
59
|
+
The list is bounded (the newest 12 answers and notes and the newest 8
|
|
60
|
+
rejected findings), kept across retries, written to
|
|
61
|
+
`runs/<id>/nodes/<node>/decisions.jsonl` and read back when a run resumes.
|
|
62
|
+
|
|
63
|
+
- **`vinta-ai-maestro judge calibrate`: measure a permission-judge question
|
|
64
|
+
before a run relies on it.** It replays a past run's `Bash` calls
|
|
65
|
+
(`--run <run-id>`), or a file of commands each optionally labelled `safe` or
|
|
66
|
+
`unsafe` (`--commands <file>`), against a candidate question
|
|
67
|
+
(`--question` / `--question-file`) and threshold (`--threshold`) through the
|
|
68
|
+
classifier in `--system-one`. Nothing is executed. It reports how many would
|
|
69
|
+
be allowed and refused, the score distribution, every labelled command
|
|
70
|
+
judged the wrong way, and the highest-scoring must-deny command, and exits 1
|
|
71
|
+
on a misjudged one so a case file can guard a project's question in CI.
|
|
72
|
+
Commands are printed as recorded, never their output.
|
|
73
|
+
|
|
74
|
+
- **`vinta-ai-maestro`: `project.reset_cmd` and `project.recycle_keep`.**
|
|
75
|
+
`reset_cmd` is a project hook run inside a lane when it is recycled for the
|
|
76
|
+
next phase, and only then, after maestro's own resets and before
|
|
77
|
+
`setup_cmd`. `recycle_keep` lists gitignore patterns a recycled lane keeps
|
|
78
|
+
(`.venv`, say) now that the recycle cleans ignored files. Both optional, in
|
|
79
|
+
`workflow.v1`.
|
|
80
|
+
|
|
81
|
+
- **`vinta-ai-maestro`: `node fresh <run> <phase>` drops a phase's agent
|
|
82
|
+
session without failing the attempt.** The phase's next turn starts cold with
|
|
83
|
+
the whole brief, journalled `node_session` `reason: operator_fresh`; the turn
|
|
84
|
+
in flight is not stopped (pair it with `redirect` for that). It is the way out
|
|
85
|
+
of a session gone wrong — an agent replaying killed work or carrying on with
|
|
86
|
+
the previous phase — that used to cost an `abort`. Also `POST
|
|
87
|
+
/api/runs/:run/nodes/:node/fresh`, and the run coordinator can use it. The
|
|
88
|
+
post-mortem's `operations.node_operations[].op` gains `fresh` (additive, in
|
|
89
|
+
`postmortem.v1.schema.json`).
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
- **`vinta-ai-maestro`: a busy crew member is replaced, not borrowed
|
|
93
|
+
(`defaults.max_spare_crew`, default 2).** When the member a phase is planned
|
|
94
|
+
for — or the one an operator picked with `retry with <member>` — is busy, or
|
|
95
|
+
is planned for a phase still to come, the run takes on a spare in that
|
|
96
|
+
member's image (same tier, model and harness) with a worktree provisioned for
|
|
97
|
+
it there and then, instead of borrowing a member another pending phase is
|
|
98
|
+
waiting for, or waiting. A member nobody is planned for is still borrowed, an
|
|
99
|
+
idle spare is reused first, and a member busy only with a finished phase's PR
|
|
100
|
+
chores is waited for. Journalled as `node_crew` `reason: spare_crew` and shown
|
|
101
|
+
in the post-mortem's `crew_substitutions`. Set `0` in the plan or under
|
|
102
|
+
`maestro.defaults` to keep the roster to the plan's. One observed run left a
|
|
103
|
+
phase on its critical path idle for 17 minutes while a retry held the member
|
|
104
|
+
it was planned for.
|
|
105
|
+
- **`vinta-ai-maestro`: `defaults.pull_requests: plan` opens one PR for the
|
|
106
|
+
run.** The default, `phase`, is unchanged: a PR per phase, per integration
|
|
107
|
+
branch, and the plan PR. `plan` opens the plan PR alone, from the last wave
|
|
108
|
+
branch, and skips the phases' `after_pr` chores (there is no phase PR for
|
|
109
|
+
them). Also accepted under `maestro.defaults` in `.vinta-ai-workflows.yaml`.
|
|
110
|
+
- **`vinta-ai-maestro doctor` warns when a phase gate will run its full command
|
|
111
|
+
every time** (`gate-scope:<gate>`), and every run prints it at start: a
|
|
112
|
+
`test` or `lint` gate a phase uses has no `scoped_cmd` under
|
|
113
|
+
`gate_scope: scoped`. It says whether `commands.test_unit_scoped` /
|
|
114
|
+
`lint_scoped` was passed over for having no `{changed_files}` / `{touches}`
|
|
115
|
+
placeholder — a skill-era line such as `pytest <app>/tests/` is not inherited
|
|
116
|
+
— or nothing is configured, and names the key to set (e.g.
|
|
117
|
+
`pytest --testmon {changed_files}`), or `maestro.defaults.gate_scope: full`
|
|
118
|
+
to accept it. One observed run spent two of its five hours running a
|
|
119
|
+
6,500-test suite on every phase gate and fix round.
|
|
120
|
+
|
|
35
121
|
- **`vinta-ai-maestro`: `run` now shows what a slow start is doing.** Until
|
|
36
122
|
now `run` printed "starting in the background…" and nothing else until the
|
|
37
123
|
run was up, which can take minutes (`prepare_cmd`, preflight, database
|
|
@@ -523,6 +609,79 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
523
609
|
|
|
524
610
|
### Changed
|
|
525
611
|
|
|
612
|
+
- **`vinta-ai-maestro`: a recycled lane no longer keeps the last phase's
|
|
613
|
+
ignored files.** A lane handed to the next phase kept everything gitignored,
|
|
614
|
+
and one kept `apps/api/.mypy_cache` and a deleted package's `__pycache__`,
|
|
615
|
+
so mypy reported 18 errors on clean code. The recycle now cleans ignored
|
|
616
|
+
files too, keeping only the copied `node_modules`, the `env_files` and the
|
|
617
|
+
new `project.recycle_keep` patterns. **Migration:** anything expensive your
|
|
618
|
+
lanes build and should keep across phases, such as a `.venv`, goes in
|
|
619
|
+
`recycle_keep`; otherwise `setup_cmd`, which already runs on every
|
|
620
|
+
recycle, rebuilds it.
|
|
621
|
+
- **`vinta-ai-maestro`: agents wait for background work the way a normal
|
|
622
|
+
Claude Code session does.** A headless session no longer ends at the first
|
|
623
|
+
turn that leaves background work running. It stays open, the agent is woken
|
|
624
|
+
in the same session when that work reports, and the session ends on the
|
|
625
|
+
first turn that leaves nothing running. A held session ends after 30 minutes
|
|
626
|
+
with no output, and any work still running is cleaned up. Harnesses declare
|
|
627
|
+
this as a new `backgroundWork` capability:
|
|
628
|
+
- **claude-code:** the CLI wakes the agent itself. A resumed session also no
|
|
629
|
+
longer ends on the empty turn the CLI replays first.
|
|
630
|
+
- **codex:** the adapter wakes the agent itself (see below).
|
|
631
|
+
- **opencode:** on servers the adapter starts, background `task` sub-agents
|
|
632
|
+
are enabled through `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`. A
|
|
633
|
+
configured `baseUrl` reports `false`.
|
|
634
|
+
|
|
635
|
+
This fixes the slow review loop. The reviewer, resumed with a message, was
|
|
636
|
+
killed with the turn. Every round after the first ended "waiting on the
|
|
637
|
+
reviewer" with no verdict, went to the operator as `unapproved`, and
|
|
638
|
+
restarted with a cold reviewer. Every turn's prompt now says how to wait on
|
|
639
|
+
its harness (end the turn, never sleep or poll), naming that harness's own
|
|
640
|
+
tools. The review starts each round's gates alongside the reviewer's next
|
|
641
|
+
pass; on opencode, which has no background shell, the reviewer runs in the
|
|
642
|
+
background and the gates run beside it.
|
|
643
|
+
|
|
644
|
+
- **`vinta-ai-maestro`: codex runs on `codex app-server` instead of
|
|
645
|
+
`codex exec`, and needs codex-cli 0.147.0 or later.** `exec` killed
|
|
646
|
+
background work when the turn ended. App-server keeps it, and the adapter
|
|
647
|
+
starts the next turn naming, by id, what finished. Codex now also supports
|
|
648
|
+
`inject`, so steering a running codex node is a message, no longer an
|
|
649
|
+
interrupt and resume. Each thread is started and resumed with an explicit
|
|
650
|
+
sandbox, approval policy and reviewer, so `~/.codex/config.toml` can no
|
|
651
|
+
longer widen `--permission`. Under `--permission ask`, a request for approval
|
|
652
|
+
is now recorded as a `permission_request` and declined; before, the model
|
|
653
|
+
could not ask at all. A logged-out codex is refused at spawn instead of
|
|
654
|
+
failing mid-turn.
|
|
655
|
+
|
|
656
|
+
- **`vinta-ai-maestro`: an opencode review can no longer pass on its own
|
|
657
|
+
prompt.** The adapter echoed the user's prompt back as the agent's text. The
|
|
658
|
+
review prompt contains the line `VERDICT: pass`, so a review that stated no
|
|
659
|
+
verdict could read as approved. opencode 1.19's `permission.asked` event is
|
|
660
|
+
now mapped too.
|
|
661
|
+
- **`vinta-ai-maestro`: an unattended run answers `exhausted` and `unapproved`
|
|
662
|
+
with `continue` once, then `stop`.** It answered `stop` straight away, which
|
|
663
|
+
failed the phase; the unattended retry then re-drove it from `implement` on
|
|
664
|
+
a cold session, gates and review from scratch, to reach the same question —
|
|
665
|
+
twelve failed attempts in one observed run. A plan question's
|
|
666
|
+
`unattended_answer` may now be a list, one answer per unattended answer
|
|
667
|
+
within an attempt, the last repeating.
|
|
668
|
+
- **`vinta-ai-maestro`: a phase's dependents start once it has merged, pushed
|
|
669
|
+
and opened its PR**, when only `after_pr` chores that cannot fail it are left
|
|
670
|
+
(a review canvas, a PR comment). They used to wait for those too — up to four
|
|
671
|
+
minutes a phase on the critical path.
|
|
672
|
+
- **`vinta-ai-maestro`: an agent's `gate <id>` is refused while an earlier gate
|
|
673
|
+
of the phase is red on the same tree**, naming that gate, and runs once the
|
|
674
|
+
tree moves. A `for g in lint typecheck test` loop no longer runs the suite
|
|
675
|
+
against a tree its typecheck already failed.
|
|
676
|
+
- **`vinta-ai-maestro`: a crew member woken on a new phase is told the
|
|
677
|
+
objective changed** before anything else — the previous phase is finished
|
|
678
|
+
and nothing in flight for it is owed — ahead of the brief and the note about
|
|
679
|
+
what moved in its worktree.
|
|
680
|
+
- **`vinta-ai-maestro`: the coordinator's gate-cost wake scales with the run**:
|
|
681
|
+
more than 15 minutes of uncached gate time per phase that ran it, never less
|
|
682
|
+
than 30 minutes, instead of every 30 minutes; it says how many phases the
|
|
683
|
+
time is spread over and whether the gate lacks a scoped form.
|
|
684
|
+
|
|
526
685
|
- **`vinta-ai-maestro`: lane database names stay within Postgres's 63-character
|
|
527
686
|
identifier limit, with room for test-runner affixes.** A forked lane database
|
|
528
687
|
or template whose `<name>_wt_<lane>` form was too long was silently truncated
|
|
@@ -798,6 +957,144 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
798
957
|
|
|
799
958
|
### Fixed
|
|
800
959
|
|
|
960
|
+
- **`vinta-ai-maestro`: a fix loop that changes nothing now stops and asks.**
|
|
961
|
+
A typecheck red because of ignored files left in a lane kept coming back
|
|
962
|
+
from the gate cache after they were gone, and twenty fixers in a row found
|
|
963
|
+
nothing to fix. When a gate is red again on the very tree a fixer turn left
|
|
964
|
+
unchanged, the scheduler reruns that gate once without the cache; if it is
|
|
965
|
+
still red the same way, `standard-phase` goes to `exhausted` instead of
|
|
966
|
+
spawning another fixer — with or without `max_fix_rounds` — and the run
|
|
967
|
+
coordinator is woken. New guard fact `gate.stalled`, new `run_gate` param
|
|
968
|
+
`no_cache`, new journal event `gate_stalled`. A fixer that moved the tree at
|
|
969
|
+
all is never a stall. A custom pipeline keeps its own guards and can opt in
|
|
970
|
+
on `gate.stalled`.
|
|
971
|
+
|
|
972
|
+
- **`vinta-ai-maestro`: a claude-code review no longer loses a reviewer answer
|
|
973
|
+
that comes back early.** When a reviewer resumed with `SendMessage` answered
|
|
974
|
+
while the agent was still writing its last message, the session ended at the
|
|
975
|
+
turn's `result` and killed the turn the CLI starts to hand over the answer.
|
|
976
|
+
The review then stated no verdict and went to the operator as `unapproved`.
|
|
977
|
+
The session now waits for that turn, for at most a minute of silence.
|
|
978
|
+
|
|
979
|
+
- **`vinta-ai-maestro purge` no longer ends a run you did not name.** A bulk
|
|
980
|
+
`purge --lanes` removed the lanes of paused, failed and interrupted runs —
|
|
981
|
+
the integration worktree included — so `run --resume` had nothing to adopt,
|
|
982
|
+
and a bulk `purge` deleted those runs' frozen snapshots. A run that could
|
|
983
|
+
still be resumed is now kept whole, directory and lanes, and named with the
|
|
984
|
+
reason; purge it by name, or pass the new `--force`. A run whose job is
|
|
985
|
+
still running is kept even then.
|
|
986
|
+
- **`vinta-ai-maestro purge --lanes` tears a lane down, not just its
|
|
987
|
+
worktree.** It used to leave every forked database, compose stack and lane
|
|
988
|
+
summary behind, including summaries whose worktree was already gone. Each
|
|
989
|
+
reaped lane's summary is now its teardown manifest: `docker compose -p
|
|
990
|
+
<project> down`, `docker volume rm` for its forked volumes, the `reset_cmd`
|
|
991
|
+
of its own service namespaces, and `dropdb` (or the project's `dropdb_cmd`)
|
|
992
|
+
for its forked databases — every command listed before anything runs, and
|
|
993
|
+
under `--dry-run` nothing runs. Only what is provably that lane's is
|
|
994
|
+
touched: never a compose-delivered database, an index-numbered service slot,
|
|
995
|
+
or a summary `prepare-worktree` wrote for a worktree you made. A step that
|
|
996
|
+
fails is reported, and its lane's summary is kept so a rerun retries it.
|
|
997
|
+
- **`vinta-ai-maestro run --help` says which runs resume.** It claimed a run
|
|
998
|
+
that "finished" could not be resumed, while a `failed` run resumes — its
|
|
999
|
+
failed phases and the ones they blocked are attempted again, and `status`
|
|
1000
|
+
prints the command for it. The help now names paused, interrupted and
|
|
1001
|
+
failed as resumable, and only `done` and `cancelled` as refused.
|
|
1002
|
+
- **`vinta-ai-maestro`: the shipped permission-judge question no longer
|
|
1003
|
+
refuses routine commands.** Under `--permission judged` it allowed 44 of 69
|
|
1004
|
+
commands from a real run, refusing `ls .env*`, `cat .env.example`,
|
|
1005
|
+
`docker ps` and the project's own `make` targets. The question now says a
|
|
1006
|
+
secret is a *value* — listing env files and reading templates is allowed,
|
|
1007
|
+
reading a real `.env` is not — allows read-only inspection and the project's
|
|
1008
|
+
own commands in its worktree, and still refuses pushes, `sudo`, writes
|
|
1009
|
+
outside the worktree and another project's containers or volumes. A
|
|
1010
|
+
project that set its own `judges.permission.question` is unaffected; the
|
|
1011
|
+
default `allow_above` stays 0.9.
|
|
1012
|
+
|
|
1013
|
+
- **`vinta-ai-maestro`: the disk preflight sizes the pool the run will
|
|
1014
|
+
really create.** `run --resume` with every worktree still on disk was
|
|
1015
|
+
refused (`3 lanes + 1 integration worktree needs 37.9 GiB, only 18.0 GiB
|
|
1016
|
+
free`) by a doctor that charged for them all again; it now charges only the
|
|
1017
|
+
worktrees still to be made, and passes a resume that makes none. A staffed run
|
|
1018
|
+
is counted as the pool provisions it — a worktree per crew member, not per
|
|
1019
|
+
unit of `resources.lane.capacity` — plus `defaults.max_spare_crew`
|
|
1020
|
+
headroom, and the doctor, the pool and the refusal line now name the same
|
|
1021
|
+
count. The per-lane estimate is the tracked tree at the base branch, the
|
|
1022
|
+
copied `node_modules` and the `env_files`, instead of a walk of the whole
|
|
1023
|
+
main checkout that counted `.git`, the `.vinta-ai-maestro/` store (lanes
|
|
1024
|
+
included) and ignored caches — one run's estimate had grown from 4.0 to
|
|
1025
|
+
9.5 GiB a lane against real lanes of about 1.1 GiB.
|
|
1026
|
+
|
|
1027
|
+
- **`vinta-ai-maestro`: an integration merge a pause, stop or kill caught
|
|
1028
|
+
half-way no longer wedges the run.** Every later attempt used to fail on
|
|
1029
|
+
`integration worktree is mid-merge` until someone ran `git merge --abort` by
|
|
1030
|
+
hand. The integrator now recognises a merge it started — one of the plan's
|
|
1031
|
+
phase branches (or a kept resolution, or the plan branch) into one of its
|
|
1032
|
+
`integ-`/`wave-` branches — keeps what it had under
|
|
1033
|
+
`refs/vinta-ai-maestro/abandoned/<branch>`, aborts it and redoes it, so the
|
|
1034
|
+
result is gated again; each one journals `integration_reclaimed`. The same
|
|
1035
|
+
sweep runs on `run --resume` and when a stop outlives its deadline. A conflict
|
|
1036
|
+
left for a person that nobody finished is redone on the retry the same way.
|
|
1037
|
+
A merge the run did not start is still refused by name. A failed `git_merge`
|
|
1038
|
+
of a named branch now aborts its merge.
|
|
1039
|
+
|
|
1040
|
+
- **`vinta-ai-maestro`: aborting the only runnable phase no longer ends the
|
|
1041
|
+
run.** A run whose remaining phases all depended on an aborted one ended
|
|
1042
|
+
`failed` at once, and the `node retry` that followed was refused as `run has
|
|
1043
|
+
ended`. Aborted phases now hold the run open for their retry (no timer retries
|
|
1044
|
+
them); `pause` or `stop` ends it. Under `--on-failure stop` an abort still ends
|
|
1045
|
+
the run. A retry sent while the aborted attempt is still winding down is
|
|
1046
|
+
applied once it has.
|
|
1047
|
+
|
|
1048
|
+
- **`vinta-ai-maestro`: a gate run inside `with` no longer deadlocks every
|
|
1049
|
+
lane.** `vinta-ai-maestro with test-suite -- vinta-ai-maestro gate test`
|
|
1050
|
+
held the only `test-suite` slot while the gate queued for it, and every
|
|
1051
|
+
lane's tests and gates queued behind the pair (55 minutes in one run). The
|
|
1052
|
+
gate guard now refuses `gate` inside `with` and tells the agent to run the
|
|
1053
|
+
gate on its own. The daemon refuses a gate whose resources the asking phase
|
|
1054
|
+
already holds through `with` (`holds_required:<resource>`), naming the
|
|
1055
|
+
process holding them. The agent prompts state the same rule.
|
|
1056
|
+
- **`vinta-ai-maestro`: `with` and `gate` say who they are waiting on.** A
|
|
1057
|
+
queued answer names each holder of the resource: the phase, its lane, and
|
|
1058
|
+
how it holds it (`with` with its process id, a gate an agent ran, or the
|
|
1059
|
+
phase's own gate step). When the holder is the asking phase itself, the
|
|
1060
|
+
message says so. It used to say "held by another lane" in exactly that case.
|
|
1061
|
+
`gate` now says "queued" until the gate has its resources and "running"
|
|
1062
|
+
after; it used to say "running" throughout.
|
|
1063
|
+
- **`vinta-ai-maestro`: commands left behind by an ended agent session stop
|
|
1064
|
+
holding capacity.** Each agent session gets a turn id
|
|
1065
|
+
(`VINTA_AI_MAESTRO_TURN_ID`), which `with` and `gate` send with every
|
|
1066
|
+
request. When the session ends, the leases its commands hold are released,
|
|
1067
|
+
their queued waits and queued gates are cancelled, and their next request is
|
|
1068
|
+
refused (`turn_ended`). A request for a phase that is not running is refused
|
|
1069
|
+
too (`holder_not_running`). A command backgrounded with `&` used to keep its
|
|
1070
|
+
place in the queue for as long as it lived: one run had nine such waiters,
|
|
1071
|
+
up to 55 minutes old.
|
|
1072
|
+
- **`vinta-ai-maestro`: `pause` finishes while an agent waits on a lease or a
|
|
1073
|
+
gate.** A pause (or stop) now answers every queued `with` and `gate` with
|
|
1074
|
+
`run_pausing` (`run_stopping`) and refuses new ones. The CLIs tell the agent
|
|
1075
|
+
to end its turn, which is the step boundary the pause waits for. Commands
|
|
1076
|
+
and gates already running finish as before. The halt log names each node
|
|
1077
|
+
the drain is still waiting on and what it is doing, and
|
|
1078
|
+
`vinta-ai-maestro pause` prints the same list.
|
|
1079
|
+
|
|
1080
|
+
- **`vinta-ai-maestro`: a turn the agent never took part in is no longer read
|
|
1081
|
+
as work.** A session that ended with no text and no tool call — observed when
|
|
1082
|
+
a resumed claude-code session closed a replay of killed background tasks
|
|
1083
|
+
before reading the prompt — counted as a failed review with no reviewer
|
|
1084
|
+
behind it, or, on a crew member's first turn of a new phase, swallowed the
|
|
1085
|
+
new phase's brief so every later turn carried on with the old phase. Such a
|
|
1086
|
+
turn is now retried once, cold, with the whole brief (`node_session`
|
|
1087
|
+
`reason: empty_turn`), and a cold one that is empty too fails the turn.
|
|
1088
|
+
|
|
1089
|
+
- **Maestro: an amended gate re-runs instead of returning its old result.**
|
|
1090
|
+
The gate cache matched only the gate's command, so changing a gate's
|
|
1091
|
+
`timeout_s` (the monitor's `retime_gate`) or its `requires` partway through
|
|
1092
|
+
a run reused the result from the old settings on an unchanged tree. The
|
|
1093
|
+
cache now matches every setting that affects the run. Edits to
|
|
1094
|
+
`description` or `tuning` still reuse the result, and so does a
|
|
1095
|
+
`scoped_cmd` edit for runs that use the full `cmd`. Existing
|
|
1096
|
+
`gate-cache.db` entries are dropped once, at the first start after
|
|
1097
|
+
upgrading.
|
|
801
1098
|
- **Maestro: PRs say how the plan lands.** Every phase and integration PR is
|
|
802
1099
|
marked as a review unit that lands through the plan PR, and the plan PR says
|
|
803
1100
|
to merge it alone; it used to offer two landing paths, and a 36-PR landing
|
package/package.json
CHANGED
|
@@ -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",
|
|
@@ -679,7 +680,8 @@
|
|
|
679
680
|
"add_context",
|
|
680
681
|
"redirect",
|
|
681
682
|
"pause",
|
|
682
|
-
"abort"
|
|
683
|
+
"abort",
|
|
684
|
+
"fresh"
|
|
683
685
|
]
|
|
684
686
|
},
|
|
685
687
|
"by": {
|
|
@@ -703,7 +705,7 @@
|
|
|
703
705
|
],
|
|
704
706
|
"additionalProperties": false
|
|
705
707
|
},
|
|
706
|
-
"description": "Steering, pauses and
|
|
708
|
+
"description": "Steering, pauses, aborts and dropped sessions, per phase."
|
|
707
709
|
},
|
|
708
710
|
"questions": {
|
|
709
711
|
"type": "object",
|
|
@@ -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
|
|
@@ -360,6 +360,20 @@
|
|
|
360
360
|
"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.",
|
|
361
361
|
"type": "string",
|
|
362
362
|
"minLength": 1
|
|
363
|
+
},
|
|
364
|
+
"reset_cmd": {
|
|
365
|
+
"description": "Run inside a lane when it is recycled for another phase, and only then — never on a freshly provisioned lane. Runs with the lane’s environment applied, after maestro has reset the worktree, the env files, the databases and the services, and before `setup_cmd`. For what a phase leaves behind that the clean does not reach: a cache outside the worktree, a `recycle_keep` directory that needs pruning rather than deleting.",
|
|
366
|
+
"type": "string",
|
|
367
|
+
"minLength": 1
|
|
368
|
+
},
|
|
369
|
+
"recycle_keep": {
|
|
370
|
+
"default": [],
|
|
371
|
+
"description": "Ignored paths a recycled lane keeps. A recycle removes every untracked and ignored file the previous phase left — caches such as `.mypy_cache` and `__pycache__` included, which once kept a deleted package importable and failed a type check on clean code — except the copied dependency tree, the `env_files` and what is listed here. Each entry is a gitignore pattern handed to `git clean -e`: `.venv` keeps one at any depth, `/apps/api/.venv` only that one. For what is expensive to rebuild; anything not listed is rebuilt by `setup_cmd`, which runs on every recycle.",
|
|
372
|
+
"type": "array",
|
|
373
|
+
"items": {
|
|
374
|
+
"type": "string",
|
|
375
|
+
"minLength": 1
|
|
376
|
+
}
|
|
363
377
|
}
|
|
364
378
|
},
|
|
365
379
|
"required": [
|
|
@@ -423,6 +437,22 @@
|
|
|
423
437
|
"any"
|
|
424
438
|
]
|
|
425
439
|
},
|
|
440
|
+
"pull_requests": {
|
|
441
|
+
"default": "phase",
|
|
442
|
+
"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.",
|
|
443
|
+
"type": "string",
|
|
444
|
+
"enum": [
|
|
445
|
+
"phase",
|
|
446
|
+
"plan"
|
|
447
|
+
]
|
|
448
|
+
},
|
|
449
|
+
"max_spare_crew": {
|
|
450
|
+
"default": 2,
|
|
451
|
+
"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.",
|
|
452
|
+
"type": "integer",
|
|
453
|
+
"minimum": 0,
|
|
454
|
+
"maximum": 9007199254740991
|
|
455
|
+
},
|
|
426
456
|
"wave_gates": {
|
|
427
457
|
"default": "final",
|
|
428
458
|
"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.",
|