@skitterbyte/skitterspec-linear 12.0.0 → 14.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 +296 -10
- package/README.md +32 -2
- package/assets/claude-md-section.md +38 -2
- package/assets/commands/spec-connect.md +2 -2
- package/assets/commands/spec-live.md +2 -2
- package/assets/core/SETUP.md +21 -3
- package/assets/core/env.config.json.example +7 -3
- package/assets/core/env.config.md +90 -30
- package/assets/core/linear.config.md +58 -0
- package/assets/hooks/review-gate.js +141 -0
- package/assets/review/page.html +1787 -0
- package/assets/rules/spec-planning.md +250 -10
- package/assets/rules/spec-reports.md +321 -0
- package/assets/skills/spec/SKILL.md +33 -5
- package/assets/skills/spec-bug/SKILL.md +172 -9
- package/assets/skills/spec-cancel/SKILL.md +98 -21
- package/assets/skills/spec-claim/SKILL.md +114 -0
- package/assets/skills/spec-complete/SKILL.md +94 -25
- package/assets/skills/spec-diff/SKILL.md +678 -0
- package/assets/skills/spec-hotfix/SKILL.md +172 -11
- package/assets/skills/spec-init/SKILL.md +56 -7
- package/assets/skills/spec-linear-setup/SKILL.md +55 -1
- package/assets/skills/spec-list/SKILL.md +218 -0
- package/assets/skills/spec-next/SKILL.md +419 -7
- package/assets/skills/spec-push/SKILL.md +32 -8
- package/assets/skills/spec-review/SKILL.md +40 -5
- package/assets/skills/spec-reviewed/SKILL.md +258 -0
- package/assets/skills/spec-start/SKILL.md +386 -106
- package/assets/skills/spec-status/SKILL.md +24 -2
- package/assets/skills/spec-sync/SKILL.md +40 -4
- package/assets/skills/spec-to-main/SKILL.md +28 -6
- package/package.json +11 -7
- package/src/cli.js +1808 -89
- package/src/env/building.js +143 -0
- package/src/env/commitcmd.js +108 -0
- package/src/env/config.js +58 -9
- package/src/env/hooks.js +117 -0
- package/src/env/provision.js +54 -15
- package/src/env/proxy.js +34 -1
- package/src/env/render.js +3 -12
- package/src/env/resolve.js +295 -9
- package/src/env/review.js +1536 -0
- package/src/env/serve.js +573 -0
- package/src/env/teardown.js +13 -6
- package/src/init.js +150 -1
- package/src/vendor/linear/api.js +104 -1
- package/src/vendor/linear/cli-sync.js +854 -17
- package/src/vendor/linear/config.js +8 -0
- package/src/vendor/linear/credentials.js +94 -0
- package/src/vendor/linear/doctor.js +35 -0
- package/src/vendor/linear/identity.js +105 -0
- package/src/vendor/linear/mcp.js +26 -0
- package/src/vendor/sync-core/index.js +6 -2
- package/src/vendor/sync-core/src/compare.js +49 -3
- package/src/vendor/sync-core/src/normalize.js +30 -0
- package/src/vendor/sync-core/src/push.js +11 -1
- package/src/vendor/sync-core/src/write.js +38 -0
- package/LICENSE +0 -21
|
@@ -5,22 +5,30 @@ description: Put a spec in flight — provision its branch, move it to in-progre
|
|
|
5
5
|
|
|
6
6
|
# /spec-start — put a spec in flight
|
|
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
|
One checkout, one spec in flight. This skill is how a spec gets there:
|
|
9
14
|
provision, move it to `in-progress`, refresh the tracker, then hand straight on
|
|
10
15
|
to `/spec-next` for phase 1. Continuing a spec afterwards is `/spec-next`;
|
|
11
16
|
finishing it is `/spec-complete`.
|
|
12
17
|
|
|
13
|
-
## 1. The gate —
|
|
18
|
+
## 1. The gate — what each mode demands of the tree
|
|
14
19
|
|
|
15
20
|
**Check this first, before resolving anything or touching a file.** What the
|
|
16
21
|
gate demands depends on the mode, because the two modes hold work in different
|
|
17
22
|
places — read `mode` from `specs/.core/env.config.json` (default `worktree`).
|
|
18
23
|
|
|
19
|
-
**`worktree` mode —
|
|
20
|
-
|
|
21
|
-
parallelism the mode exists for.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
**`worktree` mode — there is no tree gate.** The spec is built in its own
|
|
25
|
+
worktree, so neither another spec being in flight nor its uncommitted files are a
|
|
26
|
+
conflict; both are the parallelism the mode exists for. `git worktree add`
|
|
27
|
+
carries nothing and forks from a commit, and the one thing this run writes into
|
|
28
|
+
the checkout — the spec's own commit, which `spec-env up` plans for you — names
|
|
29
|
+
its paths on both the `add` and the `commit`, so it cannot reach a file that is
|
|
30
|
+
not this spec's. Work belonging to someone else is **reported and left alone**,
|
|
31
|
+
never a refusal. Nothing is switched here and nothing is parked.
|
|
24
32
|
|
|
25
33
|
**`checkout` mode — the workbench must be free**: on the base branch (`main`, or
|
|
26
34
|
the configured `baseBranch`) and clean, since the branch is built right here and
|
|
@@ -37,21 +45,30 @@ and a half-built phase are each a decision someone must make deliberately — an
|
|
|
37
45
|
the cost of guessing is another spec's work moved without its author asking. A
|
|
38
46
|
refusal costs one command; the alternative can cost an afternoon.
|
|
39
47
|
|
|
40
|
-
**
|
|
41
|
-
|
|
42
|
-
what it says rather than deciding for yourself:
|
|
48
|
+
**What `spec-env up` does with the tree**, in both modes — relay what it says
|
|
49
|
+
rather than deciding for yourself:
|
|
43
50
|
|
|
44
|
-
| What it found |
|
|
45
|
-
|
|
46
|
-
| clean | provisions
|
|
47
|
-
| every path
|
|
48
|
-
|
|
|
51
|
+
| What it found | `worktree` | `checkout` |
|
|
52
|
+
|---------------|-------------|-------------|
|
|
53
|
+
| clean | provisions | provisions |
|
|
54
|
+
| every path is this spec's | commits those paths **first**, then forks | same, then switches |
|
|
55
|
+
| some path is not | commits this spec's, provisions, **reports the rest** | refuses, naming them |
|
|
49
56
|
|
|
50
57
|
That is membership in an exactly-known set — the spec's own folder plus the
|
|
51
58
|
project's `spec.companionPaths` — and **not** a judgement about whether the
|
|
52
|
-
changes look important.
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
changes look important. It never decides that.
|
|
60
|
+
|
|
61
|
+
**The last row is the only real difference, and it is mechanical.**
|
|
62
|
+
`git switch -c` carries the working tree onto the new branch, so in
|
|
63
|
+
`checkout` mode a colleague's files really would be moved without them asking.
|
|
64
|
+
`git worktree add` carries nothing, so in `worktree` mode the same files are
|
|
65
|
+
simply not this run's business — and refusing over them fired on the commonest
|
|
66
|
+
tree this workflow produces: a second spec authored while the first is still
|
|
67
|
+
uncommitted. When it reports them, say how many and whose in the `Untouched` row
|
|
68
|
+
and **keep the verdict `✅`** — nothing went wrong.
|
|
69
|
+
|
|
70
|
+
When it plans the commit, the paths are printed above the commands, so run them
|
|
71
|
+
as printed; when it refuses, relay the reason and stop.
|
|
55
72
|
|
|
56
73
|
It also refuses a **clean** tree whose spec is not in the commit the worktree
|
|
57
74
|
would fork from — otherwise you get a branch missing the very spec it is for.
|
|
@@ -71,69 +88,131 @@ would fork from — otherwise you get a branch missing the very spec it is for.
|
|
|
71
88
|
probably still exists, so this is a re-attach: say so rather than reporting a
|
|
72
89
|
fresh start, and skip the housekeeping that is already done.
|
|
73
90
|
|
|
91
|
+
## 2b. Bring the review server up — from here, before anything else
|
|
92
|
+
|
|
93
|
+
**Only when the project has per-spec isolation** (`specs/.core/env.config.json`
|
|
94
|
+
present). Without it there is no review server and this step does not exist —
|
|
95
|
+
skip it in silence rather than explaining an absence.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
skitterspec spec-env review serve --host 0.0.0.0
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Here is the point.** Right now this session is standing in the
|
|
102
|
+
**primary checkout**, and in a moment step 3 `cd`s into a worktree and stays
|
|
103
|
+
there. A
|
|
104
|
+
daemon started after that `cd` is started *by the worktree's copy of the code* —
|
|
105
|
+
and when `/spec-complete` removes that worktree, the daemon keeps answering on
|
|
106
|
+
its port and fails on every page it is asked for, for every spec. The engine
|
|
107
|
+
defends against that now, but the cheapest fix is to never create the situation:
|
|
108
|
+
start it while you are still somewhere that outlives the spec.
|
|
109
|
+
|
|
110
|
+
It is also what lets **several specs be reviewed at once**. Reviews all render
|
|
111
|
+
into the primary checkout's `.spec-env/reviews/`, so one server serves every
|
|
112
|
+
provisioned spec — including specs another agent is building in another
|
|
113
|
+
worktree. Starting it here means that one server belongs to the checkout none of
|
|
114
|
+
them can delete.
|
|
115
|
+
|
|
116
|
+
**Say nothing when it is already up.** The usual outcome is adoption — a server
|
|
117
|
+
is running and this changes nothing — and a line per `/spec-start` about a
|
|
118
|
+
daemon nobody asked about is the narration `.claude/rules/spec-reports.md`
|
|
119
|
+
forbids. Speak only if it could not start.
|
|
120
|
+
|
|
121
|
+
**Never fatal, never a gate.** A busy port, no network address, a refused
|
|
122
|
+
spawn — say it in one line and **carry on**; provisioning is not conditional on
|
|
123
|
+
it, and the page falls back to its `file://` URL exactly as it does today.
|
|
124
|
+
|
|
125
|
+
**`--host 0.0.0.0` is the deliberate half.** It binds the server to this
|
|
126
|
+
machine's network addresses so the page opens on a phone, and the engine mints a
|
|
127
|
+
token with that bind as its only guard. On a machine you would rather not expose,
|
|
128
|
+
drop the flag — the server is then reachable from this machine alone and the
|
|
129
|
+
render says so. On **`--plan`** this step does not run at all.
|
|
130
|
+
|
|
74
131
|
## 3. Build its branch
|
|
75
132
|
|
|
76
133
|
### `worktree` mode
|
|
77
134
|
|
|
135
|
+
**One path. There is no branching here and none should be added back.**
|
|
136
|
+
|
|
78
137
|
1. **Provision.** Run `skitterspec spec-env up <name>` — a planner, so run the
|
|
79
|
-
`to provision, run:` commands it prints and confirm they succeeded
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`
|
|
121
|
-
`
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
`to provision, run:` commands it prints and confirm they succeeded.
|
|
139
|
+
|
|
140
|
+
2. **Trust the worktree.** `spec-env up` wrote the printed `trusted:` root into
|
|
141
|
+
`.claude/settings.local.json`, but that file does not hot-reload in this
|
|
142
|
+
session — run **`/add-dir <trusted root>`** before editing into the worktree,
|
|
143
|
+
or the first write prompts. This is not tab machinery: worktrees live outside
|
|
144
|
+
the checkout, and the trust entry is what stops the prompt.
|
|
145
|
+
|
|
146
|
+
3. **Bootstrap it, and move into it.** The `cd` is in the command itself, and it
|
|
147
|
+
**moves this session** — that is what it is for, not a side effect of it. The
|
|
148
|
+
Bash working directory persists between calls, so from here on this session is
|
|
149
|
+
standing in the worktree, which is what lets a bare `/spec-next` resolve the
|
|
150
|
+
spec on its own:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
cd "<worktreePath>" && <the planner's "then, in the worktree, run:" steps>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Run the seeding steps before `setup`: a fresh worktree has no dependencies and
|
|
157
|
+
none of the repo's gitignored files, so hooks, typechecks and tests fail until
|
|
158
|
+
both have happened.
|
|
159
|
+
|
|
160
|
+
**Then confirm the move landed — never assume it.** Ask for a positive signal
|
|
161
|
+
rather than reading silence as success (`.claude/rules/negative-checks.md`
|
|
162
|
+
rule 1): run `skitterspec spec-env resolve` with **no argument** and read the
|
|
163
|
+
`spec:` line it prints.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
skitterspec spec-env resolve # must name this spec
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Three states, not two. It names this spec → carry on. It names something else,
|
|
170
|
+
or resolves nothing → **the `cd` did not take**. Say so plainly and fall back
|
|
171
|
+
to the stop-here ending in step 6, printing the path so the operator can open
|
|
172
|
+
a session there themselves; do not build a phase from a session whose location
|
|
173
|
+
you could not confirm. A failed `cd` leaves you in the primary checkout on the
|
|
174
|
+
base branch, where a phase's worth of code looks entirely normal at the time.
|
|
175
|
+
|
|
176
|
+
4. **Housekeep with `git -C <worktreePath>`** — step 4 below, against the
|
|
177
|
+
worktree.
|
|
178
|
+
|
|
179
|
+
**Keep the `-C` prefix**, even though the session is inside the worktree now
|
|
180
|
+
and a bare `git` would usually do the same thing. It is immune to the one
|
|
181
|
+
failure this sequence can have — a `cd` that silently did not take — where a
|
|
182
|
+
bare `git` would instead write the spec's move into the primary checkout on
|
|
183
|
+
the base branch. It costs nothing and removes a whole failure mode, so do not
|
|
184
|
+
tidy it away.
|
|
185
|
+
|
|
186
|
+
5. **Print the worktree path.** What happens next is step 6 — it is offered
|
|
187
|
+
there, not decided here.
|
|
188
|
+
|
|
189
|
+
**The session moves into the worktree, and nothing opens a window.** Those are
|
|
190
|
+
two different claims and both are load-bearing. The move is real, and the `cd` in
|
|
191
|
+
step 3 is its whole mechanism — no tool call, because an approval prompt is
|
|
192
|
+
unusable on a phone and leaves the session stuck. Nothing is *spawned*: no new
|
|
193
|
+
terminal, no tab, no editor sent anywhere, because that machinery had nothing
|
|
194
|
+
left to do and was removed deliberately.
|
|
195
|
+
|
|
196
|
+
**Do not move the branch into this checkout**, and do not ask the operator to.
|
|
197
|
+
`/spec-live` is for testing a finished-enough spec on the already-running dev
|
|
198
|
+
server; it is not how work gets started.
|
|
199
|
+
|
|
200
|
+
**`/spec-next`'s refusal is unchanged by this.** Its rule 2 — "the worktree you
|
|
201
|
+
are standing in" — is what answers afterwards, and step 3 is what puts the session
|
|
202
|
+
there; nothing about rules 1 to 3 is loosened, and it must stay that way, because
|
|
203
|
+
the refusal exists so the wrong branch is never built. What changed is where the
|
|
204
|
+
session stands, not how weakly the rules read: a bare `/spec-next` typed from
|
|
205
|
+
somewhere that is neither a worktree nor a live checkout still refuses exactly as
|
|
206
|
+
it did. `--worktree <path>` survives untouched beside it — it answers before those
|
|
207
|
+
rules and cannot be reached by guessing.
|
|
208
|
+
|
|
209
|
+
**And the `cd` is a convenience, not the only thing holding this together.**
|
|
210
|
+
`/spec-next`'s **rule 4** asks the engine for the sole provisioned spec, so a
|
|
211
|
+
bare `/spec-next` resolves this spec from anywhere in the repo — after a
|
|
212
|
+
`/clear`, from a new tab, tomorrow morning. That is what makes step 6's
|
|
213
|
+
stop-here ending an honest offer rather than a promise only this session can
|
|
214
|
+
keep. Do not delete rule 4 as redundant with the `cd`: the `cd` is session state,
|
|
215
|
+
and rule 4 is what is left once it is gone.
|
|
137
216
|
|
|
138
217
|
### `checkout` mode
|
|
139
218
|
|
|
@@ -145,20 +224,149 @@ refusal and stop.
|
|
|
145
224
|
## 4. Move the spec into development
|
|
146
225
|
|
|
147
226
|
**Do this before you report anything**, so no path can end with a provisioned
|
|
148
|
-
worktree and a spec still reading `Ready` in `specs/backlog/`.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
in `checkout` mode the branch is already here.
|
|
227
|
+
worktree and a spec still reading `Ready` in `specs/backlog/`. In `worktree` mode
|
|
228
|
+
run it against the worktree with `git -C <worktreePath>`; in `checkout` mode the
|
|
229
|
+
branch is already here.
|
|
152
230
|
|
|
153
231
|
- `git mv "specs/backlog/<name>" "specs/in-progress/<name>"` if it isn't there
|
|
154
232
|
already (`mkdir -p specs/in-progress` first). Use `git mv` to keep history.
|
|
155
233
|
- Set the **Status** header: `> **Status:** In Progress — Phase 1 (started <YYYY-MM-DD>)`.
|
|
156
234
|
- Set **Developer** if it is still `—` (`git config user.name`).
|
|
235
|
+
|
|
236
|
+
**Only when all three hold**: `specs/.core/linear.config.json` exists, its
|
|
237
|
+
`sync.fieldOwnership` includes `assignee`, and the spec carries a
|
|
238
|
+
`linear_identifier`. Any one missing → skip this step silently and carry on; a
|
|
239
|
+
project that has not opted in must see no trace of assignment.
|
|
240
|
+
|
|
241
|
+
**Never blocks, never fails the skill.** Everything below is best-effort: the
|
|
242
|
+
branch is provisioned and the spec is moving either way, and an unassigned issue
|
|
243
|
+
is a cosmetic gap that `/spec-claim` closes later.
|
|
244
|
+
|
|
245
|
+
1. **Work out who you are.** Run `skitterspec spec-sync whoami --json`.
|
|
246
|
+
- `ok: true` → use `id` and `name`. Nothing to ask.
|
|
247
|
+
- `source: "mcp"` or the command reports no API key → call the discovered
|
|
248
|
+
user-read tool with `me`, then cache it:
|
|
249
|
+
`skitterspec spec-sync whoami --set <id> --name "<name>"`.
|
|
250
|
+
- `ok: false` → identity is **unknown**, which is an ordinary state (a shared
|
|
251
|
+
or bot key, an offline machine). Go to step 2.
|
|
252
|
+
2. **Unknown identity — three states, not two.** Decide by what is actually
|
|
253
|
+
reachable, and route the third to inaction:
|
|
254
|
+
- **Linear reachable *and* this is an interactive session** → offer a short
|
|
255
|
+
user search (`skitterspec spec-sync users <name-or-email>`, or the
|
|
256
|
+
user-list tool on MCP), let the operator pick, and offer to cache it with
|
|
257
|
+
`whoami --set` so this is asked once per machine rather than once per spec.
|
|
258
|
+
- **Not reachable, or not interactive** → say so in one line
|
|
259
|
+
(`assignment skipped — no Linear identity`) and **carry on**. Do not prompt
|
|
260
|
+
for something you could not act on, and never stall a `/spec-start` on it.
|
|
261
|
+
3. **Record it on the spec** — through the engine, never by hand-editing
|
|
262
|
+
frontmatter:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
skitterspec spec-sync assign <spec> --to <user-id> --name "<display name>"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
4. **Leave `> **Developer:**` as `git config user.name`** — the step above this
|
|
269
|
+
seam already set it, and this seam must not overwrite it with the tracker's
|
|
270
|
+
display name.
|
|
271
|
+
|
|
272
|
+
Those two names are the same person and often not the same string, and the
|
|
273
|
+
git one is the one everything else in the spec already uses: `Author:`, every
|
|
274
|
+
**State log** `By` row, and every commit. Writing the tracker's name into this
|
|
275
|
+
one field would leave a spec whose own audit trail contradicts its header —
|
|
276
|
+
a worse problem than the one it would solve. Anyone wanting a different name
|
|
277
|
+
on their specs sets `git config user.name`, and it stays consistent
|
|
278
|
+
everywhere.
|
|
279
|
+
|
|
280
|
+
The header names **who is building this**; the stamp in step 3 names
|
|
281
|
+
**which tracker account it is assigned to**. They answer different questions,
|
|
282
|
+
so they need not be the same string. The one place that reasoning does not hold
|
|
283
|
+
is handing a spec to *someone else* — there is no local git name for them —
|
|
284
|
+
and that case belongs to `/spec-claim --to`, which sets the header from the
|
|
285
|
+
tracker deliberately.
|
|
286
|
+
|
|
287
|
+
**Nothing is pushed here.** `assign` writes the repo only, and the refresh these
|
|
288
|
+
skills already run sends it. Assignment is an ordinary field of the projection,
|
|
289
|
+
not a side errand with its own network call.
|
|
290
|
+
|
|
291
|
+
**There is no unassign step anywhere.** The projection derives the assignee from
|
|
292
|
+
the spec's lifecycle bucket, so `/spec-complete` and `/spec-cancel` release the
|
|
293
|
+
issue through the push they already make. The stamp deliberately stays in the
|
|
294
|
+
file: who actioned the work outlives who is currently holding it.
|
|
295
|
+
|
|
157
296
|
- Append a **State log** row: `| <YYYY-MM-DD> | In Progress | in-progress | <git user.name> |`.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
297
|
+
|
|
298
|
+
**Only when `specs/.core/linear.config.json` exists** and the spec's overview
|
|
299
|
+
carries a `linear_identifier`. Either missing → **skip**, in one line
|
|
300
|
+
(`not linked to Linear — /spec-push to mirror it`), and carry on. Nothing else in
|
|
301
|
+
this step changes.
|
|
302
|
+
|
|
303
|
+
**Refresh the mirror now, without asking.** Run `/spec-push`. The spec has just
|
|
304
|
+
moved to `in-progress/` and been stamped with a developer — two real state
|
|
305
|
+
changes, and the tracker is a generated mirror of them. It costs one engine call
|
|
306
|
+
and no model tokens when a Linear API key is set (see `apply.transport` in
|
|
307
|
+
`linear.config.md`).
|
|
308
|
+
|
|
309
|
+
- **Never mint.** An unlinked spec is skipped, not created. A spec kept
|
|
310
|
+
deliberately local should stay that way — `/spec-push` is how someone opts in.
|
|
311
|
+
- **Never fatal.** If the push fails — offline, no key, a Linear error — say so
|
|
312
|
+
and **finish the operation anyway**. The spec is in flight in the repo
|
|
313
|
+
regardless; the mirror is disposable and the next push repairs it. Do not roll
|
|
314
|
+
the provisioning back, and do not stop to ask.
|
|
315
|
+
- **Say what happened** in the skill's report: mirror updated, skipped as
|
|
316
|
+
unlinked, or failed with the reason.
|
|
317
|
+
|
|
318
|
+
### Why it sits here
|
|
319
|
+
|
|
320
|
+
Pinned **after the `git mv` and before the commit**, and both halves matter:
|
|
321
|
+
|
|
322
|
+
- **After the move**, because the projection reads a spec's workflow state from
|
|
323
|
+
its folder bucket. Push while the folder is still in `backlog/` and the issue
|
|
324
|
+
is set to the state the spec is *leaving*.
|
|
325
|
+
- **Before the commit**, because the push stamps ids into the spec and writes a
|
|
326
|
+
snapshot under `specs/.core/`. The `git add` that follows sweeps both up with
|
|
327
|
+
the status change; push after it instead and those files are left uncommitted —
|
|
328
|
+
which strands a dirty worktree on the hand-off, and makes `spec-env integrate`
|
|
329
|
+
refuse to land the branch.
|
|
330
|
+
|
|
331
|
+
### Why this is not the refresh `/spec-next` already runs
|
|
332
|
+
|
|
333
|
+
It was once reasoned that a push here would send the same thing twice, one commit
|
|
334
|
+
apart, because `/spec-next` refreshes the moment it starts. That holds only in
|
|
335
|
+
`checkout` mode, where `/spec-next` follows immediately. In `worktree` mode the
|
|
336
|
+
spec is built elsewhere and the refresh can be hours away or never come — and
|
|
337
|
+
until it does, the issue sits in its old workflow state with nobody assigned
|
|
338
|
+
while the repo reads `in-progress` with a developer on it.
|
|
339
|
+
|
|
340
|
+
The two pushes are not duplicates either way. This one carries the issue's
|
|
341
|
+
workflow state and its assignee; the one `/spec-next` runs carries phase 1
|
|
342
|
+
starting. Every other lifecycle skill mirrors the state change it makes, and this
|
|
343
|
+
is the seam that stops `/spec-start` being the exception.
|
|
344
|
+
|
|
345
|
+
- **Commit it.** One commit, the spec's own — it records the in-progress state on
|
|
346
|
+
the branch, and sweeps up everything above so the worktree is clean when you
|
|
347
|
+
hand it over.
|
|
348
|
+
|
|
349
|
+
**Publishing that branch is yours to do, and nothing here does it for you.**
|
|
350
|
+
Whenever you want the work somewhere other than this machine:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
git -C <worktreePath> push -u origin <branch>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Print it if it is useful; never run it. This skill once pushed here, justified as
|
|
357
|
+
recording the state "for everyone" and firing the tracker's automation — and
|
|
358
|
+
neither half survives reading. The automation needs `{identifier}` in
|
|
359
|
+
`branch.pattern` (see `env.config.md`) to put an issue id in the branch name, and
|
|
360
|
+
the shipped default carries none, so the tracker has nothing to match. Nor was it
|
|
361
|
+
an invariant: `/spec-bug` provisions a worktree the same way and has never
|
|
362
|
+
pushed, and `/spec-hotfix` forbids it outright. This was the odd one out.
|
|
363
|
+
|
|
364
|
+
**It also kept a guard from ever firing.** `refuseTeardownIfUnpushed` blocks
|
|
365
|
+
teardown on commits that are unpushed and unlanded — which described no branch at
|
|
366
|
+
all while this skill published every one of them at provisioning. It can fire now,
|
|
367
|
+
and the place it does is `/spec-cancel`, where the work really is about to be
|
|
368
|
+
destroyed: that skill relays the refusal and offers both ways out. Nothing to do
|
|
369
|
+
here beyond knowing it is no longer dead code.
|
|
162
370
|
|
|
163
371
|
A spec ideally arrives `Ready` from `/spec`; a `Draft` works too — sanity-check
|
|
164
372
|
it is well-formed first.
|
|
@@ -193,18 +401,75 @@ marks phase 1 started, refreshes the mirror again, builds it with tests and
|
|
|
193
401
|
reports. Do not stop and ask the operator to run it: the branch is here and they
|
|
194
402
|
asked to start the spec.
|
|
195
403
|
|
|
196
|
-
**`worktree` mode —
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
404
|
+
**`worktree` mode — offer it, then do what they say.** Step 3 left this session
|
|
405
|
+
standing in the worktree, so both endings happen right here and neither needs a
|
|
406
|
+
second session opened anywhere. In prose: say the worktree is ready and name its
|
|
407
|
+
path, then ask **"build phase 1 now?"** — recommending that they do, and naming
|
|
408
|
+
both endings so the decline is a real answer rather than a formality.
|
|
409
|
+
|
|
410
|
+
**Never fence that question.** A grey box is read as an artefact to skim rather
|
|
411
|
+
than as something someone is being asked, which is the whole reason
|
|
412
|
+
`.claude/rules/spec-reports.md` bans fencing a message to the reader. This step
|
|
413
|
+
prescribed a fenced block for a long time and an operator read straight past it,
|
|
414
|
+
which is the evidence, not a preference.
|
|
415
|
+
|
|
416
|
+
- **Build it here** — carry on into a bare **`/spec-next`**. Bare is right: the
|
|
417
|
+
session is in the worktree, so its rule 2 resolves this spec with nothing
|
|
418
|
+
passed and nothing guessed.
|
|
419
|
+
- **Stop here** — the operator is left standing in the worktree on a provisioned
|
|
420
|
+
branch, which is a perfectly good place to leave things. Nothing has to be
|
|
421
|
+
reopened or handed anywhere, and `/spec-next` typed an hour later — from this
|
|
422
|
+
session or a fresh one — does exactly what it would have done now.
|
|
423
|
+
|
|
424
|
+
**Ask rather than deciding for them, and mean it.** Provisioning is cheap and
|
|
425
|
+
reversible; a phase build is neither, and one yes should not cover both. A large
|
|
426
|
+
phase is often better started in a session of its own with a whole context budget
|
|
427
|
+
to spend, and only the operator knows which this is. On **`--plan`** this step
|
|
428
|
+
does not run at all — nothing was provisioned to build in.
|
|
429
|
+
|
|
430
|
+
**`--worktree <path>` is still there, and is still not a way around the refusal.**
|
|
431
|
+
It builds a spec the session is *not* standing in, which after step 3 is the
|
|
432
|
+
exception rather than the normal path. Reach for it in exactly two cases: the
|
|
433
|
+
confirm in step 3 reported the `cd` did not take, or you deliberately mean to
|
|
434
|
+
build some other spec's phase from here. A path someone typed is not a path
|
|
435
|
+
anything guessed, which is why it was never a loosening of the refusal and still
|
|
436
|
+
is not.
|
|
437
|
+
|
|
438
|
+
## 7. Report
|
|
439
|
+
|
|
440
|
+
**When step 6 carried on into `/spec-next`, emit no block here.** That skill ends
|
|
441
|
+
with its own, and its `Branch` and `Spec` fields already carry everything this
|
|
442
|
+
one would say. Two blocks for one run is the noise the contract exists to
|
|
443
|
+
remove — this section is for the run that stops at step 6.
|
|
444
|
+
|
|
445
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
446
|
+
the shape; this section carries only what is specific here.
|
|
447
|
+
|
|
448
|
+
**Verdicts**
|
|
449
|
+
|
|
450
|
+
- `✅` — the branch is provisioned, the spec is `in-progress`, the session is
|
|
451
|
+
standing in the worktree.
|
|
452
|
+
- `⚠️` — provisioned, with something worth knowing (dev servers that did not
|
|
453
|
+
come up, a mirror that did not refresh, a missing gating decision).
|
|
454
|
+
- `❌` — provisioning failed part-way and left something behind. Say what, and
|
|
455
|
+
where.
|
|
456
|
+
- `⏸` — the gate refused: in `checkout` mode a dirty tree or someone else's
|
|
457
|
+
unfinished work, in either mode a spec whose own files are not in the commit
|
|
458
|
+
the worktree would fork from. Nothing changed. Another spec's uncommitted work
|
|
459
|
+
is **not** on this list in `worktree` mode — it is an `Untouched` row on a
|
|
460
|
+
`✅`.
|
|
461
|
+
|
|
462
|
+
**Fields:** `Tracker` · `Branch` · `Spec` · `Worktree` · `Untouched` · `Follow-ups` · `Next`
|
|
463
|
+
|
|
464
|
+
`Worktree` carries the path, because the session is now standing in it and the
|
|
465
|
+
operator's next command depends on knowing that. `Next` is `/spec-next`.
|
|
466
|
+
|
|
467
|
+
`Untouched` appears only when `spec-env up` reported uncommitted work that was
|
|
468
|
+
not this spec's — say how much and whose, and **keep the verdict `✅`**. Nothing
|
|
469
|
+
went wrong: a worktree carries nothing, so that work was never in play.
|
|
470
|
+
|
|
471
|
+
On **`--plan`** nothing was provisioned, so the verdict is `⏸` and `Built`
|
|
472
|
+
carries the plan rather than a claim about the repo.
|
|
208
473
|
|
|
209
474
|
## Opt-outs
|
|
210
475
|
|
|
@@ -213,14 +478,29 @@ from here.
|
|
|
213
478
|
that the work lands wherever you are (usually the base branch); reserve it for
|
|
214
479
|
a trivial change or an explicit request.
|
|
215
480
|
|
|
216
|
-
There is no `--here
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
and
|
|
226
|
-
|
|
481
|
+
There is no `--here`. It existed to ask for the branch in the checkout you are
|
|
482
|
+
standing in — and in `checkout` mode that is already what happens, while in
|
|
483
|
+
`worktree` mode `--no-worktree` is the way to say it.
|
|
484
|
+
|
|
485
|
+
## Why this skill links nothing, but does mirror what it changes
|
|
486
|
+
|
|
487
|
+
This skill creates no spec and mints no issue, so it has **nothing to link** —
|
|
488
|
+
the intake and picker steps belong to `/spec`, `/spec-bug` and `/spec-hotfix`.
|
|
489
|
+
|
|
490
|
+
It does push, though, and that is step 4's seam. The state change it makes — the
|
|
491
|
+
spec moving to `in-progress` with a developer stamped on it — is mirrored by the
|
|
492
|
+
skill that makes it, exactly as `/spec-complete`, `/spec-cancel` and
|
|
493
|
+
`/spec-review` mirror theirs. It was once reasoned that the refresh `/spec-next`
|
|
494
|
+
runs would cover it, so a push here would send the same thing twice one commit
|
|
495
|
+
apart. That only ever held in `checkout` mode, where `/spec-next` follows
|
|
496
|
+
immediately; in `worktree` mode it can be hours away or never come, and the
|
|
497
|
+
issue sits in its old state with nobody assigned meanwhile.
|
|
498
|
+
|
|
499
|
+
The **assignment** seam just above it is still not a push, and does not need to
|
|
500
|
+
be. This is the one moment in the lifecycle where "who is building this" is
|
|
501
|
+
actually decided — the branch is being provisioned for someone, and that someone
|
|
502
|
+
is at the keyboard. It stamps the spec file and stops there, so it costs no
|
|
503
|
+
tracker call and rides out on the push below like every other field. Deferring
|
|
504
|
+
the stamp to `/spec-next` would be worse than untidy: in `worktree` mode the two
|
|
505
|
+
can be separated by hours, and a spec in flight with nobody named on it is
|
|
506
|
+
exactly the gap assignment exists to close.
|
|
@@ -6,6 +6,11 @@ disable-model-invocation: true
|
|
|
6
6
|
|
|
7
7
|
# /spec-status — one-way sync drift report
|
|
8
8
|
|
|
9
|
+
> Stay silent while this runs — speak only to ask something you cannot answer
|
|
10
|
+
> yourself, or to report a failure at the moment it happens. Read
|
|
11
|
+
> `.claude/rules/spec-reports.md` before reporting; it defines the block this
|
|
12
|
+
> skill ends with.
|
|
13
|
+
|
|
9
14
|
Read-only. Reports two things and writes nothing:
|
|
10
15
|
|
|
11
16
|
1. **Pending push** — has the spec changed since the last push (is there an issue
|
|
@@ -67,8 +72,25 @@ a hand edit misses some (it has, twice).
|
|
|
67
72
|
|
|
68
73
|
## 4. Report
|
|
69
74
|
|
|
70
|
-
Relay the engine's output verbatim
|
|
71
|
-
Never write to either side.
|
|
75
|
+
Relay the engine's output **verbatim, above the block** — it is the answer, and
|
|
76
|
+
the block is the verdict on it. Never write to either side.
|
|
77
|
+
|
|
78
|
+
End with the block defined in `.claude/rules/spec-reports.md`. That file carries
|
|
79
|
+
the shape; this section carries only what is specific here.
|
|
80
|
+
|
|
81
|
+
**Verdicts**
|
|
82
|
+
|
|
83
|
+
- `✅` — in sync; nothing would push.
|
|
84
|
+
- `⚠️` — drift: N objects would push, or Linear's workflow state was moved by
|
|
85
|
+
hand. Neither is an error — the repo wins on the next push — but both are the
|
|
86
|
+
reason someone ran this.
|
|
87
|
+
- `⏸` — no config, or the spec is not linked. Nothing to compare.
|
|
88
|
+
|
|
89
|
+
**Fields:** `Tracker` · `Follow-ups` · `Next`
|
|
90
|
+
|
|
91
|
+
`Tracker` is the drift in one line — what would push, and whether the tracker's
|
|
92
|
+
state diverged. `Next` is `/spec-push` when a push is pending, and nothing to do
|
|
93
|
+
when it is not.
|
|
72
94
|
|
|
73
95
|
A **`phases: <mode>`** line names the phase mode that resolved for this spec's
|
|
74
96
|
lifecycle bucket, and appears only when it is not the default `subissue`.
|