vinta-ai-workflows 0.7.0-alpha34 → 0.7.0-alpha36
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 +323 -0
- package/package.json +1 -1
- package/schemas/postmortem.v1.schema.json +213 -2
- package/schemas/vinta-ai-workflows-config.v1.schema.json +107 -4
- package/schemas/workflow.v1.schema.json +152 -4
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +16 -2
- package/skills/vinta-bootstrap-ai-tools/resources/stacks/django/notes.md +14 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +27 -2
- package/skills/vinta-derive-skills/resources/plan-execution/README.md +9 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/maestro-live-run.md +40 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +8 -1
- package/skills/vinta-derive-skills/resources/write-unit-test-packs/stacks/django.md +4 -1
- package/skills/vinta-derive-skills/resources/write-unit-test-template.md +3 -0
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-alpha36 on 2026-10-18 -->
|
|
11
|
+
<!-- pre-release: 0.7.0-alpha35 on 2026-10-16 -->
|
|
10
12
|
<!-- pre-release: 0.7.0-alpha34 on 2026-10-16 -->
|
|
11
13
|
<!-- pre-release: 0.7.0-alpha33 on 2026-10-16 -->
|
|
12
14
|
<!-- pre-release: 0.7.0-alpha31 on 2026-10-09 -->
|
|
@@ -34,6 +36,208 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
34
36
|
|
|
35
37
|
### Added
|
|
36
38
|
|
|
39
|
+
- **`amend-plan` amends a live `vinta-ai-maestro` run instead of rewriting
|
|
40
|
+
its branches.** When a run of the plan is running, paused, interrupted or
|
|
41
|
+
failed, the skill asks whether to amend it, then edits the plan and the
|
|
42
|
+
run's own workflow copy (`vinta-ai-maestro workflow`, so earlier operator and
|
|
43
|
+
coordinator amendments survive), commits both on `plan/<workflow-id>/base`
|
|
44
|
+
and applies them with `vinta-ai-maestro amend`, which rebases the done
|
|
45
|
+
phases the change reaches. Before, the plan file changed and the run went on
|
|
46
|
+
building its snapshot. The skill also carries operator recipes for a live
|
|
47
|
+
run: switching harness or model, switching the account a harness runs as,
|
|
48
|
+
resuming with the same policy flags, and moving the base forward. New
|
|
49
|
+
partial `plan-execution/partials/maestro-live-run.md`; re-run
|
|
50
|
+
`vinta-derive-skills` to pick it up.
|
|
51
|
+
- **`vinta-ai-maestro`: a phase the review keeps rejecting moves up a crew
|
|
52
|
+
tier.** One run left a Tier 1 phase on its desk through 16 review rejections
|
|
53
|
+
over six hours until the coordinator moved it by hand. New
|
|
54
|
+
`defaults.escalate_after_rejections` (default `3`, `0` turns it off): when a
|
|
55
|
+
phase's review has failed to approve that many times on one desk, counted
|
|
56
|
+
across attempts, the scheduler amends the run. The phase moves to the
|
|
57
|
+
cheapest member of the next tier up and takes the new desk from its next
|
|
58
|
+
attempt. The amendment is journalled as `workflow_amended` with the new
|
|
59
|
+
author `scheduler`, a one-line `reason` and a new `rule` field
|
|
60
|
+
(`review_rejections`, the members, the tiers and the count). A phase already at the top tier stays
|
|
61
|
+
where it is. Amendments may now leave a member the run already had with no
|
|
62
|
+
phase, so moving a member's last phase is no longer refused.
|
|
63
|
+
- **`vinta-ai-maestro`: `purge` drops lane rescues whose work landed.** The
|
|
64
|
+
`refs/vinta-ai-maestro/wip/*` commits a recycled lane keeps of a phase's
|
|
65
|
+
uncommitted files piled up (~80 in one project). A purge now drops those of
|
|
66
|
+
the runs it purges whose phase is done and merged into a branch outside
|
|
67
|
+
`plan/` and `wt/`, and those of already-purged runs whose rescued files read
|
|
68
|
+
the same at `HEAD`. Every other rescue is kept, and `--dry-run` lists them.
|
|
69
|
+
- **`vinta-ai-maestro`: a `ui` left up across an upgrade says so.** It warns
|
|
70
|
+
once when the installed package's version no longer matches the one it
|
|
71
|
+
loaded, and `run` and `status` warn about a live `ui` serving another
|
|
72
|
+
release. A `ui` started days earlier used to keep serving pre-upgrade code
|
|
73
|
+
without a word.
|
|
74
|
+
- **`vinta-ai-maestro`: a resume remembers how the run was started.** Every
|
|
75
|
+
job records its `--permission`, `--system-one`, `--on-failure`, `--retries`,
|
|
76
|
+
`--retry-after`, `--no-coordinator`, `--host` and `--harness-profile` in
|
|
77
|
+
`runs/<id>/policy.json`, and `run --resume` (and the UI's Resume) reuses
|
|
78
|
+
them, saying which; a flag given on the command line still wins. Each resume
|
|
79
|
+
used to need all of them restated.
|
|
80
|
+
- **`vinta-ai-maestro`: harness profiles.** `--harness-profile <name>` on `run`
|
|
81
|
+
and `ui` picks a named binary and environment for one harness — a second
|
|
82
|
+
Claude account's `CLAUDE_CONFIG_DIR`, say — from the operator's
|
|
83
|
+
`~/.config/vinta-ai-maestro/profiles.yaml` (or `$VINTA_AI_MAESTRO_PROFILES`).
|
|
84
|
+
The run keeps the name, so a resume picks the same account.
|
|
85
|
+
- **`vinta-ai-maestro`: `propagate` works on a live run.** It refused one, so
|
|
86
|
+
a fix on a phase could not reach older `integ-*` branches until the run
|
|
87
|
+
ended, and their PRs stayed red. On a live run the run's job now makes the
|
|
88
|
+
merges, holding the integration worktree's queue, so none of the run's own
|
|
89
|
+
merges interleave with them.
|
|
90
|
+
- **`vinta-ai-maestro`: `sync-base <run>` brings a moved `base_branch` into a
|
|
91
|
+
running plan.** It merges the base (the remote's, when ahead) into
|
|
92
|
+
`plan/<id>/base`, then the plan branch into the lowest wave and each wave
|
|
93
|
+
into the one above. On a live run a wave's conflict goes to the run's
|
|
94
|
+
conflict fixer, gated as a phase merge is; the plan branch's own conflict,
|
|
95
|
+
and any conflict on a run that is not live, stops it with the branch as it
|
|
96
|
+
was. Both commands journal `branches_moved` on a live run.
|
|
97
|
+
- **`vinta-ai-maestro`: a list of what the run decided without you.** One
|
|
98
|
+
run answered 79 of its 125 questions unattended — review loops stopped,
|
|
99
|
+
agents' design questions given their first option, one of which added a
|
|
100
|
+
migration — and none of it was anywhere a person would look. Every question
|
|
101
|
+
a `--retry-after` timer or the run coordinator answered, and everything the
|
|
102
|
+
coordinator did on its own (steering, pauses, aborts, amendments, `exec`),
|
|
103
|
+
is now listed in:
|
|
104
|
+
- `status <run>` (*Decided without you*; `unattended` in `--json`), with an
|
|
105
|
+
agent question's header and the option it got;
|
|
106
|
+
- the run view's **Decided without you** panel (`GET /api/runs/:runId/unattended`);
|
|
107
|
+
- the plan PR's *Decided without a person* section;
|
|
108
|
+
- the post-mortem's new optional `unattended_decisions` finding
|
|
109
|
+
(`schemas/postmortem.v1.schema.json`, additive), which `plan-feature`
|
|
110
|
+
now reads: an agent question answered unattended is a decision the plan
|
|
111
|
+
left open.
|
|
112
|
+
- **`vinta-ai-maestro`: merge resolutions that undo a phase's deletion are
|
|
113
|
+
flagged.** One run's phase deleted an unused notification kind; the
|
|
114
|
+
conflict fixer kept "both sides" and every gate passed, so the deletion
|
|
115
|
+
never landed. After every merge in the integration worktree — a fixer's
|
|
116
|
+
resolution, or the `append_only` union driver, which re-adds lines one side
|
|
117
|
+
removed — the integrator counts lines one side deleted that the result
|
|
118
|
+
kept (codegen outputs excluded). Each is recorded as path, phase and line
|
|
119
|
+
count, never the lines: on the `node_conflict` journal row
|
|
120
|
+
(`reverted_deletions`), in the post-mortem's `wave_conflicts` and new
|
|
121
|
+
optional `unattended_decisions.reverted_deletions`
|
|
122
|
+
(`schemas/postmortem.v1.schema.json`, additive), and in the list of what
|
|
123
|
+
was decided without you (`status`, the run view, the plan PR body). It
|
|
124
|
+
flags; it does not block the merge. The conflict-fixer prompt now says a
|
|
125
|
+
phase's intent includes what it deleted.
|
|
126
|
+
- **`vinta-ai-maestro`: `--agent-answer-after` on `run` and `ui`.** How long an
|
|
127
|
+
agent's own question waits before it takes the recommended options. It
|
|
128
|
+
used to be `--retry-after`, so a one-minute retry interval answered design
|
|
129
|
+
questions in a minute. Unset, it is twice `--retry-after` and never under 30
|
|
130
|
+
minutes; set alone, agents' questions are answered while failures still
|
|
131
|
+
wait for a person.
|
|
132
|
+
- **`vinta-ai-maestro`: `doctor` checks the gates against CI.** It reads
|
|
133
|
+
`.github/workflows/*.yml` and warns about each `run:` command no gate runs,
|
|
134
|
+
or runs without a flag CI passes (`manage.py check` for CI's `check
|
|
135
|
+
--deploy`) — the reason every gate passed while CI failed on twenty review
|
|
136
|
+
PRs of one run. Matching is heuristic (tool and subcommand after `uv run`,
|
|
137
|
+
`npx`, `docker compose run`, …; `uses:` steps are not read) and it is always
|
|
138
|
+
a warning.
|
|
139
|
+
- **`vinta-ai-maestro`: project-wide custom gates.** `maestro.gates` in
|
|
140
|
+
`.vinta-ai-workflows.yaml` now takes ids of your own beside the four types —
|
|
141
|
+
`django-check: { cmd: uv run python manage.py check --fail-level WARNING }`,
|
|
142
|
+
`api-client-fresh: { cmd: make api_client && git diff --exit-code … }` —
|
|
143
|
+
with the fields a type takes (`cmd`, `scoped_cmd`, `requires`, `timeout_s`,
|
|
144
|
+
`tuning`). Every plan has each one under its id without declaring it, and
|
|
145
|
+
`maestro.defaults.gates` names it like a type, so a check CI runs is
|
|
146
|
+
declared once and keeps its own verdict instead of being chained onto
|
|
147
|
+
`typecheck` or repeated in every plan. A plan's gate of the same id replaces
|
|
148
|
+
it whole. `validate` refuses an id that is not lowercase kebab-case, the
|
|
149
|
+
reserved `codegen`, and a custom gate with no `cmd`. Previously any key but
|
|
150
|
+
a type failed with "Unrecognized keys". Additive in the config schema.
|
|
151
|
+
`plan-feature` and the Django stack notes now point CI-mirroring checks at
|
|
152
|
+
`maestro.gates.<id>`.
|
|
153
|
+
- **`vinta-ai-maestro`: codegen steps — generated files are regenerated, not
|
|
154
|
+
merged.** A workflow (or `maestro.codegen` in `.vinta-ai-workflows.yaml`)
|
|
155
|
+
declares `codegen.<id>`: a deterministic command, the files it writes
|
|
156
|
+
(`outputs`), the files they are a function of (`inputs`), and `after` for the
|
|
157
|
+
chain (schema, then client). The steps run in every lane before the gates
|
|
158
|
+
and their outputs are committed there; in the integration worktree a
|
|
159
|
+
conflict confined to their outputs is settled by taking either side and
|
|
160
|
+
running them, with no conflict fixer, and every merge is regenerated before
|
|
161
|
+
it is committed. A failing step is a red gate. `inputs` skip a step whose
|
|
162
|
+
inputs and outputs match a recorded run, a step that writes different
|
|
163
|
+
outputs from the same inputs is journalled `nondeterministic`, and a
|
|
164
|
+
`format: true` step formats only the files the phase changed. Agents are told
|
|
165
|
+
which files are generated and regenerate them mid-turn with
|
|
166
|
+
`vinta-ai-maestro codegen [<step>]`; `doctor --codegen` runs each step twice
|
|
167
|
+
to flag non-deterministic or stale output. Generated files caused most of one
|
|
168
|
+
run's 37 wave conflicts. `workflow.v1` and the config schema gain the
|
|
169
|
+
additive `codegen` field.
|
|
170
|
+
|
|
171
|
+
- **`vinta-ai-maestro`: append-only files merge as a union.** `append_only` (or
|
|
172
|
+
`maestro.append_only`) lists files phases only add entries to — DI
|
|
173
|
+
containers, URL routes, notification kinds, beat schedules — and merges in
|
|
174
|
+
the integration worktree use git's `union` driver for them, so two phases
|
|
175
|
+
each adding an entry no longer needs a fixer round. The merged tree is gated
|
|
176
|
+
as before. Additive in `workflow.v1` and the config schema.
|
|
177
|
+
|
|
178
|
+
- **`plan-feature`: emits codegen steps and append-only files** detected from the
|
|
179
|
+
project — the OpenAPI schema, generated API clients, route trees and
|
|
180
|
+
file-modifying pre-commit hooks as `codegen`; DI containers, URL routes and
|
|
181
|
+
beat schedules as `append_only` — preferring `maestro.codegen` /
|
|
182
|
+
`maestro.append_only` in the project file. `vinta-bootstrap-ai-tools` offers
|
|
183
|
+
to record them there once.
|
|
184
|
+
- **`vinta-ai-maestro`: take a phase over without failing it.** Moving a phase
|
|
185
|
+
to another agent took abort, amend and retry, and the retry restarted its
|
|
186
|
+
pipeline from the top — one phase went from review back to implementing.
|
|
187
|
+
`node interrupt <run> <phase>` stops the turn now and **holds** the phase,
|
|
188
|
+
lane, branch, commits and uncommitted work kept, until `resume`, `reassign`
|
|
189
|
+
or `step` releases it; the cut step runs again then. `node reassign <run>
|
|
190
|
+
<phase> --crew <member> | --harness <h> --model <m>` is interrupt, amend and
|
|
191
|
+
resume in one (`--after-turn` lets the turn finish): the phase carries on at
|
|
192
|
+
the same step, keeps its session when only the model changes, and otherwise
|
|
193
|
+
starts fresh with a handoff — the last report, the work in the lane, the red
|
|
194
|
+
gate. `node step <run> <phase> <state>` moves the pipeline by hand, never
|
|
195
|
+
into a final state. Journalled as `node_reassigned` and `node_operation`.
|
|
196
|
+
- **`vinta-ai-maestro`: `amend` accepts a held phase, or one waiting on a
|
|
197
|
+
question.** Its harness, model or crew member (`crew_changed`, a new
|
|
198
|
+
amendment kind) may change while it is parked, and it carries on at its
|
|
199
|
+
step. A brief, a gate list or a base still waits for the phase to stop.
|
|
200
|
+
- **`vinta-ai-maestro`: `switch-harness <run> --from --to --model` and
|
|
201
|
+
`defaults.harness_fallbacks`.** A live run moves off a harness — an account
|
|
202
|
+
out of credits — without a pause and resume: phases in flight are
|
|
203
|
+
reassigned at their step, later phases run on the new harness, and the
|
|
204
|
+
conflict fixer moves too. With `harness_fallbacks` the run does it by
|
|
205
|
+
itself when a harness parks on a quota refusal, the refused turn going
|
|
206
|
+
again at once on the fallback. Journalled `harness_switched`.
|
|
207
|
+
- **`vinta-ai-maestro`: `fixer interrupt|reassign <run>`.** End the conflict
|
|
208
|
+
fixer's running round as a round spent (what it left stays), or hand later
|
|
209
|
+
rounds to another harness and model. Journalled `fixer_operation`.
|
|
210
|
+
- **`vinta-ai-maestro`: one API for the operator and the coordinator, and every
|
|
211
|
+
act says why.** Every acting request takes a reason (`--reason` on every
|
|
212
|
+
verb, the `X-Maestro-Reason` header on the API), journalled with what it
|
|
213
|
+
explains; the coordinator is refused without one (`reason_required`). The
|
|
214
|
+
coordinator gains `interrupt`, `reassign`, `switch-harness`, `fixer`,
|
|
215
|
+
`resume` of its own holds, and `pause` of the run (`run_halt_requested`).
|
|
216
|
+
`run --coordinator-authority full` also lets it change the plan's shape —
|
|
217
|
+
phases, dependency edges, briefs, touch lists, deferrals — answer any
|
|
218
|
+
question you are not holding, `node step`, and stop the run. A check is
|
|
219
|
+
never weakened under either authority.
|
|
220
|
+
|
|
221
|
+
- **`vinta-ai-maestro`: integration is visible while it happens.** A phase in
|
|
222
|
+
`integrate` — a merge, a suite on the merged tree, a push, none with an
|
|
223
|
+
agent — showed `running` over a silent transcript, and one queued behind it
|
|
224
|
+
looked the same. Now:
|
|
225
|
+
- The phase view has an **Integration** timeline: each merge, conflict fix
|
|
226
|
+
round, `verify`, wave gate, push and PR of this attempt, a live clock on
|
|
227
|
+
the one in progress, and the log of every step that has one.
|
|
228
|
+
- The run view has an **Integration worktree** panel: who holds it, the step
|
|
229
|
+
it is on and for how long, and who is queued behind it. The roster and
|
|
230
|
+
the phase header show each running phase's pipeline step.
|
|
231
|
+
- `status` prints the step beside each running phase and an `integration`
|
|
232
|
+
line (`a · wave 1 gate unit · 6m 13s · 1 queued (b)`); `status --json`
|
|
233
|
+
carries `nodes[].step` and `integration`.
|
|
234
|
+
- A conflict fixer's turn is also written to the transcript of the phase
|
|
235
|
+
being based or building the wave, not only to the incoming phase's.
|
|
236
|
+
|
|
237
|
+
Two additive journal events back it: `node_step` (the pipeline state an
|
|
238
|
+
attempt entered) and `integration_step` (a step started or finished in the
|
|
239
|
+
worktree, identifiers and a log path only). Older runs have neither and
|
|
240
|
+
show no timeline.
|
|
37
241
|
- **`vinta-ai-maestro`: `accept` on an unapproved review.** The review-budget
|
|
38
242
|
question offered `continue` or `stop`, and operators answered `stop` meaning
|
|
39
243
|
"take it as it is" — which fails the attempt and, under `--on-failure
|
|
@@ -638,6 +842,91 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
638
842
|
|
|
639
843
|
### Changed
|
|
640
844
|
|
|
845
|
+
- **`plan-feature`: a rename of a shared symbol is an interface change.** A
|
|
846
|
+
phase that renames something a same-wave sibling also uses does it in the
|
|
847
|
+
foundation phase or keeps the old name as an alias until the wave lands.
|
|
848
|
+
One run had a phase call a helper its sibling renamed; each passed alone and
|
|
849
|
+
the merged wave failed the typecheck.
|
|
850
|
+
- **`plan-feature`: gates cover what CI runs.** The workflow rules now tell the
|
|
851
|
+
author to read the CI config and give every locally runnable CI job a gate,
|
|
852
|
+
naming the checks plans miss: a deploy check at warning level, a
|
|
853
|
+
migrations-complete check, a "generated client is up to date" check. The
|
|
854
|
+
Django stack notes suggest `manage.py check --deploy --fail-level WARNING`,
|
|
855
|
+
`makemigrations --check` and a regenerate-and-diff gate for the schema or
|
|
856
|
+
client. One run's regex-style routes passed every gate and failed CI.
|
|
857
|
+
- **`write-unit-test`: time-independent tests and deterministic generated
|
|
858
|
+
output.** Rule 8 now requires a frozen clock and an explicit timezone on any
|
|
859
|
+
test touching dates, and host-independent schema or client generation
|
|
860
|
+
(values from a locked package such as `tzdata`, sorted). The Django pack and
|
|
861
|
+
stack notes spell out `freezegun` / `time-machine` with
|
|
862
|
+
`override_settings(TIME_ZONE=...)` and `tzdata`-sourced timezone choices.
|
|
863
|
+
One run had a date test fail only near midnight UTC, and a timezone enum
|
|
864
|
+
differ by machine.
|
|
865
|
+
- **`vinta-bootstrap-ai-tools`: test-tool caches are ignored before lanes run.**
|
|
866
|
+
The maestro defaults step reads `.gitignore` for the selected runners'
|
|
867
|
+
caches and reports (`.testmondata*`, `junit/`, coverage outputs, …),
|
|
868
|
+
checks for the `check-added-large-files` pre-commit hook, which a committed
|
|
869
|
+
testmon database trips on every lane and merge commit, and offers to append
|
|
870
|
+
the missing entries.
|
|
871
|
+
- **`vinta-ai-maestro`: a review that stated no verdict is no longer reported
|
|
872
|
+
as a rejection.** One run recorded 22 attempts as `review verdict "fail"; 0
|
|
873
|
+
fix rounds spent`. None had rejected anything: each was a review turn that
|
|
874
|
+
ended before its reviewer answered (fixed in alpha33 and alpha34). The
|
|
875
|
+
pipeline still treats silence as `fail`, but the failure reason now says
|
|
876
|
+
`the review turn stated no verdict`, and the `review` fact carries
|
|
877
|
+
`stated: false`. The budget clause is renamed to `gate fix rounds spent`,
|
|
878
|
+
since a review's findings are fixed inside its own turn and never in a
|
|
879
|
+
separate fix round.
|
|
880
|
+
|
|
881
|
+
- **`vinta-ai-maestro`: two empty turns in a row park the phase instead of
|
|
882
|
+
burning its retries.** Right after one run moved its desks to codex, "codex
|
|
883
|
+
ended the turn without the agent doing anything" failed 20 attempts across 4
|
|
884
|
+
phases in about two minutes. When one harness ends two turns in a row
|
|
885
|
+
without the agent doing anything, across attempts, the phase now skips its
|
|
886
|
+
automatic retries and asks: the question says the harness is failing and
|
|
887
|
+
suggests `retry with` a member on another harness. `--retry-after` still
|
|
888
|
+
answers it, and backs off from the first question.
|
|
889
|
+
|
|
890
|
+
- **`vinta-ai-maestro`: a red gate on a merged base is re-run on retry.** One
|
|
891
|
+
run's base test gate failed once on a test that broke only near midnight.
|
|
892
|
+
Seven retries replayed the cached verdict, each reported as "failed during
|
|
893
|
+
setup, before any agent ran", and the unattended retry was withheld until an
|
|
894
|
+
operator ran `gate-cache invalidate`. The base gates now replay only a green
|
|
895
|
+
verdict and re-run a red one, and a red base gate no longer counts as a
|
|
896
|
+
setup failure, so `--retry-after` keeps retrying it with its usual backoff.
|
|
897
|
+
|
|
898
|
+
- **`vinta-ai-maestro`: a lane that will not recycle says why.** One run
|
|
899
|
+
failed 20 attempts with only "could not be recycled (worktree)" or
|
|
900
|
+
"(database)" in the journal and the daemon log. The failure now carries the
|
|
901
|
+
failing step's exit code and the tail of its stderr, the way a failed
|
|
902
|
+
`setup_cmd` already did, and a `reset_cmd`, `setup_cmd` or `prepare_cmd`
|
|
903
|
+
failing during a recycle reports that command's own error instead of
|
|
904
|
+
`LaneSetupError`. Command lines are still left out.
|
|
905
|
+
|
|
906
|
+
- **`vinta-ai-maestro`: the full suite runs before a PR, and on every merged
|
|
907
|
+
tree.** Under `gate_scope: scoped`, a phase's gates ran their scoped form
|
|
908
|
+
everywhere, so a PR could carry a suite only a scoped run had seen, and most
|
|
909
|
+
phases were `done` before any full suite touched their code.
|
|
910
|
+
- A phase's gates run scoped until its review approves; `verify`, after the
|
|
911
|
+
review, runs every gate's full `cmd` on the phase branch. A red full run
|
|
912
|
+
goes back to a fixer and through the review again — more review rounds,
|
|
913
|
+
no failing suite in a PR. The fixer is told the gate failed in full, and
|
|
914
|
+
`vinta-ai-maestro gate <id> --full` runs the whole command for an agent.
|
|
915
|
+
- `defaults.wave_gates` now defaults to `every`: each wave's gates run in
|
|
916
|
+
full on the merged tree, so the next wave builds on a passing one. Set
|
|
917
|
+
`final` for the previous behaviour.
|
|
918
|
+
- A multi-dependency phase's `integ-<id>` base runs its dependencies' gates
|
|
919
|
+
in full before the phase starts, cached on the merged tree; red fails the
|
|
920
|
+
phase's setup with `BaseGateError`. A clean base merge used to run nothing.
|
|
921
|
+
- Wave and base gate failures now reach the journal with their gate and log,
|
|
922
|
+
not just their error name.
|
|
923
|
+
- Each full run can be skipped in a plan's `defaults` or the project's
|
|
924
|
+
`maestro.defaults` (`.vinta-ai-workflows.yaml`): `verify_gates: scoped`
|
|
925
|
+
keeps `verify` on the scoped form, `base_gates: off` leaves merged bases
|
|
926
|
+
unchecked, and `wave_gates: final` / `off` as before. Both new fields are
|
|
927
|
+
in `vinta-ai-workflows-config.v1.schema.json`, and the bootstrap asks when
|
|
928
|
+
the full suite should run. The run coordinator may tighten them, never
|
|
929
|
+
loosen them.
|
|
641
930
|
- **`vinta-ai-maestro`: a recycled lane no longer keeps the last phase's
|
|
642
931
|
ignored files.** A lane handed to the next phase kept everything gitignored,
|
|
643
932
|
and one kept `apps/api/.mypy_cache` and a deleted package's `__pycache__`,
|
|
@@ -986,6 +1275,40 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
986
1275
|
|
|
987
1276
|
### Fixed
|
|
988
1277
|
|
|
1278
|
+
- **`vinta-ai-maestro`: the integrator no longer stages untracked files into
|
|
1279
|
+
a merge resolution.** It stages tracked paths and the merge's own paths
|
|
1280
|
+
instead of `git add --all`, so a gate's leftovers in the integration
|
|
1281
|
+
worktree (a `.testmondata-wal`, say) stay out of the merge commit. A kept
|
|
1282
|
+
resolution that changes paths outside the merge, or whose commit is refused
|
|
1283
|
+
again, is dropped instead of replayed on every retry. A conflict fixer now
|
|
1284
|
+
stages any new file it creates itself, and its prompt says so.
|
|
1285
|
+
- **`vinta-ai-maestro`: an amendment reaches the conflict fixer.** The fixer
|
|
1286
|
+
is rebuilt from the amended crew when the plan is amended, so a run whose
|
|
1287
|
+
desks moved to another harness stops failing on "conflict fixer spawn
|
|
1288
|
+
refused" for the harness it left.
|
|
1289
|
+
- **`vinta-ai-maestro`: a conflict fixer stopped by its time limit keeps its
|
|
1290
|
+
work.** What the round had is kept under `refs/vinta-ai-maestro/abandoned/`
|
|
1291
|
+
(an interrupted round's too), and the retry's fixer is pointed at it to
|
|
1292
|
+
continue instead of redoing the resolution from scratch.
|
|
1293
|
+
- **`vinta-ai-maestro`: an amended harness reaches every phase it moved.** An
|
|
1294
|
+
amendment changed the harness only on phases still `pending`; a failed or
|
|
1295
|
+
blocked one waiting on a retry kept naming the old harness in `status` and
|
|
1296
|
+
the run view, and once the job loaded only the new harness's adapter the
|
|
1297
|
+
run view answered 500 `unknown harness`. Phases past `pending` now take the
|
|
1298
|
+
new harness without losing their status, and the run view leaves out the
|
|
1299
|
+
capacity of a harness the live run loaded no adapter for.
|
|
1300
|
+
- **`vinta-ai-maestro`: `land` decides "already in the base" by content.** A
|
|
1301
|
+
review PR is landed when merging it into the base would leave the base's
|
|
1302
|
+
tree unchanged. The ancestry test it used alone kept an `integ-*` PR open
|
|
1303
|
+
after its plan PR merged: three bookkeeping merges were not in `main`, though
|
|
1304
|
+
every branch they merged was. A draft plan PR is now named as one (GitHub
|
|
1305
|
+
will not merge a draft), and `land <run> --ready` marks it ready for review.
|
|
1306
|
+
- **`vinta-ai-maestro`: a hook that rewrites files no longer refuses the
|
|
1307
|
+
commit outright.** When a commit fails having left modified files behind — a
|
|
1308
|
+
`pre-commit` formatter or schema generator — they are staged and the commit
|
|
1309
|
+
is tried once more before the operator is asked, in lanes and in the
|
|
1310
|
+
integration worktree. One run had seven merge commits refused this way.
|
|
1311
|
+
|
|
989
1312
|
- **`vinta-ai-maestro`: a claude-code turn the CLI calls "blocked" no longer
|
|
990
1313
|
fails unless a tool was refused in it.** The CLI's own turn summary marks a
|
|
991
1314
|
turn `blocked` whenever its last message names a next step for a person —
|
package/package.json
CHANGED
|
@@ -199,6 +199,37 @@
|
|
|
199
199
|
"minimum": 0,
|
|
200
200
|
"maximum": 9007199254740991,
|
|
201
201
|
"description": "Conflict-fixer rounds the resolution took. High is a strong split signal."
|
|
202
|
+
},
|
|
203
|
+
"reverted_deletions": {
|
|
204
|
+
"description": "Paths where the resolution kept lines one side had deleted — a fixer keeping “both sides”, or the `append_only` union driver. Flagged, not refused: check each deletion reached the branch. Absent when there were none.",
|
|
205
|
+
"type": "array",
|
|
206
|
+
"items": {
|
|
207
|
+
"type": "object",
|
|
208
|
+
"properties": {
|
|
209
|
+
"path": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"maxLength": 500
|
|
212
|
+
},
|
|
213
|
+
"node": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"maxLength": 200,
|
|
216
|
+
"description": "The phase whose deletion was reverted, or the branch standing in when no phase on that side touched the path."
|
|
217
|
+
},
|
|
218
|
+
"lines": {
|
|
219
|
+
"type": "integer",
|
|
220
|
+
"minimum": 1,
|
|
221
|
+
"maximum": 9007199254740991,
|
|
222
|
+
"description": "Lines that side deleted and the resolution kept. A count, never the lines."
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"required": [
|
|
226
|
+
"path",
|
|
227
|
+
"node",
|
|
228
|
+
"lines"
|
|
229
|
+
],
|
|
230
|
+
"additionalProperties": false,
|
|
231
|
+
"description": "A deletion a merge resolution undid. Identifiers and a count only."
|
|
232
|
+
}
|
|
202
233
|
}
|
|
203
234
|
},
|
|
204
235
|
"required": [
|
|
@@ -651,7 +682,8 @@
|
|
|
651
682
|
"operator",
|
|
652
683
|
"monitor",
|
|
653
684
|
"coordinator",
|
|
654
|
-
"config"
|
|
685
|
+
"config",
|
|
686
|
+
"scheduler"
|
|
655
687
|
]
|
|
656
688
|
}
|
|
657
689
|
},
|
|
@@ -680,8 +712,13 @@
|
|
|
680
712
|
"add_context",
|
|
681
713
|
"redirect",
|
|
682
714
|
"pause",
|
|
715
|
+
"resume",
|
|
683
716
|
"abort",
|
|
684
|
-
"fresh"
|
|
717
|
+
"fresh",
|
|
718
|
+
"interrupt",
|
|
719
|
+
"reassign",
|
|
720
|
+
"step",
|
|
721
|
+
"retry"
|
|
685
722
|
]
|
|
686
723
|
},
|
|
687
724
|
"by": {
|
|
@@ -840,6 +877,180 @@
|
|
|
840
877
|
"additionalProperties": false,
|
|
841
878
|
"description": "A gate that every phase passed on its own branch and that failed on the merged wave: two of `members` (or one of them and an earlier wave) disagree. The evidence of a missing dependency or a missing contract test that `missing_dependencies`’ ordering cannot see."
|
|
842
879
|
}
|
|
880
|
+
},
|
|
881
|
+
"unattended_decisions": {
|
|
882
|
+
"type": "object",
|
|
883
|
+
"properties": {
|
|
884
|
+
"answers": {
|
|
885
|
+
"type": "array",
|
|
886
|
+
"items": {
|
|
887
|
+
"type": "object",
|
|
888
|
+
"properties": {
|
|
889
|
+
"node": {
|
|
890
|
+
"type": "string",
|
|
891
|
+
"minLength": 1,
|
|
892
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
893
|
+
},
|
|
894
|
+
"at_ms": {
|
|
895
|
+
"type": "integer",
|
|
896
|
+
"minimum": -9007199254740991,
|
|
897
|
+
"maximum": 9007199254740991
|
|
898
|
+
},
|
|
899
|
+
"category": {
|
|
900
|
+
"type": "string",
|
|
901
|
+
"enum": [
|
|
902
|
+
"retry",
|
|
903
|
+
"agent_question",
|
|
904
|
+
"plan_question",
|
|
905
|
+
"merge_commit",
|
|
906
|
+
"other"
|
|
907
|
+
],
|
|
908
|
+
"description": "`retry`: a failed attempt retried or stopped. `agent_question`: a design question the agent stopped to ask, given its recommended options. `plan_question`: a pipeline question such as an exhausted review budget, given the plan’s `unattended_answer`. `merge_commit`: a merge commit git refused."
|
|
909
|
+
},
|
|
910
|
+
"by": {
|
|
911
|
+
"type": "string",
|
|
912
|
+
"enum": [
|
|
913
|
+
"unattended",
|
|
914
|
+
"coordinator"
|
|
915
|
+
]
|
|
916
|
+
},
|
|
917
|
+
"answer": {
|
|
918
|
+
"anyOf": [
|
|
919
|
+
{
|
|
920
|
+
"type": "string",
|
|
921
|
+
"maxLength": 200
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
"type": "number"
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
"type": "boolean"
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
"type": "null"
|
|
931
|
+
}
|
|
932
|
+
],
|
|
933
|
+
"description": "The answer’s value: a choice, or an agent question’s picked option indices as JSON."
|
|
934
|
+
},
|
|
935
|
+
"waited_ms": {
|
|
936
|
+
"description": "How long the question stood unanswered.",
|
|
937
|
+
"type": "integer",
|
|
938
|
+
"minimum": 0,
|
|
939
|
+
"maximum": 9007199254740991
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
"required": [
|
|
943
|
+
"node",
|
|
944
|
+
"at_ms",
|
|
945
|
+
"category",
|
|
946
|
+
"by",
|
|
947
|
+
"answer"
|
|
948
|
+
],
|
|
949
|
+
"additionalProperties": false
|
|
950
|
+
},
|
|
951
|
+
"description": "Questions a `--retry-after` timer or the run coordinator answered, oldest first."
|
|
952
|
+
},
|
|
953
|
+
"coordinator_actions": {
|
|
954
|
+
"type": "array",
|
|
955
|
+
"items": {
|
|
956
|
+
"type": "object",
|
|
957
|
+
"properties": {
|
|
958
|
+
"at_ms": {
|
|
959
|
+
"type": "integer",
|
|
960
|
+
"minimum": -9007199254740991,
|
|
961
|
+
"maximum": 9007199254740991
|
|
962
|
+
},
|
|
963
|
+
"node": {
|
|
964
|
+
"type": "string",
|
|
965
|
+
"minLength": 1,
|
|
966
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
967
|
+
},
|
|
968
|
+
"action": {
|
|
969
|
+
"type": "string",
|
|
970
|
+
"enum": [
|
|
971
|
+
"add_context",
|
|
972
|
+
"redirect",
|
|
973
|
+
"pause",
|
|
974
|
+
"abort",
|
|
975
|
+
"fresh",
|
|
976
|
+
"amend",
|
|
977
|
+
"exec",
|
|
978
|
+
"gate_cache_invalidate",
|
|
979
|
+
"interrupt",
|
|
980
|
+
"reassign",
|
|
981
|
+
"retry",
|
|
982
|
+
"step"
|
|
983
|
+
]
|
|
984
|
+
},
|
|
985
|
+
"target": {
|
|
986
|
+
"description": "An amendment’s targets, an `exec` target or a gate id.",
|
|
987
|
+
"type": "string",
|
|
988
|
+
"maxLength": 500
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
"required": [
|
|
992
|
+
"at_ms",
|
|
993
|
+
"action"
|
|
994
|
+
],
|
|
995
|
+
"additionalProperties": false
|
|
996
|
+
},
|
|
997
|
+
"description": "What the coordinator did on its own, oldest first. Its reasons are in its conversation and, for steering, in the phase’s decisions — not here."
|
|
998
|
+
},
|
|
999
|
+
"reverted_deletions": {
|
|
1000
|
+
"description": "Merge resolutions that kept lines a phase deliberately deleted, oldest first: flagged, not refused. Unused code breaks no gate, so check each deletion reached the branch. Absent when there were none, and on post-mortems written before it was checked.",
|
|
1001
|
+
"type": "array",
|
|
1002
|
+
"items": {
|
|
1003
|
+
"type": "object",
|
|
1004
|
+
"properties": {
|
|
1005
|
+
"at_ms": {
|
|
1006
|
+
"type": "integer",
|
|
1007
|
+
"minimum": -9007199254740991,
|
|
1008
|
+
"maximum": 9007199254740991
|
|
1009
|
+
},
|
|
1010
|
+
"node": {
|
|
1011
|
+
"type": "string",
|
|
1012
|
+
"maxLength": 200,
|
|
1013
|
+
"description": "The phase whose deletion was reverted, or the branch standing in when no phase on that side touched the path."
|
|
1014
|
+
},
|
|
1015
|
+
"path": {
|
|
1016
|
+
"type": "string",
|
|
1017
|
+
"maxLength": 500
|
|
1018
|
+
},
|
|
1019
|
+
"lines": {
|
|
1020
|
+
"type": "integer",
|
|
1021
|
+
"minimum": 1,
|
|
1022
|
+
"maximum": 9007199254740991,
|
|
1023
|
+
"description": "Lines that side deleted and the resolution kept. A count, never the lines."
|
|
1024
|
+
},
|
|
1025
|
+
"branch": {
|
|
1026
|
+
"type": "string",
|
|
1027
|
+
"description": "The branch the merge was made on."
|
|
1028
|
+
},
|
|
1029
|
+
"fix_rounds": {
|
|
1030
|
+
"type": "integer",
|
|
1031
|
+
"minimum": 0,
|
|
1032
|
+
"maximum": 9007199254740991,
|
|
1033
|
+
"description": "Conflict-fixer rounds the resolution took; 0 is git’s own merge, the `append_only` union driver."
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
"required": [
|
|
1037
|
+
"at_ms",
|
|
1038
|
+
"node",
|
|
1039
|
+
"path",
|
|
1040
|
+
"lines",
|
|
1041
|
+
"branch",
|
|
1042
|
+
"fix_rounds"
|
|
1043
|
+
],
|
|
1044
|
+
"additionalProperties": false
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
"required": [
|
|
1049
|
+
"answers",
|
|
1050
|
+
"coordinator_actions"
|
|
1051
|
+
],
|
|
1052
|
+
"additionalProperties": false,
|
|
1053
|
+
"description": "Every decision made with nobody watching. A plan whose run decided much on its own left those choices to chance: a design question answered unattended belonged in the plan."
|
|
843
1054
|
}
|
|
844
1055
|
},
|
|
845
1056
|
"required": [
|