@skitterbyte/skitterspec-linear 10.8.0 → 12.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 (40) hide show
  1. package/MIGRATION.md +88 -0
  2. package/README.md +3 -3
  3. package/assets/claude-md-section.md +20 -48
  4. package/assets/core/SETUP.md +1 -1
  5. package/assets/core/env.config.json.example +7 -1
  6. package/assets/core/env.config.md +56 -11
  7. package/assets/core/gating.config.json.example +4 -0
  8. package/assets/core/gating.config.md +81 -0
  9. package/assets/core/linear.config.md +12 -11
  10. package/assets/rules/spec-planning.md +53 -13
  11. package/assets/skills/spec/SKILL.md +46 -18
  12. package/assets/skills/spec-bug/SKILL.md +32 -27
  13. package/assets/skills/spec-cancel/SKILL.md +26 -0
  14. package/assets/skills/spec-complete/SKILL.md +70 -17
  15. package/assets/skills/spec-hotfix/SKILL.md +40 -19
  16. package/assets/skills/spec-init/SKILL.md +31 -8
  17. package/assets/skills/spec-linear-setup/SKILL.md +32 -7
  18. package/assets/skills/spec-next/SKILL.md +141 -0
  19. package/assets/skills/spec-push/SKILL.md +15 -16
  20. package/assets/skills/spec-review/SKILL.md +24 -11
  21. package/assets/skills/spec-start/SKILL.md +226 -0
  22. package/assets/skills/spec-status/SKILL.md +2 -2
  23. package/assets/skills/spec-sync/SKILL.md +8 -8
  24. package/assets/skills/spec-to-main/SKILL.md +21 -19
  25. package/package.json +1 -1
  26. package/src/cli.js +405 -15
  27. package/src/env/classify.js +91 -0
  28. package/src/env/config.js +39 -1
  29. package/src/env/integrate.js +61 -1
  30. package/src/env/live.js +27 -3
  31. package/src/env/provision.js +196 -5
  32. package/src/env/resolve.js +1 -0
  33. package/src/env/teardown.js +41 -3
  34. package/src/gating.js +155 -0
  35. package/src/init.js +45 -9
  36. package/src/prompts.js +41 -4
  37. package/src/vendor/linear/cli-sync.js +22 -2
  38. package/src/vendor/linear/config.js +1 -1
  39. package/src/vendor/sync-core/src/compare.js +25 -2
  40. package/assets/skills/spec-go/SKILL.md +0 -233
@@ -6,10 +6,10 @@ description: Create a new spec-driven-development spec. Grills the user to a cle
6
6
  # /spec — author a new spec
7
7
 
8
8
  Produce ONE concise spec in `specs/backlog/`. Do not start coding — this skill
9
- plans only. Implementation happens later via `/spec-go`.
9
+ plans only. Implementation happens later via `/spec-start`.
10
10
 
11
11
  Lifecycle (the governing skills) — status in parentheses:
12
- `/spec` (writes **Ready** when fully groomed, else Draft; backlog) → `/spec-go`
12
+ `/spec` (writes **Ready** when fully groomed, else Draft; backlog) → `/spec-start`
13
13
  (In Progress, in-progress; implement phase 1) → `/spec-complete` (Complete) /
14
14
  `/spec-cancel` (Cancelled). See `.claude/rules/spec-planning.md`. (There is no
15
15
  separate grooming command — `/spec` grills to a Ready spec directly.)
@@ -91,7 +91,7 @@ it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step
91
91
 
92
92
  - **Stamp `linear_identifier` and `linear_url`** in `00-overview.md` frontmatter
93
93
  from the adopted issue. That is the whole link: every later skill
94
- (`/spec-push`, `/spec-status`, `/spec-go`) keys off it being present.
94
+ (`/spec-push`, `/spec-status`, `/spec-start`) keys off it being present.
95
95
  - **Do not run the project picker** and never send `project`. The issue was filed
96
96
  somewhere deliberately — where it lives is Linear's business, and adoption is
97
97
  not a mint.
@@ -113,7 +113,12 @@ not write the spec until this is resolved.
113
113
 
114
114
  - Break the problem into **distinctive areas** and work them in logical order,
115
115
  resolving dependencies between decisions one at a time.
116
- - Ask **one question at a time**. For each, give your **recommended answer**.
116
+ - **Batch independent questions; sequence dependent ones.** When several
117
+ questions do not affect each other's answers, put them to the user together
118
+ (up to four at once — use the harness's multi-question ask tool where it has
119
+ one) rather than spending a round trip on each. When an answer would change
120
+ what you ask next, ask that one alone and wait for it. Give your
121
+ **recommended answer** either way.
117
122
  - If a question can be answered by **reading the codebase, read it** instead of
118
123
  asking. Verify endpoints/models/files actually exist before relying on them.
119
124
  - Cover, at minimum, the areas that apply:
@@ -132,9 +137,19 @@ not write the spec until this is resolved.
132
137
  this spec touch the DB / stateful services (so its worktree needs a Docker
133
138
  stack), or is a plain worktree enough? Default `worktree`; escalate to
134
139
  `worktree + docker` only when it must. This sets the `> **Stack:**` header
135
- that `/spec-go` acts on (it can be escalated later). Skip when isolation
140
+ that `/spec-start` acts on (it can be escalated later). Skip when isolation
136
141
  isn't enabled — leave the default `worktree`.
137
- 10. **Open questions** anything still undecided.
142
+ 10. **Release gating** *(only when `specs/.core/gating.config.json` exists)* —
143
+ should this ship behind a feature flag, or land live?
144
+ **Offer, don't impose**: the user decides and you raise it, so a spec
145
+ never reaches
146
+ `/spec-complete` with the question unasked. Cite the project's own
147
+ `guidance` path from that config when it names one — skitterspec knows
148
+ nothing about how this project does flags, and must not guess. Record the
149
+ answer **either way**: a flag name, or `none: <one-line reason>`. "No" is a
150
+ decision and belongs in the header; silence is not. Skip entirely when the
151
+ config is absent — that project does not use flags.
152
+ 11. **Open questions** — anything still undecided.
138
153
 
139
154
  Stop grilling when there are no unresolved branches that would change the spec.
140
155
  Briefly play back the agreed understanding before writing.
@@ -185,7 +200,7 @@ the codebase, link rather than duplicate):
185
200
  # <Feature title>
186
201
 
187
202
  > **Type:** Feature
188
- > **Name:** feat-<kebab-name> (the spec folder name — the handle you paste into `/spec-go`)
203
+ > **Name:** feat-<kebab-name> (the spec folder name — the handle you paste into `/spec-start`)
189
204
  > **Status:** Ready — not started
190
205
  > **Author:** <git user.name — `git config user.name`>
191
206
  > **Developer:** —
@@ -193,6 +208,9 @@ the codebase, link rather than duplicate):
193
208
  > **Area:** <comma-separated files/modules this touches>
194
209
  > **Stack:** <worktree — or "worktree + docker" if it touches the DB/stateful
195
210
  > services; only acted on when isolation is enabled — see Phase A item 9>
211
+ > **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
212
+ > is configured; omit the line entirely otherwise. An empty value or a bare
213
+ > "none" is not a valid outcome — see Phase A item 10>
196
214
 
197
215
  ## Problem
198
216
 
@@ -281,7 +299,7 @@ Keep the `00-overview.md` phase index and the phase files in sync: the index row
281
299
  is the one-line summary + status; the phase file is the detail.
282
300
 
283
301
  The **State log** is the audit trail of folder/status transitions — every
284
- lifecycle skill (`/spec-go`, `/spec-complete`, `/spec-cancel`) appends one row
302
+ lifecycle skill (`/spec-start`, `/spec-complete`, `/spec-cancel`) appends one row
285
303
  when it moves the spec. The **Changelog** is for decisions and course-corrections
286
304
  only — keep the two separate.
287
305
 
@@ -296,13 +314,14 @@ Rules for the spec body:
296
314
  phase index.
297
315
  - **Honour project conventions** when writing tasks — reference the relevant
298
316
  `.claude/rules/*.md` rather than re-explaining them.
299
- - **The `## Impact` table is derived from Phase A items 3 (Affected areas) & 5
300
- (Data/API impact)** a structured place to record what those already surface,
301
- not new grilling. It is the scannable substitute for spelling impact out in
302
- prose: name the surfaces (endpoints, schemas, DB tables, domain objects,
303
- routes, business rules) instead of describing them, keep `Detail` terse, and
304
- let it not paragraphs carry the blast radius. It complements the `Area:`
305
- header (files) by naming behavioural surfaces.
317
+ -
318
+ **The `## Impact` table is derived from Phase A items 3 (Affected areas) & 5 (Data/API impact)**
319
+ a structured place to record what those already surface, not new grilling.
320
+ It is the scannable substitute for spelling impact out in prose: name the
321
+ surfaces (endpoints, schemas, DB tables, domain objects, routes, business
322
+ rules) instead of describing them, keep `Detail` terse, and let it not
323
+ paragraphs carry the blast radius. It complements the `Area:` header
324
+ (files) by naming behavioural surfaces.
306
325
  - **Changelog** is mandatory and lives in the spec. Every later decision or
307
326
  course-correction gets a dated one-line entry. Convert relative dates to
308
327
  absolute.
@@ -313,20 +332,29 @@ Rules for the spec body:
313
332
  After writing, tell the user the path and that it's **`Ready`** in `backlog`
314
333
  (grilling in Phase A resolved the open questions). If you deliberately left open
315
334
  questions unresolved, write `Draft` instead and say what still needs deciding.
316
- Either way, the next step is `/spec-go` to start building.
335
+ Either way, the next step is `/spec-start` to start building.
317
336
 
318
337
  ## Phase D — record the isolation stack (only if configured)
319
338
 
320
339
  **Only when `specs/.core/env.config.json` exists** (per-spec isolation is
321
340
  enabled), make sure the `> **Stack:**` header reflects the Phase A item 9
322
341
  decision — `worktree` (default) or `worktree + docker` when it touches the DB /
323
- stateful services. Nothing to provision now: `/spec-go` gives every in-progress
342
+ stateful services. Nothing to provision now: `/spec-start` gives every in-progress
324
343
  spec its own worktree automatically, and brings up Docker only when the Stack
325
344
  says so. Mention the operator can escalate the Stack later (edit the header, or
326
345
  run `skitterspec spec-env up <name>` to add Docker to an existing worktree). If
327
346
  `env.config.json` is absent, isolation is off — leave the default `worktree` and
328
347
  finish as above.
329
348
 
349
+ ## Phase D2 — record the gating decision (only if configured)
350
+
351
+ **Only when `specs/.core/gating.config.json` exists.** Make sure the
352
+ `> **Gating:**` header carries the Phase A item 10 answer — a flag name, or
353
+ `none: <reason>` using the config's `default` wording if it sets one. Nothing is
354
+ provisioned or enforced by this: the header exists so the decision is
355
+ **on the record and reviewable**, and `skitterspec gating check` reports a spec that has
356
+ none. It never blocks. If the config is absent, do not write the line at all.
357
+
330
358
  ## Phase E — link to a ticketing provider (only if one is installed)
331
359
 
332
360
  **Only when a ticketing provider is installed and configured** (it ships the
@@ -370,7 +398,7 @@ does — you never create the issue by hand:
370
398
  `spec-sync record`. That path is fully supported — it is what anyone without
371
399
  an API key uses.
372
400
  5. **Echo the branch name** from `branch.pattern` so the user knows what
373
- `/spec-go` will fork.
401
+ `/spec-start` will fork.
374
402
 
375
403
  **If Linear can't be reached**, say so in one line and leave the spec written and
376
404
  local — it is still a perfectly good spec, and `/spec-push` links it later. Do
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec-bug
3
- description: Investigate a bug, capture it as a Bug-type spec, and drive it red→green. When per-spec isolation is enabled it provisions a worktree first, so the failing test and fix land on the bug's own branch, never on main. ALWAYS starts by reproducing the bug with a failing test, then writes the spec and works the test to green. Creates specs/in-progress/bug-<name>/00-overview.md. Use when the user reports a bug, says "/spec-bug", "investigate this bug", "this is broken — find and fix it", or pastes an error/stack trace.
3
+ description: Investigate a bug, capture it as a Bug-type spec, and drive it red→green. ALWAYS starts by reproducing the bug with a failing test, then writes the spec and works the test to green on the bug's own branch, never on main. Use when the user reports a bug, says "/spec-bug", "investigate this bug", "this is broken — find and fix it", or pastes an error/stack trace.
4
4
  ---
5
5
 
6
6
  # /spec-bug — investigate a bug, prove it with a failing test, fix it
@@ -90,7 +90,7 @@ it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step
90
90
 
91
91
  - **Stamp `linear_identifier` and `linear_url`** in `00-overview.md` frontmatter
92
92
  from the adopted issue. That is the whole link: every later skill
93
- (`/spec-push`, `/spec-status`, `/spec-go`) keys off it being present.
93
+ (`/spec-push`, `/spec-status`, `/spec-start`) keys off it being present.
94
94
  - **Do not run the project picker** and never send `project`. The issue was filed
95
95
  somewhere deliberately — where it lives is Linear's business, and adoption is
96
96
  not a mint.
@@ -127,7 +127,7 @@ place), skip this whole section and fix on the current branch — same as when
127
127
  isolation is off. Warn that the fix will land wherever you currently are (usually
128
128
  `main`); reserve it for a trivial one-liner or an explicit request.
129
129
 
130
- A bug fix changes real source, so — exactly like `/spec-go` — it belongs on the
130
+ A bug fix changes real source, so — exactly like `/spec-start` — it belongs on the
131
131
  bug's **own branch**, never directly on `main`. Provision the worktree **now**,
132
132
  before the failing test, so the test, the fix, and the spec all land together and
133
133
  arrive as one reviewable PR.
@@ -141,24 +141,12 @@ provision from — you'll flesh it out in §4:
141
141
  - Run `skitterspec spec-env up bug-<name>` (the `spec-env` CLI engine). It prints
142
142
  the `git worktree add … -b bug/<name>` command (a branch forked from `main`),
143
143
  the worktree path, the opener, and any `in the worktree, run:` bootstrap steps.
144
- - Run the printed `git worktree add`. **The worktree forks from `main`'s last
145
- commit, so your uncommitted stub doesn't travel with it**move it across so
146
- `main` is left pristine. **Create the destination bucket first:**
147
-
148
- ```
149
- mkdir -p <worktreePath>/specs/in-progress
150
- mv specs/in-progress/bug-<name> <worktreePath>/specs/in-progress/
151
- ```
152
-
153
- The `mkdir -p` is not belt-and-braces. Git does not store empty directories,
154
- so `specs/in-progress/` is **absent** from a fresh worktree whenever that
155
- bucket happens to be empty on `main` — the common case, since it empties every
156
- time the last in-progress spec completes. `mv` into a missing destination
157
- renames your spec folder **to** `specs/in-progress`, silently: the spec's files
158
- end up one level too high, `00-overview.md` sits where the bucket should be,
159
- and every later step still appears to work until something cannot find the
160
- spec. Confirm the result before carrying on — you want
161
- `<worktreePath>/specs/in-progress/bug-<name>/00-overview.md`.
144
+ - Run the printed commands in order. The plan **commits the stub first** — the
145
+ worktree forks from `main`'s last commit, so the stub has to be in it — and
146
+ then adds the worktree. Nothing to move afterwards: the spec is already there.
147
+ The commit is planned, not silent; it appears in the printed plan above the
148
+ `git worktree add`, and `spec-env up` refuses outright if anything *other* than
149
+ this spec is uncommitted.
162
150
  - **Bootstrap the worktree.** A fresh worktree has no installed dependencies and
163
151
  none of the repo's gitignored files (`.env`, local overrides). Run the printed
164
152
  `in the worktree, run:` steps (file seeding, then `setup`) in order, before
@@ -201,12 +189,14 @@ because work is already underway. Keep it lean:
201
189
  # Bug: <short title>
202
190
 
203
191
  > **Type:** Bug
204
- > **Name:** bug-<kebab-name> (the spec folder name — the handle you paste into `/spec-go`)
192
+ > **Name:** bug-<kebab-name> (the spec folder name — the handle you paste into `/spec-start`)
205
193
  > **Status:** In Progress — fixing (red test added)
206
194
  > **Author:** <git user.name — who reported/captured it>
207
195
  > **Developer:** <git user.name — you, since you're fixing it now>
208
196
  > **Raised:** <YYYY-MM-DD (today)>
209
197
  > **Area:** <files/modules>
198
+ > **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
199
+ > is configured; omit the line entirely otherwise>
210
200
 
211
201
  ## Symptom
212
202
 
@@ -237,8 +227,10 @@ Service, CLI command, Config key, Skill/rule, Business rule — or whatever fits
237
227
  this project (skitterspec itself is a CLI with no HTTP surface). Keep `Detail`
238
228
  terse — names/signatures, not sentences. List **only** surfaces that actually
239
229
  change; the heading is always present, but if nothing external changes write the
240
- single line below instead of an empty table. A bug fix often changes no external
241
- surface — that's fine, use the one-liner.>
230
+ single line below instead of an empty table.>
231
+
232
+ <A bug fix often changes no external surface — that's fine, use the
233
+ one-liner.>
242
234
 
243
235
  | Surface | Change | Detail |
244
236
  |---------|--------|--------|
@@ -295,7 +287,7 @@ does — you never create the issue by hand:
295
287
  `spec-sync record`. That path is fully supported — it is what anyone without
296
288
  an API key uses.
297
289
  5. **Echo the branch name** from `branch.pattern` so the user knows what
298
- `/spec-go` will fork.
290
+ `/spec-start` will fork.
299
291
 
300
292
  **If Linear can't be reached**, say so in one line and leave the spec written and
301
293
  local — it is still a perfectly good spec, and `/spec-push` links it later. Do
@@ -344,6 +336,19 @@ say so in one line — *"project picker unavailable"* — and carry on with
344
336
  If `linear.projectId` is set but that Project is archived or missing, relay
345
337
  Linear's error and stop rather than silently minting an unparented issue.
346
338
 
339
+ ### Release gating (only when configured)
340
+
341
+ **Only when `specs/.core/gating.config.json` exists.** Before writing the spec,
342
+ ask: should this fix ship behind a feature flag, or land live?
343
+ **Offer, don't impose** — cite the project's own `guidance` path when the config
344
+ names one, and
345
+ record the answer either way, as a flag name or `none: <one-line reason>`.
346
+
347
+ A bug fix ships in the next release exactly like a feature, so it gets the same
348
+ question rather than an assumption. A risky rewrite of a broken path is precisely
349
+ where a kill-switch earns its keep; a one-line null check is precisely where
350
+ `none:` is the honest answer. Skip entirely when the config is absent.
351
+
347
352
  ## 5. Drive to GREEN
348
353
 
349
354
  - Implement the **minimal, root-cause** fix. Match surrounding code; honour all
@@ -354,12 +359,12 @@ Linear's error and stop rather than silently minting an unparented issue.
354
359
 
355
360
  If the root cause is large/uncertain and can't be fixed in one pass: keep the red
356
361
  test, split the fix into phase files (`01-<slug>.md` …) with a phase index in
357
- `00-overview.md`, and leave the spec in `in-progress` for `/spec-go` to continue.
362
+ `00-overview.md`, and leave the spec in `in-progress` for `/spec-next` to continue.
358
363
  Say so explicitly — don't fake green.
359
364
 
360
365
  **Then refresh the mirror (only if a provider is installed).** The Fix tasks are
361
366
  ticked, so the repo is now the truth about this fix — and this skill can take a
362
- bug all the way to green without `/spec-go` ever running. Without a provider this
367
+ bug all the way to green without `/spec-next` ever running. Without a provider this
363
368
  is a no-op.
364
369
 
365
370
  **Only when `specs/.core/linear.config.json` exists** and the spec carries a
@@ -110,6 +110,32 @@ directly (the old `/spec-env-down` skill is gone — teardown is folded in here)
110
110
  1. If `.spec-env/connected` names this spec, run `skitterspec spec-env connect
111
111
  main` first to free the canonical ports.
112
112
  2. `skitterspec spec-env dev down <name>` — stop its host dev servers.
113
+ **Standing in the worktree? Leave it before you tear it down.** If this
114
+ session's cwd is inside the spec's own worktree, get out **first**, then run the
115
+ teardown commands. How you leave depends on how you got in:
116
+
117
+ - **`/spec-start` moved this session in** — the normal path in `worktree` mode.
118
+ Call **`ExitWorktree`** with `action: "keep"`. It restores the session to the
119
+ directory it started from *and* clears the caches still pointing at the
120
+ worktree, which a bare `cd` does not: after a `cd` the session stays registered
121
+ against the tree and asks you about it again when it ends.
122
+ - **You opened the terminal yourself** — `cd` to the primary checkout, as always.
123
+ `ExitWorktree` is a no-op outside a session it moved, so trying it costs
124
+ nothing, but the `cd` is what relocates you.
125
+
126
+ **Always `keep`, never `remove`.** `ExitWorktree` refuses to remove a worktree
127
+ entered by path in any case, and the `spec-env down` plan below has to stay the
128
+ single thing that deletes — a second deleter is how the teardown guards get
129
+ bypassed.
130
+
131
+ Not because git refuses — it does not. `git worktree remove` **succeeds** on the
132
+ tree you are standing in, and that is the problem: the directory vanishes under
133
+ the shell, `pwd` keeps reporting the path that no longer exists, and every
134
+ command after it dies with `fatal: Unable to read current working directory`.
135
+ The teardown looks fine and everything following it breaks — the report, the
136
+ prune, any check you meant to run. Relocating first costs nothing and is the
137
+ only ordering that survives.
138
+
113
139
  3. `skitterspec spec-env down <name>` — then execute the printed commands to
114
140
  remove the worktree/stack and free the slot. It respects the teardown guards
115
141
  (won't destroy a dirty/unpushed worktree without `--force`).
@@ -25,7 +25,7 @@ Before marking complete, confirm the work is actually finished:
25
25
  originally-failing test named in the spec now passes — that test is the proof
26
26
  the fix works.
27
27
  - If genuinely incomplete work remains, **stop and tell the user** rather than
28
- forcing completion. Offer to finish it (`/spec-go`) or to complete with the
28
+ forcing completion. Offer to finish it (`/spec-next`) or to complete with the
29
29
  remaining items explicitly listed as deferred.
30
30
  - **Check for pre-existing uncommitted changes — before you touch anything.**
31
31
  Run `git status`. Anything already uncommitted is the *user's* work (a
@@ -34,6 +34,23 @@ Before marking complete, confirm the work is actually finished:
34
34
  is its own, and step 4 commits that.
35
35
 
36
36
 
37
+ ### Release gating (only when configured)
38
+
39
+ **Only when `specs/.core/gating.config.json` exists.** Run
40
+ `skitterspec gating check <spec>` and report what it says. This is the last point
41
+ at which the question can still be answered cheaply, which is exactly why it is
42
+ asked here — a spec that reaches `Complete` with no decision recorded is the gap
43
+ the feature exists to close.
44
+
45
+ **Never refuse to complete over it.** Say plainly that no decision was recorded,
46
+ offer to record one now, and finish either way. The work is done; withholding the
47
+ status flip would punish the wrong thing.
48
+
49
+ **This check is advisory.** It reports; it never refuses, and nothing below is
50
+ conditional on it. A spec written before the project adopted gating has no header
51
+ and is not broken — turning this into a gate would accuse the very specs the
52
+ feature was designed not to disturb.
53
+
37
54
  ## 3. Update the spec
38
55
 
39
56
  - Tick all completed tasks in the phase files; flip every finished phase-file
@@ -95,9 +112,10 @@ git add specs/ && git commit -m "chore(spec): complete <name>"
95
112
  ```
96
113
 
97
114
  Step 2 established the tree was otherwise clean, so this commits exactly the
98
- status flip and the move — nothing of yours rides along. **This is what lets step
99
- 6 land:** `integrate` refuses a dirty worktree, so without committing here the
100
- skill would block on the very edits it just made. Do not `git push`.
115
+ status flip and the move — nothing of yours rides along.
116
+ **This is what lets step 6 land:** `integrate` refuses a dirty worktree, so
117
+ without committing here the skill would block on the very edits it just made.
118
+ Do not `git push`.
101
119
 
102
120
  ## 5. Report
103
121
 
@@ -105,8 +123,11 @@ Confirm the move, the commit, the final test result, and list anything deferred.
105
123
 
106
124
  ## 6. Land the branch (opt-in, only if isolated)
107
125
 
108
- **Only when `specs/.core/env.config.json` exists and the spec is on a worktree**
109
- (it was provisioned by `/spec-go` or `/spec-hotfix`). Otherwise skip this entirely
126
+ **Only when `specs/.core/env.config.json` exists and the spec was provisioned**
127
+ (a worktree in `worktree` mode, or its branch in the checkout in `checkout`
128
+ mode — `spec-env integrate` and `down` pick the right plan from `mode`, so the
129
+ commands you run differ but these steps do not)
130
+ (it was provisioned by `/spec-start` or `/spec-hotfix`). Otherwise skip this entirely
110
131
  — a non-isolated spec has nothing to land, and `/spec-complete` behaves exactly as
111
132
  before. When it applies, offer to land the finished branch so the work reaches its
112
133
  destination in one flow. **How it lands depends on the spec type:**
@@ -157,7 +178,7 @@ is finished — e.g. to run a later phase in CI or a shared test env? Use
157
178
  work is actually landable and **aborts loudly** rather than finalize a spec
158
179
  having landed nothing. Two cases, both leaving the live session intact:
159
180
  - *stranded commits* — commits sit on the worktree's **detached HEAD** (e.g. a
160
- pre-fix `/spec-go` committed there instead of on the branch). It prints the
181
+ pre-fix `/spec-next` committed there instead of on the branch). It prints the
161
182
  count, the sha, and a `git -C <worktree> branch <tmp> <sha>` recovery hint —
162
183
  recover those commits onto the branch, then re-run.
163
184
  - *no worktree* — the spec is live but its worktree is gone. Re-isolate it with
@@ -200,6 +221,32 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
200
221
  canonical ports go back to the primary checkout.
201
222
  2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
202
223
  no-op when none are running / configured).
224
+ **Standing in the worktree? Leave it before you tear it down.** If this
225
+ session's cwd is inside the spec's own worktree, get out **first**, then run the
226
+ teardown commands. How you leave depends on how you got in:
227
+
228
+ - **`/spec-start` moved this session in** — the normal path in `worktree` mode.
229
+ Call **`ExitWorktree`** with `action: "keep"`. It restores the session to the
230
+ directory it started from *and* clears the caches still pointing at the
231
+ worktree, which a bare `cd` does not: after a `cd` the session stays registered
232
+ against the tree and asks you about it again when it ends.
233
+ - **You opened the terminal yourself** — `cd` to the primary checkout, as always.
234
+ `ExitWorktree` is a no-op outside a session it moved, so trying it costs
235
+ nothing, but the `cd` is what relocates you.
236
+
237
+ **Always `keep`, never `remove`.** `ExitWorktree` refuses to remove a worktree
238
+ entered by path in any case, and the `spec-env down` plan below has to stay the
239
+ single thing that deletes — a second deleter is how the teardown guards get
240
+ bypassed.
241
+
242
+ Not because git refuses — it does not. `git worktree remove` **succeeds** on the
243
+ tree you are standing in, and that is the problem: the directory vanishes under
244
+ the shell, `pwd` keeps reporting the path that no longer exists, and every
245
+ command after it dies with `fatal: Unable to read current working directory`.
246
+ The teardown looks fine and everything following it breaks — the report, the
247
+ prune, any check you meant to run. Relocating first costs nothing and is the
248
+ only ordering that survives.
249
+
203
250
  3. **Remove worktree + stack + slot:** run `skitterspec spec-env down <name>`
204
251
  and execute the commands it prints, in order. After a landing — merged into
205
252
  base for a **feature/bug**, captured by the deploy tag for a **hotfix** —
@@ -209,16 +256,16 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
209
256
  worktree without `--force`), so if it *does* refuse, relay that and stop
210
257
  rather than reaching for `--force`.
211
258
 
212
- **If the plan prints a `remote branch — confirm with the user first:`
213
- section, ask before running it.** `/spec-go` pushed this branch when it
214
- provisioned, so the remote copy outlives teardown unless someone deletes it.
215
- The planner only ever offers this for a branch that has **landed**, so say so
216
- plainly when you ask — the commits are on the base branch (or captured by the
217
- deploy tag), so deleting the remote branch loses nothing. On a yes, run the
218
- printed `git push <remote> --delete <branch>`; on a no, leave it and say the
219
- remote branch is still there. Never fold it into the `run these:` batch — a
220
- project that wants it unattended sets `teardown.deleteRemoteBranch: "always"`
221
- in `env.config.json`, and then it appears in `run these:` instead of here.
259
+ **If the plan prints a `remote branch — confirm with the user first:` section, ask before running it.**
260
+ `/spec-start` pushed this branch when it provisioned, so the remote copy
261
+ outlives teardown unless someone deletes it. The planner only ever offers
262
+ this for a branch that has **landed**, so say so plainly when you ask — the
263
+ commits are on the base branch (or captured by the deploy tag), so deleting
264
+ the remote branch loses nothing. On a yes, run the printed
265
+ `git push <remote> --delete <branch>`; on a no, leave it and say the remote
266
+ branch is still there. Never fold it into the `run these:` batch — a project
267
+ that wants it unattended sets `teardown.deleteRemoteBranch: "always"` in
268
+ `env.config.json`, and then it appears in `run these:` instead of here.
222
269
  4. **Reap orphaned test-DB volumes:** run `skitterspec spec-env prune`. It lists
223
270
  Docker volumes in the repo namespace that belong to **no live spec** (no
224
271
  worktree) — leftovers from declined/aborted teardowns, manual
@@ -230,6 +277,12 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
230
277
  finish completing anyway — never block the spec on it. Skip when Docker isn't
231
278
  in use (the command self-reports "no orphaned volumes").
232
279
 
280
+ **Report from where you now are.** After a teardown you relocated for, the
281
+ worktree path in the report is a directory that no longer exists and the landed
282
+ work is on the base branch in the primary checkout — say both, so nobody goes
283
+ looking for a tree that is gone. A session that was a worktree tab has finished
284
+ its job at that point; its shell is the operator's own to close.
285
+
233
286
  **Say what you reclaimed.** With no confirmation step the user never saw this
234
287
  coming, so the final report must name the worktree path removed and the branch
235
288
  deleted (or, under `--keep-env`, that both were kept). A teardown nobody
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec-hotfix
3
- description: Fix a production bug on the exact released version — fork a worktree from a release tag, drive it red→green like /spec-bug, then land it by tagging a new patch (for CI/CD to deploy) and cherry-picking the fix back onto main. ALWAYS starts from a base tag and works on the hotfix's own branch, never on main. Can start from a tracker issue when a provider is installed (`/spec-hotfix <tag> <ISSUE-REF>`), adopting it as the hotfix's issue. Creates specs/in-progress/hotfix-<name>/00-overview.md. Use when the user says "/spec-hotfix", "hotfix <tag>", "prod is broken on <version>", "patch the released version", or needs a fix shipped against a tagged release rather than main.
3
+ description: Fix a production bug on the exact released version — fork a worktree from a release tag, drive it red→green, then land it by tagging a new patch for CI/CD and cherry-picking the fix onto main. ALWAYS starts from a base tag and works on the hotfix's own branch, never on main. Use when the user says "/spec-hotfix", "hotfix <tag>", "prod is broken on <version>", "patch the released version", or needs a fix shipped against a tagged release rather than main.
4
4
  ---
5
5
 
6
6
  # /spec-hotfix — fix a released version, tag it, cherry-pick back to main
@@ -97,7 +97,7 @@ it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step
97
97
 
98
98
  - **Stamp `linear_identifier` and `linear_url`** in `00-overview.md` frontmatter
99
99
  from the adopted issue. That is the whole link: every later skill
100
- (`/spec-push`, `/spec-status`, `/spec-go`) keys off it being present.
100
+ (`/spec-push`, `/spec-status`, `/spec-start`) keys off it being present.
101
101
  - **Do not run the project picker** and never send `project`. The issue was filed
102
102
  somewhere deliberately — where it lives is Linear's business, and adoption is
103
103
  not a mint.
@@ -153,9 +153,12 @@ that header — must exist **before** `spec-env up`:
153
153
  -b hotfix/<slug> <tag>` command (the branch forks from **the tag**, not
154
154
  `main`), the worktree path, the opener, and any `in the worktree, run:`
155
155
  bootstrap steps.
156
- - Run the printed `git worktree add`. **The worktree is checked out at the tag,
157
- so your uncommitted stub doesn't travel with it** move it across so `main`
158
- stays pristine. **Create the destination bucket first:**
156
+ - Run the printed `git worktree add`, then **move the stub across yourself.**
157
+ This is where a hotfix differs from `/spec-bug`, which no longer needs the move:
158
+ that skill's worktree forks from `main`, so committing the stub puts it there,
159
+ while **this worktree is checked out at the tag** — a commit on `main` is not in
160
+ it and never will be. The move is not redundant here; keep it.
161
+ **Create the destination bucket first:**
159
162
 
160
163
  ```
161
164
  mkdir -p <worktreePath>/specs/in-progress
@@ -164,16 +167,15 @@ that header — must exist **before** `spec-env up`:
164
167
 
165
168
  The `mkdir -p` is not belt-and-braces. Git does not store empty directories, so
166
169
  `specs/in-progress/` is **absent** from the worktree whenever nothing was in
167
- progress at that point in history — and here that point is an **old release
168
- tag**, where it is absent more often than not. `mv` into a missing destination
169
- renames your spec folder **to** `specs/in-progress`, silently: the spec's files
170
- end up one level too high, `00-overview.md` sits where the bucket should be, and
171
- every later step still appears to work until something cannot find the spec.
172
- Confirm the result before carrying on — you want
173
- `<worktreePath>/specs/in-progress/hotfix-<name>/00-overview.md`.
174
- - **Bootstrap the worktree.** A fresh worktree has no installed dependencies and
170
+ progress at that point in history — and here that point is an
171
+ **old release tag**, where it is absent more often than not. `mv` into a
172
+ missing destination renames your spec folder **to** `specs/in-progress`,
173
+ silently: the spec's files end up one level too high, `00-overview.md` sits
174
+ where the bucket should be, and every later step still appears to work until
175
+ something cannot find the spec. Confirm the result before carrying on — you
176
+ want `<worktreePath>/specs/in-progress/hotfix-<name>/00-overview.md`. - **Bootstrap the worktree.** A fresh worktree has no installed dependencies and
175
177
  none of the repo's gitignored files (`.env`, local overrides). Run the printed
176
- `in the worktree, run:` steps (file seeding, then setup) in order, before
178
+ `in the worktree, run:` steps (file seeding, then `setup`) in order, before
177
179
  anything else.
178
180
  - **Trust the worktree for this session.** The engine wrote the printed
179
181
  `trusted:` root into `.claude/settings.local.json`, but it won't hot-reload now
@@ -206,13 +208,15 @@ is usually a single-pass fix, so the `## Fix` block can live directly in
206
208
  # Hotfix: <short title>
207
209
 
208
210
  > **Type:** Hotfix
209
- > **Name:** hotfix-<kebab-name> (the spec folder name — the handle you paste into `/spec-go`)
211
+ > **Name:** hotfix-<kebab-name> (the spec folder name — the handle you paste into `/spec-start`)
210
212
  > **Status:** In Progress — fixing (red test added)
211
213
  > **Author:** <git user.name — who reported/captured it>
212
214
  > **Developer:** <git user.name — you, since you're fixing it now>
213
215
  > **Base version:** <tag prod is running, e.g. v33.16.4>
214
216
  > **Raised:** <YYYY-MM-DD (today)>
215
217
  > **Area:** <files/modules>
218
+ > **Gating:** <pre-filled "none: hotfix — restoring released behaviour"; only
219
+ > when release gating is configured, and overridable — see below>
216
220
 
217
221
  ## Symptom
218
222
 
@@ -243,8 +247,10 @@ Service, CLI command, Config key, Skill/rule, Business rule — or whatever fits
243
247
  this project (skitterspec itself is a CLI with no HTTP surface). Keep `Detail`
244
248
  terse — names/signatures, not sentences. List **only** surfaces that actually
245
249
  change; the heading is always present, but if nothing external changes write the
246
- single line below instead of an empty table. A hotfix should be minimal — often
247
- no external surface changes; that's fine, use the one-liner.>
250
+ single line below instead of an empty table.>
251
+
252
+ <A hotfix should be minimal — often no external surface changes; that's
253
+ fine, use the one-liner.>
248
254
 
249
255
  | Surface | Change | Detail |
250
256
  |---------|--------|--------|
@@ -306,7 +312,7 @@ does — you never create the issue by hand:
306
312
  `spec-sync record`. That path is fully supported — it is what anyone without
307
313
  an API key uses.
308
314
  5. **Echo the branch name** from `branch.pattern` so the user knows what
309
- `/spec-go` will fork.
315
+ `/spec-start` will fork.
310
316
 
311
317
  **If Linear can't be reached**, say so in one line and leave the spec written and
312
318
  local — it is still a perfectly good spec, and `/spec-push` links it later. Do
@@ -355,6 +361,21 @@ say so in one line — *"project picker unavailable"* — and carry on with
355
361
  If `linear.projectId` is set but that Project is archived or missing, relay
356
362
  Linear's error and stop rather than silently minting an unparented issue.
357
363
 
364
+ ### Release gating (only when configured)
365
+
366
+ **Only when `specs/.core/gating.config.json` exists.** A hotfix is the one spec
367
+ type that does **not** ask the question cold: it writes
368
+ `none: hotfix — restoring released behaviour` and asks only for confirmation.
369
+
370
+ The default differs on purpose. A hotfix restores behaviour a release already
371
+ had, under time pressure, and the fix is captured by a deploy tag rather than
372
+ riding the next release — so a flag has nothing to gate and nothing to roll back
373
+ to. Making someone answer a design question mid-incident buys nothing.
374
+
375
+ It is a **default, not a rule**: say what you are writing and let the user
376
+ override it. If they name a flag, record that instead. Skip entirely when the
377
+ config is absent.
378
+
358
379
  ## 6. Drive to GREEN
359
380
 
360
381
  - Implement the **minimal, root-cause** fix on the branch. Match surrounding code;
@@ -366,7 +387,7 @@ Linear's error and stop rather than silently minting an unparented issue.
366
387
 
367
388
  **Then refresh the mirror (only if a provider is installed).** The Fix tasks are
368
389
  ticked, so the repo is now the truth about this fix — and this skill can take a
369
- bug all the way to green without `/spec-go` ever running. Without a provider this
390
+ bug all the way to green without `/spec-next` ever running. Without a provider this
370
391
  is a no-op.
371
392
 
372
393
  **Only when `specs/.core/linear.config.json` exists** and the spec carries a
@@ -22,10 +22,33 @@ clobber customised content. Finish with a summary of created vs already-present.
22
22
  > **Leave alone**. Non-interactively it only adds what's missing; `--resync` /
23
23
  > `--reset` (reset needs `--yes`) drive the stronger actions.
24
24
 
25
- The system is **eight skills**: `spec` (feature), `spec-bug` (bug), `spec-ready`,
26
- `spec-review`, `spec-go`, `spec-complete`, `spec-cancel`, and this `spec-init`. The lifecycle is
27
- `backlog in-progress → complete / cancelled`, with `.core` holding always-apply
28
- project rules.
25
+ The system is **ten skills**: `spec` (feature), `spec-bug` (bug),
26
+ `spec-hotfix` (a bug fixed on a released tag), `spec-review`, `spec-start`,
27
+ `spec-next`, `spec-to-main`, `spec-complete`, `spec-cancel`, and this
28
+ `spec-init`. The lifecycle is `backlog → in-progress → complete / cancelled`,
29
+ with `.core` holding always-apply project rules.
30
+
31
+ ## 0. Workspace mode (only when adopting isolation)
32
+
33
+ If this project is adopting per-spec isolation, ask which `mode` belongs in
34
+ `specs/.core/env.config.json` — `worktree` (default; a checkout per spec, several
35
+ at once, one terminal session each) or `checkout` (the branch is built in the
36
+ checkout you are already in; one spec at a time, no hand-off). It is a question
37
+ about how the operator works, not about what the repo contains, so ask rather
38
+ than infer it from whether dev servers or Docker are configured.
39
+
40
+ ## 0b. Release gating (optional, and separate)
41
+
42
+ Ask whether specs should record a **release-gating** decision — does this change
43
+ ship behind a feature flag, or land live? Adopting it copies
44
+ `specs/.core/gating.config.json.example` → `gating.config.json` and sets
45
+ `guidance` to wherever this project documents its flags.
46
+
47
+ It is **orthogonal to isolation**: a project can adopt either, both, or neither.
48
+ Skitterspec never learns how the flags work — it asks the question, cites that
49
+ path, and records the answer on each spec. Leave it off and nothing appears:
50
+ no question, no header, no check. Off is a perfectly good answer for a project
51
+ that does not use flags.
29
52
 
30
53
  ## 1. Folders
31
54
 
@@ -54,7 +77,7 @@ project wants everything tracked, remove it. Confirm with
54
77
 
55
78
  ## 3. Verify the skills are installed
56
79
 
57
- Check each of the eight skills resolves — `.claude/skills/<name>/SKILL.md`
80
+ Check each of the ten skills resolves — `.claude/skills/<name>/SKILL.md`
58
81
  (project) or `~/.claude/skills/<name>/` (global). List any missing. This skill
59
82
  scaffolds the project; it does **not** regenerate skill bodies — missing skills
60
83
  must be copied in (e.g. `npx @skitterbyte/skitterspec init`) from a global install
@@ -70,10 +93,10 @@ Ensure it exists. If missing, create it documenting:
70
93
  - the **Author** / **Developer** header fields;
71
94
  - the **State log** audit table (folder/status transitions), kept separate from
72
95
  the **Changelog** (decisions);
73
- - the project's concrete typecheck/test commands, and the rule that **every
74
- phase ends with creating + running tests**.
96
+ - the project's concrete typecheck/test commands, and the rule that
97
+ **every phase ends with creating + running tests**.
75
98
 
76
- Read a sibling spec skill (e.g. `spec`, `spec-go`) for the canonical shapes
99
+ Read a sibling spec skill (e.g. `spec`, `spec-next`) for the canonical shapes
77
100
  rather than inventing them. If the rule already exists, leave it unless stale.
78
101
 
79
102
  ## 5. CLAUDE.md