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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinta-ai-workflows",
3
- "version": "0.7.0-alpha19",
3
+ "version": "0.7.0-alpha20",
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": {
@@ -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**:
@@ -37,7 +37,7 @@ tiers:
37
37
  anthropic:
38
38
  - id: claude-haiku-4-5
39
39
  openai:
40
- - id: gpt-5.6-luna
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-5-mini
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-sonnet-5
69
+ - id: claude-opus-5-5
71
70
  openai:
72
- - id: gpt-5.6-terra
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-opus-5
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).
@@ -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.
@@ -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).
@@ -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`).