@skitterbyte/skitterspec-linear 7.0.2 → 8.0.1
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 +23 -25
- package/assets/core/SETUP.md +46 -51
- package/assets/core/linear.config.json.example +6 -7
- package/assets/core/linear.config.md +47 -77
- package/assets/rules/spec-planning.md +9 -7
- package/assets/skills/spec/SKILL.md +2 -2
- package/assets/skills/spec-go/SKILL.md +16 -18
- package/assets/skills/spec-push/SKILL.md +53 -47
- package/assets/skills/spec-status/SKILL.md +31 -26
- package/package.json +2 -2
- package/src/init.js +25 -0
- package/src/vendor/linear/cli-sanitise.js +0 -0
- package/src/vendor/linear/cli-sync.js +123 -204
- package/src/vendor/linear/config.js +18 -14
- package/src/vendor/sync-core/index.js +23 -19
- package/src/vendor/sync-core/src/base.js +8 -10
- package/src/vendor/sync-core/src/compare.js +83 -174
- package/src/vendor/sync-core/src/normalize.js +84 -80
- package/src/vendor/sync-core/src/push.js +39 -133
- package/src/vendor/sync-core/src/sanitise.js +4 -8
- package/src/vendor/sync-core/src/task-block.js +13 -1
- package/src/vendor/sync-core/src/write.js +15 -205
- package/assets/skills/spec-pull/SKILL.md +0 -49
- package/src/vendor/sync-core/src/apply.js +0 -66
- package/src/vendor/sync-core/src/pull.js +0 -115
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @skitterbyte/skitterspec-linear
|
|
2
2
|
|
|
3
3
|
Spec-driven development for [Claude Code](https://claude.com/claude-code), **with
|
|
4
|
-
Linear
|
|
4
|
+
one-way Linear sync**. A strict **superset** of
|
|
5
5
|
[`@skitterbyte/skitterspec`](https://www.npmjs.com/package/@skitterbyte/skitterspec):
|
|
6
|
-
everything in the base filesystem workflow, plus
|
|
7
|
-
its linked Linear project.
|
|
6
|
+
everything in the base filesystem workflow, plus one-way sync from a spec up to
|
|
7
|
+
its linked Linear project — the repo is canonical, Linear is a generated mirror.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
npx @skitterbyte/skitterspec-linear init
|
|
@@ -46,27 +46,26 @@ fuller guide):
|
|
|
46
46
|
That file is the opt-in gate — until it exists, everything below is inert and
|
|
47
47
|
the package behaves exactly like the base.
|
|
48
48
|
|
|
49
|
-
4. **Link and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
4. **Link and push** — `/spec` creates a linked Linear Project (a Milestone per
|
|
50
|
+
phase) and stamps the id; then `/spec-push` publishes the spec up and
|
|
51
|
+
`/spec-status` reports what would push. Sync is **one-way**: the repo is the
|
|
52
|
+
source of truth and Linear is a generated mirror.
|
|
53
53
|
|
|
54
54
|
## What the superset adds
|
|
55
55
|
|
|
56
56
|
On top of the base skills (`/spec`, `/spec-go`, isolation, …):
|
|
57
57
|
|
|
58
|
-
- **`/spec-status`** — read-only
|
|
59
|
-
|
|
60
|
-
- **`/spec-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
concurrency-checked; refuses if Linear moved since base unless `--force`.
|
|
58
|
+
- **`/spec-status`** — read-only drift report: what the next push would create /
|
|
59
|
+
update, plus any workflow-state drift. Changes nothing.
|
|
60
|
+
- **`/spec-push`** — repo → Linear, one-way. Diffs the spec against a committed
|
|
61
|
+
last-pushed snapshot and applies only what changed (project description +
|
|
62
|
+
status, milestones, issues), stamping the returned ids back into the spec.
|
|
64
63
|
- **`spec-sync` CLI** (`skitterspec-linear spec-sync …`) — the deterministic
|
|
65
64
|
engine behind the skills, for CI / local runs.
|
|
66
65
|
|
|
67
66
|
The shared `/spec` and `/spec-go` skills come composed with the Linear steps
|
|
68
|
-
filled in: `/spec` links a new spec to a Linear Project (a Milestone per phase)
|
|
69
|
-
|
|
67
|
+
filled in: `/spec` links a new spec to a Linear Project (a Milestone per phase).
|
|
68
|
+
There is no pull — the repo is already canonical, so `/spec-go` just builds.
|
|
70
69
|
|
|
71
70
|
## Opt-in
|
|
72
71
|
|
|
@@ -78,16 +77,15 @@ behaves exactly like the base.
|
|
|
78
77
|
setup guide — connecting the `linear` MCP server, finding your team id, linking a
|
|
79
78
|
spec, and a smoke test. Per-field docs live in `specs/.core/linear.config.md`.
|
|
80
79
|
|
|
81
|
-
**What
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
(`specs/.core/linear-backups/`) are gitignored.
|
|
80
|
+
**What pushes:** the spec body travels as the Linear Project **`description`**,
|
|
81
|
+
phases as **Milestones**, tasks as **Issues** (a short first-sentence title, the
|
|
82
|
+
full task text as the description), and the lifecycle bucket sets the project's
|
|
83
|
+
**workflow state**. Priority, labels, cycles and comments are **Linear-native
|
|
84
|
+
triage** — the PM's to set in Linear; one-way sync neither pushes nor reads them,
|
|
85
|
+
so they're never clobbered. A workflow-state a teammate moves in Linear is
|
|
86
|
+
surfaced by `/spec-status` as drift and overwritten on the next push. **Last-pushed
|
|
87
|
+
snapshots** (`specs/.core/linear-base/`, content hashes) are committed so push
|
|
88
|
+
sends only what changed.
|
|
91
89
|
|
|
92
90
|
Branch naming that embeds the Linear id lives in the isolation config
|
|
93
91
|
(`env.config.json` → `branch.pattern` with `{identifier}`, `branch.identifierField:
|
package/assets/core/SETUP.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
# Setting up Linear
|
|
1
|
+
# Setting up Linear sync (one-way)
|
|
2
2
|
|
|
3
|
-
A start-to-finish guide to getting `/spec-status
|
|
4
|
-
|
|
5
|
-
the
|
|
6
|
-
|
|
3
|
+
A start-to-finish guide to getting `/spec-status` and `/spec-push` working against
|
|
4
|
+
a real Linear workspace. Sync is **one-way**: the repo is the source of truth and
|
|
5
|
+
the linked Linear project is a **generated mirror** — content is pushed up, never
|
|
6
|
+
read back or merged. Covers the **Linear side** (connecting the MCP server,
|
|
7
|
+
finding your team) that the config reference (`linear.config.md`) assumes you
|
|
8
|
+
already have.
|
|
7
9
|
|
|
8
10
|
> The whole feature is **opt-in**: until `specs/.core/linear.config.json` exists,
|
|
9
11
|
> everything below is inert and the package behaves exactly like the base
|
|
@@ -47,7 +49,7 @@ claude mcp list
|
|
|
47
49
|
```
|
|
48
50
|
|
|
49
51
|
> Read-only trial: use `https://mcp.linear.app/mcp/readonly` to exercise
|
|
50
|
-
> `/spec-status`
|
|
52
|
+
> `/spec-status` (drift report) without granting write access. `/spec-push`
|
|
51
53
|
> needs the full (writable) endpoint.
|
|
52
54
|
|
|
53
55
|
## 3. Find your team id
|
|
@@ -108,33 +110,30 @@ Two ways to get there:
|
|
|
108
110
|
|
|
109
111
|
| Command | Direction | What it does |
|
|
110
112
|
|---------|-----------|--------------|
|
|
111
|
-
| `/spec-status` | — | Read-only
|
|
112
|
-
| `/spec-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
(`linear_milestone_id`) and inline on task lines (`- [ ] do it (SKI-123)`).
|
|
136
|
-
Deletions are report-only (surfaced by `/spec-status`, never auto-applied). Full
|
|
137
|
-
details in the "Body round-trip" section of `linear.config.md`.
|
|
113
|
+
| `/spec-status` | — | Read-only drift report: what would push (create/update), and whether Linear's workflow-state drifted from the spec. Writes nothing. |
|
|
114
|
+
| `/spec-push` | repo → Linear | Computes a create/update plan vs the last-pushed snapshot and applies it (project description/status, milestones, issues), stamping new ids back into the spec. |
|
|
115
|
+
|
|
116
|
+
Typical loop: edit the spec in-repo → `/spec-status` (what's pending) →
|
|
117
|
+
`/spec-push` (send it up). There is no pull — Linear is a generated mirror.
|
|
118
|
+
|
|
119
|
+
### What gets pushed
|
|
120
|
+
|
|
121
|
+
| Field | What |
|
|
122
|
+
|-------|------|
|
|
123
|
+
| `description` | the spec body (problem, solution, acceptance criteria) as the project description |
|
|
124
|
+
| `milestones` | one per phase (name + goal) |
|
|
125
|
+
| `issues` | one per task — first-sentence **title**, full task text as the **description** |
|
|
126
|
+
| `workflowState` → project status | the spec's lifecycle bucket, mapped via `states` |
|
|
127
|
+
|
|
128
|
+
Priority, labels, cycles and comments are **Linear-native triage** — the PM's to
|
|
129
|
+
set in Linear. One-way sync neither pushes nor reads them, so they're never
|
|
130
|
+
clobbered. A workflow-state a teammate moves in Linear is surfaced by
|
|
131
|
+
`/spec-status` as drift and overwritten on the next push.
|
|
132
|
+
|
|
133
|
+
Phases push as **Milestones** and tasks as **Issues** by default. The link ids
|
|
134
|
+
live in the phase-file frontmatter (`linear_milestone_id`) and inline on task
|
|
135
|
+
lines (`- [ ] do it (SKI-123)`); `/spec-push` stamps them the first time it
|
|
136
|
+
creates each object, so later pushes update instead of recreate.
|
|
138
137
|
|
|
139
138
|
Sections listed in `sync.localOnlySections` (default: **State log**, **Changelog**,
|
|
140
139
|
**Open questions**) are stripped from the pushed description — they never leave
|
|
@@ -142,24 +141,20 @@ the repo.
|
|
|
142
141
|
|
|
143
142
|
## 7. What to commit
|
|
144
143
|
|
|
145
|
-
- **Commit** `specs/.core/linear-base/` — the
|
|
146
|
-
|
|
147
|
-
travel with the branch.
|
|
148
|
-
- **Gitignore** `specs/.core/linear-backups/` — `--force` recovery copies, local
|
|
149
|
-
and per-machine. Add `specs/.core/linear-backups/` to `.gitignore`.
|
|
144
|
+
- **Commit** `specs/.core/linear-base/` — the last-pushed snapshots (content
|
|
145
|
+
hashes per spec, so `/spec-push` knows what changed without reading Linear
|
|
146
|
+
back). Each worktree carries its own, so it must travel with the branch.
|
|
150
147
|
|
|
151
148
|
## 8. Smoke test (verify your setup)
|
|
152
149
|
|
|
153
|
-
With a linked spec, confirm
|
|
150
|
+
With a linked spec, confirm push end-to-end:
|
|
154
151
|
|
|
155
|
-
1. `/spec-status` →
|
|
156
|
-
2. `/spec-
|
|
157
|
-
|
|
158
|
-
3. `/spec-status` again → **
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
4. Edit the spec body locally, `/spec-push` → the change lands on the Linear
|
|
162
|
-
project's description; `/spec-status` returns to **in sync**.
|
|
152
|
+
1. `/spec-status` → shows what would push (`pending — N to create, M to update`).
|
|
153
|
+
2. `/spec-push` → creates the project's milestones/issues and sets the
|
|
154
|
+
description/status; ids are stamped back into the spec.
|
|
155
|
+
3. `/spec-status` again → **up to date** (nothing changed since the last push).
|
|
156
|
+
4. Edit a task locally, `/spec-push` → the matching issue updates;
|
|
157
|
+
`/spec-status` returns to **up to date**.
|
|
163
158
|
|
|
164
159
|
## Troubleshooting
|
|
165
160
|
|
|
@@ -167,9 +162,9 @@ With a linked spec, confirm the round-trip end-to-end:
|
|
|
167
162
|
this session. Re-run step 2; remember a fresh add needs a Claude Code restart.
|
|
168
163
|
- **"missing required tools: projectUpdate"** — you're on the read-only endpoint
|
|
169
164
|
(or a restricted API key). Use `https://mcp.linear.app/mcp` for push.
|
|
170
|
-
- **A
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
- **A configured status name silently does nothing** — Linear ignores an unknown
|
|
166
|
+
project status. Run `/spec-status` (it validates the `states` names against the
|
|
167
|
+
workspace) and fix `linear.config.json` to the real project-status names
|
|
168
|
+
(`Backlog / Planned / In Progress / Completed / Canceled`).
|
|
174
169
|
- **Reconnecting doesn't switch workspace** — Linear ties the OAuth session to one
|
|
175
170
|
workspace. Remove and re-add the server to authenticate against another.
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"states": {
|
|
13
13
|
"backlog": "Backlog",
|
|
14
14
|
"in-progress": "In Progress",
|
|
15
|
-
"complete": "
|
|
16
|
-
"cancelled": "
|
|
15
|
+
"complete": "Completed",
|
|
16
|
+
"cancelled": "Canceled"
|
|
17
17
|
},
|
|
18
18
|
"snapshot": {
|
|
19
19
|
"overviewFile": "00-overview.md"
|
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"sync": {
|
|
25
25
|
"baseDir": "specs/.core/linear-base",
|
|
26
|
-
"backupDir": "specs/.core/linear-backups",
|
|
27
26
|
"fieldOwnership": {
|
|
28
|
-
"description": "
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
27
|
+
"description": "push",
|
|
28
|
+
"milestones": "push",
|
|
29
|
+
"tasks": "push",
|
|
30
|
+
"workflowState": "push"
|
|
32
31
|
},
|
|
33
32
|
"localOnlySections": ["State log", "Changelog", "Open questions"],
|
|
34
33
|
"keyedFields": {}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# `linear.config.json` — Linear
|
|
1
|
+
# `linear.config.json` — Linear one-way sync config
|
|
2
2
|
|
|
3
|
-
Opt-in config for the
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**
|
|
3
|
+
Opt-in config for the Linear sync (`/spec-status`, `/spec-push`, and the
|
|
4
|
+
Linear-aware paths of `/spec` and `/spec-go`). Sync is **one-way**: the repo is
|
|
5
|
+
the source of truth and the Linear project is a **generated mirror**. Content is
|
|
6
|
+
pushed up and never read back or merged — `/spec-push` diffs the spec against a
|
|
7
|
+
committed **last-pushed snapshot** and applies only what changed; `/spec-status`
|
|
8
|
+
is a read-only drift report. The `sync.fieldOwnership` map now just selects the
|
|
9
|
+
projection field set (every field is repo-owned and pushed).
|
|
8
10
|
|
|
9
11
|
**Every Linear step is gated on this file.** While `specs/.core/linear.config.json`
|
|
10
12
|
is absent the feature is simply unused — `/spec`, `/spec-go`, and the CLI's
|
|
@@ -59,101 +61,69 @@ absence). A `sync.fieldOwnership` value outside `both|pull|push` is a hard error
|
|
|
59
61
|
"pattern": "{type}/{slug}"
|
|
60
62
|
},
|
|
61
63
|
|
|
62
|
-
//
|
|
64
|
+
// One-way push engine state.
|
|
63
65
|
"sync": {
|
|
64
|
-
// Committed
|
|
65
|
-
// {baseDir}/{identifier}.base.json. Committed so each worktree
|
|
66
|
-
// own
|
|
66
|
+
// Committed last-pushed snapshot dir: content hashes of the last push per
|
|
67
|
+
// spec, as {baseDir}/{identifier}.base.json. Committed so each worktree
|
|
68
|
+
// carries its own, and push knows what changed without reading Linear back.
|
|
67
69
|
"baseDir": "specs/.core/linear-base",
|
|
68
70
|
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
//
|
|
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. The default set is scoped to the
|
|
81
|
-
// fields that round-trip through the live skill today: the whole spec body
|
|
82
|
-
// travels as `description`; status/priority/labels are Linear-owned (pull).
|
|
83
|
-
// A phase/milestone or per-issue round-trip is a future extension — opt in by
|
|
84
|
-
// adding `milestones`/`phaseBodies`/`taskBreakdown` here once that write path
|
|
85
|
-
// exists (see the deferred body write-back note in the package README).
|
|
71
|
+
// The pushed projection field set (repo → Linear, one-way). The `push` marker
|
|
72
|
+
// is retained for shape; there is no pull. The default set is the project
|
|
73
|
+
// `description`, `milestones` (one per phase), `tasks` (one issue each), and
|
|
74
|
+
// the lifecycle `workflowState`. Priority, labels, cycles and comments are
|
|
75
|
+
// Linear-native triage — deliberately NOT here, so a PM's triage is never
|
|
76
|
+
// touched. Any key you add joins the pushed projection.
|
|
86
77
|
"fieldOwnership": {
|
|
87
|
-
"description": "
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
78
|
+
"description": "push",
|
|
79
|
+
"milestones": "push",
|
|
80
|
+
"tasks": "push",
|
|
81
|
+
"workflowState": "push"
|
|
91
82
|
},
|
|
92
83
|
|
|
93
84
|
// Markdown sections of 00-overview.md that are local-only scaffolding and
|
|
94
85
|
// are stripped from the pushed `description` (never sent to Linear).
|
|
95
86
|
"localOnlySections": ["State log", "Changelog", "Open questions"],
|
|
96
87
|
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
88
|
+
// Reserved. Milestones and tasks are always projected per item (each phase →
|
|
89
|
+
// a Milestone, each task → an Issue), so this no longer needs setting; it is
|
|
90
|
+
// validated but unused. Leave it `{}`.
|
|
100
91
|
"keyedFields": {}
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
94
|
```
|
|
104
95
|
|
|
105
|
-
##
|
|
96
|
+
## Phases → Milestones, tasks → Issues
|
|
106
97
|
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
Push maps the spec's structure to Linear's, keyed by id so it updates rather than
|
|
99
|
+
recreates:
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"fieldOwnership": { "milestones": "both", "tasks": "both" },
|
|
113
|
-
"keyedFields": { "milestones": "id", "tasks": "id" }
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
With this on:
|
|
118
|
-
|
|
119
|
-
- **Phases ↔ Milestones.** Each phase file maps to a Linear Milestone. The link id
|
|
120
|
-
lives in the phase file's frontmatter (`linear_milestone_id`); its title ← the
|
|
101
|
+
- **Phases → Milestones.** Each phase file maps to a Linear Milestone. The link id
|
|
102
|
+
lives in the phase file's frontmatter (`linear_milestone_id`); its name ← the
|
|
121
103
|
phase h1, its description ← the phase `**Goal:**` line. The `Phases` index is
|
|
122
|
-
|
|
123
|
-
- **Tasks
|
|
124
|
-
is carried **inline** on the line — `- [ ] do the thing (SKI-123)`.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
milestone B in Linear both apply; only the *same* item moving on both sides is a
|
|
128
|
-
conflict.
|
|
129
|
-
- **Deletions are report-only.** A phase/milestone or task/issue removed on either
|
|
130
|
-
side is surfaced by `/spec-status` (and the pull/push summaries) for you to
|
|
131
|
-
resolve by hand — it is never auto-deleted.
|
|
104
|
+
stripped from the pushed `description` (no duplication).
|
|
105
|
+
- **Tasks → Issues.** Each `- [ ]` task line maps to a Linear Issue. The link id
|
|
106
|
+
is carried **inline** on the line — `- [ ] do the thing (SKI-123)`. The issue
|
|
107
|
+
**title** is the task's first sentence; the **description** is the full task
|
|
108
|
+
text; `[x]`/`[ ]` ↔ a completed / non-completed issue state.
|
|
132
109
|
|
|
133
110
|
Unlinked local items (a new phase with no `linear_milestone_id`, a task with no
|
|
134
111
|
inline id) are created in Linear on the next `/spec-push`, which stamps the new id
|
|
135
112
|
back so they link from then on.
|
|
136
113
|
|
|
137
|
-
##
|
|
138
|
-
|
|
139
|
-
The spec is a set of structured fields, most written by only one side. Marking a
|
|
140
|
-
field's owner collapses which fields can genuinely conflict:
|
|
141
|
-
|
|
142
|
-
- A `pull` field (Linear owns it) never reports as **pushable** — a stray local
|
|
143
|
-
edit is informational and gets reverted on the next pull.
|
|
144
|
-
- A `push` field (the repo owns it) never reports as **pullable**.
|
|
145
|
-
- Only a `both` field where **both** sides moved off the committed base is a real
|
|
146
|
-
`conflict` — `/spec-push` / `/spec-pull` refuse it unless `--force` (which
|
|
147
|
-
backs up the losing side into `sync.backupDir` first).
|
|
114
|
+
## One direction — nothing to reconcile
|
|
148
115
|
|
|
149
|
-
|
|
150
|
-
|
|
116
|
+
The repo owns the spec and pushes it; there is no pull. `/spec-push` diffs the
|
|
117
|
+
current spec against the committed **last-pushed snapshot** and sends only what
|
|
118
|
+
changed — creates for unlinked items, updates for changed ones. A workflow-state a
|
|
119
|
+
teammate moves in Linear is surfaced by `/spec-status` as **drift** (advisory) and
|
|
120
|
+
overwritten on the next push. Priority, labels, cycles and comments are
|
|
121
|
+
Linear-native triage and are never touched. No base merge, no conflicts, no
|
|
122
|
+
`--force`.
|
|
151
123
|
|
|
152
124
|
## What to commit
|
|
153
125
|
|
|
154
|
-
- **`sync.baseDir`** (default `specs/.core/linear-base/`) — **commit it.** The
|
|
155
|
-
|
|
156
|
-
worktree carries its own
|
|
157
|
-
|
|
158
|
-
These are `--force` recovery copies (a local reflog), per-machine and not shared.
|
|
159
|
-
Add `specs/.core/linear-backups/` to your `.gitignore`.
|
|
126
|
+
- **`sync.baseDir`** (default `specs/.core/linear-base/`) — **commit it.** The
|
|
127
|
+
last-pushed snapshot is content hashes of the last push, so `/spec-push` knows
|
|
128
|
+
what changed without reading Linear back; each worktree carries its own, so it
|
|
129
|
+
must travel with the branch.
|
|
@@ -68,13 +68,15 @@ Beneath it, `skitterspec spec-env live <take|release|abort|status>` is the engin
|
|
|
68
68
|
tracker-free: it knows nothing about any specific ticketing system. A
|
|
69
69
|
ticketing provider is installed as its own distribution that plugs into two named
|
|
70
70
|
**seams** in the shared skills (`/spec` Phase E, `/spec-go` step 3b) and fulfils a
|
|
71
|
-
skill-name + CLI contract
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
skill-name + CLI contract. Sync is **one-way**: the repo is the source of truth
|
|
72
|
+
and the tracker is a **generated mirror**. It ships `/spec-push` (repo→tracker;
|
|
73
|
+
computes a create/update plan against a committed last-pushed snapshot and applies
|
|
74
|
+
it) and `/spec-status` (read-only drift report — what would push, and whether the
|
|
75
|
+
tracker's workflow-state drifted), backed by a `spec-sync` CLI. There is no
|
|
76
|
+
content pull — the tracker is never read back or merged. When a provider is
|
|
77
|
+
present, `/spec` also links the spec to the tracker. With no provider installed
|
|
78
|
+
the seams are empty and every skill behaves as a plain filesystem workflow. See
|
|
79
|
+
the provider package's own docs for its config and field reference.
|
|
78
80
|
|
|
79
81
|
## Project conventions (fill this in)
|
|
80
82
|
|
|
@@ -217,11 +217,11 @@ finish as above.
|
|
|
217
217
|
## Phase E — link to a ticketing provider (only if one is installed)
|
|
218
218
|
|
|
219
219
|
**Only when a ticketing provider is installed and configured** (it ships the
|
|
220
|
-
`/spec-push` · `/spec-
|
|
220
|
+
`/spec-push` · `/spec-status` skills and a provider config under
|
|
221
221
|
`specs/.core/`). If none is present, skip this phase entirely — the spec stays
|
|
222
222
|
local-only and `/spec` behaves exactly as above. When a provider is present, link
|
|
223
223
|
the spec to the tracker after writing it, so status and discussion live there
|
|
224
|
-
while the repo stays the
|
|
224
|
+
while the repo stays the source of truth — follow the provider's link steps
|
|
225
225
|
below (nothing to do here without one).
|
|
226
226
|
|
|
227
227
|
**Only when `specs/.core/linear.config.json` exists** (Linear sync is opted in).
|
|
@@ -129,27 +129,25 @@ Before writing any code for this phase, get the workspace clean:
|
|
|
129
129
|
next phase on top of an uncommitted one. (Skip if this is the first phase —
|
|
130
130
|
there's nothing prior to commit.)
|
|
131
131
|
|
|
132
|
-
## 3b.
|
|
132
|
+
## 3b. Sync with the tracker (only if a provider is installed)
|
|
133
133
|
|
|
134
|
-
**Only when a ticketing provider
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
here without one).
|
|
134
|
+
**Only when a ticketing provider is installed** and the spec is linked to the
|
|
135
|
+
tracker. Otherwise skip this step — no provider means zero change to the flow
|
|
136
|
+
below. Follow the provider's steps below (nothing to do here without one).
|
|
138
137
|
|
|
139
138
|
**Only when `specs/.core/linear.config.json` exists** and the spec carries a
|
|
140
|
-
`linear_project_id
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
-
|
|
148
|
-
`
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
keep any manual status edits minimal to avoid fighting it.
|
|
139
|
+
`linear_project_id`. Otherwise skip this step — no config means zero change.
|
|
140
|
+
|
|
141
|
+
- **No pull.** Linear is a generated mirror in one-way sync, so there is nothing
|
|
142
|
+
to bring down before building — the repo is already the source of truth. (A
|
|
143
|
+
workflow-state a teammate moved in Linear is advisory only; `/spec-status`
|
|
144
|
+
surfaces it. It is overwritten on the next push.)
|
|
145
|
+
- **Optional: refresh the mirror.** If you want Linear to reflect the now
|
|
146
|
+
in-progress spec, run `/spec-push` to send it up. This is optional at
|
|
147
|
+
`/spec-go` time — the mirror can equally be refreshed later.
|
|
148
|
+
- Linear's GitHub branch/PR automation may drive status transitions off the
|
|
149
|
+
branch/PR you pushed in step 2; that's expected and the repo still wins on the
|
|
150
|
+
next `/spec-push`.
|
|
153
151
|
|
|
154
152
|
## 4. Implement the phase
|
|
155
153
|
|