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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinta-ai-workflows",
3
- "version": "0.7.0-alpha34",
3
+ "version": "0.7.0-alpha36",
4
4
  "description": "Bootstrap AI tooling (AGENTS.md, skills, sub-agents) across Claude Code, Codex, Cursor, and VS Code + GitHub Copilot.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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": [