vinta-ai-workflows 0.7.0-alpha33 → 0.7.0-alpha35
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 +119 -0
- package/package.json +1 -1
- package/schemas/vinta-ai-workflows-config.v1.schema.json +18 -2
- package/schemas/workflow.v1.schema.json +21 -3
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +5 -2
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +6 -2
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-alpha35 on 2026-10-16 -->
|
|
11
|
+
<!-- pre-release: 0.7.0-alpha34 on 2026-10-16 -->
|
|
10
12
|
<!-- pre-release: 0.7.0-alpha33 on 2026-10-16 -->
|
|
11
13
|
<!-- pre-release: 0.7.0-alpha31 on 2026-10-09 -->
|
|
12
14
|
<!-- pre-release: 0.7.0-alpha30 on 2026-10-09 -->
|
|
@@ -33,6 +35,54 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
33
35
|
|
|
34
36
|
### Added
|
|
35
37
|
|
|
38
|
+
- **`vinta-ai-maestro`: integration is visible while it happens.** A phase in
|
|
39
|
+
`integrate` — a merge, a suite on the merged tree, a push, none with an
|
|
40
|
+
agent — showed `running` over a silent transcript, and one queued behind it
|
|
41
|
+
looked the same. Now:
|
|
42
|
+
- The phase view has an **Integration** timeline: each merge, conflict fix
|
|
43
|
+
round, `verify`, wave gate, push and PR of this attempt, a live clock on
|
|
44
|
+
the one in progress, and the log of every step that has one.
|
|
45
|
+
- The run view has an **Integration worktree** panel: who holds it, the step
|
|
46
|
+
it is on and for how long, and who is queued behind it. The roster and
|
|
47
|
+
the phase header show each running phase's pipeline step.
|
|
48
|
+
- `status` prints the step beside each running phase and an `integration`
|
|
49
|
+
line (`a · wave 1 gate unit · 6m 13s · 1 queued (b)`); `status --json`
|
|
50
|
+
carries `nodes[].step` and `integration`.
|
|
51
|
+
- A conflict fixer's turn is also written to the transcript of the phase
|
|
52
|
+
being based or building the wave, not only to the incoming phase's.
|
|
53
|
+
|
|
54
|
+
Two additive journal events back it: `node_step` (the pipeline state an
|
|
55
|
+
attempt entered) and `integration_step` (a step started or finished in the
|
|
56
|
+
worktree, identifiers and a log path only). Older runs have neither and
|
|
57
|
+
show no timeline.
|
|
58
|
+
- **`vinta-ai-maestro`: `accept` on an unapproved review.** The review-budget
|
|
59
|
+
question offered `continue` or `stop`, and operators answered `stop` meaning
|
|
60
|
+
"take it as it is" — which fails the attempt and, under `--on-failure
|
|
61
|
+
retry`, starts the phase over. It now offers `continue`, `accept` and `stop`
|
|
62
|
+
and says what each does. `accept` ends the review loop and keeps the work:
|
|
63
|
+
the polish chores, the gates and integration still run, and the answer is
|
|
64
|
+
journalled. The unattended answer is unchanged (`continue` once, then
|
|
65
|
+
`stop`); the coordinator cannot answer `accept`.
|
|
66
|
+
- **`vinta-ai-maestro validate` warns about parallel phases adding migrations
|
|
67
|
+
to one app**, and `plan-feature` treats them as real overlap. Two phases
|
|
68
|
+
that neither depends on the other, each adding a migration under the same
|
|
69
|
+
directory (Django `<app>/migrations/`, Alembic `…/versions/`, Rails
|
|
70
|
+
`db/migrate/`, Prisma `prisma/migrations/`), start from the same latest
|
|
71
|
+
migration; merged, the graph has two leaves and every gate on the merged
|
|
72
|
+
tree fails. Their Touch Lists name different files, so nothing flagged it.
|
|
73
|
+
`validate --json` gains a `warnings` list, printed as `warning:` lines in
|
|
74
|
+
text mode, and the review page shows them in their own panel; neither
|
|
75
|
+
changes the exit code or `valid`. `plan-feature` now says to add the edge,
|
|
76
|
+
or to name the phase whose integration writes the merge migration.
|
|
77
|
+
- **`vinta-ai-maestro node resume <run> <phase>`**, the undo of `node pause`:
|
|
78
|
+
it withdraws a pause not yet taken, releases a phase parked on the pause, or
|
|
79
|
+
gives a held phase its `--retry-after` timer back. It used to take
|
|
80
|
+
answering an undocumented "The operator paused this node. Resume it?"
|
|
81
|
+
question, or answering a held phase's question by hand.
|
|
82
|
+
- **`vinta-ai-maestro status` shows the question each phase is asking**, with
|
|
83
|
+
its choices, and `status --json` carries it per node as `question`
|
|
84
|
+
(`effectId`, `question`, `kind`, `choices`, `askedAt`). `asking: true` alone
|
|
85
|
+
sent operators into `flow.db` to read it.
|
|
36
86
|
- **`vinta-ai-maestro`: ways past a stale gate verdict.** `gate <id>
|
|
37
87
|
--no-cache` runs a gate past its cached result and keeps the new one in its
|
|
38
88
|
place; a cached red now says so. `gate-cache invalidate <run-id> <gate-id>
|
|
@@ -609,6 +659,30 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
609
659
|
|
|
610
660
|
### Changed
|
|
611
661
|
|
|
662
|
+
- **`vinta-ai-maestro`: the full suite runs before a PR, and on every merged
|
|
663
|
+
tree.** Under `gate_scope: scoped`, a phase's gates ran their scoped form
|
|
664
|
+
everywhere, so a PR could carry a suite only a scoped run had seen, and most
|
|
665
|
+
phases were `done` before any full suite touched their code.
|
|
666
|
+
- A phase's gates run scoped until its review approves; `verify`, after the
|
|
667
|
+
review, runs every gate's full `cmd` on the phase branch. A red full run
|
|
668
|
+
goes back to a fixer and through the review again — more review rounds,
|
|
669
|
+
no failing suite in a PR. The fixer is told the gate failed in full, and
|
|
670
|
+
`vinta-ai-maestro gate <id> --full` runs the whole command for an agent.
|
|
671
|
+
- `defaults.wave_gates` now defaults to `every`: each wave's gates run in
|
|
672
|
+
full on the merged tree, so the next wave builds on a passing one. Set
|
|
673
|
+
`final` for the previous behaviour.
|
|
674
|
+
- A multi-dependency phase's `integ-<id>` base runs its dependencies' gates
|
|
675
|
+
in full before the phase starts, cached on the merged tree; red fails the
|
|
676
|
+
phase's setup with `BaseGateError`. A clean base merge used to run nothing.
|
|
677
|
+
- Wave and base gate failures now reach the journal with their gate and log,
|
|
678
|
+
not just their error name.
|
|
679
|
+
- Each full run can be skipped in a plan's `defaults` or the project's
|
|
680
|
+
`maestro.defaults` (`.vinta-ai-workflows.yaml`): `verify_gates: scoped`
|
|
681
|
+
keeps `verify` on the scoped form, `base_gates: off` leaves merged bases
|
|
682
|
+
unchecked, and `wave_gates: final` / `off` as before. Both new fields are
|
|
683
|
+
in `vinta-ai-workflows-config.v1.schema.json`, and the bootstrap asks when
|
|
684
|
+
the full suite should run. The run coordinator may tighten them, never
|
|
685
|
+
loosen them.
|
|
612
686
|
- **`vinta-ai-maestro`: a recycled lane no longer keeps the last phase's
|
|
613
687
|
ignored files.** A lane handed to the next phase kept everything gitignored,
|
|
614
688
|
and one kept `apps/api/.mypy_cache` and a deleted package's `__pycache__`,
|
|
@@ -957,6 +1031,51 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
957
1031
|
|
|
958
1032
|
### Fixed
|
|
959
1033
|
|
|
1034
|
+
- **`vinta-ai-maestro`: a claude-code turn the CLI calls "blocked" no longer
|
|
1035
|
+
fails unless a tool was refused in it.** The CLI's own turn summary marks a
|
|
1036
|
+
turn `blocked` whenever its last message names a next step for a person —
|
|
1037
|
+
a review ending "Nothing was pushed." over `VERDICT: pass`, an implementer
|
|
1038
|
+
listing an open point — and each such turn was recorded as failed
|
|
1039
|
+
("claude-code: the turn ended blocked"), an approving review included. One
|
|
1040
|
+
observed phase had 43 of them. The summary now fails a turn only alongside a
|
|
1041
|
+
`permission_denied` in the same turn, the case it was added for; whether
|
|
1042
|
+
the rest did their job is for verdicts, gates and the agent's questions. A
|
|
1043
|
+
turn's errors no longer outlive it in a session held open for background
|
|
1044
|
+
work.
|
|
1045
|
+
- **`vinta-ai-maestro`: a merge git stopped with nothing unmerged no longer
|
|
1046
|
+
starts a fixer.** `rerere.autoupdate` replaying a recorded resolution, or a
|
|
1047
|
+
`pre-merge-commit` / `commit-msg` hook refusing the merged code, leaves
|
|
1048
|
+
`git merge` non-zero with no unmerged path, and the integrator handed the
|
|
1049
|
+
conflict fixer an empty file list — nineteen rounds in one observed phase,
|
|
1050
|
+
each told to resolve nothing. With no conflict markers left, the merge now
|
|
1051
|
+
goes to `verify` and is committed, recorded as resolved in 0 rounds; a hook
|
|
1052
|
+
that refuses that commit gets the refused-commit question (commit, commit
|
|
1053
|
+
past the hooks, or abort), worded for a clean merge. Only a red gate sends it
|
|
1054
|
+
to the fixer. A merge git refused before merging anything (an untracked file
|
|
1055
|
+
in the way) fails with git's own error.
|
|
1056
|
+
- **`vinta-ai-maestro`: a merge finished by hand survives the next attempt.**
|
|
1057
|
+
A conflict the fixer could not settle fails the phase with "finish the merge
|
|
1058
|
+
by hand … and commit it", and the next attempt's `mergeWave` cut
|
|
1059
|
+
`wave-<N>` again from `wave-<N-1>` and re-merged every member, throwing the
|
|
1060
|
+
resolution away and hitting the same conflict on every attempt. A wave or
|
|
1061
|
+
`integ-<id>` branch that still contains its starting point, and whose merges
|
|
1062
|
+
all brought in work still in the members' current tips, is now kept, and only
|
|
1063
|
+
the members it lacks are merged. A member redone after it was merged still
|
|
1064
|
+
forces a rebuild. The error now says the branch is kept.
|
|
1065
|
+
- **`vinta-ai-maestro`: a merged tree that fails its gates is no longer
|
|
1066
|
+
reported as an unresolved conflict with no paths.** Two sibling phases that
|
|
1067
|
+
each added a Django migration on the same parent merged cleanly and failed
|
|
1068
|
+
`verify` together, and the phase failed "unresolved conflict: p4a both
|
|
1069
|
+
changed , …". Each fix round is now told which gate the merged tree failed
|
|
1070
|
+
and where its log is, and the rounds end in `IntegrationVerifyError`, naming
|
|
1071
|
+
the gate and its log path (`nodes/_integration/gates/<gate>.log`).
|
|
1072
|
+
- **`vinta-ai-maestro`: `exec` accepts a lane's short name or its phase, and
|
|
1073
|
+
`with` / `gate` work inside it.** `exec <run> crew-3-tier2-2` and
|
|
1074
|
+
`exec <run> p1` were refused `unknown_target`; only the full
|
|
1075
|
+
`<run-id>-crew-3-tier2-2` worked. Both now resolve, and a refusal lists the
|
|
1076
|
+
run's targets. Inside `exec`, `with test-suite -- make test` said "no live
|
|
1077
|
+
run is available" while the run was live: the command now holds leases as the
|
|
1078
|
+
phase running in the lane, or as itself on an idle one, until it exits.
|
|
960
1079
|
- **`vinta-ai-maestro`: a fix loop that changes nothing now stops and asks.**
|
|
961
1080
|
A typecheck red because of ignored files left in a lane kept coming back
|
|
962
1081
|
from the gate cache after they were gone, and twenty fixers in a row found
|
package/package.json
CHANGED
|
@@ -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`, `max_spare_crew`, `pull_requests` (`phase` | `plan`).",
|
|
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`), `verify_gates` (`full` | `scoped`), `base_gates` (`full` | `off`), `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": {
|
|
@@ -379,6 +379,22 @@
|
|
|
379
379
|
"full"
|
|
380
380
|
]
|
|
381
381
|
},
|
|
382
|
+
"verify_gates": {
|
|
383
|
+
"type": "string",
|
|
384
|
+
"enum": [
|
|
385
|
+
"full",
|
|
386
|
+
"scoped"
|
|
387
|
+
],
|
|
388
|
+
"description": "What a phase's `verify` runs once its review approves. `full` (the default): every gate's whole command on the phase branch, so a PR never carries a suite only a scoped run saw. `scoped`: the scoped form there too, for a suite too slow to run once per phase; the merged-tree gates are then the first full run. No effect under `gate_scope: full`."
|
|
389
|
+
},
|
|
390
|
+
"base_gates": {
|
|
391
|
+
"type": "string",
|
|
392
|
+
"enum": [
|
|
393
|
+
"full",
|
|
394
|
+
"off"
|
|
395
|
+
],
|
|
396
|
+
"description": "Whether a multi-dependency phase's merged base is gated before the phase starts on it. `full` (the default): every command gate its dependencies declare, whole command, on the merged base; red fails the phase's setup. `off`: the phase starts on the merged base unchecked."
|
|
397
|
+
},
|
|
382
398
|
"substitution": {
|
|
383
399
|
"type": "string",
|
|
384
400
|
"enum": [
|
|
@@ -395,7 +411,7 @@
|
|
|
395
411
|
"final",
|
|
396
412
|
"every"
|
|
397
413
|
],
|
|
398
|
-
"description": "When the phases' gates run in full on a merged wave, in the integration worktree. `
|
|
414
|
+
"description": "When the phases' gates run in full on a merged wave, in the integration worktree. `every` (the default): a wave's gates on every wave, so the next wave builds on a passing tree, and every gate of the plan on the last wave before the plan PR. `final`: the last wave's, and a wave's gates only on a wave whose merge needed a conflict resolution. `off`: none of these (the full form of a `scoped_cmd` gate still runs per wave under `gate_scope: scoped`)."
|
|
399
415
|
},
|
|
400
416
|
"allow_ungated_phases": {
|
|
401
417
|
"type": "boolean",
|
|
@@ -420,7 +420,7 @@
|
|
|
420
420
|
},
|
|
421
421
|
"gate_scope": {
|
|
422
422
|
"default": "scoped",
|
|
423
|
-
"description": "`scoped` — the default — runs a gate’s `scoped_cmd` on phase gates and its full `cmd`
|
|
423
|
+
"description": "`scoped` — the default — runs a gate’s `scoped_cmd` on a phase’s gates up to and during its review, and its full `cmd` once the review approves (`verify`) and on every merged tree in the integration worktree. `full` runs `cmd` everywhere.",
|
|
424
424
|
"type": "string",
|
|
425
425
|
"enum": [
|
|
426
426
|
"scoped",
|
|
@@ -453,9 +453,27 @@
|
|
|
453
453
|
"minimum": 0,
|
|
454
454
|
"maximum": 9007199254740991
|
|
455
455
|
},
|
|
456
|
+
"verify_gates": {
|
|
457
|
+
"default": "full",
|
|
458
|
+
"description": "What `standard-phase`’s `verify` runs once the review approves. `full` — the default — runs every gate’s whole `cmd` on the phase branch, so a PR never carries a suite only a scoped run saw; a red full run goes back to a fixer and the review. `scoped` keeps the scoped form there too, for a suite too slow to run once per phase — the merged-tree gates are then the first full run. No effect under `gate_scope: full`.",
|
|
459
|
+
"type": "string",
|
|
460
|
+
"enum": [
|
|
461
|
+
"full",
|
|
462
|
+
"scoped"
|
|
463
|
+
]
|
|
464
|
+
},
|
|
465
|
+
"base_gates": {
|
|
466
|
+
"default": "full",
|
|
467
|
+
"description": "Whether a multi-dependency phase’s base (`integ-<id>`, its dependencies merged) is gated before the phase starts on it. `full` — the default — runs every command gate its dependencies declare, whole `cmd`, on the merged base, cached on its tree; red fails the phase’s setup. `off` starts the phase on the merged base unchecked.",
|
|
468
|
+
"type": "string",
|
|
469
|
+
"enum": [
|
|
470
|
+
"full",
|
|
471
|
+
"off"
|
|
472
|
+
]
|
|
473
|
+
},
|
|
456
474
|
"wave_gates": {
|
|
457
|
-
"default": "
|
|
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. `
|
|
475
|
+
"default": "every",
|
|
476
|
+
"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. `every` — the default — runs the wave’s phases’ gates on every wave, so the next wave builds on a passing tree and a disagreement is caught at the wave that introduced it, and every gate of the plan once on the last wave before the plan PR opens. It costs one full suite per wave. `final` runs the last wave’s and any wave whose merge needed a conflict resolution only. `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.",
|
|
459
477
|
"type": "string",
|
|
460
478
|
"enum": [
|
|
461
479
|
"off",
|
|
@@ -101,7 +101,8 @@ These bleed across sub-skills, so capture once now:
|
|
|
101
101
|
On `Set project defaults`, capture:
|
|
102
102
|
- **Gates every phase runs.** Multi-select `AskUserQuestion` over the types the commands above give a command for: `typecheck` (from `commands.build`), `lint`, `test` (from `commands.test_unit`), and `e2e` only when `add-e2e-test` is enabled. → `maestro.defaults.gates`.
|
|
103
103
|
- **A different command for maestro.** `AskUserQuestion` per selected type: `Same as commands.* (Recommended)`, `Different for maestro (I'll give it)`. Teams pick a different one when lanes need a flag the inner loop does not, e.g. `pytest --reuse-db -n auto`. → `maestro.gates.<type>.cmd`. `implement-plan` keeps running `commands.*`.
|
|
104
|
-
- **Scoped gates.** Only when `test` (or `lint`) is selected: should a phase's gate run narrowed to the files the phase changed, with the full command run
|
|
104
|
+
- **Scoped gates.** Only when `test` (or `lint`) is selected: should a phase's gate run narrowed to the files the phase changed until its review approves, with the full command run after the review and on every merged tree? `AskUserQuestion`: `Yes — scoped until review, full after (Recommended)`, `No — full command every time`. On `Yes`, propose a template with `{changed_files}` for the detected runner (`vitest related {changed_files} --run`, `jest --findRelatedTests {changed_files}`, `pytest --testmon {changed_files}`, `ruff check {changed_files}`) and confirm it. It lands in `commands.test_unit_scoped` / `commands.lint_scoped`, where `implement-plan` reads it too. `No` sets `maestro.defaults.gate_scope: full`.
|
|
105
|
+
- **When the full suite runs.** Only on `Yes` above. `AskUserQuestion`: `After each review and on every merged tree (Recommended)`, `Only on merged trees`, `Only on merged waves`. The first is the default and emits nothing. `Only on merged trees` skips the per-phase full run after review (`maestro.defaults.verify_gates: scoped`), for a suite too slow to run once per phase. `Only on merged waves` also leaves multi-dependency bases unchecked (`verify_gates: scoped` and `base_gates: off`).
|
|
105
106
|
- **A test-suite pool.** `AskUserQuestion`: `One suite at a time (Recommended for DB-backed suites)`, `Two at a time`, `No limit`. A limit emits `maestro.resources.test-suite` (`kind: semaphore`, `capacity` 1 or 2) and `maestro.gates.test.requires: [test-suite]`. Then offer the runner's command patterns as the pool's `match` (`pytest*`, `uv run pytest*`, `pnpm test*`), which makes agents take the lease before running tests by hand.
|
|
106
107
|
- **Files git does not carry.** A maestro lane is a worktree and has only what git tracks. Read `.gitignore` for entries with a tracked `*.example` / `*.sample` / `*.template` sibling, the compose file's `env_file:` lines, and the gitignored settings module the framework imports (`DJANGO_SETTINGS_MODULE`'s local module, typically). Propose the list in a multi-select `AskUserQuestion` (header `Env files`), each path pre-selected; the user confirms or drops entries. → `maestro.project.env_files`. Every hook, gate and `manage.py` call in a lane depends on these, and a plan that omits them fails in ways that look like code problems. Nothing found → omit the key and say so.
|
|
107
108
|
|
|
@@ -366,7 +367,9 @@ run_options:
|
|
|
366
367
|
maestro:
|
|
367
368
|
defaults:
|
|
368
369
|
gates: <C.11 → gates every phase runs, e.g. [typecheck, lint, test]>
|
|
369
|
-
gate_scope: <C.11 → omit for "scoped" (the default); `full` on "No — full command every
|
|
370
|
+
gate_scope: <C.11 → omit for "scoped" (the default); `full` on "No — full command every time">
|
|
371
|
+
verify_gates: <C.11 → omit (the default, `full`); `scoped` on "Only on merged trees" or "Only on merged waves">
|
|
372
|
+
base_gates: <C.11 → omit (the default, `full`); `off` on "Only on merged waves">
|
|
370
373
|
gates:
|
|
371
374
|
test:
|
|
372
375
|
cmd: <C.11 → maestro-only test command; omit to use commands.test_unit>
|
|
@@ -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
|
|
|
@@ -607,7 +608,8 @@ First key in the file is `"$schema"`, pointing at `https://github.com/vintasoftw
|
|
|
607
608
|
| `chores.review-canvas` | **Emit only when `.vinta-ai-workflows.yaml` has `integrations.pr-review-canvas: enabled`.** An `after_pr` chore: maestro runs it once each phase's PR is open, and it posts a review canvas on that PR. Emit it exactly as shown under "The review-canvas chore" below. Never emit it when the integration is `disabled` or absent. The skill it names is installed only when the integration is enabled. |
|
|
608
609
|
| `defaults.chores` | `["review", "deslop"]`, plus `"review-canvas"` when that chore is emitted — or **omit the key** when `maestro.defaults.chores` already names the same list. The chores every phase runs unless it names its own. **Never write `"chores": []` to mean "the project's"**: an empty list *replaces* the project's list with nothing, and the run then runs no chore at all. |
|
|
609
610
|
| `defaults.substitution` | **Omit** — it defaults to `up_one_tier`: a busy member's phase may go to the next tier up the roster has, and a warm session never moves a phase up. Set `same_tier` when the top tier is a limited credit allowance that must not absorb other tiers' work. |
|
|
610
|
-
| `defaults.wave_gates` | **Omit** — it defaults to `
|
|
611
|
+
| `defaults.wave_gates` | **Omit** — it defaults to `every`: each wave's gates run in full on the merged tree, so the next wave builds on a passing one, and every gate of the plan once more before the plan PR opens. Set `final` only for a plan whose suite is too slow to run once per wave. Never `off` unless the project's suite cannot run in the integration worktree. |
|
|
612
|
+
| `defaults.verify_gates` / `defaults.base_gates` | **Omit** — both default to `full`: a phase runs its gates' full commands once its review approves, and a multi-dependency phase's merged base is gated before it starts. Set `verify_gates: scoped` or `base_gates: off` only when the human says the suite is too slow for it, and say so in the plan. |
|
|
611
613
|
| `defaults.gates` | The gate ids every phase runs. **Omit the key** when `maestro.defaults.gates` is already the list this plan wants; set it when most phases share a list the project does not name. A node with no `gates` key takes it. **Never `[]`**: that is a run with no gates. |
|
|
612
614
|
| `nodes[].chores` | **Omit the key** on almost every phase — absent means the run-wide default. Name a list only to give one phase a different set, and `[]` only to deliberately opt one phase out. A list *replaces* the default rather than adding to it; `[]` is "no chores", never "the default". |
|
|
613
615
|
| `nodes[]` | One per phase, in plan order. |
|
|
@@ -640,7 +642,7 @@ Rules the mapping depends on:
|
|
|
640
642
|
Read `.vinta-ai-workflows.yaml` before writing the document. Its `commands` and its `maestro:` section are laid **under** this file when maestro loads it: every value the project already supplies reaches this plan without being written here, and a value written here overrides it for this plan alone. So the document says what is particular to this plan and nothing else. A copy of a project value is not harmless — it freezes the value into the plan, and a later change to the project's file never reaches it.
|
|
641
643
|
|
|
642
644
|
- **Gates by type.** A gate typed `test`, `lint`, `typecheck` or `e2e` takes its command from the project — `maestro.gates.<type>`, else `commands.test_unit` / `lint` / `build` / `e2e`. Each type the project has a command for is available under the type's own name, so a node can list `"gates": ["typecheck", "test"]` with no `gates` table at all. Write an entry only to change something for this plan: `{ "type": "test", "timeout_s": 3600 }` keeps the project's command and extends its timeout; `{ "cmd": "…" }` (no type) replaces it outright.
|
|
643
|
-
- **`scoped_cmd`** is the same check narrowed to what a phase changed, with `{changed_files}` / `{touches}` in it. The project normally supplies it (`commands.test_unit_scoped`, `maestro.gates.<type>.scoped_cmd`).
|
|
645
|
+
- **`scoped_cmd`** is the same check narrowed to what a phase changed, with `{changed_files}` / `{touches}` in it. The project normally supplies it (`commands.test_unit_scoped`, `maestro.gates.<type>.scoped_cmd`). A phase's gates run it until its review approves; the full command runs after that, on the phase branch, and on every merged tree — each wave and each multi-dependency base. Set one here only for a plan whose phases need a different narrowing.
|
|
644
646
|
- **Pools, chores and the `project` block** merge per id: an entry here replaces the project's entry of the same id whole. Declare `resources.lane` (it is this plan's — one desk per implementer), and a pool, chore or database only when the project has none or this plan needs a different one.
|
|
645
647
|
- **`defaults`** merge field by field: omit what `maestro.defaults` already says.
|
|
646
648
|
- **Omitting a key inherits; writing `[]` replaces with nothing.** `nodes[].gates`, `defaults.gates`, `defaults.chores` and `nodes[].chores` are lists, and lists replace rather than merge. The only way to take the project's list is to leave the key out. `"gates": []` on a node is a phase with no gates, `"chores": []` in `defaults` is a run with no review and no comment pass — both silent at run time, and both exactly what the observed run did. When in doubt, omit the key and read what `validate --json` resolved.
|
|
@@ -1071,6 +1073,8 @@ Fix each issue by its `source`:
|
|
|
1071
1073
|
- **`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
1074
|
- **`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
1075
|
|
|
1076
|
+
**`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.
|
|
1077
|
+
|
|
1074
1078
|
## Review the plan with the human
|
|
1075
1079
|
|
|
1076
1080
|
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?"
|