@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.
Files changed (40) hide show
  1. package/MIGRATION.md +260 -10
  2. package/README.md +53 -4
  3. package/assets/claude-md-section.md +48 -2
  4. package/assets/commands/spec-connect.md +2 -2
  5. package/assets/commands/spec-live.md +2 -2
  6. package/assets/core/env.config.json.example +9 -3
  7. package/assets/core/env.config.md +102 -30
  8. package/assets/core/gating.config.json.example +4 -0
  9. package/assets/core/gating.config.md +81 -0
  10. package/assets/review/page.html +1501 -0
  11. package/assets/rules/spec-planning.md +224 -15
  12. package/assets/rules/spec-reports.md +269 -0
  13. package/assets/skills/spec/SKILL.md +63 -12
  14. package/assets/skills/spec-bug/SKILL.md +134 -26
  15. package/assets/skills/spec-cancel/SKILL.md +85 -6
  16. package/assets/skills/spec-complete/SKILL.md +109 -20
  17. package/assets/skills/spec-diff/SKILL.md +564 -0
  18. package/assets/skills/spec-hotfix/SKILL.md +143 -21
  19. package/assets/skills/spec-init/SKILL.md +49 -9
  20. package/assets/skills/spec-next/SKILL.md +289 -7
  21. package/assets/skills/spec-review/SKILL.md +45 -9
  22. package/assets/skills/spec-reviewed/SKILL.md +241 -0
  23. package/assets/skills/spec-start/SKILL.md +323 -66
  24. package/assets/skills/spec-to-main/SKILL.md +42 -20
  25. package/package.json +11 -7
  26. package/src/cli.js +1710 -80
  27. package/src/env/building.js +143 -0
  28. package/src/env/classify.js +91 -0
  29. package/src/env/config.js +57 -9
  30. package/src/env/provision.js +192 -19
  31. package/src/env/proxy.js +34 -1
  32. package/src/env/render.js +3 -12
  33. package/src/env/resolve.js +296 -9
  34. package/src/env/review.js +1329 -0
  35. package/src/env/serve.js +549 -0
  36. package/src/env/teardown.js +13 -6
  37. package/src/gating.js +155 -0
  38. package/src/init.js +124 -2
  39. package/src/prompts.js +10 -1
  40. package/LICENSE +0 -21
package/MIGRATION.md CHANGED
@@ -1,5 +1,253 @@
1
1
  # Migration guide
2
2
 
3
+ ## `@skitterbyte/skitterspec` v18 → v19 (starting a spec offers phase 1)
4
+
5
+ ### Breaking change
6
+
7
+ **skitterspec now requires Node 22.13 or newer.** `engines.node` was `>=18`, and
8
+ that floor was a claim nobody was testing: the test suite cannot run without an
9
+ install, and pnpm 11.11 — the package manager this repo pins — itself requires
10
+ 22.13. A floor CI cannot exercise is a promise rather than a guarantee, so it was
11
+ raised to the version the toolchain actually needs.
12
+
13
+ On Node 18 or 20, `npm install` now warns — or fails, under `engine-strict`.
14
+ Upgrade Node, or stay on v18 of skitterspec. Nothing in your config changes
15
+ either way.
16
+
17
+ ### Releases now carry provenance
18
+
19
+ Every release from v19 onwards is built and signed by GitHub Actions through npm
20
+ Trusted Publishing, so no publish token exists anywhere to be leaked. Each
21
+ published version carries a signed attestation you can verify back to the commit
22
+ it was built from:
23
+
24
+ ```
25
+ npm view @skitterbyte/skitterspec@19.0.0 dist.attestations
26
+ ```
27
+
28
+ Nothing to do — it is a property of the package you receive.
29
+
30
+ ### Breaking change
31
+
32
+ **`/spec-start` no longer pushes the spec's branch.** It provisions the worktree
33
+ and commits the spec's move to `in-progress/` exactly as before, and then stops.
34
+ Publishing is yours to do, whenever you want the work somewhere other than your
35
+ machine:
36
+
37
+ ```
38
+ git -C <worktreePath> push -u origin <branch>
39
+ ```
40
+
41
+ Two things change for you, and neither is in your config:
42
+
43
+ - **Spec branches stop appearing on the remote.** Nothing is lost — the branch
44
+ and its commits are in the worktree — but a branch you have not pushed is on
45
+ one machine only, and that is now the default rather than something the
46
+ tooling quietly undid.
47
+ - **Cancelling a spec with unpublished work now refuses.** `/spec-cancel` has
48
+ always respected `guards.refuseTeardownIfUnpushed`, but the guard could never
49
+ fire while provisioning published every branch. It fires now, at the one moment
50
+ it was written for: the work really is about to be destroyed, and the worktree
51
+ is the only copy. `/spec-cancel` names both ways out — publish the branch and
52
+ re-run, or `spec-env down <name> --force` accepting the loss. Nothing was
53
+ removed from your config and nothing needs adding to it.
54
+
55
+ The justification for the old behaviour does not survive reading, which is why
56
+ it went rather than becoming a setting: it claimed to fire the tracker's branch
57
+ automation, and that needs `{identifier}` in `branch.pattern`, which the shipped
58
+ default does not carry. `/spec-bug` never pushed and `/spec-hotfix` forbids it,
59
+ so this also makes the three consistent.
60
+
61
+ **The `open.command` config key is gone.** It was the editor/terminal-agnostic
62
+ opener — `code {worktreePath}`, a `tmux` command, a `warp://` deeplink — that
63
+ `/spec-start` ran when it could not move your session into the worktree.
64
+
65
+ **Leaving it in `env.config.json` is harmless and silent** — which is the part
66
+ to watch. The config merge copies known keys only, so a leftover `open` block is
67
+ ignored rather than rejected: nothing errors, and your editor simply stops
68
+ opening. If you set it deliberately, that absence is the only signal you get.
69
+
70
+ **`/spec-start` no longer moves your session into the worktree either.** It
71
+ provisions the worktree, does the housekeeping there, prints the path, and stops.
72
+ This **supersedes the "opens a session in it" half of v17 → v18 below** — the
73
+ branch still never leaves its worktree, but nothing tries to relocate your shell
74
+ to reach it.
75
+
76
+ | v18 | v19 |
77
+ |-----|-----|
78
+ | Three paths through `/spec-start`: enter the session, or fall back two ways | **One path.** Provision, bootstrap, print the path. |
79
+ | Reaching the work meant getting a shell or a window into the worktree | **`/spec-diff`** renders the worktree's diff as a page you read anywhere |
80
+ | `open.command` opened an editor on the fallback path | Removed. Nothing opens anything. |
81
+
82
+ ### What replaced it
83
+
84
+ **`/spec-diff`** — a new skill, and the reason the opener had nothing left to do.
85
+ A phase is built in its own worktree, so `git diff` in your terminal answers
86
+ about the base branch. `/spec-diff` collects that worktree's changes with
87
+ `git -C` and writes a self-contained HTML page: whole-file context that folds
88
+ away, a file tree, untracked files included. Open it locally, or publish it and
89
+ read it on a phone.
90
+
91
+ The page lands in `.spec-env/reviews/<spec>.html` (gitignored), and
92
+ **the diff never passes through the model** — so it costs no context tokens
93
+ however large it is. The optional *written* review is the part that costs, and it
94
+ is offered rather than assumed. `/spec-next` writes the page at the end of every
95
+ phase. Beneath it, `skitterspec spec-env review <spec> [--branch]` is the engine.
96
+
97
+ ### `/spec-start` lands you in the worktree, and offers phase 1
98
+
99
+ Two changes to the same moment. In `worktree` mode `/spec-start` used to
100
+ provision the branch, print the path and leave your session where it was; opening
101
+ a session in the worktree was then yours to do. It now
102
+ **moves your session into the worktree** as part of bootstrapping it, and asks
103
+ whether to build phase 1:
104
+
105
+ ```
106
+ worktree ready — this session is now in it:
107
+ ../myrepo-wt/sort-inbox
108
+
109
+ build phase 1 now?
110
+ yes -> carries on into /spec-next
111
+ no -> you are already there; type /spec-next whenever you like
112
+ ```
113
+
114
+ **Your shell will not be where it was.** A session that was on `main` in the
115
+ primary checkout is standing in the spec's worktree afterwards, on the spec's
116
+ branch — so the next command you type runs there. That is the point of it, and it
117
+ is still a real change to plan for. The move is a plain `cd`: nothing prompts you
118
+ for approval, nothing opens a new terminal or window, and your primary checkout
119
+ is untouched and still on the base branch.
120
+
121
+ **`/spec-next` needs no argument now.** Say **yes** and `/spec-start` carries on
122
+ into a bare `/spec-next`; say **no** and typing `/spec-next` an hour later does
123
+ the same thing, because you are already standing in the right place. Neither is
124
+ assumed, because provisioning is cheap and reversible while a phase build is
125
+ neither.
126
+
127
+ **This is not a loosened refusal.** A bare `/spec-next` still refuses to build a
128
+ spec it is not standing in, exactly as before — what changed is where you are
129
+ standing, not how weakly the rule reads. `--worktree <path>` survives beside it as
130
+ the explicit way to build a spec you are *not* in, and a path you pass is still
131
+ not a path anything guessed.
132
+
133
+ **Leaving is a `cd` too.** `/spec-complete` and `/spec-cancel` delete the
134
+ worktree, which is now the directory you are standing in, so both tell you to `cd`
135
+ to the primary checkout first. `git worktree remove` **succeeds** on the tree you
136
+ occupy rather than refusing — the teardown looks fine and every command after it
137
+ dies with `Unable to read current working directory`.
138
+
139
+ **The `--worktree` build checks itself.** On that path, `/spec-next` first records
140
+ what your primary checkout looked like, and afterwards reports anything that
141
+ appeared in it — the signature of a relative path that missed the worktree. It
142
+ reports rather than accuses: it cannot know who wrote a file, so it names both
143
+ readings and deletes nothing.
144
+ `skitterspec spec-env resolve <spec> --record-primary` and
145
+ `--assert-primary-clean` are the engine underneath, usable on their own.
146
+
147
+ ### What to do
148
+
149
+ 1. **Upgrade** — `npx @skitterbyte/skitterspec update`.
150
+ 2. **Delete the `open` block from `specs/.core/env.config.json`**, if you have
151
+ one. Optional — it is ignored either way — but leaving it implies a setting
152
+ that no longer does anything.
153
+ 3. **Answer the question `/spec-start` now asks.** In `worktree` mode it offers
154
+ phase 1 before it finishes. Take the offer and it is built there and then;
155
+ decline and you are left standing in the provisioned worktree, free to type
156
+ `/spec-next` whenever you like. Both endings are fully supported — decline when
157
+ the phase is a big one and you would rather spend a fresh context on it.
158
+ 4. **Expect your shell to move, whichever you answer.** Anything you had queued
159
+ for the primary checkout — a `git` command, a script, a relative path — now
160
+ runs in the worktree instead. `cd` back when you want the base branch, and note
161
+ that `/spec-complete` and `/spec-cancel` require exactly that before they tear
162
+ the worktree down.
163
+ 5. **Use `/spec-diff` to read the work** rather than reaching for a terminal in
164
+ the worktree. It is gated on nothing — half a phase, a hand edit, or a
165
+ colleague's branch are all ordinary inputs.
166
+
167
+ `checkout` mode is unchanged.
168
+
169
+ ## `@skitterbyte/skitterspec-linear` v12 → v13 (starting a spec offers phase 1)
170
+
171
+ The same change as `@skitterbyte/skitterspec` v18 → v19 above — this
172
+ distribution composes the same lifecycle skills. Read that entry first.
173
+
174
+ ### Breaking change
175
+
176
+ **`spec-sync push` is now `spec-sync plan`.** The verb computes a create/update
177
+ plan and performs no network I/O; `spec-sync apply` is what writes to Linear.
178
+ Calling it `push` put three unrelated things behind one word — this verb, the
179
+ `/spec-push` skill, and `git push` — and it was the one that pushes nothing.
180
+
181
+ ```
182
+ skitterspec spec-sync plan <spec> --workspace-states <file> --json > plan.json
183
+ skitterspec spec-sync apply <spec> --plan plan.json
184
+ ```
185
+
186
+ The old name is **not** aliased. It is recognised and exits 1 naming its
187
+ replacement, so a script that calls it fails loudly with the fix in the message
188
+ rather than drifting on a name that will be removed later.
189
+
190
+ **Your `linear.config.json` needs no change.** The `"push"` values under
191
+ `sync.fieldOwnership` — `assignee: "push"`, `description: "push"`,
192
+ `workflowState: "push"` — are a different vocabulary: they name a direction of
193
+ ownership, not a subcommand. They are untouched and still mean what they meant.
194
+ Do not search-and-replace `push` in your config.
195
+
196
+ ### What else is here
197
+
198
+ **One thing here is Linear-specific.** `/spec-start` now pushes to Linear itself,
199
+ right after it commits the spec's move to `in-progress/`. It used to push nothing
200
+ and leave the mirror to the refresh `/spec-next` runs — immediate in `checkout`
201
+ mode, but in `worktree` mode hours away or never. Until it came, the issue sat in
202
+ its old workflow state with nobody assigned while the repo read `in-progress`
203
+ with a developer on it. Expect one more Linear call per `/spec-start`, and expect
204
+ the issue to be current the moment the spec is in flight. Nothing else changes:
205
+ sync is still one-way, and an unlinked spec is still skipped rather than minted.
206
+
207
+ ## `@skitterbyte/skitterspec` v17 → v18 (a spec is built in its own worktree)
208
+
209
+ ### Breaking change
210
+
211
+ **`/spec-start` no longer moves the branch into your checkout.** In `worktree`
212
+ mode it provisions the spec's worktree, does the housekeeping there, opens a
213
+ session in it, and stops. The spec is built where it was provisioned — which is
214
+ what worktrees are for, and why `main` stays free.
215
+
216
+ This **supersedes the "one checkout holds one spec in flight" rule** described
217
+ under v16 → v17 below. That rule was a consequence of moving the branch into the
218
+ primary checkout; with the move gone, so is the restriction:
219
+
220
+ | v17 | v18 |
221
+ |-----|-----|
222
+ | `/spec-start` refused while another spec held your checkout | In `worktree` mode it only requires a **clean tree**. Several specs in flight is what the mode is for. |
223
+ | Starting a spec took two invocations, with a `/spec-live <name>` you typed in between | **One invocation.** No hand-off command, no re-run. |
224
+ | `/spec-live main` was one of the ways out of the gate | The ways out are `/spec-complete` and `/spec-cancel`. Parking to free a workbench is a one-workbench answer, and only `checkout` mode holds one spec now. |
225
+
226
+ **`/spec-live` is for testing only** — reusing your running dev server to reach a
227
+ spec at the canonical URL. It was never meant to be how work gets started, and no
228
+ lifecycle skill calls it.
229
+
230
+ `checkout` mode is unchanged: the branch is built in the primary checkout, the
231
+ gate still requires the workbench free, and `/spec-start` carries straight on
232
+ into phase 1 in the same session.
233
+
234
+ ### What to do
235
+
236
+ 1. **Upgrade** — `npx @skitterbyte/skitterspec update`.
237
+ 2. **Expect a session, not a swap.** After `/spec-start` in `worktree` mode, run
238
+ `/spec-next` from the session it opens in the worktree. `/spec-next` builds
239
+ the spec it is *standing in* and refuses to build one from elsewhere.
240
+ 3. **Nothing to configure.** `spec.companionPaths` in `env.config.json` is new
241
+ and optional — it names paths that belong to a spec alongside its own folder
242
+ (a tracker's per-spec snapshot), so `/spec-start` can commit them together.
243
+ Empty by default; `/spec-linear-setup` sets it for you.
244
+
245
+ ## `@skitterbyte/skitterspec-linear` v11 → v12 (a spec is built in its own worktree)
246
+
247
+ The same change as `@skitterbyte/skitterspec` v17 → v18 above — this
248
+ distribution composes the same lifecycle skills. Read that entry; nothing here
249
+ is Linear-specific.
250
+
3
251
  ## `@skitterbyte/skitterspec` v16 → v17 (`/spec-go` splits in two)
4
252
 
5
253
  ### Breaking change
@@ -36,10 +284,17 @@ branch nobody asked for.
36
284
  provider distribution must rename its fragment file to match, or the build
37
285
  fails on an orphaned seam.
38
286
 
287
+ ## `@skitterbyte/skitterspec-linear` v10 → v11 (`/spec-go` splits in two)
288
+
289
+ The same change as `@skitterbyte/skitterspec` v16 → v17 above — this
290
+ distribution composes the same lifecycle skills, so `/spec-go` is removed here
291
+ too and replaced by `/spec-start` + `/spec-next`. Read that entry; nothing here
292
+ is Linear-specific. (Backfilled: the base entry was written when the change
293
+ landed and this one was missed.)
294
+
39
295
  ## `@skitterbyte/skitterspec-linear` v9 → v10 (`push` validates your issue states)
40
296
 
41
- **`spec-sync push` now refuses to run until the configured `states` names have
42
- been checked against your Linear workspace.** The check itself is not new — it
297
+ **`spec-sync push` now refuses to run until the configured `states` names have been checked against your Linear workspace.** The check itself is not new — it
43
298
  already existed on `spec-sync status --workspace-states` — but it was advisory,
44
299
  and skipping it sent a state name Linear **silently ignores**: the description
45
300
  lands, the issue never moves, and nothing errors. The base
@@ -53,8 +308,7 @@ lands, the issue never moves, and nothing errors. The base
53
308
  | A configured state absent from the workspace | pushed, silently no-op | **exits 1**, naming the workspace's real states |
54
309
 
55
310
  `/spec-push` handles this for you — it fetches the workspace's issue
56
- workflow-state names over MCP and passes them on. **Nothing changes if you drive
57
- sync through the skill.** Only a direct CLI caller needs updating.
311
+ workflow-state names over MCP and passes them on. **Nothing changes if you drive sync through the skill.** Only a direct CLI caller needs updating.
58
312
 
59
313
  ### What to do
60
314
 
@@ -104,8 +358,7 @@ sync through the skill.** Only a direct CLI caller needs updating.
104
358
  **v9 remaps the Linear mirror.** A spec is now a Linear **issue** (not a Project),
105
359
  each phase a **sub-issue** (not a Milestone), and **tasks are no longer synced**
106
360
  (they stay in the repo phase files). This collapses a large spec from ~1 project +
107
- N milestones + dozens of task-issues down to **one issue + one sub-issue per
108
- phase**. The base `@skitterbyte/skitterspec` is unaffected (still v15).
361
+ N milestones + dozens of task-issues down to **one issue + one sub-issue per phase**. The base `@skitterbyte/skitterspec` is unaffected (still v15).
109
362
 
110
363
  ### Breaking changes
111
364
 
@@ -222,10 +475,7 @@ both as optional — `/spec-review` adds them if you want them.
222
475
 
223
476
  ## `@skitterbyte/skitterspec` v2 → v3 (slimmer surface + local traffic diversion)
224
477
 
225
- **v3 shrinks the everyday command surface to five verbs — `spec → go → connect →
226
- commit → complete` — by folding provisioning, teardown, and grooming into the
227
- lifecycle skills, and adds `/spec-connect` for testing a worktree at your normal
228
- `localhost` URL.** (`@skitterbyte/skitterspec-linear` moves to v2.0.0 in lockstep.)
478
+ **v3 shrinks the everyday command surface to five verbs — `spec → go → connect → commit → complete` — by folding provisioning, teardown, and grooming into the lifecycle skills, and adds `/spec-connect` for testing a worktree at your normal `localhost` URL.** (`@skitterbyte/skitterspec-linear` moves to v2.0.0 in lockstep.)
229
479
 
230
480
  ### Removed skills (breaking) → where they went
231
481
 
package/README.md CHANGED
@@ -6,13 +6,14 @@ Spec-driven development for [Claude Code](https://claude.com/claude-code) — a
6
6
  ```
7
7
  /spec → /spec-start → /spec-next → /commit → /spec-complete
8
8
  plan build it test it live save it finish + land
9
+ ↳ /spec-diff — read what the phase changed
9
10
  ```
10
11
 
11
12
  Ships the spec-lifecycle skills (`/spec`, `/spec-start`, `/spec-next`, `/spec-complete`,
12
- `/spec-cancel`, `/spec-bug`, `/spec-hotfix`, `/spec-review`, `/spec-init`) plus
13
- per-spec **isolation** — a git worktree per in-progress spec, Docker on demand,
14
- host dev servers on reserved ports, and `/spec-connect` to test a worktree at your
15
- normal `localhost` URL.
13
+ `/spec-cancel`, `/spec-bug`, `/spec-hotfix`, `/spec-review`, `/spec-diff`,
14
+ `/spec-init`) plus per-spec **isolation** — a git worktree per in-progress spec,
15
+ Docker on demand, host dev servers on reserved ports, and `/spec-connect` to test
16
+ a worktree at your normal `localhost` URL.
16
17
 
17
18
  ```sh
18
19
  npx @skitterbyte/skitterspec init
@@ -63,6 +64,54 @@ no external install), so you test at the exact URL you always use.
63
64
  `/spec-connect main` hands the ports back. Exclusive: one spec at a time. See
64
65
  `specs/.core/env.config.md` for the `dev`/`proxy` config.
65
66
 
67
+ ## Reading the diff — `/spec-diff`
68
+
69
+ A phase is built in its own worktree, so `git diff` in your terminal answers
70
+ about the base branch — and a 350-line diff read as terminal text is scrolling,
71
+ not review. **`/spec-diff`** collects the worktree's changes with `git -C` and
72
+ writes a self-contained HTML page: whole-file context that folds away, a file
73
+ tree, new files included. Open it locally, or publish it and read it on a phone.
74
+
75
+ The page lands in `.spec-env/reviews/<spec>.html`, which is gitignored — so
76
+ reviewing a branch leaves no change in the branch you are reviewing. **The diff never passes through the model**, so it costs no context tokens however large it
77
+ is; the optional *written* review (a short read plus `flag`/`confirm`/`good`
78
+ notes) is the part that costs, and it is offered rather than assumed. Publishing
79
+ is always opt-in, and one page per spec — later phases update the same link.
80
+
81
+ `/spec-next` writes the page at the end of every phase. Nothing about it depends
82
+ on where your shell is.
83
+
84
+ ## One ending, every skill — `.claude/rules/spec-reports.md`
85
+
86
+ Every skill finishes with the same block, and says nothing while it runs beyond
87
+ a question it cannot answer itself or a failure at the moment it happens:
88
+
89
+ ✅ **Phase 2 built** — `feat-orders`, 2 of 4
90
+
91
+ | | |
92
+ |---|---|
93
+ | **Branch** | `spec/feat-orders` · 3 commits, clean |
94
+ | **Built** | POST /orders handler, orders schema |
95
+ | **Tests** | 128 passed · npm test |
96
+ | **Review** | 7 files, +212 −18 · [open the page](file:///…) — want a written review before you commit? |
97
+ | **Follow-ups** | none |
98
+ | **Next** | `/spec-next` → phase 3 (Auth) |
99
+
100
+ Four verdicts, and the last two are different facts about your repo: `✅` done ·
101
+ `⚠️` done with caveats · `❌` failed part-way, so there is a mess to clear ·
102
+ `⏸` refused before acting, so nothing changed.
103
+ **A refusal emits the block too**, so "nothing happened" is a reported outcome
104
+ rather than an absent one.
105
+
106
+ Fields come from a fixed vocabulary in a fixed order, and a skill emits only the
107
+ ones it declares — `Next` is last because it is the only row you act on. (A `Tracker` row leads the
108
+ table once a ticketing provider is installed; the base has none, so it never
109
+ appears.)
110
+ `Follow-ups` is always there: a recorded `none` is a decision where a missing
111
+ line is an oversight. The block covers **that run only** — what else is in
112
+ flight is a different question, and answering it here leaves you unable to tell
113
+ what followed from the run you just watched.
114
+
66
115
  ## Production hotfixes — `/spec-hotfix`
67
116
 
68
117
  When prod is on a tagged release, a fix must be built on **that** version, not
@@ -2,14 +2,60 @@
2
2
 
3
3
  Spec-driven development runs through the lifecycle **skills** — use them so
4
4
  structure and lifecycle stay consistent. The everyday loop is
5
- **`spec → start → next → commit → complete`**, with `/spec-connect` when you want to test
6
- the spec in a browser.
5
+ **`spec → start → next → commit → complete`**, with **`/spec-diff`** to read what
6
+ a phase changed and `/spec-connect` when you want to test the spec in a browser.
7
7
 
8
8
  **Skills vs commands.** The lifecycle skills are read by Claude, which exercises
9
9
  judgment. `/spec-connect` and `/spec-live` are **slash commands** instead — each
10
10
  pre-executes one `spec-env` verb and relays it, so only you can run them; a
11
11
  skill that wants one will tell you to type it.
12
12
 
13
+ **Seeing the work.** A phase is built in its own worktree, so `git diff` in your
14
+ terminal answers about the base branch. **`/spec-diff`** renders that worktree's
15
+ diff as a self-contained HTML page you open locally — or publish, and read on a
16
+ phone. The diff never passes through the model, so the page costs no context
17
+ tokens however large it is; the optional written review is the part that costs,
18
+ and it is offered rather than assumed. `/spec-next` writes the page at the end of
19
+ every phase.
20
+
21
+ **Handing the review back.** The page takes marks: tick `✓ accept` per file as
22
+ you read, note anything against a line or a whole file, answer the questions a
23
+ written review asked — then **end it in a decision**. Three buttons, each
24
+ carrying its own verdict:
25
+ `✓ Commit` commits it, `✓ Commit & Continue` commits and builds the next phase,
26
+ `↺ Request changes` sends it straight back to be worked, `… Discuss first` asks
27
+ you what's up. A **served** page hands the pass to
28
+ the engine, which **holds** it and shows a six-digit code. Type `/spec-reviewed`
29
+ and the waiting pass is picked up and acted on; paste the code after it
30
+ (`/spec-reviewed 324199`) to name one exactly, which matters only when two are
31
+ waiting. **Nothing pushes** — a device that reaches your page cannot reach this
32
+ conversation, which is what keeps a stray approval out of your review, and is
33
+ why the code is an address rather than a password. A `file://` page has no server to talk to, so it
34
+ copies and you paste, as before. Approve is unavailable while a note is open —
35
+ you asked for something, so it cannot also be fine — and it hands off to your
36
+ own commit skill (`review.commitWith`, `/commit` by default) rather than a copy
37
+ living here. Fixes come back as resolutions, so the next render shows each note
38
+ struck through with what changed. An accept remembers the file's content, so it
39
+ lapses by itself when that file changes again. The marks are information —
40
+ nothing gates on them; the verdict is the one thing you choose, once.
41
+
42
+ **One ending.** Every spec skill finishes with the same block — a verdict
43
+ (`✅` · `⚠️` · `❌` · `⏸`), then a table of the fields that skill declares,
44
+ ending on the one thing to do next. A refusal emits it too, so "nothing
45
+ happened" is reported rather than absent, and `Follow-ups` is always there
46
+ because a recorded `none` is a decision. Skills stay quiet while they run. The
47
+ shape lives in **`.claude/rules/spec-reports.md`**.
48
+
13
49
  The skill table, the spec type/folder conventions and the per-spec isolation
14
50
  model all live in **`.claude/rules/spec-planning.md`**, the canonical reference
15
51
  every spec skill points at. Tailor its per-phase test commands to this stack.
52
+
53
+ **Release gating** *(only when `specs/.core/gating.config.json` exists)* — each
54
+ spec records whether it ships behind a feature flag, as
55
+ `> **Gating:** <flag name>` or `> **Gating:** none: <one-line reason>`. `/spec`,
56
+ `/spec-bug` and `/spec-hotfix` ask; `/spec-review`, `/spec-start` and
57
+ `/spec-complete` report a spec that has no answer, and never block over it.
58
+ `skitterspec gating check` lists them and always exits 0. The point is that the
59
+ question is **on the record**: a missing line is an oversight, a reason is a
60
+ decision. Skitterspec never reads your flag system — it asks and cites the doc
61
+ you point it at. Without that config, none of this appears.
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: Expose one spec's dev servers on the canonical localhost ports (or `main` to hand them back)
3
- argument-hint: "[spec|main]"
2
+ description: Expose one spec's dev servers on the canonical localhost ports bare uses the spec you are on, `main` hands them back
3
+ argument-hint: "[<spec> | main]"
4
4
  allowed-tools: Bash({{exec}} skitterspec spec-env connect:*)
5
5
  disable-model-invocation: true
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: Put one spec live on the already-running dev server — `<spec>` takes it, a bare `take` takes the spec you are on, `main` hands the instance back
3
- argument-hint: "[<spec> | main | take | release | abort | status]"
2
+ description: Put one spec live on the already-running dev server — bare takes the spec you are on, `<spec>` takes that one, `main` hands the instance back
3
+ argument-hint: "[<spec> | main | status | release | abort]"
4
4
  allowed-tools: Bash({{exec}} skitterspec spec-env live:*)
5
5
  disable-model-invocation: true
6
6
  ---
@@ -25,19 +25,25 @@
25
25
  "enabled": true,
26
26
  "host": "127.0.0.1"
27
27
  },
28
- "open": {
29
- "command": ""
30
- },
31
28
  "registry": ".spec-env/registry.json",
32
29
  "branch": {
33
30
  "pattern": "{type}/{slug}",
34
31
  "identifierField": ""
35
32
  },
33
+ "spec": {
34
+ "companionPaths": []
35
+ },
36
36
  "baseBranch": "",
37
37
  "guards": {
38
38
  "refuseTeardownIfDirty": true,
39
39
  "refuseTeardownIfUnpushed": true
40
40
  },
41
+ "review": {
42
+ "reader": "detect",
43
+ "servePort": 7777,
44
+ "serveOnRemote": true,
45
+ "commitWith": "/commit"
46
+ },
41
47
  "live": {
42
48
  "migrations": []
43
49
  }