@skitterbyte/skitterspec-jira 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/MIGRATION.md +1266 -0
  2. package/README.md +44 -2
  3. package/assets/claude-md-section.md +85 -0
  4. package/assets/commands/allow-main.md +33 -0
  5. package/assets/commands/spec-connect.md +13 -0
  6. package/assets/commands/spec-live.md +14 -0
  7. package/assets/commands/spec-remote-review.md +37 -0
  8. package/assets/commands/spec-skip.md +24 -0
  9. package/assets/core/SETUP.md +192 -0
  10. package/assets/core/env.config.json.example +56 -0
  11. package/assets/core/env.config.md +606 -0
  12. package/assets/core/gating.config.json.example +4 -0
  13. package/assets/core/gating.config.md +81 -0
  14. package/assets/core/jira.config.json.example +18 -0
  15. package/assets/core/jira.config.md +88 -0
  16. package/assets/hooks/main-guard.cjs +162 -0
  17. package/assets/hooks/review-gate.cjs +146 -0
  18. package/assets/review/page.html +3384 -0
  19. package/assets/rules/commit-trailers.md +96 -0
  20. package/assets/rules/negative-checks.md +69 -0
  21. package/assets/rules/offered-unblocks.md +100 -0
  22. package/assets/rules/spec-planning.md +702 -0
  23. package/assets/rules/spec-reports.md +572 -0
  24. package/assets/skills/no-spec/SKILL.md +249 -0
  25. package/assets/skills/spec/SKILL.md +714 -0
  26. package/assets/skills/spec-bug/SKILL.md +585 -0
  27. package/assets/skills/spec-cancel/SKILL.md +261 -0
  28. package/assets/skills/spec-complete/SKILL.md +451 -0
  29. package/assets/skills/spec-diff/SKILL.md +870 -0
  30. package/assets/skills/spec-hotfix/SKILL.md +607 -0
  31. package/assets/skills/spec-init/SKILL.md +178 -0
  32. package/assets/skills/spec-jira-setup/SKILL.md +221 -0
  33. package/assets/skills/spec-list/SKILL.md +173 -0
  34. package/assets/skills/spec-next/SKILL.md +678 -0
  35. package/assets/skills/spec-push/SKILL.md +306 -0
  36. package/assets/skills/spec-review/SKILL.md +256 -0
  37. package/assets/skills/spec-reviewed/SKILL.md +284 -0
  38. package/assets/skills/spec-start/SKILL.md +496 -0
  39. package/assets/skills/spec-status/SKILL.md +114 -0
  40. package/assets/skills/spec-sync/SKILL.md +220 -0
  41. package/assets/skills/spec-to-main/SKILL.md +141 -0
  42. package/bin/skitterspec-jira.js +68 -0
  43. package/package.json +40 -4
  44. package/src/cli.js +5967 -0
  45. package/src/deprecate.js +138 -0
  46. package/src/env/bootstrap.js +188 -0
  47. package/src/env/building.js +143 -0
  48. package/src/env/classify.js +176 -0
  49. package/src/env/commitcmd.js +108 -0
  50. package/src/env/config.js +850 -0
  51. package/src/env/dev.js +63 -0
  52. package/src/env/glob.js +34 -0
  53. package/src/env/hooks.js +226 -0
  54. package/src/env/hotfix.js +413 -0
  55. package/src/env/hotfixland.js +302 -0
  56. package/src/env/integrate.js +167 -0
  57. package/src/env/live.js +479 -0
  58. package/src/env/mainguard.js +299 -0
  59. package/src/env/provision.js +466 -0
  60. package/src/env/proxy.js +225 -0
  61. package/src/env/prune.js +119 -0
  62. package/src/env/registry.js +159 -0
  63. package/src/env/render.js +17 -0
  64. package/src/env/resolve.js +716 -0
  65. package/src/env/review.js +2622 -0
  66. package/src/env/reviewers/coderabbit.js +228 -0
  67. package/src/env/reviewers.js +517 -0
  68. package/src/env/serve.js +1044 -0
  69. package/src/env/supervise.js +157 -0
  70. package/src/env/teardown.js +248 -0
  71. package/src/env/trust.js +87 -0
  72. package/src/gating.js +155 -0
  73. package/src/init.js +1150 -0
  74. package/src/lines-diff.js +114 -0
  75. package/src/prompts.js +129 -0
  76. package/src/vendor/jira/adf.js +414 -0
  77. package/src/vendor/jira/api.js +295 -0
  78. package/src/vendor/jira/cli-sync.js +1252 -0
  79. package/src/vendor/jira/commands.js +44 -0
  80. package/src/vendor/jira/config.js +286 -0
  81. package/src/vendor/jira/credentials.js +108 -0
  82. package/src/vendor/jira/dialect.js +45 -0
  83. package/src/vendor/jira/mcp.js +129 -0
  84. package/src/vendor/provider-kit/index.js +41 -0
  85. package/src/vendor/provider-kit/src/adapter.js +23 -0
  86. package/src/vendor/provider-kit/src/apply.js +207 -0
  87. package/src/vendor/provider-kit/src/args.js +68 -0
  88. package/src/vendor/provider-kit/src/assignment.js +277 -0
  89. package/src/vendor/provider-kit/src/config.js +251 -0
  90. package/src/vendor/provider-kit/src/credentials.js +404 -0
  91. package/src/vendor/provider-kit/src/doctor.js +35 -0
  92. package/src/vendor/provider-kit/src/http.js +63 -0
  93. package/src/vendor/provider-kit/src/identity.js +93 -0
  94. package/src/vendor/provider-kit/src/list-assignee.js +71 -0
  95. package/src/vendor/provider-kit/src/list-rows.js +35 -0
  96. package/src/vendor/provider-kit/src/md-compare.js +39 -0
  97. package/src/vendor/provider-kit/src/md-tables.js +94 -0
  98. package/src/vendor/provider-kit/src/prompt.js +50 -0
  99. package/src/vendor/provider-kit/src/released.js +219 -0
  100. package/src/vendor/provider-kit/src/specs.js +248 -0
  101. package/src/vendor/provider-kit/src/transport.js +22 -0
  102. package/src/vendor/sync-core/index.js +92 -0
  103. package/src/vendor/sync-core/src/base.js +81 -0
  104. package/src/vendor/sync-core/src/compare.js +338 -0
  105. package/src/vendor/sync-core/src/dialect.js +51 -0
  106. package/src/vendor/sync-core/src/fields.js +36 -0
  107. package/src/vendor/sync-core/src/legacy.js +100 -0
  108. package/src/vendor/sync-core/src/normalize.js +971 -0
  109. package/src/vendor/sync-core/src/push.js +85 -0
  110. package/src/vendor/sync-core/src/retarget.js +278 -0
  111. package/src/vendor/sync-core/src/task-block.js +308 -0
  112. package/src/vendor/sync-core/src/verify.js +67 -0
  113. package/src/vendor/sync-core/src/write.js +191 -0
@@ -0,0 +1,451 @@
1
+ ---
2
+ name: spec-complete
3
+ description: Finish a spec — verify all phases are genuinely done, update progress, then move it into specs/complete/. Targets a spec by name (arg) or the spec currently in context. Use when the user says "/spec-complete", "mark this spec done", or "this spec is complete".
4
+ ---
5
+
6
+ # /spec-complete — verify, finalise, archive a spec
7
+
8
+ > Stay silent while this runs — speak only to ask something you cannot answer
9
+ > yourself, or to report a failure at the moment it happens. Read
10
+ > `.claude/rules/spec-reports.md` before reporting; it defines the block this
11
+ > skill ends with.
12
+
13
+ ## 1. Identify the target spec
14
+
15
+ - Use the name/path argument if given, else the spec **in context**. If unclear,
16
+ ask which spec.
17
+ - Locate the spec folder under `specs/` (usually `specs/in-progress/`). Entry
18
+ point is its `00-overview.md`; phases are separate files (`01-<slug>.md`, `02-…`)
19
+ listed in its phase index (legacy specs may be a bare `<name>.md`).
20
+ - **A hotfix already in `specs/complete/` whose worktree still stands** is a
21
+ hotfix waiting on its deploy. That run is the teardown only: go straight to
22
+ step 7's hotfix paragraph, and do not re-verify, re-move or re-land it.
23
+
24
+ ## 2. Double-check progress — don't rubber-stamp
25
+
26
+ Before marking complete, confirm the work is actually finished:
27
+
28
+ - Read every phase file. For each **unchecked** task, check whether it is in fact
29
+ done in the code — tick it (`- [x]`) if so, or surface it if not.
30
+ - Run the project's typecheck and test commands. The suite must be **green** to
31
+ call a spec complete.
32
+ - For a **Bug** or **Hotfix** spec (`Type: Bug` / `Type: Hotfix`), confirm the
33
+ originally-failing test named in the spec now passes — that test is the proof
34
+ the fix works.
35
+ - If genuinely incomplete work remains, **stop and tell the user** rather than
36
+ forcing completion. Offer to finish it (`/spec-next`) or to complete with the
37
+ remaining items explicitly listed as deferred.
38
+ - **Check for pre-existing uncommitted changes — before you touch anything.**
39
+ Run `git status`. Anything already uncommitted is the *user's* work (a
40
+ half-finished phase, a stray fix) and must not be swept into the completion
41
+ commit: offer `/commit` and **stop**. Everything this skill writes in steps 3–4
42
+ is its own, and step 4 commits that.
43
+
44
+
45
+ ### Release gating (only when configured)
46
+
47
+ **Only when `specs/.core/gating.config.json` exists.** Run
48
+ `skitterspec gating check <spec>` and report what it says. This is the last point
49
+ at which the question can still be answered cheaply, which is exactly why it is
50
+ asked here — a spec that reaches `Complete` with no decision recorded is the gap
51
+ the feature exists to close.
52
+
53
+ **Never refuse to complete over it.** Say plainly that no decision was recorded,
54
+ offer to record one now, and finish either way. The work is done; withholding the
55
+ status flip would punish the wrong thing.
56
+
57
+ **This check is advisory.** It reports; it never refuses, and nothing below is
58
+ conditional on it. A spec written before the project adopted gating has no header
59
+ and is not broken — turning this into a gate would accuse the very specs the
60
+ feature was designed not to disturb.
61
+
62
+ ## 3. Update the spec
63
+
64
+ - Tick all completed tasks in the phase files; flip every finished phase-file
65
+ heading **and** every row in the `00-overview.md` phase index to `✅`.
66
+ - Set the **Status** header in the entry point:
67
+ `> **Status:** Complete (<YYYY-MM-DD>)`.
68
+ - Append a **State log** row:
69
+ `| <YYYY-MM-DD> | Complete | complete | <git user.name> |`.
70
+ - Add a **Changelog** entry:
71
+ `- <YYYY-MM-DD> — Completed; all phases done, tests green.`
72
+ (Note any consciously-deferred items here too.)
73
+
74
+ ## 4. Move to complete — and commit it
75
+
76
+ `mkdir -p specs/complete` then **`git mv`** the file or folder:
77
+ `git mv "specs/in-progress/<name>" "specs/complete/<name>"` (preserve history;
78
+ move the whole folder). The `specs/complete/` folder is the record of finished
79
+ specs — `git log`/the per-spec State log give the completion order.
80
+
81
+ **Only when `specs/.core/jira.config.json` exists** and the spec's overview
82
+ carries a `jira_identifier`. Either missing → **skip**, in one line
83
+ (`not linked to Jira — /spec-push to mirror it`), and carry on. Nothing else in
84
+ this step changes.
85
+
86
+ **Refresh the mirror now, without asking.** Run `/spec-push`. The repo has just
87
+ become the truth about this spec's state, and the tracker is a generated mirror
88
+ of it — leaving them to diverge until someone remembers to push by hand is the
89
+ gap this exists to close. It costs one engine call and no model tokens when
90
+ Jira credentials are set (see `apply.transport` in `jira.config.md`).
91
+
92
+ - **Never mint.** An unlinked spec is skipped, not created. An issue born `Done`
93
+ is tracker noise, and a spec kept deliberately local should stay that way —
94
+ `/spec-push` is how someone opts in.
95
+ - **Never fatal.** If the push fails — offline, no credentials, a Jira error —
96
+ say so and **finish the operation anyway**. The spec is
97
+ complete/cancelled/reviewed in the repo regardless; the mirror is disposable
98
+ and the next push repairs it. Do not roll anything back, and do not stop to
99
+ ask.
100
+ - **Say what happened** in the skill's report: mirror updated, skipped as
101
+ unlinked, or failed with the reason.
102
+ - **There is no assignment to release here.** Assignment sync is not yet
103
+ supported by the Jira provider; the `> **Developer:**` header stays put as the
104
+ repo's own record of who actioned the work.
105
+
106
+ ### Why it sits here
107
+
108
+ In `/spec-complete` and `/spec-cancel` this step is deliberately pinned
109
+ **after the `git mv` and before the commit**, and both halves matter:
110
+
111
+ - **After the move**, because the projection reads a spec's workflow status from
112
+ its folder bucket. Push while the folder is still in `in-progress/` and the
113
+ issue is set to the status the spec is *leaving*.
114
+ - **Before the commit**, because the push stamps keys into the spec and writes a
115
+ snapshot under `specs/.core/`. Push after the commit instead and both are left
116
+ uncommitted, which makes `spec-env integrate` refuse to land the branch.
117
+
118
+ The snapshot is reached **by name**, not by breadth. It is a declared
119
+ `spec.companionPaths` entry (`specs/.core/jira-base/{identifier}.base.json`),
120
+ so `spec-env stage` returns it among this spec's owned paths and the commit below
121
+ names it. This is why the commit no longer stages `specs/` wholesale: the one
122
+ file outside the spec's folder that had to be swept up is addressable, so the
123
+ breadth bought nothing and cost another session's work.
124
+
125
+ Then **commit the completion edits** — steps 3–4 are this skill's own output, so
126
+ it finishes its own work rather than handing you a dirty tree. Ask the engine
127
+ which paths are this spec's, then stage and commit **exactly those**:
128
+
129
+ ```
130
+ skitterspec spec-env stage <name> # lists them; --json to consume
131
+ git add -- <the owned paths>
132
+ git commit -m "chore(spec): complete <name>" -- <the owned paths>
133
+ ```
134
+
135
+ **Never `git add specs/`.** That stages a *directory*, so a spec another
136
+ session is part-way through writing lands in this commit under this spec's
137
+ ticket. The `--` on the **commit** is the other half: a checkout has one
138
+ `.git/index`, shared by every session standing in it, so a bare `git commit`
139
+ takes whatever else is staged there however carefully you staged your own.
140
+ `.claude/rules/spec-planning.md` carries the full account.
141
+
142
+ So this commits exactly the status flip and the move, and nothing of yours — or
143
+ of anyone else's — rides along.
144
+ **This is what lets step 6 land:** `integrate` refuses a dirty worktree, so
145
+ without committing here the skill would block on the very edits it just made.
146
+ Do not `git push`.
147
+
148
+ ## 5. Report
149
+
150
+ **The block is emitted when the run ends, not where this section sits.** The
151
+ sections below run after it, so their outcome belongs in the block — write it
152
+ once, at the end, with what actually happened.
153
+
154
+ End with the block defined in `.claude/rules/spec-reports.md`. That file carries
155
+ the shape; this section carries only what is specific here.
156
+
157
+ **Verdicts**
158
+
159
+ - `✅` — every phase done, tests green, landed, torn down.
160
+ - `⚠️` — finished, with something worth knowing: teardown declined, a tracker
161
+ refresh that failed, a hotfix tag still waiting to be pushed, an after-land
162
+ command that failed.
163
+ - `❌` — it acted and stopped part-way — a conflicted rebase, a failed
164
+ cherry-pick, a red base after landing. Say what state the repo is in and quote
165
+ the failure; there is a mess to clear and the reader needs to know where.
166
+ - `⏸` — phases are unfinished, tests are red, or the tree is dirty. Nothing
167
+ moved.
168
+
169
+ **Fields:** `Tracker` · `Spec` · `Tests` · `Notes` · `Landed` · `Worktree` ·
170
+ `Follow-ups` · `Next`
171
+
172
+ `Notes` carries the after-land commands step 6 ran, and where, and any it
173
+ skipped because the land matched no `when` — or, on a failure, the command that failed and the ones that did not run. A project with
174
+ no `afterLand` says nothing about it.
175
+
176
+ **Report this spec and no other.** A completion is the moment it is most
177
+ tempting to survey what is left — what else is in flight, what is in the
178
+ backlog, which worktrees are still standing — and it is the moment it helps
179
+ least: the reader has just finished something and cannot tell whether a line
180
+ about another spec followed from it. `Next` is one action for this spec, and
181
+ `/spec-list` is where the wider picture lives if they want it.
182
+
183
+ `Landed` says how, because the two ways differ: a fast-forward names the base
184
+ and the commit, a hotfix names the release tag, whether it was pushed, and the
185
+ `main` carry — and a release tag that has not been pushed is a `⚠️`, not a `✅`. `Worktree` says
186
+ what was reclaimed, including any orphaned volumes swept, or that teardown was
187
+ declined and the worktree still stands.
188
+
189
+ ## 6. Land the branch (opt-in, only if isolated)
190
+
191
+ **Only when `specs/.core/env.config.json` exists and the spec was provisioned**
192
+ (a worktree in `worktree` mode, or its branch in the checkout in `checkout`
193
+ mode — `spec-env integrate` and `down` pick the right plan from `mode`, so the
194
+ commands you run differ but these steps do not)
195
+ (it was provisioned by `/spec-start` or `/spec-hotfix`). Otherwise skip this entirely
196
+ — a non-isolated spec has nothing to land, and `/spec-complete` behaves exactly as
197
+ before. When it applies, offer to land the finished branch so the work reaches its
198
+ destination in one flow. **How it lands depends on the spec type:**
199
+
200
+ ### 6-hotfix. `Type: Hotfix` — release, push, carry (not fast-forward)
201
+
202
+ A hotfix is built on an old release **tag**, so it can't fast-forward onto `main`.
203
+ The engine runs its landing itself — `hotfix land` releases, pushes and carries,
204
+ in that order, and stops at the first step that fails:
205
+
206
+ 1. **Require a clean worktree.** Step 4 already committed the completion edits, so
207
+ this should pass. If the tree is *still* dirty, that's unrelated work — offer
208
+ `/commit` and **stop**. The order matters beyond tidiness: a project's
209
+ `hotfix.releaseWith` (skittership's `release-hotfix.cjs`) refuses a dirty tree
210
+ and an already-tagged HEAD, so the release has to run **after** the §4 commit.
211
+ 2. **Ask before pushing.** Pushing the tag is what starts the prod deploy, so it is
212
+ the operator's to say yes to — ask once, plainly, naming the tag-to-be and the
213
+ remote (`skitterspec spec-env hotfix land <name> --plan` says what will run).
214
+ 3. **Land it.** On a yes:
215
+
216
+ ```
217
+ skitterspec spec-env hotfix land <name> --push
218
+ ```
219
+
220
+ On a no, run it **without** `--push`. Add `--also <tag>` for each extra release
221
+ line to patch (test/demo lines — tag-only, never through `releaseWith`).
222
+ It:
223
+ - **releases** the branch head — through `hotfix.releaseWith` when the project
224
+ set one (its version commit and tag; its one JSON line names the tag), else
225
+ the built-in tagger (`hotfix.bump`: `patch`, or `hotfix-suffix` for
226
+ `X.Y.Z-hotfix.N`). The release is recorded, so a re-run never cuts a second;
227
+ - **pushes** the branch, then the tag, to `origin` — only with `--push`. Without
228
+ it the run **holds** here, exit 0, printing both push commands; a re-run after
229
+ the operator pushes by hand finds the tag on the remote and carries on;
230
+ - **carries** the fix onto `main`: the branch's commits minus the version commit
231
+ and minus anything `main` already has, so the spec's completion commits arrive
232
+ and `main`'s version line does not move. In place when `main` is checked out
233
+ and clean, through a throwaway worktree when it is not checked out, and
234
+ **refused** — with the command to run later — when it is checked out and
235
+ dirty.
236
+
237
+ Relay its step lines. A **non-zero exit** is a refusal or a failure at a named
238
+ step — a refused release, a failed push, a conflicted carry (already aborted,
239
+ naming the commit). Relay it and **stop**; whatever was cut is recorded, so the
240
+ fix is to clear the cause and re-run the same command. On a **no-op** ("nothing
241
+ to land"), say so and continue.
242
+ 4. **Run the after-land commands** when the run ends with a
243
+ `then, in the primary checkout (<path>), run:` section — exactly as step 3
244
+ of 6-feature/bug below says. `hotfix land` prints it only when the carry moved
245
+ `main`, so a held, prod-only or already-carried land has none.
246
+ 5. **Re-test on `main`** after the carry — it must be **green**.
247
+ 6. **Report** — `Landed` names the release tag and whether it was **pushed**, and
248
+ what the carry did. A tag cut but not pushed is a `⚠️`, not a `✅`.
249
+ **Then stop: the worktree is kept** (step 7). The branch is what the next
250
+ `-hotfix.N` is cut from if this deploy fails, so it waits for the deploy.
251
+
252
+ ### 6-feature/bug. `Type: Feature` / `Type: Bug` — rebase + fast-forward
253
+
254
+ Land the finished branch on the base branch so the work reaches `main` (or your
255
+ configured `baseBranch`) in one flow. (Need the work on `main` *before* the spec
256
+ is finished — e.g. to run a later phase in CI or a shared test env? Use
257
+ **`/spec-to-main`**: same rebase + fast-forward, but it leaves the spec
258
+ `In Progress` and the worktree standing, and it's repeatable.)
259
+
260
+ 1. **Require a clean worktree.** Step 4 already committed the completion edits, so
261
+ integrate's dirty-tree guard should pass. If the tree is *still* dirty, that's
262
+ unrelated work — offer `/commit` and **stop**; don't sweep it in.
263
+ **If the spec is live** (you took the running instance with `/spec-live`):
264
+ `integrate` is live-aware — it ends the live session first (releases the branch
265
+ back to base, re-isolates it into its worktree, clears the receipt), then prints
266
+ the normal landing plan. Commit any live fixes to the branch first; it refuses
267
+ if the primary checkout is dirty, or if a *different* spec holds it (release that
268
+ one by typing `/spec-live main`). Teardown (step 7) is unchanged.
269
+ **Work-loss abort.** Before it ends the live session, `integrate` checks the
270
+ work is actually landable and **aborts loudly** rather than finalize a spec
271
+ having landed nothing. Two cases, both leaving the live session intact:
272
+ - *stranded commits* — commits sit on the worktree's **detached HEAD** (e.g. a
273
+ pre-fix `/spec-next` committed there instead of on the branch). It prints the
274
+ count, the sha, and a `git -C <worktree> branch <tmp> <sha>` recovery hint —
275
+ recover those commits onto the branch, then re-run.
276
+ - *no worktree* — the spec is live but its worktree is gone. Re-isolate it with
277
+ `skitterspec spec-env up <name>`, then re-run.
278
+ Relay the diagnostic to the user and **stop** — do not tear anything down.
279
+ 2. **Plan + execute.** Run `skitterspec spec-env integrate <name>` and run the
280
+ printed commands **in order**:
281
+ - `git -C <worktree> rebase <base>` — replay the branch onto base.
282
+ - `git -C <mainRepoPath> merge --ff-only <branch>` — fast-forward base.
283
+ On a **rebase conflict** (non-zero exit), run
284
+ `git -C <worktree> rebase --abort`, relay the conflict, and **stop** — leave it
285
+ to the user; do not tear anything down.
286
+ On a **no-op** ("already landed"), just say so and continue.
287
+ 3. **Run the after-land commands.** Where the plan ends with a
288
+ `then, in the primary checkout (<path>), run:` section — the project's
289
+ `afterLand`, such as a data migration — run those commands in that
290
+ directory, in order, after the fast-forward succeeds. Run only the lines under that
291
+ heading; a `skipped (nothing in the land matched):` block lists conditional
292
+ entries this land did not need, and is information, never commands.
293
+ **Also on the no-op**:
294
+ the engine prints them there deliberately, because a land made on another
295
+ machine leaves them pending here. **Stop at the first non-zero exit** — a
296
+ later command may depend on it — and carry on with the rest of the skill:
297
+ a failure makes the run `⚠️`, naming the command that failed and the ones
298
+ that did not run, and it
299
+ **never undoes the land and never blocks teardown** (step 7). The land is correct; catching up is the operator's.
300
+ No section, nothing to run, and nothing to say about it.
301
+ 4. **Re-test on base.** Run the project's test command from the primary checkout;
302
+ it must be **green** before you call the landing done.
303
+ 5. **Report** the landing (base branch, fast-forward result). It **never pushes** —
304
+ mention the user can `git push` the base branch themselves.
305
+
306
+ ## 7. Tear down the environment (opt-in, only if configured)
307
+
308
+ **A hotfix is torn down later, by `hotfix finish`.**
309
+ It is never torn down here on the run that landed it. Skip sub-steps 0–3 for a `Type: Hotfix` spec that step 6 just landed,
310
+ and say the worktree is kept until the deploy is confirmed. When the operator runs
311
+ `/spec-complete` **again** on that hotfix — it is already in `specs/complete/` and
312
+ already landed — this step is the whole run: `cd` to the primary checkout, then
313
+
314
+ ```
315
+ skitterspec spec-env hotfix finish <name>
316
+ ```
317
+
318
+ It tears down only when the tree is clean, a release tag is at HEAD and the branch
319
+ is on `origin` at HEAD, naming every check that fails; there is no `--force`.
320
+ Relay a refusal and stop. Report what it removed in `Worktree`.
321
+
322
+
323
+ **Only when `specs/.core/env.config.json` exists.** Reclaiming the environment is
324
+ what completing a spec *is*, so sub-steps 1–3 run **automatically — do not ask**.
325
+ Run the `spec-env` CLI directly (the old `/spec-env-down` skill is gone —
326
+ teardown is folded in here).
327
+
328
+ **The precondition is that the work actually landed.** Only tear down when step 6
329
+ completed: it landed (or reported "already landed") **and** the base suite came
330
+ back green. A failed after-land command is **not** a reason to stop here — the
331
+ land stands, and the command is the operator's to re-run from the primary
332
+ checkout, which teardown does not touch. A rebase conflict, a work-loss abort or a red suite means step 6 told
333
+ you to stop — tear nothing down, because the worktree is where the user picks the
334
+ problem up. That precondition is what makes a confirmation redundant: by the time
335
+ you get here the branch is an ancestor of base and the engine's guards have
336
+ nothing left to protect.
337
+
338
+ **Opt-out:** if the user passed **`--keep-env`**, skip sub-steps 1–3, say the
339
+ worktree and branch are being kept, and go straight to sub-step 4. Mention
340
+ `skitterspec spec-env up <name>` re-attaches it later either way.
341
+
342
+ 0. **Name a pass still waiting for THIS spec — before anything is removed.**
343
+
344
+ ```
345
+ skitterspec spec-env review waiting --json
346
+ ```
347
+
348
+ Filter to the spec being completed and say nothing about any other: a run
349
+ reports itself and nothing else (`.claude/rules/spec-reports.md`). For each
350
+ one, give the code, the verdict and the age, then the two exits — claim it
351
+ now with `/spec-reviewed <code>` **while the worktree still stands**, or
352
+ disown it with `skitterspec spec-env review <spec> --drop <code>`.
353
+
354
+ **Why here and not anywhere else.** Nothing is about to become unreachable —
355
+ a pass can be disowned long after its spec is gone. What is about to pass is
356
+ the last moment the verdict can still be **honoured**: before teardown a
357
+ `commit` pass can be claimed and acted on, after it there is no branch left
358
+ to commit to and disowning is all that remains.
359
+
360
+ **It reports, and it never blocks.** No refusal, no confirmation of its own,
361
+ no non-zero exit. And **it never claims** — reporting a pass is not taking
362
+ one, and `/spec-diff` §0 is untouched by this step.
363
+
364
+ Put what you found in the report's `Notes` row.
365
+ Silent when nothing is waiting, which is the usual case.
366
+
367
+ 1. **Disconnect the proxy if this spec is connected.** If `.spec-env/connected`
368
+ names this spec, run `skitterspec spec-env connect main` first so the
369
+ canonical ports go back to the primary checkout.
370
+ 2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
371
+ no-op when none are running / configured).
372
+ **Standing in the worktree? Leave it before you tear it down.** If this session's
373
+ cwd is inside the spec's own worktree — the normal case in `worktree` mode, since
374
+ `/spec-start` moves you there — get out **first**, then run the teardown commands.
375
+ One instruction covers it:
376
+
377
+ ```
378
+ cd <primary checkout>
379
+ ```
380
+
381
+ That is the whole mechanism, and it does not matter how you got in: the session
382
+ was moved by a `cd` and it leaves by one. There is no tool to call here, and none
383
+ should be reached for — the move in is a plain `cd` precisely because a tool that
384
+ asks for approval is unusable on a phone.
385
+
386
+ **`spec-env down` stays the single thing that deletes a worktree.** The plan
387
+ below is the only deleter, because a second one is how the teardown guards get
388
+ bypassed.
389
+
390
+ Not because git refuses — it does not. `git worktree remove` **succeeds** on the
391
+ tree you are standing in, and that is the problem: the directory vanishes under
392
+ the shell, `pwd` keeps reporting the path that no longer exists, and every
393
+ command after it dies with `fatal: Unable to read current working directory`.
394
+ The teardown looks fine and everything following it breaks — the report, the
395
+ prune, any check you meant to run. Relocating first costs nothing and is the
396
+ only ordering that survives.
397
+
398
+ 3. **Remove worktree + stack + slot:** run `skitterspec spec-env down <name>`
399
+ and execute the commands it prints, in order. After a landing — merged into
400
+ base for a **feature/bug** (a hotfix never reaches this sub-step; see the top
401
+ of step 7) —
402
+ teardown needs **no `--force`** and drops the branch with `git branch -D`,
403
+ which is safe precisely because the commits are already somewhere else. It
404
+ still respects the guards (won't destroy a dirty, or unpushed-and-unlanded,
405
+ worktree without `--force`), so if it *does* refuse, relay that and stop
406
+ rather than reaching for `--force`.
407
+
408
+ **The unpushed half of that guard cannot fire here, by construction.**
409
+ Step 6 landed the branch, so `merged` is true and `planDown` skips the check
410
+ entirely (`env/teardown.js`) — completing a spec never meets it, however the
411
+ branch was or was not published. `/spec-cancel` is where it does fire, because
412
+ a cancelled spec is unlanded and its worktree really is the only copy; the
413
+ publish-or-`--force` path therefore lives in that skill and this one needs no
414
+ equivalent. A refusal reaching this step is about a **dirty tree** instead,
415
+ which means something wrote into the worktree after the landing — read it,
416
+ don't force it.
417
+
418
+ **If the plan prints a `remote branch — confirm with the user first:` section, ask before running it.**
419
+ Nothing published this branch for you, so a remote copy exists only because
420
+ you pushed it by hand — and it outlives teardown unless someone deletes it. The planner only ever offers
421
+ this for a branch that has **landed**, so say so plainly when you ask — the
422
+ commits are on the base branch (or captured by the deploy tag), so deleting
423
+ the remote branch loses nothing. On a yes, run the printed
424
+ `git push <remote> --delete <branch>`; on a no, leave it and say the remote
425
+ branch is still there. Never fold it into the `run these:` batch — a project
426
+ that wants it unattended sets `teardown.deleteRemoteBranch: "always"` in
427
+ `env.config.json`, and then it appears in `run these:` instead of here.
428
+ 4. **Reap orphaned test-DB volumes:** run `skitterspec spec-env prune`. It lists
429
+ Docker volumes in the repo namespace that belong to **no live spec** (no
430
+ worktree) — leftovers from declined/aborted teardowns, manual
431
+ `git worktree remove`, or `--keep-volumes`. Show the user the orphan list and,
432
+ **only on their confirmation**, execute the printed `docker volume rm`
433
+ commands. **This one still asks**, unlike 1–3: it reaps volumes belonging to
434
+ *other* specs, and this spec having landed cleanly says nothing about those.
435
+ Non-fatal: if prune can't run (Docker down) or the user declines, report it and
436
+ finish completing anyway — never block the spec on it. Skip when Docker isn't
437
+ in use (the command self-reports "no orphaned volumes").
438
+
439
+ **Report from where you now are.** After a teardown you relocated for, the
440
+ worktree path in the report is a directory that no longer exists and the landed
441
+ work is on the base branch in the primary checkout — say both, so nobody goes
442
+ looking for a tree that is gone. A session that was a worktree tab has finished
443
+ its job at that point; its shell is the operator's own to close.
444
+
445
+ **Say what you reclaimed.** With no confirmation step the user never saw this
446
+ coming, so the final report must name the worktree path removed and the branch
447
+ deleted (or, under `--keep-env`, that both were kept). A teardown nobody
448
+ authorised and nobody was told about is the one way this step can lose someone's
449
+ place.
450
+
451
+ If `env.config.json` is absent, skip this entirely — behave exactly as before.