vinta-ai-workflows 0.2.0-alpha3 → 0.2.0-alpha5

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 (31) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/README.md +5 -2
  3. package/package.json +1 -1
  4. package/schemas/vinta-ai-workflows-config.v1.schema.json +57 -0
  5. package/skills/vinta-analyze-codebase/SKILL.md +1 -1
  6. package/skills/vinta-bootstrap-ai-tools/SKILL.md +37 -8
  7. package/skills/vinta-derive-skills/SKILL.md +92 -39
  8. package/skills/vinta-derive-skills/resources/foundation-skills/deslop-comments/SKILL.md +148 -0
  9. package/skills/vinta-derive-skills/resources/foundation-skills/handoff/SKILL.md +100 -0
  10. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +39 -4
  11. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/claude-worktree-write-guard.py +118 -0
  12. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-claude-sandbox-settings.sh +167 -0
  13. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/sandbox-run.sh +3 -1
  14. package/skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md +129 -0
  15. package/skills/vinta-derive-skills/resources/handoff-to-client-template.md +107 -0
  16. package/skills/vinta-derive-skills/resources/plan-execution/README.md +42 -0
  17. package/skills/vinta-derive-skills/resources/{implement-plan-template-modular-substitutions.md → plan-execution/partials/commit-strategy/modular.md} +38 -70
  18. package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +46 -0
  19. package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +78 -0
  20. package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +19 -0
  21. package/skills/vinta-derive-skills/resources/plan-execution/partials/pr-context.md +61 -0
  22. package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +50 -0
  23. package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +68 -0
  24. package/skills/vinta-derive-skills/resources/{amend-plan-template.md → plan-execution/shell/amend-plan-template.md} +48 -42
  25. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +47 -0
  26. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +186 -0
  27. package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +30 -0
  28. package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +25 -0
  29. package/skills/vinta-derive-skills/resources/systematic-debugging-template.md +7 -3
  30. package/skills/vinta-update-project-skills/SKILL.md +1 -1
  31. package/skills/vinta-derive-skills/resources/implement-plan-template.md +0 -436
@@ -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`, `handoff`, `handoff-to-client`) — 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**.
@@ -47,14 +47,17 @@ These are project-agnostic enough to ship as-is. Copy from this skill's [resourc
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
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
+ | `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
+ | `handoff` | [resources/foundation-skills/handoff/SKILL.md](resources/foundation-skills/handoff/SKILL.md) | Write or consume a session-continuation handoff doc under `.vinta-ai-workflows/handoffs/` — goal, current state, decisions + rationale, landmines, next step — so a fresh agent session (or a teammate) resumes in-flight work without re-deriving context. **Always ships** — fully project-agnostic; standalone (no cross-links into the always-on trio). |
50
52
  | `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). |
53
+ | `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
54
 
52
55
  These reference each other:
53
56
 
54
57
  - `plan-feature` reads spec; dispatches `create-qa-use-cases` when the doc is missing (e2e-enabled projects only — see below).
55
58
  - `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
59
 
57
- The **always-on unit** is `plan-feature` + `create-spec` + `open-pr-from-context` — copy all three or none. `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
+ 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. `handoff` also always ships independently — its body is fully project-agnostic and nothing else depends on it. `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.
58
61
 
59
62
  **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:
60
63
 
@@ -64,15 +67,47 @@ The **always-on unit** is `plan-feature` + `create-spec` + `open-pr-from-context
64
67
 
65
68
  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.
66
69
 
67
- ### B. Generate — `implement-plan`, `amend-plan`, `systematic-debugging` from templates
70
+ ### B. Generate — the **plan-execution unit** + `systematic-debugging` from templates
68
71
 
69
- 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
+ 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.
70
73
 
71
- - **`implement-plan`** — forward execution: drives a fresh plan phase-by-phase. From [resources/implement-plan-template.md](resources/implement-plan-template.md).
72
- - **`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.
73
- - **`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.
74
+ 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
 
75
- `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.
76
+ - **`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`.
77
+ - **`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.
78
+ - **`implement-phase`** — compose implementer prompt + pick model + spawn one implementer subagent. From `shell/implement-phase-template.md`.
79
+ - **`review-phase`** — three-layer review + fix loop. From `shell/review-phase-template.md`. **Shared by all three conductors** (implement-plan, amend-plan, systematic-debugging).
80
+ - **`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)).
81
+
82
+ 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.
83
+
84
+ - **`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).
85
+
86
+ - **`handoff-to-client`** — generate a markdown API-change handoff document for the client teams consuming an API-only repo: endpoints/operations added / changed / deprecated / removed on the current branch vs the default branch, with request/response shapes, breaking-change flags, and per-platform migration notes. From [resources/handoff-to-client-template.md](resources/handoff-to-client-template.md). **Opt-in** — only generated when `foundation_skills.handoff-to-client` is `enabled` in `.vinta-ai-workflows.yaml` (the bootstrap interview asks only when the repo looks API-only with separate client codebases). The rendered body bakes in `skills.handoff-to-client.*` config: client platforms, API style, optional API spec path, output dir. Standalone — no cross-links into the plan-execution unit.
87
+
88
+ 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.
89
+
90
+ #### Rendering the plan-execution unit
91
+
92
+ 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:
93
+
94
+ 1. **Expand includes.** Replace every include directive with the referenced partial content:
95
+ - `<!-- include: partials/<file>.md -->` — the whole partial body.
96
+ - `<!-- 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.
97
+ - `<!-- 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.
98
+ - 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.
99
+ 2. **Substitute `{{PLACEHOLDER}}`** using the table below (+ the commit-strategy table).
100
+ 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.
101
+
102
+ **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.
103
+
104
+ **`ask` → two `integrate-phase` variants, no dual-render.** When `policies.commit_strategy = ask`, render `integrate-phase-template.md` **twice**:
105
+ - `ai-tools/skills/integrate-phase-stacked/SKILL.md` — `<RESOLVED>` = `stacked`, `{{INTEGRATE_PHASE_NAME}}` = `integrate-phase-stacked`.
106
+ - `ai-tools/skills/integrate-phase-modular/SKILL.md` — `<RESOLVED>` = `modular`, `{{INTEGRATE_PHASE_NAME}}` = `integrate-phase-modular`.
107
+
108
+ 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.
109
+
110
+ The remaining per-shell placeholders:
76
111
 
77
112
  | Placeholder | Source | Example |
78
113
  |---|---|---|
@@ -99,35 +134,46 @@ Project-specific skills, generated from templates because their bodies cite real
99
134
  | `{{ANTI_GIT_ADD_ALL_REASON}}` | derived from common artifacts in target | `secrets, .env files, build artifacts, .auth/ live in the tree` |
100
135
  | `{{STAGE_PATTERN}}` | derived from monorepo shape | `apps/... lib/... e2e/... ai-plans/...` or `<app>/... tests/... ai-plans/...` |
101
136
  | `{{PROJECT_SKILLS_LIST}}` | computed from skills emitted | comma-separated names of the skills the project actually has |
102
- | `{{AGENT_DISPATCH_TABLE}}` | from [vinta-derive-subagents](../vinta-derive-subagents/SKILL.md) output | markdown table mapping phase shapes → agent type |
137
+ | `{{AGENT_DISPATCH_TABLE}}` | from [vinta-derive-subagents](../vinta-derive-subagents/SKILL.md) output | markdown table mapping phase shapes → agent type. Rendered into `implement-phase`. |
138
+ | `{{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. |
139
+ | `{{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) ``. |
140
+ | `{{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). |
103
141
  | `{{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. |
104
142
  | `{{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. |
143
+ | `{{API_STYLE}}` | `skills.handoff-to-client.api_style` (handoff-to-client only) | `REST`, `GraphQL`, `gRPC`, `REST + webhooks` |
144
+ | `{{CLIENT_PLATFORMS_LIST}}` | `skills.handoff-to-client.client_platforms` (handoff-to-client only) | comma-separated, e.g. `iOS (Swift), Android (Kotlin), web SPA (React)` |
145
+ | `{{CLIENT_HANDOFF_DIR}}` | `skills.handoff-to-client.output_dir` (handoff-to-client only) | `.vinta-ai-workflows/client-handoffs` (default) |
146
+ | `{{API_SPEC_BLOCK}}` | derived from `skills.handoff-to-client.api_spec_path` (handoff-to-client only) | when set: a paragraph instructing the agent to regenerate/read the spec at that path on both base and branch and diff the two as the primary change-enumeration source (cross-checking against the code diff). When absent: a one-line note that no machine-readable spec exists, so shapes come from route/serializer code alone. |
105
147
  | `{{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. |
106
148
  | `{{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.` |
107
149
  | `{{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`. |
108
- | `{{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. |
150
+ | `{{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. |
109
151
 
110
152
  ### `{{COMMIT_STRATEGY_*}}` substitution table
111
153
 
112
- 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`.
113
-
114
- | Placeholder | `stacked-branches` substitution | `modular-commits` substitution | `ask` substitution |
115
- |---|---|---|---|
116
- | `{{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) |
117
- | `{{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) |
118
- | `{{COMMIT_STRATEGY_CONFIRM_NOTE}}` | empty string | empty string | ` + \`run_options.commit_strategy_resolved\`` (leading ` + ` — appends to the captured-options list) |
119
- | `{{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)`` |
120
- | `{{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 |
121
- | `{{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 |
122
- | `{{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 |
123
- | `{{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` `` |
124
- | `{{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 |
125
- | `{{TRACKING_PHASE_BRANCH_FIELD}}` | `, branch, base` (per-phase fields kept inline) | empty string (no per-phase branch under modular) | both gated by runtime markers |
126
- | `{{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 |
127
- | `{{BRANCH_CHECKLIST_LINE}}` | `Stacked branch created; pushed.` | `Plan branch updated with phase commits; pushed.` | dynamic per resolved strategy |
128
- | `{{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 |
129
-
130
- 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).
154
+ 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.
155
+
156
+ **Where each block lands, and how `ask` is handled:**
157
+ - `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}}`.
158
+ - `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`.
159
+ - `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).
160
+
161
+ | Placeholder | Lands in | `stacked-branches` | `modular-commits` | `ask` |
162
+ |---|---|---|---|---|
163
+ | `{{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) |
164
+ | `{{COMMIT_STRATEGY_STEP0_TRAILER}}` | implement-plan | empty string | empty string | ` Commit-strategy behavior follows \`run_options.commit_strategy_resolved\`.` (leading space) |
165
+ | `{{COMMIT_STRATEGY_CONFIRM_NOTE}}` | implement-plan | empty string | empty string | ` + \`run_options.commit_strategy_resolved\`` (leading ` + `) |
166
+ | `{{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)`` |
167
+ | `{{BRANCH_NAMING_BLOCK}}` | integrate-phase | `stacked.md#BRANCH_NAMING` | `modular.md#BRANCH_NAMING` | baked per-variant (no runtime marker) |
168
+ | `{{PER_PHASE_COMMIT_BLOCK}}` | implement-phase | `stacked.md#PER_PHASE_COMMIT` | `modular.md#PER_PHASE_COMMIT` | both blocks gated by runtime markers |
169
+ | `{{PR_OPEN_TIMING_BLOCK}}` | integrate-phase | `stacked.md#PR_OPEN_TIMING` | `modular.md#PR_OPEN_TIMING` | baked per-variant (no runtime marker) |
170
+ | `{{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` `` |
171
+ | `{{TRACKING_BRANCH_FIELD}}` | implement-plan | `stacked.md` footer value (empty) | `modular.md` footer value | both gated by runtime markers |
172
+ | `{{TRACKING_PHASE_BRANCH_FIELD}}` | implement-plan | `stacked.md` footer value (`, branch, base`) | `modular.md` footer value (empty) | both gated by runtime markers |
173
+ | `{{FINAL_REPORT_BRANCH_SUMMARY}}` | implement-plan | `stacked.md` footer value | `modular.md` footer value | dynamic per resolved strategy |
174
+ | `{{BRANCH_CHECKLIST_LINE}}` | implement-plan | `stacked.md` footer value | `modular.md` footer value | dynamic per resolved strategy |
175
+ | `{{COMMIT_STRATEGY_CHECKLIST_BLOCK}}` | implement-plan | `stacked.md#CHECKLIST` (empty) | `modular.md#CHECKLIST` (3 items) | both variants gated by runtime markers |
176
+ | `{{BRANCH_PUSH_HEADING}}` | integrate-phase | `Push stacked branch` | `Push to plan branch` | baked per-variant (`Push stacked branch` / `Push to plan branch`) |
131
177
 
132
178
  ### `{{COMMIT_STRATEGY_REFUSAL_BLOCK}}` (amend-plan only)
133
179
 
@@ -158,15 +204,21 @@ Amending under modular commits requires rewriting an arbitrary number of inline
158
204
  Refuse with this guidance; do not proceed.
159
205
  ```
160
206
 
161
- 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`.
207
+ 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`).
162
208
 
163
- Render each template with substitutions, write to:
209
+ Render each template (expand includes → substitute placeholders → write), producing:
164
210
 
165
- - `ai-tools/skills/implement-plan/SKILL.md` (from `implement-plan-template.md`)
166
- - `ai-tools/skills/amend-plan/SKILL.md` (from `amend-plan-template.md`)
211
+ - `ai-tools/skills/implement-plan/SKILL.md` (from `plan-execution/shell/implement-plan-template.md`)
212
+ - `ai-tools/skills/implement-phase/SKILL.md` (from `plan-execution/shell/implement-phase-template.md`)
213
+ - `ai-tools/skills/review-phase/SKILL.md` (from `plan-execution/shell/review-phase-template.md`)
214
+ - `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)
215
+ - `ai-tools/skills/amend-plan/SKILL.md` (from `plan-execution/shell/amend-plan-template.md`)
167
216
  - `ai-tools/skills/systematic-debugging/SKILL.md` (from `systematic-debugging-template.md`) — only when `foundation_skills.systematic-debugging: enabled`
217
+ - `ai-tools/skills/handoff-to-client/SKILL.md` (from `handoff-to-client-template.md`) — only when `foundation_skills.handoff-to-client: enabled`
218
+
219
+ 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.
168
220
 
169
- 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).
221
+ 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).
170
222
 
171
223
  ### C. Optional — ask the user
172
224
 
@@ -239,8 +291,8 @@ Length: 100–300 lines. Shorter = under-specified. Longer = should probably spl
239
291
 
240
292
  - **Don't ship copy-pasted source-project SKILL.md files unchanged.** Run search-and-replace for source project names / paths / commands before saving.
241
293
  - **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.
242
- - **`implement-plan` is generated, not copied.** Its body has too much project-specific content for verbatim shipping.
243
- - **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.
294
+ - **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.
295
+ - **Optional skills (`add-e2e-test`, `add-env-var`, `add-one-off-script`, `prepare-worktree`, `thermo-nuclear-code-quality-review`, `handoff-to-client`) are gated by user answer.** Don't ship them by default. `handoff-to-client` is additionally template-rendered (bucket B) — its config lives under `skills.handoff-to-client.*`.
244
296
  - **Each skill solves one job.** Two unrelated checklists → split.
245
297
  - **Reference real files in the target.** Skill links must point to existing paths.
246
298
  - **Skills auto-load by description.** Specific triggers, specific outcomes.
@@ -248,7 +300,7 @@ Length: 100–300 lines. Shorter = under-specified. Longer = should probably spl
248
300
 
249
301
  ## Pitfalls
250
302
 
251
- - **Forgetting to substitute placeholders in `implement-plan`.** A surviving `{{TEST_CMD}}` is a runtime confusion. Validate before saving.
303
+ - **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.)
252
304
  - **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.
253
305
  - **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.
254
306
  - **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?".
@@ -261,5 +313,6 @@ After all skills written:
261
313
  2. `name` field matches dir name.
262
314
  3. No `{{PLACEHOLDER}}` strings survive in any output skill.
263
315
  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`.
264
- 5. `implement-plan` body cites real commands + the project's actual code host + branch / PR / co-author policy.
265
- 6. Setup script ([vinta-install-ai-tools-setup](../vinta-install-ai-tools-setup/SKILL.md)) runs cleanly with the new skills in place.
316
+ 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.
317
+ 6. `implement-plan` (+ the plan-execution sub-skills) cite real commands + the project's actual code host + branch / PR / co-author policy.
318
+ 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
+ ```
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: handoff
3
+ description: Write or consume a session-continuation handoff document under `.vinta-ai-workflows/handoffs/` so a fresh agent session (or a different agent / teammate) can resume in-flight work without re-deriving context. Use when the user says "hand this off", "write a handoff", "wrap this up for another agent", "prepare a handoff doc", or — in resume mode — "pick up from the handoff", "resume from handoff", or points at a handoff file. Captures goal, current state, decisions with rationale, files touched, verification status, and the exact next step.
4
+ ---
5
+
6
+ # Handoff
7
+
8
+ Context dies with the session. A handoff document is the bridge: everything a fresh agent needs to continue the work, written down while the outgoing agent still knows it. The reader has none of your conversation history — write for someone who knows the codebase conventions but nothing about this task.
9
+
10
+ Two modes. Pick by what the user asked for:
11
+
12
+ - **Write mode** — capture the current task into a handoff doc. Trigger: "hand this off", "wrap up for another agent", "write a handoff before we stop".
13
+ - **Resume mode** — start from an existing handoff doc. Trigger: "resume from the handoff", "pick up where the last session left off", or the user pastes/points at a handoff file.
14
+
15
+ ## Write mode
16
+
17
+ ### 1. Gather real state — never from memory alone
18
+
19
+ Collect each of these from the repo, not from what you recall doing:
20
+
21
+ 1. `git status --short` + `git diff --stat` — uncommitted work.
22
+ 2. `git log --oneline <default-branch>..HEAD` — commits this task produced (skip if on the default branch).
23
+ 3. Current branch name and its upstream/base.
24
+ 4. Any in-flight plan or tracking artifacts: `ai-plans/*_IMPLEMENTATION_PLAN.md`, `TRACKING_*.md`, `.vinta-ai-workflows/prs-context/` files with `status: pending`.
25
+ 5. Open PRs for this branch, if a PR CLI is available (`gh pr view` / `glab mr view`).
26
+ 6. Test / lint / build status: what was last run, and did it pass? If you don't know, say "not verified" — do not guess.
27
+
28
+ ### 2. Write the document
29
+
30
+ Path: `.vinta-ai-workflows/handoffs/{YYYY-MM-DD}-{task-slug}.md` (e.g. `2026-07-11-fix-invoice-rounding.md`). Create the directory if missing. If a handoff for the same task slug already exists, supersede it: write the new file with today's date and add a `supersedes:` line pointing at the old one — never silently overwrite.
31
+
32
+ Structure:
33
+
34
+ ```markdown
35
+ # Handoff: {one-line task title}
36
+
37
+ - **Date:** {YYYY-MM-DD}
38
+ - **Branch:** {branch} (base: {base-branch})
39
+ - **Status:** {in-progress | blocked | ready-for-review}
40
+ - **Supersedes:** {path to previous handoff, or omit}
41
+
42
+ ## Goal
43
+ What the task is trying to achieve and why — the original ask, plus any scope changes agreed with the user since.
44
+
45
+ ## Current state
46
+ What is DONE and verified, what is done but NOT verified, what is not started.
47
+ Cite commits (`abc1234`) and files. Include the exact uncommitted-work summary
48
+ from `git status` if anything is uncommitted.
49
+
50
+ ## Decisions made
51
+ Each decision the next agent must not accidentally relitigate:
52
+ - **{decision}** — {rationale}. Alternatives considered: {…, and why rejected}.
53
+
54
+ ## Landmines
55
+ Gotchas discovered the hard way: flaky tests, misleading names, code that looks
56
+ wrong but is correct (and why), env quirks, anything you'd warn a teammate about.
57
+
58
+ ## Next step
59
+ The SINGLE most important next action, concrete enough to start immediately
60
+ (file, command, expected outcome). Then the rest of the remaining work as an
61
+ ordered list.
62
+
63
+ ## Verification
64
+ How to confirm the finished work is correct: commands to run, flows to exercise,
65
+ what "passing" looks like.
66
+ ```
67
+
68
+ ### 3. Quality gate before saving
69
+
70
+ - Every claim of "done" is backed by a commit hash or a passing command you actually ran this session. Anything else is labeled "not verified".
71
+ - No references to "the conversation", "as discussed above", or "earlier" — the reader has no conversation.
72
+ - A stranger could execute **Next step** without asking a question.
73
+
74
+ ### 4. Tell the user where it landed
75
+
76
+ Report the file path. Note that `.vinta-ai-workflows/` is typically gitignored — if the handoff must reach another machine or teammate, they should share the file explicitly or commit it deliberately.
77
+
78
+ ## Resume mode
79
+
80
+ 1. **Read the handoff doc** in full. If the user didn't name one, list `.vinta-ai-workflows/handoffs/` and pick the most recent that isn't superseded; confirm the pick with the user if more than one task is plausible.
81
+ 2. **Verify its claims against the repo before trusting them.** The repo may have moved since the handoff was written:
82
+ - Named branch exists and you're on it (or switch after confirming with the user).
83
+ - Cited commits exist (`git log`), cited files exist, `git status` matches the described uncommitted state.
84
+ - Re-run the cheapest verification command from the **Verification** section to confirm the "done" claims still hold.
85
+ 3. **On mismatch, stop and reconcile** — tell the user what the handoff claims vs. what the repo shows. Never continue from stale premises.
86
+ 4. **Adopt the decisions.** Treat **Decisions made** as settled unless the user reopens one; do not re-explore rejected alternatives.
87
+ 5. **Continue from Next step.** When the work later stops again unfinished, write a fresh handoff (write mode) superseding this one.
88
+
89
+ ## Pitfalls
90
+
91
+ - **Writing the handoff from memory.** The doc says "tests pass" but they were never run after the last edit. Gather state from the repo (step 1) — every time.
92
+ - **Recording what happened instead of what's next.** A chronological diary of the session is not a handoff. The reader needs current state + next action, not the journey.
93
+ - **Omitting rejected alternatives.** The next agent re-derives the rejected approach, spends an hour, and hits the same wall. One line per rejection prevents this.
94
+ - **Vague next step.** "Continue the refactor" is useless. "Extract the retry logic in `client.py` into `retry.py`, then make `test_client.py` pass again" is a handoff.
95
+ - **Resume mode trusting the doc blindly.** Handoffs go stale — a teammate may have pushed to the branch since. Verify (resume step 2) before building on it.
96
+
97
+ ## Verification
98
+
99
+ - Write mode: the file exists at the reported path, follows the structure above, and contains no unverified "done" claims.
100
+ - Resume mode: repo state matched the doc (or mismatches were surfaced), and work continued from the doc's **Next step** — not from a fresh re-derivation.
@@ -203,7 +203,7 @@ If the **Plan inspection** step inferred the plan doesn't touch any of these: sy
203
203
 
204
204
  Symlinking + threading the worktree path through prompts keeps *cooperative* agents in the worktree, but it's not a guarantee: a buggy agent (often a smaller model spawned for a phase) can resolve an absolute path back to the **main checkout** and silently write there. Those writes never reach the worktree's commit — they sit as uncommitted thrash in the main checkout, and the "missing" edits read as a silent agent failure. Prompt instructions don't stop it; they rely on the agent's goodwill.
205
205
 
206
- The deterministic, **harness-agnostic** fix is to confine the *process* (not the tool) at the OS filesystem layer: make the main checkout read-only for any command run inside the sandbox, regardless of which agent CLI issues the writes. The bundled [scripts/sandbox-run.sh](scripts/sandbox-run.sh) does this — `sandbox-exec` on macOS, `bwrap` (bubblewrap) on Linux.
206
+ The deterministic fix is to confine the *process* (not the tool) at the OS filesystem layer: make the main checkout read-only for any command run inside the sandbox, regardless of which agent CLI issues the writes. The bundled [scripts/sandbox-run.sh](scripts/sandbox-run.sh) does this — `sandbox-exec` on macOS, `bwrap` (bubblewrap) on Linux. **This process-wrapping model works for runtimes that spawn subagents as subprocesses** (`codex exec`, a `claude -p` child, a custom runner). Runtimes that run subagents **in-process** — notably claude-code's Task tool, where there's no child command to wrap — use the harness-config guard in [In-process runtimes (claude-code)](#55a--in-process-runtimes-claude-code) instead.
207
207
 
208
208
  **Model: deny-main, allow-rest.** The whole filesystem stays writable EXCEPT the main checkout, with the worktree (and the shared `.vinta-ai-workflows` dir) punched back to writable — even though the worktree usually lives *under* the main checkout (`.claude/worktrees/<name>/`). This deliberately does **not** lock `HOME` / caches / `/tmp`, so package managers, build tools, and test runners behave exactly as unsandboxed. The only thing the cage blocks is a write to the main checkout's own tracked tree — which is exactly the bug.
209
209
 
@@ -236,6 +236,36 @@ Escape hatch: `VINTA_SANDBOX=off` makes the wrapper a transparent pass-through (
236
236
  - The existing `deps_change=true` → copy/reinstall logic already gives the worktree its own writable `node_modules/` when a phase installs deps; non-churn phases keep the read-only symlink (agents shouldn't write deps anyway — correct).
237
237
  - The `.vinta-ai-workflows/` dir (summary YAMLs, prs-context) is shared/symlinked but **written** during a run, so it must be passed as an `--allow` path (or made a real writable dir in the worktree). Record which.
238
238
 
239
+ ### 5.5a — In-process runtimes (claude-code)
240
+
241
+ `sandbox-run.sh` confines a **process**: it wraps the command that launches a subagent. That only works when the runtime spawns subagents as **subprocesses** (`codex exec …`, a `claude -p …` child, a custom runner) — you wrap that argv and the kernel blocks the child's main-checkout writes.
242
+
243
+ **Claude Code runs subagents in-process.** Its Task tool shares the orchestrator's OS process and tool pipeline — there's no child launch command to wrap. The guard has to live in the harness's own config instead. Two complementary layers, both bundled here, both matching the same deny-main/allow-rest model:
244
+
245
+ - **Layer A — `PreToolUse` write-guard hook** ([scripts/claude-worktree-write-guard.py](scripts/claude-worktree-write-guard.py)). A session-level `PreToolUse` hook fires for **every** tool call in the session — orchestrator **and** every in-process Task subagent — because they run through the same pipeline. This hook matches the file-editing tools (`Edit|Write|MultiEdit|NotebookEdit`), realpath-resolves the target, and blocks (exit 2 → the model gets the reason and retries) any write that escapes the worktree into the main checkout. Filesystem-only, **no network impact**. This is the load-bearing claude-code guard and the concrete form of implement-plan's "runtime pre-write guard hook" option.
246
+
247
+ - **Layer B — Claude Code's native OS sandbox** (opt-in). Claude Code has adopted the same OS model `sandbox-run.sh` implements (`sandbox-exec` / `bwrap`), configured via `sandbox.filesystem.denyWrite` / `allowWrite` in `settings.json` instead of a wrapper, and it applies to **Bash run inside subagents** too. Turning it on closes the one gap Layer A leaves — a write issued through **Bash** (`echo > <main>/f`), which a file-tool hook can't see. **Caveat: enabling the native sandbox also turns on network isolation**, so `pnpm install` / `git push` break unless you allow-list the registry + git-remote hosts. Without Layer B, Bash-issued stray writes fall back to implement-plan's post-run `git -C <main> status` backstop (Layer 1).
248
+
249
+ Generate both with [scripts/gen-claude-sandbox-settings.sh](scripts/gen-claude-sandbox-settings.sh):
250
+
251
+ ```bash
252
+ # Layer A only (filesystem-only, no network config needed):
253
+ scripts/gen-claude-sandbox-settings.sh --worktree <worktree-path> --main <main-checkout-root>
254
+
255
+ # Layer A + B (also OS-block Bash writes; must allow-list network hosts):
256
+ scripts/gen-claude-sandbox-settings.sh --worktree <worktree-path> --main <main-checkout-root> \
257
+ --os-sandbox --allow-domain <registry-host> --allow-domain <git-remote-host>
258
+ ```
259
+
260
+ It writes `<worktree>/.claude/settings.json` (merging, not clobbering) with `<worktree>`, `<main>/.git` (git worktrees commit into the main repo's `.git` — **must** stay writable), and `.vinta-ai-workflows` punched back to writable.
261
+
262
+ **Two deployment models — settings are read at session start:**
263
+
264
+ 1. **Worktree-rooted session (recommended).** Run the plan from inside the worktree (`cd <worktree> && claude`). The generated `<worktree>/.claude/settings.json` governs that session and all its Task subagents. As a bonus, claude-code's default working-directory boundary already discourages file writes above cwd, so this is belt-and-suspenders. Provision the worktree + settings **before** starting the plan session.
265
+ 2. **Config in the invocation's project root.** If the orchestrator already runs in the main checkout (implement-plan's default topology), the hook + sandbox must instead live in the main checkout's `settings.json` / `settings.local.json` **before** that session starts — the native `sandbox` block in particular is read at launch and won't retroactively cage an already-running session. Provision first, then start the plan; don't expect a mid-run `use_worktree` opt-in to enable the OS sandbox for the session that requested it.
266
+
267
+ Record the achieved tier the same way as the subprocess path: `enforced` when Layer B is active (or Layer A alone if the team accepts hook-only), `none` when neither is wired.
268
+
239
269
  ## Step 6 — Write the summary file
240
270
 
241
271
  `.vinta-ai-workflows/worktrees/<name>.yaml` (committed to `.gitignore` via the existing `.vinta-ai-workflows/` umbrella). Schema:
@@ -279,9 +309,13 @@ state:
279
309
  cron_disabled: <bool>
280
310
  sandbox:
281
311
  tier: enforced | none # from the Filesystem sandbox step's capability probe
282
- launcher: sandbox-exec | bwrap | null # null when tier=none
312
+ launcher: sandbox-exec | bwrap | claude-hook | claude-native-sandbox | null
313
+ # sandbox-exec / bwrap → subprocess runtimes via scripts/sandbox-run.sh
314
+ # claude-hook → in-process (claude-code) Layer A hook only (file tools)
315
+ # claude-native-sandbox → in-process (claude-code) Layer A hook + Layer B OS sandbox
316
+ # null → tier=none (no guard wired)
283
317
  deny: [<main-checkout-root>] # subtree(s) made read-only inside the cage
284
- allow: [<worktree-path>, <.vinta-ai-workflows-dir>] # writable exceptions
318
+ allow: [<worktree-path>, <main-checkout-root>/.git, <.vinta-ai-workflows-dir>] # writable exceptions
285
319
  notes: |
286
320
  <freeform — anything the user / agent should know>
287
321
  ```
@@ -349,7 +383,8 @@ Every step gated on user confirmation when the worktree has un-pushed branches.
349
383
  - **Every fork decision lands in `.vinta-ai-workflows/worktrees/<name>.yaml`.** Teardown reads it; humans grep it; agents resuming a stalled plan read it. No decision lives only in conversation memory.
350
384
  - **Worktree root governed by runtime conventions.** claude-code uses `.claude/worktrees/`; other harnesses use sibling dirs. Don't fight the harness — match it.
351
385
  - **Don't mutate the main checkout from this skill.** Every write goes to the worktree or to `.vinta-ai-workflows/`. Forking a Postgres DB is the one exception (the new DB lives in the same server) — document it loudly in the summary.
352
- - **Sandbox is the deterministic guard; the prompt is not.** When a sandbox tool exists, the [Filesystem sandbox](#step-55--filesystem-sandbox-os-level-write-guard) step's `sandbox-run.sh` makes the main checkout read-only for any spawned command — this is what actually *prevents* stray main-checkout writes across harnesses. Telling the agent "stay in the worktree" is best-effort only. Always probe + record the tier; never claim prevention when `tier=none`.
386
+ - **Sandbox is the deterministic guard; the prompt is not.** When a sandbox tool exists, the [Filesystem sandbox](#step-55--filesystem-sandbox-os-level-write-guard) step's `sandbox-run.sh` makes the main checkout read-only for any spawned command — this is what actually *prevents* stray main-checkout writes for subprocess runtimes. For **in-process** runtimes (claude-code's Task tool) there's no subprocess to wrap: wire the `PreToolUse` hook (+ optional native OS sandbox) from [In-process runtimes (claude-code)](#55a--in-process-runtimes-claude-code) into the session's `settings.json` instead. Telling the agent "stay in the worktree" is best-effort only either way. Always probe + record the tier; never claim prevention when `tier=none`.
387
+ - **`<main>/.git` stays writable in every model.** Git worktrees store their commits in the main repo's `.git`; a deny that also blocks `<main>/.git` breaks every commit made inside the worktree. `sandbox-run.sh` callers and the generated claude-code settings must both `--allow` / `allowWrite` `<main>/.git`.
353
388
  - **Deny-main, allow-rest — never allow-worktree-only.** Locking everything except the worktree breaks package managers / caches / `$HOME`. Lock only the main checkout subtree; re-allow the worktree (nested under it) and the written `.vinta-ai-workflows` dir. Don't tighten further without testing the project's real lint / test / build / migrate commands inside the cage.
354
389
  - **Worktree base = `origin/<default-branch>` by default.** `HEAD` only when the user explicitly confirms; record the choice in the summary.
355
390
  - **Refuse to provision a second worktree for the same branch.** Git enforces this — don't try to work around it.