vinta-ai-workflows 0.7.0-alpha32 → 0.7.0-alpha34
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-alpha34 on 2026-10-16 -->
|
|
11
|
+
<!-- pre-release: 0.7.0-alpha33 on 2026-10-16 -->
|
|
10
12
|
<!-- pre-release: 0.7.0-alpha31 on 2026-10-09 -->
|
|
11
13
|
<!-- pre-release: 0.7.0-alpha30 on 2026-10-09 -->
|
|
12
14
|
<!-- pre-release: 0.7.0-alpha20 on 2026-09-24 -->
|
|
@@ -32,6 +34,90 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
32
34
|
|
|
33
35
|
### Added
|
|
34
36
|
|
|
37
|
+
- **`vinta-ai-maestro`: `accept` on an unapproved review.** The review-budget
|
|
38
|
+
question offered `continue` or `stop`, and operators answered `stop` meaning
|
|
39
|
+
"take it as it is" — which fails the attempt and, under `--on-failure
|
|
40
|
+
retry`, starts the phase over. It now offers `continue`, `accept` and `stop`
|
|
41
|
+
and says what each does. `accept` ends the review loop and keeps the work:
|
|
42
|
+
the polish chores, the gates and integration still run, and the answer is
|
|
43
|
+
journalled. The unattended answer is unchanged (`continue` once, then
|
|
44
|
+
`stop`); the coordinator cannot answer `accept`.
|
|
45
|
+
- **`vinta-ai-maestro validate` warns about parallel phases adding migrations
|
|
46
|
+
to one app**, and `plan-feature` treats them as real overlap. Two phases
|
|
47
|
+
that neither depends on the other, each adding a migration under the same
|
|
48
|
+
directory (Django `<app>/migrations/`, Alembic `…/versions/`, Rails
|
|
49
|
+
`db/migrate/`, Prisma `prisma/migrations/`), start from the same latest
|
|
50
|
+
migration; merged, the graph has two leaves and every gate on the merged
|
|
51
|
+
tree fails. Their Touch Lists name different files, so nothing flagged it.
|
|
52
|
+
`validate --json` gains a `warnings` list, printed as `warning:` lines in
|
|
53
|
+
text mode, and the review page shows them in their own panel; neither
|
|
54
|
+
changes the exit code or `valid`. `plan-feature` now says to add the edge,
|
|
55
|
+
or to name the phase whose integration writes the merge migration.
|
|
56
|
+
- **`vinta-ai-maestro node resume <run> <phase>`**, the undo of `node pause`:
|
|
57
|
+
it withdraws a pause not yet taken, releases a phase parked on the pause, or
|
|
58
|
+
gives a held phase its `--retry-after` timer back. It used to take
|
|
59
|
+
answering an undocumented "The operator paused this node. Resume it?"
|
|
60
|
+
question, or answering a held phase's question by hand.
|
|
61
|
+
- **`vinta-ai-maestro status` shows the question each phase is asking**, with
|
|
62
|
+
its choices, and `status --json` carries it per node as `question`
|
|
63
|
+
(`effectId`, `question`, `kind`, `choices`, `askedAt`). `asking: true` alone
|
|
64
|
+
sent operators into `flow.db` to read it.
|
|
65
|
+
- **`vinta-ai-maestro`: ways past a stale gate verdict.** `gate <id>
|
|
66
|
+
--no-cache` runs a gate past its cached result and keeps the new one in its
|
|
67
|
+
place; a cached red now says so. `gate-cache invalidate <run-id> <gate-id>
|
|
68
|
+
[--tree <hash>]` forgets a gate's results for the operator and the run
|
|
69
|
+
coordinator (`POST /api/runs/:id/gates/:gate/invalidate`, journalled as
|
|
70
|
+
`gate_cache_invalidated`; an agent's token cannot reach it). Both are for a
|
|
71
|
+
verdict whose cause git does not track — an ignored file, a tool's cache, an
|
|
72
|
+
installed dependency — which the tree-keyed cache would otherwise replay.
|
|
73
|
+
|
|
74
|
+
- **`vinta-ai-maestro`: a phase's decisions outlive the session they were
|
|
75
|
+
made in.** Each node now keeps a list of what was settled for it: agents'
|
|
76
|
+
questions with their answers (marked as the operator's, the coordinator's,
|
|
77
|
+
or unattended), the operator's `node context` and `redirect` text, and the
|
|
78
|
+
findings the review rejected with their counter-evidence. Every later
|
|
79
|
+
prompt carries the list — implementer, fixer, chores, and the review, which
|
|
80
|
+
hands it to its reviewer — so a fresh reviewer, a replaced session or a
|
|
81
|
+
retry no longer re-asks or re-raises what was already decided. Before
|
|
82
|
+
this, an unattended run answered one question four times and a phase ran
|
|
83
|
+
some thirty rounds without converging, and operator context sent with
|
|
84
|
+
`node context` had to be re-sent after a session change. The review now
|
|
85
|
+
rejects a finding that re-opens a decision, citing it, and asks the
|
|
86
|
+
operator about it at most once, and only with new evidence. It ends its
|
|
87
|
+
report with a `REJECTED:` line per rejected finding, which the run keeps.
|
|
88
|
+
The list is bounded (the newest 12 answers and notes and the newest 8
|
|
89
|
+
rejected findings), kept across retries, written to
|
|
90
|
+
`runs/<id>/nodes/<node>/decisions.jsonl` and read back when a run resumes.
|
|
91
|
+
|
|
92
|
+
- **`vinta-ai-maestro judge calibrate`: measure a permission-judge question
|
|
93
|
+
before a run relies on it.** It replays a past run's `Bash` calls
|
|
94
|
+
(`--run <run-id>`), or a file of commands each optionally labelled `safe` or
|
|
95
|
+
`unsafe` (`--commands <file>`), against a candidate question
|
|
96
|
+
(`--question` / `--question-file`) and threshold (`--threshold`) through the
|
|
97
|
+
classifier in `--system-one`. Nothing is executed. It reports how many would
|
|
98
|
+
be allowed and refused, the score distribution, every labelled command
|
|
99
|
+
judged the wrong way, and the highest-scoring must-deny command, and exits 1
|
|
100
|
+
on a misjudged one so a case file can guard a project's question in CI.
|
|
101
|
+
Commands are printed as recorded, never their output.
|
|
102
|
+
|
|
103
|
+
- **`vinta-ai-maestro`: `project.reset_cmd` and `project.recycle_keep`.**
|
|
104
|
+
`reset_cmd` is a project hook run inside a lane when it is recycled for the
|
|
105
|
+
next phase, and only then, after maestro's own resets and before
|
|
106
|
+
`setup_cmd`. `recycle_keep` lists gitignore patterns a recycled lane keeps
|
|
107
|
+
(`.venv`, say) now that the recycle cleans ignored files. Both optional, in
|
|
108
|
+
`workflow.v1`.
|
|
109
|
+
|
|
110
|
+
- **`vinta-ai-maestro`: `node fresh <run> <phase>` drops a phase's agent
|
|
111
|
+
session without failing the attempt.** The phase's next turn starts cold with
|
|
112
|
+
the whole brief, journalled `node_session` `reason: operator_fresh`; the turn
|
|
113
|
+
in flight is not stopped (pair it with `redirect` for that). It is the way out
|
|
114
|
+
of a session gone wrong — an agent replaying killed work or carrying on with
|
|
115
|
+
the previous phase — that used to cost an `abort`. Also `POST
|
|
116
|
+
/api/runs/:run/nodes/:node/fresh`, and the run coordinator can use it. The
|
|
117
|
+
post-mortem's `operations.node_operations[].op` gains `fresh` (additive, in
|
|
118
|
+
`postmortem.v1.schema.json`).
|
|
119
|
+
|
|
120
|
+
|
|
35
121
|
- **`vinta-ai-maestro`: a busy crew member is replaced, not borrowed
|
|
36
122
|
(`defaults.max_spare_crew`, default 2).** When the member a phase is planned
|
|
37
123
|
for — or the one an operator picked with `retry with <member>` — is busy, or
|
|
@@ -552,6 +638,15 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
552
638
|
|
|
553
639
|
### Changed
|
|
554
640
|
|
|
641
|
+
- **`vinta-ai-maestro`: a recycled lane no longer keeps the last phase's
|
|
642
|
+
ignored files.** A lane handed to the next phase kept everything gitignored,
|
|
643
|
+
and one kept `apps/api/.mypy_cache` and a deleted package's `__pycache__`,
|
|
644
|
+
so mypy reported 18 errors on clean code. The recycle now cleans ignored
|
|
645
|
+
files too, keeping only the copied `node_modules`, the `env_files` and the
|
|
646
|
+
new `project.recycle_keep` patterns. **Migration:** anything expensive your
|
|
647
|
+
lanes build and should keep across phases, such as a `.venv`, goes in
|
|
648
|
+
`recycle_keep`; otherwise `setup_cmd`, which already runs on every
|
|
649
|
+
recycle, rebuilds it.
|
|
555
650
|
- **`vinta-ai-maestro`: agents wait for background work the way a normal
|
|
556
651
|
Claude Code session does.** A headless session no longer ends at the first
|
|
557
652
|
turn that leaves background work running. It stays open, the agent is woken
|
|
@@ -891,6 +986,171 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
891
986
|
|
|
892
987
|
### Fixed
|
|
893
988
|
|
|
989
|
+
- **`vinta-ai-maestro`: a claude-code turn the CLI calls "blocked" no longer
|
|
990
|
+
fails unless a tool was refused in it.** The CLI's own turn summary marks a
|
|
991
|
+
turn `blocked` whenever its last message names a next step for a person —
|
|
992
|
+
a review ending "Nothing was pushed." over `VERDICT: pass`, an implementer
|
|
993
|
+
listing an open point — and each such turn was recorded as failed
|
|
994
|
+
("claude-code: the turn ended blocked"), an approving review included. One
|
|
995
|
+
observed phase had 43 of them. The summary now fails a turn only alongside a
|
|
996
|
+
`permission_denied` in the same turn, the case it was added for; whether
|
|
997
|
+
the rest did their job is for verdicts, gates and the agent's questions. A
|
|
998
|
+
turn's errors no longer outlive it in a session held open for background
|
|
999
|
+
work.
|
|
1000
|
+
- **`vinta-ai-maestro`: a merge git stopped with nothing unmerged no longer
|
|
1001
|
+
starts a fixer.** `rerere.autoupdate` replaying a recorded resolution, or a
|
|
1002
|
+
`pre-merge-commit` / `commit-msg` hook refusing the merged code, leaves
|
|
1003
|
+
`git merge` non-zero with no unmerged path, and the integrator handed the
|
|
1004
|
+
conflict fixer an empty file list — nineteen rounds in one observed phase,
|
|
1005
|
+
each told to resolve nothing. With no conflict markers left, the merge now
|
|
1006
|
+
goes to `verify` and is committed, recorded as resolved in 0 rounds; a hook
|
|
1007
|
+
that refuses that commit gets the refused-commit question (commit, commit
|
|
1008
|
+
past the hooks, or abort), worded for a clean merge. Only a red gate sends it
|
|
1009
|
+
to the fixer. A merge git refused before merging anything (an untracked file
|
|
1010
|
+
in the way) fails with git's own error.
|
|
1011
|
+
- **`vinta-ai-maestro`: a merge finished by hand survives the next attempt.**
|
|
1012
|
+
A conflict the fixer could not settle fails the phase with "finish the merge
|
|
1013
|
+
by hand … and commit it", and the next attempt's `mergeWave` cut
|
|
1014
|
+
`wave-<N>` again from `wave-<N-1>` and re-merged every member, throwing the
|
|
1015
|
+
resolution away and hitting the same conflict on every attempt. A wave or
|
|
1016
|
+
`integ-<id>` branch that still contains its starting point, and whose merges
|
|
1017
|
+
all brought in work still in the members' current tips, is now kept, and only
|
|
1018
|
+
the members it lacks are merged. A member redone after it was merged still
|
|
1019
|
+
forces a rebuild. The error now says the branch is kept.
|
|
1020
|
+
- **`vinta-ai-maestro`: a merged tree that fails its gates is no longer
|
|
1021
|
+
reported as an unresolved conflict with no paths.** Two sibling phases that
|
|
1022
|
+
each added a Django migration on the same parent merged cleanly and failed
|
|
1023
|
+
`verify` together, and the phase failed "unresolved conflict: p4a both
|
|
1024
|
+
changed , …". Each fix round is now told which gate the merged tree failed
|
|
1025
|
+
and where its log is, and the rounds end in `IntegrationVerifyError`, naming
|
|
1026
|
+
the gate and its log path (`nodes/_integration/gates/<gate>.log`).
|
|
1027
|
+
- **`vinta-ai-maestro`: `exec` accepts a lane's short name or its phase, and
|
|
1028
|
+
`with` / `gate` work inside it.** `exec <run> crew-3-tier2-2` and
|
|
1029
|
+
`exec <run> p1` were refused `unknown_target`; only the full
|
|
1030
|
+
`<run-id>-crew-3-tier2-2` worked. Both now resolve, and a refusal lists the
|
|
1031
|
+
run's targets. Inside `exec`, `with test-suite -- make test` said "no live
|
|
1032
|
+
run is available" while the run was live: the command now holds leases as the
|
|
1033
|
+
phase running in the lane, or as itself on an idle one, until it exits.
|
|
1034
|
+
- **`vinta-ai-maestro`: a fix loop that changes nothing now stops and asks.**
|
|
1035
|
+
A typecheck red because of ignored files left in a lane kept coming back
|
|
1036
|
+
from the gate cache after they were gone, and twenty fixers in a row found
|
|
1037
|
+
nothing to fix. When a gate is red again on the very tree a fixer turn left
|
|
1038
|
+
unchanged, the scheduler reruns that gate once without the cache; if it is
|
|
1039
|
+
still red the same way, `standard-phase` goes to `exhausted` instead of
|
|
1040
|
+
spawning another fixer — with or without `max_fix_rounds` — and the run
|
|
1041
|
+
coordinator is woken. New guard fact `gate.stalled`, new `run_gate` param
|
|
1042
|
+
`no_cache`, new journal event `gate_stalled`. A fixer that moved the tree at
|
|
1043
|
+
all is never a stall. A custom pipeline keeps its own guards and can opt in
|
|
1044
|
+
on `gate.stalled`.
|
|
1045
|
+
|
|
1046
|
+
- **`vinta-ai-maestro`: a claude-code review no longer loses a reviewer answer
|
|
1047
|
+
that comes back early.** When a reviewer resumed with `SendMessage` answered
|
|
1048
|
+
while the agent was still writing its last message, the session ended at the
|
|
1049
|
+
turn's `result` and killed the turn the CLI starts to hand over the answer.
|
|
1050
|
+
The review then stated no verdict and went to the operator as `unapproved`.
|
|
1051
|
+
The session now waits for that turn, for at most a minute of silence.
|
|
1052
|
+
|
|
1053
|
+
- **`vinta-ai-maestro purge` no longer ends a run you did not name.** A bulk
|
|
1054
|
+
`purge --lanes` removed the lanes of paused, failed and interrupted runs —
|
|
1055
|
+
the integration worktree included — so `run --resume` had nothing to adopt,
|
|
1056
|
+
and a bulk `purge` deleted those runs' frozen snapshots. A run that could
|
|
1057
|
+
still be resumed is now kept whole, directory and lanes, and named with the
|
|
1058
|
+
reason; purge it by name, or pass the new `--force`. A run whose job is
|
|
1059
|
+
still running is kept even then.
|
|
1060
|
+
- **`vinta-ai-maestro purge --lanes` tears a lane down, not just its
|
|
1061
|
+
worktree.** It used to leave every forked database, compose stack and lane
|
|
1062
|
+
summary behind, including summaries whose worktree was already gone. Each
|
|
1063
|
+
reaped lane's summary is now its teardown manifest: `docker compose -p
|
|
1064
|
+
<project> down`, `docker volume rm` for its forked volumes, the `reset_cmd`
|
|
1065
|
+
of its own service namespaces, and `dropdb` (or the project's `dropdb_cmd`)
|
|
1066
|
+
for its forked databases — every command listed before anything runs, and
|
|
1067
|
+
under `--dry-run` nothing runs. Only what is provably that lane's is
|
|
1068
|
+
touched: never a compose-delivered database, an index-numbered service slot,
|
|
1069
|
+
or a summary `prepare-worktree` wrote for a worktree you made. A step that
|
|
1070
|
+
fails is reported, and its lane's summary is kept so a rerun retries it.
|
|
1071
|
+
- **`vinta-ai-maestro run --help` says which runs resume.** It claimed a run
|
|
1072
|
+
that "finished" could not be resumed, while a `failed` run resumes — its
|
|
1073
|
+
failed phases and the ones they blocked are attempted again, and `status`
|
|
1074
|
+
prints the command for it. The help now names paused, interrupted and
|
|
1075
|
+
failed as resumable, and only `done` and `cancelled` as refused.
|
|
1076
|
+
- **`vinta-ai-maestro`: the shipped permission-judge question no longer
|
|
1077
|
+
refuses routine commands.** Under `--permission judged` it allowed 44 of 69
|
|
1078
|
+
commands from a real run, refusing `ls .env*`, `cat .env.example`,
|
|
1079
|
+
`docker ps` and the project's own `make` targets. The question now says a
|
|
1080
|
+
secret is a *value* — listing env files and reading templates is allowed,
|
|
1081
|
+
reading a real `.env` is not — allows read-only inspection and the project's
|
|
1082
|
+
own commands in its worktree, and still refuses pushes, `sudo`, writes
|
|
1083
|
+
outside the worktree and another project's containers or volumes. A
|
|
1084
|
+
project that set its own `judges.permission.question` is unaffected; the
|
|
1085
|
+
default `allow_above` stays 0.9.
|
|
1086
|
+
|
|
1087
|
+
- **`vinta-ai-maestro`: the disk preflight sizes the pool the run will
|
|
1088
|
+
really create.** `run --resume` with every worktree still on disk was
|
|
1089
|
+
refused (`3 lanes + 1 integration worktree needs 37.9 GiB, only 18.0 GiB
|
|
1090
|
+
free`) by a doctor that charged for them all again; it now charges only the
|
|
1091
|
+
worktrees still to be made, and passes a resume that makes none. A staffed run
|
|
1092
|
+
is counted as the pool provisions it — a worktree per crew member, not per
|
|
1093
|
+
unit of `resources.lane.capacity` — plus `defaults.max_spare_crew`
|
|
1094
|
+
headroom, and the doctor, the pool and the refusal line now name the same
|
|
1095
|
+
count. The per-lane estimate is the tracked tree at the base branch, the
|
|
1096
|
+
copied `node_modules` and the `env_files`, instead of a walk of the whole
|
|
1097
|
+
main checkout that counted `.git`, the `.vinta-ai-maestro/` store (lanes
|
|
1098
|
+
included) and ignored caches — one run's estimate had grown from 4.0 to
|
|
1099
|
+
9.5 GiB a lane against real lanes of about 1.1 GiB.
|
|
1100
|
+
|
|
1101
|
+
- **`vinta-ai-maestro`: an integration merge a pause, stop or kill caught
|
|
1102
|
+
half-way no longer wedges the run.** Every later attempt used to fail on
|
|
1103
|
+
`integration worktree is mid-merge` until someone ran `git merge --abort` by
|
|
1104
|
+
hand. The integrator now recognises a merge it started — one of the plan's
|
|
1105
|
+
phase branches (or a kept resolution, or the plan branch) into one of its
|
|
1106
|
+
`integ-`/`wave-` branches — keeps what it had under
|
|
1107
|
+
`refs/vinta-ai-maestro/abandoned/<branch>`, aborts it and redoes it, so the
|
|
1108
|
+
result is gated again; each one journals `integration_reclaimed`. The same
|
|
1109
|
+
sweep runs on `run --resume` and when a stop outlives its deadline. A conflict
|
|
1110
|
+
left for a person that nobody finished is redone on the retry the same way.
|
|
1111
|
+
A merge the run did not start is still refused by name. A failed `git_merge`
|
|
1112
|
+
of a named branch now aborts its merge.
|
|
1113
|
+
|
|
1114
|
+
- **`vinta-ai-maestro`: aborting the only runnable phase no longer ends the
|
|
1115
|
+
run.** A run whose remaining phases all depended on an aborted one ended
|
|
1116
|
+
`failed` at once, and the `node retry` that followed was refused as `run has
|
|
1117
|
+
ended`. Aborted phases now hold the run open for their retry (no timer retries
|
|
1118
|
+
them); `pause` or `stop` ends it. Under `--on-failure stop` an abort still ends
|
|
1119
|
+
the run. A retry sent while the aborted attempt is still winding down is
|
|
1120
|
+
applied once it has.
|
|
1121
|
+
|
|
1122
|
+
- **`vinta-ai-maestro`: a gate run inside `with` no longer deadlocks every
|
|
1123
|
+
lane.** `vinta-ai-maestro with test-suite -- vinta-ai-maestro gate test`
|
|
1124
|
+
held the only `test-suite` slot while the gate queued for it, and every
|
|
1125
|
+
lane's tests and gates queued behind the pair (55 minutes in one run). The
|
|
1126
|
+
gate guard now refuses `gate` inside `with` and tells the agent to run the
|
|
1127
|
+
gate on its own. The daemon refuses a gate whose resources the asking phase
|
|
1128
|
+
already holds through `with` (`holds_required:<resource>`), naming the
|
|
1129
|
+
process holding them. The agent prompts state the same rule.
|
|
1130
|
+
- **`vinta-ai-maestro`: `with` and `gate` say who they are waiting on.** A
|
|
1131
|
+
queued answer names each holder of the resource: the phase, its lane, and
|
|
1132
|
+
how it holds it (`with` with its process id, a gate an agent ran, or the
|
|
1133
|
+
phase's own gate step). When the holder is the asking phase itself, the
|
|
1134
|
+
message says so. It used to say "held by another lane" in exactly that case.
|
|
1135
|
+
`gate` now says "queued" until the gate has its resources and "running"
|
|
1136
|
+
after; it used to say "running" throughout.
|
|
1137
|
+
- **`vinta-ai-maestro`: commands left behind by an ended agent session stop
|
|
1138
|
+
holding capacity.** Each agent session gets a turn id
|
|
1139
|
+
(`VINTA_AI_MAESTRO_TURN_ID`), which `with` and `gate` send with every
|
|
1140
|
+
request. When the session ends, the leases its commands hold are released,
|
|
1141
|
+
their queued waits and queued gates are cancelled, and their next request is
|
|
1142
|
+
refused (`turn_ended`). A request for a phase that is not running is refused
|
|
1143
|
+
too (`holder_not_running`). A command backgrounded with `&` used to keep its
|
|
1144
|
+
place in the queue for as long as it lived: one run had nine such waiters,
|
|
1145
|
+
up to 55 minutes old.
|
|
1146
|
+
- **`vinta-ai-maestro`: `pause` finishes while an agent waits on a lease or a
|
|
1147
|
+
gate.** A pause (or stop) now answers every queued `with` and `gate` with
|
|
1148
|
+
`run_pausing` (`run_stopping`) and refuses new ones. The CLIs tell the agent
|
|
1149
|
+
to end its turn, which is the step boundary the pause waits for. Commands
|
|
1150
|
+
and gates already running finish as before. The halt log names each node
|
|
1151
|
+
the drain is still waiting on and what it is doing, and
|
|
1152
|
+
`vinta-ai-maestro pause` prints the same list.
|
|
1153
|
+
|
|
894
1154
|
- **`vinta-ai-maestro`: a turn the agent never took part in is no longer read
|
|
895
1155
|
as work.** A session that ended with no text and no tool call — observed when
|
|
896
1156
|
a resumed claude-code session closed a replay of killed background tasks
|
package/package.json
CHANGED
|
@@ -680,7 +680,8 @@
|
|
|
680
680
|
"add_context",
|
|
681
681
|
"redirect",
|
|
682
682
|
"pause",
|
|
683
|
-
"abort"
|
|
683
|
+
"abort",
|
|
684
|
+
"fresh"
|
|
684
685
|
]
|
|
685
686
|
},
|
|
686
687
|
"by": {
|
|
@@ -704,7 +705,7 @@
|
|
|
704
705
|
],
|
|
705
706
|
"additionalProperties": false
|
|
706
707
|
},
|
|
707
|
-
"description": "Steering, pauses and
|
|
708
|
+
"description": "Steering, pauses, aborts and dropped sessions, per phase."
|
|
708
709
|
},
|
|
709
710
|
"questions": {
|
|
710
711
|
"type": "object",
|
|
@@ -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": [
|
|
@@ -254,6 +254,7 @@ Before publishing the plan, cross-check the **Touch List**: for every pair of ph
|
|
|
254
254
|
|
|
255
255
|
- **Trivial overlap** (a shared `__init__.py`, a settings registry, a route table) — fine. Note it under the graph table so the reviewer isn't surprised.
|
|
256
256
|
- **Real overlap** (the same use case, the same serializer, the same view) — **add the dependency edge** and let one phase build on the other. A serialized pair that merges cleanly beats a parallel pair that needs a human to untangle.
|
|
257
|
+
- **Migrations in one app count as real overlap, even in different files.** Two phases that run in parallel and each add a migration to the same app both start from the same latest migration. Each is green alone; merged, the migration graph has two leaves (Django) or two heads (Alembic), and every gate on the merged tree fails. Their Touch Lists name two different new files, so path overlap never shows it. Prefer the edge — usually by folding both schema changes into the foundation phase — or, when the two must stay parallel, say in the later phase's body that the integration step writes the merge migration. `vinta-ai-maestro validate` warns about such a pair; put the migrations directory in each phase's Touch List so it can.
|
|
257
258
|
|
|
258
259
|
### Design *for* parallelism when it's cheap
|
|
259
260
|
|
|
@@ -1071,6 +1072,8 @@ Fix each issue by its `source`:
|
|
|
1071
1072
|
- **`workflow`**: the document's shape or graph, such as an unknown node, a cycle, an undeclared pool, a crew member that cannot take its phase, a gate entry with neither `cmd` nor `type`, a phase with no gates, or an id that does not match the filename. Re-derive the field from the plan. A gate `"has no cmd"` is an override that forgot its `"type"`. A phase that `"has no gates"` is a node carrying `"gates": []` — remove the key so it takes `defaults.gates`, name its gates, or, for a plan whose phases genuinely have nothing to run, set `defaults.allow_ungated_phases: true`. If the plan itself is wrong (a cycle in its `**Depends on**:` lines), fix the plan first.
|
|
1072
1073
|
- **`config`**: `.vinta-ai-workflows.yaml` does not parse. That file is the project's, not the plan's. Do not edit it to make the plan pass. Tell the human what is wrong and where.
|
|
1073
1074
|
|
|
1075
|
+
**`warnings` do not change the exit code, and each still gets an answer.** Today the one warning is two phases that run in parallel and both add migrations to the same app (see "Same-wave phases must not fight over the same files"). Add the dependency edge, or keep the pair parallel and say in the later phase's body that the merge migration is written at integration. Either way, name the choice when you hand the plan over. An older maestro prints no `warnings` key.
|
|
1076
|
+
|
|
1074
1077
|
## Review the plan with the human
|
|
1075
1078
|
|
|
1076
1079
|
A plan is cheapest to change before anything runs. Once both files are written and the workflow validates, hand the plan over for review. Ask with `AskUserQuestion`: header `Review`, question "The plan and its workflow are written — `<plan path>` and `<workflow path>`. How do you want to review it?"
|