vinta-ai-workflows 0.2.0-alpha2 → 0.2.0-alpha4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +5 -2
  3. package/package.json +7 -1
  4. package/schemas/README.md +1 -0
  5. package/schemas/ai-models.v1.schema.json +87 -0
  6. package/schemas/vinta-ai-workflows-config.v1.schema.json +20 -1
  7. package/skills/vinta-analyze-codebase/SKILL.md +1 -1
  8. package/skills/vinta-bootstrap-ai-tools/SKILL.md +19 -9
  9. package/skills/vinta-derive-skills/SKILL.md +98 -47
  10. package/skills/vinta-derive-skills/resources/foundation-skills/deslop-comments/SKILL.md +148 -0
  11. package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +24 -27
  12. package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml +98 -0
  13. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +89 -0
  14. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/claude-worktree-write-guard.py +118 -0
  15. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-claude-sandbox-settings.sh +167 -0
  16. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/sandbox-run.sh +106 -0
  17. package/skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md +129 -0
  18. package/skills/vinta-derive-skills/resources/plan-execution/README.md +42 -0
  19. package/skills/vinta-derive-skills/resources/{implement-plan-template-modular-substitutions.md → plan-execution/partials/commit-strategy/modular.md} +38 -70
  20. package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +46 -0
  21. package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +78 -0
  22. package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +19 -0
  23. package/skills/vinta-derive-skills/resources/plan-execution/partials/pr-context.md +61 -0
  24. package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +50 -0
  25. package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +68 -0
  26. package/skills/vinta-derive-skills/resources/{amend-plan-template.md → plan-execution/shell/amend-plan-template.md} +48 -42
  27. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +47 -0
  28. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +186 -0
  29. package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +30 -0
  30. package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +25 -0
  31. package/skills/vinta-derive-skills/resources/systematic-debugging-template.md +7 -3
  32. package/skills/vinta-update-project-skills/SKILL.md +1 -1
  33. package/skills/vinta-derive-skills/resources/implement-plan-template.md +0 -416
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vinta-derive-skills
3
- description: Author project-specific skills under `ai-tools/skills/`. Always copies the project-agnostic foundation set (`plan-feature`, `create-spec`, `create-qa-use-cases`) verbatim from this skill's bundled resources. Generates `implement-plan` from a parameterized template using project-specific commands + branch / commit / PR conventions captured in the bootstrap interview. Asks the user whether `add-e2e-test` and `add-env-var` are needed (optional foundation skills — only ship if applicable). Stack-specific skills (Medplum, Django, etc) come from user-supplied templates pointed at by the orchestrator — see [bootstrap-ai-tools/resources/stacks/<stack>/notes.md](../vinta-bootstrap-ai-tools/resources/stacks/) for the categories per stack.
3
+ description: Author project-specific skills under `ai-tools/skills/`. Always copies the project-agnostic foundation set (`plan-feature`, `create-spec`, `open-pr-from-context`) verbatim from this skill's bundled resources; ships `create-qa-use-cases` and keeps `plan-feature`'s e2e sections only when `add-e2e-test` is enabled. Generates `implement-plan` from a parameterized template using project-specific commands + branch / commit / PR conventions captured in the bootstrap interview. Asks the user whether `add-e2e-test` and `add-env-var` are needed (optional foundation skills — only ship if applicable). Stack-specific skills (Medplum, Django, etc) come from user-supplied templates pointed at by the orchestrator — see [bootstrap-ai-tools/resources/stacks/<stack>/notes.md](../vinta-bootstrap-ai-tools/resources/stacks/) for the categories per stack.
4
4
  ---
5
5
 
6
6
  # Derive skills
@@ -26,7 +26,7 @@ Before drafting any new SKILL.md, walk through every entry in `existing_ai_artif
26
26
  - **Migrate to `ai-tools/skills/<name>/`** — `git mv` the existing skill folder (with all its resources) into the canonical layout. After move, scan the body for hard-coded vendor paths that no longer apply (`.cursor/skills/...` self-references, etc.) and rewrite to the new path. `setup-ai-tools.mjs` will re-link to the chosen vendors.
27
27
  - **Keep in current vendor path, don't touch** — leave it where it is; AGENTS.md may reference it; downstream skill setup won't manage it. **Don't ship a foundation duplicate that would shadow it.**
28
28
  - **Drop** — log removal; don't emit anything.
29
- - **Replace with Vinta foundation version** (foundation-shape only — `plan-feature`, `create-spec`, `create-qa-use-cases`, `implement-plan`, `add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`) — proceed with the bucket A / B / C flow below for that name. The user has explicitly opted into overwriting their version.
29
+ - **Replace with Vinta foundation version** (foundation-shape only — `plan-feature`, `create-spec`, `create-qa-use-cases`, `implement-plan`, `add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`, `thermo-nuclear-code-quality-review`, `deslop-comments`) — proceed with the bucket A / B / C flow below for that name. The user has explicitly opted into overwriting their version.
30
30
  - **`vinta-managed`** (any skill whose dir starts with `vinta-`) — leave alone. These come from the `vinta-ai-workflows` CLI; `vinta-derive-skills` doesn't manage them.
31
31
 
32
32
  For each name in the foundation set: only emit it (verbatim copy / generate from template / interview-draft) when no existing skill of that name is being migrated or kept. If the user said `Migrate` or `Keep` for an existing `plan-feature/SKILL.md`, **do not overwrite**.
@@ -45,28 +45,66 @@ These are project-agnostic enough to ship as-is. Copy from this skill's [resourc
45
45
  |---|---|---|
46
46
  | `plan-feature` | [resources/foundation-skills/plan-feature/SKILL.md](resources/foundation-skills/plan-feature/SKILL.md) | Author phased implementation plans for a new feature, with interview-driven scoping. |
47
47
  | `create-spec` | [resources/foundation-skills/create-spec/SKILL.md](resources/foundation-skills/create-spec/SKILL.md) | Turn a raw feature prompt into a structured spec doc. |
48
- | `create-qa-use-cases` | [resources/foundation-skills/create-qa-use-cases/SKILL.md](resources/foundation-skills/create-qa-use-cases/SKILL.md) | Bootstrap a project's `QA_USE_CASES.md` from the active spec/plan. |
48
+ | `create-qa-use-cases` | [resources/foundation-skills/create-qa-use-cases/SKILL.md](resources/foundation-skills/create-qa-use-cases/SKILL.md) | Bootstrap a project's `QA_USE_CASES.md` from the active spec/plan. **E2E-gated** — ship only when `foundation_skills.add-e2e-test` is `enabled`; QA use-cases exist to seed e2e specs, so a no-e2e project has no use for it. |
49
49
  | `open-pr-from-context` | [resources/foundation-skills/open-pr-from-context/](resources/foundation-skills/open-pr-from-context/) | Thin SKILL.md wrapper around [scripts/open-pr.sh](resources/foundation-skills/open-pr-from-context/scripts/open-pr.sh) — bash script that publishes one `.vinta-ai-workflows/prs-context/{feature}/{phase}.md` file as a real PR + inline comments via the project's PR CLI (`gh` / `glab`). Used by `implement-plan` (when CLI + `yq`/`jq` are available) and standalone (to publish a `pending` file later). |
50
- | `prepare-worktree` | [resources/foundation-skills/prepare-worktree/SKILL.md](resources/foundation-skills/prepare-worktree/SKILL.md) | Provision a runnable, isolated git worktree for parallel feature work — symlink dep dirs (or copy / reinstall when the plan churns deps), copy + mutate env files, fork dev + test DBs when migrations are in scope, namespace docker-compose project + network, write a teardown-ready summary YAML. Used by `implement-plan` when Step 0 question (c) opts in; standalone for ad-hoc isolation. Opt-in (bucket A + ask-first via the bootstrap's **Optional foundation skills** interview). |
50
+ | `deslop-comments` | [resources/foundation-skills/deslop-comments/SKILL.md](resources/foundation-skills/deslop-comments/SKILL.md) | Rewrite comments + doc blocks touched during a task into Simple English — strip AI-slop vocabulary and negative framing; comment-only, no renames or behavior change. **Always ships** — the `review-phase` Layer 2 comment-hygiene check + fix loop dispatch it, so it must exist in the target. Also user-invokable standalone ("deslop these comments"). |
51
+ | `prepare-worktree` | [resources/foundation-skills/prepare-worktree/](resources/foundation-skills/prepare-worktree/) | Provision a runnable, isolated git worktree for parallel feature work — symlink dep dirs (or copy / reinstall when the plan churns deps), copy + mutate env files, fork dev + test DBs when migrations are in scope, namespace docker-compose project + network, OS-sandbox the worktree so main-checkout writes are blocked (bundled [scripts/sandbox-run.sh](resources/foundation-skills/prepare-worktree/scripts/sandbox-run.sh) — `sandbox-exec` / `bwrap`), write a teardown-ready summary YAML. Used by `implement-plan` when Step 0 question (c) opts in; standalone for ad-hoc isolation. Opt-in (bucket A + ask-first via the bootstrap's **Optional foundation skills** interview). |
52
+ | `thermo-nuclear-code-quality-review` | [resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md](resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md) | Opt-in deep structural-maintainability audit of a diff — abstraction quality, giant files, spaghetti-condition growth — harsher than the standard per-phase review; hunts for "code-judo" reframes that collapse branches / helpers / layers rather than polishing them. Read-only; hands each fix to the `fixer` agent. Invoked deliberately by the user, or escalated from the `review-phase` Layer 3 structural-simplification lens. Opt-in (bucket A + ask-first via the bootstrap's **Optional foundation skills** interview). |
51
53
 
52
- These four reference each other:
54
+ These reference each other:
53
55
 
54
- - `plan-feature` reads spec; dispatches `create-qa-use-cases` when the doc is missing.
56
+ - `plan-feature` reads spec; dispatches `create-qa-use-cases` when the doc is missing (e2e-enabled projects only — see below).
55
57
  - `implement-plan` (bucket B below) writes per-phase PR-context files following [resources/prs-context-template.md](resources/prs-context-template.md), then invokes `open-pr-from-context` when a PR CLI is detected.
56
58
 
57
- Copy all four or none — they form a unit.
59
+ The **always-on unit** is `plan-feature` + `create-spec` + `open-pr-from-context` — copy all three or none. `deslop-comments` also always ships (independently — it doesn't cross-reference the trio), because the `review-phase` Layer 2 comment-hygiene check + fix loop dispatch it; a target missing it would leave that review step dangling. `create-qa-use-cases` joins the unit **only when `foundation_skills.add-e2e-test` is `enabled`**; when add-e2e-test is disabled (or absent), don't ship it and set `foundation_skills.create-qa-use-cases: disabled` in `.vinta-ai-workflows.yaml` to match.
60
+
61
+ **E2E stripping in `plan-feature`.** `plan-feature` ships verbatim but carries e2e-only regions delimited by `<!-- e2e:start -->` / `<!-- e2e:end -->` lines. When copying it, run an e2e pass:
62
+
63
+ - **Always delete the marker lines themselves** (`<!-- e2e:start -->` / `<!-- e2e:end -->`) so they never reach the shipped file.
64
+ - **When `foundation_skills.add-e2e-test` is `disabled` or absent**, also delete everything *between* each marker pair. Result: a no-e2e project gets a `plan-feature` with zero Playwright / `QA_USE_CASES.md` / `pr-screenshots/` / `add-e2e-test` references — no noise in the plans it authors.
65
+ - **When `add-e2e-test` is `enabled`**, keep the enclosed content (strip only the markers).
58
66
 
59
67
  After copying, scan each for project-specific path references that no longer match the target (the bundled copies may still carry source-repo paths such as `<source-repo>/ai-plans/` or `apps/<service>/`). Replace with the target's paths from the inventory.
60
68
 
61
- ### B. Generate — `implement-plan`, `amend-plan`, `systematic-debugging` from templates
69
+ ### B. Generate — the **plan-execution unit** + `systematic-debugging` from templates
70
+
71
+ Project-specific skills, generated from templates because their bodies cite real test commands, branch conventions, agent dispatch table, PR / co-author policy, or selected MCP tools.
72
+
73
+ The forward-execution + amend flow is decomposed into a **plan-execution unit** — two thin conductors that dispatch to three shared single-purpose sub-skills. All five render from [resources/plan-execution/shell/](resources/plan-execution/shell/), which pulls shared bodies from [resources/plan-execution/partials/](resources/plan-execution/partials/) (see [Rendering the plan-execution unit](#rendering-the-plan-execution-unit) below):
74
+
75
+ - **`implement-plan`** — thin conductor: parse plan → classify phases → resolve one `WORKROOT` → per-phase loop (dispatch to the three sub-skills) → track → report. From `shell/implement-plan-template.md`.
76
+ - **`amend-plan`** — thin conductor: history rewriting (revise plan, amend commits, force-push, rebase stacked downstreams, refresh PR-context). From `shell/amend-plan-template.md`. Reuses `implement-phase` (for `amend-existing` rewrites) + `review-phase` (verbatim), keeps its own git topology.
77
+ - **`implement-phase`** — compose implementer prompt + pick model + spawn one implementer subagent. From `shell/implement-phase-template.md`.
78
+ - **`review-phase`** — three-layer review + fix loop. From `shell/review-phase-template.md`. **Shared by all three conductors** (implement-plan, amend-plan, systematic-debugging).
79
+ - **`integrate-phase`** — push the reviewed phase + open PR via context file. From `shell/integrate-phase-template.md`, rendered **commit-strategy-resolved** (see the [commit-strategy substitution table](#commit_strategy_-substitution-table)).
80
+
81
+ The five are a **co-shipped unit**: `implement-plan` is always generated, so `implement-phase` / `review-phase` / `integrate-phase` are always generated alongside it (they are its decomposition, not independently opt-in — there is no `foundation_skills` enum entry for them, and the bootstrap interview never asks about them). `amend-plan` is likewise always generated. They are separate `ai-tools/skills/<name>/SKILL.md` files so each can be reviewed and evolved on its own.
82
+
83
+ - **`systematic-debugging`** — root-cause-first debugging flow with project-specific reproduction commands and enforced observability MCP evidence-gathering. From [resources/systematic-debugging-template.md](resources/systematic-debugging-template.md) plus the **MCP-agnostic** evidence-categories block at [resources/systematic-debugging-mcp-tools.md](resources/systematic-debugging-mcp-tools.md) — the rendered SKILL.md instructs the agent to list available MCP tools at runtime and map them to evidence categories (error tracking, traces, logs, metrics, alerts, deploys, dashboards), instead of hardcoding tool names that go stale. **Opt-in** — only generated when `foundation_skills.systematic-debugging` is `enabled` in `.vinta-ai-workflows.yaml`. The free-form list of MCP server identifiers rendered into the body comes from `skills.systematic-debugging.observability_mcp_servers` in the same config. It reuses `review-phase` for its Phase 4 review gate (an `invoke [review-phase]` reference — always resolvable because the plan-execution unit is always shipped).
62
84
 
63
- Project-specific skills, generated from templates because their bodies cite real test commands, branch conventions, agent dispatch table, PR / co-author policy, or selected MCP tools:
85
+ Every plan-execution shell + `systematic-debugging` consumes the placeholder set below — render each by substituting from the inventory + Step 0 interview answers. `systematic-debugging` reuses the same set plus two extra placeholders (`{{OBSERVABILITY_MCP_BLOCK}}`, `{{OBSERVABILITY_MCP_LIST}}`) rendered from the catalogue.
64
86
 
65
- - **`implement-plan`** — forward execution: drives a fresh plan phase-by-phase. From [resources/implement-plan-template.md](resources/implement-plan-template.md).
66
- - **`amend-plan`** — history rewriting: revises an in-flight plan, amends already-implemented phase commits, force-pushes, rebases stacked downstream branches. From [resources/amend-plan-template.md](resources/amend-plan-template.md). Companion to `implement-plan` — same agents, same review gates, same PR-context flow, opposite git topology direction.
67
- - **`systematic-debugging`** — root-cause-first debugging flow with project-specific reproduction commands and enforced observability MCP evidence-gathering. From [resources/systematic-debugging-template.md](resources/systematic-debugging-template.md) plus the **MCP-agnostic** evidence-categories block at [resources/systematic-debugging-mcp-tools.md](resources/systematic-debugging-mcp-tools.md) — the rendered SKILL.md instructs the agent to list available MCP tools at runtime and map them to evidence categories (error tracking, traces, logs, metrics, alerts, deploys, dashboards), instead of hardcoding tool names that go stale. **Opt-in** — only generated when `foundation_skills.systematic-debugging` is `enabled` in `.vinta-ai-workflows.yaml`. The free-form list of MCP server identifiers rendered into the body comes from `skills.systematic-debugging.observability_mcp_servers` in the same config.
87
+ #### Rendering the plan-execution unit
68
88
 
69
- `implement-plan` and `amend-plan` consume the same placeholder set below — render each by substituting from the inventory + Step 0 interview answers. `systematic-debugging` reuses the same set plus two extra placeholders (`{{OBSERVABILITY_MCP_BLOCK}}`, `{{OBSERVABILITY_MCP_LIST}}`) rendered from the catalogue.
89
+ Shells under `plan-execution/shell/` are thin — most content lives in `plan-execution/partials/` and is spliced in at render time. Per shell, in order:
90
+
91
+ 1. **Expand includes.** Replace every include directive with the referenced partial content:
92
+ - `<!-- include: partials/<file>.md -->` — the whole partial body.
93
+ - `<!-- include: partials/<file>.md#BLOCK -->` — only the block between `<!-- block-begin: BLOCK -->` / `<!-- block-end: BLOCK -->` (markers excluded). Includes may nest (e.g. `implementer-prompt.md#FULL` itself includes `#INNER_OUTER_LOOP`) — expand recursively.
94
+ - `<!-- include: partials/commit-strategy/<RESOLVED>.md#BLOCK -->` — bind `<RESOLVED>` to the project's `policies.commit_strategy` (`stacked` → `stacked.md`, `modular` → `modular.md`). For `ask`, see the split below.
95
+ - Ignore the `rendering-guidance` appendix at the bottom of `commit-strategy/modular.md` — it is derive-time reference for the `{{MODULAR_*}}` placeholders, not shippable content.
96
+ 2. **Substitute `{{PLACEHOLDER}}`** using the table below (+ the commit-strategy table).
97
+ 3. **Write** the fully-expanded, fully-substituted body to `ai-tools/skills/<name>/SKILL.md`. No `<!-- include -->` marker and no `{{...}}` may survive in the shipped file.
98
+
99
+ **The `WORKROOT` seam.** The conductor resolves `WORKROOT` / `BASE_BRANCH` / `SANDBOX_TIER` once (`partials/worktree-seam.md#WORKROOT_RESOLUTION`) and every sub-skill uses them as data — this is why the rendered skills carry no scattered `if use_worktree` branches. `<WORKROOT>` / `<BASE_BRANCH>` / `<main_checkout>` / `<RESOLVED>` are **angle-bracket runtime slots the agent fills at execution time**, not `{{...}}` derive-time placeholders — leave them verbatim in the shipped file.
100
+
101
+ **`ask` → two `integrate-phase` variants, no dual-render.** When `policies.commit_strategy = ask`, render `integrate-phase-template.md` **twice**:
102
+ - `ai-tools/skills/integrate-phase-stacked/SKILL.md` — `<RESOLVED>` = `stacked`, `{{INTEGRATE_PHASE_NAME}}` = `integrate-phase-stacked`.
103
+ - `ai-tools/skills/integrate-phase-modular/SKILL.md` — `<RESOLVED>` = `modular`, `{{INTEGRATE_PHASE_NAME}}` = `integrate-phase-modular`.
104
+
105
+ The conductor's `{{INTEGRATE_PHASE_DISPATCH}}` then renders as a one-line runtime dispatch on `run_options.commit_strategy_resolved` (see the commit-strategy table). For `stacked` / `modular` projects, `integrate-phase-template.md` renders **once** to `ai-tools/skills/integrate-phase/` with a single topology body and no runtime branch.
106
+
107
+ The remaining per-shell placeholders:
70
108
 
71
109
  | Placeholder | Source | Example |
72
110
  |---|---|---|
@@ -93,35 +131,42 @@ Project-specific skills, generated from templates because their bodies cite real
93
131
  | `{{ANTI_GIT_ADD_ALL_REASON}}` | derived from common artifacts in target | `secrets, .env files, build artifacts, .auth/ live in the tree` |
94
132
  | `{{STAGE_PATTERN}}` | derived from monorepo shape | `apps/... lib/... e2e/... ai-plans/...` or `<app>/... tests/... ai-plans/...` |
95
133
  | `{{PROJECT_SKILLS_LIST}}` | computed from skills emitted | comma-separated names of the skills the project actually has |
96
- | `{{AGENT_DISPATCH_TABLE}}` | from [vinta-derive-subagents](../vinta-derive-subagents/SKILL.md) output | markdown table mapping phase shapes → agent type |
134
+ | `{{AGENT_DISPATCH_TABLE}}` | from [vinta-derive-subagents](../vinta-derive-subagents/SKILL.md) output | markdown table mapping phase shapes → agent type. Rendered into `implement-phase`. |
135
+ | `{{INTEGRATE_PHASE_NAME}}` | derived from `policies.commit_strategy` | The integrate-phase skill's own `name:` + dir. `integrate-phase` for `stacked` / `modular`; `integrate-phase-stacked` / `integrate-phase-modular` for the two `ask` renders. |
136
+ | `{{INTEGRATE_PHASE_DISPATCH}}` | derived from `policies.commit_strategy` | How the `implement-plan` conductor names the integrate step. `stacked` / `modular`: `` [integrate-phase](../integrate-phase/SKILL.md) ``. `ask`: `` the resolved integrate-phase variant — [integrate-phase-stacked](../integrate-phase-stacked/SKILL.md) when `run_options.commit_strategy_resolved = stacked-branches`, else [integrate-phase-modular](../integrate-phase-modular/SKILL.md) ``. |
137
+ | `{{INTEGRATE_PHASE_LINK}}` | derived from `policies.commit_strategy` | Path used by `amend-plan`'s PR-template cross-reference. `../integrate-phase/SKILL.md` for `stacked` / `modular`; `../integrate-phase-stacked/SKILL.md` for `ask` (amend-plan only runs under the stacked topology). |
97
138
  | `{{OBSERVABILITY_MCP_BLOCK}}` | rendered from [resources/systematic-debugging-mcp-tools.md](resources/systematic-debugging-mcp-tools.md) | the verbatim "Phase 0 evidence categories" block — same content for every project. When `skills.systematic-debugging.observability_mcp_servers` is empty, renders the no-tools fallback paragraph instead. The block is MCP-agnostic on purpose: tool names are discovered at runtime, not baked at generation time. |
98
139
  | `{{OBSERVABILITY_MCP_LIST}}` | derived from `skills.systematic-debugging.observability_mcp_servers` | comma-separated MCP server identifiers as the user named them at bootstrap (e.g. `sentry, datadog, our-internal-traces`). Renders `none configured` when empty. |
99
140
  | `{{DEPENDENCY_LICENSE_BLOCK}}` | derived from `policies.dependency_licenses` | Top-level `## Adding new third-party dependencies` section. Empty string when `enforcement: off` or the block is absent. When `block` / `warn`: short paragraph naming the enforcement mode + the forbidden SPDX list + the pre-install check (`npm view <pkg> license`, PyPI metadata, etc.) + a one-line pointer to the **Dependency licenses** section in [AGENTS.md](AGENTS.md) for the full table including `allowed_overrides` and `notes`. See [resources/dependency-license-block.md](resources/dependency-license-block.md) for the canonical body. |
100
141
  | `{{DEPENDENCY_LICENSE_LAYER1_CHECK}}` | derived from `policies.dependency_licenses` | Layer 1 review checklist item. Empty string when `enforcement: off`. When `block` / `warn`: `6. **Dependency license scan**: \`git diff package.json pyproject.toml ...\` (project-relevant manifests) — for every added dep look up its SPDX license (\`npm view <pkg> license\`, PyPI metadata, repo \`LICENSE\`). A license in \`policies.dependency_licenses.forbidden_spdx\` and not in \`allowed_overrides\` is a BLOCKER (when \`block\`) or a SHOULD-FIX (when \`warn\`). A missing / \`UNKNOWN\` / undeclared license is **always a BLOCKER** regardless of enforcement mode — there is no override to silently bless undisclosed terms.` |
101
142
  | `{{DEPENDENCY_LICENSE_RULE_LINE}}` | derived from `policies.dependency_licenses` | Important-rules bullet. Empty string when `enforcement: off`. When `block`: `- **License check before any new dep.** Refuse \`npm add\` / \`pnpm add\` / \`pip install\` / \`poetry add\` / \`uv add\` / \`cargo add\` / \`go get\` when the package's SPDX license is in the forbidden list — see AGENTS.md **Dependency licenses**. User can grant a one-off override after acknowledging the violation; record the override in \`policies.dependency_licenses.allowed_overrides\` before re-running.` When `warn`: same line but `Proceed but record the violation in the phase report` instead of `Refuse`. |
102
- | `{{COMMIT_STRATEGY_*}}` family (`{{COMMIT_STRATEGY_STEP0_QUESTION}}`, `{{COMMIT_STRATEGY_STEP0_TRAILER}}`, `{{COMMIT_STRATEGY_CONFIRM_NOTE}}`, `{{BRANCH_NAMING_PATTERN_SUMMARY}}`, `{{BRANCH_NAMING_BLOCK}}`, `{{PER_PHASE_COMMIT_BLOCK}}`, `{{PR_OPEN_TIMING_BLOCK}}`, `{{PRS_CONTEXT_FILE_PATH_DESCRIPTION}}`, `{{TRACKING_BRANCH_FIELD}}`, `{{TRACKING_PHASE_BRANCH_FIELD}}`, `{{FINAL_REPORT_BRANCH_SUMMARY}}`, `{{BRANCH_CHECKLIST_LINE}}`, `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}`) | Step 0 (`policies.commit_strategy`) | Render based on the project's commit strategy (`stacked-branches` / `modular-commits` / `ask`). See the per-placeholder substitution table immediately after this row for the three columns. The `{{BRANCH_PUSH_HEADING}}` placeholder above is also part of this family — its substitution depends on the strategy. |
143
+ | `{{COMMIT_STRATEGY_*}}` family (`{{COMMIT_STRATEGY_STEP0_QUESTION}}`, `{{COMMIT_STRATEGY_STEP0_TRAILER}}`, `{{COMMIT_STRATEGY_CONFIRM_NOTE}}`, `{{BRANCH_NAMING_PATTERN_SUMMARY}}`, `{{BRANCH_NAMING_BLOCK}}`, `{{PER_PHASE_COMMIT_BLOCK}}`, `{{PR_OPEN_TIMING_BLOCK}}`, `{{PRS_CONTEXT_FILE_PATH}}`, `{{TRACKING_BRANCH_FIELD}}`, `{{TRACKING_PHASE_BRANCH_FIELD}}`, `{{FINAL_REPORT_BRANCH_SUMMARY}}`, `{{BRANCH_CHECKLIST_LINE}}`, `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}`, `{{BRANCH_PUSH_HEADING}}`) | Step 0 (`policies.commit_strategy`) | Render based on the project's commit strategy (`stacked-branches` / `modular-commits` / `ask`). Multi-line blocks now come from the commit-strategy partials; see the substitution table immediately after this row. |
103
144
 
104
145
  ### `{{COMMIT_STRATEGY_*}}` substitution table
105
146
 
106
- Per-placeholder rendering for each enum value of `policies.commit_strategy`. The `ask` column embeds **both** branch / commit / PR paths gated by inline runtime markers (`If \`run_options.commit_strategy_resolved = "modular-commits"\`:` ... `Else (\`stacked-branches\`):` ...) — same pattern already used for `pause_between_phases` / `generate_inline_comments`.
107
-
108
- | Placeholder | `stacked-branches` substitution | `modular-commits` substitution | `ask` substitution |
109
- |---|---|---|---|
110
- | `{{COMMIT_STRATEGY_STEP0_QUESTION}}` | empty string | empty string | a third opt-in block analogous to 4a/4b: `\n\n c. **Commit strategy?** *"This project's commit_strategy is set to ask. Pick one for this run: one branch + one PR per phase (stacked), or one branch + one PR for the whole plan with one atomic commit per logical unit (modular)?"* Options: \`Stacked branches — one branch + PR per phase\`, \`Modular commits — atomic commits, one PR for whole plan\`. Cache answer in tracking under \`run_options.commit_strategy_resolved\`.` (note: the substitution starts with `\n\n` because the placeholder hugs the end of the preceding paragraph in the template — under stacked / modular the placeholder vanishes without leaving a stray blank line) |
111
- | `{{COMMIT_STRATEGY_STEP0_TRAILER}}` | empty string | empty string | ` Commit-strategy behavior follows \`run_options.commit_strategy_resolved\`.` (note: leading space — appended to the previous sentence) |
112
- | `{{COMMIT_STRATEGY_CONFIRM_NOTE}}` | empty string | empty string | ` + \`run_options.commit_strategy_resolved\`` (leading ` + ` — appends to the captured-options list) |
113
- | `{{BRANCH_NAMING_PATTERN_SUMMARY}}` | ``branch naming pattern (default: `plan/{plan-id-kebab}/phase-{phase-id}`)`` | ``plan branch (one branch for whole plan: `plan/{plan-id-kebab}`)`` | ``branch naming pattern (depends on `run_options.commit_strategy_resolved` — resolved at Step 0)`` |
114
- | `{{BRANCH_NAMING_BLOCK}}` | the current per-phase stacked block: `Branch naming: \`plan/{plan-id-kebab}/phase-{phase.id}\`.\n\n**First executed phase** (branches from \`{{DEFAULT_BRANCH}}\`):\n\`\`\`bash\ngit checkout {{DEFAULT_BRANCH}}\ngit pull --ff-only\ngit checkout -b plan/{plan-id-kebab}/phase-{phase.id}\n# subagent's commits land on this branch\ngit push -u origin plan/{plan-id-kebab}/phase-{phase.id}\n\`\`\`\n\n**Subsequent phases** (stacked on the previous phase's branch):\n\`\`\`bash\ngit checkout plan/{plan-id-kebab}/phase-{prev.id}\ngit checkout -b plan/{plan-id-kebab}/phase-{phase.id}\ngit push -u origin plan/{plan-id-kebab}/phase-{phase.id}\n\`\`\`` | single-branch block — see [Phase 3 substitution body](#modular-commits-branch-naming-block) | both blocks gated by `if run_options.commit_strategy_resolved == "modular-commits"` runtime markers |
115
- | `{{PER_PHASE_COMMIT_BLOCK}}` | the current 4-step stacked block (steps 7–10 of Step 1a): `7. Stage right files (NEVER \`git add -A\` — {{ANTI_GIT_ADD_ALL_REASON}}). Stage explicitly: \`git add {{STAGE_PATTERN}}\`.\n8. Commit with the repo's style — look at \`git log -10 --oneline\` first. {{COMMIT_STYLE_LINE}}.\n9. {{COAUTHOR_INSTRUCTION_LINE}}\n10. {{PUSH_INSTRUCTION_LINE}}` | the modular-commits multi-commit block — see [Phase 3 substitution body](#modular-commits-per-phase-commit-block) | both blocks gated by runtime markers |
116
- | `{{PR_OPEN_TIMING_BLOCK}}` | empty string (current behavior: PR opens per phase after review) | `**PR opens once after Phase 1 passes review.** Subsequent phases push commits to the same plan branch and append an "Phase {N} complete" comment to the PR (or the orchestrator re-runs \`open-pr.sh\` with the existing context file path — the script is idempotent for existing PRs).` | both blocks gated by runtime markers |
117
- | `{{PRS_CONTEXT_FILE_PATH_DESCRIPTION}}` | `` a `.vinta-ai-workflows/prs-context/{feature-kebab}/phase-{phase.id}.md` file `` | `` a single `.vinta-ai-workflows/prs-context/{feature-kebab}/plan.md` file (one PR per plan, not per phase) `` | `` either `.../phase-{phase.id}.md` or `.../plan.md` depending on `run_options.commit_strategy_resolved` `` |
118
- | `{{TRACKING_BRANCH_FIELD}}` | empty string (per-phase branch lives under `{{TRACKING_PHASE_BRANCH_FIELD}}`) | `top-level \`plan_branch:\` field` (single branch for whole plan) | both gated by runtime markers |
119
- | `{{TRACKING_PHASE_BRANCH_FIELD}}` | `, branch, base` (per-phase fields kept inline) | empty string (no per-phase branch under modular) | both gated by runtime markers |
120
- | `{{FINAL_REPORT_BRANCH_SUMMARY}}` | `branches pushed (with bases, in stack order)` | ``single plan branch `plan/{plan-id-kebab}` with commit log organized by phase`` | dynamic per resolved strategy |
121
- | `{{BRANCH_CHECKLIST_LINE}}` | `Stacked branch created; pushed.` | `Plan branch updated with phase commits; pushed.` | dynamic per resolved strategy |
122
- | `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}` | empty string | block of 3 extra checklist items (commit units planned upfront; each commit = one unit; tests in same commit as code; no "and" in commit messages) — see [Phase 3 substitution body](#modular-commits-checklist-block) | both variants gated by runtime markers |
123
-
124
- For `{{BRANCH_PUSH_HEADING}}` the substitutions are: `Push stacked branch` (stacked) / `Push to plan branch` (modular) / `Push branch` (ask — heading stays generic; the body inside the section explains both flows under runtime markers).
147
+ The multi-line blocks now live in the partials `plan-execution/partials/commit-strategy/{stacked,modular}.md` (marker-delimited blocks). The single-line values are documented in each partial's footer comment. Bind the block/value from the partial matching `policies.commit_strategy`. The `{{MODULAR_EXAMPLE_*}}` + `{{MODULAR_COMMIT_MESSAGE_FORMAT_BLOCK}}` placeholders inside `modular.md`'s `PER_PHASE_COMMIT` block are resolved from that partial's own **rendering-guidance appendix** (driven by `policies.commit_style` — conventional / imperative / other); that appendix is derive-time reference only and is never shipped.
148
+
149
+ **Where each block lands, and how `ask` is handled:**
150
+ - `BRANCH_NAMING` + `PR_OPEN_TIMING` land in **`integrate-phase`**. Under `ask`, `integrate-phase` is rendered **twice** (once per strategy — see [Rendering the plan-execution unit](#rendering-the-plan-execution-unit)); each variant bakes in one strategy's blocks with **no runtime marker**. The conductor dispatches by name via `{{INTEGRATE_PHASE_DISPATCH}}`.
151
+ - `PER_PHASE_COMMIT` lands in **`implement-phase`** (the implementer prompt's working instructions). `implement-phase` is not split, so under `ask` it embeds **both** strategy blocks gated by inline runtime markers (`If \`run_options.commit_strategy_resolved = "modular-commits"\`: … Else (\`stacked-branches\`): …`) — same pattern as `pause_between_phases` / `generate_inline_comments`.
152
+ - `CHECKLIST` + all single-line values land in the single **`implement-plan`** conductor; under `ask` they render dynamically on `run_options.commit_strategy_resolved` (runtime markers for the block, inline `If … Else …` for single-line values).
153
+
154
+ | Placeholder | Lands in | `stacked-branches` | `modular-commits` | `ask` |
155
+ |---|---|---|---|---|
156
+ | `{{COMMIT_STRATEGY_STEP0_QUESTION}}` | implement-plan | empty string | empty string | a third opt-in block analogous to 4a/4b: `\n\n c. **Commit strategy?** *"This project's commit_strategy is set to ask. Pick one for this run: one branch + one PR per phase (stacked), or one branch + one PR for the whole plan with one atomic commit per logical unit (modular)?"* Options: \`Stacked branches — one branch + PR per phase\`, \`Modular commits — atomic commits, one PR for whole plan\`. Cache answer in tracking under \`run_options.commit_strategy_resolved\`.` (starts with `\n\n`; under stacked / modular the placeholder vanishes without a stray blank line) |
157
+ | `{{COMMIT_STRATEGY_STEP0_TRAILER}}` | implement-plan | empty string | empty string | ` Commit-strategy behavior follows \`run_options.commit_strategy_resolved\`.` (leading space) |
158
+ | `{{COMMIT_STRATEGY_CONFIRM_NOTE}}` | implement-plan | empty string | empty string | ` + \`run_options.commit_strategy_resolved\`` (leading ` + `) |
159
+ | `{{BRANCH_NAMING_PATTERN_SUMMARY}}` | implement-plan | `stacked.md` footer value | `modular.md` footer value | ``branch naming pattern (depends on `run_options.commit_strategy_resolved` — resolved at Step 0)`` |
160
+ | `{{BRANCH_NAMING_BLOCK}}` | integrate-phase | `stacked.md#BRANCH_NAMING` | `modular.md#BRANCH_NAMING` | baked per-variant (no runtime marker) |
161
+ | `{{PER_PHASE_COMMIT_BLOCK}}` | implement-phase | `stacked.md#PER_PHASE_COMMIT` | `modular.md#PER_PHASE_COMMIT` | both blocks gated by runtime markers |
162
+ | `{{PR_OPEN_TIMING_BLOCK}}` | integrate-phase | `stacked.md#PR_OPEN_TIMING` | `modular.md#PR_OPEN_TIMING` | baked per-variant (no runtime marker) |
163
+ | `{{PRS_CONTEXT_FILE_PATH}}` | implement-plan + integrate-phase | `stacked.md` footer value | `modular.md` footer value | ``either `.../phase-{phase.id}.md` or `.../plan.md` depending on `run_options.commit_strategy_resolved` `` |
164
+ | `{{TRACKING_BRANCH_FIELD}}` | implement-plan | `stacked.md` footer value (empty) | `modular.md` footer value | both gated by runtime markers |
165
+ | `{{TRACKING_PHASE_BRANCH_FIELD}}` | implement-plan | `stacked.md` footer value (`, branch, base`) | `modular.md` footer value (empty) | both gated by runtime markers |
166
+ | `{{FINAL_REPORT_BRANCH_SUMMARY}}` | implement-plan | `stacked.md` footer value | `modular.md` footer value | dynamic per resolved strategy |
167
+ | `{{BRANCH_CHECKLIST_LINE}}` | implement-plan | `stacked.md` footer value | `modular.md` footer value | dynamic per resolved strategy |
168
+ | `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}` | implement-plan | `stacked.md#CHECKLIST` (empty) | `modular.md#CHECKLIST` (3 items) | both variants gated by runtime markers |
169
+ | `{{BRANCH_PUSH_HEADING}}` | integrate-phase | `Push stacked branch` | `Push to plan branch` | baked per-variant (`Push stacked branch` / `Push to plan branch`) |
125
170
 
126
171
  ### `{{COMMIT_STRATEGY_REFUSAL_BLOCK}}` (amend-plan only)
127
172
 
@@ -152,15 +197,20 @@ Amending under modular commits requires rewriting an arbitrary number of inline
152
197
  Refuse with this guidance; do not proceed.
153
198
  ```
154
199
 
155
- The Phase 3 substitution bodies referenced above (`{{BRANCH_NAMING_BLOCK}}` modular variant, `{{PER_PHASE_COMMIT_BLOCK}}` modular variant, `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}` modular variant) live in [resources/implement-plan-template-modular-substitutions.md](resources/implement-plan-template-modular-substitutions.md) — a sister file that derive-skills reads when rendering `modular-commits` or `ask`.
200
+ The commit-strategy block bodies (branch naming, per-phase commit, PR-open timing, checklist) for both strategies live in [resources/plan-execution/partials/commit-strategy/](resources/plan-execution/partials/commit-strategy/) — `stacked.md` + `modular.md`. derive-skills reads the one matching `policies.commit_strategy` (both, under `ask`).
156
201
 
157
- Render each template with substitutions, write to:
202
+ Render each template (expand includes → substitute placeholders → write), producing:
158
203
 
159
- - `ai-tools/skills/implement-plan/SKILL.md` (from `implement-plan-template.md`)
160
- - `ai-tools/skills/amend-plan/SKILL.md` (from `amend-plan-template.md`)
204
+ - `ai-tools/skills/implement-plan/SKILL.md` (from `plan-execution/shell/implement-plan-template.md`)
205
+ - `ai-tools/skills/implement-phase/SKILL.md` (from `plan-execution/shell/implement-phase-template.md`)
206
+ - `ai-tools/skills/review-phase/SKILL.md` (from `plan-execution/shell/review-phase-template.md`)
207
+ - `ai-tools/skills/integrate-phase/SKILL.md` (from `plan-execution/shell/integrate-phase-template.md`) — **or**, under `ask`, `ai-tools/skills/integrate-phase-stacked/SKILL.md` + `ai-tools/skills/integrate-phase-modular/SKILL.md` (same template rendered twice)
208
+ - `ai-tools/skills/amend-plan/SKILL.md` (from `plan-execution/shell/amend-plan-template.md`)
161
209
  - `ai-tools/skills/systematic-debugging/SKILL.md` (from `systematic-debugging-template.md`) — only when `foundation_skills.systematic-debugging: enabled`
162
210
 
163
- Validate each output: every `{{PLACEHOLDER}}` should be replaced; if any survive, the template needs a new substitution rule. `implement-plan` and `amend-plan` share the same placeholder set — fix substitutions for both at the same time. `systematic-debugging` adds the two MCP-related placeholders; if a project picks an observability tool the catalogue doesn't list, ask the user for a free-form snippet (heading, required calls, fields to extract) and inline it before saving — and consider upstreaming the snippet to [resources/systematic-debugging-mcp-tools.md](resources/systematic-debugging-mcp-tools.md).
211
+ The first five are the **plan-execution unit** — render them together (they share partials + placeholders + cross-links); a change to one partial ripples to every shell that includes it.
212
+
213
+ Validate each output before saving: **no `<!-- include: … -->` marker and no `{{PLACEHOLDER}}` may survive** (angle-bracket runtime slots like `<WORKROOT>` / `<BASE_BRANCH>` / `<RESOLVED-already-bound>` are expected to remain — they are execution-time, not derive-time). If a placeholder survives, the shell/partial needs a substitution rule. Cross-link check: the conductor's links to `implement-phase` / `review-phase` / the integrate variant resolve, and `amend-plan` / `systematic-debugging` → `review-phase` resolve. `systematic-debugging` adds the two MCP-related placeholders; if a project picks an observability tool the catalogue doesn't list, ask the user for a free-form snippet (heading, required calls, fields to extract) and inline it before saving — and consider upstreaming the snippet to [resources/systematic-debugging-mcp-tools.md](resources/systematic-debugging-mcp-tools.md).
164
214
 
165
215
  ### C. Optional — ask the user
166
216
 
@@ -171,7 +221,7 @@ Two skills depend on whether the project actually does the thing. Ask via `AskUs
171
221
  | `add-e2e-test` | "Does this project have e2e tests, or plan to add them?" Options: `Yes — already has them`, `Yes — planning to add`, `No — skip` | Ask if user has an existing template (path / URL). If yes → copy + adapt. If no → draft from scratch using the canonical structure (see "Skill SKILL.md structure" below) interviewing user about: e2e framework, page-object pattern, auth/storage-state pattern, seed-helpers location, tenant scoping in seeds, screenshot conventions for PRs. |
172
222
  | `add-env-var` | "Does this project have a non-trivial env-var propagation flow (multiple files / build configs / CI to update for one new var)?" Options: `Yes`, `No — single .env file is enough` | Ask if user has an existing template. If yes → copy + adapt. If no → draft from scratch interviewing user about every layer the new var must touch (`.env.example`, build tool envPrefix allowlist, build cache hash inputs, app config module, AGENTS.md env section, CI workflows, deploy-time injection). |
173
223
  | `add-one-off-script` | "Does this project ever need one-off operational scripts — backfills, cleanups, ad-hoc data fixes?" Options: `Yes — enable`, `No — skip`. Recommend `Yes` for any project with a relational DB; the skill bundles a `BaseOneOffScript` class enforcing dry-run-by-default, idempotent re-runs, batched DB ops, streamed reads, segmented CSV backups (1M cells/file, never nested across tables), interruption-safe signal handlers, and console + filesystem + S3 logging. | Copy [resources/foundation-skills/add-one-off-script/](resources/foundation-skills/add-one-off-script/) into `ai-tools/skills/add-one-off-script/` (SKILL.md + the bundled `resources/one_off_script_base.{py,ts}` templates). Then ask the user whether they want the base class file pre-staged at `<scripts_dir>/one_off/_base.{py,ts}` — default `Yes` for projects whose primary lang is Python or TS. The skill itself prompts to copy the base class on first use if the user said `No` here. |
174
- | `prepare-worktree` | "Do you ever want to run plans / experiments inside an isolated git worktree (separate runnable copy of the app with its own DB, env, compose stack) instead of swapping branches in the main checkout?" Options: `Yes — enable`, `No — skip`. Recommend `Yes` for projects with long migrations, multi-day plans, or where the user is likely to want parallel concurrent work. | Copy [resources/foundation-skills/prepare-worktree/SKILL.md](resources/foundation-skills/prepare-worktree/SKILL.md) into `ai-tools/skills/prepare-worktree/SKILL.md`. Then ask three short follow-ups landing in `skills.prepare-worktree.*`: worktree root (default `.claude/worktrees` for claude-code projects, `../<repo>-wt-` sibling dirs otherwise), default deps strategy (`symlink` / `copy` / `reinstall`), default test-DB strategy (`always-fork` / `fork-on-schema-change` / `share`). Also ask whether `implement-plan` should default its Step 0 question (c) to `yes` — lands in `run_options.implement-plan.use_worktree`. |
224
+ | `prepare-worktree` | "Do you ever want to run plans / experiments inside an isolated git worktree (separate runnable copy of the app with its own DB, env, compose stack) instead of swapping branches in the main checkout?" Options: `Yes — enable`, `No — skip`. Recommend `Yes` for projects with long migrations, multi-day plans, or where the user is likely to want parallel concurrent work. | Copy the whole [resources/foundation-skills/prepare-worktree/](resources/foundation-skills/prepare-worktree/) dir into `ai-tools/skills/prepare-worktree/` — both `SKILL.md` and `scripts/sandbox-run.sh` (mark the script executable: `chmod +x ai-tools/skills/prepare-worktree/scripts/sandbox-run.sh`). Then ask three short follow-ups landing in `skills.prepare-worktree.*`: worktree root (default `.claude/worktrees` for claude-code projects, `../<repo>-wt-` sibling dirs otherwise), default deps strategy (`symlink` / `copy` / `reinstall`), default test-DB strategy (`always-fork` / `fork-on-schema-change` / `share`). Also ask whether `implement-plan` should default its Step 0 question (c) to `yes` — lands in `run_options.implement-plan.use_worktree`. |
175
225
 
176
226
  If user answers "No" to any: don't ship that skill. Record nothing — the foundation set is just smaller.
177
227
 
@@ -232,9 +282,9 @@ Length: 100–300 lines. Shorter = under-specified. Longer = should probably spl
232
282
  ## Rules
233
283
 
234
284
  - **Don't ship copy-pasted source-project SKILL.md files unchanged.** Run search-and-replace for source project names / paths / commands before saving.
235
- - **Foundation set is a unit.** Always copy `plan-feature` + `create-spec` + `create-qa-use-cases` together — they reference each other.
236
- - **`implement-plan` is generated, not copied.** Its body has too much project-specific content for verbatim shipping.
237
- - **Optional skills (`add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`) are gated by user answer.** Don't ship them by default.
285
+ - **Foundation set is a unit.** Always copy `plan-feature` + `create-spec` + `open-pr-from-context` together — they reference each other. `create-qa-use-cases` joins only when `add-e2e-test` is enabled; when it's not, strip `plan-feature`'s e2e regions (the `<!-- e2e:start/end -->` pass) so no cross-link dangles.
286
+ - **The plan-execution unit is generated, not copied.** `implement-plan` + `implement-phase` + `review-phase` + `integrate-phase` + `amend-plan` all have too much project-specific content for verbatim shipping. They are separate files but a **co-shipped unit** — never emit the conductors without the three sub-skills; a conductor whose `[implement-phase]` / `[review-phase]` / integrate link dangles is a broken skill.
287
+ - **Optional skills (`add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`, `thermo-nuclear-code-quality-review`) are gated by user answer.** Don't ship them by default.
238
288
  - **Each skill solves one job.** Two unrelated checklists → split.
239
289
  - **Reference real files in the target.** Skill links must point to existing paths.
240
290
  - **Skills auto-load by description.** Specific triggers, specific outcomes.
@@ -242,7 +292,7 @@ Length: 100–300 lines. Shorter = under-specified. Longer = should probably spl
242
292
 
243
293
  ## Pitfalls
244
294
 
245
- - **Forgetting to substitute placeholders in `implement-plan`.** A surviving `{{TEST_CMD}}` is a runtime confusion. Validate before saving.
295
+ - **Forgetting to substitute placeholders (or expand includes) in the plan-execution skills.** A surviving `{{TEST_CMD}}` or `<!-- include: … -->` is a runtime confusion. Expand every include, substitute every `{{…}}`, and validate before saving. (Angle-bracket runtime slots like `<WORKROOT>` are meant to stay.)
246
296
  - **Copying foundation skills and forgetting to scrub source-project paths.** Bundled copies may still carry source-repo paths (e.g. `<source-repo>/ai-plans/`, `apps/<service>/`). Replace with the target's paths.
247
297
  - **Asking too many questions for optional skills.** If `vinta-analyze-codebase` shows no e2e dir and the user clearly has no e2e setup, it's OK to skip the question and not ship `add-e2e-test`. Use judgment.
248
298
  - **Skipping the foundation copy because "the user already has them".** They probably don't. Confirm by reading the target's `ai-tools/skills/` (if it exists at all). If it does have these, ask the user "refresh from bundled copies, or keep what you have?".
@@ -254,6 +304,7 @@ After all skills written:
254
304
  1. Each `ai-tools/skills/<name>/SKILL.md` has frontmatter `name` + `description`, plus markdown body.
255
305
  2. `name` field matches dir name.
256
306
  3. No `{{PLACEHOLDER}}` strings survive in any output skill.
257
- 4. Foundation skills (plan-feature, create-spec, create-qa-use-cases) reference each other correctly via `[name](../<name>/SKILL.md)` links.
258
- 5. `implement-plan` body cites real commands + the project's actual code host + branch / PR / co-author policy.
259
- 6. Setup script ([vinta-install-ai-tools-setup](../vinta-install-ai-tools-setup/SKILL.md)) runs cleanly with the new skills in place.
307
+ 4. Foundation skills (plan-feature, create-spec, open-pr-from-context — plus create-qa-use-cases when e2e is enabled) reference each other correctly via `[name](../<name>/SKILL.md)` links. When e2e is disabled, confirm `plan-feature` has **no** surviving `<!-- e2e:start/end -->` markers and no dangling link to `create-qa-use-cases` / `add-e2e-test`.
308
+ 5. **Plan-execution unit intact.** All five files exist; no surviving `<!-- include -->` markers; the conductor's `[implement-phase]` / `[review-phase]` / integrate-phase links resolve, and `amend-plan` + `systematic-debugging` (when shipped) → `[review-phase]` resolve. Under `ask`: both `integrate-phase-stacked` + `integrate-phase-modular` exist and the conductor's dispatch line names both.
309
+ 6. `implement-plan` (+ the plan-execution sub-skills) cite real commands + the project's actual code host + branch / PR / co-author policy.
310
+ 7. Setup script ([vinta-install-ai-tools-setup](../vinta-install-ai-tools-setup/SKILL.md)) runs cleanly with the new skills in place.
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: deslop-comments
3
+ description: Rewrite code comments and docs touched during the current conversation into Simple English, stripping AI-slop / AI-lingo vocabulary and negative framing. Use when the user says "deslop these comments", "clean up the comments we just wrote", "rewrite this in plain English", or after a review flags comments as convoluted/AI-sounding. Comment-only - never changes function names, APIs, or behavior.
4
+ ---
5
+
6
+ # Deslop comments
7
+
8
+ Rewrites comments and doc blocks into short, direct Simple English. This is a comment-only pass: no renames, no logic changes, no behavior changes.
9
+
10
+ ## Scope
11
+
12
+ Default scope is **files the current task actually created or edited** - find the candidate set with `git diff --name-only` / `git status --short` against the base the conversation started from. If the user names specific files, a directory, or a Pull Request instead, use that scope.
13
+
14
+ Do not expand scope to a file just because the agent `Read` it or reviewed it in passing - a debugging or review session routinely reads many unrelated files, and reading one is not authorization to rewrite its comments. If there is no edited-file set and no explicit scope from the user, ask which files to cover.
15
+
16
+ ## What counts as slop
17
+
18
+ Rewrite a comment if it has any of:
19
+
20
+ 1. **Dense, multi-clause sentences** that try to explain everything at once instead of one idea per sentence.
21
+ 2. **Negative / before-state framing** - describing removed code, or leading with "not X" when X was never the point. Keep "not X, it's Y" only for a genuine edge case, a non-obvious gotcha, or bug-fix rationale worth flagging; otherwise state the current behavior directly.
22
+ 3. **AI buzzwords** in place of plain software engineering vocabulary. Common offenders and their replacements:
23
+ - `gate` / `gates` / `gating` → "check", "decides whether to show...", "authentication check", "feature check"
24
+ - `guard` → "check" / "prevent"
25
+ - `backstop` → "handles the case where..." / "protects us either way"
26
+ - `load-bearing` → "useful"
27
+ - `predicate` → "helper"
28
+ - `presentational` (component) → "this component only displays the result"
29
+ - "flattens to a bare new Error" → "turns into a plain new Error"
30
+ - `harness` → name the concrete thing: "test setup", "test wrapper", "fixture", "mock server", or "runner"
31
+ - `realm` → "environment", "runtime", or "context" (unless it's the precise JS-realm technical term)
32
+ - `landed` → "implemented"
33
+ - `mint` → "created"
34
+ - `leverage` → "use"
35
+ - `utilize` → "use"
36
+ - `surface` → "show" / "return" / "report"
37
+ - `plumb` / `wire` → "pass" / "connect" / "call"
38
+ - `broker` → "handle" / "route" / name the service/helper (keep broker if that's the concept name in some architecture, library, service, etc.)
39
+ - `canonical` → "shared" / "standard"
40
+ - `churn` → "unnecessary changes"
41
+ - `invariant` → "rule" / "condition" / "constraint" / or something like "state that must stay true"
42
+
43
+ Do not apply this as a blind blacklist. Keep the original word when it is the precise domain term, especially for security, type-system, React, database, FHIR, or JavaScript runtime concepts. For example, `opaque` is a precise security/API term for an id, token, or string whose structure and meaning a caller must not depend on ("recipient is an opaque viewer-supplied string; never log it"); it is not on the replacement list above, so leave it — "generic" would drop that contract.
44
+ 4. **Undocumented return shapes.** A function returning an object or union should say what each field/variant means, not just that it "returns a result."
45
+ 5. **Any other AI-slop or AI-lingo words, framing, structure.**
46
+
47
+ ## What to leave alone
48
+
49
+ - Function, type, and API names - this is a comment-only pass.
50
+ - Log strings and error messages shown to users.
51
+ - Generated files (e.g. `*.gen.ts`, Prisma client output).
52
+ - Genuine security markers or contracts written as caps-negatives (`NEVER log PHI`, `Does NOT upsert`) - these are useful warnings, not slop.
53
+ - User-facing UI copy (JSX text nodes rendered to the browser) - that's product copy, not a code comment.
54
+ - Already-clear one-liners that don't exhibit any of the problems above. Don't rewrite for the sake of rewriting.
55
+
56
+ ## Process
57
+
58
+ 1. Build the file list per Scope above.
59
+ 2. For each file, read every comment/doc block (`//`, `/* */`, `/** */`, `#`, `%`, depending on the language) and check it against "What counts as slop." Skip anything covered by "What to leave alone."
60
+ 3. Rewrite in place with `Edit`. Prefer one idea per sentence. When a function's return type is a union or object with meaningful fields, spell out what each branch/field means using the comment body or JSDoc (or similar) if it's a project pattern.
61
+ 4. After editing, confirm the diff is comment-only: `git diff -- <files>` should show no code-line changes, only comment text.
62
+ 5. Run the project's lint/typecheck on touched files (e.g. `pnpm biome check <files>`, `pnpm --filter <pkg> typecheck`) to confirm nothing broke.
63
+ 6. Summarize what changed in 1-2 sentences - don't restate every line edited.
64
+
65
+ ## Example
66
+
67
+ Bad (dense, negative framing, buzzwords):
68
+ ```
69
+ // Server-to-server auth gate. Fail closed if the token is unconfigured.
70
+ try {
71
+ if (!verifyServiceAuth(request)) { ... }
72
+ }
73
+ ```
74
+
75
+ Good (Simple English, positive framing, no AI-lingo structure):
76
+ ```
77
+ // Server-to-server authentication check. Deny the request if the token is unconfigured.
78
+ try {
79
+ if (!verifyServiceAuth(request)) { ... }
80
+ }
81
+ ```
82
+
83
+ Bad:
84
+ ```
85
+ // NOTE: this forward-block is a UI courtesy only, not a per-step gate.
86
+ // `currentStepIndex` derives from the current URL path — not from saved
87
+ // progress — so navigating to a later step's URL directly bypasses it
88
+ // entirely. The template deliberately ships no per-step route guards
89
+ // (forks may want different ordering/skipping rules); the integrity
90
+ // backstop is the server-side readiness check at intake completion,
91
+ // which refuses to finalize while any step is missing.
92
+ const canNavigateToStep = useCallback(...)
93
+ ```
94
+
95
+ Good:
96
+ ```
97
+ // This limits forward navigation in the UI only. `currentStepIndex` comes from
98
+ // the current URL path, not from saved progress, so visiting a later step's URL
99
+ // directly still works. The template intentionally ships no per-step route
100
+ // checks, since forks may want different ordering or skipping rules. The real
101
+ // safeguard is the server-side check at intake completion, which refuses to
102
+ // finalize while any step is missing.
103
+ const canNavigateToStep = useCallback(...)
104
+ ```
105
+
106
+ Bad:
107
+ ```
108
+ // Throttle first — cheapest gate. Bounds Argon2 CPU amplification and
109
+ // request flooding on this public endpoint (best-effort, per-instance).
110
+ const rate = await checkRateLimit(getClientKey(request))
111
+ ```
112
+
113
+ Good:
114
+ ```
115
+ // Check the rate limit first, since it's the cheapest check to run. Bounds
116
+ // Argon2 CPU amplification and request flooding on this public endpoint.
117
+ const rate = await checkRateLimit(getClientKey(request))
118
+ ```
119
+
120
+ Bad:
121
+ ```
122
+ /**
123
+ * Whether the patient may finalize their intake: every required step must be
124
+ * validly filled and one of the payment steps must have a saved response.
125
+ * Server-authoritative — `completeIntake` enforces the same rule, so a client
126
+ * skipping this check cannot complete a partial intake.
127
+ *
128
+ * Required steps are validated (not presence-checked) so a Save-for-later draft
129
+ * counts as still-missing: the patient is sent back to the earliest unfinished
130
+ * step (e.g. Demographics) rather than skipped past it to a later one.
131
+ */
132
+ export async function getIntakeReadiness(headers: Headers): Promise<IntakeReadiness> { ... }
133
+ ```
134
+
135
+ Good:
136
+ ```
137
+ /**
138
+ * Returns whether the patient can finalize their intake: every required step
139
+ * must pass its schema, and one of the payment steps must have a saved
140
+ * response. `completeIntake` enforces the same rule on the server, so a
141
+ * client cannot complete a partial intake by skipping this check. A
142
+ * Save-for-later draft counts as missing, which sends the patient back to
143
+ * the earliest unfinished step (e.g. Demographics) instead of skipping past
144
+ * it. In the returned object, `ready` tells whether the intake can be
145
+ * completed, and `missingSteps` lists the keys of the steps still missing.
146
+ */
147
+ export async function getIntakeReadiness(headers: Headers): Promise<IntakeReadiness> { ... }
148
+ ```
@@ -55,6 +55,7 @@ Never flatten ten questions into one prose paragraph just because tool unavailab
55
55
  ### B. Scope & non-goals
56
56
  1. **Explicitly out of scope** for v1? Force non-goals list — where most plans drift.
57
57
  2. v1.x / v2 already implied? Name it so we don't bake assumptions into v1's data model.
58
+ 3. **Phase granularity:** one phase per spec use-case (more, smaller PRs) or allow bundling closely-related use-cases (fewer, larger phases)? **Default: one use-case per phase** — confirm or override. Drives the "One use-case per phase" rule under **Phase structure**.
58
59
 
59
60
  ### C. Data model & storage
60
61
  1. New table, new column on existing, JSONB blob, side table, no persistence?
@@ -179,25 +180,28 @@ Don't invent new top-level sections. Skip non-applicable (e.g. omit "API Design"
179
180
 
180
181
  ### Each phase MR-sized
181
182
 
182
- Reviewer reads diff in 30 minutes:
183
+ Reviewer should read ≤1500 LoC + understand in isolation. Guidelines:
183
184
 
184
- - **Target**: ~100–300 LoC (tests included).
185
+ - **Target**: Up to 1500 LoC (tests included).
185
186
  - **One concern per phase.** "Add field + write migration + wire into 4 use cases + update 3 SQL views" = four phases.
186
187
  - **Independently mergeable.** Phase N merged + Phase N+1 stalled → system still working. No half-finished features behind flag with no flag-on path.
187
188
  - **Own tests.** Every phase ships unit/integration tests. No "tests come in Phase 8."
188
189
  - **Acceptance criterion.** Each phase ends with one-line "Acceptance:" — literally testable.
189
190
 
190
- ### One use-case per phase (mandatory)
191
+ ### One use-case per phase (default — confirm in Step 0)
191
192
 
192
- Every spec use-case (entries under **Decisions → Use-cases** in the SPEC) gets **its own phase**. Never bundle two use-cases in one phase even when the diff is tiny. Bundling = larger PR + reviewer needs context for both flows + rollback drags both. Cost of an extra phase = one PR header. Cost of a bundled regression = hotfix + split-after-the-fact.
193
+ **Default ON.** Skip only when the Step 0 **Phase granularity** answer opted into bundling. When on: every spec use-case (entries under **Decisions → Use-cases** in the SPEC) gets **its own phase**. Never bundle two use-cases in one phase even when the diff is tiny. Bundling = larger PR + reviewer needs context for both flows + rollback drags both. Cost of an extra phase = one PR header. Cost of a bundled regression = hotfix + split-after-the-fact.
193
194
 
194
- Apply even when:
195
+ When on, apply even when:
195
196
  - Two use-cases share the same endpoint — split anyway, the second phase is "wire use-case 2 into the existing endpoint." Reviewer reads ≤50 LoC.
196
197
  - Use-cases are CRUD on same entity — Create / Read / Update / Delete are four phases, not one.
197
198
  - "It's just one extra branch" — that branch hides edge cases. Separate phase forces explicit acceptance + tests for that branch.
198
199
 
200
+ **If the user opted into bundling** (Step 0): group closely-related use-cases into one phase where it reduces churn, but each phase still stays MR-sized (≤1500 LoC), one concern, independently mergeable, with its own tests + acceptance. Bundling is a granularity dial, not a license for kitchen-sink phases.
201
+
199
202
  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.
200
203
 
204
+ <!-- e2e:start -->
201
205
  ### Phases creating new UI flow need happy-path E2E
202
206
 
203
207
  Every phase that introduces or substantially changes a user-facing flow (new page, new modal, new wizard step, new gated action) **must** ship 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.
@@ -205,6 +209,7 @@ Every phase that introduces or substantially changes a user-facing flow (new pag
205
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 required when the phase reaches the browser.
206
210
 
207
211
  **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
+ <!-- e2e:end -->
208
213
 
209
214
  Doesn't fit → split: `Phase 4a — Static validation`, `Phase 4b — Resolution engine`, `Phase 4c — Apply engine`, `Phase 4d — View wiring`.
210
215
 
@@ -229,7 +234,9 @@ Spec use-case: {SPEC **Decisions → Use-cases** id/name this phase implements,
229
234
  Tests:
230
235
  - **Unit**: {file path} — {what it covers}.
231
236
  - **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
+ <!-- e2e:start -->
232
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).
239
+ <!-- e2e:end -->
233
240
 
234
241
  **Suggested AI model**: {tier choice + why}. See "AI model selection".
235
242
 
@@ -285,7 +292,7 @@ Tests:
285
292
  - Existing test suite passes unchanged on on-branch.
286
293
  - Remove flag-parametrized tests no longer make sense.
287
294
 
288
- **Suggested AI model**: Tier 1 — `claude-haiku-4-5` / `gpt-5-nano` / `gemini-2.5-flash-lite`. Mechanical deletion + inlining; cheap models excel.
295
+ **Suggested AI model**: Tier 1 (IDs in `resources/ai-models.yaml`). Mechanical deletion + inlining; cheap models excel.
289
296
 
290
297
  **Reusable skills**: none — pure cleanup.
291
298
 
@@ -306,41 +313,29 @@ Common mistake: leave cross-repo producer wiring for last, then discover the ups
306
313
 
307
314
  For each phase, suggest **cheapest/fastest model likely to one-shot work**. Iterating with cheap model usually beats burning Opus tokens on CRUD scaffold.
308
315
 
316
+ **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
+
309
318
  ### Tier 1 — cheapest/fastest (boilerplate, exact-precedent edits)
310
319
  **Use for**: single migration adding column or index, exporting from `__init__.py`, registering admin, scaffolding empty Django app, thin serializer mirroring existing pattern verbatim.
311
320
 
312
- - Anthropic: `claude-haiku-4-5`
313
- - OpenAI: `gpt-5-nano` (fall back to `gpt-5-mini` if file has any branching logic)
314
- - Google: `gemini-2.5-flash-lite`
315
-
316
321
  ### Tier 2 — standard pattern application
317
322
  **Use for**: repository methods, DRF serializer with non-trivial validation, ViewSet wiring with filterset, pytest unit/integration tests against established fixtures, simple HStore/ArrayField additions.
318
323
 
319
- - Anthropic: `claude-haiku-4-5` (with iteration), step up to `claude-sonnet-4-6` if phase touches >3 files
320
- - OpenAI: `gpt-5-mini`
321
- - Google: `gemini-2.5-flash`
322
-
323
324
  ### Tier 3 — multi-file orchestration, business logic, SQL views
324
325
  **Use for**: use case coordinating across repositories with non-trivial branching, new `vw_*` view + non-managed model + migration, serializer with cross-field validation affecting use-case behavior, integration tests covering concurrency edges.
325
326
 
326
- - Anthropic: `claude-sonnet-4-6`
327
- - OpenAI: `gpt-5`
328
- - Google: `gemini-2.5-pro` (or `gemini-3-pro` for stronger code recall)
329
-
330
327
  ### Tier 4 — architectural / novel / hard
331
328
  **Use for**: cycle detection in user-mutable trees, transactional batch protocols with deferred constraints, partitioned-to-partitioned FK design, perf tuning slow query against partitioned hot table, debugging heisenbug.
332
329
 
333
- - Anthropic: `claude-opus-4-7` (`[1m]` 1M-context for large codebases)
334
- - OpenAI: `gpt-5` with extended thinking, or `o3` / `o4` for pure reasoning
335
- - Google: `gemini-3-pro` with thinking
336
-
337
330
  ### Writing the suggestion
338
331
 
339
- > **Suggested AI model**: Tier 1 — `claude-haiku-4-5` / `gpt-5-nano` / `gemini-2.5-flash-lite`. Single-field migration + model export, exact precedent in `@<app>/<module>/models/<file>.py`.
332
+ Pick the tier from the rubric above, then pull the matching vendor IDs out of [resources/ai-models.yaml](resources/ai-models.yaml):
333
+
334
+ > **Suggested AI model**: Tier 1 (IDs in [resources/ai-models.yaml](resources/ai-models.yaml)). Single-field migration + model export, exact precedent in `@<app>/<module>/models/<file>.py`.
340
335
 
341
- When one tier doesn't fit:
336
+ When one tier doesn't fit, name both:
342
337
 
343
- > **Suggested AI model**: Tier 2 for repository + serializer (Haiku 4.5 / GPT-5 mini / Gemini Flash); step up to Tier 3 (`claude-sonnet-4-6`) for integration test spanning upsert → routing → reprocess.
338
+ > **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).
344
339
 
345
340
  **Default to cheapest tier that plausibly works, not safest.** Cheap models failing fast beats expensive succeeding slowly.
346
341
 
@@ -402,11 +397,13 @@ When in doubt, model the plan after a recent example in `ai-plans/` — look for
402
397
  - [ ] Filename: `ai-plans/{TODAY}-{FEATURE_NAME}_IMPLEMENTATION_PLAN.md`.
403
398
  - [ ] **Goals + Non-goals** section present.
404
399
  - [ ] **Guiding Decisions** table — each row has *why*.
405
- - [ ] Phases MR-sized (~100–300 LoC) + independently mergeable.
400
+ - [ ] Phases MR-sized (≤1500 LoC) + independently mergeable.
406
401
  - [ ] Phase numbering uses numbers + letters consistently.
407
- - [ ] **At least one phase per spec use-case** (entries under SPEC **Decisions → Use-cases**). No phase implements two use-cases. Cross-cutting scaffolding is its own foundation phase.
402
+ - [ ] **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.
408
403
  - [ ] Each phase has Goal / Spec use-case / Feature flag (or explicit waiver) / Changes / Tests / Suggested AI model / Reusable skills / Acceptance.
404
+ <!-- e2e:start -->
409
405
  - [ ] Every phase introducing a new UI flow has an **E2E happy-path test** in its Tests block, with screenshot output to `pr-screenshots/`.
406
+ <!-- e2e:end -->
410
407
  - [ ] Feature flag declared in **Guiding Decisions** (key, scope, default, flip-on criterion) **unless** **Guiding Decisions** explicitly justifies "no flag — purely additive surface".
411
408
  - [ ] ≥1 test per gated phase asserts flag-off behavior unchanged.
412
409
  - [ ] If flag declared, **final entry under Phased Rollout is dedicated flag-removal phase** with prerequisite (soak window), full deletion touch list, `grep` acceptance check.