vinta-ai-workflows 0.1.7 → 0.2.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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`) — 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`) — 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,6 +47,7 @@ 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. |
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
51
 
51
52
  These four reference each other:
52
53
 
@@ -95,6 +96,63 @@ Project-specific skills, generated from templates because their bodies cite real
95
96
  | `{{AGENT_DISPATCH_TABLE}}` | from [vinta-derive-subagents](../vinta-derive-subagents/SKILL.md) output | markdown table mapping phase shapes → agent type |
96
97
  | `{{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. |
97
98
  | `{{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
+ | `{{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
+ | `{{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
+ | `{{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. |
103
+
104
+ ### `{{COMMIT_STRATEGY_*}}` substitution table
105
+
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).
125
+
126
+ ### `{{COMMIT_STRATEGY_REFUSAL_BLOCK}}` (amend-plan only)
127
+
128
+ `amend-plan-template.md` carries one additional placeholder not used by `implement-plan-template.md`. It renders the "this skill doesn't support this strategy yet" refusal block immediately after the description in the rendered SKILL.md.
129
+
130
+ | `commit_strategy` | Substitution |
131
+ |---|---|
132
+ | `stacked-branches` | empty string (current behavior — full amend flow runs) |
133
+ | `modular-commits` | a refusal section — see the [refusal-block body](#commit_strategy_refusal_block-body) below |
134
+ | `ask` | the same refusal section, but its preamble reads tracking's `run_options.commit_strategy_resolved` first and only refuses when that value resolves to `modular-commits`; when it resolves to `stacked-branches` the section renders an empty string (full amend flow runs) |
135
+
136
+ #### `{{COMMIT_STRATEGY_REFUSAL_BLOCK}}` body
137
+
138
+ ```markdown
139
+
140
+ ## Unsupported commit strategy
141
+
142
+ **This skill does not yet support `commit_strategy = modular-commits`.** Detected from `.vinta-ai-workflows.yaml` `policies.commit_strategy` (or `TRACKING_{plan-id}.md` `run_options.commit_strategy_resolved` when the project policy is `ask` and a run is already in flight).
143
+
144
+ Amending under modular commits requires rewriting an arbitrary number of inline atomic commits across a shared `plan/{plan-id-kebab}` branch. The git topology is fundamentally different from the per-phase stacked branches this skill is designed around — the rewrite plan, force-push targets, and downstream rebase fan-out all differ. Full support is tracked as a follow-up.
145
+
146
+ **Resolve the amendment one of three ways:**
147
+
148
+ 1. **Append a new phase** — extend the plan with the change as a new `Phase N+1`, then run [implement-plan](../implement-plan/SKILL.md). Cleanest path; preserves the existing commit log.
149
+ 2. **Hand-craft the amendment** — `git rebase -i plan/{plan-id-kebab}` (or `git commit --fixup` + `git rebase --autosquash`) on the plan branch, force-push, and re-run review manually. Skip this skill entirely.
150
+ 3. **Re-run the plan from scratch on a new branch** — abandon the in-flight commits (leave them for audit), regenerate the plan with [plan-feature](../plan-feature/SKILL.md), implement forward.
151
+
152
+ Refuse with this guidance; do not proceed.
153
+ ```
154
+
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`.
98
156
 
99
157
  Render each template with substitutions, write to:
100
158
 
@@ -113,6 +171,7 @@ Two skills depend on whether the project actually does the thing. Ask via `AskUs
113
171
  | `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. |
114
172
  | `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). |
115
173
  | `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`. |
116
175
 
117
176
  If user answers "No" to any: don't ship that skill. Record nothing — the foundation set is just smaller.
118
177
 
@@ -175,7 +234,7 @@ Length: 100–300 lines. Shorter = under-specified. Longer = should probably spl
175
234
  - **Don't ship copy-pasted source-project SKILL.md files unchanged.** Run search-and-replace for source project names / paths / commands before saving.
176
235
  - **Foundation set is a unit.** Always copy `plan-feature` + `create-spec` + `create-qa-use-cases` together — they reference each other.
177
236
  - **`implement-plan` is generated, not copied.** Its body has too much project-specific content for verbatim shipping.
178
- - **Optional skills (`add-e2e-test`, `add-env-var`, `add-one-off-script`) are gated by user answer.** Don't ship them by default.
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.
179
238
  - **Each skill solves one job.** Two unrelated checklists → split.
180
239
  - **Reference real files in the target.** Skill links must point to existing paths.
181
240
  - **Skills auto-load by description.** Specific triggers, specific outcomes.
@@ -7,7 +7,7 @@ description: Adjust an existing implementation plan in `{{PLAN_DIR}}/` after imp
7
7
 
8
8
  Revise a plan in [`{{PLAN_DIR}}/`]({{PLAN_DIR}}/) after work has begun. Companion to [implement-plan](../implement-plan/SKILL.md): same agents, same review gates, same PR-context flow — but the orchestrator's job here is **history rewriting** instead of forward execution.
9
9
 
10
- The flow is destructive (force-push). Every modification is gated on user confirmation. Default disposition for any ambiguous case is "stop and ask" — never force-push without an explicit per-branch `Confirm` from the user.
10
+ The flow is destructive (force-push). Every modification is gated on user confirmation. Default disposition for any ambiguous case is "stop and ask" — never force-push without an explicit per-branch `Confirm` from the user.{{COMMIT_STRATEGY_REFUSAL_BLOCK}}
11
11
 
12
12
  ## Working assumptions
13
13
 
@@ -211,6 +211,8 @@ Use `git commit --amend` ONLY when:
211
211
  - The amendment is small (≤30% of the original diff size), AND
212
212
  - The user explicitly authorized amend in Step 0.
213
213
 
214
+ {{DEPENDENCY_LICENSE_BLOCK}}
215
+
214
216
  ## Working instructions (same loops as implement-plan)
215
217
  1. Read existing code paths your changes touch.
216
218
  2. Edit. Match existing patterns.
@@ -325,6 +327,7 @@ After every queue entry processes:
325
327
  - **PR-context file is a derived artifact.** Refresh it after the rewrite; never edit the file as a substitute for fixing the diff.
326
328
  - **Subagents commit but never push.** Orchestrator owns force-push. {{PR_RULE_TAIL}}.
327
329
  {{COAUTHOR_RULE_LINE}}
330
+ {{DEPENDENCY_LICENSE_RULE_LINE}}
328
331
  - **Stop on Tier-4 failure** (model escalation, same rules as the [implement-plan "Pick model" step](../implement-plan/SKILL.md#1b-pick-model-from-plans-per-phase-suggestion)).
329
332
  - **Stop on rebase failure** the fixer can't resolve in one retry. Don't ship half-rebased branches.
330
333
 
@@ -0,0 +1,60 @@
1
+ # Canonical `{{DEPENDENCY_LICENSE_BLOCK}}` body
2
+
3
+ Rendered into `implement-plan` / `amend-plan` / `systematic-debugging` SKILL.md by [vinta-derive-skills](../SKILL.md) when `policies.dependency_licenses.enforcement` is `block` or `warn`. Empty string when `off` or the block is absent.
4
+
5
+ The rendered section sits **directly before** `## Working instructions` so subagents see it before they start coding.
6
+
7
+ ## Variants
8
+
9
+ ### `enforcement: block`
10
+
11
+ ```markdown
12
+ ## Adding new third-party dependencies
13
+
14
+ Before running any install command (`npm add`, `pnpm add`, `yarn add`, `pip install`, `poetry add`, `uv add`, `cargo add`, `go get`, `gem install`, equivalents), check the package's SPDX license against the project's forbidden list — see the **Dependency licenses** section in [AGENTS.md](AGENTS.md) for the full list, the per-package overrides, and any project-specific notes.
15
+
16
+ Quick lookup:
17
+
18
+ - **npm / pnpm / yarn**: `npm view <pkg> license`.
19
+ - **PyPI**: `pip index versions <pkg>` then read the project metadata, or open `https://pypi.org/project/<pkg>/`.
20
+ - **Cargo**: `cargo metadata --format-version 1 | jq '.packages[] | select(.name=="<pkg>") | .license'` (after a temporary `cargo add` in a scratch dir, or read `Cargo.toml` upstream).
21
+ - **Go**: open the module's repo `LICENSE` file directly.
22
+ - **Gem**: `gem specification <pkg> licenses`.
23
+
24
+ If the license is in the forbidden list AND the `(package, license)` pair is **not** listed under **Approved overrides** in AGENTS.md:
25
+
26
+ 1. Stop. Do not run the install command.
27
+ 2. Surface the violation to the user with: package name, SPDX identifier, why it's forbidden, link to the upstream license.
28
+ 3. Offer alternatives (search the ecosystem for an MIT / Apache-2.0 / BSD-licensed equivalent) before asking for an override.
29
+ 4. If the user grants a one-off override, the orchestrator must record it in `policies.dependency_licenses.allowed_overrides[]` of `.vinta-ai-workflows.yaml` (package + SPDX + one-line reason) before re-running the install. Undocumented overrides leak into the diff and the reviewer agent will flag them.
30
+
31
+ **License unknown / undeclared.** When the lookup above returns no license, an empty value, `UNKNOWN`, `SEE LICENSE IN <file>`, or only an unstructured `LICENSE` file in the repo with no SPDX identifier, treat it as a **policy decision the user owns** — don't guess, don't auto-infer, don't fall back to "assume MIT". The package may be unlicensed (all-rights-reserved by default in most jurisdictions), proprietary, or simply missing metadata.
32
+
33
+ 1. Stop. Do not run the install command.
34
+ 2. Surface to the user: package name, what was found (e.g. "the `license` field is absent in `package.json`", "PyPI metadata returned `UNKNOWN`", "no LICENSE file in the repo"), the upstream repo / registry URL so the user can verify.
35
+ 3. Ask via `AskUserQuestion`: `Skip — find a licensed alternative`, `Treat as forbidden — refuse install`, `Treat as allowed — record an override` (the third option only when the user has independently confirmed the license off-channel; record the resolved SPDX in `allowed_overrides[]` with the source in the `reason` field, e.g. `"unlicensed but author confirmed MIT via GitHub issue #42"`).
36
+ 4. Don't add the dep until the user picks one of the three.
37
+
38
+ Transitive deps follow the same rule, but checking every transitive license at install time is impractical — the project's CI (or a separate license-audit run) handles the deep walk. The subagent's responsibility is the **direct** add.
39
+ ```
40
+
41
+ ### `enforcement: warn`
42
+
43
+ Identical body, with two edits to the **forbidden-license** branch:
44
+
45
+ - Step 1 changes to: *"Proceed with the install, but record the violation."*
46
+ - Step 4 reframes the override entry as *"the team should still review and either record an `allowed_overrides` entry or remove the dep."*
47
+
48
+ The **License unknown / undeclared** branch is **unchanged** — even in `warn` mode the subagent must stop and ask. Unknown ≠ permissive, and the project has no way to record an override without a confirmed SPDX identifier; treating unknown as "warn + proceed" would silently take on potentially-proprietary code.
49
+
50
+ ### `enforcement: off`
51
+
52
+ Empty string. No section emitted.
53
+
54
+ ## Why a top-level section, not a sub-step
55
+
56
+ The numbered list under `## Working instructions` describes the inner-loop / outer-gate / commit / push cadence. A license-check inserted mid-list would either renumber every downstream step (churn in the rendered diff) or appear as an unnumbered orphan (broken markdown numbering). A separate `## Adding new third-party dependencies` section keeps the policy visible while leaving the existing flow untouched.
57
+
58
+ ## Why repeat the SPDX list in AGENTS.md, not inline
59
+
60
+ The skill body cites AGENTS.md so a single change (new forbidden license, new override, edited notes) doesn't require regenerating every skill. AGENTS.md is the source of truth; the skill is the *pointer*. [vinta-write-agents-md](../../vinta-write-agents-md/SKILL.md) renders the full table from `policies.dependency_licenses`.
@@ -0,0 +1,327 @@
1
+ ---
2
+ name: prepare-worktree
3
+ description: Provision a fully-runnable git worktree for parallel feature work so a long-running plan (or experiment) can build, test, lint, run migrations, and hit databases without disturbing the main checkout — or other parallel worktrees. Reads the active plan (when given one) plus the project's `.gitignore`, package manifests, env templates, and docker config to decide what to symlink, what to copy, what to fork (DBs, env files, compose project names, test databases, sandboxes). Use when the user says "set up a worktree for plan X", "create an isolated env for this feature", "I want to run two plans in parallel without breaking the main checkout", or when [implement-plan](../implement-plan/SKILL.md) opts in via Step 0 question (c). NOT for one-off branch switches that don't need a separate runnable copy of the app.
4
+ ---
5
+
6
+ # Prepare worktree
7
+
8
+ Provision a git worktree the agent (or human) can `cd` into and immediately `lint` / `test` / `build` / `migrate` / `run` against — without touching the main checkout, and without colliding with any other live worktree on the same machine.
9
+
10
+ A bare `git worktree add` is not enough. The runnable parts of any non-trivial app live in *ignored* files and dirs (`node_modules/`, `.env*`, `venv/`, `vendor/`, local SQLite DBs, `.localstack/`, `docker-compose.override.yml`, etc.). This skill walks those ignored paths and decides, per-path:
11
+
12
+ - **Symlink** — when the file/dir is read-only-ish from the feature's perspective (dep install with no new deps, a frozen `.envrc`, a static fixture set).
13
+ - **Copy** — when the feature mutates it (`node_modules/` for a `pnpm add`, `.env` for a new var, the migrations dir).
14
+ - **Fork** — when sharing would corrupt the main checkout's state (`db.sqlite3`, a local Postgres DB used by tests, a docker-compose project name).
15
+
16
+ The output is a worktree that is the **same shape as the main checkout** from the perspective of every dev command in the project, plus a short summary file recording every fork decision so teardown is mechanical.
17
+
18
+ ## When to use
19
+
20
+ - A plan / spec that takes hours-to-days where parallel work in the main checkout would be disruptive (a long migration, a refactor, a feature you want to experiment on while still serving customer support out of `main`).
21
+ - Two-or-more concurrent plans where each needs its own running app + own DB state.
22
+ - A risky migration where the user wants the migration to run against a forked DB, then walk the diff before promoting.
23
+ - [implement-plan](../implement-plan/SKILL.md) Step 0 (c) — when the user opts in, that orchestrator runs this skill once before phase 1, captures the resulting path, and threads it through every subagent's prompt.
24
+
25
+ ## When NOT to use
26
+
27
+ - A small branch switch with no dep churn / no DB writes — `git switch -c …` is enough.
28
+ - The project has no ignored runnable state (rare — usually means the project is so simple a worktree adds friction with no upside).
29
+ - The user is on a filesystem that doesn't support symlinks (Windows non-NTFS volumes, some corporate fileshares). Fall back to copy-only and warn the user up front.
30
+
31
+ ## Inputs (Step 0 — interview)
32
+
33
+ Use `AskUserQuestion` for every finite-choice question. Open prose only when the answer is genuinely free-form.
34
+
35
+ ### 0.1 — Scope of the worktree
36
+
37
+ 1. **Plan-driven or freeform?** `AskUserQuestion`:
38
+ - `Plan-driven — point me at a plan file` → ask for path; read it in the **Plan inspection** step below.
39
+ - `Freeform — just isolate the env, no plan to consult` → skip **Plan inspection**; default every "does the feature do X?" question to "unsure → fork to be safe".
40
+
41
+ 2. **Worktree name** (used as the dir name + as the suffix appended to DB names / docker project names). Default = kebab(plan's feature name) when plan-driven; else ask the user.
42
+
43
+ 3. **Worktree root**. Default = `.claude/worktrees/<name>/` when the runtime (claude-code, codex) writes worktrees there; else `../<repo-name>-wt-<name>/` (a sibling dir of the main checkout, so relative-path tooling that walks up keeps working). Read `.vinta-ai-workflows.yaml` → `run_options.prepare-worktree.worktree_root` for a project override.
44
+
45
+ ### 0.2 — Plan inspection (when plan-driven)
46
+
47
+ Read the plan once and extract (don't ask the user to repeat what's already written):
48
+
49
+ - **New dependencies?** Scan the plan's **Data Model Changes**, **Phased Rollout**, and **Guiding Decisions** sections for `pnpm add`, `npm install`, `pip install`, `poetry add`, `cargo add`, `go get`, `Gemfile` edits. If yes → record `deps_change: true`. Drives the `node_modules` / `vendor/` / `venv/` decision in the **Inventory ignored runnable state** step.
50
+ - **Migrations / data-model changes?** Look at the plan's **Data Model Changes** section, plus `alembic`, `manage.py makemigrations`, `prisma migrate`, `knex migrate`, `goose`, `sqlx migrate`, schema files (`.sql`, `schema.prisma`, `models.py`). If yes → record `schema_change: true`. Drives the DB fork decision in the **Database fork** step.
51
+ - **New env vars?** Look for `process.env.<NEW>`, `os.environ['<NEW>']`, `.env.example` edits, `config.<new>` reads. If yes → record `env_change: true`. Drives the `.env` copy-vs-symlink decision in the **Inventory ignored runnable state** step.
52
+ - **Touches test infra?** New fixtures, factories, seed scripts, a custom `pytest` plugin, a new `vitest` setup file. If yes → record `test_infra_change: true`. Drives whether the worktree gets its own per-suite scratch dir (`tmp/`, `__snapshots__/`, `playwright-report/`).
53
+ - **New services / sidecars?** New `docker-compose.yml` entries, new background workers, a new local proxy. If yes → record `compose_change: true`. Drives the compose project name / network strategy in the **Docker / compose isolation** step.
54
+
55
+ If freeform (the **Scope** step's first answer was `Freeform`): set every flag to `true` (fork everything) — the cost is wasted disk; the cost of a wrong shared-state decision is corrupted data.
56
+
57
+ ### 0.3 — Sanity checks
58
+
59
+ - `git worktree list` — current worktrees + their branches. Refuse to provision a second worktree for the same branch.
60
+ - `git status` of the main checkout — refuse to provision if main has uncommitted changes on a branch you're about to fork from, **unless** the user explicitly says "use HEAD as the worktree base" (record their answer; worktree base = `head` instead of the default `origin/<default-branch>`).
61
+ - Disk space — `df -h .` of the worktree root's filesystem. Warn if `< 2 × du -sh node_modules/` (or equivalent for the project's primary dep dir).
62
+ - Filesystem symlink support — `ln -s /tmp/test-symlink /tmp/.prepare-worktree-symlink-probe && rm /tmp/.prepare-worktree-symlink-probe`. If symlinks aren't supported, flip every "symlink" decision below to "copy" and warn the user.
63
+
64
+ ## Step 1 — Create the worktree
65
+
66
+ ```bash
67
+ git fetch origin
68
+ git worktree add -b <branch> <worktree-path> <base-ref>
69
+ ```
70
+
71
+ - `<branch>` — `<worktree-name>` (or a per-plan convention from the caller; e.g. [implement-plan](../implement-plan/SKILL.md) passes `plan/<plan-id-kebab>/wt`).
72
+ - `<base-ref>` — `origin/<default-branch>` by default; `HEAD` only when the **Sanity checks** step confirmed.
73
+ - `<worktree-path>` — from the worktree-root answer in the **Scope** step.
74
+
75
+ Confirm `git worktree list` shows the new entry. From here, every command runs **inside** the worktree (`cd <worktree-path>` or pass `-C <worktree-path>` on every git call). Don't leave the user's shell in the worktree without explicit confirmation.
76
+
77
+ ## Step 2 — Inventory ignored runnable state
78
+
79
+ Walk the `.gitignore` rules + repo conventions and produce a list of every ignored path the runtime depends on. Read each `.gitignore` file in the repo (root + nested) — `.gitignore` is the closest the project has to a manifest of "things the app needs but we don't commit".
80
+
81
+ Common categories (extend per project):
82
+
83
+ | Category | Typical paths | Default decision (no plan info) |
84
+ |---|---|---|
85
+ | Dep dirs | `node_modules/`, `vendor/`, `venv/`, `.venv/`, `target/`, `bin/`, `obj/` | Symlink (deps_change=false) / Copy + reinstall (deps_change=true) |
86
+ | Build / cache | `dist/`, `build/`, `.next/`, `.turbo/`, `.cache/`, `__pycache__/`, `.pytest_cache/`, `.mypy_cache/` | Skip (rebuilt on next run) |
87
+ | Env files | `.env`, `.env.local`, `.env.development`, `.env.test`, `.envrc` | Copy (env_change=true) / Symlink (env_change=false), then mutate per the **Database fork** + **Docker / compose isolation** steps |
88
+ | Local DBs | `db.sqlite3`, `*.sqlite`, `data/`, `pgdata/`, `.localstack/` | Fork (schema_change=true) / Symlink (schema_change=false) |
89
+ | Per-machine config | `.idea/`, `.vscode/settings.json` (when ignored), `local.settings.json`, `.tool-versions` | Copy (independent edits per worktree) |
90
+ | Test artefacts | `coverage/`, `playwright-report/`, `test-results/`, `__snapshots__/` (when ignored) | Skip; per-worktree fresh dirs |
91
+ | Tool state | `.terraform/`, `.serverless/`, `.aws-sam/`, `.gradle/` | Symlink (read-mostly) / Copy (changes per worktree) |
92
+ | Tracked-by-project AI tooling state | `.vinta-ai-workflows/` | Symlink (shared cache is fine across worktrees) |
93
+
94
+ Record every decision in `.vinta-ai-workflows/worktrees/<name>.yaml` (see the **Write the summary file** step) so teardown can reverse them mechanically.
95
+
96
+ ### 2a — Dep dirs
97
+
98
+ `deps_change = false` → `ln -s <main>/node_modules <worktree>/node_modules` (and same for `vendor/`, `venv/`, …). Save disk + skip `pnpm install`.
99
+
100
+ `deps_change = true` → copy or reinstall:
101
+ - **Copy** (`cp -aR <main>/node_modules <worktree>/`) — fast, but only correct if the package manager doesn't keep absolute paths inside (pnpm's `node_modules/.pnpm/` stores relative symlinks → safe; some yarn PnP setups bake absolute paths → reinstall instead).
102
+ - **Reinstall** (`pnpm install`, `npm ci`, `pip install -r requirements.txt`, `poetry install`, `cargo build`, `go mod download`) — slow but always correct.
103
+
104
+ Default: **copy** for pnpm + npm + cargo + go; **reinstall** for poetry + venv + yarn PnP. Override per-project via `.vinta-ai-workflows.yaml` → `run_options.prepare-worktree.deps_strategy: copy | reinstall`.
105
+
106
+ ### 2b — Env files
107
+
108
+ `env_change = false` → `ln -s <main>/.env <worktree>/.env`. Mutations the agent makes to `.env` would leak into main — flag this in the worktree's `README.md` (written in the **Write the summary file** step).
109
+
110
+ `env_change = true` → `cp <main>/.env <worktree>/.env`. Then mutate per the **Database fork** step (DB URL) + the **Docker / compose isolation** step (compose project name).
111
+
112
+ `.env.example` is **always** symlinked — it's committed; the worktree should keep tracking it.
113
+
114
+ ### 2c — Skip rules
115
+
116
+ Build / cache dirs (`dist/`, `.next/`, `__pycache__/`) — don't copy, don't symlink. Let the next `build` / `test` populate them fresh in the worktree.
117
+
118
+ Test artefact dirs (`coverage/`, `playwright-report/`) — same.
119
+
120
+ ## Step 3 — Database fork (when `schema_change = true` or test DB collision is possible)
121
+
122
+ Two distinct database axes need handling:
123
+
124
+ ### 3a — Dev / app database
125
+
126
+ Whatever the app reads + writes during local dev. Detect the connection string source:
127
+
128
+ 1. `.env` / `.envrc` keys: `DATABASE_URL`, `POSTGRES_URL`, `MYSQL_URL`, `MONGODB_URI`, `REDIS_URL`, …
129
+ 2. Project config files: `config/database.yml` (Rails), `database.ini`, `prisma/schema.prisma`'s `datasource`, `knexfile.js`, Django `settings.py`'s `DATABASES`.
130
+ 3. Docker compose service env: `services.db.environment.POSTGRES_DB`.
131
+
132
+ For each detected DB, ask the user (`AskUserQuestion`) once:
133
+
134
+ - **Fork the DB** — recommended when `schema_change = true` or when the plan's phases run destructive migrations. Strategy depends on the engine:
135
+ - **Postgres (server-based)** — `createdb -T <main_db> <main_db>_wt_<name>` (template-clone if rights allow; else `pg_dump <main_db> | psql <main_db>_wt_<name>` after `createdb`). Update the worktree's `DATABASE_URL` to point at the forked DB. Append `?application_name=wt-<name>` so the user can grep `pg_stat_activity`.
136
+ - **MySQL (server-based)** — `mysqldump <main_db> | mysql <main_db>_wt_<name>` after `CREATE DATABASE`.
137
+ - **SQLite (file-based)** — `cp <main>/db.sqlite3 <worktree>/db.sqlite3`. Symlink would defeat the point.
138
+ - **Mongo / Redis / Elasticsearch** — engine-specific clone, OR per-worktree DB name / key prefix (`REDIS_URL=redis://localhost:6379/<index>` with a free index; `MONGODB_URI=mongodb://localhost:27017/<db>_wt_<name>`).
139
+
140
+ - **Share the DB (don't fork)** — only safe when `schema_change = false` AND there's no risk of conflicting writes. Symlink the SQLite file or point the worktree's env at the same URL. Warn explicitly: "DB shared with main checkout — destructive ops here will be visible in main."
141
+
142
+ - **Stub the DB** — point the worktree at a fresh empty DB (`createdb <main_db>_wt_<name>` + run all migrations + seed if a seed exists). Best when the feature needs schema parity but no production data.
143
+
144
+ Record the chosen strategy + the forked DB name in the worktree summary (written in the **Write the summary file** step).
145
+
146
+ ### 3b — Test database
147
+
148
+ A different beast — tests on the same engine but a different DB name (`<main_db>_test`, `test_<repo>`). Parallel worktrees running tests against the same `<main_db>_test` will overwrite each other's fixtures and produce flaky failures.
149
+
150
+ - **`pytest-django` / `pytest`** — set `--reuse-db` per worktree via a per-worktree `DJANGO_SETTINGS_MODULE` env, OR override `DATABASES['default']['NAME']` to `<main_db>_test_wt_<name>` in `conftest.py` when the env var `WORKTREE_NAME` is set. Drop a `conftest_worktree.py` patch in the worktree (don't edit `conftest.py` in tracked code — too easy to commit by accident).
151
+ - **`vitest` / `jest`** — set `TEST_DATABASE_URL` per worktree; ensure the test setup respects it.
152
+ - **Rails** — `DATABASE_URL` for the `test` env, `<main_db>_test_wt_<name>`.
153
+ - **Docker-compose-based test DB** — see the **Docker / compose isolation** step below; per-worktree compose project name fixes it.
154
+
155
+ If `test_infra_change = true` → fork the test DB unconditionally. If `false` and the user says "share is fine" → still flag the race risk; offer a one-line fix to switch later.
156
+
157
+ ### 3c — Migrations against the forked DB
158
+
159
+ When the plan has migrations: run them once now against the forked DB so subsequent agent runs in the worktree don't surprise the user. Use the project's standard migration command (`pnpm migrate`, `python manage.py migrate`, `alembic upgrade head`, `prisma migrate dev`, `knex migrate:latest`).
160
+
161
+ Failure → surface the error, leave the DB un-migrated, ask the user how to proceed (skip, retry, drop and recreate the DB).
162
+
163
+ ## Step 4 — Docker / compose isolation (when `compose_change = true` OR project uses compose)
164
+
165
+ If the project has a `docker-compose.yml` / `compose.yaml` / `docker-compose.override.yml`, every worktree needs **its own project name** so containers, networks, and volumes don't collide.
166
+
167
+ 1. Set `COMPOSE_PROJECT_NAME=<repo>_<worktree-name>` in the worktree's `.env` (or in a `.envrc` if the project uses direnv). Then `docker compose up` inside the worktree spins a fresh container set.
168
+
169
+ 2. **Shared network** — if the worktree's app needs to reach services running in the *main* checkout's compose stack (a queue, a cache, a search index that's expensive to spin twice), put them on an external network:
170
+
171
+ ```yaml
172
+ # docker-compose.override.yml in the worktree (or in main; pick one place)
173
+ networks:
174
+ shared:
175
+ external: true
176
+ name: <repo>_shared
177
+ services:
178
+ app:
179
+ networks:
180
+ - shared
181
+ ```
182
+
183
+ Or use `network_mode: host` if the project uses that pattern. Decide once per project and record in `.vinta-ai-workflows.yaml` → `run_options.prepare-worktree.compose_network: per-worktree | shared-external | host`.
184
+
185
+ 3. **Linters / formatters / test runners that run inside docker** (a Dockerfile-based `pnpm test`, a `lint` target that mounts source into a sidecar container) — these MUST run with the worktree's `COMPOSE_PROJECT_NAME`. Otherwise concurrent runs hit the same container and one wins. The standard fix: ensure the project's lint/test scripts read `COMPOSE_PROJECT_NAME` from env instead of hard-coding it.
186
+
187
+ 4. **Per-worktree volumes** — let compose auto-create volumes namespaced by project name. Don't manually `external: true` data volumes (defeats isolation).
188
+
189
+ ## Step 5 — Other shared infra to fork
190
+
191
+ Quick walk-through of common gotchas. For each, follow the same fork/share pattern:
192
+
193
+ - **Redis** — pick a free DB index (`/0` … `/15`) per worktree, or per-worktree key prefix if all indices are taken.
194
+ - **Object storage / S3 / GCS** — per-worktree bucket prefix in `.env` (`S3_PREFIX=wt-<name>/`); always set, even when sharing.
195
+ - **Message queues** — per-worktree queue name suffix.
196
+ - **Search index** — per-worktree index suffix.
197
+ - **Cron / background jobs** — disable cron in the worktree's `.env` (`DISABLE_CRON=true`) unless the feature needs them. Two workers polling the same queue against a shared DB is a footgun.
198
+ - **Webhooks / dev tunnels** (ngrok, cloudflared) — each worktree needs its own tunnel hostname; if the project hard-codes one URL, document the override.
199
+
200
+ If the **Plan inspection** step inferred the plan doesn't touch any of these: symlink / share. If unsure: fork. Cheap.
201
+
202
+ ## Step 6 — Write the summary file
203
+
204
+ `.vinta-ai-workflows/worktrees/<name>.yaml` (committed to `.gitignore` via the existing `.vinta-ai-workflows/` umbrella). Schema:
205
+
206
+ ```yaml
207
+ name: <worktree-name>
208
+ path: <abs-path-to-worktree>
209
+ branch: <branch-name>
210
+ base_ref: <origin/main | HEAD>
211
+ created_at: <ISO 8601>
212
+ plan_path: <ai-plans/...> # null when freeform
213
+ flags:
214
+ deps_change: <bool>
215
+ schema_change: <bool>
216
+ env_change: <bool>
217
+ test_infra_change: <bool>
218
+ compose_change: <bool>
219
+ state:
220
+ deps:
221
+ strategy: symlink | copy | reinstall
222
+ paths: [node_modules, vendor, venv, ...]
223
+ env:
224
+ strategy: symlink | copy
225
+ files: [.env, .envrc, ...]
226
+ dev_db:
227
+ engine: postgres | mysql | sqlite | mongo | redis
228
+ strategy: fork | share | stub
229
+ forked_name: <forked db name> # null when share / stub
230
+ connection_url_var: DATABASE_URL
231
+ test_db:
232
+ engine: ...
233
+ strategy: fork | share
234
+ forked_name: ...
235
+ connection_url_var: TEST_DATABASE_URL
236
+ compose:
237
+ project_name: <repo>_<worktree-name>
238
+ network_strategy: per-worktree | shared-external | host
239
+ other:
240
+ redis_db_index: <int>
241
+ s3_prefix: wt-<name>/
242
+ cron_disabled: <bool>
243
+ notes: |
244
+ <freeform — anything the user / agent should know>
245
+ ```
246
+
247
+ Also drop a `WORKTREE.md` at the worktree root:
248
+
249
+ ```markdown
250
+ # Worktree: <name>
251
+
252
+ Branch: `<branch>` (based on `<base-ref>`).
253
+
254
+ ## What's forked vs shared
255
+ <one-line per row in `state` above>
256
+
257
+ ## How to run things
258
+ - Lint: `<project lint command>` (runs inside this worktree)
259
+ - Tests: `<project test command>` (against `<test_db.forked_name>`)
260
+ - App: <`pnpm dev` / `python manage.py runserver` / …>
261
+ - DB: <forked db name + connection url>
262
+
263
+ ## Teardown
264
+ When the plan is merged / abandoned:
265
+ git worktree remove <path>
266
+ <drop-db command> # if fork strategy was `fork`
267
+ docker compose -p <project_name> down -v # if compose was forked
268
+
269
+ The summary file at `.vinta-ai-workflows/worktrees/<name>.yaml` records every
270
+ decision for mechanical teardown.
271
+ ```
272
+
273
+ ## Step 7 — Report
274
+
275
+ One paragraph to the caller:
276
+
277
+ - Worktree path + branch.
278
+ - One line per fork decision (deps / env / dev DB / test DB / compose / other).
279
+ - Anything the user must do manually before running the app (`source .envrc`, `direnv allow`, login to a cloud CLI in the worktree, etc.).
280
+ - Teardown command.
281
+
282
+ When called from [implement-plan](../implement-plan/SKILL.md), this report becomes the orchestrator's confirmation that subagents can be spawned against the worktree path.
283
+
284
+ ## Teardown
285
+
286
+ A sibling skill / explicit command — not auto-run by this skill. Steps:
287
+
288
+ 1. Confirm no uncommitted changes in the worktree (`git -C <path> status`).
289
+ 2. `git worktree remove <path>` (or `--force` after explicit confirmation if the user is fine losing work).
290
+ 3. Drop the forked DB (`dropdb <forked_name>`, `DROP DATABASE`, `rm <sqlite-file>`, …) — read the strategy from the summary YAML.
291
+ 4. `docker compose -p <project_name> down -v` for forked compose.
292
+ 5. Remove `.vinta-ai-workflows/worktrees/<name>.yaml`.
293
+
294
+ Every step gated on user confirmation when the worktree has un-pushed branches.
295
+
296
+ ## Rules
297
+
298
+ - **Symlink for reads, copy for writes, fork for state.** This is the only mental model that scales. Default to fork when unsure — disk is cheap, corrupted main-checkout DBs are not.
299
+ - **Never share a writable DB across worktrees by default.** The race conditions are subtle and the failure mode is silent data corruption.
300
+ - **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.
301
+ - **Worktree root governed by runtime conventions.** claude-code uses `.claude/worktrees/`; other harnesses use sibling dirs. Don't fight the harness — match it.
302
+ - **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.
303
+ - **Worktree base = `origin/<default-branch>` by default.** `HEAD` only when the user explicitly confirms; record the choice in the summary.
304
+ - **Refuse to provision a second worktree for the same branch.** Git enforces this — don't try to work around it.
305
+ - **Don't auto-install heavy deps** (e.g. `pnpm install` from scratch) without confirmation when the project's main `node_modules/` is already populated — symlink first, ask if reinstall is needed.
306
+
307
+ ## Pitfalls
308
+
309
+ - **Symlinking `node_modules` for a `pnpm add` phase.** The new package writes back through the symlink into the main checkout's store. Detect dep churn in the **Plan inspection** step and copy/reinstall instead.
310
+ - **Forking the dev DB but forgetting the test DB.** Tests still hit the shared test DB and stomp on parallel worktrees' fixtures. Both axes need their own decision in the **Database fork** step.
311
+ - **Forgetting `COMPOSE_PROJECT_NAME`.** Containers from worktree-A overwrite worktree-B's containers; volumes get nuked. Set it in `.env` so every `docker compose` call inherits.
312
+ - **Sharing a Redis DB without per-worktree prefix.** Tests writing `user:123` collide across worktrees. Pick an index OR a key prefix.
313
+ - **Copying `node_modules` for yarn PnP / absolute-path setups.** The copy carries baked-in paths from the main checkout. Reinstall instead — pnpm's relative-symlink store is the safe-to-copy exception.
314
+ - **Leaving cron / background workers on in the worktree.** They poll the shared DB and double-process jobs. Default to off.
315
+ - **Provisioning a worktree, running migrations, then realizing the user wanted to share the DB.** The **Database fork** step asks BEFORE migrating; rollback of a forked-DB migration is mechanical (drop the DB), but rollback of a shared-DB migration is a half-day.
316
+ - **Symlinking `.env` and then editing it.** The edit leaks into main. Copy (not symlink) the moment `env_change = true`.
317
+
318
+ ## Verification
319
+
320
+ After the **Write the summary file** step writes the summary:
321
+
322
+ 1. `git worktree list` shows the new entry.
323
+ 2. `cd <worktree-path>` then run the project's standard lint + test commands. Both must run clean against the worktree's forked DB / env.
324
+ 3. `git -C <worktree-path> status` is clean (no accidental file additions from the prep step).
325
+ 4. The summary YAML parses (`python3 -c "import yaml; yaml.safe_load(open('.vinta-ai-workflows/worktrees/<name>.yaml'))"`).
326
+ 5. `WORKTREE.md` exists at the worktree root with accurate fork / share annotations.
327
+ 6. Optional smoke test: run a single new-test command in the worktree (e.g. `pytest -x tests/health.py`) — confirms env vars resolved, DB reachable, deps importable.