@skitterbyte/skitterspec-linear 1.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 (45) hide show
  1. package/README.md +56 -0
  2. package/assets/claude-md-section.md +39 -0
  3. package/assets/core/env.config.json.example +28 -0
  4. package/assets/core/env.config.md +99 -0
  5. package/assets/core/linear.config.json.example +39 -0
  6. package/assets/core/linear.config.md +121 -0
  7. package/assets/rules/spec-planning.md +152 -0
  8. package/assets/skills/spec/SKILL.md +232 -0
  9. package/assets/skills/spec-bug/SKILL.md +110 -0
  10. package/assets/skills/spec-cancel/SKILL.md +61 -0
  11. package/assets/skills/spec-complete/SKILL.md +87 -0
  12. package/assets/skills/spec-env/SKILL.md +63 -0
  13. package/assets/skills/spec-env-down/SKILL.md +64 -0
  14. package/assets/skills/spec-go/SKILL.md +134 -0
  15. package/assets/skills/spec-init/SKILL.md +84 -0
  16. package/assets/skills/spec-pull/SKILL.md +46 -0
  17. package/assets/skills/spec-push/SKILL.md +53 -0
  18. package/assets/skills/spec-ready/SKILL.md +50 -0
  19. package/assets/skills/spec-review/SKILL.md +69 -0
  20. package/assets/skills/spec-status/SKILL.md +46 -0
  21. package/bin/skitterspec-linear.js +26 -0
  22. package/package.json +38 -0
  23. package/src/cli.js +495 -0
  24. package/src/deprecate.js +138 -0
  25. package/src/env/config.js +165 -0
  26. package/src/env/integrate.js +46 -0
  27. package/src/env/provision.js +76 -0
  28. package/src/env/registry.js +95 -0
  29. package/src/env/render.js +26 -0
  30. package/src/env/resolve.js +202 -0
  31. package/src/env/teardown.js +109 -0
  32. package/src/env/trust.js +87 -0
  33. package/src/init.js +311 -0
  34. package/src/prompts.js +56 -0
  35. package/src/vendor/linear/cli-sync.js +256 -0
  36. package/src/vendor/linear/config.js +198 -0
  37. package/src/vendor/linear/mcp.js +112 -0
  38. package/src/vendor/sync-core/index.js +35 -0
  39. package/src/vendor/sync-core/src/apply.js +66 -0
  40. package/src/vendor/sync-core/src/base.js +83 -0
  41. package/src/vendor/sync-core/src/compare.js +99 -0
  42. package/src/vendor/sync-core/src/normalize.js +249 -0
  43. package/src/vendor/sync-core/src/pull.js +84 -0
  44. package/src/vendor/sync-core/src/push.js +106 -0
  45. package/src/vendor/sync-core/src/write.js +86 -0
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @skitterbyte/skitterspec-linear
2
+
3
+ Spec-driven development for [Claude Code](https://claude.com/claude-code), **with
4
+ Linear hybrid-sync**. A strict **superset** of
5
+ [`@skitterbyte/skitterspec`](https://www.npmjs.com/package/@skitterbyte/skitterspec):
6
+ everything in the base filesystem workflow, plus git-like sync between a spec and
7
+ its linked Linear project.
8
+
9
+ ```sh
10
+ npx @skitterbyte/skitterspec-linear init
11
+ ```
12
+
13
+ Install **this OR the base**, never both — this package contains the entire base.
14
+
15
+ ## What the superset adds
16
+
17
+ On top of the base skills (`/spec`, `/spec-go`, isolation, …):
18
+
19
+ - **`/spec-status`** — read-only, per-field divergence (local-only / remote-only /
20
+ conflict / in-sync). Changes nothing.
21
+ - **`/spec-pull [--force]`** — Linear → repo. Applies remote-only fields; refuses
22
+ to clobber a conflicting local edit unless `--force`.
23
+ - **`/spec-push [--force]`** — repo → Linear. Ownership-respecting,
24
+ concurrency-checked; refuses if Linear moved since base unless `--force`.
25
+ - **`spec-sync` CLI** (`skitterspec-linear spec-sync …`) — the deterministic
26
+ engine behind the skills, for CI / local runs.
27
+
28
+ The shared `/spec` and `/spec-go` skills come composed with the Linear steps
29
+ filled in: `/spec` links a new spec to a Linear Project (a Milestone per phase),
30
+ and `/spec-go` pulls first so you build against the current shared state.
31
+
32
+ ## Opt-in
33
+
34
+ Linear sync is inert until `specs/.core/linear.config.json` exists — copy the
35
+ scaffolded `linear.config.json.example` and fill in your team / initiative IDs
36
+ (every field is documented in `specs/.core/linear.config.md`). Without it, this
37
+ behaves exactly like the base.
38
+
39
+ **Mapping** (config-driven): spec folder → Linear **Project**; each phase → a
40
+ **Milestone**; tasks → **Issues**; an optional **Initiative** groups specs.
41
+ **Field ownership** (`both` / `pull` / `push`) collapses conflicts — only a `both`
42
+ field that moved on both sides is a real conflict, and `--force` backs up the
43
+ losing side before winning. **Base sidecars** (`specs/.core/linear-base/`) are
44
+ committed; **backups** (`specs/.core/linear-backups/`) are gitignored.
45
+
46
+ Branch naming that embeds the Linear id lives in the isolation config
47
+ (`env.config.json` → `branch.pattern` with `{identifier}`, `branch.identifierField:
48
+ "linear_identifier"`), not in `linear.config.json`.
49
+
50
+ ## Migrating from `@skitterbyte/skitterspec` v1
51
+
52
+ If you used Linear sync on the old base, switch here and re-run `init` — see
53
+ [MIGRATION.md](../../MIGRATION.md). Your `specs/.core/linear.config.json` path is
54
+ unchanged.
55
+
56
+ MIT © Reuben Greaves
@@ -0,0 +1,39 @@
1
+ ## Spec workflow
2
+
3
+ Spec-driven development runs through eight skills — use them so structure and
4
+ lifecycle stay consistent (see `.claude/rules/spec-planning.md`):
5
+
6
+ | Skill | Action | Status | Folder |
7
+ |-------|--------|--------|--------|
8
+ | `/spec` | (Feature) Grill to a clear shared understanding, then write a concise spec | `Draft` | `specs/backlog/` |
9
+ | `/spec-bug` | (Bug) Reproduce with a failing test, capture spec, drive red→green | `In Progress` | `specs/in-progress/` |
10
+ | `/spec-ready` | Confirm the spec is groomed (no open questions, phases + tests defined) | `Ready` | `specs/backlog/` |
11
+ | `/spec-review` | Re-validate a spec against the codebase; refresh stale parts | `—` | (unchanged) |
12
+ | `/spec-go` | Implement the next phase (with tests) | `In Progress` | `specs/in-progress/` |
13
+ | `/spec-complete` | Verify all phases done + tests green | `Complete` | `specs/complete/` |
14
+ | `/spec-cancel` | Record progress, stamp a reason on the header | `Cancelled` | `specs/cancelled/` |
15
+ | `/spec-init` | Bootstrap/repair this workflow in a project (idempotent) | — | — |
16
+
17
+ Every spec has a **type** (`> **Type:** Feature\|Bug`) and a filename prefix
18
+ (`feat-<name>` / `bug-<name>`) — never `[BUG]` brackets (glob hazard). Specs use
19
+ markdown checkboxes (`- [ ]`) for task tracking and are the single source of
20
+ truth for progress. Every spec is a folder: `00-overview.md` is the dashboard
21
+ (problem, decisions, solution, **phase index**, logs) and **each phase is its own
22
+ file** (`01-<slug>.md`, `02-…`) holding that phase's tasks — never a bare file,
23
+ never phases lumped into the overview. **Every phase ends with creating and
24
+ running tests**; decisions go in the spec's Changelog, state transitions in its
25
+ State log.
26
+
27
+ > Tailor the per-phase test commands and project conventions referenced by the
28
+ > spec skills to this project's stack (see `.claude/rules/spec-planning.md`).
29
+
30
+ **Per-spec isolation (adopt once, then default):** with isolation adopted
31
+ (`skitterspec init --isolation`, or `specs/.core/env.config.json` present),
32
+ **`/spec-go`** gives every in-progress spec its own git worktree automatically —
33
+ parallel specs, no stashing, `main` left free. Docker is a **per-spec
34
+ escalation**: `/spec` sets `> **Stack:** worktree` (default) or `worktree +
35
+ docker` when the spec touches the DB / stateful services, and only the latter
36
+ gets a namespaced stack (isolated volumes + reserved port block). **`/spec-env`**
37
+ · **`/spec-env-down`** are the manual engine (escalate Docker later, re-attach,
38
+ tear down — guarding dirty/unpushed work, backing up volumes first). Independent
39
+ of lifecycle status; inactive when `env.config.json` is absent.
@@ -0,0 +1,28 @@
1
+ {
2
+ "worktree": {
3
+ "root": "../{repo}-wt",
4
+ "folderPattern": "{slug}"
5
+ },
6
+ "docker": {
7
+ "enabled": true,
8
+ "composeFile": "docker-compose.yml",
9
+ "projectNamePattern": "{repoSlug}_{slug}",
10
+ "portBase": 3000,
11
+ "portsPerSpec": 10,
12
+ "envFile": ".env",
13
+ "backupCommand": ""
14
+ },
15
+ "open": {
16
+ "command": ""
17
+ },
18
+ "registry": ".spec-env/registry.json",
19
+ "branch": {
20
+ "pattern": "{type}/{slug}",
21
+ "identifierField": ""
22
+ },
23
+ "baseBranch": "",
24
+ "guards": {
25
+ "refuseTeardownIfDirty": true,
26
+ "refuseTeardownIfUnpushed": true
27
+ }
28
+ }
@@ -0,0 +1,99 @@
1
+ # `env.config.json` — per-spec isolation config
2
+
3
+ Opt-in config for per-spec isolation (git worktree + optional namespaced Docker
4
+ stack + an optional opener per in-progress spec), driven by `/spec-go` and the
5
+ `/spec-env` · `/spec-env-down` skills.
6
+
7
+ **Once this file is present, isolation is the default policy:** `/spec-go` gives
8
+ **every** in-progress spec its own git worktree automatically. Docker is a **per-
9
+ spec escalation** — a spec brings up a stack only when its `> **Stack:**` header
10
+ is `worktree + docker` (set at `/spec` when it touches the DB / stateful
11
+ services). A `worktree`-only spec takes no registry slot, no port block, and no
12
+ `.env`.
13
+
14
+ **Adopt it** with `skitterspec init --isolation` (or copy
15
+ `env.config.json.example` → `env.config.json` here) and edit the values. While
16
+ `env.config.json` is absent the feature is simply unused — every skill behaves
17
+ exactly as it does today.
18
+
19
+ The loader (`src/env/config.js` → `loadEnvConfig`) merges your file over the
20
+ frozen defaults below and returns `{ config, present }`; `present:false` means
21
+ no live `env.config.json` was found.
22
+
23
+ ## Fields
24
+
25
+ ```jsonc
26
+ {
27
+ // Where sibling worktrees are created and how their dirs are named.
28
+ "worktree": {
29
+ "root": "../{repo}-wt", // dir that holds all spec worktrees; sibling of
30
+ // the primary checkout, never nested inside it.
31
+ "folderPattern": "{slug}" // per-spec worktree dir name.
32
+ },
33
+
34
+ // Per-spec Docker stack. COMPOSE_PROJECT_NAME namespaces containers,
35
+ // networks, and named volumes; PORT_OFFSET shifts the spec's port block.
36
+ "docker": {
37
+ // Master switch: "is Docker escalation available on this project?" — NOT
38
+ // "always run Docker". true = specs MAY escalate (a spec still needs
39
+ // `Stack: worktree + docker` to actually get a stack); the default stack is
40
+ // worktree-only. false = every spec is worktree-only and the escalation is
41
+ // hidden. (Was "always provision Docker" in the pre-Stack engine.)
42
+ "enabled": true,
43
+ "composeFile": "docker-compose.yml",
44
+ "projectNamePattern": "{repoSlug}_{slug}", // → COMPOSE_PROJECT_NAME
45
+ "portBase": 3000, // first port of slot 0's block
46
+ "portsPerSpec": 10, // block width; slot n → portBase + n*portsPerSpec
47
+ "envFile": ".env", // written into the worktree
48
+ "backupCommand": "" // optional pre-teardown backup (e.g. pg_dump);
49
+ // empty = no backup, volumes dropped directly.
50
+ },
51
+
52
+ // Optional, editor/terminal-agnostic opener run after `spec-env up`. The
53
+ // template is expanded with {worktreePath}, {slug}, {branch}, {projectName},
54
+ // {portOffset}. Empty = nothing is opened (the path is just printed).
55
+ // Examples: "code {worktreePath}", "tmux new-window -c {worktreePath}",
56
+ // or a "warp://..." deeplink for Warp users.
57
+ "open": {
58
+ "command": ""
59
+ },
60
+
61
+ // Machine-local slot registry (spec → slot index). Resolved against the
62
+ // primary checkout root, shared by all worktrees, gitignored.
63
+ "registry": ".spec-env/registry.json",
64
+
65
+ // Git branch naming, provider-neutral. `pattern` expands {type} and {slug}
66
+ // (e.g. "feat/add-widget"). When a ticketing provider is linked and you want
67
+ // tracker ids in branch names, use {identifier} in the pattern and point
68
+ // `identifierField` at the 00-overview.md frontmatter field the provider
69
+ // writes the id into — pushing that branch can then fire the tracker's
70
+ // automation. Empty `identifierField` (or a spec missing that field) makes a
71
+ // pattern with {identifier} fall back to {type}/{slug}.
72
+ "branch": {
73
+ "pattern": "{type}/{slug}",
74
+ "identifierField": ""
75
+ },
76
+
77
+ // Integration base branch — the branch specs fork from and land back onto
78
+ // (used by the teardown "merged?" guard and, later, the integrate step).
79
+ // Empty = auto-detect: origin/HEAD → main → master. Set it when your default
80
+ // branch isn't discoverable (e.g. no remote) or differs (trunk, develop).
81
+ "baseBranch": "",
82
+
83
+ // Teardown safety. --force overrides both. refuseTeardownIfUnpushed only
84
+ // blocks when the commits are ALSO unmerged into the base branch — a branch
85
+ // already landed on base tears down (and its branch is deleted) without
86
+ // --force, even with no remote.
87
+ "guards": {
88
+ "refuseTeardownIfDirty": true,
89
+ "refuseTeardownIfUnpushed": true
90
+ }
91
+ }
92
+ ```
93
+
94
+ ## Token expansion
95
+
96
+ - `{repo}` — primary checkout dir basename (e.g. `skitterspec`).
97
+ - `{repoSlug}` — `{repo}` lower-cased, non-alphanumerics collapsed to `-`
98
+ (safe for a `COMPOSE_PROJECT_NAME`).
99
+ - `{slug}` — the spec slug (folder name minus its `feat-`/`bug-` prefix).
@@ -0,0 +1,39 @@
1
+ {
2
+ "linear": {
3
+ "teamKey": "",
4
+ "teamId": "",
5
+ "initiativeId": ""
6
+ },
7
+ "mapping": {
8
+ "specFolder": "project",
9
+ "phases": "milestone",
10
+ "tasks": "issue"
11
+ },
12
+ "states": {
13
+ "backlog": "Backlog",
14
+ "in-progress": "In Progress",
15
+ "complete": "Done",
16
+ "cancelled": "Cancelled"
17
+ },
18
+ "snapshot": {
19
+ "overviewFile": "00-overview.md"
20
+ },
21
+ "branch": {
22
+ "pattern": "{type}/{slug}"
23
+ },
24
+ "sync": {
25
+ "baseDir": "specs/.core/linear-base",
26
+ "backupDir": "specs/.core/linear-backups",
27
+ "fieldOwnership": {
28
+ "description": "both",
29
+ "milestones": "both",
30
+ "phaseBodies": "both",
31
+ "acceptanceCriteria": "both",
32
+ "taskBreakdown": "both",
33
+ "workflowState": "pull",
34
+ "priority": "pull",
35
+ "labels": "pull"
36
+ },
37
+ "localOnlySections": ["State log", "Changelog", "Open questions"]
38
+ }
39
+ }
@@ -0,0 +1,121 @@
1
+ # `linear.config.json` — Linear hybrid-sync config
2
+
3
+ Opt-in config for the git-like Linear sync (`/spec-status`, `/spec-pull`,
4
+ `/spec-push`, and the Linear-aware paths of `/spec` and `/spec-go`). Linear owns
5
+ **status and discussion**; the repo stays the **co-authoring surface for spec
6
+ content**. Sync is bidirectional but git-like: explicit commands, a committed
7
+ **base sidecar** for three-way merge, and no blind overwrites.
8
+
9
+ **Every Linear step is gated on this file.** While `specs/.core/linear.config.json`
10
+ is absent the feature is simply unused — `/spec`, `/spec-go`, and the CLI's
11
+ `spec-sync` subcommands behave exactly as they do today (local-only). Adopt it by
12
+ copying `linear.config.json.example` → `linear.config.json` here and filling in
13
+ your team / initiative IDs.
14
+
15
+ The loader (`src/sync/config.js` → `loadLinearConfig`) merges your file over the
16
+ frozen defaults below and returns `{ config, present }`; `present:false` means no
17
+ live `linear.config.json` was found (the opt-in gate — it never throws on
18
+ absence). A `sync.fieldOwnership` value outside `both|pull|push` is a hard error.
19
+
20
+ ## Fields
21
+
22
+ ```jsonc
23
+ {
24
+ // Which Linear team/initiative specs sync into. IDs are read by the Phase 2
25
+ // MCP adapter; leave blank until you connect the `linear` MCP server.
26
+ "linear": {
27
+ "teamKey": "", // human-facing key, e.g. "ENG" (optional)
28
+ "teamId": "", // Linear team UUID (create target)
29
+ "initiativeId": "" // optional Initiative that groups these specs
30
+ },
31
+
32
+ // How a spec's parts map onto Linear objects. Defaults mirror Decision 7:
33
+ // spec folder → Project, phases → Milestones, tasks → Issues. `phases` may be
34
+ // switched to "issue" if your workspace doesn't expose project milestones.
35
+ "mapping": {
36
+ "specFolder": "project",
37
+ "phases": "milestone", // "milestone" | "issue"
38
+ "tasks": "issue"
39
+ },
40
+
41
+ // Map the spec's lifecycle bucket → the Linear workflow-state name. Used when
42
+ // translating workflowState across the boundary (Linear owns status → `pull`).
43
+ "states": {
44
+ "backlog": "Backlog",
45
+ "in-progress": "In Progress",
46
+ "complete": "Done",
47
+ "cancelled": "Cancelled"
48
+ },
49
+
50
+ // The spec's entry-point file the local snapshot + frontmatter live in.
51
+ "snapshot": {
52
+ "overviewFile": "00-overview.md"
53
+ },
54
+
55
+ // Git branch name derived for a linked spec. Tokens: {type}, {slug},
56
+ // {identifier} (the Linear issue/project identifier, e.g. ENG-123). Shared
57
+ // with the isolation engine's branch derivation (src/env/resolve.js).
58
+ "branch": {
59
+ "pattern": "{type}/{slug}"
60
+ },
61
+
62
+ // The three-way merge engine's on-disk state.
63
+ "sync": {
64
+ // Committed base sidecar dir: the last-synced snapshot per spec, as
65
+ // {baseDir}/{identifier}.base.json. Committed so each worktree carries its
66
+ // own base and the divergence check stays accurate.
67
+ "baseDir": "specs/.core/linear-base",
68
+
69
+ // Backup-before-force lands the about-to-be-clobbered side here (the
70
+ // reflog). --force never destroys without first writing a copy.
71
+ "backupDir": "specs/.core/linear-backups",
72
+
73
+ // Per-field sync direction — collapses which fields can ever conflict:
74
+ // "both" — co-authored: push + pull, may conflict (both moved off base).
75
+ // "pull" — Linear→local only (e.g. status/priority); a local edit never
76
+ // pushes and a conflict resolves to remote-wins.
77
+ // "push" — local→Linear only; a remote edit never pulls and a conflict
78
+ // resolves to local-wins.
79
+ // Any field key you add here joins the compared field set; a value outside
80
+ // both|pull|push is rejected at load time.
81
+ "fieldOwnership": {
82
+ "description": "both",
83
+ "milestones": "both",
84
+ "phaseBodies": "both",
85
+ "acceptanceCriteria": "both",
86
+ "taskBreakdown": "both",
87
+ "workflowState": "pull",
88
+ "priority": "pull",
89
+ "labels": "pull"
90
+ },
91
+
92
+ // Markdown sections of 00-overview.md that are local-only scaffolding and
93
+ // are stripped from the pushed `description` (never sent to Linear).
94
+ "localOnlySections": ["State log", "Changelog", "Open questions"]
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Field ownership & conflicts
100
+
101
+ The spec is a set of structured fields, most written by only one side. Marking a
102
+ field's owner collapses which fields can genuinely conflict:
103
+
104
+ - A `pull` field (Linear owns it) never reports as **pushable** — a stray local
105
+ edit is informational and gets reverted on the next pull.
106
+ - A `push` field (the repo owns it) never reports as **pullable**.
107
+ - Only a `both` field where **both** sides moved off the committed base is a real
108
+ `conflict` — `/spec-push` / `/spec-pull` refuse it unless `--force` (which
109
+ backs up the losing side into `sync.backupDir` first).
110
+
111
+ After any successful pull/push/force the engine **rewrites the base** so the next
112
+ three-way compare starts clean.
113
+
114
+ ## What to commit
115
+
116
+ - **`sync.baseDir`** (default `specs/.core/linear-base/`) — **commit it.** The base
117
+ sidecar is the last-synced snapshot the three-way merge compares against; each
118
+ worktree carries its own base, so it must travel with the branch.
119
+ - **`sync.backupDir`** (default `specs/.core/linear-backups/`) — **gitignore it.**
120
+ These are `--force` recovery copies (a local reflog), per-machine and not shared.
121
+ Add `specs/.core/linear-backups/` to your `.gitignore`.
@@ -0,0 +1,152 @@
1
+ # Spec Planning
2
+
3
+ Spec-driven development is driven by eight skills — use them rather than
4
+ hand-rolling specs so the structure and lifecycle stay consistent. Each sets a
5
+ status on the spec header (`> **Status:** …`):
6
+
7
+ | Skill | Purpose | Status | Folder |
8
+ |-------|---------|--------|--------|
9
+ | `/spec` | (Feature) Grill to a clear shared understanding, then write a new spec | `Draft` | `specs/backlog/` |
10
+ | `/spec-bug` | (Bug) Reproduce with a failing test, capture spec, drive red→green | `In Progress` | `specs/in-progress/` |
11
+ | `/spec-ready` | Confirm it's groomed (no open questions, phases + tests defined) | `Ready` | `specs/backlog/` |
12
+ | `/spec-review` | Re-validate a spec against the codebase; refresh stale parts | `—` | (unchanged) |
13
+ | `/spec-go` | Implement the next phase (with tests) | `In Progress` | `specs/in-progress/` |
14
+ | `/spec-complete` | Verify all phases done + tests green | `Complete` | `specs/complete/` |
15
+ | `/spec-cancel` | Record progress, stamp a reason on the header | `Cancelled` | `specs/cancelled/` |
16
+ | `/spec-init` | Bootstrap/repair this workflow in a project (idempotent) | — | — |
17
+
18
+ Status flow: `Draft → Ready → In Progress → Complete` (or `Cancelled` from any
19
+ state). `/spec-ready` is a grooming gate only — it does not move the folder.
20
+ `/spec-bug` is test-first and starts straight in `In Progress` (work begins
21
+ immediately), so it skips Draft/Ready.
22
+
23
+ **Per-spec isolation (opt-in to adopt, then the default policy).** When a project
24
+ adopts isolation (`skitterspec init --isolation`, or `specs/.core/env.config.json`
25
+ present), `/spec-go` gives **every** in-progress spec its own git worktree
26
+ automatically — several specs run side by side without stashing or clashing, and
27
+ `main` stays free. Docker is a **per-spec escalation**: `/spec` records
28
+ `> **Stack:** worktree` (default) or `worktree + docker` when the spec touches the
29
+ DB / stateful services, and `/spec-go` brings up a namespaced stack only for the
30
+ latter. All housekeeping (the backlog→in-progress move, header edits, the code)
31
+ happens on the spec's branch in the worktree; `main` changes only when it merges.
32
+ `/spec-env` · `/spec-env-down` remain the manual engine (escalate Docker later,
33
+ re-attach, tear down). Isolation is **orthogonal to lifecycle status** and
34
+ inactive when `env.config.json` is absent — every skill then behaves as it does
35
+ today.
36
+
37
+ **Ticketing-provider sync (opt-in, a separate package).** The base is
38
+ tracker-free: it knows nothing about any specific ticketing system. A
39
+ ticketing provider is installed as its own distribution that plugs into two named
40
+ **seams** in the shared skills (`/spec` Phase E, `/spec-go` step 3b) and fulfils a
41
+ skill-name + CLI contract — it ships `/spec-status` (read-only per-field
42
+ divergence), `/spec-pull` (tracker→repo), and `/spec-push` (repo→tracker), backed
43
+ by a `spec-sync` CLI, three-way merged against a committed base sidecar. When a
44
+ provider is present, `/spec` also links the spec to the tracker and `/spec-go`
45
+ pulls first. With no provider installed the seams are empty and every skill
46
+ behaves as a plain filesystem workflow. See the provider package's own docs for
47
+ its config and field reference.
48
+
49
+ ## Project conventions (fill this in)
50
+
51
+ The spec skills tell you to run "your project's typecheck and test commands" and
52
+ to "honour project conventions". Make those concrete here so specs stay
53
+ consistent with the codebase:
54
+
55
+ - **Typecheck command:** `<e.g. npm run typecheck>`
56
+ - **Test command:** `<e.g. npm test>` (single file/dir: `<e.g. npx vitest run path>`)
57
+ - **Lint/format:** `<e.g. npm run lint>`
58
+ - **Other rules specs must honour:** link the relevant `.claude/rules/*.md`
59
+ (architecture, code style, testing, database, etc.) rather than restating them.
60
+
61
+ ## Spec types — Feature vs Bug
62
+
63
+ Every spec is one of two types, recorded **both** in the header and the filename:
64
+
65
+ - **Header field:** `> **Type:** Feature` or `> **Type:** Bug` (authoritative,
66
+ greppable: `grep -rl 'Type:.*Bug' specs/`).
67
+ - **Filename prefix:** `feat-<name>` for features, `bug-<name>` for bugs
68
+ (visible in listings; glob-safe — never use `[BUG]`/`[FEATURE]` brackets).
69
+
70
+ Both types share the same lifecycle folders below — type is orthogonal to status.
71
+
72
+ ## Header fields & State log (audit trail)
73
+
74
+ Every spec header carries:
75
+
76
+ - `> **Author:**` — who created the spec (set at `/spec` / `/spec-bug`, defaults
77
+ to `git config user.name`).
78
+ - `> **Developer:**` — who implements it (`—` until `/spec-go` starts work, then
79
+ set to `git config user.name`; `/spec-bug` sets it immediately).
80
+
81
+ Every spec also has a **State log** table — the audit trail of folder/status
82
+ transitions. Each lifecycle skill appends exactly one row when it changes state:
83
+
84
+ ```
85
+ ## State log
86
+
87
+ | Date | Status | Folder | By |
88
+ |------|--------|--------|----|
89
+ | 2026-01-01 | Draft | backlog | Jane Dev |
90
+ | 2026-01-02 | In Progress | in-progress | Jane Dev |
91
+ ```
92
+
93
+ Keep the **State log** (state transitions) separate from the **Changelog**
94
+ (decisions and course-corrections) — state moves go in the table, not the
95
+ changelog.
96
+
97
+ When asked for a plan, implementation strategy, or feature breakdown:
98
+
99
+ 1. Create or update a spec under `specs/` — never plan only in chat.
100
+ 2. Reach a clear shared understanding of the requirement AND the proposed
101
+ solution before writing (the `/spec` skill grills for this).
102
+ 3. Use markdown checkboxes `- [ ]` for tasks, `- [x]` when done.
103
+ 4. Organise work into phased sections with short goal descriptions.
104
+ 5. Tasks must be granular enough to complete in one coding session.
105
+ 6. Every phase ends with creating and running tests — a phase is not done until
106
+ its tests are green (run the project's typecheck + test commands above).
107
+ 7. Keep specs **as concise as possible**.
108
+ 8. Record decisions and course-corrections in the spec's **Changelog** section.
109
+
110
+ ## Lifecycle folders
111
+
112
+ ```
113
+ specs/backlog/ Draft + Ready specs (/spec, /spec-ready)
114
+ specs/in-progress/ under active implementation (/spec-go, /spec-bug)
115
+ specs/complete/ finished (/spec-complete)
116
+ specs/cancelled/ abandoned, with a reason on the header (/spec-cancel)
117
+ specs/.core/ project rules — ALWAYS APPLY, never moved
118
+ ```
119
+
120
+ Every spec is a **folder** `specs/<bucket>/<name>/` — never a bare file, even for
121
+ simple changes. Inside it:
122
+
123
+ - `00-overview.md` is the entry point / dashboard: header, Problem, Decisions,
124
+ Solution overview, the **phase index** (a table linking to each phase file with
125
+ its status), Open questions, State log, Changelog. **No per-phase task lists
126
+ live here.**
127
+ - **One file per phase** — `01-<phase-slug>.md`, `02-<phase-slug>.md`, … in
128
+ execution order. Each holds that phase's goal, its task checkboxes (tests
129
+ included), and any phase-specific notes. Even a single-phase spec gets `01-….md`
130
+ — so each phase is easy to open and work on its own.
131
+
132
+ Keep the index and the phase files in sync (`⬜`/`🔄`/`✅`). Legacy specs may be a
133
+ bare `<name>.md`, or a `00-overview.md` with inline phases — the skills read
134
+ those, but new specs always use the folder + phase-file form.
135
+
136
+ ## Finding specs
137
+
138
+ The **folder buckets are the source of truth** — a spec's bucket is its status.
139
+ To see the backlog, list `specs/backlog/`; for the latest completed specs, use
140
+ `git log`/mtime on `specs/complete/` or each spec's dated **State log**. Live
141
+ status also lives in the tracker when a ticketing provider is linked. (There are
142
+ no `00-index.md` summary files — the folder tree, headers, and State logs are
143
+ queried directly.)
144
+
145
+ ## Rules
146
+
147
+ - If a spec already exists, update it — don't rewrite from scratch.
148
+ - Preserve completed `[x]` tasks.
149
+ - Add new tasks to the appropriate phase.
150
+ - Never delete historical notes.
151
+ - The spec file is the single source of truth for implementation progress.
152
+ - Move specs between buckets with `git mv` to preserve history.