@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.
- package/MIGRATION.md +88 -0
- package/README.md +3 -3
- package/assets/claude-md-section.md +20 -48
- package/assets/core/SETUP.md +1 -1
- package/assets/core/env.config.json.example +7 -1
- package/assets/core/env.config.md +56 -11
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/core/linear.config.md +12 -11
- package/assets/rules/spec-planning.md +53 -13
- package/assets/skills/spec/SKILL.md +46 -18
- package/assets/skills/spec-bug/SKILL.md +32 -27
- package/assets/skills/spec-cancel/SKILL.md +26 -0
- package/assets/skills/spec-complete/SKILL.md +70 -17
- package/assets/skills/spec-hotfix/SKILL.md +40 -19
- package/assets/skills/spec-init/SKILL.md +31 -8
- package/assets/skills/spec-linear-setup/SKILL.md +32 -7
- package/assets/skills/spec-next/SKILL.md +141 -0
- package/assets/skills/spec-push/SKILL.md +15 -16
- package/assets/skills/spec-review/SKILL.md +24 -11
- package/assets/skills/spec-start/SKILL.md +226 -0
- package/assets/skills/spec-status/SKILL.md +2 -2
- package/assets/skills/spec-sync/SKILL.md +8 -8
- package/assets/skills/spec-to-main/SKILL.md +21 -19
- package/package.json +1 -1
- package/src/cli.js +405 -15
- package/src/env/classify.js +91 -0
- package/src/env/config.js +39 -1
- package/src/env/integrate.js +61 -1
- package/src/env/live.js +27 -3
- package/src/env/provision.js +196 -5
- package/src/env/resolve.js +1 -0
- package/src/env/teardown.js +41 -3
- package/src/gating.js +155 -0
- package/src/init.js +45 -9
- package/src/prompts.js +41 -4
- package/src/vendor/linear/cli-sync.js +22 -2
- package/src/vendor/linear/config.js +1 -1
- package/src/vendor/sync-core/src/compare.js +25 -2
- 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-
|
|
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-
|
|
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-
|
|
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
|
-
-
|
|
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-
|
|
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. **
|
|
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-
|
|
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-
|
|
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
|
-
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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-
|
|
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-
|
|
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
|
|
145
|
-
commit, so
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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-
|
|
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
|
|
241
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
99
|
-
6 land:** `integrate` refuses a dirty worktree, so
|
|
100
|
-
skill would block on the very edits it just made.
|
|
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
|
|
109
|
-
(
|
|
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-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
|
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-
|
|
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
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
168
|
-
tag**, where it is absent more often than not. `mv` into a
|
|
169
|
-
renames your spec folder **to** `specs/in-progress`,
|
|
170
|
-
end up one level too high, `00-overview.md` sits
|
|
171
|
-
every later step still appears to work until
|
|
172
|
-
Confirm the result before carrying on — you
|
|
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-
|
|
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
|
|
247
|
-
|
|
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-
|
|
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-
|
|
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 **
|
|
26
|
-
`spec-
|
|
27
|
-
`
|
|
28
|
-
|
|
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
|
|
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
|
|
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-
|
|
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
|