@skitterbyte/skitterspec 17.0.0 → 18.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 +52 -0
- package/assets/claude-md-section.md +10 -0
- package/assets/core/env.config.json.example +3 -0
- package/assets/core/env.config.md +36 -11
- package/assets/core/gating.config.json.example +4 -0
- package/assets/core/gating.config.md +81 -0
- package/assets/rules/spec-planning.md +31 -10
- package/assets/skills/spec/SKILL.md +31 -8
- package/assets/skills/spec-bug/SKILL.md +21 -18
- package/assets/skills/spec-cancel/SKILL.md +16 -2
- package/assets/skills/spec-complete/SKILL.md +47 -15
- package/assets/skills/spec-hotfix/SKILL.md +30 -11
- package/assets/skills/spec-init/SKILL.md +15 -2
- package/assets/skills/spec-next/SKILL.md +2 -2
- package/assets/skills/spec-review/SKILL.md +19 -6
- package/assets/skills/spec-start/SKILL.md +133 -53
- package/assets/skills/spec-to-main/SKILL.md +14 -14
- package/package.json +1 -1
- package/src/cli.js +209 -3
- package/src/env/classify.js +91 -0
- package/src/env/config.js +15 -0
- package/src/env/provision.js +145 -11
- package/src/env/resolve.js +1 -0
- package/src/gating.js +155 -0
- package/src/init.js +28 -1
- package/src/prompts.js +10 -1
package/MIGRATION.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
|
+
## `@skitterbyte/skitterspec` v17 → v18 (a spec is built in its own worktree)
|
|
4
|
+
|
|
5
|
+
### Breaking change
|
|
6
|
+
|
|
7
|
+
**`/spec-start` no longer moves the branch into your checkout.** In `worktree`
|
|
8
|
+
mode it provisions the spec's worktree, does the housekeeping there, opens a
|
|
9
|
+
session in it, and stops. The spec is built where it was provisioned — which is
|
|
10
|
+
what worktrees are for, and why `main` stays free.
|
|
11
|
+
|
|
12
|
+
This **supersedes the "one checkout holds one spec in flight" rule** described
|
|
13
|
+
under v16 → v17 below. That rule was a consequence of moving the branch into the
|
|
14
|
+
primary checkout; with the move gone, so is the restriction:
|
|
15
|
+
|
|
16
|
+
| v17 | v18 |
|
|
17
|
+
|-----|-----|
|
|
18
|
+
| `/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. |
|
|
19
|
+
| Starting a spec took two invocations, with a `/spec-live <name>` you typed in between | **One invocation.** No hand-off command, no re-run. |
|
|
20
|
+
| `/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. |
|
|
21
|
+
|
|
22
|
+
**`/spec-live` is for testing only** — reusing your running dev server to reach a
|
|
23
|
+
spec at the canonical URL. It was never meant to be how work gets started, and no
|
|
24
|
+
lifecycle skill calls it.
|
|
25
|
+
|
|
26
|
+
`checkout` mode is unchanged: the branch is built in the primary checkout, the
|
|
27
|
+
gate still requires the workbench free, and `/spec-start` carries straight on
|
|
28
|
+
into phase 1 in the same session.
|
|
29
|
+
|
|
30
|
+
### What to do
|
|
31
|
+
|
|
32
|
+
1. **Upgrade** — `npx @skitterbyte/skitterspec update`.
|
|
33
|
+
2. **Expect a session, not a swap.** After `/spec-start` in `worktree` mode, run
|
|
34
|
+
`/spec-next` from the session it opens in the worktree. `/spec-next` builds
|
|
35
|
+
the spec it is *standing in* and refuses to build one from elsewhere.
|
|
36
|
+
3. **Nothing to configure.** `spec.companionPaths` in `env.config.json` is new
|
|
37
|
+
and optional — it names paths that belong to a spec alongside its own folder
|
|
38
|
+
(a tracker's per-spec snapshot), so `/spec-start` can commit them together.
|
|
39
|
+
Empty by default; `/spec-linear-setup` sets it for you.
|
|
40
|
+
|
|
41
|
+
## `@skitterbyte/skitterspec-linear` v11 → v12 (a spec is built in its own worktree)
|
|
42
|
+
|
|
43
|
+
The same change as `@skitterbyte/skitterspec` v17 → v18 above — this
|
|
44
|
+
distribution composes the same lifecycle skills. Read that entry; nothing here
|
|
45
|
+
is Linear-specific.
|
|
46
|
+
|
|
3
47
|
## `@skitterbyte/skitterspec` v16 → v17 (`/spec-go` splits in two)
|
|
4
48
|
|
|
5
49
|
### Breaking change
|
|
@@ -36,6 +80,14 @@ branch nobody asked for.
|
|
|
36
80
|
provider distribution must rename its fragment file to match, or the build
|
|
37
81
|
fails on an orphaned seam.
|
|
38
82
|
|
|
83
|
+
## `@skitterbyte/skitterspec-linear` v10 → v11 (`/spec-go` splits in two)
|
|
84
|
+
|
|
85
|
+
The same change as `@skitterbyte/skitterspec` v16 → v17 above — this
|
|
86
|
+
distribution composes the same lifecycle skills, so `/spec-go` is removed here
|
|
87
|
+
too and replaced by `/spec-start` + `/spec-next`. Read that entry; nothing here
|
|
88
|
+
is Linear-specific. (Backfilled: the base entry was written when the change
|
|
89
|
+
landed and this one was missed.)
|
|
90
|
+
|
|
39
91
|
## `@skitterbyte/skitterspec-linear` v9 → v10 (`push` validates your issue states)
|
|
40
92
|
|
|
41
93
|
**`spec-sync push` now refuses to run until the configured `states` names have
|
|
@@ -13,3 +13,13 @@ skill that wants one will tell you to type it.
|
|
|
13
13
|
The skill table, the spec type/folder conventions and the per-spec isolation
|
|
14
14
|
model all live in **`.claude/rules/spec-planning.md`**, the canonical reference
|
|
15
15
|
every spec skill points at. Tailor its per-phase test commands to this stack.
|
|
16
|
+
|
|
17
|
+
**Release gating** *(only when `specs/.core/gating.config.json` exists)* — each
|
|
18
|
+
spec records whether it ships behind a feature flag, as
|
|
19
|
+
`> **Gating:** <flag name>` or `> **Gating:** none: <one-line reason>`. `/spec`,
|
|
20
|
+
`/spec-bug` and `/spec-hotfix` ask; `/spec-review`, `/spec-start` and
|
|
21
|
+
`/spec-complete` report a spec that has no answer, and never block over it.
|
|
22
|
+
`skitterspec gating check` lists them and always exits 0. The point is that the
|
|
23
|
+
question is **on the record**: a missing line is an oversight, a reason is a
|
|
24
|
+
decision. Skitterspec never reads your flag system — it asks and cites the doc
|
|
25
|
+
you point it at. Without that config, none of this appears.
|
|
@@ -8,11 +8,11 @@ in-progress spec). Provisioning is folded into `/spec-start`, teardown into
|
|
|
8
8
|
beneath them.
|
|
9
9
|
|
|
10
10
|
**Once this file is present, isolation is the default policy:** `/spec-start` gives
|
|
11
|
-
**every** in-progress spec its own git worktree automatically. Docker is a
|
|
12
|
-
spec escalation** — a spec brings up a stack only when its
|
|
13
|
-
is `worktree + docker` (set at `/spec` when it touches
|
|
14
|
-
services). A `worktree`-only spec takes no registry slot, no
|
|
15
|
-
`.env`.
|
|
11
|
+
**every** in-progress spec its own git worktree automatically. Docker is a
|
|
12
|
+
**per- spec escalation** — a spec brings up a stack only when its
|
|
13
|
+
`> **Stack:**` header is `worktree + docker` (set at `/spec` when it touches
|
|
14
|
+
the DB / stateful services). A `worktree`-only spec takes no registry slot, no
|
|
15
|
+
port block, and no `.env`.
|
|
16
16
|
|
|
17
17
|
**Adopt it** with `skitterspec init --isolation` (or copy
|
|
18
18
|
`env.config.json.example` → `env.config.json` here) and edit the values. While
|
|
@@ -124,13 +124,18 @@ no live `env.config.json` was found.
|
|
|
124
124
|
"host": "127.0.0.1" // bind host for the canonical ports
|
|
125
125
|
},
|
|
126
126
|
|
|
127
|
-
// Optional, editor/terminal-agnostic opener
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
127
|
+
// Optional, editor/terminal-agnostic opener — the FALLBACK for reaching a
|
|
128
|
+
// worktree. `/spec-start` normally moves the session you typed into into the
|
|
129
|
+
// worktree, and then there is nothing to open: it runs this only when it
|
|
130
|
+
// could not switch in place (the session is already inside another worktree,
|
|
131
|
+
// or the harness cannot move it). Run after provisioning and bootstrap, so
|
|
132
|
+
// the session opens onto a tree that is ready to work in. The template is
|
|
133
|
+
// expanded with {worktreePath}, {slug}, {branch}, {projectName},
|
|
134
|
+
// {portOffset}.
|
|
131
135
|
// Empty = nothing is opened (the path is just printed), which is how you turn
|
|
132
|
-
// the auto-open off
|
|
133
|
-
// window
|
|
136
|
+
// the auto-open off, and the right value unless you actually want a second
|
|
137
|
+
// window on the fallback path. A non-interactive run skips it either way — an
|
|
138
|
+
// opened window nobody is sitting at helps no one.
|
|
134
139
|
// Examples: "code {worktreePath}", "tmux new-window -c {worktreePath}",
|
|
135
140
|
// or a "warp://..." deeplink for Warp users.
|
|
136
141
|
"open": {
|
|
@@ -153,6 +158,26 @@ no live `env.config.json` was found.
|
|
|
153
158
|
"identifierField": ""
|
|
154
159
|
},
|
|
155
160
|
|
|
161
|
+
// Paths that belong to a spec ALONGSIDE its own `specs/<bucket>/<name>/`
|
|
162
|
+
// folder. `/spec-start` uses this to tell "the spec you just wrote, not yet
|
|
163
|
+
// committed" apart from someone else's uncommitted work: if every dirty path
|
|
164
|
+
// belongs to the spec being started it is committed for you, and if a single
|
|
165
|
+
// path does not, the start is refused as before.
|
|
166
|
+
//
|
|
167
|
+
// Provider-neutral by design — the base engine must not know that any
|
|
168
|
+
// particular tracker exists — so you declare the shape here. `{slug}` and
|
|
169
|
+
// `{identifier}` expand exactly as in `branch.pattern` above, `{identifier}`
|
|
170
|
+
// via `branch.identifierField`.
|
|
171
|
+
//
|
|
172
|
+
// A pattern using {identifier} matches NOTHING when no identifier resolves
|
|
173
|
+
// (no `identifierField` set, or a spec never pushed to a tracker). That is
|
|
174
|
+
// deliberate: the file it names then belongs to some other spec, and the safe
|
|
175
|
+
// failure is a refusal you clear with /commit, not a stranger's file swept
|
|
176
|
+
// into your commit. Default: none — a spec owns only its own folder.
|
|
177
|
+
"spec": {
|
|
178
|
+
"companionPaths": []
|
|
179
|
+
},
|
|
180
|
+
|
|
156
181
|
// Integration base branch — the branch specs fork from and land back onto
|
|
157
182
|
// (used by the teardown "merged?" guard and, later, the integrate step).
|
|
158
183
|
// Empty = auto-detect: origin/HEAD → main → master. Set it when your default
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `gating.config.json` — release-gating config
|
|
2
|
+
|
|
3
|
+
Opt-in config for **release gating**: whether each spec records a decision about
|
|
4
|
+
shipping behind a feature flag.
|
|
5
|
+
|
|
6
|
+
The problem it solves is not "we forgot to use a flag" — it is that
|
|
7
|
+
**nobody can tell whether the question was asked**. A spec can go from `/spec`
|
|
8
|
+
through
|
|
9
|
+
implementation to `/spec-complete` with no flag and no mention of one, and that
|
|
10
|
+
is indistinguishable from "we considered it and decided against". Prose guidance
|
|
11
|
+
in a project rule has already been shown not to close that gap; a required header
|
|
12
|
+
does, because its absence is visible.
|
|
13
|
+
|
|
14
|
+
**Skitterspec bakes in the offer, never the mechanism.** How your project does
|
|
15
|
+
flags — a resolver, an admin toggle, env precedence, a vendor SDK — is none of
|
|
16
|
+
its business, and it never reads your flag code. It asks the question, cites your
|
|
17
|
+
own documentation, and records the answer.
|
|
18
|
+
|
|
19
|
+
**Adopt it** by copying `gating.config.json.example` → `gating.config.json` here
|
|
20
|
+
(or `skitterspec init --gating`). While this file is absent the feature is
|
|
21
|
+
entirely unused: no grill question, no header, nothing to check — which is read
|
|
22
|
+
as "this project does not use feature flags".
|
|
23
|
+
|
|
24
|
+
## Fields
|
|
25
|
+
|
|
26
|
+
```jsonc
|
|
27
|
+
{
|
|
28
|
+
// Where THIS project documents how its flags work — a repo-relative path,
|
|
29
|
+
// cited when the question is asked so the answer is an informed one.
|
|
30
|
+
// Skitterspec never reads the file; it only names it. Empty = say nothing.
|
|
31
|
+
"guidance": ".claude/rules/feature-flags.md",
|
|
32
|
+
|
|
33
|
+
// The value written when the user declines a flag, so a project can
|
|
34
|
+
// standardise its wording. Must keep the `none: <reason>` shape — the reason
|
|
35
|
+
// half is the whole point (see below). Empty falls back to `none: <reason>`.
|
|
36
|
+
"default": "none: <reason>"
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## The header it drives
|
|
41
|
+
|
|
42
|
+
With this file present, every spec `/spec`, `/spec-bug` and `/spec-hotfix` write
|
|
43
|
+
carries a `Gating:` field beside `Stack:` in `00-overview.md`:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
> **Gating:** search-ranking-v2
|
|
47
|
+
> **Gating:** none: additive, nothing to revert
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Two valid shapes, and one that is not:
|
|
51
|
+
|
|
52
|
+
| Value | Meaning |
|
|
53
|
+
|-------|---------|
|
|
54
|
+
| a flag name | ships behind that flag |
|
|
55
|
+
| `none: <reason>` | deliberately not flagged, and why |
|
|
56
|
+
| *missing, empty, or a bare `none`* | **not a decision** — nobody answered |
|
|
57
|
+
|
|
58
|
+
The reason half is load-bearing. `none: additive, nothing to revert` is a
|
|
59
|
+
decision a reviewer can disagree with; a bare `none` is a shrug, and a missing
|
|
60
|
+
line is an oversight. Distinguishing those three is the entire feature.
|
|
61
|
+
|
|
62
|
+
## What it never does
|
|
63
|
+
|
|
64
|
+
- **It never blocks.** `skitterspec gating check` reports and exits 0;
|
|
65
|
+
`/spec-start` and `/spec-complete` mention a missing header and carry on.
|
|
66
|
+
Nothing here can stop a spec being started, completed or landed.
|
|
67
|
+
- **It never accuses an old spec.** Only `specs/backlog/` and
|
|
68
|
+
`specs/in-progress/` are read. Specs finished or abandoned before you adopted
|
|
69
|
+
gating are out of range by construction, not by a filter someone has to
|
|
70
|
+
remember.
|
|
71
|
+
- **It never learns your flag system.** `guidance` is a path it prints, nothing
|
|
72
|
+
more.
|
|
73
|
+
|
|
74
|
+
## Checking
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
skitterspec gating check # the spec in flight here
|
|
78
|
+
skitterspec gating check <spec> # one named spec
|
|
79
|
+
skitterspec gating check --all # every active spec
|
|
80
|
+
skitterspec gating check --json # for tooling
|
|
81
|
+
```
|
|
@@ -11,8 +11,8 @@ and lifecycle stay consistent. Each sets a status on the spec header
|
|
|
11
11
|
| `/spec-bug` | (Bug) Reproduce with a failing test, capture spec, drive red→green | `In Progress` | `specs/in-progress/` |
|
|
12
12
|
| `/spec-hotfix` | (Hotfix) Fork a worktree from a release tag, red→green, land by tag + cherry-pick | `In Progress` | `specs/in-progress/` |
|
|
13
13
|
| `/spec-review` | Re-validate a spec against the codebase; refresh stale parts | `—` | (unchanged) |
|
|
14
|
-
| `/spec-start` | Put a spec in flight
|
|
15
|
-
| `/spec-next` | Build the next phase of the spec in
|
|
14
|
+
| `/spec-start` | Put a spec in flight — provision its branch, then build phase 1 | `In Progress` | `specs/in-progress/` |
|
|
15
|
+
| `/spec-next` | Build the next phase of the spec this session is in (re-run per phase) | `In Progress` (unchanged) | (unchanged) |
|
|
16
16
|
| `/spec-to-main` | Land the branch on the base (rebase + ff) **without** finishing — for running the work in CI / a shared env mid-spec; repeatable | `In Progress` (unchanged) | (unchanged) |
|
|
17
17
|
| `/spec-complete` | Verify all phases done + tests green; land + tear down | `Complete` | `specs/complete/` |
|
|
18
18
|
| `/spec-cancel` | Record progress, stamp a reason on the header; tear down | `Cancelled` | `specs/cancelled/` |
|
|
@@ -22,9 +22,9 @@ and lifecycle stay consistent. Each sets a status on the spec header
|
|
|
22
22
|
exercises judgment. `/spec-connect` and `/spec-live` are instead **slash commands**
|
|
23
23
|
(`.claude/commands/`): each pre-executes one `skitterspec spec-env` verb and
|
|
24
24
|
relays its output, so there is no judgment to apply and no model turn spent
|
|
25
|
-
finding one. They are marked `disable-model-invocation`, meaning
|
|
26
|
-
run them** — a skill that wants one will tell you to type it
|
|
27
|
-
it.
|
|
25
|
+
finding one. They are marked `disable-model-invocation`, meaning
|
|
26
|
+
**only you can run them** — a skill that wants one will tell you to type it
|
|
27
|
+
rather than invoking it.
|
|
28
28
|
|
|
29
29
|
`/spec-to-main`, `/spec-status` and `/spec-sync` stay **skills** — each carries
|
|
30
30
|
real judgment (green tests before a land; an MCP fetch and a team-key check; ten
|
|
@@ -44,10 +44,11 @@ mid-spec (so the work can run in CI / a shared test env) while the spec stays
|
|
|
44
44
|
|
|
45
45
|
**Two workspace modes.** `specs/.core/env.config.json` → `mode` decides where a
|
|
46
46
|
spec's branch is built. **`worktree`** (the default) gives each spec its own
|
|
47
|
-
checkout — several specs at once and `main` left free
|
|
48
|
-
|
|
49
|
-
the branch in the primary checkout instead:
|
|
50
|
-
|
|
47
|
+
checkout — several specs at once and `main` left free — and `/spec-start` moves
|
|
48
|
+
the session you typed into *into* that checkout, so the work carries on in the
|
|
49
|
+
same terminal. **`checkout`** builds the branch in the primary checkout instead:
|
|
50
|
+
one spec at a time, and nothing to move into. Neither mode costs you a second
|
|
51
|
+
terminal; the difference is how many specs can be in flight at once.
|
|
51
52
|
Pick it for how you work rather than for what the project contains — a repo with
|
|
52
53
|
no dev servers may still want several specs in flight. In `checkout` mode
|
|
53
54
|
`/spec-connect` and `/spec-live` do not apply and say so: both exist to reach
|
|
@@ -86,7 +87,8 @@ feature at the normal URL — no second stack, no proxy. The branch checked out
|
|
|
86
87
|
the primary checkout **is** the lock: exactly one spec is live at a time, and
|
|
87
88
|
`/spec-live main` hands the instance back (fixes you make while live commit
|
|
88
89
|
straight onto the branch; `/spec-complete` is live-aware and lands them). Rule of
|
|
89
|
-
thumb: **live overlay is the light default for code-only
|
|
90
|
+
thumb: **live overlay is the light default for *testing* a code-only spec** — it
|
|
91
|
+
is not how work gets started, and no lifecycle skill invokes it; it *refuses*
|
|
90
92
|
stateful ones (`Stack: worktree + docker`, or a branch touching migrations) — keep
|
|
91
93
|
`/spec-connect` + a Docker stack for those, and for genuinely parallel testing.
|
|
92
94
|
Beneath it, `skitterspec spec-env live <take|release|abort|status>` is the engine.
|
|
@@ -118,6 +120,21 @@ With no provider installed the seams are empty and every skill behaves as a plai
|
|
|
118
120
|
filesystem workflow. See the provider package's own docs for its config and field
|
|
119
121
|
reference.
|
|
120
122
|
|
|
123
|
+
**Release gating (opt-in, config-gated).** With
|
|
124
|
+
`specs/.core/gating.config.json` present, every spec `/spec`, `/spec-bug` and
|
|
125
|
+
`/spec-hotfix` write carries a `> **Gating:**` header recording one decision:
|
|
126
|
+
does this ship behind a feature flag, or land live? The value is a **flag name**,
|
|
127
|
+
or **`none: <one-line reason>`** — and the reason half is the load-bearing part,
|
|
128
|
+
because `none: additive, nothing to revert` is a decision a reviewer can argue
|
|
129
|
+
with while a bare `none` is a shrug and a missing line is an oversight.
|
|
130
|
+
|
|
131
|
+
Skitterspec bakes in **the offer, never the mechanism**: it asks, cites the
|
|
132
|
+
project's own doc (`guidance` in that config), and records the answer. It never
|
|
133
|
+
reads your flag code. `skitterspec gating check` reports specs with no decision
|
|
134
|
+
and **always exits 0** — it reads only `backlog/` and `in-progress/`, so specs
|
|
135
|
+
finished before you adopted gating are out of range by construction. With the
|
|
136
|
+
config absent nothing appears at all: no question, no header, no check.
|
|
137
|
+
|
|
121
138
|
## Project conventions (fill this in)
|
|
122
139
|
|
|
123
140
|
The spec skills tell you to run "your project's typecheck and test commands" and
|
|
@@ -160,6 +177,10 @@ Every spec header carries:
|
|
|
160
177
|
to `git config user.name`).
|
|
161
178
|
- `> **Developer:**` — who implements it (`—` until `/spec-start` starts work, then
|
|
162
179
|
set to `git config user.name`; `/spec-bug` sets it immediately).
|
|
180
|
+
- `> **Gating:**` — the release-gating decision,
|
|
181
|
+
**only when `specs/.core/gating.config.json` exists**: a flag name, or
|
|
182
|
+
`none: <one-line reason>`. Absent entirely in a project that has not adopted
|
|
183
|
+
gating.
|
|
163
184
|
|
|
164
185
|
Every spec also has a **State log** table — the audit trail of folder/status
|
|
165
186
|
transitions. Each lifecycle skill appends exactly one row when it changes state:
|
|
@@ -49,7 +49,17 @@ not write the spec until this is resolved.
|
|
|
49
49
|
`worktree + docker` only when it must. This sets the `> **Stack:**` header
|
|
50
50
|
that `/spec-start` acts on (it can be escalated later). Skip when isolation
|
|
51
51
|
isn't enabled — leave the default `worktree`.
|
|
52
|
-
10. **
|
|
52
|
+
10. **Release gating** *(only when `specs/.core/gating.config.json` exists)* —
|
|
53
|
+
should this ship behind a feature flag, or land live?
|
|
54
|
+
**Offer, don't impose**: the user decides and you raise it, so a spec
|
|
55
|
+
never reaches
|
|
56
|
+
`/spec-complete` with the question unasked. Cite the project's own
|
|
57
|
+
`guidance` path from that config when it names one — skitterspec knows
|
|
58
|
+
nothing about how this project does flags, and must not guess. Record the
|
|
59
|
+
answer **either way**: a flag name, or `none: <one-line reason>`. "No" is a
|
|
60
|
+
decision and belongs in the header; silence is not. Skip entirely when the
|
|
61
|
+
config is absent — that project does not use flags.
|
|
62
|
+
11. **Open questions** — anything still undecided.
|
|
53
63
|
|
|
54
64
|
Stop grilling when there are no unresolved branches that would change the spec.
|
|
55
65
|
Briefly play back the agreed understanding before writing.
|
|
@@ -108,6 +118,9 @@ the codebase, link rather than duplicate):
|
|
|
108
118
|
> **Area:** <comma-separated files/modules this touches>
|
|
109
119
|
> **Stack:** <worktree — or "worktree + docker" if it touches the DB/stateful
|
|
110
120
|
> services; only acted on when isolation is enabled — see Phase A item 9>
|
|
121
|
+
> **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
|
|
122
|
+
> is configured; omit the line entirely otherwise. An empty value or a bare
|
|
123
|
+
> "none" is not a valid outcome — see Phase A item 10>
|
|
111
124
|
|
|
112
125
|
## Problem
|
|
113
126
|
|
|
@@ -211,13 +224,14 @@ Rules for the spec body:
|
|
|
211
224
|
phase index.
|
|
212
225
|
- **Honour project conventions** when writing tasks — reference the relevant
|
|
213
226
|
`.claude/rules/*.md` rather than re-explaining them.
|
|
214
|
-
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
227
|
+
-
|
|
228
|
+
**The `## Impact` table is derived from Phase A items 3 (Affected areas) & 5 (Data/API impact)**
|
|
229
|
+
— a structured place to record what those already surface, not new grilling.
|
|
230
|
+
It is the scannable substitute for spelling impact out in prose: name the
|
|
231
|
+
surfaces (endpoints, schemas, DB tables, domain objects, routes, business
|
|
232
|
+
rules) instead of describing them, keep `Detail` terse, and let it — not
|
|
233
|
+
paragraphs — carry the blast radius. It complements the `Area:` header
|
|
234
|
+
(files) by naming behavioural surfaces.
|
|
221
235
|
- **Changelog** is mandatory and lives in the spec. Every later decision or
|
|
222
236
|
course-correction gets a dated one-line entry. Convert relative dates to
|
|
223
237
|
absolute.
|
|
@@ -242,6 +256,15 @@ run `skitterspec spec-env up <name>` to add Docker to an existing worktree). If
|
|
|
242
256
|
`env.config.json` is absent, isolation is off — leave the default `worktree` and
|
|
243
257
|
finish as above.
|
|
244
258
|
|
|
259
|
+
## Phase D2 — record the gating decision (only if configured)
|
|
260
|
+
|
|
261
|
+
**Only when `specs/.core/gating.config.json` exists.** Make sure the
|
|
262
|
+
`> **Gating:**` header carries the Phase A item 10 answer — a flag name, or
|
|
263
|
+
`none: <reason>` using the config's `default` wording if it sets one. Nothing is
|
|
264
|
+
provisioned or enforced by this: the header exists so the decision is
|
|
265
|
+
**on the record and reviewable**, and `skitterspec gating check` reports a spec that has
|
|
266
|
+
none. It never blocks. If the config is absent, do not write the line at all.
|
|
267
|
+
|
|
245
268
|
## Phase E — link to a ticketing provider (only if one is installed)
|
|
246
269
|
|
|
247
270
|
**Only when a ticketing provider is installed and configured** (it ships the
|
|
@@ -51,24 +51,12 @@ provision from — you'll flesh it out in §4:
|
|
|
51
51
|
- Run `skitterspec spec-env up bug-<name>` (the `spec-env` CLI engine). It prints
|
|
52
52
|
the `git worktree add … -b bug/<name>` command (a branch forked from `main`),
|
|
53
53
|
the worktree path, the opener, and any `in the worktree, run:` bootstrap steps.
|
|
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`.
|
|
54
|
+
- Run the printed commands in order. The plan **commits the stub first** — the
|
|
55
|
+
worktree forks from `main`'s last commit, so the stub has to be in it — and
|
|
56
|
+
then adds the worktree. Nothing to move afterwards: the spec is already there.
|
|
57
|
+
The commit is planned, not silent; it appears in the printed plan above the
|
|
58
|
+
`git worktree add`, and `spec-env up` refuses outright if anything *other* than
|
|
59
|
+
this spec is uncommitted.
|
|
72
60
|
- **Bootstrap the worktree.** A fresh worktree has no installed dependencies and
|
|
73
61
|
none of the repo's gitignored files (`.env`, local overrides). Run the printed
|
|
74
62
|
`in the worktree, run:` steps (file seeding, then `setup`) in order, before
|
|
@@ -117,6 +105,8 @@ because work is already underway. Keep it lean:
|
|
|
117
105
|
> **Developer:** <git user.name — you, since you're fixing it now>
|
|
118
106
|
> **Raised:** <YYYY-MM-DD (today)>
|
|
119
107
|
> **Area:** <files/modules>
|
|
108
|
+
> **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
|
|
109
|
+
> is configured; omit the line entirely otherwise>
|
|
120
110
|
|
|
121
111
|
## Symptom
|
|
122
112
|
|
|
@@ -178,6 +168,19 @@ fix narrative and decisions — keep them separate.
|
|
|
178
168
|
|
|
179
169
|
|
|
180
170
|
|
|
171
|
+
### Release gating (only when configured)
|
|
172
|
+
|
|
173
|
+
**Only when `specs/.core/gating.config.json` exists.** Before writing the spec,
|
|
174
|
+
ask: should this fix ship behind a feature flag, or land live?
|
|
175
|
+
**Offer, don't impose** — cite the project's own `guidance` path when the config
|
|
176
|
+
names one, and
|
|
177
|
+
record the answer either way, as a flag name or `none: <one-line reason>`.
|
|
178
|
+
|
|
179
|
+
A bug fix ships in the next release exactly like a feature, so it gets the same
|
|
180
|
+
question rather than an assumption. A risky rewrite of a broken path is precisely
|
|
181
|
+
where a kill-switch earns its keep; a one-line null check is precisely where
|
|
182
|
+
`none:` is the honest answer. Skip entirely when the config is absent.
|
|
183
|
+
|
|
181
184
|
## 5. Drive to GREEN
|
|
182
185
|
|
|
183
186
|
- Implement the **minimal, root-cause** fix. Match surrounding code; honour all
|
|
@@ -79,8 +79,22 @@ directly (the old `/spec-env-down` skill is gone — teardown is folded in here)
|
|
|
79
79
|
main` first to free the canonical ports.
|
|
80
80
|
2. `skitterspec spec-env dev down <name>` — stop its host dev servers.
|
|
81
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,
|
|
83
|
-
|
|
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
|
|
97
|
+
bypassed.
|
|
84
98
|
|
|
85
99
|
Not because git refuses — it does not. `git worktree remove` **succeeds** on the
|
|
86
100
|
tree you are standing in, and that is the problem: the directory vanishes under
|
|
@@ -34,6 +34,23 @@ Before marking complete, confirm the work is actually finished:
|
|
|
34
34
|
is its own, and step 4 commits that.
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
### Release gating (only when configured)
|
|
38
|
+
|
|
39
|
+
**Only when `specs/.core/gating.config.json` exists.** Run
|
|
40
|
+
`skitterspec gating check <spec>` and report what it says. This is the last point
|
|
41
|
+
at which the question can still be answered cheaply, which is exactly why it is
|
|
42
|
+
asked here — a spec that reaches `Complete` with no decision recorded is the gap
|
|
43
|
+
the feature exists to close.
|
|
44
|
+
|
|
45
|
+
**Never refuse to complete over it.** Say plainly that no decision was recorded,
|
|
46
|
+
offer to record one now, and finish either way. The work is done; withholding the
|
|
47
|
+
status flip would punish the wrong thing.
|
|
48
|
+
|
|
49
|
+
**This check is advisory.** It reports; it never refuses, and nothing below is
|
|
50
|
+
conditional on it. A spec written before the project adopted gating has no header
|
|
51
|
+
and is not broken — turning this into a gate would accuse the very specs the
|
|
52
|
+
feature was designed not to disturb.
|
|
53
|
+
|
|
37
54
|
## 3. Update the spec
|
|
38
55
|
|
|
39
56
|
- Tick all completed tasks in the phase files; flip every finished phase-file
|
|
@@ -63,9 +80,10 @@ git add specs/ && git commit -m "chore(spec): complete <name>"
|
|
|
63
80
|
```
|
|
64
81
|
|
|
65
82
|
Step 2 established the tree was otherwise clean, so this commits exactly the
|
|
66
|
-
status flip and the move — nothing of yours rides along.
|
|
67
|
-
6 land:** `integrate` refuses a dirty worktree, so
|
|
68
|
-
skill would block on the very edits it just made.
|
|
83
|
+
status flip and the move — nothing of yours rides along.
|
|
84
|
+
**This is what lets step 6 land:** `integrate` refuses a dirty worktree, so
|
|
85
|
+
without committing here the skill would block on the very edits it just made.
|
|
86
|
+
Do not `git push`.
|
|
69
87
|
|
|
70
88
|
## 5. Report
|
|
71
89
|
|
|
@@ -172,8 +190,22 @@ worktree and branch are being kept, and go straight to sub-step 4. Mention
|
|
|
172
190
|
2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
|
|
173
191
|
no-op when none are running / configured).
|
|
174
192
|
**Standing in the worktree? Leave it before you tear it down.** If this
|
|
175
|
-
session's cwd is inside the spec's own worktree,
|
|
176
|
-
|
|
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
|
|
208
|
+
bypassed.
|
|
177
209
|
|
|
178
210
|
Not because git refuses — it does not. `git worktree remove` **succeeds** on the
|
|
179
211
|
tree you are standing in, and that is the problem: the directory vanishes under
|
|
@@ -192,16 +224,16 @@ only ordering that survives.
|
|
|
192
224
|
worktree without `--force`), so if it *does* refuse, relay that and stop
|
|
193
225
|
rather than reaching for `--force`.
|
|
194
226
|
|
|
195
|
-
**If the plan prints a `remote branch — confirm with the user first:`
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
227
|
+
**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
|
|
230
|
+
this for a branch that has **landed**, so say so plainly when you ask — the
|
|
231
|
+
commits are on the base branch (or captured by the deploy tag), so deleting
|
|
232
|
+
the remote branch loses nothing. On a yes, run the printed
|
|
233
|
+
`git push <remote> --delete <branch>`; on a no, leave it and say the remote
|
|
234
|
+
branch is still there. Never fold it into the `run these:` batch — a project
|
|
235
|
+
that wants it unattended sets `teardown.deleteRemoteBranch: "always"` in
|
|
236
|
+
`env.config.json`, and then it appears in `run these:` instead of here.
|
|
205
237
|
4. **Reap orphaned test-DB volumes:** run `skitterspec spec-env prune`. It lists
|
|
206
238
|
Docker volumes in the repo namespace that belong to **no live spec** (no
|
|
207
239
|
worktree) — leftovers from declined/aborted teardowns, manual
|