vinta-ai-workflows 0.2.0-alpha6 → 0.2.0
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 +59 -1
- package/README.md +42 -25
- package/package.json +1 -1
- package/schemas/vinta-ai-workflows-config.v1.schema.json +31 -0
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +11 -0
- package/skills/vinta-derive-skills/SKILL.md +8 -2
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +30 -5
- package/skills/vinta-derive-skills/resources/plan-execution/partials/agent-models.md +29 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +1 -1
- package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +2 -2
- package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +1 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +2 -2
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +20 -11
- package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,65 @@ All notable changes to `vinta-ai-workflows` are documented in this file.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
6
6
|
the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.2.0] —
|
|
8
|
+
## [0.2.0] — 2026-07-15
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Project-wide model selection for the review sub-agents and mechanical
|
|
13
|
+
steps, via a new optional `agent_models` section in
|
|
14
|
+
`.vinta-ai-workflows.yaml`.** Until now only the per-phase *implementer*
|
|
15
|
+
model was configurable (each plan phase's `**Suggested AI model**:` line);
|
|
16
|
+
the reviewer, the fixer, and the mechanical steps (worktree prep, opening
|
|
17
|
+
the PR / integrate) always inherited the runtime default. `agent_models`
|
|
18
|
+
maps each of `reviewer` / `fixer` / `worktree_prep` / `integrate` to a
|
|
19
|
+
**tier (1–4)** into the same `plan-feature/resources/ai-models.yaml` table
|
|
20
|
+
the implementer uses — so no model IDs are hard-coded and the nightly
|
|
21
|
+
`check-ai-models` job keeps them fresh. Behavior:
|
|
22
|
+
- `review-phase` spawns the reviewer at `agent_models.reviewer` and the
|
|
23
|
+
fixer at `agent_models.fixer` **as the project default** — a plan phase
|
|
24
|
+
can override either for that phase via an optional `**Review models**:`
|
|
25
|
+
line (per-phase override wins → else `agent_models` → else runtime
|
|
26
|
+
default). Lets a plan raise the review model on its few critical /
|
|
27
|
+
high-blast-radius phases without changing the project-wide setting.
|
|
28
|
+
- `implement-plan` **delegates** the mechanical steps to a cheap subagent
|
|
29
|
+
when `agent_models.worktree_prep` / `agent_models.integrate` is set —
|
|
30
|
+
provisioning the worktree and pushing + opening the PR run on the
|
|
31
|
+
configured model instead of the conductor's (usually pricier) session.
|
|
32
|
+
The PR-context file + `open-pr.sh` stays the only PR-creation path.
|
|
33
|
+
- `plan-feature` phases always pick the implementer model
|
|
34
|
+
(`**Suggested AI model**:`) and MAY add an optional `**Review models**:`
|
|
35
|
+
line to override the reviewer / fixer tier for that phase; the mechanical
|
|
36
|
+
models stay config-only.
|
|
37
|
+
Every key is optional and backward-compatible: an unset key (or the whole
|
|
38
|
+
section absent) means that spawn keeps using the runtime default.
|
|
39
|
+
**Consumers**: re-sync to pick up the `agent_models` bootstrap question and
|
|
40
|
+
the updated plan-execution skills; existing configs are unaffected until
|
|
41
|
+
they add the section.
|
|
42
|
+
|
|
43
|
+
<!-- pre-release: 0.2.0-alpha7 on 2026-07-13 -->
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **E2E tests are now opt-in in both `plan-feature` and `implement-plan`,
|
|
48
|
+
because they make the implementation run a LOT slower.** Previously, for
|
|
49
|
+
e2e-enabled projects, `plan-feature` *required* a happy-path Playwright spec on
|
|
50
|
+
every phase that reached the browser, and `implement-plan` ran those specs in
|
|
51
|
+
every phase's outer gate + review layers — browser boot, seeded data, and
|
|
52
|
+
screenshot capture on each phase. Now:
|
|
53
|
+
- `plan-feature` gained a Step 0 (group I) **E2E coverage** question that
|
|
54
|
+
**defaults to NO**. When not opted in, phases ship unit + integration tests
|
|
55
|
+
only and carry no e2e spec, `QA_USE_CASES.md`, or `pr-screenshots/`
|
|
56
|
+
references; e2e can still be added later per-flow via `add-e2e-test`.
|
|
57
|
+
- `implement-plan` gained a Step 0 question **(e) Run E2E tests this run?**
|
|
58
|
+
(`run_options.run_e2e`, **default off**). Even when a plan carries e2e specs,
|
|
59
|
+
the outer-gate e2e step, the Layer 1 verification note, the Layer 2
|
|
60
|
+
walkthrough check, and the implementer report field only run when the user
|
|
61
|
+
opts in for that run. The shared review/debug e2e gate follows the same
|
|
62
|
+
`run_options.run_e2e` flag, so standalone `systematic-debugging` /
|
|
63
|
+
`amend-plan` runs skip e2e unless it is set.
|
|
64
|
+
**Consumers**: re-sync to pick up the opt-in questions. E2E-disabled projects
|
|
65
|
+
are unaffected (the e2e regions strip out as before).
|
|
66
|
+
|
|
9
67
|
|
|
10
68
|
<!-- pre-release: 0.2.0-alpha6 on 2026-07-13 -->
|
|
11
69
|
|
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Bootstrap AI tooling — AGENTS.md, sub-agents, project skills, multi-vendor wiring — into any project, then get out of the way.
|
|
4
4
|
|
|
5
|
-
Distributed as
|
|
5
|
+
Distributed as an npm package (`vinta-ai-workflows`) with a CLI that installs / updates / uninstalls a set of one-shot bootstrap skills into the project's vendor-specific skill directories (Claude Code, Codex, Cursor, VS Code + GitHub Copilot).
|
|
6
6
|
|
|
7
7
|
## Quick start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
# 1. Add the package to the target project.
|
|
11
11
|
cd ~/code/my-project
|
|
12
|
-
npm install -D
|
|
12
|
+
npm install -D vinta-ai-workflows
|
|
13
13
|
|
|
14
14
|
# 2. Install the bootstrap skills for your AI tool.
|
|
15
15
|
npx vinta-ai-workflows install --tool claude-code
|
|
@@ -28,7 +28,7 @@ npx vinta-ai-workflows uninstall --tool claude-code
|
|
|
28
28
|
npm uninstall vinta-ai-workflows
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Pin a version for determinism with `npm install -D vinta-ai-workflows@0.2.0`. Prerequisites: Node ≥ 18.
|
|
32
32
|
|
|
33
33
|
## Why this is useful
|
|
34
34
|
|
|
@@ -208,10 +208,22 @@ Standalone re-invocation works at every step — kick `create-spec` again when t
|
|
|
208
208
|
|
|
209
209
|
Quick inventory of what `vinta-bootstrap-ai-tools` writes into your repo's `ai-tools/` layout (and exposes via per-vendor wiring at `.claude/skills/`, `.agents/skills/`, etc.). Two disclaimers up front:
|
|
210
210
|
|
|
211
|
-
> **Optional foundation skills are gated by the bootstrap interview.** `systematic-debugging`, `add-e2e-test`, `add-env-var`,
|
|
211
|
+
> **Optional foundation skills are gated by the bootstrap interview.** `systematic-debugging`, `add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`, `thermo-nuclear-code-quality-review`, and `handoff-to-client` ship **only** if the user says yes during `vinta-bootstrap-ai-tools` Step 0 (or later opts in via `vinta-sync-ai-tools`). They are not in every bootstrapped project. The opt-in is recorded in `.vinta-ai-workflows.yaml` under `foundation_skills.*.enabled` and is sticky across syncs.
|
|
212
212
|
>
|
|
213
213
|
> **Stack-specific skills and sub-agents are user-supplied.** This package ships **detection signals + category lists** per stack ([resources/stacks/](skills/vinta-bootstrap-ai-tools/resources/stacks/) — Django, Medplum, Next.js App Router, Python package, TypeScript monorepo), not the bodies. When a stack is detected, the orchestrator asks the user for a path / URL to their team's existing template. If no template exists, the category is recorded as a gap in the final summary, not auto-drafted. Bodies are project- and team-specific; one shared template doesn't fit every team's conventions.
|
|
214
214
|
|
|
215
|
+
### New in the 0.2.0 releases
|
|
216
|
+
|
|
217
|
+
The 0.2.0 alpha line adds five foundation skills and restructures plan execution. Two ship in every bootstrapped project, three are opt-in:
|
|
218
|
+
|
|
219
|
+
- **`handoff` (always)** — writes a session-continuation doc to `.vinta-ai-workflows/handoffs/` so a fresh session, a different agent, or a teammate can resume in-flight work without re-deriving context. Write mode gathers the goal, what's verified vs. still assumed, decisions and the alternatives rejected, landmines, and the single concrete next step **from the repo, never from memory**. Resume mode reads a handoff and re-checks its claims against the repo before trusting them.
|
|
220
|
+
- **`deslop-comments` (always)** — rewrites the comments and doc blocks a task touched into Simple English, stripping AI-slop vocabulary and negative framing. Comment-only: no renames, no behavior change. It's wired into the review flow — `review-phase` Layer 2 gained a comment-hygiene check and dispatches a `fixer` to run this skill on any comment-slop finding — and is also invokable standalone ("deslop these comments").
|
|
221
|
+
- **`prepare-worktree` (opt-in)** — provisions a fully-runnable git worktree for parallel plan work so a long-running plan can build, test, migrate, and hit databases without disturbing the main checkout. It reads the plan plus `.gitignore` / manifests / env / docker config and decides per ignored path whether to symlink, copy, or fork (dev DB, env files, compose project name). It also ships an OS-level write-guard — `sandbox-run.sh` (`sandbox-exec` / `bwrap`) for subprocess runtimes and a `PreToolUse` hook for Claude Code's in-process subagents — so a stray write back to the main checkout fails at the kernel layer instead of relying on a cooperative "stay in the worktree" instruction. `implement-plan` Step 0 question (c) opts a run into it.
|
|
222
|
+
- **`thermo-nuclear-code-quality-review` (opt-in)** — a deliberately harsh, on-demand structural-maintainability audit of a diff (abstraction quality, giant files, spaghetti-condition growth) that hunts for "code-judo" reframes collapsing whole branches / helpers / modes / layers rather than polishing them. Read-only — it reports findings and hands each fix to a `fixer`. `review-phase` Layer 3 now applies a condensed version of this lens on every phase and escalates to the full audit only when a phase touches core architecture, crosses ~1,000 lines, or surfaces a structural smell too big to fix inline.
|
|
223
|
+
- **`handoff-to-client` (opt-in, API-only repos)** — generates a self-contained markdown document for the client teams consuming the repo's API: every endpoint / operation added, changed, deprecated, or removed on the current branch vs. the default branch, with request/response shapes derived from the code, auth + error changes, breaking-change flags judged from the strictest plausible client, one realistic example per operation, and per-platform migration notes. Template-rendered from `skills.handoff-to-client.*` config (required `client_platforms`, plus `api_style` / `api_spec_path` / `output_dir`).
|
|
224
|
+
|
|
225
|
+
Alongside the new skills, **`implement-plan` was decomposed into a modular plan-execution unit** — a thin conductor plus three co-shipped single-purpose sub-skills (`implement-phase`, `review-phase`, `integrate-phase`). `review-phase` is now the one review implementation shared by `implement-plan`, `amend-plan`, and `systematic-debugging`. The behavior is unchanged; the split is for reviewability and determinism. Two more knobs land in `.vinta-ai-workflows.yaml`: **`agent_models`** (set a tier per `reviewer` / `fixer` / `worktree_prep` / `integrate` so the review sub-agents and mechanical steps aren't stuck on the runtime default), and **E2E tests are now opt-in** in both `plan-feature` and `implement-plan` (they make runs materially slower; default off, re-enable per-run or per-project).
|
|
226
|
+
|
|
215
227
|
### Foundation skills (project-agnostic)
|
|
216
228
|
|
|
217
229
|
Land at `ai-tools/skills/<name>/SKILL.md`. Always-on unless flagged optional.
|
|
@@ -227,10 +239,15 @@ Land at `ai-tools/skills/<name>/SKILL.md`. Always-on unless flagged optional.
|
|
|
227
239
|
| `review-phase` | always (unit) | Plan-execution sub-skill: the three-layer review (mechanical / plan-compliance / independent reviewer) + fix loop. Shared by `implement-plan`, `amend-plan`, and `systematic-debugging`. |
|
|
228
240
|
| `integrate-phase` | always (unit) | Plan-execution sub-skill: push the reviewed phase along the project's commit strategy + open the PR via context file. Under `commit_strategy = ask`, ships as `integrate-phase-stacked` + `integrate-phase-modular`. |
|
|
229
241
|
| `amend-plan` | always (generated) | History-rewriting companion to `implement-plan` — revises in-flight plans, amends prior-phase commits, force-pushes, rebases stacked downstream branches. Reuses `review-phase` + `implement-phase`'s shared prompt loop. |
|
|
242
|
+
| [`handoff`](skills/vinta-derive-skills/resources/foundation-skills/handoff/SKILL.md) | always | Session-continuation handoff doc → `.vinta-ai-workflows/handoffs/`. **Write** captures goal, verified-vs-unverified state, decisions + rejected alternatives, landmines, and the single next step — gathered from the repo, never memory. **Resume** verifies a handoff's claims against the repo before continuing from its next step. |
|
|
243
|
+
| [`deslop-comments`](skills/vinta-derive-skills/resources/foundation-skills/deslop-comments/SKILL.md) | always | Rewrite comments + doc blocks touched during a task into Simple English (strips AI-slop vocabulary + negative framing; comment-only — no renames, no behavior change). `review-phase` Layer 2 depends on it; also invokable standalone. |
|
|
230
244
|
| `systematic-debugging` | **opt-in** | Root-cause-first debugging with project-specific repro commands + MCP evidence-gathering (error tracking, traces, logs, metrics, alerts). Renders from a catalogue of observability MCP servers the user declares. |
|
|
231
245
|
| `add-e2e-test` | **opt-in** | Add an e2e test. Body covers e2e framework, page-object pattern, auth/storage-state, seed helpers, tenant scoping, screenshot conventions. |
|
|
232
246
|
| `add-env-var` | **opt-in** | Propagate a new env var through every layer (`.env.example`, build tool envPrefix, build cache hash, app config, AGENTS.md, CI, deploy injection). |
|
|
233
247
|
| [`add-one-off-script`](skills/vinta-derive-skills/resources/foundation-skills/add-one-off-script/SKILL.md) | **opt-in** | Author one-off operational scripts (backfills, cleanups, ad-hoc fixes). Ships a `BaseOneOffScript` class (Python + TS) enforcing dry-run default, idempotency, batched DB ops, segmented CSV backups, signal-safe interruption, multi-sink logging. |
|
|
248
|
+
| [`prepare-worktree`](skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md) | **opt-in** | Provision a fully-runnable git worktree for parallel plan work — reads the plan + `.gitignore` + manifests + env / docker config and decides per ignored path whether to **symlink / copy / fork** (dev DB, env files, compose project name). Ships an OS-level write-guard (`sandbox-run.sh` + a Claude Code `PreToolUse` hook) so stray writes to the main checkout fail at the kernel layer. `implement-plan` Step 0 question (c) opts a run into it. |
|
|
249
|
+
| [`thermo-nuclear-code-quality-review`](skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md) | **opt-in** | Deliberately harsh structural-maintainability audit of a diff — abstraction quality, giant files, spaghetti-condition growth — hunting "code-judo" reframes that collapse whole branches / helpers / modes / layers. Read-only; hands each fix to `fixer`. `review-phase` Layer 3 runs a condensed lens every phase and escalates to this full audit on core-architecture / ~1000-line / structurally-smelly phases. |
|
|
250
|
+
| `handoff-to-client` | **opt-in** | (API-only repos) Generate a self-contained API-change handoff doc for the client teams consuming the repo's API — every endpoint / operation added / changed / deprecated / removed vs the default branch, with request/response shapes, auth + error changes, breaking-change flags, one example per operation, and per-platform migration notes. Template-rendered from `skills.handoff-to-client.*` config (client platforms, API style, spec path). |
|
|
234
251
|
|
|
235
252
|
### Foundation sub-agents (project-agnostic)
|
|
236
253
|
|
|
@@ -251,7 +268,7 @@ Bootstrap is a snapshot. `vinta-ai-workflows` keeps shipping — new foundation
|
|
|
251
268
|
### The flow at a glance
|
|
252
269
|
|
|
253
270
|
```bash
|
|
254
|
-
# 1. Pull the new package version
|
|
271
|
+
# 1. Pull the new package version.
|
|
255
272
|
npm update vinta-ai-workflows
|
|
256
273
|
|
|
257
274
|
# 2. Run the sync skill from your AI tool.
|
|
@@ -314,48 +331,48 @@ To bring a previously-bootstrapped project up to date with a newer package versi
|
|
|
314
331
|
|
|
315
332
|
## Install variants
|
|
316
333
|
|
|
317
|
-
The package is
|
|
318
|
-
|
|
319
|
-
### A. From git+ssh (no registry needed)
|
|
334
|
+
The package is published to the public npm registry. Three install paths.
|
|
320
335
|
|
|
321
|
-
|
|
336
|
+
### A. From the npm registry (default)
|
|
322
337
|
|
|
323
338
|
```bash
|
|
324
339
|
# inside the target project
|
|
325
|
-
npm install -D
|
|
326
|
-
pnpm add -D
|
|
327
|
-
yarn add -D
|
|
328
|
-
bun add -d
|
|
340
|
+
npm install -D vinta-ai-workflows
|
|
341
|
+
pnpm add -D vinta-ai-workflows
|
|
342
|
+
yarn add -D vinta-ai-workflows
|
|
343
|
+
bun add -d vinta-ai-workflows
|
|
329
344
|
```
|
|
330
345
|
|
|
331
|
-
Pin a
|
|
346
|
+
Pin a version when you want determinism:
|
|
332
347
|
|
|
333
348
|
```bash
|
|
334
|
-
npm install -D
|
|
349
|
+
npm install -D vinta-ai-workflows@0.2.0
|
|
335
350
|
```
|
|
336
351
|
|
|
337
|
-
### B. From
|
|
352
|
+
### B. From git+ssh (an unreleased ref)
|
|
338
353
|
|
|
339
|
-
|
|
354
|
+
Use this to install a branch or commit that isn't published yet — each developer's GitHub SSH key needs read access to the repo:
|
|
340
355
|
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
|
|
356
|
+
```bash
|
|
357
|
+
# inside the target project
|
|
358
|
+
npm install -D git+ssh://git@github.com:vintasoftware/vinta-ai-workflows.git
|
|
359
|
+
pnpm add -D git+ssh://git@github.com:vintasoftware/vinta-ai-workflows.git
|
|
360
|
+
yarn add -D git+ssh://git@github.com:vintasoftware/vinta-ai-workflows.git
|
|
361
|
+
bun add -d git+ssh://git@github.com:vintasoftware/vinta-ai-workflows.git
|
|
344
362
|
```
|
|
345
363
|
|
|
346
|
-
|
|
364
|
+
Pin a tag/commit when you want determinism:
|
|
347
365
|
|
|
348
366
|
```bash
|
|
349
|
-
npm install -D vinta-ai-workflows
|
|
367
|
+
npm install -D git+ssh://git@github.com:vintasoftware/vinta-ai-workflows.git#0.1.0
|
|
350
368
|
```
|
|
351
369
|
|
|
352
|
-
### One-shot via npx (no install)
|
|
370
|
+
### C. One-shot via npx (no install)
|
|
353
371
|
|
|
354
372
|
If you don't want the dep tracked in `package.json`:
|
|
355
373
|
|
|
356
374
|
```bash
|
|
357
|
-
npx -y -p
|
|
358
|
-
vinta-ai-workflows install --tool all
|
|
375
|
+
npx -y -p vinta-ai-workflows vinta-ai-workflows install --tool all
|
|
359
376
|
```
|
|
360
377
|
|
|
361
378
|
### Picking a tool
|
package/package.json
CHANGED
|
@@ -245,6 +245,37 @@
|
|
|
245
245
|
"fixer": { "type": "string", "enum": ["enabled", "disabled"] }
|
|
246
246
|
}
|
|
247
247
|
},
|
|
248
|
+
"agent_models": {
|
|
249
|
+
"description": "Per-role / per-task model selection for the plan-execution family, expressed as **tiers** (1–4) into the same table `plan-feature` uses — `ai-tools/skills/plan-feature/resources/ai-models.yaml`. Each consuming skill resolves a tier to a concrete model at runtime: take that tier's `models`, filter to the vendors the runtime exposes, pick the cheapest survivor. This keeps the config free of hard-coded model IDs (which go stale) — the nightly `check-ai-models` job keeps the tier table fresh. Every key is optional; an unset key (or the whole section absent) means the spawn uses the runtime's default model, i.e. today's behavior. These are the **project defaults**: the `reviewer` / `fixer` tiers here can be overridden per phase by a plan's optional `**Review models**:` line (a phase override wins over the value here for that phase only — see `plan-feature`). The per-phase implementer model is NOT here — that stays owned by each plan phase's `**Suggested AI model**:` line. The mechanical steps (`worktree_prep`, `integrate`) are never plan-named.",
|
|
250
|
+
"type": "object",
|
|
251
|
+
"additionalProperties": false,
|
|
252
|
+
"properties": {
|
|
253
|
+
"reviewer": {
|
|
254
|
+
"description": "Default tier for the independent reviewer subagent spawned by `review-phase` (Layer 3). Reviewing wants a capable model — Tier 3 is the usual default. A plan phase's `**Review models**:` line can override this for that phase. Unset (and no phase override) → runtime default.",
|
|
255
|
+
"type": "integer",
|
|
256
|
+
"minimum": 1,
|
|
257
|
+
"maximum": 4
|
|
258
|
+
},
|
|
259
|
+
"fixer": {
|
|
260
|
+
"description": "Default tier for the fixer subagent spawned by `review-phase`'s fix loop to apply one reviewer finding. Narrow, well-specified edits — Tier 2 is the usual default. A plan phase's `**Review models**:` line can override this for that phase. Unset (and no phase override) → runtime default.",
|
|
261
|
+
"type": "integer",
|
|
262
|
+
"minimum": 1,
|
|
263
|
+
"maximum": 4
|
|
264
|
+
},
|
|
265
|
+
"worktree_prep": {
|
|
266
|
+
"description": "Tier for the mechanical worktree-provisioning step (`prepare-worktree`), which `implement-plan` / `amend-plan` run once at the start of a worktree-enabled run. When set, the conductor delegates the step to a subagent at this tier instead of running it inline in its own (usually pricier) session. Mechanical, precedent-driven work — Tier 1 is the usual default. Unset → conductor runs it inline. Irrelevant when `foundation_skills.prepare-worktree` is `disabled`.",
|
|
267
|
+
"type": "integer",
|
|
268
|
+
"minimum": 1,
|
|
269
|
+
"maximum": 4
|
|
270
|
+
},
|
|
271
|
+
"integrate": {
|
|
272
|
+
"description": "Tier for the mechanical integrate step (`integrate-phase`: push the phase branch + open/update the PR via the bundled `open-pr.sh`) that `implement-plan` runs once per phase. When set, the conductor delegates the step to a subagent at this tier instead of running it inline. Mechanical git + PR-context work — Tier 1 is the usual default. Unset → conductor runs it inline. The PR-context file + `open-pr.sh` remains the only PR-creation path regardless of who runs the step.",
|
|
273
|
+
"type": "integer",
|
|
274
|
+
"minimum": 1,
|
|
275
|
+
"maximum": 4
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
248
279
|
"stacks": {
|
|
249
280
|
"description": "Stack templates applied at bootstrap (e.g. `medplum`, `django`, `nextjs-app-router`).",
|
|
250
281
|
"type": "array",
|
|
@@ -81,6 +81,8 @@ These bleed across sub-skills, so capture once now:
|
|
|
81
81
|
|
|
82
82
|
Lands in `.vinta-ai-workflows.yaml` `policies.commit_strategy`. Drives the `implement-plan` template's branch / commit / PR-opening flow + the `amend-plan` skill's supported-strategies check.
|
|
83
83
|
|
|
84
|
+
9. **Agent model tiers (optional).** The plan-execution family picks the **implementer** model per phase from the plan itself, but the **reviewer**, **fixer**, and the mechanical steps (**worktree prep**, **opening the PR / integrate**) have no model input unless the project sets one. Offer to pin each to a **tier** (1–4) into the same table `plan-feature` uses — `ai-tools/skills/plan-feature/resources/ai-models.yaml` — so review runs on a capable model while cheap mechanical work runs on a cheap one. `AskUserQuestion`: `Use recommended defaults (reviewer 3, fixer 2, worktree prep 1, integrate 1)`, `Customize each`, `Leave unset — every spawn uses the runtime default`. On `Customize each`, ask a per-role tier (1–4) for `reviewer` / `fixer` / `worktree_prep` / `integrate`. Land the chosen tiers in `.vinta-ai-workflows.yaml` `agent_models`; omit any role the user leaves unset (unset → runtime default). Recommend the defaults for most teams; recommend `Leave unset` only when the runtime exposes a single model anyway. Note `worktree_prep` is a no-op unless `prepare-worktree` is enabled, and setting `worktree_prep` / `integrate` makes `implement-plan` delegate those steps to a cheap subagent instead of running them inline.
|
|
85
|
+
|
|
84
86
|
### D. Optional foundation skills
|
|
85
87
|
|
|
86
88
|
Seven skills are part of the foundation set but aren't always needed. Ask explicitly:
|
|
@@ -270,6 +272,15 @@ foundation_agents:
|
|
|
270
272
|
reviewer: enabled
|
|
271
273
|
fixer: enabled
|
|
272
274
|
|
|
275
|
+
# Only emit the roles the user chose in the "Agent model tiers" question (C.9).
|
|
276
|
+
# Omit the whole block on "Leave unset"; omit any individual role left unset.
|
|
277
|
+
# Each value is a tier (1–4) into ai-tools/skills/plan-feature/resources/ai-models.yaml.
|
|
278
|
+
agent_models:
|
|
279
|
+
reviewer: <C.9 → reviewer tier, default 3>
|
|
280
|
+
fixer: <C.9 → fixer tier, default 2>
|
|
281
|
+
worktree_prep: <C.9 → worktree_prep tier, default 1; omit when prepare-worktree disabled>
|
|
282
|
+
integrate: <C.9 → integrate tier, default 1>
|
|
283
|
+
|
|
273
284
|
stacks: <Stack detection → matched stacks>
|
|
274
285
|
stack_specialist_agents: <empty unless user supplied templates>
|
|
275
286
|
|
|
@@ -123,8 +123,14 @@ The remaining per-shell placeholders:
|
|
|
123
123
|
| `{{NEW_TEST_CMD_PATTERN}}` | derived | `pnpm --filter <pkg> test -- <new-test-path>` or `pytest path/to/new_test.py -x` |
|
|
124
124
|
| `{{SCOPED_TEST_PATTERN}}` | derived | `pnpm test:patient`, `pytest tests/{unit,integration}/{app}/` |
|
|
125
125
|
| `{{E2E_BLOCK}}` | populated only if e2e selected — see C below | block describing e2e command + screenshot copy |
|
|
126
|
-
| `{{E2E_OUTER_GATE_LINE}}` | depends on e2e selected | ` c. **E2E
|
|
127
|
-
| `{{E2E_LAYER1_NOTE}}`, `{{E2E_LAYER2_CHECK}}`, `{{E2E_REPORT_FIELD}}
|
|
126
|
+
| `{{E2E_OUTER_GATE_LINE}}` | depends on e2e selected | The outer-gate e2e step, **wrapped in a `run_options.run_e2e` runtime gate** so e2e is opt-in per run. Render as ` c. **E2E** (opt-in): {If \`run_options.run_e2e = true\`:} when this phase has an e2e spec, run the project's e2e suite for the touched app + copy screenshots into \`pr-screenshots/\`. {Else:} skip e2e this run (default — e2e makes the phase take a lot longer).` Empty when e2e not selected. |
|
|
127
|
+
| `{{E2E_LAYER1_NOTE}}`, `{{E2E_LAYER2_CHECK}}`, `{{E2E_REPORT_FIELD}}` | depends on e2e selected | Verification/report hooks for e2e, each **guarded by `{If run_options.run_e2e = true:}`** so they only apply when the run opted into e2e. `E2E_LAYER1_NOTE`: ` + the e2e suite when \`run_options.run_e2e = true\``. `E2E_LAYER2_CHECK`: a Layer-2 walkthrough item that checks the phase's named e2e spec exists + screenshots landed **only when `run_options.run_e2e = true`** (else it's a no-op). `E2E_REPORT_FIELD`: `- E2E: specs run + screenshot paths, or "skipped (run_options.run_e2e = false)".` Empty when e2e not selected. |
|
|
128
|
+
| `{{E2E_OUTER_GATE_CHECKLIST}}`, `{{UI_E2E_RULE_LINE}}` | depends on e2e selected | populated or empty. `E2E_OUTER_GATE_CHECKLIST`: a trailing checklist clause ` + the e2e suite when \`run_options.run_e2e = true\`` (leading ` + `) so the gate checklist only demands e2e on opted-in runs. `UI_E2E_RULE_LINE`: an important-rules bullet stating e2e is opt-in via `run_options.run_e2e` (default off, makes runs a lot slower) and only runs for phases carrying an e2e spec. |
|
|
129
|
+
| `{{E2E_RUN_OPTION_QUESTION}}` | depends on e2e selected | The Step 0 opt-in question **(e)** for `implement-plan`, e2e projects only (empty otherwise). Render as ``\n\n e. **Run E2E tests this run?** *"Some phases carry Playwright e2e specs. Running them each phase makes the implementation take a LOT longer (browser boot, seeded data, screenshot capture). Run them this time, or skip and rely on unit + integration coverage?"* Options: `Skip e2e (default) — faster`, `Run e2e — slower but exercises the browser flows`. Default = `Skip`. Records `run_options.run_e2e` (`true` only for the `Run e2e` answer).`` (starts with `\n\n`; vanishes cleanly for no-e2e projects) |
|
|
130
|
+
| `{{E2E_RUN_OPTION_CONFIRM}}` | depends on e2e selected | e2e projects: `` + `run_options.run_e2e` `` (leading ` + `). Empty otherwise. |
|
|
131
|
+
| `{{E2E_RUN_OPTION_TRAILER}}` | depends on e2e selected | e2e projects: ` E2E execution follows `run_options.run_e2e` (opt-in; off by default).` (leading space). Empty otherwise. |
|
|
132
|
+
| `{{E2E_RUN_OPTION_TRACKING}}` | depends on e2e selected | e2e projects: `` , `run_e2e` `` (leading `, `) — appends `run_e2e` to a run-options list (the tracking schema line + the implement-phase / review-phase pass-through lists). Empty otherwise. |
|
|
133
|
+
| `{{E2E_RUN_OPTION_RULE}}` | depends on e2e selected | e2e projects: ` `run_options.run_e2e` controls whether the e2e suites run in the outer gate + review layers ([Implement](#1a-implement) + [Review](#1b-review)) — off by default because e2e makes each phase a lot slower; on only when the user opts in at Step 0.` (leading space). Empty otherwise. |
|
|
128
134
|
| `{{STACK_SPECIFIC_DEPLOY_BLOCK}}` | from matched stacks | `- Bots: \`pnpm bots:build\`, \`pnpm bots:deploy --env=dev-<handle>\`...` for Medplum, or empty |
|
|
129
135
|
| `{{CODE_HOST}}` | Step 0 interview | `GitHub`, `GitLab`, `Bitbucket` |
|
|
130
136
|
| `{{DEFAULT_BRANCH}}` | inventory.repo.default_branch | `main` or `master` |
|
|
@@ -98,6 +98,9 @@ Never flatten ten questions into one prose paragraph just because tool unavailab
|
|
|
98
98
|
1. Metric / log / dashboard tells us it's working in prod?
|
|
99
99
|
2. Audit logging requirements (whatever audit-trail app/module the project uses)?
|
|
100
100
|
3. How measure producer adoption before downstream phases ship?
|
|
101
|
+
<!-- e2e:start -->
|
|
102
|
+
4. **E2E coverage (opt-in)** — should this plan include happy-path Playwright e2e specs for its new UI flows? **Default: NO.** E2E specs make the *implementation* run take a lot longer (browser boot, seeded data, screenshot capture on every affected view, per phase). Opt in only when the flow is high-risk or explicitly QA-gated. When off, phases ship unit + integration tests only and carry no e2e requirement — e2e can still be added later, per-flow, via [add-e2e-test](../add-e2e-test/SKILL.md).
|
|
103
|
+
<!-- e2e:end -->
|
|
101
104
|
|
|
102
105
|
### J. Edge cases & failure modes
|
|
103
106
|
1. Behavior when new field partially populated, malformed, oversized? Reject whole batch, drop offending entry?
|
|
@@ -202,11 +205,13 @@ When on, apply even when:
|
|
|
202
205
|
Cross-cutting infra (shared types, migration, scaffolding) lands in a foundation phase before the use-case phases. Each subsequent use-case phase consumes that scaffolding.
|
|
203
206
|
|
|
204
207
|
<!-- e2e:start -->
|
|
205
|
-
### Phases creating new UI flow
|
|
208
|
+
### Phases creating a new UI flow ship happy-path E2E — only when e2e coverage was opted into
|
|
209
|
+
|
|
210
|
+
**Gated on the Step 0 group I "E2E coverage" answer, which defaults to NO.** When e2e coverage was **not** opted in (the default), skip this section entirely: phases ship unit + integration tests only, name no e2e spec, and add no `QA_USE_CASES.md` / `pr-screenshots/` machinery. E2E specs make the implementation run take a lot longer, so they are opt-in per plan, not automatic.
|
|
206
211
|
|
|
207
|
-
|
|
212
|
+
**When e2e coverage was opted in:** every phase that introduces or substantially changes a user-facing flow (new page, new modal, new wizard step, new gated action) ships a Playwright e2e test covering at least the happy path in the same MR. Follow the [add-e2e-test](../add-e2e-test/SKILL.md) skill: pick the next free `PA###` / `PR###` id, update [QA_USE_CASES.md](QA_USE_CASES.md), add the page object + spec. **No `QA_USE_CASES.md` in the project yet?** Run [create-qa-use-cases](../create-qa-use-cases/SKILL.md) first to bootstrap the doc from this plan + spec — add-e2e-test appends, it doesn't create.
|
|
208
213
|
|
|
209
|
-
In the phase body, name the spec file path under **Tests → E2E**. If the phase is purely backend (API-only, bot, migration), skip — happy-path E2E only
|
|
214
|
+
In the phase body, name the spec file path under **Tests → E2E**. If the phase is purely backend (API-only, bot, migration), skip — happy-path E2E only applies when the phase reaches the browser.
|
|
210
215
|
|
|
211
216
|
**Capture screenshots on every affected UI**: the e2e spec takes one screenshot per distinct rendered state (landing page, each modal/wizard step, success toast, final state) — not just the final state. Spec writes to Playwright's **default per-test output dir** via `testInfo.outputPath('<id>-<NN>-<view-slug>.png')` (zero-padded step number, kebab-case slug describing what's on screen). Never hard-code `pr-screenshots/` in the spec. After the suite runs, a copy step moves matching files from `test-results/**/` into `pr-screenshots/`. The `pr-screenshots/` directory is gitignored — author drags the files into the PR description in numeric order so reviewers see the journey, not just the destination. See [add-e2e-test](../add-e2e-test/SKILL.md) for the strict filename convention + spec example + copy command.
|
|
212
217
|
<!-- e2e:end -->
|
|
@@ -235,11 +240,13 @@ Tests:
|
|
|
235
240
|
- **Unit**: {file path} — {what it covers}.
|
|
236
241
|
- **Integration**: {file path} — {what it covers, including edge cases user flagged in Step 0, AND flag-off test proving existing callers see no behavior change}.
|
|
237
242
|
<!-- e2e:start -->
|
|
238
|
-
- **E2E** (only when this phase reaches the browser): {e2e/tests/<app>/<id>-<slug>.spec.ts} — happy path covering the new flow. Spec writes screenshots to the Playwright default output dir via `testInfo.outputPath(...)`; post-run copy step moves them into `pr-screenshots/<id>-<step>.png`. Follow [add-e2e-test](../add-e2e-test/SKILL.md).
|
|
243
|
+
- **E2E** (only when e2e coverage was opted into at Step 0 AND this phase reaches the browser): {e2e/tests/<app>/<id>-<slug>.spec.ts} — happy path covering the new flow. Spec writes screenshots to the Playwright default output dir via `testInfo.outputPath(...)`; post-run copy step moves them into `pr-screenshots/<id>-<step>.png`. Follow [add-e2e-test](../add-e2e-test/SKILL.md).
|
|
239
244
|
<!-- e2e:end -->
|
|
240
245
|
|
|
241
246
|
**Suggested AI model**: {tier choice + why}. See "AI model selection".
|
|
242
247
|
|
|
248
|
+
**Review models** (optional — omit for the project defaults): reviewer Tier {N}, fixer Tier {N} — {why this phase warrants a non-default review model}. See "AI model selection".
|
|
249
|
+
|
|
243
250
|
**Reusable skills**: {invoke `Skill(name)` — see "Project skills"}.
|
|
244
251
|
|
|
245
252
|
Acceptance: {one literal statement true after merge + deploy of this phase, only this phase}.
|
|
@@ -313,6 +320,15 @@ Common mistake: leave cross-repo producer wiring for last, then discover the ups
|
|
|
313
320
|
|
|
314
321
|
For each phase, suggest **cheapest/fastest model likely to one-shot work**. Iterating with cheap model usually beats burning Opus tokens on CRUD scaffold.
|
|
315
322
|
|
|
323
|
+
**Scope: the plan always picks the *implementer* model, and MAY override the reviewer / fixer models per phase.**
|
|
324
|
+
|
|
325
|
+
- `**Suggested AI model**:` drives the implementer subagent that writes the phase — **required on every phase**.
|
|
326
|
+
- `**Review models**:` (optional) overrides the reviewer and/or fixer tier **for this phase only**. Use it when a phase is riskier than average — high blast-radius change, subtle concurrency / transaction logic, security-sensitive surface, a migration that's hard to undo — and you want a more capable reviewer or fixer than the project default. Name a tier for reviewer, fixer, or both; omit either to leave that role on the default.
|
|
327
|
+
- **Precedence** (resolved by `implement-plan` / `review-phase`): a phase's `**Review models**:` override wins → else the project-wide `agent_models.reviewer` / `agent_models.fixer` tier in `.vinta-ai-workflows.yaml` → else the runtime default. So the project keeps sane defaults and the plan only speaks up for the phases that need a different review model.
|
|
328
|
+
- The mechanical-step models (worktree prep, opening the PR / integrate) are **not** plan-owned — they stay under `agent_models` in `.vinta-ai-workflows.yaml`. Don't add worktree/PR model hints to a phase; they'd be ignored.
|
|
329
|
+
|
|
330
|
+
**Most phases carry only the implementer line.** Add `**Review models**:` deliberately, for the few phases that earn it — not by default on every phase.
|
|
331
|
+
|
|
316
332
|
**Concrete model IDs per tier live in [resources/ai-models.yaml](resources/ai-models.yaml) — read that file when writing each suggestion. Never recall model names from memory; they go stale as vendors ship.** The tiers below define *when* each applies (stable judgement); the IDs drift, and a nightly job keeps the resource current. Note the file's `last_verified` date — if it's far in the past, the IDs may be stale; flag that rather than trusting them blindly.
|
|
317
333
|
|
|
318
334
|
### Tier 1 — cheapest/fastest (boilerplate, exact-precedent edits)
|
|
@@ -337,6 +353,14 @@ When one tier doesn't fit, name both:
|
|
|
337
353
|
|
|
338
354
|
> **Suggested AI model**: Tier 2 for repository + serializer; step up to Tier 3 for the integration test spanning upsert → routing → reprocess. IDs per tier in [resources/ai-models.yaml](resources/ai-models.yaml).
|
|
339
355
|
|
|
356
|
+
### Overriding the review models on a critical phase (optional)
|
|
357
|
+
|
|
358
|
+
Add a `**Review models**:` line **only** when the phase justifies a non-default reviewer / fixer. Pick the tier from the same rubric — a higher tier for the *review* of a delicate change, not for its authoring:
|
|
359
|
+
|
|
360
|
+
> **Review models**: reviewer Tier 4 — this phase rewrites the transactional batch-apply protocol with deferred constraints; a subtle ordering bug here corrupts data, so the independent review runs on the most capable model. Fixer left on the project default.
|
|
361
|
+
|
|
362
|
+
Name only the role you're changing (`reviewer`, `fixer`, or both). Omitting the line entirely — the common case — leaves both roles on the project's `agent_models` defaults.
|
|
363
|
+
|
|
340
364
|
**Default to cheapest tier that plausibly works, not safest.** Cheap models failing fast beats expensive succeeding slowly.
|
|
341
365
|
|
|
342
366
|
## Project skills to leverage
|
|
@@ -401,8 +425,9 @@ When in doubt, model the plan after a recent example in `ai-plans/` — look for
|
|
|
401
425
|
- [ ] Phase numbering uses numbers + letters consistently.
|
|
402
426
|
- [ ] **Phase granularity matches the Step 0 answer.** Default (one-use-case-per-phase): at least one phase per spec use-case, no phase implements two use-cases. If bundling was chosen: grouped phases stay MR-sized, one concern, independently mergeable. Cross-cutting scaffolding is its own foundation phase either way.
|
|
403
427
|
- [ ] Each phase has Goal / Spec use-case / Feature flag (or explicit waiver) / Changes / Tests / Suggested AI model / Reusable skills / Acceptance.
|
|
428
|
+
- [ ] `**Review models**:` appears **only** on phases that justify a non-default reviewer / fixer (not on every phase); each such line names a tier + why. Phases without it inherit the project's `agent_models` defaults.
|
|
404
429
|
<!-- e2e:start -->
|
|
405
|
-
- [ ]
|
|
430
|
+
- [ ] **If e2e coverage was opted into at Step 0:** every phase introducing a new UI flow has an **E2E happy-path test** in its Tests block, with screenshot output to `pr-screenshots/`. If it was not opted into (default), **no phase carries an e2e spec** and there is no `QA_USE_CASES.md` / `pr-screenshots/` reference.
|
|
406
431
|
<!-- e2e:end -->
|
|
407
432
|
- [ ] Feature flag declared in **Guiding Decisions** (key, scope, default, flip-on criterion) **unless** **Guiding Decisions** explicitly justifies "no flag — purely additive surface".
|
|
408
433
|
- [ ] ≥1 test per gated phase asserts flag-off behavior unchanged.
|
|
@@ -0,0 +1,29 @@
|
|
|
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. -->
|
|
2
|
+
|
|
3
|
+
<!-- block-begin: TIER_RESOLVE -->
|
|
4
|
+
## Resolve an `agent_models` tier to a spawn model
|
|
5
|
+
|
|
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
|
+
|
|
8
|
+
1. Determine the **effective tier** for the role: for `reviewer` / `fixer`, a per-phase override the conductor passed wins over `agent_models.<role>`; for the mechanical steps, it's simply `agent_models.<role>`.
|
|
9
|
+
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
|
+
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
|
+
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
|
+
|
|
13
|
+
Record the **model actually used** in tracking: for `reviewer` / `fixer`, alongside the review note; for the mechanical steps, in the phase's tracking row next to the branch/PR fields.
|
|
14
|
+
<!-- block-end: TIER_RESOLVE -->
|
|
15
|
+
|
|
16
|
+
<!-- block-begin: MECHANICAL_DELEGATION -->
|
|
17
|
+
## Delegate a mechanical step to a configured model
|
|
18
|
+
|
|
19
|
+
Two steps the conductor would otherwise run **inline in its own (usually pricier) session** — provisioning the worktree ([prepare-worktree](../prepare-worktree/SKILL.md)) and integrating a phase ([integrate-phase](../integrate-phase/SKILL.md): push the branch + open/update the PR through the bundled `open-pr.sh`) — are mechanical, precedent-driven work that a cheap model handles fine. The `agent_models.worktree_prep` / `agent_models.integrate` tiers let a project push that work down.
|
|
20
|
+
|
|
21
|
+
- **Tier set** (`worktree_prep` / `integrate`) → **spawn exactly one subagent** at the [resolved model](#resolve-an-agent_models-tier-to-a-spawn-model), hand it the step's SKILL.md plus the same inputs the conductor would use, and consume its returned report exactly as if the conductor had done the work inline. This subagent is a **labor delegate, not a decision-maker**: the conductor still owns git topology (which branch stacks on which base) and still holds every value the step returns (`WORKROOT` / `BASE_BRANCH` / worktree summary for `worktree_prep`; branch + PR-context path + `status` for `integrate`). The delegate executes and reports those back.
|
|
22
|
+
- **Tier unset** → run the step inline in the conductor's own session — today's behavior, no subagent.
|
|
23
|
+
|
|
24
|
+
Rules that hold **regardless of who runs the step**:
|
|
25
|
+
|
|
26
|
+
- The **PR-context file + `open-pr.sh` is still the only PR-creation path.** An `integrate` delegate uses the bundled script; it never calls raw `gh pr create` / `glab mr create`.
|
|
27
|
+
- The delegate is `read-write` (worktree provisioning writes dirs/DBs; integrate pushes + writes the PR-context file) but **makes no plan or code decisions** — a malformed or failed delegate report is surfaced to the user, never worked around.
|
|
28
|
+
- This delegation is **separate from the phase-work sub-agents** (implementer / reviewer / fixer). Those still never branch, push, or open PRs — that prohibition is about code-authoring agents, not the dedicated mechanical delegate the conductor spawns to run the integrate step itself.
|
|
29
|
+
<!-- block-end: MECHANICAL_DELEGATION -->
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!-- block-begin: MODEL_PICK -->
|
|
3
3
|
## Pick the model from the plan's per-phase suggestion
|
|
4
4
|
|
|
5
|
-
**The plan owns model
|
|
5
|
+
**The plan owns the *implementer* model — this skill does not re-derive tiers and doesn't assume a vendor.** Each phase carries a `**Suggested AI model**:` line listing one model per vendor. (The reviewer / fixer models default to `.vinta-ai-workflows.yaml`'s `agent_models` section, though a phase's optional `**Review models**:` line can override them for that phase; the mechanical-step models are `agent_models`-only. All of that is handled by `review-phase` / the conductor, not this implementer step.)
|
|
6
6
|
|
|
7
7
|
Pick:
|
|
8
8
|
|
|
@@ -30,7 +30,7 @@ Open the phase body alongside the diff and walk:
|
|
|
30
30
|
|
|
31
31
|
## Layer 3 — Independent reviewer subagent
|
|
32
32
|
|
|
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)). Read-only by design.
|
|
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.
|
|
34
34
|
|
|
35
35
|
Reviewer prompt template — see the reviewer agent's body for the standard form. Triage findings:
|
|
36
36
|
- **BLOCKER**: must fix before the phase is pushed (the conductor's integrate step).
|
|
@@ -43,7 +43,7 @@ The reviewer finds nothing on a >300-LoC multi-file phase → suspicious. Read o
|
|
|
43
43
|
|
|
44
44
|
## Fix loop
|
|
45
45
|
|
|
46
|
-
1. Spawn a **new** subagent — the project's `fixer` agent type ([ai-tools/agents/fixer.md](ai-tools/agents/fixer.md)). 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.
|
|
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
47
|
2. The `fixer`'s system prompt mandates re-running the inner loop + outer gate (in `<WORKROOT>`).
|
|
48
48
|
3. After the fixer returns, redo Layer 1 in full + the affected portion of Layer 2.
|
|
49
49
|
4. Loop until Layers 1, 2, 3 are all clean.
|
|
@@ -13,7 +13,7 @@ Resolve three values **once**, before any phase runs, and record them in trackin
|
|
|
13
13
|
|
|
14
14
|
**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
15
|
|
|
16
|
-
**When `use_worktree = true`:**
|
|
16
|
+
**When `use_worktree = true`:** run [prepare-worktree](../prepare-worktree/SKILL.md) **once**. This is a mechanical step: when `agent_models.worktree_prep` is set, **delegate it to a subagent** per the [Delegate a mechanical step to a configured model](#delegate-a-mechanical-step-to-a-configured-model) pattern (hand the subagent prepare-worktree's SKILL.md + the inputs below; consume its returned `worktree_path` / `worktree_branch` / `worktree_summary` / `sandbox_tier` report). When the tier is unset, run it inline — the steps below read the same either way:
|
|
17
17
|
|
|
18
18
|
1. **Inputs.** Plan path (so prepare-worktree can read it for deps / migrations / env / compose churn — see prepare-worktree's **Plan inspection** step), suggested worktree name = `plan-{plan-id-kebab}`, plan-driven mode.
|
|
19
19
|
2. **Pre-run sanity.** Confirm no existing worktree at the target path (`git worktree list | grep <name>` — refuse if collision). Confirm `git -C <main_checkout> status` of the main checkout (warn if dirty; defer to prepare-worktree's **Sanity checks** step for the call).
|
|
@@ -126,7 +126,7 @@ Always the first write. Plan file is durable; commits get rewritten next.
|
|
|
126
126
|
- Letter: `1b` between `1` (relabeled `1a`) and `2`. Requires renaming `1` → `1a` inside **Phased Rollout** + updating downstream references.
|
|
127
127
|
Ask the user. Default: decimal — no rename of existing ids.
|
|
128
128
|
|
|
129
|
-
3. **Appends** — new `## Phase N+1` block at end of **Phased Rollout**. Same shape as siblings: Goal, Suggested AI model, reusable_skills, Changes, Tests, Acceptance.
|
|
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.
|
|
130
130
|
|
|
131
131
|
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
132
|
|
|
@@ -241,7 +241,7 @@ For `change_kind = rebase-only` (downstream phase whose parent moved): skip the
|
|
|
241
241
|
|
|
242
242
|
### 4c. Run the three-layer review
|
|
243
243
|
|
|
244
|
-
Invoke [review-phase](../review-phase/SKILL.md) against the rewritten branch, passing the **new** phase body to walk against and `
|
|
244
|
+
Invoke [review-phase](../review-phase/SKILL.md) against the rewritten branch, passing the **new** phase body to walk against, `WORKROOT`, and the `reviewer` / `fixer` agent types with their `agent_models` tiers plus this phase's `reviewer_model_tier` / `fixer_model_tier` overrides (parsed from the rewritten body's `**Review models**:` line, null when absent). Layer 2 walks: every "Changes" item in the new body, every "Tests" entry, the new acceptance line.
|
|
245
245
|
|
|
246
246
|
Skip this step only when `change_kind = rebase-only` (no body change → no compliance walk). Even then, spot-run review-phase's Layer 1 mechanical checks to verify the rebase didn't lose unrelated work.
|
|
247
247
|
|
package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md
CHANGED
|
@@ -37,7 +37,7 @@ Parse once, reuse for every phase:
|
|
|
37
37
|
- **Goals + Non-goals** section — verbatim, used in every phase prompt.
|
|
38
38
|
- **Guiding Decisions** section — verbatim. Pay attention to: feature flag (key, scope, default, flip-on criterion), storage shape, tenant scoping, API contract decisions.
|
|
39
39
|
- **Data Model Changes** section — keep full body; later phases reference earlier subsections.
|
|
40
|
-
- **Phased Rollout** section — parse into phase records: `{ id, title, goal, body, spec_use_case, suggested_model_tier, reusable_skills, has_e2e, acceptance, is_cross_repo, is_flag_removal }`.
|
|
40
|
+
- **Phased Rollout** section — parse into phase records: `{ id, title, goal, body, spec_use_case, suggested_model_tier, reviewer_model_tier, fixer_model_tier, reusable_skills, has_e2e, acceptance, is_cross_repo, is_flag_removal }`. `reviewer_model_tier` / `fixer_model_tier` come from the phase's optional `**Review models**:` line (null when the phase doesn't override — most phases).
|
|
41
41
|
- **Risk & Rollout Notes**, **Open Questions**, **Touch List** sections — keep available; include in phase prompts only when relevant.
|
|
42
42
|
3. **Classify each phase**: `is_cross_repo`, `is_flag_removal` — the conductor does NOT auto-execute these (see [Cross-repo phases](#cross-repo-phases) + [Flag-removal phase](#flag-removal-phase-always-out-of-scope)).
|
|
43
43
|
4. **Ask the user three opt-in questions** via `AskUserQuestion`. Defaults are project-specific (see below); record every answer in tracking under `run_options`:
|
|
@@ -53,12 +53,20 @@ Parse once, reuse for every phase:
|
|
|
53
53
|
Skip this question entirely when `foundation_skills.prepare-worktree` is `disabled` in `.vinta-ai-workflows.yaml`: record `run_options.use_worktree = false`; surface a one-line note that worktree isolation is available if the team opts in via [vinta-sync-ai-tools](../../skills/vinta-sync-ai-tools/SKILL.md).
|
|
54
54
|
|
|
55
55
|
d. **Full test suite each phase?** *"Each phase's outer gate always runs the repo-wide type/build gate. For tests, do you want the quick path (run only the scoped suite covering the apps/files that phase touched — faster phases) or the full repo test suite every phase (slower, but guards against regressions in untouched code)? New tests still pass individually in the inner loop either way."* Options: `Quick — scoped tests only each phase (default)`, `Full — run the whole test suite each phase`. Default = value of `run_options.implement-plan.full_test_suite` in `.vinta-ai-workflows.yaml` (`Quick`/false when unset). Records `run_options.full_test_suite` (`true` only for the `Full` answer).
|
|
56
|
-
|
|
56
|
+
{{E2E_RUN_OPTION_QUESTION}}
|
|
57
57
|
PR opening itself is **not** asked here — it's governed by the project's PR creation policy captured at bootstrap (see `{{PR_POLICY_BLOCK}}` above). When that policy = "agents create PRs", the {{INTEGRATE_PHASE_DISPATCH}} step always opens the PR via [open-pr.sh](../open-pr-from-context/scripts/open-pr.sh) regardless of the comment opt-in.{{COMMIT_STRATEGY_STEP0_QUESTION}}
|
|
58
58
|
|
|
59
|
-
5. **Confirm with user before starting.** Show plan path, phase list (id + title + tier + cross-repo/flag-removal flags + e2e flag), phases this skill will execute vs defer, {{BRANCH_NAMING_PATTERN_SUMMARY}}, captured `run_options.pause_between_phases` + `run_options.generate_inline_comments` + `run_options.use_worktree` + `run_options.full_test_suite`{{COMMIT_STRATEGY_CONFIRM_NOTE}}{{PR_REMINDER_LINE}}.
|
|
59
|
+
5. **Confirm with user before starting.** Show plan path, phase list (id + title + tier + cross-repo/flag-removal flags + e2e flag), phases this skill will execute vs defer, {{BRANCH_NAMING_PATTERN_SUMMARY}}, captured `run_options.pause_between_phases` + `run_options.generate_inline_comments` + `run_options.use_worktree` + `run_options.full_test_suite`{{E2E_RUN_OPTION_CONFIRM}}{{COMMIT_STRATEGY_CONFIRM_NOTE}}{{PR_REMINDER_LINE}}.
|
|
60
|
+
|
|
61
|
+
Wait for "go". After that, the per-phase pause behavior follows `run_options.pause_between_phases`. Inline-comment drafting follows `run_options.generate_inline_comments`. Worktree isolation follows `run_options.use_worktree`. Outer-gate test scope follows `run_options.full_test_suite`.{{E2E_RUN_OPTION_TRAILER}}{{COMMIT_STRATEGY_STEP0_TRAILER}}
|
|
62
|
+
|
|
63
|
+
## Agent models — reviewer, fixer, and mechanical steps
|
|
64
|
+
|
|
65
|
+
The per-phase **implementer** model stays plan-owned (each phase's `**Suggested AI model**:` line — see [implement-phase](../implement-phase/SKILL.md)). Every *other* model this conductor spawns — the review sub-agents and the mechanical steps — is chosen from `.vinta-ai-workflows.yaml`'s `agent_models` section, never from the plan. Read that section once in [Step 0](#step-0--locate--parse-plan) alongside `run_options`.
|
|
66
|
+
|
|
67
|
+
<!-- include: partials/agent-models.md#TIER_RESOLVE -->
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
<!-- include: partials/agent-models.md#MECHANICAL_DELEGATION -->
|
|
62
70
|
|
|
63
71
|
<!-- include: partials/worktree-seam.md#WORKROOT_RESOLUTION -->
|
|
64
72
|
|
|
@@ -70,23 +78,23 @@ For each phase that's `not is_cross_repo and not is_flag_removal`, in plan order
|
|
|
70
78
|
|
|
71
79
|
### 1a. Implement
|
|
72
80
|
|
|
73
|
-
Invoke [implement-phase](../implement-phase/SKILL.md), passing the phase record, the plan-level decisions (**Goals + Non-goals**, **Guiding Decisions**, the relevant **Data Model Changes** subsection), the prior-phase tracking summaries, `run_options.full_test_suite
|
|
81
|
+
Invoke [implement-phase](../implement-phase/SKILL.md), passing the phase record, the plan-level decisions (**Goals + Non-goals**, **Guiding Decisions**, the relevant **Data Model Changes** subsection), the prior-phase tracking summaries, `run_options.full_test_suite`{{E2E_RUN_OPTION_TRACKING}}, and `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER`. It returns the implementer's report.
|
|
74
82
|
|
|
75
83
|
**Model escalation.** implement-phase escalates one tier + retries once on a clear capability gap. After Tier 4 fails, it stops and hands back the failure — update tracking with `❌`, post the report to the user, ask how to proceed. Don't silently re-derive tier.
|
|
76
84
|
|
|
77
85
|
### 1b. Review
|
|
78
86
|
|
|
79
|
-
Invoke [review-phase](../review-phase/SKILL.md) against the phase diff, passing the phase body to walk, `WORKROOT`, `main_checkout`, `run_options.full_test_suite
|
|
87
|
+
Invoke [review-phase](../review-phase/SKILL.md) against the phase diff, passing the phase body to walk, `WORKROOT`, `main_checkout`, `run_options.full_test_suite`{{E2E_RUN_OPTION_TRACKING}}, and the `reviewer` / `fixer` agent types with their `agent_models.reviewer` / `agent_models.fixer` tiers **plus this phase's `reviewer_model_tier` / `fixer_model_tier` overrides (null when the phase didn't set a `**Review models**:` line)**. review-phase prefers a phase override over the `agent_models` default. It loops its three layers + fix loop until clean, then returns `PASS` (or the surfaced findings). Do not proceed to integrate while any layer is red.
|
|
80
88
|
|
|
81
89
|
### 1c. Integrate
|
|
82
90
|
|
|
83
|
-
Invoke {{INTEGRATE_PHASE_DISPATCH}}, passing `WORKROOT` / `BASE_BRANCH`, the `{{PR_POLICY_BLOCK}}` policy, and `run_options.generate_inline_comments`. It pushes the branch and routes the PR through the context file, returning the branch + PR-context path + status.
|
|
91
|
+
Invoke {{INTEGRATE_PHASE_DISPATCH}}, passing `WORKROOT` / `BASE_BRANCH`, the `{{PR_POLICY_BLOCK}}` policy, and `run_options.generate_inline_comments`. It pushes the branch and routes the PR through the context file, returning the branch + PR-context path + status. This is a mechanical step: when `agent_models.integrate` is set, run it as a delegated subagent per the [Delegate a mechanical step to a configured model](#delegate-a-mechanical-step-to-a-configured-model) pattern (the delegate pushes + writes the PR-context file + runs `open-pr.sh`, then reports the branch / path / status back); when unset, run it inline. Either way the PR-context file + `open-pr.sh` is the only PR-creation path.
|
|
84
92
|
|
|
85
93
|
### 1d. Update tracking file
|
|
86
94
|
|
|
87
95
|
Tracking lives at `{{PLAN_DIR}}/TRACKING_{plan-id}.md`. Commit on the **current** phase's branch — deletion in [Step 2](#step-2--final-report).
|
|
88
96
|
|
|
89
|
-
Schema: feature-name, plan path, started/last-updated dates, optional feature-flag info, **run options** (`pause_between_phases`, `generate_inline_comments`, `full_test_suite
|
|
97
|
+
Schema: 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`, `worktree_path`, `worktree_branch`, `worktree_summary`, `sandbox_tier` — last four only when `use_worktree = true`), {{TRACKING_BRANCH_FIELD}}, completed-phases (with status, model{{TRACKING_PHASE_BRANCH_FIELD}}, e2e+screenshots if any, 5–15 line summary), current phase, remaining phases, deferred phases.
|
|
90
98
|
|
|
91
99
|
The conductor writes this from the git diff + the agent's summary — not from the agent's narration.
|
|
92
100
|
|
|
@@ -151,9 +159,10 @@ After all executable phases complete:
|
|
|
151
159
|
- **Read AGENTS.md** in every phase prompt.
|
|
152
160
|
- **Stage explicitly.** No `git add -A`.
|
|
153
161
|
- **Subagents work in fresh sessions.** Each phase = a new subagent. Tracking + plan files = the context handoff.
|
|
154
|
-
- **Conductor owns git topology.**
|
|
162
|
+
- **Conductor owns git topology.** Phase-work subagents (implementer / reviewer / fixer) commit but never branch, push, {{PR_RULE_TAIL}}. The one exception is a **mechanical `integrate` delegate** spawned per `agent_models.integrate` — it exists precisely to run the conductor's integrate step (push + PR via `open-pr.sh`) on a cheaper model, and the conductor still dictates the branch/base topology it uses.
|
|
155
163
|
{{COAUTHOR_RULE_LINE}}
|
|
156
|
-
- **Trust the plan's per-phase model suggestion.**
|
|
164
|
+
- **Trust the plan's per-phase model suggestion.** Implementer model selection lives in [implement-phase](../implement-phase/SKILL.md); the conductor never re-derives tiers.
|
|
165
|
+
- **Reviewer / fixer / mechanical-step models come from `agent_models`, not the plan.** Resolve each configured tier via the [Agent models](#agent-models--reviewer-fixer-and-mechanical-steps) step; an unset key means the spawn uses the runtime default. The plan never names these models.
|
|
157
166
|
- **Don't re-implement what a project skill encodes.**
|
|
158
167
|
{{UI_E2E_RULE_LINE}}
|
|
159
168
|
- **Two-tier verification, in order, every phase.** Inner scoped, then the outer gate — enforced inside [implement-phase](../implement-phase/SKILL.md). The outer gate always runs the repo-wide type/build gate; its test scope follows `run_options.full_test_suite` (scoped suite by default, full repo suite when opted in).
|
|
@@ -162,7 +171,7 @@ After all executable phases complete:
|
|
|
162
171
|
- **Feature flags = gates, not toggles for tests.**
|
|
163
172
|
- **Never remove a feature flag from this skill.**
|
|
164
173
|
- **Stop on Tier-4 failure.**
|
|
165
|
-
- **Honor opt-in flags.** `run_options.pause_between_phases` controls the [Per-phase pause gate](#1f-per-phase-pause-gate-opt-in); `run_options.generate_inline_comments` controls whether {{INTEGRATE_PHASE_DISPATCH}} drafts inline comments (always writes the file when that step runs at all — empty comments when off); `run_options.use_worktree` controls whether the [Resolve WORKROOT step](#step-05--resolve-workroot) provisions a worktree and thus what `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER` resolve to; `run_options.full_test_suite` controls the outer-gate test scope ([Implement](#1a-implement) + [Review](#1b-review) Layer 1) — scoped suite by default, full repo suite when `true`.
|
|
174
|
+
- **Honor opt-in flags.** `run_options.pause_between_phases` controls the [Per-phase pause gate](#1f-per-phase-pause-gate-opt-in); `run_options.generate_inline_comments` controls whether {{INTEGRATE_PHASE_DISPATCH}} drafts inline comments (always writes the file when that step runs at all — empty comments when off); `run_options.use_worktree` controls whether the [Resolve WORKROOT step](#step-05--resolve-workroot) provisions a worktree and thus what `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER` resolve to; `run_options.full_test_suite` controls the outer-gate test scope ([Implement](#1a-implement) + [Review](#1b-review) Layer 1) — scoped suite by default, full repo suite when `true`.{{E2E_RUN_OPTION_RULE}}
|
|
166
175
|
- **One worktree per plan run.** When `use_worktree = true`, provision once in the [Resolve WORKROOT step](#step-05--resolve-workroot) and reuse for every phase. Never spawn a second worktree mid-plan; never silently fall back to the main checkout on prepare-worktree failure (ask the user).
|
|
167
176
|
- **Don't auto-tear-down the worktree.** Step 2 surfaces the teardown command; the user runs it when ready.
|
|
168
177
|
- **`WORKROOT` is resolved once, used everywhere.** Every sub-skill takes `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER` as data — no step re-derives worktree state. OS-level prevention (sandbox wrap in implement-phase when `SANDBOX_TIER = enforced`) plus the review-phase stray-write backstop keep main-checkout writes out; see [worktree-seam](../implement-phase/SKILL.md#3-spawn-the-subagent).
|
|
@@ -14,7 +14,20 @@ The single review implementation shared by every plan-execution conductor: [impl
|
|
|
14
14
|
- `WORKROOT`, `SANDBOX_TIER` — resolved once by the conductor.
|
|
15
15
|
- `main_checkout` — the repo root the run was invoked from (equals `WORKROOT` when no worktree).
|
|
16
16
|
- `run_options.full_test_suite` — resolves which outer gate Layer 1 item 3 verifies ran (false = scoped suite; true = full repo suite).
|
|
17
|
-
- The project's `reviewer` + `fixer` agent types.
|
|
17
|
+
- The project's `reviewer` + `fixer` agent types, plus their `agent_models.reviewer` / `agent_models.fixer` tiers (when set in `.vinta-ai-workflows.yaml`).
|
|
18
|
+
- Optional per-phase `reviewer_model_tier` / `fixer_model_tier` overrides — the tiers parsed from this phase's `**Review models**:` line in the plan (null when the phase didn't set one, which is the common case).
|
|
19
|
+
|
|
20
|
+
## Resolve the reviewer + fixer model
|
|
21
|
+
|
|
22
|
+
Each of `reviewer` and `fixer` spawns at an **effective tier**, resolved per role with this precedence:
|
|
23
|
+
|
|
24
|
+
1. The phase's `reviewer_model_tier` / `fixer_model_tier` override, when the conductor passed one (the plan chose a non-default review model for this critical phase).
|
|
25
|
+
2. Else the project-wide `agent_models.reviewer` / `agent_models.fixer` tier from `.vinta-ai-workflows.yaml`.
|
|
26
|
+
3. Else unset → the runtime default model.
|
|
27
|
+
|
|
28
|
+
Feed that effective tier into the resolution below (it turns a tier into a concrete spawn model). A phase override applies to that phase only; the next phase falls back to the `agent_models` default unless it too overrides.
|
|
29
|
+
|
|
30
|
+
<!-- include: partials/agent-models.md#TIER_RESOLVE -->
|
|
18
31
|
|
|
19
32
|
## Review
|
|
20
33
|
|