vinta-ai-workflows 0.6.1 → 0.7.0-alpha2

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.
Files changed (25) hide show
  1. package/CHANGELOG.md +288 -0
  2. package/README.md +21 -13
  3. package/package.json +2 -1
  4. package/schemas/README.md +5 -1
  5. package/schemas/postmortem.v1.schema.json +339 -0
  6. package/schemas/vinta-ai-workflows-config.v1.schema.json +13 -1
  7. package/schemas/workflow.v1.schema.json +833 -0
  8. package/skills/vinta-bootstrap-ai-tools/SKILL.md +9 -1
  9. package/skills/vinta-derive-skills/SKILL.md +6 -2
  10. package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +622 -32
  11. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +24 -2
  12. package/skills/vinta-derive-skills/resources/plan-execution/README.md +21 -4
  13. package/skills/vinta-derive-skills/resources/plan-execution/partials/agent-models.md +30 -3
  14. package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/modular.md +27 -14
  15. package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +12 -18
  16. package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +19 -9
  17. package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +25 -9
  18. package/skills/vinta-derive-skills/resources/plan-execution/partials/parallel-lanes.md +240 -0
  19. package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +56 -5
  20. package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +13 -6
  21. package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +17 -12
  22. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +5 -3
  23. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +94 -57
  24. package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +2 -1
  25. package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +16 -5
@@ -0,0 +1,240 @@
1
+ <!-- Partial: parallel-lanes — the DAG scheduler seam. The plan declares `**Depends on**:` per phase; the conductor builds the graph, assigns each ready phase to a lane (one worktree per lane, pooled), and dispatches lanes concurrently. Blocks: DAG_PARSE (conductor Step 0), LANE_WORKTREE_POOL (conductor Step 0.5), LANE_TOPOLOGY (conductor + integrate-phase), LANE_SCHEDULER (conductor Step 1 — dispatch + the crew claim), TRACKING_DIR (conductor), SIBLING_LANE_ISOLATION (implement-phase prompt + review-phase). Sequential runs use the SAME code paths with `max_parallel_lanes = 1` — there is no separate sequential topology. -->
2
+
3
+ <!-- block-begin: DAG_PARSE -->
4
+ ## Build the phase dependency graph
5
+
6
+ The plan's **Phased Rollout** section opens with an **Execution graph** table and gives every phase a `**Depends on**:` line ([plan-feature](../plan-feature/SKILL.md) authors both). Parse them into a DAG once, alongside the rest of the plan:
7
+
8
+ 1. **Read each phase's `**Depends on**:` line** into `phase.depends_on` — a list of phase ids, or `[]` for `nothing`. Ignore the prose after the em dash; it explains *why* the edge exists and is passed through to the implementer prompt, not to the scheduler.
9
+ 2. **Compute `phase.wave`** — longest path from a root: `wave(P) = 1` when `depends_on` is empty, else `1 + max(wave(d) for d in P.depends_on)`. Waves are **derived, never read from the plan**; a plan whose **Execution graph** table disagrees with the computed waves is a plan bug — surface the mismatch to the user before starting and go with the computed values.
10
+ 3. **Validate the graph.** Each failure below stops the run before any phase is dispatched:
11
+ - **Unknown id** in a `**Depends on**:` line → name the phase and the bad id; ask the user to fix the plan.
12
+ - **Cycle** → print the cycle (`Phase 3 → Phase 5 → Phase 3`); ask the user to fix the plan.
13
+ - **Missing line** on any executable phase → do **not** guess. Ask the user whether that phase depends on everything before it (safe, serializing) or nothing (parallel) — then write the answer back into the plan file before starting, so a resume reads the same graph.
14
+ - **Dependency on a deferred phase** (cross-repo or flag-removal, which this skill never executes) → that dependent is also deferred, transitively. Report both.
15
+ 4. **Warn on file overlap.** For every pair of phases in the same wave, intersect their **Touch List** entries. A non-empty intersection means two lanes will edit the same file concurrently and the wave merge will conflict. Surface the pairs and the shared paths, then ask: `Serialize them (add a dependency edge and re-derive waves)`, `Run anyway — I'll take the merge conflict`, `Stop — let me fix the plan`. Default to serializing. This is a warning, not a hard stop: two phases legitimately touching one `__init__.py` merge fine, two rewriting the same use case do not.
16
+ 5. **Read the plan's staffing, and check it against the graph you just computed.** The **Crew** table and the phases' `**Assigned to**:` lines are one decision in two places, and the arithmetic they claim is checkable before anything is dispatched:
17
+ - **An `**Assigned to**:` naming an agent the table does not list** → stop and ask. Don't invent a member: the roster is the plan's answer to "how many agents does this feature need", and adding one changes it.
18
+ - **A phase with no `**Assigned to**:` line in a plan that has a Crew table** → stop and ask, for the same reason. Half a roster means two staffing rules running at once.
19
+ - **A declared member assigned no phase** → stop and ask. That is an agent the plan budgeted for and never uses.
20
+ - **A phase assigned to a reviewer, or a reviewer that also takes phases** → stop and ask. The two roles are disjoint precisely so that an agent reviewing its own work is unrepresentable.
21
+ - **A reviewer below every phase on the plan** → stop and ask. A reviewer's tier is a floor too, so one below the cheapest phase would never be picked.
22
+ - **No reviewer at all** → not an error. Reviews fall back to `agent_models.reviewer`, cold, one session per phase. Say so once in the pool report so nobody reads the roster and assumes otherwise.
23
+ - **A wave the roster cannot staff** → warn. Sort the wave's assigned tiers and the roster's tiers, compare one for one: a wave of two Tier 3 phases needs two members at Tier 3 or above, and a junior on the roster does not help because the floor forbids handing them one. Such a wave still runs — it serializes — so say so now rather than letting it look like a slow machine later.
24
+ - **A plan with no Crew table at all** is a legacy plan. Read each phase's `**Suggested AI model**:` tier instead and skip every check above.
25
+ 6. **Record the resolved graph and the roster** in `run.md` (see [Update tracking](#1d-update-tracking)) so a resume rebuilds the identical schedule and the identical staffing without re-asking anything.
26
+
27
+ **The graph decides ordering — plan order does not.** Phase numbering is a reading aid for humans. A phase with no dependencies runs in wave 1 no matter how high its number is.
28
+ <!-- block-end: DAG_PARSE -->
29
+
30
+ <!-- block-begin: LANE_WORKTREE_POOL -->
31
+ ## Provision the lane worktree pool
32
+
33
+ Parallel execution **requires** [prepare-worktree](../prepare-worktree/SKILL.md). Two agents cannot write two branches in one working tree, and two concurrent test runs cannot share one dev / test database or one compose project name.
34
+
35
+ **Hard gate — refuse rather than degrade.** When `run_options.parallel_phases = true` but worktrees are unavailable — `foundation_skills.prepare-worktree` is `disabled`, or the user answered `No` to the worktree question, or provisioning fails — **stop and tell the user why**. Do not silently fall back to sequential; the plan's whole schedule was built around concurrency. Offer: `Enable worktrees and continue in parallel`, `Run this plan sequentially instead (max_parallel_lanes = 1)`, `Stop`. The user picks; the conductor never picks for them.
36
+
37
+ **Pool, don't provision per phase.** Provisioning a runnable worktree costs a dep install plus a DB fork. A plan with 14 phases must not pay that 14 times. Provision **`max_parallel_lanes` worktrees once** and reuse each one across the phases assigned to it:
38
+
39
+ 1. **Size the pool by the roster: one worktree per *implementer***, named for the member rather than numbered. Reviewers get none — see below. A plan with no **Crew** table falls back to `lanes = min(run_options.max_parallel_lanes, widest_wave)`.
40
+
41
+ **An implementer keeps their worktree for the whole run**, and that is the point rather than a detail. A sub-agent can only be continued into a directory it is already standing in, so a member that moved between phases would have to start cold every time — which is most of a phase's first turn spent rediscovering a codebase the same agent read an hour ago. Pinning the directory is what makes "reuse the agent" possible at all.
42
+
43
+ It costs a checkout and a set of forked databases per implementer, including for members idle in most waves. That is the trade, and it is worth stating to the user in the pool report rather than discovering on a full disk.
44
+
45
+ **Reviewers work in the lane they are reviewing.** A reviewer reads the phase's `WORKROOT` directly — the implementer's own worktree, with that phase's changes still uncommitted in it. That is deliberate and is the whole reason review sits where it does in the phase: findings are fixed **before the commit**, in the working tree, rather than recorded as a mistake and then a correction on top of it. A reviewer with a checkout of its own would be reading a committed snapshot, which is strictly less than what is there and too late to act on.
46
+
47
+ The cost is that a reviewer's directory moves from phase to phase, so its session carries only when two consecutive reviews happen to land in the same lane. Nothing to configure: the runtime decides per turn, the same way it decides for anyone whose lane changed.
48
+ 2. **Provision each lane.** Run [prepare-worktree](../prepare-worktree/SKILL.md) once per lane, plan-driven, with worktree name `plan-{plan-id-kebab}-crew-{implementer-id}` (or `plan-{plan-id-kebab}-lane-{i}` on a plan with no roster). This is the mechanical `worktree_prep` step — delegate all of them per the [Delegate a mechanical step to a configured model](#delegate-a-mechanical-step-to-a-configured-model) pattern when `agent_models.worktree_prep` is set, and **dispatch the provisioning calls concurrently** — they are independent.
49
+ 3. **Provision the integration worktree.** One more, named `plan-{plan-id-kebab}-integ`. The conductor merges lane branches into wave integration branches here (see [Lane branch topology](#lane-branch-topology)) so a merge never disturbs a lane that is still working.
50
+ 4. **Record the pool** in `run.md`: for each lane, `workroot`, `branch`, `worktree_summary`, `sandbox_tier`, plus `current_phase` (null when idle). `SANDBOX_TIER` is probed **per lane** — a mixed result is possible in principle and each lane's spawn wrapping follows its own tier.
51
+ 5. **Report once, then hold.** Show the user the pool (paths, DB names, compose project names, teardown commands) and the computed wave schedule together. `AskUserQuestion`: `Looks good — start`, `Fewer lanes`, `Stop — let me adjust`.
52
+
53
+ ### Re-orienting a member after the reset
54
+
55
+ A lane worktree carries state from the phase it just ran — most dangerously **applied migrations** in its forked dev / test DB. The next phase assigned to that lane branches from a different base, which may not contain those migrations, and a leftover schema silently invalidates its test run.
56
+
57
+ **And the agent standing in it has a memory of the old tree.** Continuing a member across phases is only safe if it is told what moved, so the message that starts its next phase is the phase's **full brief** — it is new work, not a delta — preceded by three facts:
58
+
59
+ 1. **It is the same agent in the same directory**, and everything it knows about the repository's layout, conventions and tooling still holds. Say so: an agent told only that things changed will re-read work it does not need to.
60
+ 2. **Whether the previous phase's own work is in this tree** — true exactly when this phase depends on it. This is the sentence that matters most. An agent that remembers writing a model and does not know it is absent will code against something that is not there.
61
+ 3. **Which files differ from what it last saw**, as a list:
62
+
63
+ ```bash
64
+ git -C <lane.workroot> diff --name-only plan/{plan-id-kebab}/phase-{prior.id} HEAD
65
+ ```
66
+
67
+ A list is checkable; "things may have changed" invites the agent to decide for itself what to trust. If the diff cannot be computed, say the set is **unknown** and to re-read before editing — never that nothing changed, which is the one wording that would stop it re-reading.
68
+
69
+ **Start a member cold when their previous phase failed.** Their session is the context that failed with it, and whatever wrong turn it took is exactly what a continuation preserves. Re-reading a repository is cheaper than inheriting a wrong conclusion about it.
70
+
71
+ Before handing a lane to its next phase:
72
+
73
+ ```bash
74
+ git -C <lane.workroot> checkout <phase.base_branch>
75
+ git -C <lane.workroot> checkout -b plan/{plan-id-kebab}/phase-{phase.id}
76
+ ```
77
+
78
+ then **reset that lane's databases to the new base** using the `db_reset_cmd` recorded in the lane's `worktree_summary` (prepare-worktree writes it — drop + recreate from the template, or re-run migrations from zero, per engine). A lane whose summary carries no `db_reset_cmd`, and whose outgoing or incoming phase touches the **Data Model Changes** section, is not safe to reuse: re-provision that lane instead. Never reuse a lane across a migration boundary without the reset.
79
+
80
+ Dep churn matters less but is real: when the incoming phase's plan body installs dependencies, re-run the project's install command in that lane before dispatching.
81
+
82
+ ### Teardown
83
+
84
+ The pool is torn down **only at the end of the run, and only by the user**. [Step 2](#step-2--final-report) prints every lane's teardown command plus the integration worktree's. Do not auto-run them — a failed lane's worktree is the only place its state survives.
85
+ <!-- block-end: LANE_WORKTREE_POOL -->
86
+
87
+ <!-- block-begin: LANE_TOPOLOGY -->
88
+ ## Lane branch topology
89
+
90
+ Every phase still gets its own branch. What changes under a DAG is **what that branch is based on** — no longer "the previous phase in plan order", but the phase's own declared dependencies.
91
+
92
+ **Base branch of phase `P`:**
93
+
94
+ | `P.depends_on` | `P.base_branch` |
95
+ |---|---|
96
+ | empty | `<BASE_BRANCH>` |
97
+ | exactly one phase `Q` | `plan/{plan-id-kebab}/phase-{Q.id}` |
98
+ | two or more phases | `plan/{plan-id-kebab}/integ-{P.id}` — built by merging every dependency's branch (see below) |
99
+
100
+ **Multi-dependency base.** Built in the **integration worktree**, before the phase is dispatched:
101
+
102
+ ```bash
103
+ git -C <integ.workroot> checkout -B plan/{plan-id-kebab}/integ-{P.id} plan/{plan-id-kebab}/phase-{first-dep.id}
104
+ git -C <integ.workroot> merge --no-ff plan/{plan-id-kebab}/phase-{next-dep.id} # once per remaining dep, in plan order
105
+ git -C <integ.workroot> push -u origin plan/{plan-id-kebab}/integ-{P.id}
106
+ ```
107
+
108
+ **Wave integration branches** are the durable spine. `plan/{plan-id-kebab}/wave-0` is `<BASE_BRANCH>`. When every phase at wave `N` has passed review, the conductor builds `plan/{plan-id-kebab}/wave-{N}` in the integration worktree by merging each wave-`N` lane branch into `wave-{N-1}` with `--no-ff`, in plan order. Wave branches are what a resume anchors on, what the final report points at, and what a phase whose dependency set covers an entire earlier wave may use directly as its base.
109
+
110
+ **A phase does not wait for its wave — it waits for its dependencies.** Wave branches are built behind the scheduler, not in front of it: a wave-3 phase whose two dependencies are both green starts immediately, even while other wave-2 phases are still running. The wave branch is bookkeeping and integration; the `depends_on` set is the gate.
111
+
112
+ ### Merge conflicts during integration
113
+
114
+ The orchestrator **never edits code**, including merge conflicts. On a conflicted `merge`:
115
+
116
+ 1. Capture `git -C <integ.workroot> diff --name-only --diff-filter=U`.
117
+ 2. Spawn a **fixer** subagent (the project's `fixer` agent type, at the `agent_models.fixer` model) inside the integration worktree. Its prompt carries: the conflicted paths, both phases' bodies from the plan, both phases' `phase-{id}.md` summaries, and the instruction to resolve for **both** intents — never to `--ours` / `--theirs` a conflict away.
118
+ 3. After the fixer returns, re-run the **outer gate** (`{{BUILD_CMD}}` plus the test scope `run_options.full_test_suite` selects) in the integration worktree. Red → loop back to step 2 with the failure.
119
+ 4. Green → commit the merge, push the branch, and record the conflict + resolution in `waves/wave-{N}.md`.
120
+
121
+ A conflict that survives its fixer-round budget (default **two**) is a **plan defect**, not a code problem: two phases in the same wave own the same code. Stop, report both phases and the paths, and ask whether to serialize them (add the edge, re-derive waves, re-run the loser) or continue by hand.
122
+
123
+ The budget is an **integration-level** setting, not either phase's `max_fix_rounds`. A conflict belongs to a *pair* of phases, so deriving it from one of them would make the answer depend on which phase happened to merge second.
124
+
125
+ **Confirming a fix requires reading the files, not asking git.** `git add` clears a path's unmerged flag whether or not `<<<<<<<` is still sitting in it, so git cannot tell you whether the fixer actually resolved anything. Scan the conflicted paths for conflict markers before committing the merge. Skip this and a fixer that did nothing produces a merge commit full of markers that passes straight into the wave branch.
126
+
127
+ ### One PR per phase, based on the phase's base
128
+
129
+ The PR `base` written into the prs-context frontmatter is the phase's **computed `base_branch`** — `<BASE_BRANCH>`, a single dependency's branch, or the `integ-{P.id}` branch. Never `<BASE_BRANCH>` for a phase that has dependencies; a wrong base makes the PR diff include every upstream phase and the review is unusable.
130
+ <!-- block-end: LANE_TOPOLOGY -->
131
+
132
+ <!-- block-begin: LANE_SCHEDULER -->
133
+ ### Dispatch loop
134
+
135
+ Continuous, dependency-driven. A phase starts the moment its dependencies are green and a lane is free — it does **not** wait for its wave to fill or drain.
136
+
137
+ ```
138
+ DONE = {} # phase ids that passed review + integrate
139
+ RUNNING = {} # lane -> phase currently in flight
140
+ BLOCKED = {} # phase ids whose upstream failed
141
+ PENDING = every executable phase (not cross-repo, not flag-removal)
142
+
143
+ while PENDING or RUNNING:
144
+ ready = [p for p in PENDING
145
+ if set(p.depends_on) <= DONE
146
+ and p.id not in BLOCKED]
147
+
148
+ while ready and some lane is idle:
149
+ p = ready.pop(0) # plan order breaks ties
150
+ agent = claim_agent(p) # the plan's member, a qualified peer, or None
151
+ if agent is None: continue # every hand at or above p's tier is busy
152
+ lane = claim_idle_lane()
153
+ reset_lane(lane, p) # checkout base + branch + DB reset
154
+ dispatch(lane, p, agent) # 1a → 1b → 1c, concurrently with other lanes
155
+ RUNNING[lane] = p ; PENDING.remove(p)
156
+
157
+ if not RUNNING: # nothing running, nothing ready
158
+ break # deadlock or done — checked below
159
+
160
+ wait for ANY lane to return
161
+ on success: DONE.add(p.id) ; free the lane ; write phase tracking ; maybe build a wave branch
162
+ on failure: mark p failed ; BLOCKED |= transitive_dependents(p) ; free the lane
163
+ ```
164
+
165
+ **Tie-breaking is plan order.** When more phases are ready than lanes are free, dispatch in the order they appear in **Phased Rollout**. Prefer a ready phase that unblocks the most dependents when the user has asked for throughput — but do not invent a scoring function; plan order is the default and is what the user can predict.
166
+
167
+ ### Claiming an agent
168
+
169
+ `claim_agent(p)` is the staffing half of a dispatch, and it runs **before** the lane is taken. A lane is disk; who is holding it is what the phase costs and whether the result is any good.
170
+
171
+ 1. **The member the plan assigned, if they are free.** The common case, and the one the plan's cost estimate is written against.
172
+ 2. **Otherwise the cheapest free member at or above that member's tier.** A wave should not serialize behind one agent when a qualified peer is idle. Reach for the *cheapest* qualified one, not the best available — covering for a peer must not quietly promote the phase to the top tier, or a busy wave silently runs every mid-tier phase on the senior's model.
173
+ 3. **Otherwise nobody, and the phase waits** — even with a lane free. This is the one place staffing costs throughput, and it is deliberate: a phase run below its tier does not fail cleanly. It produces plausible code that fails review two rounds later, by which point nothing points back at the staffing decision.
174
+
175
+ An implementer is held for the **whole phase**, not one turn of it: the fixer answering a review finding is the implementer continuing its own session, so handing the phase to someone else mid-flight would hand it to an agent with no session to continue. Release it when the phase settles.
176
+
177
+ **A reviewer is claimed per review turn, not per phase.** It has one session ledger, so two reviews running as the same reviewer would either resume one session twice or overwrite each other's record of it. Claim it when the review starts, release it when the verdict is in — holding it for a whole phase would make a plan with one reviewer and three implementers run three phases strictly in series. A phase whose reviewer is busy waits, and that wait cannot deadlock: reviewers never take phases, so it is always waiting on a review already in flight. A plan that finds one reviewer too serialising staffs a second.
178
+
179
+ Pick the reviewer the same way: the **cheapest reviewer on the roster at or above the phase's tier**. Never the phase's own implementer — the roles are disjoint, so that is not a rule to remember but a state the plan cannot describe.
180
+
181
+ **The wait cannot deadlock.** The floor is the assigned member's own tier, so a waiting phase is always waiting on somebody who is *holding another phase* — never on a qualification nobody on the roster has. If you find yourself with every agent idle and a phase that cannot be staffed, the plan assigned it to a member the **Crew** table does not list; stop and ask.
182
+
183
+ **Record every claim**, and record whether it was the plan's own assignment or a peer covering. A run where half the phases were covered by a dearer peer is a run that cost more than the plan said while every phase came back green — and that is invisible in the phase statuses.
184
+
185
+ **Deadlock check.** Loop exits with `PENDING` non-empty and nothing running → every remaining phase is blocked. Report each blocked phase with the failed upstream that blocks it.
186
+
187
+ **Failure containment.** A failed phase does **not** abort the run. Let every already-dispatched lane finish (killing a lane mid-implementation leaves a half-written worktree nobody can resume). Mark the failure's transitive dependents `BLOCKED`, keep dispatching everything still reachable, and report the whole picture at the end. The exception is the [Tier-4 escalation stop](#1a-implement) — after Tier 4 fails on a phase, that phase stops, but sibling lanes still run to completion.
188
+
189
+ **Per-lane pause gate.** `run_options.pause_between_phases = true` under parallel execution means: **stop dispatching new phases** once every in-flight lane has returned, then ask. It does not mean pausing lanes individually — a per-lane prompt with three lanes running is unreadable. Options stay `Continue`, `Pause`, `Stop`.
190
+
191
+ **Concurrency is a cap, not a target.** A graph that is a straight chain runs one lane at a time and that is correct — do not reorder or bundle phases to fill idle lanes. The same goes for the roster: an agent idle for three waves is not a reason to hand them work above their tier.
192
+ <!-- block-end: LANE_SCHEDULER -->
193
+
194
+ <!-- block-begin: TRACKING_DIR -->
195
+ Tracking lives in a **directory**, not a single file: `{{PLAN_DIR}}/TRACKING_{plan-id}/`.
196
+
197
+ ```
198
+ {{PLAN_DIR}}/TRACKING_{plan-id}/
199
+ ├─ run.md # conductor-owned run state
200
+ ├─ phase-{phase.id}.md # one per executed phase
201
+ └─ waves/wave-{N}.md # one per completed wave integration
202
+ ```
203
+
204
+ **Why a directory.** Concurrent lanes commit on different branches that later merge. A single shared tracking file would conflict on **every** wave merge, for no reason — the lanes are appending unrelated records. Splitting by owner makes the merges trivially clean, because no two branches ever touch the same path.
205
+
206
+ **Ownership rules — these are what make the merges clean. Do not relax them:**
207
+
208
+ | Path | Written by | Committed on |
209
+ |---|---|---|
210
+ | `run.md` | the conductor only | the integration worktree, on the current wave branch |
211
+ | `phase-{id}.md` | the lane that ran phase `{id}`, once, after it passes review | that phase's own lane branch, in the phase's final commit |
212
+ | `waves/wave-{N}.md` | the conductor only | `plan/{plan-id-kebab}/wave-{N}`, as part of the merge commit |
213
+
214
+ **No lane ever writes, edits, or deletes another lane's file.** A lane that needs a sibling's summary *reads* it — the conductor passes prior-phase summaries into the prompt as data (see [Implement](#1a-implement)); the lane does not go looking in the tracking dir itself.
215
+
216
+ **`run.md`** carries: feature name, plan path, started / last-updated dates, optional feature-flag info, **run options** (`pause_between_phases`, `generate_inline_comments`, `full_test_suite`{{E2E_RUN_OPTION_TRACKING}}, `use_worktree`, `parallel_phases`, `max_parallel_lanes`), the **resolved dependency graph** (phase id → `depends_on` + computed wave), the **lane pool** (per lane: `workroot`, `branch`, `worktree_summary`, `sandbox_tier`, `current_phase`), the **crew roster** (per member: id, role, tier, resolved model, its worktree for an implementer, the phases the plan assigned them, and the phases they actually took), the integration worktree, {{TRACKING_BRANCH_FIELD}}, and per-phase status (`done` / `running` / `blocked` / `failed` / `deferred`) with the lane each ran on.
217
+
218
+ **`phase-{id}.md`** carries: status, the crew member that took it + the model actually used + whether that member is the one the plan assigned + whether its session was continued from an earlier phase or started cold (and why, when cold) + the reviewer that read it{{TRACKING_PHASE_BRANCH_FIELD}}, base branch, wave, `depends_on`, e2e + screenshots if any, and the 5–15 line summary the conductor writes **from the git diff plus the agent's report** — not from the agent's narration.
219
+
220
+ **`waves/wave-{N}.md`** carries: which lane branches were merged, in what order, any conflicts and how they were resolved, and the outer-gate result on the merged tree.
221
+
222
+ **Migrating a legacy single-file tracking.** A plan started before this layout has `{{PLAN_DIR}}/TRACKING_{plan-id}.md`. On resume: create the directory, split the existing content (run options + graph → `run.md`; each completed-phase entry → its own `phase-{id}.md`), `git rm` the old file, and continue. Say so in the resume report.
223
+
224
+ **Deletion.** [Step 2](#step-2--final-report) deletes the whole directory (`git rm -r`) on the final integration branch, in one commit. The plan file stays.
225
+ <!-- block-end: TRACKING_DIR -->
226
+
227
+ <!-- block-begin: SIBLING_LANE_ISOLATION -->
228
+ **Sibling-lane writes — only when the pool has more than one lane.** A reviewer is bound by this too, and is the likeliest agent to trip it: it works in somebody else's `WORKROOT` by design, so "your own lane" for a review turn means *the lane under review*, not one it read last phase. The main checkout is not the only tree an agent can wander into: with a pool provisioned, `<lane-2>/app/models.py` is as reachable from lane 1 as the main checkout is, and a write there is worse than a stray main-checkout write — it lands in a tree another agent is actively editing and testing. The same guard covers both: everything outside the lane's own `WORKROOT` is off-limits.
229
+
230
+ - **Sandbox** (`SANDBOX_TIER = enforced`): the `--deny` / `--allow` set for a lane denies the **worktree root that holds the pool**, not just the main checkout, and allows only that lane's `WORKROOT` (plus `<main_checkout>/.git` and `<main_checkout>/.vinta-ai-workflows`). One `--deny <pool-root>` covers every sibling.
231
+ - **Backstop check** (`SANDBOX_TIER = none`, or as the cheap confirmation when enforced): after every implementer and fixer returns, run the stray-write check against the main checkout **and every sibling lane's workroot**:
232
+
233
+ ```bash
234
+ for tree in <main_checkout> <every lane workroot except this lane's>; do
235
+ git -C "$tree" status --short | grep -vE '^\?\?'
236
+ done
237
+ ```
238
+
239
+ Output from a sibling lane is a BLOCKER, handled exactly like a stray main-checkout write: diff it, recover the intent into the correct lane, then `git -C <tree> restore --` it away. Do this **before** the sibling's own review reads its diff — otherwise the sibling reviews foreign changes as its own.
240
+ <!-- block-end: SIBLING_LANE_ISOLATION -->
@@ -5,6 +5,8 @@ Three layers, all required, in order. The reviewing orchestrator never edits —
5
5
 
6
6
  ## Layer 1 — Mechanical checks
7
7
 
8
+ **Against the working tree, before the commit.** The phase's changes are still uncommitted in `<WORKROOT>`, and that is what every command here reads. It is why review sits before integrate rather than after: a finding is fixed in the tree, so the branch never records the mistake and a correction on top of it.
9
+
8
10
  1. `git -C <WORKROOT> status` + `git -C <WORKROOT> diff --stat`: confirm the file list matches the agent's report.
9
11
  2. **Read the full diff** for every changed file using `git -C <WORKROOT> diff`. Spot-checking is not enough.
10
12
  3. **Verify the outer gate** ran + green. By default that is `{{BUILD_CMD}}` (repo-wide) AND the scoped suite `{{SCOPED_TEST_PATTERN}}` covering the touched apps. {If run_options.full_test_suite = true:} the outer gate runs `{{BUILD_CMD}}` AND the full `{{TEST_CMD}}` instead — verify that. Look in the report for explicit confirmation the applicable gate was executed + passed{{E2E_LAYER1_NOTE}}. Vague confirmation → **re-run yourself** (in `<WORKROOT>`).
@@ -30,7 +32,7 @@ Open the phase body alongside the diff and walk:
30
32
 
31
33
  ## Layer 3 — Independent reviewer subagent
32
34
 
33
- After Layers 1–2 pass, spawn a **separate** subagent (different session, no implementation context) using the project's `reviewer` agent type ([ai-tools/agents/reviewer.md](ai-tools/agents/reviewer.md)) at the model resolved from `agent_models.reviewer` (see the [Resolve the reviewer + fixer model](#resolve-the-reviewer--fixer-model) step; unset → runtime default). Read-only by design.
35
+ After Layers 1–2 pass, hand the diff to a **reviewer from the plan's Crew table** — a member whose role is `reviewer`, which is never a member that writes code — using the project's `reviewer` agent type ([ai-tools/agents/reviewer.md](ai-tools/agents/reviewer.md)) at the model resolved by the [Resolve the reviewer + fixer model](#resolve-the-reviewer--fixer-model) step. Where the roster staffs no reviewer, spawn a **separate** subagent at `agent_models.reviewer` with no implementation context, as before. Read-only by design, either way.
34
36
 
35
37
  Reviewer prompt template — see the reviewer agent's body for the standard form. Triage findings:
36
38
  - **BLOCKER**: must fix before the phase is pushed (the conductor's integrate step).
@@ -43,8 +45,57 @@ The reviewer finds nothing on a >300-LoC multi-file phase → suspicious. Read o
43
45
 
44
46
  ## Fix loop
45
47
 
46
- 1. Spawn a **new** subagent — the project's `fixer` agent type ([ai-tools/agents/fixer.md](ai-tools/agents/fixer.md)) at the model resolved from `agent_models.fixer` (see the [Resolve the reviewer + fixer model](#resolve-the-reviewer--fixer-model) step; unset → runtime default). The fix prompt quotes the finding verbatim. For comment-hygiene findings (Layer 2 item 8), the fix prompt tells the fixer to run the `deslop-comments` skill ([ai-tools/skills/deslop-comments/SKILL.md](ai-tools/skills/deslop-comments/SKILL.md)) scoped to the phase's touched files — comment-only edits, no behavior change.
47
- 2. The `fixer`'s system prompt mandates re-running the inner loop + outer gate (in `<WORKROOT>`).
48
- 3. After the fixer returns, redo Layer 1 in full + the affected portion of Layer 2.
49
- 4. Loop until Layers 1, 2, 3 are all clean.
48
+ **The implementer fixes its own findings.** The agent that wrote the code still
49
+ holds the phase brief, the plan's bounds, the dependency context and its own
50
+ reasoning; a fresh fixer holds a quoted finding and has to rediscover the rest —
51
+ slower, dearer, and more likely to "fix" the symptom by changing something the
52
+ phase deliberately chose. Continuing the implementer is the default, and the
53
+ message it gets is a **delta**: the findings and what to do about them, never
54
+ the brief again.
55
+
56
+ 1. **Continue the phase's implementer sub-agent** with the findings. Quote each
57
+ one verbatim, say nothing else about the phase — it was told all of that when
58
+ it started, and repeating it invites a re-implementation rather than a fix.
59
+ Do not re-send the plan sections, the dependency summaries, or the phase body.
60
+ For comment-hygiene findings (Layer 2 item 8), tell it to run the
61
+ `deslop-comments` skill ([ai-tools/skills/deslop-comments/SKILL.md](ai-tools/skills/deslop-comments/SKILL.md))
62
+ scoped to the phase's touched files — comment-only edits, no behavior change.
63
+ Whether continued or fresh, the fixing agent re-runs the inner loop + outer
64
+ gate in `<WORKROOT>` before reporting.
65
+ 2. **Where the runtime cannot continue a finished sub-agent**, spawn a fresh one
66
+ — the project's `fixer` agent type ([ai-tools/agents/fixer.md](ai-tools/agents/fixer.md))
67
+ at the model resolved from `agent_models.fixer` — and give it the phase
68
+ context the implementer would have had, because it has none. Note in the
69
+ phase's tracking record that the fix was a cold hand-off, so a slow phase can
70
+ be read later without guessing.
71
+ 3. **The last round before you would give up goes to a fresh fixer, always.**
72
+ Reusing the implementer means the agent that wrote the bug is fixing it,
73
+ which is usually the point — it knows why the code is that way — and
74
+ occasionally exactly wrong, because that assumption *was* the bug. When a
75
+ finding has survived the implementer's own attempts, hand it to an agent that
76
+ has not seen the work before escalating a tier or stopping.
77
+ 4. After the fixer returns, redo Layer 1 in full + the affected portion of Layer 2.
78
+ 5. Loop until Layers 1, 2, 3 are all clean.
79
+
80
+ **The reviewer is never the implementer.** Continuing the *reviewer* across
81
+ rounds — and across phases — is fine and remembers what it flagged, but the
82
+ review itself must come from an agent that did not write the code. An
83
+ implementer asked to review its own phase grades its own work from inside its
84
+ own reasoning, which is the one thing the layers exist to prevent. On a plan
85
+ with a **Crew** table this is structural rather than a rule to follow: reviewers
86
+ and implementers are disjoint sets, and no phase can be assigned to a reviewer.
87
+
88
+ **Which model fixes.** A continued implementer fixes at its own crew member's
89
+ tier, because it *is* that member. `agent_models.fixer` therefore governs the
90
+ cold cases only — the runtime fallback in step 2 and the escalation in step 3. A
91
+ project that set `fixer` to a cheaper tier to save money should know it now
92
+ applies to fewer rounds than before.
93
+
94
+ **The fix does not take the reviewer's tier.** The review runs a tier above the
95
+ author deliberately, and it would be easy to carry that tier into the fix on the
96
+ grounds that the finding was hard enough to need it. Don't: the review is a
97
+ judgement about the code and the fix is a change to it, and the agent best
98
+ placed to make that change is still the one that knows why the code is that way.
99
+ A finding that genuinely needs a more capable hand is what step 3's escalation is
100
+ for.
50
101
  <!-- block-end: LAYERS -->
@@ -1,15 +1,17 @@
1
- <!-- Partial: worktree-seam — the WORKROOT abstraction. Collapses the former scattered `if use_worktree` conditionals into one resolution (conductor) + two local, data-driven checks (implement-phase spawn wrap, review-phase stray-write). Blocks: WORKROOT_RESOLUTION (conductors), WORKROOT_TOPOLOGY_RULE (conductors + integrate-phase), SANDBOX_WRAP (implement-phase), STRAY_WRITE_CHECK (review-phase). -->
1
+ <!-- Partial: worktree-seam — the WORKROOT abstraction. Collapses the former scattered `if use_worktree` conditionals into one resolution (conductor) + two local, data-driven checks (implement-phase spawn wrap, review-phase stray-write). Blocks: WORKROOT_RESOLUTION (conductors), WORKROOT_TOPOLOGY_RULE (conductors + integrate-phase), SANDBOX_WRAP (implement-phase), STRAY_WRITE_CHECK (review-phase). Under parallel execution `WORKROOT` becomes per-lane — the pool lives in parallel-lanes.md#LANE_WORKTREE_POOL and every downstream step still reads exactly one `WORKROOT`, the one for its own lane. -->
2
2
 
3
3
  <!-- block-begin: WORKROOT_RESOLUTION -->
4
4
  ## Step 0.5 — Resolve `WORKROOT`
5
5
 
6
- Resolve three values **once**, before any phase runs, and record them in tracking. Every later step uses them as data — no step re-derives worktree state.
6
+ Resolve three values **once per lane**, before any phase runs, and record them in tracking. Every later step uses them as data — no step re-derives worktree state. A sequential run has exactly one lane, so "once per lane" and "once" are the same thing.
7
7
 
8
8
  | Value | `run_options.use_worktree = false` | `run_options.use_worktree = true` |
9
9
  |---|---|---|
10
- | `WORKROOT` | the main checkout root (the repo the skill was invoked from) | `<worktree_path>` returned by prepare-worktree |
10
+ | `WORKROOT` | the main checkout root (the repo the skill was invoked from) | `<worktree_path>` returned by prepare-worktree — **this lane's** path under parallel execution |
11
11
  | `BASE_BRANCH` | `{{DEFAULT_BRANCH}}` | `<worktree_branch>` prepare-worktree created |
12
- | `SANDBOX_TIER` | `none` | `enforced` or `none` (probed by prepare-worktree) |
12
+ | `SANDBOX_TIER` | `none` | `enforced` or `none` (probed by prepare-worktree, per lane) |
13
+
14
+ **When `run_options.parallel_phases = true`:** skip the single-worktree path below entirely and provision the pool per [Provision the lane worktree pool](#provision-the-lane-worktree-pool). That step resolves one `WORKROOT` / `SANDBOX_TIER` per lane plus one integration worktree, and refuses the run outright when worktrees are unavailable. `BASE_BRANCH` stays plan-level (`{{DEFAULT_BRANCH}}`, or the worktree base when the whole plan hangs off one); each phase's *own* base is derived from its dependencies, not from `BASE_BRANCH` — see [Lane branch topology](#lane-branch-topology).
13
15
 
14
16
  **When `use_worktree = false`:** set `WORKROOT` = main checkout, `BASE_BRANCH = {{DEFAULT_BRANCH}}`, `SANDBOX_TIER = none`. Make `BASE_BRANCH` current + up to date: `git -C <WORKROOT> checkout {{DEFAULT_BRANCH}} && git -C <WORKROOT> pull --ff-only`. Jump to Step 1.
15
17
 
@@ -22,7 +24,7 @@ Resolve three values **once**, before any phase runs, and record them in trackin
22
24
  - `worktree_branch` → `BASE_BRANCH` (prepare-worktree based it on `origin/{{DEFAULT_BRANCH}}`, so it is already current).
23
25
  - `worktree_summary` — `.vinta-ai-workflows/worktrees/<name>.yaml` (read by teardown).
24
26
  - `sandbox_tier` → `SANDBOX_TIER`: `enforced` (the [Filesystem sandbox](../prepare-worktree/SKILL.md#step-55--filesystem-sandbox-os-level-write-guard) step found `sandbox-exec` / `bwrap` and will OS-block main-checkout writes) or `none` (no sandbox tool — prevention degrades to the review-phase stray-write backstop).
25
- 4. **Persist to tracking.** Write `run_options.worktree_path`, `run_options.worktree_branch`, `run_options.worktree_summary`, `run_options.sandbox_tier` into `{{PLAN_DIR}}/TRACKING_{plan-id}.md`. All later phases read them — never re-provision mid-plan.
27
+ 4. **Persist to tracking.** Write `run_options.worktree_path`, `run_options.worktree_branch`, `run_options.worktree_summary`, `run_options.sandbox_tier` into `{{PLAN_DIR}}/TRACKING_{plan-id}/run.md`. All later phases read them — never re-provision mid-plan.
26
28
  5. **Report to user.** Quote the prepare-worktree summary back: which dirs symlinked vs copied vs forked; which DB(s) forked + their names; compose project name; teardown command. Hold here until the user confirms (`AskUserQuestion`: `Looks good — start phase 1`, `Stop — let me adjust`).
27
29
 
28
30
  Failure modes:
@@ -31,7 +33,7 @@ Failure modes:
31
33
  <!-- block-end: WORKROOT_RESOLUTION -->
32
34
 
33
35
  <!-- block-begin: WORKROOT_TOPOLOGY_RULE -->
34
- **`WORKROOT` topology rule.** Every phase branches off the previous phase (first executed phase off `<BASE_BRANCH>`), and **every** `git` / lint / test / build / migrate call runs with `git -C <WORKROOT>` (or after `cd <WORKROOT>`). When `use_worktree = false`, `WORKROOT` is the main checkout and this is exactly today's in-place behavior; when `true`, `WORKROOT` is the worktree and branches / commits stack inside it, never touching the main checkout's working tree. One uniform path — no per-step worktree branching.
36
+ **`WORKROOT` topology rule.** Every phase branches off **its own computed base** — the branch derived from that phase's `**Depends on**:` set, which is `<BASE_BRANCH>` for a phase with no dependencies (see [Lane branch topology](../implement-plan/SKILL.md#lane-branch-topology)) — and **every** `git` / lint / test / build / migrate call runs with `git -C <WORKROOT>` (or after `cd <WORKROOT>`). When `use_worktree = false`, `WORKROOT` is the main checkout and phases run one at a time in place; when `true`, `WORKROOT` is a worktree and branches / commits live inside it, never touching the main checkout's working tree. Under parallel execution `WORKROOT` is **this lane's** worktree and nothing else — a lane never reads or writes a sibling lane's tree. One uniform path — no per-step worktree branching.
35
37
  <!-- block-end: WORKROOT_TOPOLOGY_RULE -->
36
38
 
37
39
  <!-- block-begin: SANDBOX_WRAP -->
@@ -40,6 +42,7 @@ Failure modes:
40
42
  ```bash
41
43
  ai-tools/skills/prepare-worktree/scripts/sandbox-run.sh \
42
44
  --deny <main_checkout> \
45
+ --deny <pool_root> \
43
46
  --allow <WORKROOT> \
44
47
  --allow <main_checkout>/.vinta-ai-workflows \
45
48
  --allow <main_checkout>/.git \
@@ -48,6 +51,8 @@ ai-tools/skills/prepare-worktree/scripts/sandbox-run.sh \
48
51
 
49
52
  `<main_checkout>` is the repo root the skill was invoked from (never `WORKROOT` when a worktree is in use). A stray write then fails with `Operation not permitted` / `EROFS`; the subagent retries against the worktree. `<main_checkout>/.git` must be allowed because git worktrees write commits into the main repo's `.git` (shared objects/refs, `.git/worktrees/<name>/index.lock`); omitting it makes the subagent's own `git commit` fail.
50
53
 
54
+ `<pool_root>` is the directory that holds the lane worktrees (the worktree root prepare-worktree provisioned into). Denying it and allowing back only this lane's `WORKROOT` blocks writes into **sibling lanes** — under parallel execution the more dangerous stray write, because a sibling's tree is being edited and tested at that moment. Omit the `--deny <pool_root>` line only when the run has a single lane and no pool exists.
55
+
51
56
  - **In-process subagent runtimes** (orchestrator and subagent share one OS process — e.g. claude-code's Task tool) can't wrap a single spawn. Two options: (a) install a runtime pre-write guard hook scoped to `WORKROOT` (prepare-worktree ships `scripts/claude-worktree-write-guard.py` + `scripts/gen-claude-sandbox-settings.sh` for claude-code); or (b) run the **entire** invocation under `sandbox-run.sh` with the same `--deny` / `--allow` set. Pick whichever the runtime supports.
52
57
  - **`SANDBOX_TIER = none`** (no sandbox tool, or `use_worktree = false`) → skip wrapping; prevention falls back entirely to the review-phase stray-write check. Surface this once to the user when a worktree run is unsandboxed so the weaker guarantee is explicit.
53
58
  <!-- block-end: SANDBOX_WRAP -->
@@ -65,4 +70,6 @@ Any output is a BLOCKER for this phase:
65
70
  - Once recovered (or confirmed superseded by the correctly-committed worktree version), discard the stray edits with `git -C <main_checkout> restore -- <path>` so the main checkout returns clean. Never leave the main checkout dirty between phases — a later phase can't tell new thrash from old.
66
71
 
67
72
  `<main_checkout>` is the repo root the skill was invoked from (NOT `WORKROOT`). When `WORKROOT == <main_checkout>` (`use_worktree = false`), skip this check entirely — your work legitimately lives in that tree.
73
+
74
+ <!-- include: partials/parallel-lanes.md#SIBLING_LANE_ISOLATION -->
68
75
  <!-- block-end: STRAY_WRITE_CHECK -->
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: amend-plan
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 downstream stacked phase branch, 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}}
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
4
  ---
5
5
 
6
6
  # Amend Plan
@@ -22,7 +22,8 @@ The flow is destructive (force-push). Every modification is gated on user confir
22
22
  - {{COAUTHOR_POLICY_BLOCK}}
23
23
  - Default branch: `{{DEFAULT_BRANCH}}`.
24
24
  - Branch naming convention (set by [implement-plan](../implement-plan/SKILL.md)): `plan/{plan-id-kebab}/phase-{phase.id}`.
25
- - **`WORKROOT`.** Resolve once, same as the [implement-plan Resolve WORKROOT step](../implement-plan/SKILL.md#step-05--resolve-workroot): the main checkout by default, or the plan's worktree when `run_options.use_worktree = true` in the tracking file. Every `git` call below runs with `git -C <WORKROOT>`; when no worktree is in play, `WORKROOT` is the main checkout and the commands read exactly as in-place git.
25
+ - **`WORKROOT`.** Resolve once, same as the [implement-plan Resolve WORKROOT step](../implement-plan/SKILL.md#step-05--resolve-workroot): the main checkout by default, or the plan's worktree when `run_options.use_worktree = true` in `run.md`. When the run used a **lane pool**, amend in the **integration worktree** — lanes are sized for forward implementation and may still hold state from their last phase. Every `git` call below runs with `git -C <WORKROOT>`; when no worktree is in play, `WORKROOT` is the main checkout and the commands read exactly as in-place git.
26
+ - **Amend only when no implementation is in flight.** A rewrite force-pushes branches other lanes may be based on. If `run.md` shows any phase `running`, stop and tell the user to let the run finish (or stop it) first.
26
27
 
27
28
  ## When to use
28
29
 
@@ -49,7 +50,7 @@ The flow is destructive (force-push). Every modification is gated on user confir
49
50
 
50
51
  3. **Parse the plan.** Same structured fields as [implement-plan's "Extract structured fields" step](../implement-plan/SKILL.md#step-0--locate--parse-plan): plan id, **Goals + Non-goals** / **Guiding Decisions** / **Data Model Changes** / phase records from **Phased Rollout** / **Risk & Rollout Notes** through **Touch List**.
51
52
 
52
- 4. **Read the tracking file** `{{PLAN_DIR}}/TRACKING_{plan-id}.md` if present. Its `Completed Phases` section tells you which phase branches were pushed, which model + base were used, and the `run_options` (including worktree state → `WORKROOT`). If absent → `git -C <WORKROOT> branch -a | grep plan/{plan-id-kebab}` to enumerate pushed phase branches.
53
+ 4. **Read the tracking directory** `{{PLAN_DIR}}/TRACKING_{plan-id}/` if present: `run.md` carries the `run_options` (including worktree state → `WORKROOT`, and the resolved dependency graph), each `phase-{id}.md` carries that phase's branch, base, and model, and `waves/wave-{N}.md` records which lane branches were merged where. A plan run before the directory layout has a single `TRACKING_{plan-id}.md` — read it the same way. If neither exists → `git -C <WORKROOT> branch -a | grep plan/{plan-id-kebab}` to enumerate pushed phase, `integ-`, and `wave-` branches.
53
54
 
54
55
  5. **Build a per-phase state map.** For every phase in the plan, record:
55
56
 
@@ -58,7 +59,8 @@ The flow is destructive (force-push). Every modification is gated on user confir
58
59
  | `phase.id`, `phase.title` | plan's **Phased Rollout** section |
59
60
  | `state` | one of `not-started` / `in-progress` / `implemented-not-merged` / `merged-to-default` |
60
61
  | `branch` | tracking file or git, pattern `plan/{plan-id-kebab}/phase-{id}` |
61
- | `base` | tracking file or `git -C <WORKROOT> merge-base origin/<branch> <prev-branch>`; root phase bases on `{{DEFAULT_BRANCH}}` |
62
+ | `base` | `phase-{id}.md`, or `git -C <WORKROOT> merge-base origin/<branch> <base-branch>`. **The base is the phase's dependency-derived branch, not the previous phase in plan order** — read `**Depends on**:` from the plan and resolve it per [Lane branch topology](../implement-plan/SKILL.md#lane-branch-topology). A phase with no dependencies bases on `{{DEFAULT_BRANCH}}`. |
63
+ | `dependents` | every phase whose `**Depends on**:` set contains this one, transitively. This — not "every phase with a higher number" — is the set a rewrite cascades into. |
62
64
  | `pr_status` | `.vinta-ai-workflows/prs-context/{feature-kebab}/phase-{id}.md` frontmatter (`pending` / `published`) when the file exists |
63
65
  | `merged_to_default` | `git -C <WORKROOT> branch --merged origin/{{DEFAULT_BRANCH}} | grep` against the branch |
64
66
 
@@ -66,9 +68,10 @@ The flow is destructive (force-push). Every modification is gated on user confir
66
68
 
67
69
  6. **Classify the requested change** by phase impact, in priority order:
68
70
 
69
- - **`body-rewrite`** — existing phase keeps its id; body changes. Cascades downstream because rewritten commits get new SHAs.
70
- - **`insert-new`** — new phase between existing ones. Cascades downstream because every later phase rebases onto the new branch.
71
- - **`append-new`** — new phase tacked on after the last one. No downstream cascade. Implementation runs forward via [implement-plan](../implement-plan/SKILL.md) — this skill hands off after editing the plan file.
71
+ - **`body-rewrite`** — existing phase keeps its id; body changes. Cascades into its `dependents` closure because rewritten commits get new SHAs. Phases outside that closure are untouched — under a parallel plan that is often most of them.
72
+ - **`insert-new`** — new phase slotted in. Cascades into whichever existing phases the user makes depend on it (and their closure). A new phase nobody depends on cascades into nothing.
73
+ - **`append-new`** — new phase with no existing dependents. No cascade. Implementation runs forward via [implement-plan](../implement-plan/SKILL.md) — this skill hands off after editing the plan file.
74
+ - **`dependency-change`** — the change is to a phase's `**Depends on**:` line rather than its body. Adding an edge to an already-implemented phase means its branch has the wrong base: it must be rebased onto the new base and its own closure re-cascaded. Removing an edge is safe to leave as-is (the branch simply carries more history than it needs) — say so and let the user decide whether a re-cut is worth it.
72
75
  - **`guiding-decisions-change`** — change inside the plan's **Guiding Decisions** section. Cascades into every phase that referenced the decision.
73
76
 
74
77
  7. **Evaluate amendment blast radius — recommend restart when too big.** Amending in place stops being a good deal once the rewrite work approaches re-implementation. Compute these signals from the per-phase state map + the requested change:
@@ -100,7 +103,7 @@ The flow is destructive (force-push). Every modification is gated on user confir
100
103
  1. Help the user draft a new `YYYY-MM-DD-FEATURE_NAME_PLAN.md` with today's date (paired with the spec, same `FEATURE_NAME`). This skill does not write the new plan body — point at [plan-feature](../plan-feature/SKILL.md) (or [create-spec](../create-spec/SKILL.md) first if the spec also changed).
101
104
  2. Annotate the **old** plan: at the top, add `**Superseded YYYY-MM-DD by ../YYYY-MM-DD-FEATURE_NAME_PLAN.md** — reason: <one line>`. Append the same line under `## Amendments`.
102
105
  3. Leave the old phase branches alone — useful audit trail, no force-push needed.
103
- 4. Update `TRACKING_{plan-id}.md` to mark the plan superseded; preserve all completed-phase entries.
106
+ 4. Update `TRACKING_{plan-id}/run.md` to mark the plan superseded; preserve every `phase-{id}.md` entry.
104
107
  5. Hand off to [plan-feature](../plan-feature/SKILL.md). This skill exits.
105
108
 
106
109
  On `Amend in place`: proceed to step 8 (the original confirmation gate, renumbered). On `Stop`: exit cleanly; nothing written.
@@ -126,7 +129,7 @@ Always the first write. Plan file is durable; commits get rewritten next.
126
129
  - Letter: `1b` between `1` (relabeled `1a`) and `2`. Requires renaming `1` → `1a` inside **Phased Rollout** + updating downstream references.
127
130
  Ask the user. Default: decimal — no rename of existing ids.
128
131
 
129
- 3. **Appends** — new `## Phase N+1` block at end of **Phased Rollout**. Same shape as siblings: Goal, Suggested AI model, optional Review models, reusable_skills, Changes, Tests, Acceptance.
132
+ 3. **Appends** — new `## Phase N+1` block at end of **Phased Rollout**. Same shape as siblings: Goal, **Assigned to**, optional Review models, reusable_skills, Changes, Tests, Acceptance. An appended phase is staffed off the existing **Crew** table; adding a member is a change to the plan's staffing arithmetic and needs the same `Takes`-column update as any other.
130
133
 
131
134
  4. **Guiding Decisions changes** — rewrite the affected row. Add a one-line note at the top of **Guiding Decisions** ("**Amended YYYY-MM-DD**: replaced storage shape from X to Y; affects phases 2, 3, 4.") so reviewers see what shifted. Reference the changed row by its **Decision** column name, not by a `§N.M` shorthand.
132
135
 
@@ -142,7 +145,9 @@ Always the first write. Plan file is durable; commits get rewritten next.
142
145
 
143
146
  ## Step 2 — Build the rewrite queue
144
147
 
145
- For each phase classified as needing commit rewrites (`body-rewrite` for already-implemented phases, downstream phases for `insert-new` / `body-rewrite` / `guiding-decisions-change`), build a queue ordered by branch stack depth: parent first, children after.
148
+ For each phase classified as needing commit rewrites (`body-rewrite` for already-implemented phases, plus each rewritten phase's `dependents` closure for `insert-new` / `body-rewrite` / `dependency-change` / `guiding-decisions-change`), build a queue in **topological order of the dependency graph**: a phase is rebased only after every phase it depends on has been. Phases outside the closure are never touched — leave their branches and PRs alone.
149
+
150
+ A phase with several dependencies rebases onto a **rebuilt** `integ-{id}` branch: re-merge its dependency branches in plan order first, then rebase the phase onto that. Rebasing it onto only one dependency silently drops the others.
146
151
 
147
152
  For each entry record:
148
153
 
@@ -299,9 +304,9 @@ Always include in the publish-log block at the bottom of the file:
299
304
  - YYYY-MM-DDThh:mm:ssZ — branch force-pushed (amend-plan); old SHA <x>, new SHA <y>
300
305
  ```
301
306
 
302
- ### 4g. Update tracking file
307
+ ### 4g. Update tracking
303
308
 
304
- Update `{{PLAN_DIR}}/TRACKING_{plan-id}.md` for the rewritten phase:
309
+ Update `{{PLAN_DIR}}/TRACKING_{plan-id}/phase-{id}.md` for the rewritten phase (and `run.md` when the graph itself changed):
305
310
  - Append to its `Completed Phases` entry: `Amended YYYY-MM-DD: <summary>; new SHA <x>; force-pushed`.
306
311
  - Don't remove the original summary — keep history.
307
312
 
@@ -9,10 +9,10 @@ Execution unit invoked by [implement-plan](../implement-plan/SKILL.md) (and by [
9
9
 
10
10
  ## Inputs (passed by the conductor as data — this skill re-derives none of them)
11
11
 
12
- - `phase` record: `{ id, title, goal, body, spec_use_case, suggested_model_tier, reusable_skills, has_e2e, acceptance }`.
12
+ - `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.
13
13
  - Plan-level decisions: **Goals + Non-goals**, **Guiding Decisions**, the relevant **Data Model Changes** subsection.
14
- - Prior-phase summaries (the tracking file's "Completed Phases" section).
15
- - `WORKROOT`, `BASE_BRANCH`, `SANDBOX_TIER` — resolved once by the conductor ([Resolve WORKROOT step](../implement-plan/SKILL.md#step-05--resolve-workroot)).
14
+ - **Dependency-closure summaries** — the `phase-{id}.md` tracking entries for this phase's transitive dependencies, and only those. Not "everything finished so far": under parallel execution a sibling lane's work is not in this phase's base branch, and describing it as done makes the implementer code against files it cannot see.
15
+ - `WORKROOT`, `SANDBOX_TIER` — **this lane's**, resolved by the conductor ([Resolve WORKROOT step](../implement-plan/SKILL.md#step-05--resolve-workroot)). `phase.base_branch` — the branch the conductor already created this phase's branch from, derived from `depends_on`.
16
16
  - `run_options.full_test_suite` — resolves the outer gate's test scope in the composed prompt's `{If run_options.full_test_suite = true:}` marker (false = scoped suite only; true = full repo suite).
17
17
 
18
18
  ## 1. Compose the agent prompt (token-efficient)
@@ -42,6 +42,8 @@ A phase that combines shapes → the agent type stays `implementer`, and the pro
42
42
 
43
43
  **Avoid bouncing the same phase between multiple agents.** Wanting to "hand off" mid-phase → the plan should have split into sub-phases instead.
44
44
 
45
+ **Concurrent invocations are expected.** The conductor may have several lanes in flight, each running its own copy of this skill against a different phase. Nothing here is shared: the prompt, the model pick, the spawn, and the returned report all belong to one phase in one `WORKROOT`. Never read another lane's worktree, branch, or tracking entry — if this phase needs something from another phase, that is a dependency edge the plan should have declared.
46
+
45
47
  ## Output
46
48
 
47
49
  Return the implementer's single final report verbatim to the conductor (status, files, summary, deviations, blockers). The conductor — not this skill — writes tracking from the git diff + the report.