@skitterbyte/skitterspec 18.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.
@@ -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,7 +55,7 @@ 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, the opener, and any `in the worktree, run:` bootstrap steps.
58
+ the worktree path, and any `in the worktree, run:` bootstrap steps.
54
59
  - Run the printed commands in order. The plan **commits the stub first** — the
55
60
  worktree forks from `main`'s last commit, so the stub has to be in it — and
56
61
  then adds the worktree. Nothing to move afterwards: the spec is already there.
@@ -67,8 +72,8 @@ provision from — you'll flesh it out in §4:
67
72
  edits will prompt.
68
73
  - **Do everything below in the worktree**, on the branch — the red test, the fix,
69
74
  and the rest of the spec. Act on the worktree with absolute paths /
70
- `git -C <worktreePath>`, or open a fresh session rooted there (the printed
71
- opener). `main` changes only when the branch merges (at `/spec-complete`).
75
+ `git -C <worktreePath>`, or open a session rooted at the printed path. `main`
76
+ changes only when the branch merges (at `/spec-complete`).
72
77
 
73
78
  ## 3. Write the failing test FIRST (RED) — mandatory
74
79
 
@@ -168,6 +173,8 @@ fix narrative and decisions — keep them separate.
168
173
 
169
174
 
170
175
 
176
+
177
+
171
178
  ### Release gating (only when configured)
172
179
 
173
180
  **Only when `specs/.core/gating.config.json` exists.** Before writing the spec,
@@ -201,10 +208,108 @@ is a no-op.
201
208
 
202
209
 
203
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
+
204
285
  ## 6. Report
205
286
 
206
- Summarise: root cause, the failing→passing test, the fix, and the full test
207
- result. The spec stays in `in-progress`; suggest `/spec-complete` to verify and
208
- archive it (**when isolated**, the fix lives on the bug's branch, and
209
- `/spec-complete` merges it back to `main`). Do **not** `git commit` unless the
210
- user asks.
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
- git add specs/ && git commit -m "chore(spec): cancel <name>"
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
- Confirm the cancellation, the reason recorded, the new location, and the commit.
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,22 +117,22 @@ 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
- session's cwd is inside the spec's own worktree, get out **first**, then run the
83
- teardown commands. How you leave depends on how you got in:
84
-
85
- - **`/spec-start` moved this session in** — the normal path in `worktree` mode.
86
- Call **`ExitWorktree`** with `action: "keep"`. It restores the session to the
87
- directory it started from *and* clears the caches still pointing at the
88
- worktree, which a bare `cd` does not: after a `cd` the session stays registered
89
- against the tree and asks you about it again when it ends.
90
- - **You opened the terminal yourself** `cd` to the primary checkout, as always.
91
- `ExitWorktree` is a no-op outside a session it moved, so trying it costs
92
- nothing, but the `cd` is what relocates you.
93
-
94
- **Always `keep`, never `remove`.** `ExitWorktree` refuses to remove a worktree
95
- entered by path in any case, and the `spec-env down` plan below has to stay the
96
- single thing that deletes a second deleter is how the teardown guards get
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
97
136
  bypassed.
98
137
 
99
138
  Not because git refuses — it does not. `git worktree remove` **succeeds** on the
@@ -108,6 +147,32 @@ only ordering that survives.
108
147
  remove the worktree/stack and free the slot. It respects the teardown guards
109
148
  (won't destroy a dirty/unpushed worktree without `--force`).
110
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
+
111
176
  If the plan prints a `remote branch — confirm with the user first:` section,
112
177
  **ask before running that line** — it is a `git push <remote> --delete`, and
113
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,
@@ -73,21 +78,63 @@ specs — `git log`/the per-spec State log give the completion order.
73
78
 
74
79
 
75
80
  Then **commit the completion edits** — steps 3–4 are this skill's own output, so
76
- 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**:
77
83
 
78
84
  ```
79
- git add specs/ && git commit -m "chore(spec): complete <name>"
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>
80
88
  ```
81
89
 
82
- Step 2 established the tree was otherwise clean, so this commits exactly the
83
- status flip and the move nothing of yours rides along.
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.
84
99
  **This is what lets step 6 land:** `integrate` refuses a dirty worktree, so
85
100
  without committing here the skill would block on the very edits it just made.
86
101
  Do not `git push`.
87
102
 
88
103
  ## 5. Report
89
104
 
90
- Confirm the move, the commit, the final test result, and list anything deferred.
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.
91
138
 
92
139
  ## 6. Land the branch (opt-in, only if isolated)
93
140
 
@@ -189,22 +236,22 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
189
236
  canonical ports go back to the primary checkout.
190
237
  2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
191
238
  no-op when none are running / configured).
192
- **Standing in the worktree? Leave it before you tear it down.** If this
193
- session's cwd is inside the spec's own worktree, get out **first**, then run the
194
- teardown commands. How you leave depends on how you got in:
195
-
196
- - **`/spec-start` moved this session in** — the normal path in `worktree` mode.
197
- Call **`ExitWorktree`** with `action: "keep"`. It restores the session to the
198
- directory it started from *and* clears the caches still pointing at the
199
- worktree, which a bare `cd` does not: after a `cd` the session stays registered
200
- against the tree and asks you about it again when it ends.
201
- - **You opened the terminal yourself** `cd` to the primary checkout, as always.
202
- `ExitWorktree` is a no-op outside a session it moved, so trying it costs
203
- nothing, but the `cd` is what relocates you.
204
-
205
- **Always `keep`, never `remove`.** `ExitWorktree` refuses to remove a worktree
206
- entered by path in any case, and the `spec-env down` plan below has to stay the
207
- single thing that deletes a second deleter is how the teardown guards get
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
208
255
  bypassed.
209
256
 
210
257
  Not because git refuses — it does not. `git worktree remove` **succeeds** on the
@@ -224,9 +271,19 @@ only ordering that survives.
224
271
  worktree without `--force`), so if it *does* refuse, relay that and stop
225
272
  rather than reaching for `--force`.
226
273
 
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
+
227
284
  **If the plan prints a `remote branch — confirm with the user first:` section, ask before running it.**
228
- `/spec-start` pushed this branch when it provisioned, so the remote copy
229
- outlives teardown unless someone deletes it. The planner only ever offers
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
230
287
  this for a branch that has **landed**, so say so plainly when you ask — the
231
288
  commits are on the base branch (or captured by the deploy tag), so deleting
232
289
  the remote branch loses nothing. On a yes, run the printed