@skitterbyte/skitterspec-jira 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/MIGRATION.md +1266 -0
  2. package/README.md +44 -2
  3. package/assets/claude-md-section.md +85 -0
  4. package/assets/commands/allow-main.md +33 -0
  5. package/assets/commands/spec-connect.md +13 -0
  6. package/assets/commands/spec-live.md +14 -0
  7. package/assets/commands/spec-remote-review.md +37 -0
  8. package/assets/commands/spec-skip.md +24 -0
  9. package/assets/core/SETUP.md +192 -0
  10. package/assets/core/env.config.json.example +56 -0
  11. package/assets/core/env.config.md +606 -0
  12. package/assets/core/gating.config.json.example +4 -0
  13. package/assets/core/gating.config.md +81 -0
  14. package/assets/core/jira.config.json.example +18 -0
  15. package/assets/core/jira.config.md +88 -0
  16. package/assets/hooks/main-guard.cjs +162 -0
  17. package/assets/hooks/review-gate.cjs +146 -0
  18. package/assets/review/page.html +3384 -0
  19. package/assets/rules/commit-trailers.md +96 -0
  20. package/assets/rules/negative-checks.md +69 -0
  21. package/assets/rules/offered-unblocks.md +100 -0
  22. package/assets/rules/spec-planning.md +702 -0
  23. package/assets/rules/spec-reports.md +572 -0
  24. package/assets/skills/no-spec/SKILL.md +249 -0
  25. package/assets/skills/spec/SKILL.md +714 -0
  26. package/assets/skills/spec-bug/SKILL.md +585 -0
  27. package/assets/skills/spec-cancel/SKILL.md +261 -0
  28. package/assets/skills/spec-complete/SKILL.md +451 -0
  29. package/assets/skills/spec-diff/SKILL.md +870 -0
  30. package/assets/skills/spec-hotfix/SKILL.md +607 -0
  31. package/assets/skills/spec-init/SKILL.md +178 -0
  32. package/assets/skills/spec-jira-setup/SKILL.md +221 -0
  33. package/assets/skills/spec-list/SKILL.md +173 -0
  34. package/assets/skills/spec-next/SKILL.md +678 -0
  35. package/assets/skills/spec-push/SKILL.md +306 -0
  36. package/assets/skills/spec-review/SKILL.md +256 -0
  37. package/assets/skills/spec-reviewed/SKILL.md +284 -0
  38. package/assets/skills/spec-start/SKILL.md +496 -0
  39. package/assets/skills/spec-status/SKILL.md +114 -0
  40. package/assets/skills/spec-sync/SKILL.md +220 -0
  41. package/assets/skills/spec-to-main/SKILL.md +141 -0
  42. package/bin/skitterspec-jira.js +68 -0
  43. package/package.json +40 -4
  44. package/src/cli.js +5967 -0
  45. package/src/deprecate.js +138 -0
  46. package/src/env/bootstrap.js +188 -0
  47. package/src/env/building.js +143 -0
  48. package/src/env/classify.js +176 -0
  49. package/src/env/commitcmd.js +108 -0
  50. package/src/env/config.js +850 -0
  51. package/src/env/dev.js +63 -0
  52. package/src/env/glob.js +34 -0
  53. package/src/env/hooks.js +226 -0
  54. package/src/env/hotfix.js +413 -0
  55. package/src/env/hotfixland.js +302 -0
  56. package/src/env/integrate.js +167 -0
  57. package/src/env/live.js +479 -0
  58. package/src/env/mainguard.js +299 -0
  59. package/src/env/provision.js +466 -0
  60. package/src/env/proxy.js +225 -0
  61. package/src/env/prune.js +119 -0
  62. package/src/env/registry.js +159 -0
  63. package/src/env/render.js +17 -0
  64. package/src/env/resolve.js +716 -0
  65. package/src/env/review.js +2622 -0
  66. package/src/env/reviewers/coderabbit.js +228 -0
  67. package/src/env/reviewers.js +517 -0
  68. package/src/env/serve.js +1044 -0
  69. package/src/env/supervise.js +157 -0
  70. package/src/env/teardown.js +248 -0
  71. package/src/env/trust.js +87 -0
  72. package/src/gating.js +155 -0
  73. package/src/init.js +1150 -0
  74. package/src/lines-diff.js +114 -0
  75. package/src/prompts.js +129 -0
  76. package/src/vendor/jira/adf.js +414 -0
  77. package/src/vendor/jira/api.js +295 -0
  78. package/src/vendor/jira/cli-sync.js +1252 -0
  79. package/src/vendor/jira/commands.js +44 -0
  80. package/src/vendor/jira/config.js +286 -0
  81. package/src/vendor/jira/credentials.js +108 -0
  82. package/src/vendor/jira/dialect.js +45 -0
  83. package/src/vendor/jira/mcp.js +129 -0
  84. package/src/vendor/provider-kit/index.js +41 -0
  85. package/src/vendor/provider-kit/src/adapter.js +23 -0
  86. package/src/vendor/provider-kit/src/apply.js +207 -0
  87. package/src/vendor/provider-kit/src/args.js +68 -0
  88. package/src/vendor/provider-kit/src/assignment.js +277 -0
  89. package/src/vendor/provider-kit/src/config.js +251 -0
  90. package/src/vendor/provider-kit/src/credentials.js +404 -0
  91. package/src/vendor/provider-kit/src/doctor.js +35 -0
  92. package/src/vendor/provider-kit/src/http.js +63 -0
  93. package/src/vendor/provider-kit/src/identity.js +93 -0
  94. package/src/vendor/provider-kit/src/list-assignee.js +71 -0
  95. package/src/vendor/provider-kit/src/list-rows.js +35 -0
  96. package/src/vendor/provider-kit/src/md-compare.js +39 -0
  97. package/src/vendor/provider-kit/src/md-tables.js +94 -0
  98. package/src/vendor/provider-kit/src/prompt.js +50 -0
  99. package/src/vendor/provider-kit/src/released.js +219 -0
  100. package/src/vendor/provider-kit/src/specs.js +248 -0
  101. package/src/vendor/provider-kit/src/transport.js +22 -0
  102. package/src/vendor/sync-core/index.js +92 -0
  103. package/src/vendor/sync-core/src/base.js +81 -0
  104. package/src/vendor/sync-core/src/compare.js +338 -0
  105. package/src/vendor/sync-core/src/dialect.js +51 -0
  106. package/src/vendor/sync-core/src/fields.js +36 -0
  107. package/src/vendor/sync-core/src/legacy.js +100 -0
  108. package/src/vendor/sync-core/src/normalize.js +971 -0
  109. package/src/vendor/sync-core/src/push.js +85 -0
  110. package/src/vendor/sync-core/src/retarget.js +278 -0
  111. package/src/vendor/sync-core/src/task-block.js +308 -0
  112. package/src/vendor/sync-core/src/verify.js +67 -0
  113. package/src/vendor/sync-core/src/write.js +191 -0
@@ -0,0 +1,714 @@
1
+ ---
2
+ name: spec
3
+ description: Create a new spec-driven-development spec. Grills the user to a clear, shared understanding of the requirement AND the proposed solution FIRST, then writes one concise, phased, test-included, change-logged spec into specs/backlog/. Use when the user wants to plan a feature, write a spec, capture a requirement, or says "/spec" or "spec this out".
4
+ ---
5
+
6
+ # /spec — author a new spec
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
+
13
+ Produce ONE concise spec in `specs/backlog/`. Do not start coding — this skill
14
+ plans only. Implementation happens later via `/spec-start`.
15
+
16
+ Lifecycle (the governing skills) — status in parentheses:
17
+ `/spec` (writes **Ready** when fully groomed, else Draft; backlog) → `/spec-start`
18
+ (In Progress, in-progress; implement phase 1) → `/spec-complete` (Complete) /
19
+ `/spec-cancel` (Cancelled). See `.claude/rules/spec-planning.md`. (There is no
20
+ separate grooming command — `/spec` grills to a Ready spec directly.)
21
+
22
+ ## Phase 0 — start from a Jira issue (only when asked)
23
+
24
+ **Only when `specs/.core/jira.config.json` exists** *and* the invocation names an
25
+ issue. Otherwise skip this phase entirely and grill from the user's own words.
26
+
27
+ Two ways in:
28
+
29
+ - **`<ISSUE-REF>`** (`PROJ-123`, an issue URL, or a bare key anywhere in the
30
+ arguments) — adopt that issue.
31
+ - **`--from-issue [query]`** — browse the intake inbox: issues carrying
32
+ `intake.label` (what the web app files under). With a `query`, filter that list
33
+ case-insensitively by title. Show the shortlist and let the user pick one.
34
+
35
+ Then:
36
+
37
+ 1. **Exclude what's already adopted.** Run `skitterspec spec-sync linked --json`
38
+ and drop any issue whose identifier is already stamped on a spec. If the user
39
+ named such an issue directly, **stop** and point them at that spec — one issue
40
+ never becomes two specs.
41
+ 2. **Read the issue** with the discovered issue-read tool (`getJiraIssue`):
42
+ title, description, labels, reporter, URL. If Jira isn't connected, say so
43
+ and offer to carry on without it — a spec written from the user's own
44
+ description is still a spec.
45
+ 3. **Route the issue to the right skill.** Match the issue's labels
46
+ (case-insensitively) against two lists, **checking `hotfixLabels` first**:
47
+
48
+ - **`intake.hotfixLabels`** → this is broken in **production**, so it needs a
49
+ fix against the released version, not `main`. Say so, name the matching
50
+ label, and tell the user to run **`/spec-hotfix <ISSUE-REF>`** — then stop.
51
+ - **`intake.bugLabels`** → a bug report, not a feature request. Say so, name
52
+ the label, and tell the user to run **`/spec-bug <ISSUE-REF>`** — then stop,
53
+ without authoring a Feature spec.
54
+
55
+ **Hotfix wins when an issue carries both**, and deliberately: production is the
56
+ more specific destination, and the two mistakes are not equally costly. Routing
57
+ a prod issue to `/spec-bug` produces a fix that lands on `main` and never
58
+ reaches the running version — discovered only when someone asks why it hasn't
59
+ shipped. The reverse is a hotfix branch for something that could have waited,
60
+ which is merely wasteful.
61
+
62
+ With a list unset, nothing routes through it; with both unset every issue is
63
+ treated as a feature request, exactly as before.
64
+
65
+ **Which checks run depends on where you are**, because a skip is about not
66
+ bouncing someone to the skill they are already in — not about ignoring an
67
+ escalation:
68
+
69
+ - **In `/spec`** — both checks run.
70
+ - **In `/spec-bug`** — the bug check is skipped (it would route you to
71
+ yourself), but the **hotfix check still runs**. A bug report labelled for
72
+ production is not "already handled" by being in the bug path: `/spec-bug`
73
+ fixes on `main`, and prod would stay broken. Say so and hand off.
74
+ - **In `/spec-hotfix`** — both are skipped. It is already the most specific
75
+ destination; there is nowhere left to route.
76
+
77
+ Then adopt the issue and reproduce it as usual: the issue body is your repro
78
+ material, and the failing test comes before the spec exactly as normal.
79
+ 4. **Seed, don't skip, the grill.** The issue's title becomes the working spec
80
+ title and its description the starting material for **Problem** — or
81
+ **Symptom** in a bug or hotfix spec, which is where a report belongs. Quote the
82
+ reporter's words rather than paraphrasing them away. Note the reporter and the
83
+ issue URL for context. Everything after this runs exactly as normal: an issue
84
+ is a *request*, not a groomed spec, so grill it as hard as anything else.
85
+
86
+ **In `/spec-hotfix`, also mine the issue for a version.** Any release-shaped
87
+ string in the report is a *suggestion* for the base tag, offered when the skill
88
+ asks which version prod is running — never used as a default. The reporter's
89
+ version is the one they saw the bug on, which is often not what is deployed.
90
+
91
+ ### Adopting the issue
92
+
93
+ The issue **becomes** the spec's issue — it is not copied and no second issue is
94
+ minted. This is identical in `/spec`, `/spec-bug` and `/spec-hotfix`; only *when*
95
+ it happens differs (`/spec` writes the spec in Phase B, `/spec-bug` in its step 4,
96
+ `/spec-hotfix` in its step 5). Once the spec file exists:
97
+
98
+ - **Stamp `jira_identifier` and `jira_url`** in `00-overview.md` frontmatter
99
+ from the adopted issue. That is the whole link: every later skill
100
+ (`/spec-push`, `/spec-status`, `/spec-start`) keys off it being present.
101
+ - **Adoption is not a mint.** The issue was filed somewhere deliberately — it
102
+ stays where it is, whatever `jira.projectKey` says, and nothing re-homes it.
103
+ - **Do not write a base sidecar.** Leaving `sync.baseDir` empty for this spec is
104
+ what makes the **linking push** send the spec over the reporter's original
105
+ description (an **update** to the existing issue, plus a subtask per phase).
106
+ Recording a snapshot here would declare the mirror already in sync and strand
107
+ the issue showing the raw report forever.
108
+ - **Preserve the original, before anything replaces it.** Run this the moment
109
+ the spec file exists and **before the linking push**:
110
+
111
+ ```
112
+ skitterspec spec-sync preserve <spec>
113
+ ```
114
+
115
+ It posts the issue's current description onto the issue as a comment — the one
116
+ surface one-way sync never touches, so no push can clobber it. On the API path
117
+ it reads the description itself; without credentials it prints the body and
118
+ the marker, and you post it with the discovered Jira comment tool after
119
+ checking the comments for that marker.
120
+
121
+ **The ordering is the whole correctness condition.** Run before the push, this
122
+ keeps what the reporter filed. Run after it, it would keep the generated spec
123
+ and report success — which is why it is a step of its own here rather than a
124
+ line in the push sequence. The engine warns when it can tell it is late, but it
125
+ cannot always tell.
126
+
127
+ **It never fails the adoption.** A project that set
128
+ `intake.preserveOriginal: false`, an unreachable Jira, an issue with no
129
+ description, a comment Jira refused — every one of them exits 0 and says so.
130
+ Relay what it printed and carry on.
131
+ - **Say what will happen** in the finish-up message. The linking step runs right
132
+ after the spec is written, so the issue's description is replaced by the spec
133
+ **then** — not on some later manual push. Say both halves: the description
134
+ becomes the spec, **and** the original is preserved as a comment on the same
135
+ issue, so the reporter can still read what they wrote.
136
+
137
+ **Do not send anyone to the issue's history for it.** This used to say the
138
+ tracker keeps the original there, and that is true and useless: history is a
139
+ diff viewer nobody opens, it is not quotable, and it degrades to "it is in
140
+ there somewhere". The comment is the answer, and the other half is the spec's
141
+ own **Problem** (or **Symptom**) section quoting the reporter.
142
+
143
+ ## Phase A — reach a clear shared understanding (grill first)
144
+
145
+ Interview the user until requirement AND proposed solution are unambiguous. Do
146
+ not write the spec until this is resolved.
147
+
148
+ - Break the problem into **distinctive areas** and work them in logical order,
149
+ resolving dependencies between decisions one at a time.
150
+ - **Batch independent questions; sequence dependent ones.** When several
151
+ questions do not affect each other's answers, put them to the user together
152
+ (up to four at once — use the harness's multi-question ask tool where it has
153
+ one) rather than spending a round trip on each. When an answer would change
154
+ what you ask next, ask that one alone and wait for it. Give your
155
+ **recommended answer** either way.
156
+ - If a question can be answered by **reading the codebase, read it** instead of
157
+ asking. Verify endpoints/models/files actually exist before relying on them.
158
+ - Cover, at minimum, the areas that apply:
159
+ 1. **Problem & why** — what's broken/missing, who feels it, why now.
160
+ 2. **Scope & non-goals** — explicit out-of-scope items.
161
+ 3. **Affected areas** — concrete files/modules/packages this touches.
162
+ 4. **Proposed solution shape** — the chosen approach and the alternatives
163
+ rejected, with the reason (this becomes "Decisions").
164
+ 5. **Data / API impact** — schema/model changes, new endpoints, and
165
+ **backward compatibility** (additive = safe; breaking = needs explicit
166
+ permission and coordination).
167
+ 6. **Security & multi-tenancy** — authz, tenant scoping, untrusted input.
168
+ 7. **Edge cases & failure modes.**
169
+ 8. **Testing approach** — what proves each phase correct.
170
+ 9. **Isolation stack** *(only when `specs/.core/env.config.json` exists)* — does
171
+ this spec touch the DB / stateful services (so its worktree needs a Docker
172
+ stack), or is a plain worktree enough? Default `worktree`; escalate to
173
+ `worktree + docker` only when it must. This sets the `> **Stack:**` header
174
+ that `/spec-start` acts on (it can be escalated later). Skip when isolation
175
+ isn't enabled — leave the default `worktree`.
176
+ 10. **Release gating** *(only when `specs/.core/gating.config.json` exists)* —
177
+ should this ship behind a feature flag, or land live?
178
+ **Offer, don't impose**: the user decides and you raise it, so a spec
179
+ never reaches
180
+ `/spec-complete` with the question unasked. Cite the project's own
181
+ `guidance` path from that config when it names one — skitterspec knows
182
+ nothing about how this project does flags, and must not guess. Record the
183
+ answer **either way**: a flag name, or `none: <one-line reason>`. "No" is a
184
+ decision and belongs in the header; silence is not. Skip entirely when the
185
+ config is absent — that project does not use flags.
186
+ 11. **Open questions** — anything still undecided.
187
+
188
+ Stop grilling when there are no unresolved branches that would change the spec.
189
+ Briefly play back the agreed understanding before writing.
190
+
191
+ ## Phase B — write the spec
192
+
193
+ This skill is for **features**. For bugs, use `/spec-bug` (test-first, red→green).
194
+
195
+ ### Write it in its own worktree
196
+
197
+ **Only when the project has per-spec isolation** (`specs/.core/env.config.json`
198
+ present). Without it there is no worktree to make and this section does not
199
+ apply — write the folder where you are standing, exactly as before.
200
+
201
+ **Provision before you write, not after.** Grilling put nothing on disk and the
202
+ spec's name is settled by the time Phase A ends, so this is the one moment where
203
+ a worktree costs nothing to obtain and everything is still ahead of it:
204
+
205
+ ```
206
+ skitterspec spec-env up <name> --docs
207
+ ```
208
+
209
+ Run the commands it prints under `to provision, run:`, and the
210
+ `skitterspec spec-env bootstrap <name> --docs` under `then, run:` where there is
211
+ one (it seeds the gitignored files, nothing more). Then move this session into
212
+ the worktree with a plain `cd` — the Bash working directory persists between
213
+ calls, so from here on the spec is written on its own branch. Confirm the move landed rather than
214
+ reading silence as success (`.claude/rules/negative-checks.md` rule 1):
215
+ `skitterspec spec-env resolve` with no argument must name this spec. If it does
216
+ not, say so and write the spec where you are standing instead; a spec in the
217
+ wrong tree is worse than a spec with no worktree.
218
+
219
+ **`--docs` is what makes this cheap.** It skips the `setup` commands and the
220
+ Docker stack — everything whose only purpose is making a tree *runnable* — so
221
+ provisioning a worktree to write markdown in is a `git worktree add` and nothing
222
+ else. `/spec-start` re-runs `up` over the same worktree without the flag, and the
223
+ setup runs then, at the first moment the tree is used for code.
224
+
225
+ **Why the base branch is no longer where this happens.** `main` is where work
226
+ **lands**, not where it happens. Authoring on it meant the spec sat uncommitted
227
+ there for as long as Phase C2's page went unanswered — which is unbounded by
228
+ design, because a reader walking away from a diff is the normal case — and a
229
+ release could not be cut through it. The verdict lands the spec instead, so
230
+ `main` gains it as one commit and is never dirty on the way.
231
+
232
+ **A spec written in ANOTHER spec's worktree is still the mistake it always was**,
233
+ and this section does not soften that. It physically lives on that branch: not on
234
+ `main` until that spec lands, invisible to anyone listing `specs/backlog/`
235
+ meanwhile, and cancelled along with its host. Committing it there also mis-stamps
236
+ the ticket trailer, which is resolved from the branch (see
237
+ `.claude/rules/commit-trailers.md`, installed with a ticketing provider). The
238
+ difference is whose worktree: **its own** is now the normal path, and someone
239
+ else's is the accident.
240
+
241
+ So if Phase A ran inside another spec's worktree — most likely because a design
242
+ question came up part-way through implementing it — the provision above is the
243
+ fix rather than something to warn about: it moves the session out of that tree
244
+ and into this spec's own. Where isolation is off and there is no worktree to
245
+ move to, **warn, don't refuse**, and say the trailer for that commit wants
246
+ `spec-sync ref <new-spec-name>` rather than the bare form.
247
+
248
+ - **Every spec is a folder** — never a bare file, even for a one-line change:
249
+ `specs/backlog/feat-<kebab-name>/`. Create it with `mkdir -p`.
250
+ - The entry point is **always `00-overview.md`** — the index/dashboard for the
251
+ spec. It holds the header block, Problem, Decisions, Solution overview, the
252
+ **phase index** (a table linking to each phase file), Open questions, State
253
+ log, and Changelog. It does **not** hold the per-phase task lists.
254
+ - **Each phase is its own file** — `01-<phase-slug>.md`, `02-<phase-slug>.md`, …
255
+ numbered in execution order; the slug is a short kebab description of the phase
256
+ goal (e.g. `01-data-model.md`, `02-api-endpoints.md`). The phase file holds
257
+ that phase's goal, its task checkboxes (tests included), and any phase-specific
258
+ notes. **Even a single-phase spec gets `01-….md`** — never lump phase tasks
259
+ into `00-overview.md`. This keeps each phase easy to dive into on its own.
260
+ - Choose a short kebab-case name and **prefix it `feat-`** (the bug counterpart
261
+ uses `bug-`).
262
+
263
+ Use this template (keep it **as concise as possible** — no filler, no restating
264
+ the codebase, link rather than duplicate):
265
+
266
+ ```markdown
267
+ # <Feature title>
268
+
269
+ > **Type:** Feature
270
+ > **Name:** feat-<kebab-name> (the spec folder name — the handle you paste into `/spec-start`)
271
+ > **Status:** Ready — not started
272
+ > **Author:** <git user.name — `git config user.name`>
273
+ > **Developer:** —
274
+ > **Raised:** <YYYY-MM-DD (today)>
275
+ > **Area:** <comma-separated files/modules this touches>
276
+ > **Stack:** <worktree — or "worktree + docker" if it touches the DB/stateful
277
+ > services; only acted on when isolation is enabled — see Phase A item 9>
278
+ > **Gating:** <flag name — or "none: <one-line reason>". Only when release gating
279
+ > is configured; omit the line entirely otherwise. An empty value or a bare
280
+ > "none" is not a valid outcome — see Phase A item 10>
281
+
282
+ ## Problem
283
+
284
+ <2–6 sentences: what's wrong/missing and why it matters. No fluff.>
285
+
286
+ ## Decisions
287
+
288
+ <Numbered, confirmed decisions from Phase A. Each: the choice + one-line why,
289
+ and the rejected alternative when it sharpens the choice. This is the heart of
290
+ the spec — be specific.>
291
+
292
+ ## Solution overview
293
+
294
+ <Short prose or bullets describing the chosen shape end-to-end. Optional small
295
+ schema/grammar/output snippets where they remove ambiguity.>
296
+
297
+ ## Impact
298
+
299
+ <The concrete surfaces this spec touches — the scannable blast radius, so a
300
+ reader can eyeball where the spec got something wrong without reading prose.
301
+ `Change` is `add` · `update` · `remove`. `Surface` is guided-but-open: use
302
+ values like Endpoint, Route/UI, Schema/model, DB table/migration, Domain object,
303
+ Service, CLI command, Config key, Skill/rule, Business rule — or whatever fits
304
+ this project (skitterspec itself is a CLI with no HTTP surface). Keep `Detail`
305
+ terse — names/signatures, not sentences. List **only** surfaces that actually
306
+ change; the heading is always present, but if nothing external changes write the
307
+ single line below instead of an empty table.>
308
+
309
+ | Surface | Change | Detail |
310
+ |---------|--------|--------|
311
+ | <e.g. Endpoint> | add | <e.g. POST /orders> |
312
+ | <e.g. DB> | update | <e.g. orders (+status col)> |
313
+
314
+ <_No external surface changes — internal refactor only._ — use this line in
315
+ place of the table when the spec touches no external surface.>
316
+
317
+ ## Phases
318
+
319
+ Each phase lives in its own file in this folder. Status: ⬜ not started ·
320
+ 🔄 in progress · ✅ done.
321
+
322
+ | # | Phase | Status | File |
323
+ |---|-------|--------|------|
324
+ | 1 | <goal> | ⬜ | [01-<phase-slug>.md](01-<phase-slug>.md) |
325
+ | 2 | <goal> | ⬜ | [02-<phase-slug>.md](02-<phase-slug>.md) |
326
+
327
+ ## Open questions
328
+
329
+ - [ ] <anything deferred — or "None">
330
+
331
+ ## State log
332
+
333
+ | Date | Status | Folder | By |
334
+ |------|--------|--------|----|
335
+ | <YYYY-MM-DD> | Ready | backlog | <author> |
336
+
337
+ ## Changelog
338
+
339
+ - <YYYY-MM-DD> — Spec created.
340
+ ```
341
+
342
+ Then create **one file per phase** (`01-<phase-slug>.md`, `02-…`, in execution
343
+ order). Each phase file uses this template:
344
+
345
+ ```markdown
346
+ # Phase 1 — <goal> ⬜
347
+
348
+ > Spec: [00-overview.md](00-overview.md) · **Status:** Not started
349
+
350
+ **Goal:** <one line — what this phase delivers and how it's proven>.
351
+
352
+ ## Tasks
353
+
354
+ - [ ] <clear, verb-first task>
355
+ - [ ] <clear, verb-first task>
356
+ - [ ] Add/extend tests covering this phase; run the project's typecheck and
357
+ test commands (see `.claude/rules/spec-planning.md`) — green before the
358
+ phase is done.
359
+
360
+ ## Notes
361
+
362
+ <Phase-specific decisions, gotchas, or context. Delete if empty.>
363
+ ```
364
+
365
+ Keep the `00-overview.md` phase index and the phase files in sync: the index row
366
+ is the one-line summary + status; the phase file is the detail.
367
+
368
+ The **State log** is the audit trail of folder/status transitions — every
369
+ lifecycle skill (`/spec-start`, `/spec-complete`, `/spec-cancel`) appends one row
370
+ when it moves the spec. The **Changelog** is for decisions and course-corrections
371
+ only — keep the two separate.
372
+
373
+ Rules for the spec body:
374
+
375
+ - **Every phase is independently shippable and ends with tests.** A phase is
376
+ not "done" until its tests are written and the suite is green. Bake a test
377
+ task into each phase — never a separate "testing phase" at the end only.
378
+ - **Tasks are checkboxes** (`- [ ]`), clear, verb-first, and granular enough to
379
+ finish in one session. They live in the **phase files**, not the overview. Use
380
+ `⬜`/`🔄`/`✅` on each phase-file heading and mirror it in the `00-overview.md`
381
+ phase index.
382
+ - **Honour project conventions** when writing tasks — reference the relevant
383
+ `.claude/rules/*.md` rather than re-explaining them.
384
+ -
385
+ **The `## Impact` table is derived from Phase A items 3 (Affected areas) & 5 (Data/API impact)**
386
+ — a structured place to record what those already surface, not new grilling.
387
+ It is the scannable substitute for spelling impact out in prose: name the
388
+ surfaces (endpoints, schemas, DB tables, domain objects, routes, business
389
+ rules) instead of describing them, keep `Detail` terse, and let it — not
390
+ paragraphs — carry the blast radius. It complements the `Area:` header
391
+ (files) by naming behavioural surfaces.
392
+ - **Changelog** is mandatory and lives in the spec. Every later decision or
393
+ course-correction gets a dated one-line entry. Convert relative dates to
394
+ absolute.
395
+ - Keep it tight. If a section adds no information, delete it.
396
+
397
+ ## Phase C — finish up
398
+
399
+ Decide the status: **`Ready`** in `backlog` when grilling in Phase A resolved the
400
+ open questions, `Draft` when you deliberately left some unresolved. Either way
401
+ the next step is `/spec-start`. The Report section below is where all of that
402
+ reaches the user — do not narrate it here as well.
403
+
404
+ ## Phase C2 — render the spec, then wait for the verdict
405
+
406
+ **Only when the project has per-spec isolation** (`specs/.core/env.config.json`
407
+ present). Without it there is no page and this phase does not exist — skip it in
408
+ silence rather than explaining an absence.
409
+
410
+ A spec is the one artefact whose whole purpose is to be
411
+ **read before work begins**, and until now it was the only one with no reading
412
+ surface: `/spec`
413
+ finished, the spec sat uncommitted, and `/spec-start` had to point that out and
414
+ commit it as a side effect of provisioning. So this phase ends where every other
415
+ phase of the lifecycle ends — on a page, in a verdict.
416
+
417
+ ```
418
+ skitterspec spec-env review <spec> --docs --buttons authoring
419
+ ```
420
+
421
+ `--docs` reads the spec's own documents from the tree you are standing in, which
422
+ after Phase B is **this spec's own worktree**. It renders
423
+ **this spec's documents and nobody else's**: the `owned` half of
424
+ `spec-env stage` is what supplies the file set, and it stays load-bearing even
425
+ now that the tree holds one spec — a project's `spec.companionPaths` and a
426
+ hand-edited `specs/.core/` both land in the same tree, and only the spec's own
427
+ documents belong under this verdict.
428
+
429
+ **This is free.** The engine reads git and splices text into a template; the
430
+ diff never passes through you.
431
+
432
+ **Never pass `--run-reviewers` here.** A spec is prose, and the reviewers
433
+ `review.reviewers` names read code — spending a rate-limited review on a
434
+ paragraph buys nothing. The engine refuses it on a `--docs` render whatever this
435
+ skill asks for, so this is a note about intent rather than a guard you are
436
+ holding: do not add the flag on the assumption that it was an oversight.
437
+
438
+ **Render nothing when nothing was written.** A `⏸` run — grilling that never
439
+ reached a shared understanding — has no spec to show, and must not ask for a
440
+ verdict on one.
441
+
442
+ **Arm nothing.** There is no `spec-env review arm` in this path, and that is a
443
+ decision rather than an omission: the gate asserts that *a phase which ended*
444
+ owes an answer, and a backlog spec owes no phase. A `/spec` run must leave
445
+ `spec-env review gate --check` exiting 0.
446
+
447
+ **And walking away from this page now costs nobody anything**, which is a
448
+ stronger reason than the one that used to sit here. It read *"the spec is simply
449
+ still uncommitted, which `/spec-start` already handles"* — true, and a
450
+ concession: an uncommitted spec meant a dirty base branch, which is everyone's
451
+ problem, so "arm nothing" was tolerating a mess rather than describing a clean
452
+ state. Since Phase B the unanswered spec sits on its own branch in its own
453
+ worktree, where it is in nothing's way: no release is blocked, no other spec has
454
+ to replay over it, and it is still exactly where it was tomorrow morning.
455
+
456
+ ### The two endings this page offers
457
+
458
+ `--buttons authoring` offers `Commit & Start` and `Commit` in place of the
459
+ committing pair, because "commit and build the next phase" is meaningless for a
460
+ spec with no phase in flight.
461
+
462
+ - **`commit-start`** — hand off to `review.commitWith` (`/commit` by default),
463
+ passing the pathspec the render reported on `docs.paths`; **land it** (below);
464
+ then **keep the worktree** and run **`/spec-start <name>`**, and **stop there**.
465
+ It never completes or tears anything down, exactly as `commit-continue` stops
466
+ after `/spec-next`. This is the `commit && /spec-start` that was typed by hand.
467
+ - **`commit`** — the same commit and the same land, then
468
+ **tear the worktree down** and finish. The spec stays `Ready` in `backlog` —
469
+ on the base branch, where anyone can see it — ready for a `/spec-start`
470
+ whenever it is wanted.
471
+
472
+ **Both committing verdicts land, and that is what keeps the backlog findable.**
473
+ `ls specs/backlog/` is how specs are found, and a folder bucket is only the truth
474
+ on the branch you are standing on — so a spec left on its own branch would be
475
+ invisible to everyone, including the next `/spec` run. The commit goes on the
476
+ branch and then the branch goes onto the base:
477
+
478
+ ```
479
+ skitterspec spec-env integrate <name>
480
+ ```
481
+
482
+ It plans a rebase onto the base branch in the worktree, then a
483
+ `merge --ff-only` in the primary checkout — run the commands it prints. It
484
+ refuses on a dirty tree, which is correct here: the commit ran first, so anything
485
+ still uncommitted at this point is not this spec's and the land must not proceed.
486
+
487
+ **Do not run its after-land section.** Where the plan ends with
488
+ `then, in the primary checkout (<path>), run:`, those are the project's
489
+ `afterLand` commands — a data migration, an install — and they exist for code
490
+ that landed. This land is documents only, so it changes nothing they would
491
+ catch up on; `/spec-complete`, `/spec-to-main` and `/no-spec` run them.
492
+
493
+ **If the rebase conflicts, stop and say so.** It is unlikely on a
494
+ documents-only branch and not impossible — two specs authored at once can both
495
+ touch `specs/.core/`. Run `git rebase --abort`, leave the commit sitting on the
496
+ branch, and report `❌` with the conflict: there is a standing worktree to clear,
497
+ which is what separates `❌` from `⏸`. Never resolve someone else's conflict to
498
+ get the land through.
499
+
500
+ **The worktree lifetime follows the verdict, and the asymmetry is deliberate.**
501
+ `commit-start` keeps it because you are carrying straight on, and `/spec-start`
502
+ re-runs `up` and `bootstrap` over it without `--docs`, which is where the setup commands
503
+ Phase B skipped finally run. Plain `commit` tears it down with
504
+ `skitterspec spec-env down <name>`, because a spec parked in the backlog should
505
+ hold no worktree and no `node_modules`; `/spec-start` provisions from scratch
506
+ whenever it is wanted. Always-keep would leave five backlog specs holding five
507
+ checkouts, and always-teardown would have `commit-start` re-provision seconds
508
+ after destroying the tree it needed.
509
+ - **`changes`** — work the notes into the spec, record a resolution for each one
510
+ so the next render shows it struck through with what changed, then
511
+ **re-render, and wait again**. The reader is still holding a decision, so
512
+ ending the turn and
513
+ making them type `/spec-diff` would reopen the loop this exists to close.
514
+ - **`discuss`** — report and talk. Claim nothing, change nothing.
515
+
516
+ **Take the pathspec from the render, not from the tree.** A checkout is shared,
517
+ so the set of this spec's uncommitted documents can differ between the render and
518
+ the verdict — and the reader's conclusion is about what the page showed them.
519
+
520
+ ### Then wait for it
521
+
522
+ **The wait is a command. Do not write one.**
523
+
524
+ 1. **Note the moment you start waiting**, as an ISO timestamp. That instant is
525
+ the whole scope of what you may claim.
526
+ 2. **Run the engine's wait, and end your turn** — under the harness's
527
+ persistent watch primitive where one exists, else in the background
528
+ (`.claude/rules/spec-reports.md` carries the preference):
529
+
530
+ ```
531
+ skitterspec spec-env review wait <spec> --since <the timestamp>
532
+ ```
533
+
534
+ It takes no timeout unless you pass one, and you must not pass one: a reader
535
+ who walks away from a spec is the normal case, and a bounded watch once lost a
536
+ verdict to a lunch break.
537
+ 3. **On waking, let the engine pick:**
538
+
539
+ **A watch that dies is re-armed on the same window**, silently and bounded by
540
+ the age of that window — `.claude/rules/spec-reports.md` carries the contract,
541
+ including when to stop and what the banner says instead. Do not restate it
542
+ here.
543
+
544
+ ```
545
+ skitterspec spec-env review <spec> --docs --claim-since <the timestamp> --json
546
+ ```
547
+
548
+ It claims the one pass that arrived inside the window, acts on nothing when
549
+ none did, and refuses to choose when two did.
550
+ 4. **Route on the verdict** as above.
551
+
552
+ **Where the page is `file://`** — the engine could not serve — there is no server
553
+ to POST to, so the pass is copied and pasted and the reader's next message is
554
+ what carries it. **Do not start a watch that cannot fire**: a wait on the pending
555
+ store would spin forever while the report underneath it claimed to be holding,
556
+ which is the exact failure `.claude/rules/spec-reports.md` records. The wait is
557
+ the turn ending, and the banner says so.
558
+
559
+ Relay the engine's **stack** — the `local:`, `network:` and `remote:` lines, all
560
+ three, in that order — never the bare `page:` path. `.claude/rules/spec-reports.md`
561
+ carries the shape and why every tier is named rather than one being chosen.
562
+
563
+ ## Phase D — record the isolation stack (only if configured)
564
+
565
+ **Only when `specs/.core/env.config.json` exists** (per-spec isolation is
566
+ enabled), make sure the `> **Stack:**` header reflects the Phase A item 9
567
+ decision — `worktree` (default) or `worktree + docker` when it touches the DB /
568
+ stateful services. Nothing to provision now: `/spec-start` gives every in-progress
569
+ spec its own worktree automatically, and brings up Docker only when the Stack
570
+ says so. Mention the operator can escalate the Stack later (edit the header, or
571
+ run `skitterspec spec-env up <name>` to add Docker to an existing worktree). If
572
+ `env.config.json` is absent, isolation is off — leave the default `worktree` and
573
+ finish as above.
574
+
575
+ ## Phase D2 — record the gating decision (only if configured)
576
+
577
+ **Only when `specs/.core/gating.config.json` exists.** Make sure the
578
+ `> **Gating:**` header carries the Phase A item 10 answer — a flag name, or
579
+ `none: <reason>` using the config's `default` wording if it sets one. Nothing is
580
+ provisioned or enforced by this: the header exists so the decision is
581
+ **on the record and reviewable**, and `skitterspec gating check` reports a spec that has
582
+ none. It never blocks. If the config is absent, do not write the line at all.
583
+
584
+ ## Phase E — link to a ticketing provider (only if one is installed)
585
+
586
+ **Only when a ticketing provider is installed and configured** (it ships the
587
+ `/spec-push` · `/spec-status` skills and a provider config under
588
+ `specs/.core/`). If none is present, skip this phase entirely — the spec stays
589
+ local-only and `/spec` behaves exactly as above. When a provider is present, link
590
+ the spec to the tracker after writing it, so status and discussion live there
591
+ while the repo stays the source of truth — follow the provider's link steps
592
+ below (nothing to do here without one).
593
+
594
+ **Only when `specs/.core/jira.config.json` exists** (Jira sync is opted in).
595
+ If it's absent, skip this entirely — the spec stays local-only and the skill
596
+ behaves exactly as above. When present, link the spec you just wrote so status
597
+ and discussion live in Jira while the repo stays the source of truth. A spec is
598
+ a Jira **issue**; each phase is a **subtask**, carrying that phase's tasks in
599
+ its description as a read-only checklist.
600
+
601
+ **Linking is just the first push**, so it runs the same engine path `/spec-push`
602
+ does — you never create the issue by hand:
603
+
604
+ 1. **Pick the transport.** `skitterspec spec-sync states --json`. With Jira
605
+ credentials set it answers `api` and prints the project's status names;
606
+ without them it answers `mcp` and you do the MCP work `/spec-push` describes.
607
+ Write the status names to a file for step 2.
608
+ 2. **Get the plan.**
609
+ `skitterspec spec-sync plan <spec> --workspace-states <file> --json > plan.json`
610
+ — the spec is unlinked, so this plan is all-creates: the issue and one
611
+ subtask per phase.
612
+ 3. **Apply it.**
613
+ `skitterspec spec-sync apply <spec> --plan plan.json`. There is no project
614
+ to pick: the issue lands in the configured `jira.projectKey` by construction.
615
+ That creates the issue and its subtasks and checks what Jira stored. It then
616
+ **stamps the keys into the spec** — `jira_identifier`/`jira_url` on the
617
+ overview, `jira_issue_id` on each phase — and **records the base snapshot**,
618
+ so `/spec-status` reports in-sync immediately. There is no hand-editing of
619
+ frontmatter and no separate `stamp` or `record` call.
620
+
621
+ If it prints `transport = mcp`, it wrote nothing: apply the plan over MCP as
622
+ `/spec-push` steps 4a–5 describe, ending with `spec-sync stamp` and
623
+ `spec-sync record`. That path is fully supported — it is what anyone without
624
+ credentials uses.
625
+ 4. **Echo the branch name** from `branch.pattern` so the user knows what
626
+ `/spec-start` will fork.
627
+
628
+ **If Jira can't be reached**, say so in one line and leave the spec written and
629
+ local — it is still a perfectly good spec, and `/spec-push` links it later. Do
630
+ nothing destructive.
631
+
632
+ **A spec that adopted an existing issue** (see the intake step) is already
633
+ stamped, so its plan is an **update**, not a create: applying it replaces the
634
+ reporter's description with the spec. That is the one-way rule working as
635
+ intended — the repo is canonical, and the original was preserved as a comment
636
+ on the issue by the intake step. Nothing re-homes an adopted issue: where it
637
+ lives stays Jira's.
638
+
639
+ Leave committing to the existing convention (the user commits the spec as usual)
640
+ and **never auto-push git** — Jira's own automation reacts to real branch/PR
641
+ events later. Report the Jira issue URL as part of the skill's finish-up
642
+ message.
643
+
644
+ Jira has no project picker: a minted issue lands in the configured
645
+ `jira.projectKey` by construction, decided once in `specs/.core/jira.config.json`.
646
+ There is nothing to ask.
647
+
648
+ ## Report
649
+
650
+ End with the block defined in `.claude/rules/spec-reports.md`. That file carries
651
+ the shape; this section carries only what is specific here.
652
+
653
+ **Verdicts**
654
+
655
+ - `✅` — a `Ready` spec is written to `specs/backlog/<name>/`.
656
+ - `⚠️` — written as `Draft`; open questions were deliberately left. Name them.
657
+ - `❌` — the spec was committed and the land failed part-way: the rebase
658
+ conflicted, or the fast-forward was refused. There is a standing worktree and a
659
+ branch to deal with, so say where both are.
660
+ - `⏸` — grilling did not reach a shared understanding, so nothing was written.
661
+ That is the skill working: a spec written over an unresolved requirement is
662
+ the outcome Phase A exists to prevent.
663
+
664
+ **Fields:** `Tracker` · `Spec` · `Built` · `Landed` · `Worktree` · `Follow-ups` ·
665
+ `Next`
666
+
667
+ `Built` is the spec's path and phase count; `Spec` is its status and bucket;
668
+ `Tracker` appears only when a provider is installed and linked it.
669
+
670
+ `Landed` and `Worktree` belong to the **verdict** half of the run and appear
671
+ only once one has been acted on — a run that is still waiting on the page has
672
+ neither. `Landed` says the base branch fast-forwarded and to what; `Worktree`
673
+ says kept (on `commit-start`) or removed (on `commit`), with the path. Neither
674
+ appears where isolation is off: nothing was provisioned and nothing was landed.
675
+
676
+ **`Next` depends on whether Phase C2 rendered a page.** Where it did, the run is
677
+ waiting on a verdict, so the block **omits the `Review` row** and ends on the
678
+ banner defined in `.claude/rules/spec-reports.md` — counts, the tier stack, and
679
+ this page's two exits:
680
+
681
+ ---
682
+
683
+ ## ⏸ Review ready — &lt;N&gt; files, +&lt;a&gt; −&lt;d&gt;
684
+
685
+ - **local** — &lt;the `local:` URL&gt;
686
+ - **network** — &lt;the `network:` URL, or off with the command that turns it on&gt;
687
+ - **remote** — &lt;the `remote:` URL, or off with the command that turns it on&gt;
688
+
689
+ I'm holding here until you send a verdict — the wait covers local and network.
690
+
691
+ `Commit & Start` puts it in flight · `Commit` keeps it for later
692
+
693
+ ---
694
+
695
+ There `Next` names the page rather than a command, because the button is what
696
+ carries the work on.
697
+
698
+ **Neither exit leaves the spec anywhere awkward**, and the banner does not
699
+ explain that — the whole point of Phase B is that there is nothing to warn
700
+ about. Both buttons commit and land, so either way the spec reaches the base
701
+ branch as one commit; the only difference is whether the worktree stays.
702
+
703
+ Where no page was rendered — isolation is not configured, or nothing was
704
+ written — `Next` is `/spec-start <name>`, with the name spelled the way it must
705
+ be typed.
706
+
707
+ **A spec written without a page is still uncommitted**, so that `Next` relies on
708
+ `/spec-start` committing the spec when it is all that is uncommitted. Say
709
+ `/commit, then /spec-start <name>` when anything else is uncommitted too, or the
710
+ row sends the reader to a refusal.
711
+
712
+ **`Follow-ups` is almost always `none` here.** This skill's whole job is to
713
+ capture work, so anything it surfaced belongs in the spec it just wrote rather
714
+ than in a follow-up line beneath it.