@skitterbyte/skitterspec 17.0.0 → 19.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 +260 -10
- package/README.md +53 -4
- package/assets/claude-md-section.md +48 -2
- package/assets/commands/spec-connect.md +2 -2
- package/assets/commands/spec-live.md +2 -2
- package/assets/core/env.config.json.example +9 -3
- package/assets/core/env.config.md +102 -30
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/review/page.html +1501 -0
- package/assets/rules/spec-planning.md +224 -15
- package/assets/rules/spec-reports.md +269 -0
- package/assets/skills/spec/SKILL.md +63 -12
- package/assets/skills/spec-bug/SKILL.md +134 -26
- package/assets/skills/spec-cancel/SKILL.md +85 -6
- package/assets/skills/spec-complete/SKILL.md +109 -20
- package/assets/skills/spec-diff/SKILL.md +564 -0
- package/assets/skills/spec-hotfix/SKILL.md +143 -21
- package/assets/skills/spec-init/SKILL.md +49 -9
- package/assets/skills/spec-next/SKILL.md +289 -7
- package/assets/skills/spec-review/SKILL.md +45 -9
- package/assets/skills/spec-reviewed/SKILL.md +241 -0
- package/assets/skills/spec-start/SKILL.md +323 -66
- package/assets/skills/spec-to-main/SKILL.md +42 -20
- package/package.json +11 -7
- package/src/cli.js +1710 -80
- package/src/env/building.js +143 -0
- package/src/env/classify.js +91 -0
- package/src/env/config.js +57 -9
- package/src/env/provision.js +192 -19
- package/src/env/proxy.js +34 -1
- package/src/env/render.js +3 -12
- package/src/env/resolve.js +296 -9
- package/src/env/review.js +1329 -0
- package/src/env/serve.js +549 -0
- package/src/env/teardown.js +13 -6
- package/src/gating.js +155 -0
- package/src/init.js +124 -2
- package/src/prompts.js +10 -1
- package/LICENSE +0 -21
|
@@ -5,6 +5,11 @@ description: Create a new spec-driven-development spec. Grills the user to a cle
|
|
|
5
5
|
|
|
6
6
|
# /spec — author a new spec
|
|
7
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
|
+
|
|
8
13
|
Produce ONE concise spec in `specs/backlog/`. Do not start coding — this skill
|
|
9
14
|
plans only. Implementation happens later via `/spec-start`.
|
|
10
15
|
|
|
@@ -49,7 +54,17 @@ not write the spec until this is resolved.
|
|
|
49
54
|
`worktree + docker` only when it must. This sets the `> **Stack:**` header
|
|
50
55
|
that `/spec-start` acts on (it can be escalated later). Skip when isolation
|
|
51
56
|
isn't enabled — leave the default `worktree`.
|
|
52
|
-
10. **
|
|
57
|
+
10. **Release gating** *(only when `specs/.core/gating.config.json` exists)* —
|
|
58
|
+
should this ship behind a feature flag, or land live?
|
|
59
|
+
**Offer, don't impose**: the user decides and you raise it, so a spec
|
|
60
|
+
never reaches
|
|
61
|
+
`/spec-complete` with the question unasked. Cite the project's own
|
|
62
|
+
`guidance` path from that config when it names one — skitterspec knows
|
|
63
|
+
nothing about how this project does flags, and must not guess. Record the
|
|
64
|
+
answer **either way**: a flag name, or `none: <one-line reason>`. "No" is a
|
|
65
|
+
decision and belongs in the header; silence is not. Skip entirely when the
|
|
66
|
+
config is absent — that project does not use flags.
|
|
67
|
+
11. **Open questions** — anything still undecided.
|
|
53
68
|
|
|
54
69
|
Stop grilling when there are no unresolved branches that would change the spec.
|
|
55
70
|
Briefly play back the agreed understanding before writing.
|
|
@@ -108,6 +123,9 @@ the codebase, link rather than duplicate):
|
|
|
108
123
|
> **Area:** <comma-separated files/modules this touches>
|
|
109
124
|
> **Stack:** <worktree — or "worktree + docker" if it touches the DB/stateful
|
|
110
125
|
> services; only acted on when isolation is enabled — see Phase A item 9>
|
|
126
|
+
> **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
|
|
127
|
+
> is configured; omit the line entirely otherwise. An empty value or a bare
|
|
128
|
+
> "none" is not a valid outcome — see Phase A item 10>
|
|
111
129
|
|
|
112
130
|
## Problem
|
|
113
131
|
|
|
@@ -211,13 +229,14 @@ Rules for the spec body:
|
|
|
211
229
|
phase index.
|
|
212
230
|
- **Honour project conventions** when writing tasks — reference the relevant
|
|
213
231
|
`.claude/rules/*.md` rather than re-explaining them.
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
232
|
+
-
|
|
233
|
+
**The `## Impact` table is derived from Phase A items 3 (Affected areas) & 5 (Data/API impact)**
|
|
234
|
+
— a structured place to record what those already surface, not new grilling.
|
|
235
|
+
It is the scannable substitute for spelling impact out in prose: name the
|
|
236
|
+
surfaces (endpoints, schemas, DB tables, domain objects, routes, business
|
|
237
|
+
rules) instead of describing them, keep `Detail` terse, and let it — not
|
|
238
|
+
paragraphs — carry the blast radius. It complements the `Area:` header
|
|
239
|
+
(files) by naming behavioural surfaces.
|
|
221
240
|
- **Changelog** is mandatory and lives in the spec. Every later decision or
|
|
222
241
|
course-correction gets a dated one-line entry. Convert relative dates to
|
|
223
242
|
absolute.
|
|
@@ -225,10 +244,10 @@ Rules for the spec body:
|
|
|
225
244
|
|
|
226
245
|
## Phase C — finish up
|
|
227
246
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
247
|
+
Decide the status: **`Ready`** in `backlog` when grilling in Phase A resolved the
|
|
248
|
+
open questions, `Draft` when you deliberately left some unresolved. Either way
|
|
249
|
+
the next step is `/spec-start`. The Report section below is where all of that
|
|
250
|
+
reaches the user — do not narrate it here as well.
|
|
232
251
|
|
|
233
252
|
## Phase D — record the isolation stack (only if configured)
|
|
234
253
|
|
|
@@ -242,6 +261,15 @@ run `skitterspec spec-env up <name>` to add Docker to an existing worktree). If
|
|
|
242
261
|
`env.config.json` is absent, isolation is off — leave the default `worktree` and
|
|
243
262
|
finish as above.
|
|
244
263
|
|
|
264
|
+
## Phase D2 — record the gating decision (only if configured)
|
|
265
|
+
|
|
266
|
+
**Only when `specs/.core/gating.config.json` exists.** Make sure the
|
|
267
|
+
`> **Gating:**` header carries the Phase A item 10 answer — a flag name, or
|
|
268
|
+
`none: <reason>` using the config's `default` wording if it sets one. Nothing is
|
|
269
|
+
provisioned or enforced by this: the header exists so the decision is
|
|
270
|
+
**on the record and reviewable**, and `skitterspec gating check` reports a spec that has
|
|
271
|
+
none. It never blocks. If the config is absent, do not write the line at all.
|
|
272
|
+
|
|
245
273
|
## Phase E — link to a ticketing provider (only if one is installed)
|
|
246
274
|
|
|
247
275
|
**Only when a ticketing provider is installed and configured** (it ships the
|
|
@@ -255,3 +283,26 @@ below (nothing to do here without one).
|
|
|
255
283
|
|
|
256
284
|
|
|
257
285
|
|
|
286
|
+
|
|
287
|
+
## Report
|
|
288
|
+
|
|
289
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
290
|
+
the shape; this section carries only what is specific here.
|
|
291
|
+
|
|
292
|
+
**Verdicts**
|
|
293
|
+
|
|
294
|
+
- `✅` — a `Ready` spec is written to `specs/backlog/<name>/`.
|
|
295
|
+
- `⚠️` — written as `Draft`; open questions were deliberately left. Name them.
|
|
296
|
+
- `⏸` — grilling did not reach a shared understanding, so nothing was written.
|
|
297
|
+
That is the skill working: a spec written over an unresolved requirement is
|
|
298
|
+
the outcome Phase A exists to prevent.
|
|
299
|
+
|
|
300
|
+
**Fields:** `Tracker` · `Spec` · `Built` · `Follow-ups` · `Next`
|
|
301
|
+
|
|
302
|
+
`Built` is the spec's path and phase count; `Spec` is its status and bucket;
|
|
303
|
+
`Next` is `/spec-start <name>`, with the name spelled the way it must be typed.
|
|
304
|
+
`Tracker` appears only when a provider is installed and linked it.
|
|
305
|
+
|
|
306
|
+
**`Follow-ups` is almost always `none` here.** This skill's whole job is to
|
|
307
|
+
capture work, so anything it surfaced belongs in the spec it just wrote rather
|
|
308
|
+
than in a follow-up line beneath it.
|
|
@@ -5,6 +5,11 @@ description: Investigate a bug, capture it as a Bug-type spec, and drive it red
|
|
|
5
5
|
|
|
6
6
|
# /spec-bug — investigate a bug, prove it with a failing test, fix it
|
|
7
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
|
+
|
|
8
13
|
This is the **bug** counterpart to `/spec` (which is for **features**, plan-only).
|
|
9
14
|
Unlike `/spec`, this skill is hands-on and test-first: it reproduces the bug as a
|
|
10
15
|
**failing test (RED)**, captures a lean Bug spec, then works the test to **GREEN**.
|
|
@@ -50,25 +55,13 @@ provision from — you'll flesh it out in §4:
|
|
|
50
55
|
`## Symptom` you established above.
|
|
51
56
|
- Run `skitterspec spec-env up bug-<name>` (the `spec-env` CLI engine). It prints
|
|
52
57
|
the `git worktree add … -b bug/<name>` command (a branch forked from `main`),
|
|
53
|
-
the worktree path,
|
|
54
|
-
- Run the printed
|
|
55
|
-
commit, so
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
mv specs/in-progress/bug-<name> <worktreePath>/specs/in-progress/
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
The `mkdir -p` is not belt-and-braces. Git does not store empty directories,
|
|
64
|
-
so `specs/in-progress/` is **absent** from a fresh worktree whenever that
|
|
65
|
-
bucket happens to be empty on `main` — the common case, since it empties every
|
|
66
|
-
time the last in-progress spec completes. `mv` into a missing destination
|
|
67
|
-
renames your spec folder **to** `specs/in-progress`, silently: the spec's files
|
|
68
|
-
end up one level too high, `00-overview.md` sits where the bucket should be,
|
|
69
|
-
and every later step still appears to work until something cannot find the
|
|
70
|
-
spec. Confirm the result before carrying on — you want
|
|
71
|
-
`<worktreePath>/specs/in-progress/bug-<name>/00-overview.md`.
|
|
58
|
+
the worktree path, and any `in the worktree, run:` bootstrap steps.
|
|
59
|
+
- Run the printed commands in order. The plan **commits the stub first** — the
|
|
60
|
+
worktree forks from `main`'s last commit, so the stub has to be in it — and
|
|
61
|
+
then adds the worktree. Nothing to move afterwards: the spec is already there.
|
|
62
|
+
The commit is planned, not silent; it appears in the printed plan above the
|
|
63
|
+
`git worktree add`, and `spec-env up` refuses outright if anything *other* than
|
|
64
|
+
this spec is uncommitted.
|
|
72
65
|
- **Bootstrap the worktree.** A fresh worktree has no installed dependencies and
|
|
73
66
|
none of the repo's gitignored files (`.env`, local overrides). Run the printed
|
|
74
67
|
`in the worktree, run:` steps (file seeding, then `setup`) in order, before
|
|
@@ -79,8 +72,8 @@ provision from — you'll flesh it out in §4:
|
|
|
79
72
|
edits will prompt.
|
|
80
73
|
- **Do everything below in the worktree**, on the branch — the red test, the fix,
|
|
81
74
|
and the rest of the spec. Act on the worktree with absolute paths /
|
|
82
|
-
`git -C <worktreePath>`, or open a
|
|
83
|
-
|
|
75
|
+
`git -C <worktreePath>`, or open a session rooted at the printed path. `main`
|
|
76
|
+
changes only when the branch merges (at `/spec-complete`).
|
|
84
77
|
|
|
85
78
|
## 3. Write the failing test FIRST (RED) — mandatory
|
|
86
79
|
|
|
@@ -117,6 +110,8 @@ because work is already underway. Keep it lean:
|
|
|
117
110
|
> **Developer:** <git user.name — you, since you're fixing it now>
|
|
118
111
|
> **Raised:** <YYYY-MM-DD (today)>
|
|
119
112
|
> **Area:** <files/modules>
|
|
113
|
+
> **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
|
|
114
|
+
> is configured; omit the line entirely otherwise>
|
|
120
115
|
|
|
121
116
|
## Symptom
|
|
122
117
|
|
|
@@ -178,6 +173,21 @@ fix narrative and decisions — keep them separate.
|
|
|
178
173
|
|
|
179
174
|
|
|
180
175
|
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
### Release gating (only when configured)
|
|
179
|
+
|
|
180
|
+
**Only when `specs/.core/gating.config.json` exists.** Before writing the spec,
|
|
181
|
+
ask: should this fix ship behind a feature flag, or land live?
|
|
182
|
+
**Offer, don't impose** — cite the project's own `guidance` path when the config
|
|
183
|
+
names one, and
|
|
184
|
+
record the answer either way, as a flag name or `none: <one-line reason>`.
|
|
185
|
+
|
|
186
|
+
A bug fix ships in the next release exactly like a feature, so it gets the same
|
|
187
|
+
question rather than an assumption. A risky rewrite of a broken path is precisely
|
|
188
|
+
where a kill-switch earns its keep; a one-line null check is precisely where
|
|
189
|
+
`none:` is the honest answer. Skip entirely when the config is absent.
|
|
190
|
+
|
|
181
191
|
## 5. Drive to GREEN
|
|
182
192
|
|
|
183
193
|
- Implement the **minimal, root-cause** fix. Match surrounding code; honour all
|
|
@@ -198,10 +208,108 @@ is a no-op.
|
|
|
198
208
|
|
|
199
209
|
|
|
200
210
|
|
|
211
|
+
## 5b. Render the page — then offer the review, never write it
|
|
212
|
+
|
|
213
|
+
**Only when the project has per-spec isolation** (`specs/.core/env.config.json`
|
|
214
|
+
present). Without it there is no worktree to read and this step does not exist —
|
|
215
|
+
skip it in silence rather than explaining an absence.
|
|
216
|
+
|
|
217
|
+
The fix is green and nothing is committed yet. That is the moment the page is
|
|
218
|
+
about, so render it now — **after** the tests pass and **before** the commit:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
skitterspec spec-env review <spec>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**This is free.** The engine reads git and splices the patches into a template;
|
|
225
|
+
the diff never passes through you, so a 266KB patch costs nothing.
|
|
226
|
+
|
|
227
|
+
**Then offer `/spec-diff`. Do not run it.** The written review is the part that
|
|
228
|
+
costs — roughly **700 output tokens**, because writing it means reading the diff
|
|
229
|
+
— and that spend is the operator's call, not a default.
|
|
230
|
+
|
|
231
|
+
**The offer is the `Review` row of step 6's block** — the counts, the page link
|
|
232
|
+
and a question, in one row:
|
|
233
|
+
|
|
234
|
+
| **Review** | <N> files, +<a> −<d> · [open the page](<the `open:` URL>) — want a written review before you commit? |
|
|
235
|
+
|
|
236
|
+
**It ends in a question, addressed to someone.** It was once a fenced block of
|
|
237
|
+
engine output, and it fired on every phase and was never once taken: two quoted
|
|
238
|
+
lines under the test counts, addressed to nobody, with the report then closing
|
|
239
|
+
on *"commit this first"* — the last instruction the reader got was to move on,
|
|
240
|
+
so they did. A row in a labelled table is findable; a question in it is
|
|
241
|
+
answerable. Both halves are load-bearing.
|
|
242
|
+
|
|
243
|
+
**Never bury it and never split it.** It sits above `Follow-ups` and `Next`, and
|
|
244
|
+
the page and the question stay in the same row: two adjacent rows about one page
|
|
245
|
+
make the reader resolve a distinction before acting on either. A later edit that
|
|
246
|
+
moves it out of the block, or separates the link from the question, undoes this
|
|
247
|
+
and should be read as a regression rather than tidying.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
Relay the **`open:`** line rather than the bare path: a path is not clickable in
|
|
251
|
+
any terminal, and a page nobody can open is a page nobody reads.
|
|
252
|
+
|
|
253
|
+
- **Never write the review unasked**, and **never publish**. Publishing leaves
|
|
254
|
+
something behind that this tooling cannot remove, so it is always an ask. A
|
|
255
|
+
`file://` link is no use on a phone, and saying so **is** the ask —
|
|
256
|
+
`/spec-diff` §6 owns how.
|
|
257
|
+
**Follow the `reader:` line the engine printed — do not sniff for it.** It
|
|
258
|
+
answers where the person reading this is sitting, and the offer changes with it:
|
|
259
|
+
|
|
260
|
+
- **absent** (`unknown`) — the `file://` URL, exactly as always. **Do not warn:**
|
|
261
|
+
unknown is the ordinary state of a local machine, and a warning there is an
|
|
262
|
+
accusation against a healthy session.
|
|
263
|
+
- **`local`** — the `file://` URL.
|
|
264
|
+
- **`remote`** — the engine has already stood its local server up and put a URL
|
|
265
|
+
the reader can open on `open:`. So there is **nothing special to say**: relay
|
|
266
|
+
that line like any other. Any `also:` lines under it are the other addresses
|
|
267
|
+
this machine has, offered because the best-guess one can be wrong — pass them
|
|
268
|
+
on rather than editing them out.
|
|
269
|
+
|
|
270
|
+
**Never read an environment variable to decide this** — not `SSH_CONNECTION`,
|
|
271
|
+
not `CLAUDE_CODE_*`, not a tty check. The engine did it, reports it on that line
|
|
272
|
+
and in `--json`, and a second implementation here could not be tested and would
|
|
273
|
+
drift.
|
|
274
|
+
|
|
275
|
+
**Serving is the engine's to do; publishing is never.** A `remote` reader
|
|
276
|
+
authorises a local server — one process, ended by one flag, leaving nothing
|
|
277
|
+
behind — and authorises nothing else. Publishing leaves a page this tooling
|
|
278
|
+
cannot remove, so it stays an ask in every case, always. If the engine could not
|
|
279
|
+
serve (a busy port, a machine with no network address) it falls back to the
|
|
280
|
+
`file://` URL with its marker, and that is when publishing is worth naming.
|
|
281
|
+
|
|
282
|
+
- **Never fatal.** A failed render — no worktree, a git error — is one line, and
|
|
283
|
+
the fix is still done. The page is a convenience; the repo is the record.
|
|
284
|
+
|
|
201
285
|
## 6. Report
|
|
202
286
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
`/spec-complete`
|
|
207
|
-
|
|
287
|
+
Do **not** `git commit` unless the user asks. The spec stays in `in-progress`;
|
|
288
|
+
`Next` names the commit and then `/spec-complete`, as
|
|
289
|
+
`/commit, then /spec-complete` — this skill leaves the fix uncommitted, and
|
|
290
|
+
`/spec-complete` §2 refuses on pre-existing uncommitted changes, so naming only
|
|
291
|
+
the second half sends the reader into a refusal. `/spec-complete` then verifies
|
|
292
|
+
and archives it (**when isolated**, the fix lives on the bug's branch, and it
|
|
293
|
+
merges that back to `main`).
|
|
294
|
+
|
|
295
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
296
|
+
the shape; this section carries only what is specific here.
|
|
297
|
+
|
|
298
|
+
**Verdicts**
|
|
299
|
+
|
|
300
|
+
- `✅` — red→green, the fix is in and the suite passes.
|
|
301
|
+
- `⚠️` — green, with something worth knowing (a narrowed repro, a mirror that
|
|
302
|
+
did not refresh).
|
|
303
|
+
- `❌` — the test is still red, or a later test broke. Quote the failure.
|
|
304
|
+
- `⏸` — the bug could not be reproduced, so nothing was written. Say what was
|
|
305
|
+
tried; an unreproduced bug is a finding, not a failed run.
|
|
306
|
+
|
|
307
|
+
**Fields:** `Tracker` · `Branch` · `Spec` · `Cause` · `Built` · `Tests` ·
|
|
308
|
+
`Review` · `Follow-ups` · `Next`
|
|
309
|
+
|
|
310
|
+
`Cause` is the root cause in one clause and `Built` is the fix — the
|
|
311
|
+
failing→passing test belongs in `Tests`, named, so the evidence is a test name
|
|
312
|
+
rather than an adjective.
|
|
313
|
+
|
|
314
|
+
Step 5b's offer is the `Review` row, not a paragraph after the block — the
|
|
315
|
+
counts, the link and the question in one row. Nothing follows the block.
|
|
@@ -5,6 +5,11 @@ description: Cancel a spec — capture the reason, record final progress, stamp
|
|
|
5
5
|
|
|
6
6
|
# /spec-cancel — record, stamp a reason, archive a spec
|
|
7
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
|
+
|
|
8
13
|
## 1. Identify the target spec
|
|
9
14
|
|
|
10
15
|
- Use the name/path argument if given, else the spec **in context**. If unclear,
|
|
@@ -54,12 +59,22 @@ move the whole folder).
|
|
|
54
59
|
|
|
55
60
|
|
|
56
61
|
Then **commit the cancellation edits** — this skill wrote them, so it commits
|
|
57
|
-
them
|
|
62
|
+
them. Ask the engine which paths are this spec's, then stage and commit
|
|
63
|
+
**exactly those**:
|
|
58
64
|
|
|
59
65
|
```
|
|
60
|
-
|
|
66
|
+
skitterspec spec-env stage <name> # lists them; --json to consume
|
|
67
|
+
git add -- <the owned paths>
|
|
68
|
+
git commit -m "chore(spec): cancel <name>" -- <the owned paths>
|
|
61
69
|
```
|
|
62
70
|
|
|
71
|
+
**Never `git add specs/`.** That stages a *directory*, so a spec another
|
|
72
|
+
session is part-way through writing lands in this commit under this spec's
|
|
73
|
+
ticket. The `--` on the **commit** is the other half: a checkout has one
|
|
74
|
+
`.git/index`, shared by every session standing in it, so a bare `git commit`
|
|
75
|
+
takes whatever else is staged there however carefully you staged your own.
|
|
76
|
+
`.claude/rules/spec-planning.md` carries the full account.
|
|
77
|
+
|
|
63
78
|
**This matters more here than anywhere else.** Teardown (step 7) refuses a dirty
|
|
64
79
|
worktree and offers `--force` as the way through — and forcing would destroy the
|
|
65
80
|
cancellation record this skill just wrote. Committing first means teardown never
|
|
@@ -67,7 +82,31 @@ needs `--force`. Do not `git push`.
|
|
|
67
82
|
|
|
68
83
|
## 6. Report
|
|
69
84
|
|
|
70
|
-
|
|
85
|
+
**The block is emitted when the run ends, not where this section sits.** The
|
|
86
|
+
sections below run after it, so their outcome belongs in the block — write it
|
|
87
|
+
once, at the end, with what actually happened.
|
|
88
|
+
|
|
89
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
90
|
+
the shape; this section carries only what is specific here.
|
|
91
|
+
|
|
92
|
+
**Verdicts**
|
|
93
|
+
|
|
94
|
+
- `✅` — stamped, moved to `cancelled`, committed, torn down.
|
|
95
|
+
- `⚠️` — cancelled, with something worth knowing: teardown declined, a tracker
|
|
96
|
+
refresh that failed, unpushed commits the operator chose to let go.
|
|
97
|
+
- `❌` — it acted and stopped part-way. Say what is where.
|
|
98
|
+
- `⏸` — no reason given, or no such spec. Nothing changed.
|
|
99
|
+
|
|
100
|
+
**Fields:** `Tracker` · `Spec` · `Worktree` · `Follow-ups` · `Next`
|
|
101
|
+
|
|
102
|
+
**The reason goes in the verdict clause** — `✅ /spec-cancel · feat-foo ·
|
|
103
|
+
superseded by feat-bar`. It is the one thing anyone reading this later wants,
|
|
104
|
+
and a cancellation is an ordinary successful run, so it must not be mistaken for
|
|
105
|
+
a `Why`: that field is for a run that did not do what it set out to.
|
|
106
|
+
|
|
107
|
+
`Worktree` says what was reclaimed, or that teardown was declined and the
|
|
108
|
+
worktree still stands — on a cancelled spec that worktree may hold the only copy
|
|
109
|
+
of the work, so an unreclaimed one is worth a line rather than a silence.
|
|
71
110
|
|
|
72
111
|
## 7. Tear down the environment (opt-in, only if configured)
|
|
73
112
|
|
|
@@ -78,9 +117,23 @@ directly (the old `/spec-env-down` skill is gone — teardown is folded in here)
|
|
|
78
117
|
1. If `.spec-env/connected` names this spec, run `skitterspec spec-env connect
|
|
79
118
|
main` first to free the canonical ports.
|
|
80
119
|
2. `skitterspec spec-env dev down <name>` — stop its host dev servers.
|
|
81
|
-
**Standing in the worktree? Leave it before you tear it down.** If this
|
|
82
|
-
|
|
83
|
-
**first**, then run the teardown commands.
|
|
120
|
+
**Standing in the worktree? Leave it before you tear it down.** If this session's
|
|
121
|
+
cwd is inside the spec's own worktree — the normal case in `worktree` mode, since
|
|
122
|
+
`/spec-start` moves you there — get out **first**, then run the teardown commands.
|
|
123
|
+
One instruction covers it:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
cd <primary checkout>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
That is the whole mechanism, and it does not matter how you got in: the session
|
|
130
|
+
was moved by a `cd` and it leaves by one. There is no tool to call here, and none
|
|
131
|
+
should be reached for — the move in is a plain `cd` precisely because a tool that
|
|
132
|
+
asks for approval is unusable on a phone.
|
|
133
|
+
|
|
134
|
+
**`spec-env down` stays the single thing that deletes a worktree.** The plan
|
|
135
|
+
below is the only deleter, because a second one is how the teardown guards get
|
|
136
|
+
bypassed.
|
|
84
137
|
|
|
85
138
|
Not because git refuses — it does not. `git worktree remove` **succeeds** on the
|
|
86
139
|
tree you are standing in, and that is the problem: the directory vanishes under
|
|
@@ -94,6 +147,32 @@ only ordering that survives.
|
|
|
94
147
|
remove the worktree/stack and free the slot. It respects the teardown guards
|
|
95
148
|
(won't destroy a dirty/unpushed worktree without `--force`).
|
|
96
149
|
|
|
150
|
+
**When it refuses over unpushed commits, relay both ways out.**
|
|
151
|
+
A cancelled spec is normally unlanded, so this is the one moment in the
|
|
152
|
+
lifecycle where that guard genuinely fires — and it fires about real loss.
|
|
153
|
+
The worktree is the only copy of this work: the branch is on no remote and
|
|
154
|
+
not merged into the base branch, so removing it ends it. Say that plainly,
|
|
155
|
+
relay the engine's reason, and give both endings:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
publish it first — keeps the work reachable, then re-run /spec-cancel:
|
|
159
|
+
git -C <worktreePath> push -u origin <branch>
|
|
160
|
+
|
|
161
|
+
or accept the loss (the worktree and its commits go):
|
|
162
|
+
skitterspec spec-env down <name> --force
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Print the push; never run it.** Publishing abandoned work to a shared
|
|
166
|
+
remote is the same unasked-for act this workflow took out of `/spec-start`,
|
|
167
|
+
and it is no more wanted here — someone may well want this branch to exist
|
|
168
|
+
nowhere but their own machine. Offer the command and wait for an answer.
|
|
169
|
+
|
|
170
|
+
**Never reach for `--force` yourself either.** The engine's own message names
|
|
171
|
+
only that half, which is the whole reason this step exists: meeting a wall
|
|
172
|
+
labelled *--force to tear down anyway* at the exact moment a backup is still
|
|
173
|
+
cheap is how work gets thrown away. Both options, then stop — the choice is
|
|
174
|
+
a decision about someone's work, and it is theirs.
|
|
175
|
+
|
|
97
176
|
If the plan prints a `remote branch — confirm with the user first:` section,
|
|
98
177
|
**ask before running that line** — it is a `git push <remote> --delete`, and
|
|
99
178
|
the branch is merged, so it loses nothing. Usually there is nothing to ask:
|
|
@@ -5,6 +5,11 @@ description: Finish a spec — verify all phases are genuinely done, update prog
|
|
|
5
5
|
|
|
6
6
|
# /spec-complete — verify, finalise, archive a spec
|
|
7
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
|
+
|
|
8
13
|
## 1. Identify the target spec
|
|
9
14
|
|
|
10
15
|
- Use the name/path argument if given, else the spec **in context**. If unclear,
|
|
@@ -34,6 +39,23 @@ Before marking complete, confirm the work is actually finished:
|
|
|
34
39
|
is its own, and step 4 commits that.
|
|
35
40
|
|
|
36
41
|
|
|
42
|
+
### Release gating (only when configured)
|
|
43
|
+
|
|
44
|
+
**Only when `specs/.core/gating.config.json` exists.** Run
|
|
45
|
+
`skitterspec gating check <spec>` and report what it says. This is the last point
|
|
46
|
+
at which the question can still be answered cheaply, which is exactly why it is
|
|
47
|
+
asked here — a spec that reaches `Complete` with no decision recorded is the gap
|
|
48
|
+
the feature exists to close.
|
|
49
|
+
|
|
50
|
+
**Never refuse to complete over it.** Say plainly that no decision was recorded,
|
|
51
|
+
offer to record one now, and finish either way. The work is done; withholding the
|
|
52
|
+
status flip would punish the wrong thing.
|
|
53
|
+
|
|
54
|
+
**This check is advisory.** It reports; it never refuses, and nothing below is
|
|
55
|
+
conditional on it. A spec written before the project adopted gating has no header
|
|
56
|
+
and is not broken — turning this into a gate would accuse the very specs the
|
|
57
|
+
feature was designed not to disturb.
|
|
58
|
+
|
|
37
59
|
## 3. Update the spec
|
|
38
60
|
|
|
39
61
|
- Tick all completed tasks in the phase files; flip every finished phase-file
|
|
@@ -56,20 +78,63 @@ specs — `git log`/the per-spec State log give the completion order.
|
|
|
56
78
|
|
|
57
79
|
|
|
58
80
|
Then **commit the completion edits** — steps 3–4 are this skill's own output, so
|
|
59
|
-
it finishes its own work rather than handing you a dirty tree
|
|
81
|
+
it finishes its own work rather than handing you a dirty tree. Ask the engine
|
|
82
|
+
which paths are this spec's, then stage and commit **exactly those**:
|
|
60
83
|
|
|
61
84
|
```
|
|
62
|
-
|
|
85
|
+
skitterspec spec-env stage <name> # lists them; --json to consume
|
|
86
|
+
git add -- <the owned paths>
|
|
87
|
+
git commit -m "chore(spec): complete <name>" -- <the owned paths>
|
|
63
88
|
```
|
|
64
89
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
90
|
+
**Never `git add specs/`.** That stages a *directory*, so a spec another
|
|
91
|
+
session is part-way through writing lands in this commit under this spec's
|
|
92
|
+
ticket. The `--` on the **commit** is the other half: a checkout has one
|
|
93
|
+
`.git/index`, shared by every session standing in it, so a bare `git commit`
|
|
94
|
+
takes whatever else is staged there however carefully you staged your own.
|
|
95
|
+
`.claude/rules/spec-planning.md` carries the full account.
|
|
96
|
+
|
|
97
|
+
So this commits exactly the status flip and the move, and nothing of yours — or
|
|
98
|
+
of anyone else's — rides along.
|
|
99
|
+
**This is what lets step 6 land:** `integrate` refuses a dirty worktree, so
|
|
100
|
+
without committing here the skill would block on the very edits it just made.
|
|
101
|
+
Do not `git push`.
|
|
69
102
|
|
|
70
103
|
## 5. Report
|
|
71
104
|
|
|
72
|
-
|
|
105
|
+
**The block is emitted when the run ends, not where this section sits.** The
|
|
106
|
+
sections below run after it, so their outcome belongs in the block — write it
|
|
107
|
+
once, at the end, with what actually happened.
|
|
108
|
+
|
|
109
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
110
|
+
the shape; this section carries only what is specific here.
|
|
111
|
+
|
|
112
|
+
**Verdicts**
|
|
113
|
+
|
|
114
|
+
- `✅` — every phase done, tests green, landed, torn down.
|
|
115
|
+
- `⚠️` — finished, with something worth knowing: teardown declined, a tracker
|
|
116
|
+
refresh that failed, a hotfix tag still waiting to be pushed.
|
|
117
|
+
- `❌` — it acted and stopped part-way — a conflicted rebase, a failed
|
|
118
|
+
cherry-pick, a red base after landing. Say what state the repo is in and quote
|
|
119
|
+
the failure; there is a mess to clear and the reader needs to know where.
|
|
120
|
+
- `⏸` — phases are unfinished, tests are red, or the tree is dirty. Nothing
|
|
121
|
+
moved.
|
|
122
|
+
|
|
123
|
+
**Fields:** `Tracker` · `Spec` · `Tests` · `Landed` · `Worktree` ·
|
|
124
|
+
`Follow-ups` · `Next`
|
|
125
|
+
|
|
126
|
+
**Report this spec and no other.** A completion is the moment it is most
|
|
127
|
+
tempting to survey what is left — what else is in flight, what is in the
|
|
128
|
+
backlog, which worktrees are still standing — and it is the moment it helps
|
|
129
|
+
least: the reader has just finished something and cannot tell whether a line
|
|
130
|
+
about another spec followed from it. `Next` is one action for this spec, and
|
|
131
|
+
`/spec-list` is where the wider picture lives if they want it.
|
|
132
|
+
|
|
133
|
+
`Landed` says how, because the two ways differ: a fast-forward names the base
|
|
134
|
+
and the commit, a hotfix names the deploy tag and the `main` cherry-pick — and
|
|
135
|
+
a deploy tag that has not been pushed is a `⚠️`, not a `✅`. `Worktree` says
|
|
136
|
+
what was reclaimed, including any orphaned volumes swept, or that teardown was
|
|
137
|
+
declined and the worktree still stands.
|
|
73
138
|
|
|
74
139
|
## 6. Land the branch (opt-in, only if isolated)
|
|
75
140
|
|
|
@@ -171,9 +236,23 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
|
|
|
171
236
|
canonical ports go back to the primary checkout.
|
|
172
237
|
2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
|
|
173
238
|
no-op when none are running / configured).
|
|
174
|
-
**Standing in the worktree? Leave it before you tear it down.** If this
|
|
175
|
-
|
|
176
|
-
**first**, then run the teardown commands.
|
|
239
|
+
**Standing in the worktree? Leave it before you tear it down.** If this session's
|
|
240
|
+
cwd is inside the spec's own worktree — the normal case in `worktree` mode, since
|
|
241
|
+
`/spec-start` moves you there — get out **first**, then run the teardown commands.
|
|
242
|
+
One instruction covers it:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
cd <primary checkout>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
That is the whole mechanism, and it does not matter how you got in: the session
|
|
249
|
+
was moved by a `cd` and it leaves by one. There is no tool to call here, and none
|
|
250
|
+
should be reached for — the move in is a plain `cd` precisely because a tool that
|
|
251
|
+
asks for approval is unusable on a phone.
|
|
252
|
+
|
|
253
|
+
**`spec-env down` stays the single thing that deletes a worktree.** The plan
|
|
254
|
+
below is the only deleter, because a second one is how the teardown guards get
|
|
255
|
+
bypassed.
|
|
177
256
|
|
|
178
257
|
Not because git refuses — it does not. `git worktree remove` **succeeds** on the
|
|
179
258
|
tree you are standing in, and that is the problem: the directory vanishes under
|
|
@@ -192,16 +271,26 @@ only ordering that survives.
|
|
|
192
271
|
worktree without `--force`), so if it *does* refuse, relay that and stop
|
|
193
272
|
rather than reaching for `--force`.
|
|
194
273
|
|
|
195
|
-
**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
274
|
+
**The unpushed half of that guard cannot fire here, by construction.**
|
|
275
|
+
Step 6 landed the branch, so `merged` is true and `planDown` skips the check
|
|
276
|
+
entirely (`env/teardown.js`) — completing a spec never meets it, however the
|
|
277
|
+
branch was or was not published. `/spec-cancel` is where it does fire, because
|
|
278
|
+
a cancelled spec is unlanded and its worktree really is the only copy; the
|
|
279
|
+
publish-or-`--force` path therefore lives in that skill and this one needs no
|
|
280
|
+
equivalent. A refusal reaching this step is about a **dirty tree** instead,
|
|
281
|
+
which means something wrote into the worktree after the landing — read it,
|
|
282
|
+
don't force it.
|
|
283
|
+
|
|
284
|
+
**If the plan prints a `remote branch — confirm with the user first:` section, ask before running it.**
|
|
285
|
+
Nothing published this branch for you, so a remote copy exists only because
|
|
286
|
+
you pushed it by hand — and it outlives teardown unless someone deletes it. The planner only ever offers
|
|
287
|
+
this for a branch that has **landed**, so say so plainly when you ask — the
|
|
288
|
+
commits are on the base branch (or captured by the deploy tag), so deleting
|
|
289
|
+
the remote branch loses nothing. On a yes, run the printed
|
|
290
|
+
`git push <remote> --delete <branch>`; on a no, leave it and say the remote
|
|
291
|
+
branch is still there. Never fold it into the `run these:` batch — a project
|
|
292
|
+
that wants it unattended sets `teardown.deleteRemoteBranch: "always"` in
|
|
293
|
+
`env.config.json`, and then it appears in `run these:` instead of here.
|
|
205
294
|
4. **Reap orphaned test-DB volumes:** run `skitterspec spec-env prune`. It lists
|
|
206
295
|
Docker volumes in the repo namespace that belong to **no live spec** (no
|
|
207
296
|
worktree) — leftovers from declined/aborted teardowns, manual
|