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.
- package/CHANGELOG.md +288 -0
- package/README.md +21 -13
- package/package.json +2 -1
- package/schemas/README.md +5 -1
- package/schemas/postmortem.v1.schema.json +339 -0
- package/schemas/vinta-ai-workflows-config.v1.schema.json +13 -1
- package/schemas/workflow.v1.schema.json +833 -0
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +9 -1
- package/skills/vinta-derive-skills/SKILL.md +6 -2
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +622 -32
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +24 -2
- package/skills/vinta-derive-skills/resources/plan-execution/README.md +21 -4
- package/skills/vinta-derive-skills/resources/plan-execution/partials/agent-models.md +30 -3
- package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/modular.md +27 -14
- package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +12 -18
- package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +19 -9
- package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +25 -9
- package/skills/vinta-derive-skills/resources/plan-execution/partials/parallel-lanes.md +240 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +56 -5
- package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +13 -6
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +17 -12
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +5 -3
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +94 -57
- package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +2 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +16 -5
|
@@ -19,8 +19,9 @@ The output is a worktree that is the **same shape as the main checkout** from th
|
|
|
19
19
|
|
|
20
20
|
- A plan / spec that takes hours-to-days where parallel work in the main checkout would be disruptive (a long migration, a refactor, a feature you want to experiment on while still serving customer support out of `main`).
|
|
21
21
|
- Two-or-more concurrent plans where each needs its own running app + own DB state.
|
|
22
|
+
- **A lane pool for one plan** — [implement-plan](../implement-plan/SKILL.md) implements independent phases concurrently and calls this skill once per lane (plus once for an integration worktree). See **Provisioning a pool** below.
|
|
22
23
|
- A risky migration where the user wants the migration to run against a forked DB, then walk the diff before promoting.
|
|
23
|
-
- [implement-plan](../implement-plan/SKILL.md) Step 0 (c) — when the user opts in, that orchestrator runs this skill
|
|
24
|
+
- [implement-plan](../implement-plan/SKILL.md) Step 0 (c) — when the user opts in, that orchestrator runs this skill before the first phase (once for a sequential run, once per lane plus one integration worktree for a parallel run), captures the resulting paths, and threads each lane's through its own subagent prompts.
|
|
24
25
|
|
|
25
26
|
## When NOT to use
|
|
26
27
|
|
|
@@ -28,6 +29,19 @@ The output is a worktree that is the **same shape as the main checkout** from th
|
|
|
28
29
|
- The project has no ignored runnable state (rare — usually means the project is so simple a worktree adds friction with no upside).
|
|
29
30
|
- The user is on a filesystem that doesn't support symlinks (Windows non-NTFS volumes, some corporate fileshares). Fall back to copy-only and warn the user up front.
|
|
30
31
|
|
|
32
|
+
## Provisioning a pool (several worktrees for one plan)
|
|
33
|
+
|
|
34
|
+
A caller that needs N isolated lanes invokes this skill N times — once per lane, with distinct names (`plan-<id>-lane-1`, `plan-<id>-lane-2`, …, plus `plan-<id>-integ` for merges). Nothing about the flow changes per invocation, but four things must hold across them:
|
|
35
|
+
|
|
36
|
+
1. **Names are the isolation key.** Every forked DB name, compose project name, redis index, and S3 prefix already derives from `<worktree-name>` — distinct names give distinct everything. Never provision two lanes with the same name.
|
|
37
|
+
2. **Answer the interview once, reuse the answers.** The caller passes the same plan and the same strategy answers to every lane. Don't re-interview per lane.
|
|
38
|
+
3. **Provision lanes concurrently when the caller asks for it** — the expensive part of a lane (dependency install or link, DB clone, summary write) shares nothing between lanes and should overlap. But **two steps must be serialized**, and skipping either corrupts something:
|
|
39
|
+
- **`git worktree add` itself.** Git rewrites `.git/worktrees/` metadata on every add, and concurrent adds against one repository clobber each other's entries. Add the worktrees one at a time, then do the per-lane work in parallel. This is forced by git, not a choice.
|
|
40
|
+
- **The DB template.** Create or refresh the template DB once, then let each lane clone from it — that clone is what makes N lanes cost N cheap copies instead of N full provisions.
|
|
41
|
+
4. **Record a `reset_cmd`** for each forked DB (see the summary schema). A pooled lane gets reused for a later phase on a different base, and the caller must be able to return its DB to a fresh state without re-provisioning. A lane whose DBs have no `reset_cmd` is single-use — say so in the report so the caller re-provisions instead of reusing it.
|
|
42
|
+
|
|
43
|
+
Disk is the real constraint: N lanes means N dep trees and N DB copies. Run the **Sanity checks** disk probe against `N ×` the estimate, not `1 ×`, and warn the caller before the first lane if the pool won't fit.
|
|
44
|
+
|
|
31
45
|
## Inputs (Step 0 — interview)
|
|
32
46
|
|
|
33
47
|
Use `AskUserQuestion` for every finite-choice question. Open prose only when the answer is genuinely free-form.
|
|
@@ -344,11 +358,17 @@ state:
|
|
|
344
358
|
strategy: fork | share | stub
|
|
345
359
|
forked_name: <forked db name> # null when share / stub
|
|
346
360
|
connection_url_var: DATABASE_URL
|
|
361
|
+
reset_cmd: <shell command that returns this DB to the state of a fresh checkout>
|
|
362
|
+
# e.g. dropdb <forked> && createdb -T <template> <forked>
|
|
363
|
+
# rm -f <path>/db.sqlite3 && <migrate cmd>
|
|
364
|
+
# null when the engine / setup has no safe reset — callers must then re-provision
|
|
365
|
+
# instead of reusing this worktree across a migration boundary.
|
|
347
366
|
test_db:
|
|
348
367
|
engine: ...
|
|
349
368
|
strategy: fork | share
|
|
350
369
|
forked_name: ...
|
|
351
370
|
connection_url_var: TEST_DATABASE_URL
|
|
371
|
+
reset_cmd: <same idea, for the test DB> # null when not resettable
|
|
352
372
|
compose:
|
|
353
373
|
project_name: <repo>_<worktree-name>
|
|
354
374
|
network_strategy: per-worktree | shared-external | host
|
|
@@ -371,7 +391,9 @@ state:
|
|
|
371
391
|
# claude-hook → in-process (claude-code) Layer A hook only (file tools)
|
|
372
392
|
# claude-native-sandbox → in-process (claude-code) Layer A hook + Layer B OS sandbox
|
|
373
393
|
# null → tier=none (no guard wired)
|
|
374
|
-
deny: [<main-checkout-root>] # subtree(s) made read-only inside the cage
|
|
394
|
+
deny: [<main-checkout-root>, <worktree-root>] # subtree(s) made read-only inside the cage
|
|
395
|
+
# <worktree-root> is the dir that holds the worktrees — denying it blocks writes into
|
|
396
|
+
# SIBLING lanes when a pool is provisioned; the allow entry below re-opens only this one.
|
|
375
397
|
allow: [<worktree-path>, <main-checkout-root>/.git, <.vinta-ai-workflows-dir>] # writable exceptions
|
|
376
398
|
notes: |
|
|
377
399
|
<freeform — anything the user / agent should know>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Source-of-truth fragments for the **plan-execution skill family** that
|
|
4
4
|
`vinta-derive-skills` renders into a target project's `ai-tools/skills/`:
|
|
5
5
|
|
|
6
|
-
- `implement-plan` (conductor) — parse → classify → resolve `WORKROOT` →
|
|
6
|
+
- `implement-plan` (conductor) — parse → classify → build the dependency graph → resolve a `WORKROOT` per lane → scheduler loop (several phases at a time) → wave integration → track → report.
|
|
7
7
|
- `implement-phase` — compose prompt + pick model + spawn implementer (one phase).
|
|
8
8
|
- `review-phase` — three-layer review + fix loop (shared by all three conductors).
|
|
9
9
|
- `integrate-phase` — push + open PR via context file (commit-strategy-resolved).
|
|
@@ -31,12 +31,29 @@ Partials and shells are **not** `SKILL.md` files, so they legitimately keep `{{.
|
|
|
31
31
|
|
|
32
32
|
## The `WORKROOT` seam (why worktree branching is gone)
|
|
33
33
|
|
|
34
|
-
The conductor resolves three values **once** (see `partials/worktree-seam.md#WORKROOT_RESOLUTION`) and passes them to every sub-skill as data:
|
|
34
|
+
The conductor resolves three values **once per lane** (see `partials/worktree-seam.md#WORKROOT_RESOLUTION`) and passes them to every sub-skill as data:
|
|
35
35
|
|
|
36
36
|
| Value | `use_worktree = false` | `use_worktree = true` |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `WORKROOT` | `<main_checkout>` | `<worktree_path>` |
|
|
38
|
+
| `WORKROOT` | `<main_checkout>` | `<worktree_path>` — this lane's, when a pool is provisioned |
|
|
39
39
|
| `BASE_BRANCH` | `{{DEFAULT_BRANCH}}` | `<worktree_branch>` |
|
|
40
|
-
| `SANDBOX_TIER` | `none` | `enforced` \| `none` (probed by prepare-worktree) |
|
|
40
|
+
| `SANDBOX_TIER` | `none` | `enforced` \| `none` (probed by prepare-worktree, per lane) |
|
|
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
|
+
|
|
44
|
+
## The parallel-lanes seam (`partials/parallel-lanes.md`)
|
|
45
|
+
|
|
46
|
+
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:
|
|
47
|
+
|
|
48
|
+
| Block | Consumed by | What it owns |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `DAG_PARSE` | `implement-plan` Step 0 | graph build, wave derivation, cycle / unknown-id / file-overlap validation |
|
|
51
|
+
| `LANE_WORKTREE_POOL` | `implement-plan` Step 0.5 | pool sizing + provisioning, lane reuse + DB reset, teardown; the hard refusal when worktrees are unavailable |
|
|
52
|
+
| `LANE_TOPOLOGY` | `implement-plan` (linked, not included, from `integrate-phase`) | per-phase base branch from `depends_on`, `integ-{id}` merge bases, `wave-{N}` integration branches, merge-conflict handling |
|
|
53
|
+
| `LANE_SCHEDULER` | `implement-plan` Step 1 | the dispatch loop, tie-breaking, failure containment, the pause gate under concurrency |
|
|
54
|
+
| `TRACKING_DIR` | `implement-plan` | the `TRACKING_{plan-id}/` directory + its ownership rules |
|
|
55
|
+
| `SIBLING_LANE_ISOLATION` | `worktree-seam.md#STRAY_WRITE_CHECK` → `review-phase` | the stray-write guard extended across sibling lanes |
|
|
56
|
+
|
|
57
|
+
**Sequential execution is `max_parallel_lanes = 1`**, running the same blocks — there is no separate sequential code path to keep in sync.
|
|
58
|
+
|
|
59
|
+
**Why tracking is a directory.** Lanes commit on branches that later merge. One shared file would conflict at every wave merge for no reason. `run.md` is conductor-owned, `phase-{id}.md` is written only by the lane that ran that phase, on that phase's own branch — so no two branches ever touch the same path and the merges are clean by construction.
|
|
@@ -1,16 +1,43 @@
|
|
|
1
|
-
<!-- Partial: agent-models — resolves the `.vinta-ai-workflows.yaml` `agent_models` section (tier per role/task) to a concrete spawn model, and the mechanical-step delegation pattern. Blocks: TIER_RESOLVE (review-phase reviewer/fixer + implement-plan mechanical steps), MECHANICAL_DELEGATION (implement-plan worktree/integrate steps). The implementer model is NOT here — that stays plan-owned via model-pick. -->
|
|
1
|
+
<!-- Partial: agent-models — resolves the `.vinta-ai-workflows.yaml` `agent_models` section (tier per role/task) to a concrete spawn model, the reviewer's tier-above-the-author derivation, and the mechanical-step delegation pattern. Blocks: TIER_RESOLVE (review-phase reviewer/fixer + implement-plan mechanical steps), MECHANICAL_DELEGATION (implement-plan worktree/integrate steps). The implementer model is NOT here — that stays plan-owned via model-pick. -->
|
|
2
2
|
|
|
3
3
|
<!-- block-begin: TIER_RESOLVE -->
|
|
4
4
|
## Resolve an `agent_models` tier to a spawn model
|
|
5
5
|
|
|
6
6
|
`.vinta-ai-workflows.yaml` may carry an `agent_models` section mapping a role/task (`reviewer`, `fixer`, `worktree_prep`, `integrate`) to a **tier** (1–4) into the same table the per-phase implementer suggestion uses — [`ai-tools/skills/plan-feature/resources/ai-models.yaml`](../plan-feature/resources/ai-models.yaml). `agent_models` is the **project default** for these roles; for `reviewer` / `fixer`, a plan phase's optional `**Review models**:` line may override the tier for that one phase (the caller resolves that precedence and hands this block the effective tier). The mechanical steps (`worktree_prep`, `integrate`) are never plan-named. To turn a tier into the model a spawn actually uses:
|
|
7
7
|
|
|
8
|
-
1. Determine the **effective tier** for the role
|
|
8
|
+
1. Determine the **effective tier** for the role. For the mechanical steps it is simply `agent_models.<role>`. For `reviewer` / `fixer` there are now three sources, in this order:
|
|
9
|
+
1. a per-phase `**Review models**:` override the conductor passed;
|
|
10
|
+
2. **the cheapest reviewer on the plan's Crew table at or above the phase's tier** (see [Who reviews](#who-reviews-a-member-not-a-tier) below);
|
|
11
|
+
3. `agent_models.<role>`.
|
|
9
12
|
2. **No effective tier (override absent AND key unset, or the whole `agent_models` section absent) → do not force a model.** Spawn with the runtime's default model (today's behavior). Skip the rest.
|
|
10
13
|
3. Open [`ai-tools/skills/plan-feature/resources/ai-models.yaml`](../plan-feature/resources/ai-models.yaml), take that tier's `models`, **filter to the vendors the runtime actually exposes**, pick the cheapest/fastest survivor, and translate it to the runner's spawn form — the same resolution [implement-phase](../implement-phase/SKILL.md) runs for the implementer, only keyed by a config tier instead of a plan line.
|
|
11
14
|
4. `ai-models.yaml` missing, or the tier has no runtime-available vendor → fall back to the runtime default and surface the fallback once. Never hard-fail a phase over a model-selection miss.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
### Who reviews: a member, not a tier
|
|
17
|
+
|
|
18
|
+
**Reviewers are their own members on the plan's Crew table**, with `role: reviewer`, and they never take phases. That is what makes an agent reviewing its own work impossible rather than merely unlikely — and it replaces an earlier rule that resolved a reviewer *model* one tier above the author, which was a proxy for independence and failed in both directions: a phase covered by the top-tier implementer had nobody above it and fell back to being read at its own tier, and a tier says nothing about *who* once a member is a durable agent rather than a model id.
|
|
19
|
+
|
|
20
|
+
Resolve it from the roster: the **cheapest reviewer at or above the phase's tier**. A reviewer's tier is a floor in the same way an implementer's is — it reads work at or below its own capability, never above it.
|
|
21
|
+
|
|
22
|
+
Three consequences worth knowing:
|
|
23
|
+
|
|
24
|
+
- **A reviewer is claimed, not borrowed.** It has one session ledger, so two reviews running as the same reviewer would collide over it; a phase whose reviewer is busy waits. That cannot deadlock — reviewers never take phases, so the wait is always on a review already running.
|
|
25
|
+
- **A reviewer reads the lane it is reviewing**, uncommitted changes and all, so findings are fixed before the commit. It has no worktree of its own, and therefore keeps a session only when consecutive reviews land in the same lane.
|
|
26
|
+
- **No reviewer on the roster → fall through to `agent_models.reviewer`**, cold, one session per phase. That is what every plan did before, and the one thing a roster-less plan leaves on the table.
|
|
27
|
+
|
|
28
|
+
A plan with no **Crew** table skips this step entirely and resolves `agent_models.<role>` as it always did.
|
|
29
|
+
|
|
30
|
+
### What `fixer` still governs
|
|
31
|
+
|
|
32
|
+
`fixer` governs fewer rounds than it used to. A finding goes back to the phase's
|
|
33
|
+
own implementer — the same agent, in the same session — which fixes at the tier
|
|
34
|
+
of the crew member that took the phase
|
|
35
|
+
because it *is* that member; `agent_models.fixer` applies to the cold cases
|
|
36
|
+
only — a runtime that cannot continue a sub-agent, and the last round before
|
|
37
|
+
giving up, which is deliberately handed to an agent that has not seen the work.
|
|
38
|
+
See the fix loop in [review-phase](../review-phase/SKILL.md).
|
|
39
|
+
|
|
40
|
+
Record the **model actually used** in tracking, **and which of the three sources it came from** — a review that quietly fell through to the project default because the roster had nobody above the author is a fact worth being able to read later. For `reviewer` / `fixer`, alongside the review note; for the mechanical steps, in the phase's tracking row next to the branch/PR fields.
|
|
14
41
|
<!-- block-end: TIER_RESOLVE -->
|
|
15
42
|
|
|
16
43
|
<!-- block-begin: MECHANICAL_DELEGATION -->
|
package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/modular.md
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
<!-- Partial: commit-strategy / modular-commits. Folds in the former implement-plan-template-modular-substitutions.md. Blocks consumed by implement-phase (PER_PHASE_COMMIT) + integrate-phase (BRANCH_NAMING, PR_OPEN_TIMING) + conductor (CHECKLIST). All git calls use `git -C <WORKROOT>`;
|
|
1
|
+
<!-- Partial: commit-strategy / modular-commits. Folds in the former implement-plan-template-modular-substitutions.md. Blocks consumed by implement-phase (PER_PHASE_COMMIT) + integrate-phase (BRANCH_NAMING, PR_OPEN_TIMING) + conductor (CHECKLIST). All git calls use `git -C <WORKROOT>`; each phase branches off its own dependency-derived `<phase.base_branch>` — all resolved by the conductor (see worktree-seam.md#WORKROOT_RESOLUTION + parallel-lanes.md#LANE_TOPOLOGY). The rendering-guidance appendix at the bottom is read by derive-skills to resolve the {{MODULAR_*}} placeholders; it is NOT part of any shipped block. -->
|
|
2
2
|
|
|
3
3
|
<!-- block-begin: BRANCH_NAMING -->
|
|
4
4
|
Branch naming: `plan/{plan-id-kebab}` (one branch for the whole plan — no per-phase suffix).
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**Sequential run** (`max_parallel_lanes = 1`) — every phase commits straight onto the plan branch:
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
git -C <WORKROOT> checkout <BASE_BRANCH>
|
|
10
|
-
git -C <WORKROOT> checkout -b plan/{plan-id-kebab}
|
|
9
|
+
git -C <WORKROOT> checkout plan/{plan-id-kebab} # created from <BASE_BRANCH> on the first phase
|
|
11
10
|
# subagent's atomic unit commits land on this branch
|
|
12
11
|
git -C <WORKROOT> push -u origin plan/{plan-id-kebab}
|
|
13
12
|
```
|
|
14
13
|
|
|
15
|
-
**
|
|
14
|
+
**Parallel run** — one branch cannot take concurrent writers, so each lane commits its atomic units on a **lane branch** and the conductor merges those into the plan branch at each wave boundary:
|
|
16
15
|
|
|
17
16
|
```bash
|
|
18
|
-
git -C <WORKROOT> checkout
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
git -C <WORKROOT> checkout <phase.base_branch>
|
|
18
|
+
git -C <WORKROOT> checkout -b plan/{plan-id-kebab}/lane-{phase.id}
|
|
19
|
+
# subagent's atomic unit commits land on this lane branch
|
|
20
|
+
git -C <WORKROOT> push -u origin plan/{plan-id-kebab}/lane-{phase.id}
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
then, once every phase at that wave is green, in the integration worktree:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git -C <integ.workroot> checkout plan/{plan-id-kebab}
|
|
27
|
+
git -C <integ.workroot> merge --no-ff plan/{plan-id-kebab}/lane-{phase.id} # once per lane, in plan order
|
|
28
|
+
git -C <integ.workroot> push origin plan/{plan-id-kebab}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`--no-ff` is what preserves the atomic units: the plan branch keeps every individual unit commit, grouped by the merge commit that names its phase. `plan/{plan-id-kebab}` **is** the wave integration branch under this strategy — there is no separate `wave-{N}` branch. Reviewers still read the commit log top-to-bottom as a table of contents; parallel phases appear as adjacent groups rather than one flat sequence.
|
|
32
|
+
|
|
33
|
+
**Never squash a lane merge.** Squashing collapses the unit commits this whole strategy exists to preserve.
|
|
24
34
|
<!-- block-end: BRANCH_NAMING -->
|
|
25
35
|
|
|
26
36
|
<!-- block-begin: PER_PHASE_COMMIT -->
|
|
@@ -72,24 +82,27 @@ Tests for a unit belong **in the same commit** as that unit. Never commit tests
|
|
|
72
82
|
<!-- block-end: PER_PHASE_COMMIT -->
|
|
73
83
|
|
|
74
84
|
<!-- block-begin: PR_OPEN_TIMING -->
|
|
75
|
-
**PR opens once — after
|
|
85
|
+
**PR opens once — after the first phase passes review.** Subsequent phases land their atomic unit commits on the same plan branch (`plan/{plan-id-kebab}`) — directly when sequential, through a wave merge when parallel; the orchestrator re-runs [open-pr.sh](../open-pr-from-context/scripts/open-pr.sh) against the same plan-level prs-context file at `.vinta-ai-workflows/prs-context/{feature-kebab}/plan.md`. The script is idempotent for already-open PRs — it updates the body, appends new inline comments, and posts a `Phase {N} complete — pushed M commits` PR comment.
|
|
86
|
+
|
|
87
|
+
Under a parallel run, the PR comment goes up **after the wave merge**, not when the lane branch is pushed — before the merge those commits are not on the branch the PR tracks.
|
|
76
88
|
<!-- block-end: PR_OPEN_TIMING -->
|
|
77
89
|
|
|
78
90
|
<!-- block-begin: CHECKLIST -->
|
|
79
91
|
- [ ] Commit units listed upfront before any staging.
|
|
80
92
|
- [ ] Each commit covers exactly one logical unit (no "and" in commit messages).
|
|
81
93
|
- [ ] Tests landed in the same commit as the code they cover (never a separate test-only commit).
|
|
82
|
-
- [ ] All unit commits pushed to `plan/{plan-id-kebab}`
|
|
94
|
+
- [ ] All unit commits pushed at end of phase — to `plan/{plan-id-kebab}` when sequential, to `plan/{plan-id-kebab}/lane-{phase.id}` when parallel.
|
|
95
|
+
- [ ] Parallel runs only: lane branch merged into `plan/{plan-id-kebab}` with `--no-ff` at the wave boundary; never squashed.
|
|
83
96
|
<!-- block-end: CHECKLIST -->
|
|
84
97
|
|
|
85
98
|
<!-- Single-line values (derive-skills substitutes these into the shells directly):
|
|
86
99
|
BRANCH_PUSH_HEADING = Push to plan branch
|
|
87
|
-
BRANCH_NAMING_PATTERN_SUMMARY = plan branch (one branch for whole plan: `plan/{plan-id-kebab}`)
|
|
100
|
+
BRANCH_NAMING_PATTERN_SUMMARY = plan branch (one branch for whole plan: `plan/{plan-id-kebab}`; parallel runs add per-lane branches `plan/{plan-id-kebab}/lane-{phase-id}` merged in at each wave boundary)
|
|
88
101
|
PRS_CONTEXT_FILE_PATH = a single `.vinta-ai-workflows/prs-context/{feature-kebab}/plan.md` file (one PR per plan, not per phase)
|
|
89
102
|
TRACKING_BRANCH_FIELD = top-level `plan_branch:` field
|
|
90
103
|
TRACKING_PHASE_BRANCH_FIELD = (empty — no per-phase branch under modular)
|
|
91
|
-
FINAL_REPORT_BRANCH_SUMMARY = single plan branch `plan/{plan-id-kebab}` with commit log organized by phase
|
|
92
|
-
BRANCH_CHECKLIST_LINE = Plan branch updated with phase commits; pushed. -->
|
|
104
|
+
FINAL_REPORT_BRANCH_SUMMARY = single plan branch `plan/{plan-id-kebab}` with commit log organized by phase (parallel runs: plus the lane branches merged into it, per wave)
|
|
105
|
+
BRANCH_CHECKLIST_LINE = Plan branch updated with phase commits (directly, or via the lane branch's wave merge); pushed. -->
|
|
93
106
|
|
|
94
107
|
<!-- ===================================================================== -->
|
|
95
108
|
<!-- rendering-guidance (read by derive-skills; NOT shipped in any block) -->
|
package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
<!-- Partial: commit-strategy / stacked-branches. Blocks consumed by implement-phase (PER_PHASE_COMMIT) + integrate-phase (BRANCH_NAMING, PR_OPEN_TIMING) + conductor (CHECKLIST). All git calls use `git -C <WORKROOT>`;
|
|
1
|
+
<!-- Partial: commit-strategy / stacked-branches. Blocks consumed by implement-phase (PER_PHASE_COMMIT) + integrate-phase (BRANCH_NAMING, PR_OPEN_TIMING) + conductor (CHECKLIST). All git calls use `git -C <WORKROOT>`; each phase branches off its own dependency-derived `<phase.base_branch>` — all resolved by the conductor (see worktree-seam.md#WORKROOT_RESOLUTION + parallel-lanes.md#LANE_TOPOLOGY). -->
|
|
2
2
|
|
|
3
3
|
<!-- block-begin: BRANCH_NAMING -->
|
|
4
|
-
Branch naming: `plan/{plan-id-kebab}/phase-{phase.id}` (one branch + one PR per phase, stacked).
|
|
4
|
+
Branch naming: `plan/{plan-id-kebab}/phase-{phase.id}` (one branch + one PR per phase, stacked on its **dependencies** rather than on plan order).
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Each phase branches from `<phase.base_branch>` — the branch the conductor computed from that phase's `**Depends on**:` set (see [Lane branch topology](../implement-plan/SKILL.md#lane-branch-topology)): `<BASE_BRANCH>` for a phase with no dependencies, the single dependency's phase branch for one, `plan/{plan-id-kebab}/integ-{phase.id}` for several. The conductor creates the branch when it assigns the phase to a lane:
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
git -C <WORKROOT> checkout <
|
|
9
|
+
git -C <WORKROOT> checkout <phase.base_branch>
|
|
10
10
|
git -C <WORKROOT> checkout -b plan/{plan-id-kebab}/phase-{phase.id}
|
|
11
11
|
# subagent's commits land on this branch
|
|
12
12
|
git -C <WORKROOT> push -u origin plan/{plan-id-kebab}/phase-{phase.id}
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
A chain-shaped plan reproduces the classic stack exactly — each phase depends on the one before it, so `<phase.base_branch>` *is* the previous phase's branch. A plan with independent phases produces several stacks rooted at `<BASE_BRANCH>`, reunited by the wave integration branches.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
git -C <WORKROOT> checkout plan/{plan-id-kebab}/phase-{prev.id}
|
|
19
|
-
git -C <WORKROOT> checkout -b plan/{plan-id-kebab}/phase-{phase.id}
|
|
20
|
-
git -C <WORKROOT> push -u origin plan/{plan-id-kebab}/phase-{phase.id}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**PR base per phase** (the `base` field written into the prs-context frontmatter — this is what `gh pr create --base` / `glab mr create --target-branch` opens the PR against; getting it wrong makes every stacked PR target `<BASE_BRANCH>` instead of its parent phase):
|
|
17
|
+
**PR base per phase** (the `base` field written into the prs-context frontmatter — this is what `gh pr create --base` / `glab mr create --target-branch` opens the PR against; getting it wrong makes the PR diff include every upstream phase and the review unusable):
|
|
24
18
|
|
|
25
|
-
-
|
|
26
|
-
- **Subsequent phases** → `base = plan/{plan-id-kebab}/phase-{prev.id}` (the previous phase's branch, **not** `<BASE_BRANCH>`). The PR must open against its parent phase so the diff shows only this phase's changes and the stack reviews cleanly.
|
|
19
|
+
- `base = <phase.base_branch>`, always. Never `<BASE_BRANCH>` for a phase that has dependencies.
|
|
27
20
|
<!-- block-end: BRANCH_NAMING -->
|
|
28
21
|
|
|
29
22
|
<!-- block-begin: PER_PHASE_COMMIT -->
|
|
@@ -38,14 +31,15 @@ One PR per phase — the [Open PR via context file](#open-pr-via-context-file) s
|
|
|
38
31
|
<!-- block-end: PR_OPEN_TIMING -->
|
|
39
32
|
|
|
40
33
|
<!-- block-begin: CHECKLIST -->
|
|
41
|
-
|
|
34
|
+
- [ ] Phase branch created from `<phase.base_branch>` (dependency-derived), not from plan order.
|
|
35
|
+
- [ ] PR `base` in the prs-context frontmatter equals `<phase.base_branch>`.
|
|
42
36
|
<!-- block-end: CHECKLIST -->
|
|
43
37
|
|
|
44
38
|
<!-- Single-line values (derive-skills substitutes these into the shells directly):
|
|
45
39
|
BRANCH_PUSH_HEADING = Push stacked branch
|
|
46
|
-
BRANCH_NAMING_PATTERN_SUMMARY = branch naming pattern (default: `plan/{plan-id-kebab}/phase-{phase-id}`)
|
|
40
|
+
BRANCH_NAMING_PATTERN_SUMMARY = branch naming pattern (default: `plan/{plan-id-kebab}/phase-{phase-id}`, each based on its dependencies; wave integration branches `plan/{plan-id-kebab}/wave-{N}`)
|
|
47
41
|
PRS_CONTEXT_FILE_PATH = `.vinta-ai-workflows/prs-context/{feature-kebab}/phase-{phase.id}.md`
|
|
48
42
|
TRACKING_BRANCH_FIELD = (empty — per-phase branch lives inline under TRACKING_PHASE_BRANCH_FIELD)
|
|
49
43
|
TRACKING_PHASE_BRANCH_FIELD = , branch, base
|
|
50
|
-
FINAL_REPORT_BRANCH_SUMMARY = branches pushed (with bases,
|
|
51
|
-
BRANCH_CHECKLIST_LINE =
|
|
44
|
+
FINAL_REPORT_BRANCH_SUMMARY = branches pushed (with bases, grouped by wave)
|
|
45
|
+
BRANCH_CHECKLIST_LINE = Phase branch created from its dependency-derived base; pushed. -->
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Partial: implementer-prompt — the token-efficient per-phase prompt. FULL = the forward-implementation prompt used by implement-phase. INNER_OUTER_LOOP = the read→edit→inner→outer verification steps (1–6), reused verbatim by amend-plan's 4b (which appends its own amend-specific commit / no-push tail instead of the commit-strategy block). The {If run_options.use_worktree = true:} markers are runtime gates the agent reads at execution time; derive-skills strips
|
|
1
|
+
<!-- Partial: implementer-prompt — the token-efficient per-phase prompt. FULL = the forward-implementation prompt used by implement-phase. INNER_OUTER_LOOP = the read→edit→inner→outer verification steps (1–6), reused verbatim by amend-plan's 4b (which appends its own amend-specific commit / no-push tail instead of the commit-strategy block). The {If run_options.use_worktree = true:} and {If run_options.parallel_phases = true:} markers are runtime gates the agent reads at execution time; derive-skills strips both entirely when foundation_skills.prepare-worktree is disabled (use_worktree can only ever be false, and parallel execution requires a worktree). -->
|
|
2
2
|
|
|
3
3
|
<!-- block-begin: FULL -->
|
|
4
4
|
```
|
|
@@ -14,11 +14,18 @@ every lint / test / build / migrate call runs there.
|
|
|
14
14
|
`<WORKROOT>` is an isolated git worktree — do NOT touch the main checkout; its DB,
|
|
15
15
|
env, and compose stack are intentionally separated. See `<WORKROOT>/WORKTREE.md` for
|
|
16
16
|
what's forked vs shared (deps, dev DB, test DB, compose project name, env file).
|
|
17
|
-
{If run_options.sandbox_tier = enforced:} Writes
|
|
18
|
-
if you see `Operation not permitted` / `EROFS` on a write, you used a
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
{If run_options.sandbox_tier = enforced:} Writes outside this worktree are OS-blocked —
|
|
18
|
+
if you see `Operation not permitted` / `EROFS` on a write, you used a path outside
|
|
19
|
+
`<WORKROOT>` by mistake; redo it against this worktree path.
|
|
20
|
+
{If run_options.parallel_phases = true:}
|
|
21
|
+
Other phases of this plan are being implemented **right now**, in sibling worktrees
|
|
22
|
+
next to yours. Never read or write any path outside `<WORKROOT>` — a sibling's tree is
|
|
23
|
+
mid-edit and mid-test, and a write there corrupts someone else's phase. Anything you
|
|
24
|
+
need from another phase is either already in your base branch or is a dependency the
|
|
25
|
+
plan failed to declare — say so in your report rather than reaching for it.
|
|
26
|
+
Branch base for this phase: `<phase.base_branch>` — derived from this phase's
|
|
27
|
+
**Depends on** set, not from plan order. The orchestrator already created your phase
|
|
28
|
+
branch there; commit straight to it.
|
|
22
29
|
|
|
23
30
|
## Read first
|
|
24
31
|
1. AGENTS.md — repo conventions.
|
|
@@ -32,8 +39,11 @@ your phase branch there; commit straight to it.
|
|
|
32
39
|
Feature flag: `{flag-key}` — scope `{per-tenant|per-request}`, default `{false|true}`.
|
|
33
40
|
Wire reads + writes per the plan's **Guiding Decisions** entry. Off-flag path = byte-for-byte pre-feature behavior.
|
|
34
41
|
|
|
35
|
-
## What
|
|
36
|
-
{
|
|
42
|
+
## What your phase builds on
|
|
43
|
+
{The `phase-{id}.md` tracking summaries for this phase's transitive dependencies, in
|
|
44
|
+
wave order. No dependencies: "Nothing yet — this phase starts from `<BASE_BRANCH>`."
|
|
45
|
+
Sibling phases running in parallel are deliberately NOT listed: their work is not in
|
|
46
|
+
your base branch and you must not code against it.}
|
|
37
47
|
|
|
38
48
|
## Your tasks (Phase {id} only)
|
|
39
49
|
{phase.body verbatim, including Goal / Spec use-case / Feature flag / Changes / Tests / Acceptance lines}
|
|
@@ -57,7 +67,7 @@ Project skills available: {{PROJECT_SKILLS_LIST}}
|
|
|
57
67
|
- Anything you couldn't do (with explanation).
|
|
58
68
|
```
|
|
59
69
|
|
|
60
|
-
**Don't** dump the full plan into every prompt.
|
|
70
|
+
**Don't** dump the full plan into every prompt. Dependency-closure tracking summaries replace prior phases as context. Always include the **Goals + Non-goals** and **Guiding Decisions** sections plus the relevant **Data Model Changes** subsection — load-bearing decisions; phases reach back frequently.
|
|
61
71
|
<!-- block-end: FULL -->
|
|
62
72
|
|
|
63
73
|
<!-- block-begin: INNER_OUTER_LOOP -->
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
<!-- Partial: model-pick — the plan-owns-model-selection rules. Included by implement-phase; referenced (linked, not included) by amend-plan's Tier-4 rule. -->
|
|
2
2
|
<!-- block-begin: MODEL_PICK -->
|
|
3
|
-
## Pick the model from the
|
|
3
|
+
## Pick the model from the phase's crew assignment
|
|
4
4
|
|
|
5
|
-
**The plan owns the *implementer* model — this skill does not re-derive tiers and doesn't assume a vendor.**
|
|
5
|
+
**The plan owns the *implementer* model — this skill does not re-derive tiers and doesn't assume a vendor.** It owns it as a **roster**: the plan's **Crew** table names the agents it is staffed with and the tier each is staffed at, and every phase carries an `**Assigned to**:` line naming one of them.
|
|
6
6
|
|
|
7
7
|
Pick:
|
|
8
8
|
|
|
9
|
-
1. Read the line,
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
14
|
-
6.
|
|
9
|
+
1. Read the phase's `**Assigned to**:` line for the crew id, then that id's row in the plan's **Crew** table for the tier.
|
|
10
|
+
2. Open the tier in [`ai-tools/skills/plan-feature/resources/ai-models.yaml`](../plan-feature/resources/ai-models.yaml) and take its models.
|
|
11
|
+
3. **Filter to what's actually available in the runtime.** Different harnesses expose different sets.
|
|
12
|
+
4. From the survivors, **pick the cheapest / fastest** the runner can use, and translate it to whatever form the runner's spawning tool expects.
|
|
13
|
+
5. Tier with no runtime-available vendor → step one tier up and say so once. Never hard-fail a phase over a model-selection miss.
|
|
14
|
+
6. `**Assigned to**:` missing or naming an agent the **Crew** table does not list → **ask the user**. Don't silently re-derive a tier from the phase body; the roster is the plan's arithmetic about how many agents this feature needs, and inventing a member changes it.
|
|
15
|
+
|
|
16
|
+
**A legacy plan carries `**Suggested AI model**:` and no Crew table.** Read the tier straight off that line and continue — same resolution, one less indirection. Don't invent a roster for it.
|
|
17
|
+
|
|
18
|
+
### Reuse the agent the plan staffed
|
|
19
|
+
|
|
20
|
+
**A crew member is an agent, and it is still alive.** If this member has already taken a phase on this run, continue that sub-agent rather than spawning a new one: it is standing in the same worktree — a member keeps one for the whole run — and it already knows where this codebase keeps things, how its suite is run and what its conventions are. Rediscovering that is most of what a cold agent's first turn costs.
|
|
21
|
+
|
|
22
|
+
Because it is new work, the continuation gets the phase's **full brief**, not a delta. Precede it with the re-orientation described in [Re-orienting a member after the reset](../implement-plan/SKILL.md#re-orienting-a-member-after-the-reset): same agent and same directory, whether the previous phase's work is in this tree, and which files differ from what it last saw.
|
|
23
|
+
|
|
24
|
+
Start cold instead when any of these hold:
|
|
25
|
+
|
|
26
|
+
- the member has taken no phase yet on this run;
|
|
27
|
+
- **their previous phase failed** — that session is the context that failed with it;
|
|
28
|
+
- the runtime cannot continue a finished sub-agent at all.
|
|
29
|
+
|
|
30
|
+
Record which of those applied, so a phase that was unexpectedly slow can be read later without guessing.
|
|
15
31
|
|
|
16
32
|
**Retry escalation (no user prompt):** the picked model fails on a clear capability gap → step **one tier up** and retry once. After Tier 4 fails, STOP. Update tracking with `❌`, post the agent's report to the user, ask how to proceed.
|
|
17
33
|
|
|
18
|
-
Record the **model actually used**
|
|
34
|
+
Record the **model actually used**, the **crew member** it came from, and **whether that member is the one the plan assigned** — a phase run by a covering peer is the difference between a run that cost what the plan said and one that did not.
|
|
19
35
|
<!-- block-end: MODEL_PICK -->
|