@skitterbyte/skitterspec-linear 10.0.1 → 10.2.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/README.md +76 -15
- package/assets/core/SETUP.md +73 -13
- package/assets/core/linear.config.json.example +8 -1
- package/assets/core/linear.config.md +243 -8
- package/assets/rules/spec-planning.md +17 -11
- package/assets/skills/spec/SKILL.md +106 -66
- package/assets/skills/spec-bug/SKILL.md +99 -17
- package/assets/skills/spec-cancel/SKILL.md +34 -0
- package/assets/skills/spec-complete/SKILL.md +70 -15
- package/assets/skills/spec-go/SKILL.md +10 -3
- package/assets/skills/spec-hotfix/SKILL.md +157 -4
- package/assets/skills/spec-linear-setup/SKILL.md +172 -0
- package/assets/skills/spec-push/SKILL.md +142 -30
- package/assets/skills/spec-review/SKILL.md +34 -0
- package/assets/skills/spec-status/SKILL.md +9 -0
- package/bin/skitterspec-linear.js +19 -0
- package/package.json +1 -1
- package/src/cli.js +30 -19
- package/src/env/resolve.js +7 -2
- package/src/env/teardown.js +23 -9
- package/src/init.js +11 -1
- package/src/vendor/linear/api.js +246 -0
- package/src/vendor/linear/cli-sync.js +788 -3
- package/src/vendor/linear/config.js +127 -7
- package/src/vendor/sync-core/index.js +8 -1
- package/src/vendor/sync-core/src/normalize.js +291 -82
- package/src/vendor/sync-core/src/push.js +18 -1
- package/src/vendor/sync-core/src/tables.js +102 -0
- package/src/vendor/sync-core/src/task-block.js +18 -7
- package/src/vendor/sync-core/src/verify.js +83 -0
|
@@ -36,28 +36,58 @@ Then:
|
|
|
36
36
|
2. **Read the issue** with the discovered issue-read tool: title, description,
|
|
37
37
|
labels, reporter, URL. If Linear isn't connected, say so and offer to carry on
|
|
38
38
|
without it — a spec written from the user's own description is still a spec.
|
|
39
|
-
3. **Route
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
3. **Route the issue to the right skill.** Match the issue's labels
|
|
40
|
+
(case-insensitively) against two lists, **checking `hotfixLabels` first**:
|
|
41
|
+
|
|
42
|
+
- **`intake.hotfixLabels`** → this is broken in **production**, so it needs a
|
|
43
|
+
fix against the released version, not `main`. Say so, name the matching
|
|
44
|
+
label, and tell the user to run **`/spec-hotfix <ISSUE-REF>`** — then stop.
|
|
45
|
+
- **`intake.bugLabels`** → a bug report, not a feature request. Say so, name
|
|
46
|
+
the label, and tell the user to run **`/spec-bug <ISSUE-REF>`** — then stop,
|
|
47
|
+
without authoring a Feature spec.
|
|
48
|
+
|
|
49
|
+
**Hotfix wins when an issue carries both**, and deliberately: production is the
|
|
50
|
+
more specific destination, and the two mistakes are not equally costly. Routing
|
|
51
|
+
a prod issue to `/spec-bug` produces a fix that lands on `main` and never
|
|
52
|
+
reaches the running version — discovered only when someone asks why it hasn't
|
|
53
|
+
shipped. The reverse is a hotfix branch for something that could have waited,
|
|
54
|
+
which is merely wasteful.
|
|
55
|
+
|
|
56
|
+
With a list unset, nothing routes through it; with both unset every issue is
|
|
57
|
+
treated as a feature request, exactly as before.
|
|
58
|
+
|
|
59
|
+
**Which checks run depends on where you are**, because a skip is about not
|
|
60
|
+
bouncing someone to the skill they are already in — not about ignoring an
|
|
61
|
+
escalation:
|
|
62
|
+
|
|
63
|
+
- **In `/spec`** — both checks run.
|
|
64
|
+
- **In `/spec-bug`** — the bug check is skipped (it would route you to
|
|
65
|
+
yourself), but the **hotfix check still runs**. A bug report labelled for
|
|
66
|
+
production is not "already handled" by being in the bug path: `/spec-bug`
|
|
67
|
+
fixes on `main`, and prod would stay broken. Say so and hand off.
|
|
68
|
+
- **In `/spec-hotfix`** — both are skipped. It is already the most specific
|
|
69
|
+
destination; there is nowhere left to route.
|
|
70
|
+
|
|
71
|
+
Then adopt the issue and reproduce it as usual: the issue body is your repro
|
|
48
72
|
material, and the failing test comes before the spec exactly as normal.
|
|
49
73
|
4. **Seed, don't skip, the grill.** The issue's title becomes the working spec
|
|
50
|
-
title and its description the starting material for **Problem** —
|
|
74
|
+
title and its description the starting material for **Problem** — or
|
|
75
|
+
**Symptom** in a bug or hotfix spec, which is where a report belongs. Quote the
|
|
51
76
|
reporter's words rather than paraphrasing them away. Note the reporter and the
|
|
52
77
|
issue URL for context. Everything after this runs exactly as normal: an issue
|
|
53
78
|
is a *request*, not a groomed spec, so grill it as hard as anything else.
|
|
54
79
|
|
|
80
|
+
**In `/spec-hotfix`, also mine the issue for a version.** Any release-shaped
|
|
81
|
+
string in the report is a *suggestion* for the base tag, offered when the skill
|
|
82
|
+
asks which version prod is running — never used as a default. The reporter's
|
|
83
|
+
version is the one they saw the bug on, which is often not what is deployed.
|
|
84
|
+
|
|
55
85
|
### Adopting the issue
|
|
56
86
|
|
|
57
87
|
The issue **becomes** the spec's issue — it is not copied and no second issue is
|
|
58
|
-
minted. This is identical in `/spec` and `/spec-
|
|
59
|
-
differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step 4
|
|
60
|
-
spec file exists:
|
|
88
|
+
minted. This is identical in `/spec`, `/spec-bug` and `/spec-hotfix`; only *when*
|
|
89
|
+
it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step 4,
|
|
90
|
+
`/spec-hotfix` in its step 5). Once the spec file exists:
|
|
61
91
|
|
|
62
92
|
- **Stamp `linear_identifier` and `linear_url`** in `00-overview.md` frontmatter
|
|
63
93
|
from the adopted issue. That is the whole link: every later skill
|
|
@@ -66,13 +96,15 @@ spec file exists:
|
|
|
66
96
|
somewhere deliberately — where it lives is Linear's business, and adoption is
|
|
67
97
|
not a mint.
|
|
68
98
|
- **Do not write a base sidecar.** Leaving `sync.baseDir` empty for this spec is
|
|
69
|
-
what makes the
|
|
99
|
+
what makes the **linking push** send the spec over the reporter's original
|
|
70
100
|
description (an **update** to the existing issue, plus a sub-issue per phase).
|
|
71
101
|
Recording a snapshot here would declare the mirror already in sync and strand
|
|
72
102
|
the issue showing the raw report forever.
|
|
73
|
-
- **Say what will happen** in the finish-up message
|
|
74
|
-
|
|
75
|
-
|
|
103
|
+
- **Say what will happen** in the finish-up message. The linking step runs right
|
|
104
|
+
after the spec is written, so the issue's description is replaced by the spec
|
|
105
|
+
**then** — not on some later manual push. The reporter's words are not lost:
|
|
106
|
+
they are quoted in the spec's **Problem** (or **Symptom**) section, and Linear
|
|
107
|
+
keeps the original in the issue's history.
|
|
76
108
|
|
|
77
109
|
## Phase A — reach a clear shared understanding (grill first)
|
|
78
110
|
|
|
@@ -286,49 +318,54 @@ while the repo stays the source of truth — follow the provider's link steps
|
|
|
286
318
|
below (nothing to do here without one).
|
|
287
319
|
|
|
288
320
|
**Only when `specs/.core/linear.config.json` exists** (Linear sync is opted in).
|
|
289
|
-
If it's absent, skip this
|
|
290
|
-
behaves exactly as above. When present,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
If it's absent, skip this entirely — the spec stays local-only and the skill
|
|
322
|
+
behaves exactly as above. When present, link the spec you just wrote so status
|
|
323
|
+
and discussion live in Linear while the repo stays the source of truth. A spec is
|
|
324
|
+
a Linear **issue**; each phase is a **sub-issue**, carrying that phase's tasks in
|
|
325
|
+
its description as a read-only checklist.
|
|
326
|
+
|
|
327
|
+
**Linking is just the first push**, so it runs the same engine path `/spec-push`
|
|
328
|
+
does — you never create the issue by hand:
|
|
329
|
+
|
|
330
|
+
1. **Pick the transport.** `skitterspec spec-sync states --json`. With a Linear
|
|
331
|
+
API key set it answers `api` and prints the workspace's state names; without
|
|
332
|
+
one it answers `mcp` and you do the MCP work `/spec-push` describes. Write the
|
|
333
|
+
state names to a file for step 3.
|
|
334
|
+
2. **Pick the Project** — run the picker in **Picking the Linear Project** below.
|
|
335
|
+
Keep the chosen id for step 4.
|
|
336
|
+
3. **Get the plan.**
|
|
337
|
+
`skitterspec spec-sync push <spec> --workspace-states <file> --json > plan.json`
|
|
338
|
+
— the spec is unlinked, so this plan is all-creates: the issue and one
|
|
339
|
+
sub-issue per phase.
|
|
340
|
+
4. **Apply it.**
|
|
341
|
+
`skitterspec spec-sync apply <spec> --plan plan.json --project <chosen id>`.
|
|
342
|
+
That creates the issue and its sub-issues and checks what Linear stored. It
|
|
343
|
+
then **stamps the ids into the spec** — `linear_identifier`/`linear_url` on the
|
|
344
|
+
overview, `linear_issue_id` on each phase — and **records the base snapshot**,
|
|
345
|
+
so `/spec-status` reports in-sync immediately. There is no hand-editing of
|
|
346
|
+
frontmatter and no separate `stamp` or `record` call.
|
|
347
|
+
|
|
348
|
+
If it prints `transport = mcp`, it wrote nothing: apply the plan over MCP as
|
|
349
|
+
`/spec-push` steps 4a–5 describe, ending with `spec-sync stamp` and
|
|
350
|
+
`spec-sync record`. That path is fully supported — it is what anyone without
|
|
351
|
+
an API key uses.
|
|
352
|
+
5. **Echo the branch name** from `branch.pattern` so the user knows what
|
|
353
|
+
`/spec-go` will fork.
|
|
354
|
+
|
|
355
|
+
**If Linear can't be reached**, say so in one line and leave the spec written and
|
|
356
|
+
local — it is still a perfectly good spec, and `/spec-push` links it later. Do
|
|
357
|
+
nothing destructive.
|
|
358
|
+
|
|
359
|
+
**A spec that adopted an existing issue** (see the intake step) is already
|
|
360
|
+
stamped, so its plan is an **update**, not a create: applying it replaces the
|
|
361
|
+
reporter's description with the spec. That is the one-way rule working as
|
|
362
|
+
intended — the repo is canonical and the original text stays in Linear's history.
|
|
363
|
+
Skip the project picker for an adopted issue: its placement is Linear's.
|
|
327
364
|
|
|
328
365
|
Leave committing to the existing convention (the user commits the spec as usual)
|
|
329
366
|
and **never auto-push git** — Linear's own automation reacts to real branch/PR
|
|
330
|
-
events later. Report the Linear issue URL
|
|
331
|
-
|
|
367
|
+
events later. Report the Linear issue URL as part of the skill's finish-up
|
|
368
|
+
message.
|
|
332
369
|
|
|
333
370
|
### Picking the Linear Project
|
|
334
371
|
|
|
@@ -338,8 +375,10 @@ placement is **Linear's to own**: never send `project` on an update, and never
|
|
|
338
375
|
record the choice in the spec file or the snapshot. A PM re-homing a spec issue
|
|
339
376
|
must not show up as drift or be overwritten on the next push.
|
|
340
377
|
|
|
341
|
-
1. **List the candidates.**
|
|
342
|
-
`
|
|
378
|
+
1. **List the candidates.** Ask the engine:
|
|
379
|
+
`skitterspec spec-sync projects --json`. On the API path it returns the team's
|
|
380
|
+
projects; on the MCP path it says so, and you call the discovered project-list
|
|
381
|
+
tool instead. Drop archived / completed projects — they can't take new work.
|
|
343
382
|
2. **Offer them.** Show the names (most recently updated first is fine), plus an
|
|
344
383
|
explicit **None (team only)** option. Pre-select `linear.projectId` from
|
|
345
384
|
`linear.config.json` when it's set and still in the list; otherwise pre-select
|
|
@@ -348,13 +387,14 @@ must not show up as drift or be overwritten on the next push.
|
|
|
348
387
|
the list case-insensitively by name and re-offer. Don't re-fetch.
|
|
349
388
|
4. **Never offer to create a project.** Projects are the PM's surface — if none
|
|
350
389
|
fits, that's **None (team only)**, and someone makes the project in Linear.
|
|
351
|
-
5. **Pass it once
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
390
|
+
5. **Pass it once**, to whichever thing mints the issue: `--project <chosen id>`
|
|
391
|
+
on `spec-sync apply`, or `project: <chosen id>` on the MCP issue-create call.
|
|
392
|
+
Chose None → omit it entirely (do not pass an empty string).
|
|
393
|
+
|
|
394
|
+
**Degrade, never block.** If the list can't be fetched — Linear not connected, no
|
|
395
|
+
project-list tool, no API key, or `spec-sync projects` reporting it couldn't ask —
|
|
396
|
+
say so in one line — *"project picker unavailable"* — and carry on with
|
|
397
|
+
`linear.projectId` if it's set, else no project at all. A missing picker must never fail the skill that called it.
|
|
358
398
|
|
|
359
399
|
If `linear.projectId` is set but that Project is archived or missing, relay
|
|
360
400
|
Linear's error and stop rather than silently minting an unparented issue.
|
|
@@ -35,28 +35,58 @@ Then:
|
|
|
35
35
|
2. **Read the issue** with the discovered issue-read tool: title, description,
|
|
36
36
|
labels, reporter, URL. If Linear isn't connected, say so and offer to carry on
|
|
37
37
|
without it — a spec written from the user's own description is still a spec.
|
|
38
|
-
3. **Route
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
3. **Route the issue to the right skill.** Match the issue's labels
|
|
39
|
+
(case-insensitively) against two lists, **checking `hotfixLabels` first**:
|
|
40
|
+
|
|
41
|
+
- **`intake.hotfixLabels`** → this is broken in **production**, so it needs a
|
|
42
|
+
fix against the released version, not `main`. Say so, name the matching
|
|
43
|
+
label, and tell the user to run **`/spec-hotfix <ISSUE-REF>`** — then stop.
|
|
44
|
+
- **`intake.bugLabels`** → a bug report, not a feature request. Say so, name
|
|
45
|
+
the label, and tell the user to run **`/spec-bug <ISSUE-REF>`** — then stop,
|
|
46
|
+
without authoring a Feature spec.
|
|
47
|
+
|
|
48
|
+
**Hotfix wins when an issue carries both**, and deliberately: production is the
|
|
49
|
+
more specific destination, and the two mistakes are not equally costly. Routing
|
|
50
|
+
a prod issue to `/spec-bug` produces a fix that lands on `main` and never
|
|
51
|
+
reaches the running version — discovered only when someone asks why it hasn't
|
|
52
|
+
shipped. The reverse is a hotfix branch for something that could have waited,
|
|
53
|
+
which is merely wasteful.
|
|
54
|
+
|
|
55
|
+
With a list unset, nothing routes through it; with both unset every issue is
|
|
56
|
+
treated as a feature request, exactly as before.
|
|
57
|
+
|
|
58
|
+
**Which checks run depends on where you are**, because a skip is about not
|
|
59
|
+
bouncing someone to the skill they are already in — not about ignoring an
|
|
60
|
+
escalation:
|
|
61
|
+
|
|
62
|
+
- **In `/spec`** — both checks run.
|
|
63
|
+
- **In `/spec-bug`** — the bug check is skipped (it would route you to
|
|
64
|
+
yourself), but the **hotfix check still runs**. A bug report labelled for
|
|
65
|
+
production is not "already handled" by being in the bug path: `/spec-bug`
|
|
66
|
+
fixes on `main`, and prod would stay broken. Say so and hand off.
|
|
67
|
+
- **In `/spec-hotfix`** — both are skipped. It is already the most specific
|
|
68
|
+
destination; there is nowhere left to route.
|
|
69
|
+
|
|
70
|
+
Then adopt the issue and reproduce it as usual: the issue body is your repro
|
|
47
71
|
material, and the failing test comes before the spec exactly as normal.
|
|
48
72
|
4. **Seed, don't skip, the grill.** The issue's title becomes the working spec
|
|
49
|
-
title and its description the starting material for **Problem** —
|
|
73
|
+
title and its description the starting material for **Problem** — or
|
|
74
|
+
**Symptom** in a bug or hotfix spec, which is where a report belongs. Quote the
|
|
50
75
|
reporter's words rather than paraphrasing them away. Note the reporter and the
|
|
51
76
|
issue URL for context. Everything after this runs exactly as normal: an issue
|
|
52
77
|
is a *request*, not a groomed spec, so grill it as hard as anything else.
|
|
53
78
|
|
|
79
|
+
**In `/spec-hotfix`, also mine the issue for a version.** Any release-shaped
|
|
80
|
+
string in the report is a *suggestion* for the base tag, offered when the skill
|
|
81
|
+
asks which version prod is running — never used as a default. The reporter's
|
|
82
|
+
version is the one they saw the bug on, which is often not what is deployed.
|
|
83
|
+
|
|
54
84
|
### Adopting the issue
|
|
55
85
|
|
|
56
86
|
The issue **becomes** the spec's issue — it is not copied and no second issue is
|
|
57
|
-
minted. This is identical in `/spec` and `/spec-
|
|
58
|
-
differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step 4
|
|
59
|
-
spec file exists:
|
|
87
|
+
minted. This is identical in `/spec`, `/spec-bug` and `/spec-hotfix`; only *when*
|
|
88
|
+
it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step 4,
|
|
89
|
+
`/spec-hotfix` in its step 5). Once the spec file exists:
|
|
60
90
|
|
|
61
91
|
- **Stamp `linear_identifier` and `linear_url`** in `00-overview.md` frontmatter
|
|
62
92
|
from the adopted issue. That is the whole link: every later skill
|
|
@@ -65,13 +95,15 @@ spec file exists:
|
|
|
65
95
|
somewhere deliberately — where it lives is Linear's business, and adoption is
|
|
66
96
|
not a mint.
|
|
67
97
|
- **Do not write a base sidecar.** Leaving `sync.baseDir` empty for this spec is
|
|
68
|
-
what makes the
|
|
98
|
+
what makes the **linking push** send the spec over the reporter's original
|
|
69
99
|
description (an **update** to the existing issue, plus a sub-issue per phase).
|
|
70
100
|
Recording a snapshot here would declare the mirror already in sync and strand
|
|
71
101
|
the issue showing the raw report forever.
|
|
72
|
-
- **Say what will happen** in the finish-up message
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
- **Say what will happen** in the finish-up message. The linking step runs right
|
|
103
|
+
after the spec is written, so the issue's description is replaced by the spec
|
|
104
|
+
**then** — not on some later manual push. The reporter's words are not lost:
|
|
105
|
+
they are quoted in the spec's **Problem** (or **Symptom**) section, and Linear
|
|
106
|
+
keeps the original in the issue's history.
|
|
75
107
|
|
|
76
108
|
## 1. Reproduce & isolate (light investigation)
|
|
77
109
|
|
|
@@ -216,6 +248,56 @@ The **State log** is the folder/status audit trail; later transitions
|
|
|
216
248
|
(`/spec-complete`, `/spec-cancel`) append a row. The **Changelog** is for the
|
|
217
249
|
fix narrative and decisions — keep them separate.
|
|
218
250
|
|
|
251
|
+
**Only when `specs/.core/linear.config.json` exists** (Linear sync is opted in).
|
|
252
|
+
If it's absent, skip this entirely — the spec stays local-only and the skill
|
|
253
|
+
behaves exactly as above. When present, link the spec you just wrote so status
|
|
254
|
+
and discussion live in Linear while the repo stays the source of truth. A spec is
|
|
255
|
+
a Linear **issue**; each phase is a **sub-issue**, carrying that phase's tasks in
|
|
256
|
+
its description as a read-only checklist.
|
|
257
|
+
|
|
258
|
+
**Linking is just the first push**, so it runs the same engine path `/spec-push`
|
|
259
|
+
does — you never create the issue by hand:
|
|
260
|
+
|
|
261
|
+
1. **Pick the transport.** `skitterspec spec-sync states --json`. With a Linear
|
|
262
|
+
API key set it answers `api` and prints the workspace's state names; without
|
|
263
|
+
one it answers `mcp` and you do the MCP work `/spec-push` describes. Write the
|
|
264
|
+
state names to a file for step 3.
|
|
265
|
+
2. **Pick the Project** — run the picker in **Picking the Linear Project** below.
|
|
266
|
+
Keep the chosen id for step 4.
|
|
267
|
+
3. **Get the plan.**
|
|
268
|
+
`skitterspec spec-sync push <spec> --workspace-states <file> --json > plan.json`
|
|
269
|
+
— the spec is unlinked, so this plan is all-creates: the issue and one
|
|
270
|
+
sub-issue per phase.
|
|
271
|
+
4. **Apply it.**
|
|
272
|
+
`skitterspec spec-sync apply <spec> --plan plan.json --project <chosen id>`.
|
|
273
|
+
That creates the issue and its sub-issues and checks what Linear stored. It
|
|
274
|
+
then **stamps the ids into the spec** — `linear_identifier`/`linear_url` on the
|
|
275
|
+
overview, `linear_issue_id` on each phase — and **records the base snapshot**,
|
|
276
|
+
so `/spec-status` reports in-sync immediately. There is no hand-editing of
|
|
277
|
+
frontmatter and no separate `stamp` or `record` call.
|
|
278
|
+
|
|
279
|
+
If it prints `transport = mcp`, it wrote nothing: apply the plan over MCP as
|
|
280
|
+
`/spec-push` steps 4a–5 describe, ending with `spec-sync stamp` and
|
|
281
|
+
`spec-sync record`. That path is fully supported — it is what anyone without
|
|
282
|
+
an API key uses.
|
|
283
|
+
5. **Echo the branch name** from `branch.pattern` so the user knows what
|
|
284
|
+
`/spec-go` will fork.
|
|
285
|
+
|
|
286
|
+
**If Linear can't be reached**, say so in one line and leave the spec written and
|
|
287
|
+
local — it is still a perfectly good spec, and `/spec-push` links it later. Do
|
|
288
|
+
nothing destructive.
|
|
289
|
+
|
|
290
|
+
**A spec that adopted an existing issue** (see the intake step) is already
|
|
291
|
+
stamped, so its plan is an **update**, not a create: applying it replaces the
|
|
292
|
+
reporter's description with the spec. That is the one-way rule working as
|
|
293
|
+
intended — the repo is canonical and the original text stays in Linear's history.
|
|
294
|
+
Skip the project picker for an adopted issue: its placement is Linear's.
|
|
295
|
+
|
|
296
|
+
Leave committing to the existing convention (the user commits the spec as usual)
|
|
297
|
+
and **never auto-push git** — Linear's own automation reacts to real branch/PR
|
|
298
|
+
events later. Report the Linear issue URL as part of the skill's finish-up
|
|
299
|
+
message.
|
|
300
|
+
|
|
219
301
|
## 5. Drive to GREEN
|
|
220
302
|
|
|
221
303
|
- Implement the **minimal, root-cause** fix. Match surrounding code; honour all
|
|
@@ -51,6 +51,40 @@ Add a **Changelog** entry:
|
|
|
51
51
|
`git mv "specs/<bucket>/<name>" "specs/cancelled/<name>"` (preserve history;
|
|
52
52
|
move the whole folder).
|
|
53
53
|
|
|
54
|
+
**Only when `specs/.core/linear.config.json` exists** and the spec's overview
|
|
55
|
+
carries a `linear_identifier`. Either missing → **skip**, in one line
|
|
56
|
+
(`not linked to Linear — /spec-push to mirror it`), and carry on. Nothing else in
|
|
57
|
+
this step changes.
|
|
58
|
+
|
|
59
|
+
**Refresh the mirror now, without asking.** Run `/spec-push`. The repo has just
|
|
60
|
+
become the truth about this spec's state, and the tracker is a generated mirror
|
|
61
|
+
of it — leaving them to diverge until someone remembers to push by hand is the
|
|
62
|
+
gap this exists to close. It costs one engine call and no model tokens when a
|
|
63
|
+
Linear API key is set (see `apply.transport` in `linear.config.md`).
|
|
64
|
+
|
|
65
|
+
- **Never mint.** An unlinked spec is skipped, not created. An issue born `Done`
|
|
66
|
+
or `Canceled` is tracker noise, and a spec kept deliberately local should stay
|
|
67
|
+
that way — `/spec-push` is how someone opts in.
|
|
68
|
+
- **Never fatal.** If the push fails — offline, no key, a Linear error — say so
|
|
69
|
+
and **finish the operation anyway**. The spec is complete/cancelled/reviewed in
|
|
70
|
+
the repo regardless; the mirror is disposable and the next push repairs it.
|
|
71
|
+
Do not roll anything back, and do not stop to ask.
|
|
72
|
+
- **Say what happened** in the skill's report: mirror updated, skipped as
|
|
73
|
+
unlinked, or failed with the reason.
|
|
74
|
+
|
|
75
|
+
### Why it sits here
|
|
76
|
+
|
|
77
|
+
In `/spec-complete` and `/spec-cancel` this step is deliberately pinned
|
|
78
|
+
**after the `git mv` and before the commit**, and both halves matter:
|
|
79
|
+
|
|
80
|
+
- **After the move**, because the projection reads a spec's workflow state from
|
|
81
|
+
its folder bucket. Push while the folder is still in `in-progress/` and the
|
|
82
|
+
issue is set to the state the spec is *leaving*.
|
|
83
|
+
- **Before the commit**, because the push stamps ids into the spec and writes a
|
|
84
|
+
snapshot under `specs/.core/`. The `git add specs/` that follows sweeps both up
|
|
85
|
+
with the status change; push after it instead and those files are left
|
|
86
|
+
uncommitted, which makes `spec-env integrate` refuse to land the branch.
|
|
87
|
+
|
|
54
88
|
Then **commit the cancellation edits** — this skill wrote them, so it commits
|
|
55
89
|
them:
|
|
56
90
|
|
|
@@ -53,6 +53,40 @@ Before marking complete, confirm the work is actually finished:
|
|
|
53
53
|
move the whole folder). The `specs/complete/` folder is the record of finished
|
|
54
54
|
specs — `git log`/the per-spec State log give the completion order.
|
|
55
55
|
|
|
56
|
+
**Only when `specs/.core/linear.config.json` exists** and the spec's overview
|
|
57
|
+
carries a `linear_identifier`. Either missing → **skip**, in one line
|
|
58
|
+
(`not linked to Linear — /spec-push to mirror it`), and carry on. Nothing else in
|
|
59
|
+
this step changes.
|
|
60
|
+
|
|
61
|
+
**Refresh the mirror now, without asking.** Run `/spec-push`. The repo has just
|
|
62
|
+
become the truth about this spec's state, and the tracker is a generated mirror
|
|
63
|
+
of it — leaving them to diverge until someone remembers to push by hand is the
|
|
64
|
+
gap this exists to close. It costs one engine call and no model tokens when a
|
|
65
|
+
Linear API key is set (see `apply.transport` in `linear.config.md`).
|
|
66
|
+
|
|
67
|
+
- **Never mint.** An unlinked spec is skipped, not created. An issue born `Done`
|
|
68
|
+
or `Canceled` is tracker noise, and a spec kept deliberately local should stay
|
|
69
|
+
that way — `/spec-push` is how someone opts in.
|
|
70
|
+
- **Never fatal.** If the push fails — offline, no key, a Linear error — say so
|
|
71
|
+
and **finish the operation anyway**. The spec is complete/cancelled/reviewed in
|
|
72
|
+
the repo regardless; the mirror is disposable and the next push repairs it.
|
|
73
|
+
Do not roll anything back, and do not stop to ask.
|
|
74
|
+
- **Say what happened** in the skill's report: mirror updated, skipped as
|
|
75
|
+
unlinked, or failed with the reason.
|
|
76
|
+
|
|
77
|
+
### Why it sits here
|
|
78
|
+
|
|
79
|
+
In `/spec-complete` and `/spec-cancel` this step is deliberately pinned
|
|
80
|
+
**after the `git mv` and before the commit**, and both halves matter:
|
|
81
|
+
|
|
82
|
+
- **After the move**, because the projection reads a spec's workflow state from
|
|
83
|
+
its folder bucket. Push while the folder is still in `in-progress/` and the
|
|
84
|
+
issue is set to the state the spec is *leaving*.
|
|
85
|
+
- **Before the commit**, because the push stamps ids into the spec and writes a
|
|
86
|
+
snapshot under `specs/.core/`. The `git add specs/` that follows sweeps both up
|
|
87
|
+
with the status change; push after it instead and those files are left
|
|
88
|
+
uncommitted, which makes `spec-env integrate` refuse to land the branch.
|
|
89
|
+
|
|
56
90
|
Then **commit the completion edits** — steps 3–4 are this skill's own output, so
|
|
57
91
|
it finishes its own work rather than handing you a dirty tree:
|
|
58
92
|
|
|
@@ -128,14 +162,14 @@ is finished — e.g. to run a later phase in CI or a shared test env? Use
|
|
|
128
162
|
recover those commits onto the branch, then re-run.
|
|
129
163
|
- *no worktree* — the spec is live but its worktree is gone. Re-isolate it with
|
|
130
164
|
`skitterspec spec-env up <name>`, then re-run.
|
|
131
|
-
Relay the diagnostic to the user and **stop** — do not
|
|
165
|
+
Relay the diagnostic to the user and **stop** — do not tear anything down.
|
|
132
166
|
2. **Plan + execute.** Run `skitterspec spec-env integrate <name>` and run the
|
|
133
167
|
printed commands **in order**:
|
|
134
168
|
- `git -C <worktree> rebase <base>` — replay the branch onto base.
|
|
135
169
|
- `git -C <mainRepoPath> merge --ff-only <branch>` — fast-forward base.
|
|
136
170
|
On a **rebase conflict** (non-zero exit), run
|
|
137
171
|
`git -C <worktree> rebase --abort`, relay the conflict, and **stop** — leave it
|
|
138
|
-
to the user; do not
|
|
172
|
+
to the user; do not tear anything down.
|
|
139
173
|
On a **no-op** ("already landed"), just say so and continue.
|
|
140
174
|
3. **Re-test on base.** Run the project's test command from the primary checkout;
|
|
141
175
|
it must be **green** before you call the landing done.
|
|
@@ -144,9 +178,22 @@ is finished — e.g. to run a later phase in CI or a shared test env? Use
|
|
|
144
178
|
|
|
145
179
|
## 7. Tear down the environment (opt-in, only if configured)
|
|
146
180
|
|
|
147
|
-
**Only when `specs/.core/env.config.json` exists
|
|
148
|
-
|
|
149
|
-
directly (the old `/spec-env-down` skill is gone —
|
|
181
|
+
**Only when `specs/.core/env.config.json` exists.** Reclaiming the environment is
|
|
182
|
+
what completing a spec *is*, so sub-steps 1–3 run **automatically — do not ask**.
|
|
183
|
+
Run the `spec-env` CLI directly (the old `/spec-env-down` skill is gone —
|
|
184
|
+
teardown is folded in here).
|
|
185
|
+
|
|
186
|
+
**The precondition is that the work actually landed.** Only tear down when step 6
|
|
187
|
+
completed: it landed (or reported "already landed") **and** the base suite came
|
|
188
|
+
back green. A rebase conflict, a work-loss abort or a red suite means step 6 told
|
|
189
|
+
you to stop — tear nothing down, because the worktree is where the user picks the
|
|
190
|
+
problem up. That precondition is what makes a confirmation redundant: by the time
|
|
191
|
+
you get here the branch is an ancestor of base and the engine's guards have
|
|
192
|
+
nothing left to protect.
|
|
193
|
+
|
|
194
|
+
**Opt-out:** if the user passed **`--keep-env`**, skip sub-steps 1–3, say the
|
|
195
|
+
worktree and branch are being kept, and go straight to sub-step 4. Mention
|
|
196
|
+
`skitterspec spec-env up <name>` re-attaches it later either way.
|
|
150
197
|
|
|
151
198
|
1. **Disconnect the proxy if this spec is connected.** If `.spec-env/connected`
|
|
152
199
|
names this spec, run `skitterspec spec-env connect main` first so the
|
|
@@ -154,20 +201,28 @@ directly (the old `/spec-env-down` skill is gone — teardown is folded in here)
|
|
|
154
201
|
2. **Stop its host dev servers:** `skitterspec spec-env dev down <name>` (a
|
|
155
202
|
no-op when none are running / configured).
|
|
156
203
|
3. **Remove worktree + stack + slot:** run `skitterspec spec-env down <name>`
|
|
157
|
-
and execute the commands it prints, in order. After a
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
204
|
+
and execute the commands it prints, in order. After a landing — merged into
|
|
205
|
+
base for a **feature/bug**, captured by the deploy tag for a **hotfix** —
|
|
206
|
+
teardown needs **no `--force`** and drops the branch with `git branch -D`,
|
|
207
|
+
which is safe precisely because the commits are already somewhere else. It
|
|
208
|
+
still respects the guards (won't destroy a dirty, or unpushed-and-unlanded,
|
|
209
|
+
worktree without `--force`), so if it *does* refuse, relay that and stop
|
|
210
|
+
rather than reaching for `--force`.
|
|
164
211
|
4. **Reap orphaned test-DB volumes:** run `skitterspec spec-env prune`. It lists
|
|
165
212
|
Docker volumes in the repo namespace that belong to **no live spec** (no
|
|
166
213
|
worktree) — leftovers from declined/aborted teardowns, manual
|
|
167
214
|
`git worktree remove`, or `--keep-volumes`. Show the user the orphan list and,
|
|
168
215
|
**only on their confirmation**, execute the printed `docker volume rm`
|
|
169
|
-
commands.
|
|
170
|
-
|
|
171
|
-
|
|
216
|
+
commands. **This one still asks**, unlike 1–3: it reaps volumes belonging to
|
|
217
|
+
*other* specs, and this spec having landed cleanly says nothing about those.
|
|
218
|
+
Non-fatal: if prune can't run (Docker down) or the user declines, report it and
|
|
219
|
+
finish completing anyway — never block the spec on it. Skip when Docker isn't
|
|
220
|
+
in use (the command self-reports "no orphaned volumes").
|
|
221
|
+
|
|
222
|
+
**Say what you reclaimed.** With no confirmation step the user never saw this
|
|
223
|
+
coming, so the final report must name the worktree path removed and the branch
|
|
224
|
+
deleted (or, under `--keep-env`, that both were kept). A teardown nobody
|
|
225
|
+
authorised and nobody was told about is the one way this step can lose someone's
|
|
226
|
+
place.
|
|
172
227
|
|
|
173
228
|
If `env.config.json` is absent, skip this entirely — behave exactly as before.
|
|
@@ -152,9 +152,16 @@ below. Follow the provider's steps below (nothing to do here without one).
|
|
|
152
152
|
to bring down before building — the repo is already the source of truth. (A
|
|
153
153
|
workflow-state a teammate moved in Linear is advisory only; `/spec-status`
|
|
154
154
|
surfaces it. It is overwritten on the next push.)
|
|
155
|
-
- **
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
- **Refresh the mirror.** Run `/spec-push` to send the now in-progress spec up.
|
|
156
|
+
Whether that is optional depends on `mapping.phases`:
|
|
157
|
+
- `"subissue"` (the default) — **optional**. The phase sub-issues already
|
|
158
|
+
exist; this push only moves their states. Refresh now or later.
|
|
159
|
+
- `"deferred"` — **do it now, without asking.** Under deferral a spec sitting
|
|
160
|
+
in the backlog is mirrored as the issue alone, and this push is what mints
|
|
161
|
+
its phase sub-issues. Skip it and a started spec stays mirrored as a
|
|
162
|
+
phase-less issue until someone happens to run `/spec-push` by hand. Run it
|
|
163
|
+
straight after the step-2 commit, so the sub-issues land with the same
|
|
164
|
+
branch push that fires the tracker's automation.
|
|
158
165
|
- Linear's GitHub branch/PR automation may drive status transitions off the
|
|
159
166
|
branch/PR you pushed in step 2; that's expected and the repo still wins on the
|
|
160
167
|
next `/spec-push`.
|