vinta-ai-workflows 0.7.0-alpha19 → 0.7.0-alpha20
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 +241 -0
- package/package.json +1 -1
- package/skills/vinta-derive-skills/SKILL.md +2 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml +5 -8
- package/skills/vinta-derive-skills/resources/plan-execution/README.md +11 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/dispatched-agent.md +31 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +2 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +3 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +3 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +3 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +1 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,7 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [0.7.0] — YYYY-MM-DD
|
|
9
9
|
|
|
10
|
+
<!-- pre-release: 0.7.0-alpha20 on 2026-09-24 -->
|
|
10
11
|
<!-- pre-release: 0.7.0-alpha19 on 2026-09-19 -->
|
|
11
12
|
<!-- pre-release: 0.7.0-alpha18 on 2026-09-17 -->
|
|
12
13
|
<!-- pre-release: 0.7.0-alpha17 on 2026-09-17 -->
|
|
@@ -29,6 +30,17 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
29
30
|
|
|
30
31
|
### Added
|
|
31
32
|
|
|
33
|
+
- **A timed-out gate says whether it hung or was slow.** A `gate_result` with
|
|
34
|
+
`status: timed_out` used to carry only the status, so a run with nine
|
|
35
|
+
timeouts could not say whether any of them was a stuck suite or a host too
|
|
36
|
+
busy to finish one. It now carries `timeout`: `quiet_ms` (how long the gate
|
|
37
|
+
had gone without writing anything when it was killed), `output_bytes`, and
|
|
38
|
+
the host's `load_1m` beside `cpus`. `quiet_ms` near the timeout is a gate
|
|
39
|
+
waiting on something; a small one with `load_1m` well above `cpus` is
|
|
40
|
+
contention. Numbers only, as before: nothing the gate printed reaches the
|
|
41
|
+
row. The monitor's brief points at the new field. Older rows have no
|
|
42
|
+
`timeout`.
|
|
43
|
+
|
|
32
44
|
- **Gate durations are now kept, not just displayed.** Every `gate_result` has
|
|
33
45
|
carried the runner's own `duration_ms` since gates were journalled, and
|
|
34
46
|
nothing read it back: the figure went to a live panel and was afterwards
|
|
@@ -564,6 +576,84 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
564
576
|
|
|
565
577
|
### Changed
|
|
566
578
|
|
|
579
|
+
- **Updated the OpenAI and Anthropic models to their latest version.**
|
|
580
|
+
|
|
581
|
+
- **Updated the generated skills to have `disable-model-invocation: true` set.**
|
|
582
|
+
|
|
583
|
+
- **An expanded panel reads in a centred column.** Full page used to mean
|
|
584
|
+
full width too, so on a large monitor a transcript or gate log ran in lines
|
|
585
|
+
too long to follow. The overlay still covers the window; its header and
|
|
586
|
+
content now sit in a centred column capped at `max-w-6xl`.
|
|
587
|
+
|
|
588
|
+
- **The node view reads like a chat.** The transcript now sits above the
|
|
589
|
+
steering box instead of below it, so the conversation comes first and the
|
|
590
|
+
box you answer it in is under its newest turn. A terminal opened by *Take
|
|
591
|
+
over* still appears under the steering box.
|
|
592
|
+
|
|
593
|
+
- **`--retry-after` backs off when attempts fail faster than it.** An
|
|
594
|
+
unanswered failure question used to answer itself on a flat interval
|
|
595
|
+
forever, so a phase whose attempts died within seconds was retried every
|
|
596
|
+
fifteen minutes for hours, against the same failure, at the price of a cold
|
|
597
|
+
lane each time. Each attempt that fails in less time than the interval now
|
|
598
|
+
doubles the next wait, up to 8× (15m becomes 2h); one attempt that runs at
|
|
599
|
+
least the interval long puts it back. It still never stops on its own: a
|
|
600
|
+
run whose cause goes away overnight is moving again before morning. The
|
|
601
|
+
`node.unattended_retry` log line reports the actual wait and the streak.
|
|
602
|
+
|
|
603
|
+
- **Notifications have an inbox, and are heard from every view.** The UI's
|
|
604
|
+
notifications used to be a row of banners in the top bar, shown only when
|
|
605
|
+
browser notifications were refused. They piled up there and could be read
|
|
606
|
+
only one at a time, by dismissing whichever was in front. Every notification
|
|
607
|
+
now lands in an inbox behind a bell with an unread count: newest first,
|
|
608
|
+
filterable by unread and by still-waiting pauses, marked read one at a time
|
|
609
|
+
or all at once, dismissed singly or cleared. A pause that gets answered stays
|
|
610
|
+
listed and says *Answered*. The inbox is kept in `localStorage` and shared by
|
|
611
|
+
every tab, so a reload no longer loses it. It stores what a notification
|
|
612
|
+
body already carries — a run id, a node id, a reason and the journal's
|
|
613
|
+
timestamp — and no question text, output or diff. Browser notifications also
|
|
614
|
+
reach the operator more often now. They used to fire only for the run open
|
|
615
|
+
on screen; the UI now follows every running run in the background, and a run
|
|
616
|
+
view takes over that run's stream instead of opening a second one. They also
|
|
617
|
+
depended on a permission prompt raised by the first pause, which Firefox and
|
|
618
|
+
Safari ignore when no click raised it, so on those browsers they could never
|
|
619
|
+
turn on. An *Enable notifications* button now sits in the top bar until the
|
|
620
|
+
browser has been asked (and in the inbox too), and confirms with one test
|
|
621
|
+
notification. It also says why the channel is off
|
|
622
|
+
when it is: blocked in the browser's site settings, or served over plain
|
|
623
|
+
HTTP on a remote `--host`, which browsers do not allow notifications from.
|
|
624
|
+
The reminder interval moved into the inbox.
|
|
625
|
+
|
|
626
|
+
- **The run view's Nodes table names a lane by what distinguishes it.**
|
|
627
|
+
Every lane in a run is named `${runId}-...`, so the column spelled them in
|
|
628
|
+
full as `2026-09-11-graphql-aggregations-mubou9ar-crew-3-tier4` on every
|
|
629
|
+
row, phase after phase — the same forty characters of prefix repeated down
|
|
630
|
+
the table, wide enough to push the whole table into a horizontal scroll for
|
|
631
|
+
the sake of a string that was identical everywhere it appeared. The cell now
|
|
632
|
+
shows the tail that actually differs (`crew-3-tier4`, `lane-2`, `integ`) and
|
|
633
|
+
keeps the full name in its `title`, because that is what the worktree
|
|
634
|
+
directory and the branch are called. The phase description beside the node
|
|
635
|
+
id is bounded and wraps for the same reason — table cells are
|
|
636
|
+
`whitespace-nowrap`, so one long phase name was setting the width of the
|
|
637
|
+
whole table.
|
|
638
|
+
|
|
639
|
+
- **A dispatched phase agent does the work itself, instead of spawning an
|
|
640
|
+
implementer under it.** claude-code sessions run by the orchestrator were
|
|
641
|
+
handing their phase to a sub-agent and reporting its summary back, and the
|
|
642
|
+
pull was not laziness — `implement-phase` ships into the same repositories,
|
|
643
|
+
its description matches "you are implementing P3 of plan X", and its content
|
|
644
|
+
is "spawn exactly one implementer subagent". So a session was loading a
|
|
645
|
+
conductor skill on its own and correctly following it, one level below the
|
|
646
|
+
conductor that had already spawned it. The cost is the whole point of session
|
|
647
|
+
reuse: a sub-agent's reading of the codebase ends with the sub-agent, so the
|
|
648
|
+
warm session that every cross-phase turn greets with "everything you learned
|
|
649
|
+
still holds" was left holding a paragraph, and each phase paid a cold agent's
|
|
650
|
+
first turn again. Both halves are now closed. Every `vinta-ai-maestro` role
|
|
651
|
+
prompt (implementer, reviewer, fixer, chore — cold and continued) carries a
|
|
652
|
+
no-delegation section that names those skills and says what delegating costs,
|
|
653
|
+
and the plan-execution unit grows a `dispatched-agent` partial: a guard at the
|
|
654
|
+
top of all four conductors refusing entry to an agent that was itself handed
|
|
655
|
+
one phase, plus the same rule inside every composed implementer prompt.
|
|
656
|
+
|
|
567
657
|
- **The node view's gate panel is a list of verdicts you can open, not a stack
|
|
568
658
|
of scrolling boxes.** Every gate used to render its whole log at once, each
|
|
569
659
|
in its own fixed-height box with its own scrollbar — so three gates in a
|
|
@@ -736,6 +826,157 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
736
826
|
|
|
737
827
|
### Fixed
|
|
738
828
|
|
|
829
|
+
- **A failed Claude Code turn no longer reads as `claude-code result:
|
|
830
|
+
success`.** When the CLI ends a turn with `is_error: true` beside
|
|
831
|
+
`subtype: "success"` (how a plan limit arrives, for one), the recorded
|
|
832
|
+
error printed the subtype and said "success" — 189 times in one observed
|
|
833
|
+
run. It now reads `claude-code result: is_error`. Other subtypes
|
|
834
|
+
(`error_max_turns`, …) are unchanged.
|
|
835
|
+
|
|
836
|
+
- **The monitor's conversation was mostly unformatted JSON.** Not its answers —
|
|
837
|
+
its *watchdog's*. An intervention turn must reply with one document matching
|
|
838
|
+
`intervention.v1.schema.json`, which is what makes the monitor's authority
|
|
839
|
+
over a live run a closed set of verbs rather than an editor over
|
|
840
|
+
`workflow.json`; and that turn is journalled into the conversation a person
|
|
841
|
+
reads, because a run that retuned itself should say so where somebody is
|
|
842
|
+
already looking. Both of those are right. Rendering the document verbatim was
|
|
843
|
+
not, and the summary paragraph the operator wanted arrived a thousand
|
|
844
|
+
characters into one unwrapped line of braces. A proposal now renders as what
|
|
845
|
+
it says: the summary, then one line per proposed change with its evidence
|
|
846
|
+
under it, and a tone dot only when something was actually proposed — a turn
|
|
847
|
+
that looked and changed nothing is the expected outcome and should not shout.
|
|
848
|
+
The verb table is keyed on the daemon's own union, so a fifth verb fails the
|
|
849
|
+
UI build rather than shipping as a bare identifier. The journal still holds
|
|
850
|
+
the bytes the model wrote — only the row changed, so conversations already on
|
|
851
|
+
disk read as well as new ones. Any other whole-JSON answer is at least
|
|
852
|
+
indented.
|
|
853
|
+
|
|
854
|
+
- **The monitor's conversation opened at its oldest entry and never scrolled.**
|
|
855
|
+
It followed the newest row *only while a turn was running*, on the reasoning
|
|
856
|
+
that the list is short and the operator is watching the answer they just
|
|
857
|
+
asked for. Neither half held: the conversation is the journal's, so it holds
|
|
858
|
+
every question ever asked about the run plus every proposal the run's own
|
|
859
|
+
watchdog made with nobody asking anything — a hundred entries is ordinary.
|
|
860
|
+
Opening the panel therefore put the reader at the oldest of them. It now
|
|
861
|
+
follows the way a phase's transcript does: stuck to the newest row, letting
|
|
862
|
+
go the moment the reader scrolls up to read, with a "Jump to latest" as the
|
|
863
|
+
way back. That rule is one module now rather than a well-tested copy in the
|
|
864
|
+
transcript and none in the monitor, which is how the panel came to be missing
|
|
865
|
+
three of its parts — including the one that survives a panel being expanded
|
|
866
|
+
to full page, where the portal hands the view a brand new list scrolled to
|
|
867
|
+
the top.
|
|
868
|
+
|
|
869
|
+
- **Hitting the plan's session or weekly limit failed the node instead of
|
|
870
|
+
waiting for the window to end.** `claude-code`'s refusal table recognised
|
|
871
|
+
"usage limit reached" and nothing else, and the classifier's default is
|
|
872
|
+
`fatal` — correctly, since an unrecognised failure must not become an
|
|
873
|
+
unbounded wait. But the CLI announces a plan window in its own words:
|
|
874
|
+
"You've hit your session limit", "Session limit reached · resets 3am",
|
|
875
|
+
"5-hour limit reached", "You've reached your weekly limit". None of those
|
|
876
|
+
contain "usage limit", so every one of them fell past the table to `fatal`,
|
|
877
|
+
which fails the node and blocks its whole dependent subtree — for a window
|
|
878
|
+
that ends by itself in a few hours. The machinery to do the right thing was
|
|
879
|
+
already there and simply never reached: a `quota` refusal parks the harness,
|
|
880
|
+
not the node, so every later node joins one wait instead of discovering the
|
|
881
|
+
limit for itself; the wake time is journalled and survives a daemon restart;
|
|
882
|
+
and the AIMD ceiling is left alone, because a plan window says nothing about
|
|
883
|
+
how many agents may run at once. Those wordings are now `quota`. The new row
|
|
884
|
+
sits *below* `concurrency` and `rate_limit` so that "concurrent session limit
|
|
885
|
+
reached" keeps the kind that halves the ceiling, and "approaching session
|
|
886
|
+
limit" — a warning from a harness that is still working — matches nothing.
|
|
887
|
+
|
|
888
|
+
- **A window that closed *under* a running turn was an ordinary turn failure.**
|
|
889
|
+
§6.1 is written about a spawn, because that is where a vendor usually says
|
|
890
|
+
"not right now" — and everything that follows from it, parking the harness so
|
|
891
|
+
every later node joins one wait, journalling the wake time so a daemon
|
|
892
|
+
restart does not forget it, leaving the AIMD ceiling alone for a window that
|
|
893
|
+
says nothing about concurrency, hung off `admit`. So the other arrival of the
|
|
894
|
+
same fact had none of it: a plan window that closes while an agent is twenty
|
|
895
|
+
minutes into a phase ends the turn, and the node then burnt its retries
|
|
896
|
+
against a closed window and failed, blocking its whole dependent subtree —
|
|
897
|
+
the exact outcome §6.1 exists to prevent, reached by the one path that did not
|
|
898
|
+
go through admission control. A session now reports a `TurnRefusal`, and the
|
|
899
|
+
caller answers it with the `CapacityRetry` a refused spawn raises: the lane
|
|
900
|
+
goes back, the harness parks until the stated reset, and the node re-drives
|
|
901
|
+
when the window opens.
|
|
902
|
+
|
|
903
|
+
Each adapter reads it where its own failure prose is, which is not where its
|
|
904
|
+
events are: `claude-code`'s terminal `result` frame carries the sentence in
|
|
905
|
+
the `result` string that `mapResult` deliberately drops (it is agent output,
|
|
906
|
+
§11), `codex` states a failure as prose on three different frames, and
|
|
907
|
+
`opencode`'s `session.error` carries it under `data`, which `#errorName`
|
|
908
|
+
refuses to put in an event. All three read that prose, match it against the
|
|
909
|
+
table they already had, and drop it — what reaches the caller is a kind, a
|
|
910
|
+
fixed reason token and a reset time, and what reaches the transcript is one
|
|
911
|
+
token-only line so the record says why the turn stopped. The stderr-and-exit
|
|
912
|
+
shape of the same event is read too, from the diagnostics buffer that is the
|
|
913
|
+
only place that sentence exists.
|
|
914
|
+
|
|
915
|
+
The re-drive is a full one, and that is deliberate: the session the turn was
|
|
916
|
+
using is gone, and resuming from a memory of files a recycle has removed is
|
|
917
|
+
worse than redoing the work. Nothing is destroyed by it — `recycle` sets
|
|
918
|
+
aside whatever the turn left uncommitted as a WIP ref first. What it costs is
|
|
919
|
+
the turn. What it used to cost was the node.
|
|
920
|
+
|
|
921
|
+
- **A stated reset time was only read when it was an hour on the same day.**
|
|
922
|
+
`parseRetryAfter` prefers a reset the vendor reported over a guessed backoff,
|
|
923
|
+
and its wall-clock pattern wanted `am`/`pm` immediately after "reset", so
|
|
924
|
+
"resets at 15:00", "resets tomorrow at 2am" and the weekly window's "resets
|
|
925
|
+
on Monday at 12am" all reported nothing — falling back to the five-minute
|
|
926
|
+
re-probe interval, which across a week-long window is two thousand spawns to
|
|
927
|
+
discover what the first sentence said. It now reads 24-hour times, `today`,
|
|
928
|
+
`tomorrow` and a named weekday (the next occurrence of it, which is today
|
|
929
|
+
only while the hour is still ahead). A reset named by date, "resets Nov 3 at
|
|
930
|
+
9am", still reports nothing on purpose: a month with no year is a guess, and
|
|
931
|
+
the re-probe is a better answer than a wait that is wrong by a year. So are
|
|
932
|
+
"reset 3", "resets at 25:00" and "resets at 13pm" — an hour with neither
|
|
933
|
+
minutes nor a meridiem is as likely to be a version or a retry count, and
|
|
934
|
+
everything this function returns becomes a wait.
|
|
935
|
+
|
|
936
|
+
- **`run --resume` could not provision a pool at all on a project that
|
|
937
|
+
disables hooks.** `#configureHooks` enables `extensions.worktreeConfig`
|
|
938
|
+
before it can set `core.hooksPath --worktree`, and that first key is
|
|
939
|
+
repository-wide: the write lands in the shared `.git/config`, which git takes
|
|
940
|
+
`.git/config.lock` to edit. Called once per lane out of the provisioning
|
|
941
|
+
`Promise.all`, the lanes collided on that lock — `could not lock config file
|
|
942
|
+
…: File exists`. A fresh provision never showed it, because `worktree add`
|
|
943
|
+
goes through the pool's serialized git turn and staggered the lanes apart by
|
|
944
|
+
the time they reached the shared write; `adopt` skips `worktree add`, so every
|
|
945
|
+
lane arrived in the same tick and **every** resume of a `hooks: false` project
|
|
946
|
+
failed, reproducibly, before a single phase was scheduled. The extension now
|
|
947
|
+
goes through the same serialized turn. The `--worktree` write stays parallel:
|
|
948
|
+
it lands in `.git/worktrees/<name>/config.worktree`, one file per lane.
|
|
949
|
+
|
|
950
|
+
- **A provision failure that was not one of three enumerated kinds said nothing
|
|
951
|
+
about itself.** `refusal` detailed `DiskProbeError`, `LaneSetupError` and
|
|
952
|
+
`LaneEnvFileError`; everything else — including the lock collision above and
|
|
953
|
+
`LaneAdoptError`, the one error a resume is most likely to hit — collapsed to
|
|
954
|
+
"could not provision the lane pool under …", and the daemon log recorded that
|
|
955
|
+
same sentence as `run.provision_failed`'s `reason` with no `error` field and
|
|
956
|
+
no stack. So the run that could not start left behind no record of why, in
|
|
957
|
+
either place an operator looks. The exclusion was a §11 reading
|
|
958
|
+
`LaneSetupError` had already revised for this exact reason: §11 keeps
|
|
959
|
+
repository *contents* out of the record — diffs, file bodies, gate output —
|
|
960
|
+
and an error's own complaint about a lock file or a ref is not that. An
|
|
961
|
+
unenumerated error's message is now carried, capped at 500 characters and run
|
|
962
|
+
through the log's redaction set; `LaneAdoptError` speaks for itself like the
|
|
963
|
+
other lane errors; and the failure is logged with its kind and stack frames.
|
|
964
|
+
|
|
965
|
+
- **`doctor` reported the resumed run's own lanes as leftovers blocking it, and
|
|
966
|
+
offered to destroy them.** The held-branch check fails a run whose phase
|
|
967
|
+
branches are checked out in another worktree, which is right for a fresh run
|
|
968
|
+
and exactly wrong for a resume: the integrator puts a lane on
|
|
969
|
+
`plan/<id>/phase-<n>` for the duration of a phase, so a run killed mid-phase
|
|
970
|
+
— the kill a resume is *for* — leaves its own lanes holding precisely those
|
|
971
|
+
branches. Every `run --resume` therefore refused preflight with one failure
|
|
972
|
+
per phase in flight, each remedied by `git worktree remove --force <lane>`,
|
|
973
|
+
which deletes the uncommitted work the resume existed to adopt. The check now
|
|
974
|
+
takes the run being resumed and excludes lanes belonging to it, identified by
|
|
975
|
+
reading the lane root and asking each worktree which branch it holds — never
|
|
976
|
+
by comparing git's printed paths with this process's, the platform trap
|
|
977
|
+
`reap` and `LanePool` both document. `doctor` takes `--resume <run-id>` to ask
|
|
978
|
+
the same question before a resume, and a lane of *another* run still fails.
|
|
979
|
+
|
|
739
980
|
- **`vinta-ai-maestro gate` gave up on the slowest gate after five minutes and
|
|
740
981
|
called it a daemon it could not reach.** The command awaited one `fetch`, on
|
|
741
982
|
the stated reasoning that a gate is slow for reasons the client cannot shorten
|
package/package.json
CHANGED
|
@@ -138,6 +138,8 @@ Shells under `plan-execution/shell/` are thin — most content lives in `plan-ex
|
|
|
138
138
|
|
|
139
139
|
**The parallel-lanes seam.** `partials/parallel-lanes.md` carries the DAG scheduler: the graph parse (`DAG_PARSE`), the worktree pool (`LANE_WORKTREE_POOL`), the dependency-derived branch topology (`LANE_TOPOLOGY`), the dispatch loop (`LANE_SCHEDULER`), the tracking-directory schema (`TRACKING_DIR`), and the sibling-lane write guard (`SIBLING_LANE_ISOLATION`, included by `worktree-seam.md#STRAY_WRITE_CHECK`). All six blocks are project-agnostic and carry no new `{{...}}` placeholders of their own beyond ones the shells already substitute (`{{PLAN_DIR}}`, `{{BUILD_CMD}}`, `{{E2E_RUN_OPTION_TRACKING}}`, `{{TRACKING_BRANCH_FIELD}}`, `{{TRACKING_PHASE_BRANCH_FIELD}}`).
|
|
140
140
|
|
|
141
|
+
**The dispatched-agent seam.** `partials/dispatched-agent.md` carries the one rule that spans both directions of the unit: the agent doing a phase never dispatches. `CONDUCTOR_ENTRY_GUARD` goes near the top of all four conductors (`implement-plan`, `implement-phase`, `review-phase`, `amend-plan`) and stops an agent that was itself handed one phase — by another conductor, or by [vinta-ai-maestro](https://github.com/vintasoftware/vinta-ai-workflows/tree/main/packages/vinta-ai-maestro) — from re-entering the orchestrator one level down. `NO_NESTED_DISPATCH` is prompt text, spliced into `implementer-prompt.md#INNER_OUTER_LOOP`, so every composed implementer prompt (forward and amend) carries it. Both blocks are project-agnostic and take no `{{...}}` placeholders.
|
|
142
|
+
|
|
141
143
|
**When `foundation_skills.prepare-worktree` is `disabled`**, parallel execution is impossible (several agents cannot share one working tree). Render the plan-execution unit sequential-only: strip the Step 0 (e) question and the `{If run_options.parallel_phases = true:}` gate from the implementer prompt, and keep the `parallel-lanes` blocks — the scheduler with `max_parallel_lanes = 1` **is** the sequential path, and the DAG still orders the phases correctly. Only the pool provisioning collapses to the single-worktree branch of `WORKROOT_RESOLUTION`.
|
|
142
144
|
|
|
143
145
|
**`ask` → two `integrate-phase` variants, no dual-render.** When `policies.commit_strategy = ask`, render `integrate-phase-template.md` **twice**:
|
package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml
CHANGED
|
@@ -37,7 +37,7 @@ tiers:
|
|
|
37
37
|
anthropic:
|
|
38
38
|
- id: claude-haiku-4-5
|
|
39
39
|
openai:
|
|
40
|
-
- id: gpt-
|
|
40
|
+
- id: gpt-6-luna
|
|
41
41
|
note: optimized for cost-sensitive workloads
|
|
42
42
|
google:
|
|
43
43
|
- id: gemini-3.1-flash-lite
|
|
@@ -50,11 +50,10 @@ tiers:
|
|
|
50
50
|
fixtures, simple HStore/ArrayField additions.
|
|
51
51
|
models:
|
|
52
52
|
anthropic:
|
|
53
|
-
- id: claude-haiku-4-5
|
|
54
53
|
- id: claude-sonnet-5
|
|
55
54
|
note: step-up target when phase touches >3 files
|
|
56
55
|
openai:
|
|
57
|
-
- id: gpt-
|
|
56
|
+
- id: gpt-6-luna
|
|
58
57
|
google:
|
|
59
58
|
- id: gemini-3.8-flash
|
|
60
59
|
|
|
@@ -67,9 +66,9 @@ tiers:
|
|
|
67
66
|
covering concurrency edges.
|
|
68
67
|
models:
|
|
69
68
|
anthropic:
|
|
70
|
-
- id: claude-
|
|
69
|
+
- id: claude-opus-5-5
|
|
71
70
|
openai:
|
|
72
|
-
- id: gpt-
|
|
71
|
+
- id: gpt-6-sol
|
|
73
72
|
google:
|
|
74
73
|
- id: gemini-3-pro
|
|
75
74
|
note: stronger code recall
|
|
@@ -82,12 +81,10 @@ tiers:
|
|
|
82
81
|
tuning slow query against partitioned hot table, debugging heisenbug.
|
|
83
82
|
models:
|
|
84
83
|
anthropic:
|
|
85
|
-
- id: claude-
|
|
84
|
+
- id: claude-fable-5-1
|
|
86
85
|
openai:
|
|
87
86
|
- id: gpt-6-astra
|
|
88
87
|
note: frontier model; roughly twice sol's price, so reserve it for tier 4
|
|
89
|
-
- id: gpt-5.6-sol
|
|
90
|
-
note: cheaper frontier option when astra's step up is not warranted
|
|
91
88
|
google:
|
|
92
89
|
- id: gemini-3-pro
|
|
93
90
|
note: with thinking
|
|
@@ -41,6 +41,17 @@ The conductor resolves three values **once per lane** (see `partials/worktree-se
|
|
|
41
41
|
|
|
42
42
|
Every `git` / lint / test / build call in every sub-skill uses `git -C <WORKROOT>` **uniformly** — no `if use_worktree` inside them. Only two genuine conditionals remain, each local and data-driven: the `SANDBOX_TIER`-gated spawn wrap in `implement-phase`, and the `WORKROOT != main_checkout`-gated stray-write check in `review-phase`.
|
|
43
43
|
|
|
44
|
+
## The dispatched-agent seam (`partials/dispatched-agent.md`)
|
|
45
|
+
|
|
46
|
+
One rule, two shapes, because it has to hold at both ends of the unit: **the agent doing a phase never dispatches.**
|
|
47
|
+
|
|
48
|
+
| Block | Consumed by | What it owns |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `CONDUCTOR_ENTRY_GUARD` | `implement-plan`, `implement-phase`, `review-phase`, `amend-plan` | Refuses entry to an agent that was itself handed one phase — by another conductor, or by an external orchestrator such as [vinta-ai-maestro](https://github.com/vintasoftware/vinta-ai-workflows/tree/main/packages/vinta-ai-maestro). The conductor is already running; it is what spawned the reader. |
|
|
51
|
+
| `NO_NESTED_DISPATCH` | `implementer-prompt.md#INNER_OUTER_LOOP` → the composed prompt in `implement-phase` and `amend-plan` 4b | Tells the spawned implementer to do the work in its own session. |
|
|
52
|
+
|
|
53
|
+
The cost this prevents is not duplicated orchestration — it is lost context. A phase agent is reused (review findings, a chore over its own diff, often the next phase), and that reuse only buys anything because the session that read the codebase is the session that gets the next turn. A sub-agent's reading dies with the sub-agent, leaving the parent holding a summary and every later turn starting cold.
|
|
54
|
+
|
|
44
55
|
## The parallel-lanes seam (`partials/parallel-lanes.md`)
|
|
45
56
|
|
|
46
57
|
The plan gives every phase a `**Depends on**:` line. The conductor turns those into a DAG and dispatches a phase the moment its dependencies are green and a lane is free. Six blocks:
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!-- Partial: dispatched-agent — the two halves of "the agent doing one phase does not dispatch". CONDUCTOR_ENTRY_GUARD stops a dispatched phase agent from re-entering a conductor skill one level down (implement-plan / implement-phase / review-phase / amend-plan). NO_NESTED_DISPATCH is prompt text: it goes inside the composed implementer prompt, via implementer-prompt.md#INNER_OUTER_LOOP, so both implement-phase's prompt and amend-plan's 4b rewrite prompt carry it. Project-agnostic — no `{{...}}` placeholders of its own. -->
|
|
2
|
+
|
|
3
|
+
<!-- block-begin: CONDUCTOR_ENTRY_GUARD -->
|
|
4
|
+
## Not for an agent that was handed one phase
|
|
5
|
+
|
|
6
|
+
This skill **orchestrates**: it composes prompts, picks models and spawns other agents. Run it only when you are the session a user or a scheduler invoked to drive a plan.
|
|
7
|
+
|
|
8
|
+
If you are reading it because something handed you a single phase — a prompt naming your phase id, a branch already cut for you, a worktree you were told to stay inside, or an orchestrator such as [vinta-ai-maestro](https://github.com/vintasoftware/vinta-ai-workflows/tree/main/packages/vinta-ai-maestro) that spawned you — then the conductor this skill describes **is already running, and it is what spawned you**. Do not start a second one underneath it. Do the work in your own session, report back the way your prompt asked, and take from here only what it says about this repository's conventions, gates and commit rules.
|
|
9
|
+
|
|
10
|
+
The duplication is the smaller cost. A dispatched agent is deliberately reused — the same session takes the review findings, the chore over its own diff, and often the next phase — and that reuse is worth something only because the session that read the codebase is the session that gets the next turn. Hand your phase to a sub-agent and its reading of the code dies with it: you are left holding a summary, and every turn after yours starts cold.
|
|
11
|
+
<!-- block-end: CONDUCTOR_ENTRY_GUARD -->
|
|
12
|
+
|
|
13
|
+
<!-- block-begin: NO_NESTED_DISPATCH -->
|
|
14
|
+
## Do this work yourself
|
|
15
|
+
|
|
16
|
+
You are the agent that implements this phase, not an orchestrator for one. Do not spawn,
|
|
17
|
+
dispatch or delegate to a sub-agent (claude-code's Task/Agent tool, or whatever your
|
|
18
|
+
runtime calls the same thing) for any part of it: not the implementation, not a search of
|
|
19
|
+
the codebase, not a second opinion on your own output. Read, run and write yourself.
|
|
20
|
+
|
|
21
|
+
The orchestrator reuses this session — for the review findings, for a chore over your own
|
|
22
|
+
diff, often for the next phase — precisely because by then you know where this codebase
|
|
23
|
+
keeps things and how its suite is run. A sub-agent's reading of the code ends when the
|
|
24
|
+
sub-agent does, so a delegated phase leaves you holding its summary and nothing else, and
|
|
25
|
+
every turn after this one starts cold.
|
|
26
|
+
|
|
27
|
+
A project skill that tells you to spawn an implementer, reviewer or fixer —
|
|
28
|
+
`implement-plan`, `implement-phase`, `review-phase`, `amend-plan`, anything shaped like
|
|
29
|
+
them — is written for the orchestrator that dispatched you, not for you. Take what it says
|
|
30
|
+
about conventions, gates and commit rules; never follow its spawn steps.
|
|
31
|
+
<!-- block-end: NO_NESTED_DISPATCH -->
|
|
@@ -85,4 +85,6 @@ Project skills available: {{PROJECT_SKILLS_LIST}}
|
|
|
85
85
|
{If run_options.full_test_suite = true:} run the **full test suite** `{{TEST_CMD}}` instead of the scoped suite — this phase guards against regressions in untouched code too.
|
|
86
86
|
{{E2E_OUTER_GATE_LINE}}
|
|
87
87
|
6. Outer gate fails → return step 2 (fix regression), re-run inner loop, then 5a/5b/5c. **Never** commit, push, or proceed while any gate is red.
|
|
88
|
+
|
|
89
|
+
<!-- include: partials/dispatched-agent.md#NO_NESTED_DISPATCH -->
|
|
88
90
|
<!-- block-end: INNER_OUTER_LOOP -->
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amend-plan
|
|
3
3
|
description: Adjust an existing implementation plan in `{{PLAN_DIR}}/` after implementation has started or finished. Updates the plan file (revising existing phases or appending new ones), then for each affected phase that was already implemented adjusts its commits (`git commit --amend` or new commits) on the phase branch, force-pushes the rewritten branch, rebases every phase branch in the rewritten phase's dependency closure, force-pushes each, and refreshes the PR-context files. Use when the user says "amend the plan", "update phase N", "add a phase to plan X", "the spec changed, fix the plan", or "rewrite the implementation for phase N". NOT for one-off changes to a single file unrelated to a plan; use the regular implement skill for that. {{PR_POLICY_DESCRIPTION}}
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Amend Plan
|
|
@@ -9,6 +10,8 @@ Revise a plan in [`{{PLAN_DIR}}/`]({{PLAN_DIR}}/) after work has begun. Companio
|
|
|
9
10
|
|
|
10
11
|
The flow is destructive (force-push). Every modification is gated on user confirmation. Default disposition for any ambiguous case is "stop and ask" — never force-push without an explicit per-branch `Confirm` from the user.{{COMMIT_STRATEGY_REFUSAL_BLOCK}}
|
|
11
12
|
|
|
13
|
+
<!-- include: partials/dispatched-agent.md#CONDUCTOR_ENTRY_GUARD -->
|
|
14
|
+
|
|
12
15
|
## Working assumptions
|
|
13
16
|
|
|
14
17
|
- Repo: {{PROJECT_NAME}} ({{STACK_SUMMARY}}). Conventions: [AGENTS.md](../../../AGENTS.md).
|
package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: implement-phase
|
|
3
3
|
description: Internal execution step of [implement-plan] / [amend-plan] — NOT a standalone entry point. Given one already-classified plan phase plus the resolved `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER`, it composes a token-efficient implementer prompt, picks the model from the phase's own suggestion, and spawns exactly one implementer subagent, returning that agent's report. Do not invoke directly for ad-hoc edits or in response to a user's raw feature request; the conductor invokes it once per executable phase in {{PROJECT_NAME}}.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Implement one phase
|
|
7
8
|
|
|
8
9
|
Execution unit invoked by [implement-plan](../implement-plan/SKILL.md) (and by [amend-plan](../amend-plan/SKILL.md) for `amend-existing` rewrites). One phase in → one implementer report out. This skill does **not** review, branch, push, or open PRs — those are [review-phase](../review-phase/SKILL.md) and [integrate-phase](../integrate-phase/SKILL.md). It also does **not** decide whether a phase runs — the conductor already filtered cross-repo / flag-removal phases.
|
|
9
10
|
|
|
11
|
+
<!-- include: partials/dispatched-agent.md#CONDUCTOR_ENTRY_GUARD -->
|
|
12
|
+
|
|
10
13
|
## Inputs (passed by the conductor as data — this skill re-derives none of them)
|
|
11
14
|
|
|
12
15
|
- `phase` record: `{ id, title, goal, body, spec_use_case, depends_on, wave, base_branch, crew_member, crew_tier, suggested_model_tier, reusable_skills, has_e2e, acceptance }`. `crew_member` / `crew_tier` come from the phase's `**Assigned to**:` line and the plan's **Crew** table; `suggested_model_tier` is the legacy path and is set only on a plan with no roster. Exactly one of the two is populated.
|
package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: implement-plan
|
|
3
3
|
description: Execute a phased implementation plan from `{{PLAN_DIR}}/` in {{PROJECT_NAME}} by orchestrating one subagent per phase (using whatever model the plan suggests and the runtime supports), running independent phases concurrently in their own worktree lanes when the plan's dependency graph allows it, pushing one branch per phase to {{CODE_HOST}}, and tracking progress. Use when the user says "implement the plan", "execute plan X", "start implementation", "run phase N of plan Y", "implement {feature} plan", or asks to drive a `*_IMPLEMENTATION_PLAN.md` file phase-by-phase. NOT for one-off changes, single-file edits, or work that doesn't have an existing plan. {{PR_POLICY_DESCRIPTION}}
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Implement Plan
|
|
@@ -17,6 +18,8 @@ The conductor itself owns only: plan parsing, the dependency graph, phase classi
|
|
|
17
18
|
|
|
18
19
|
Execution counterpart to [plan-feature](../plan-feature/SKILL.md). Plan = contract; this skill = build pipeline.
|
|
19
20
|
|
|
21
|
+
<!-- include: partials/dispatched-agent.md#CONDUCTOR_ENTRY_GUARD -->
|
|
22
|
+
|
|
20
23
|
## Working assumptions
|
|
21
24
|
|
|
22
25
|
- Repo: {{PROJECT_NAME}} ({{STACK_SUMMARY}}). Conventions: [AGENTS.md](../../../AGENTS.md).
|
package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: {{INTEGRATE_PHASE_NAME}}
|
|
3
3
|
description: Internal integration step of [implement-plan] — NOT a standalone entry point. Pushes one reviewed phase along {{PROJECT_NAME}}'s commit strategy and opens (or updates) its PR through the prs-context file + bundled open-pr.sh — the only PR-creation path. The conductor passes the resolved `WORKROOT` / `BASE_BRANCH` and the PR / inline-comment policy; do not invoke directly to push arbitrary work.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Integrate one phase
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-phase
|
|
3
3
|
description: Internal review gate of [implement-plan] / [amend-plan] / [systematic-debugging] — NOT a standalone entry point. Runs the mandatory three-layer review (mechanical checks, plan-compliance walkthrough, independent reviewer subagent) plus the fix loop against one phase's diff in {{PROJECT_NAME}}, spawning an independent reviewer, sending findings back to the phase's own implementer to fix, and looping until all three layers are clean. The invoking conductor passes the diff, the phase body to walk against, and the resolved `WORKROOT`; do not invoke directly to "review my code" — use the project's standard code-review path for that.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Review one phase
|
|
7
8
|
|
|
8
9
|
The single review implementation shared by every plan-execution conductor: [implement-plan](../implement-plan/SKILL.md) (after an implementer runs), [amend-plan](../amend-plan/SKILL.md) (after a rewrite), and [systematic-debugging](../systematic-debugging/SKILL.md) (against the fix diff). Read-only orchestration: this skill **never edits code** — every issue becomes a fix-up subagent task.
|
|
9
10
|
|
|
11
|
+
<!-- include: partials/dispatched-agent.md#CONDUCTOR_ENTRY_GUARD -->
|
|
12
|
+
|
|
10
13
|
## Inputs (passed by the conductor)
|
|
11
14
|
|
|
12
15
|
- The phase diff (on the current branch inside `WORKROOT`).
|