@gallopsystems/agent-skills 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 (52) hide show
  1. package/README.md +137 -0
  2. package/package.json +26 -0
  3. package/plugins/doctl/.claude-plugin/plugin.json +8 -0
  4. package/plugins/doctl/skills/doctl/SKILL.md +93 -0
  5. package/plugins/kysely-postgres/.claude-plugin/plugin.json +8 -0
  6. package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +1101 -0
  7. package/plugins/kysely-postgres/skills/kysely-postgres/references/aggregations.ts +167 -0
  8. package/plugins/kysely-postgres/skills/kysely-postgres/references/ctes.ts +165 -0
  9. package/plugins/kysely-postgres/skills/kysely-postgres/references/expressions.ts +272 -0
  10. package/plugins/kysely-postgres/skills/kysely-postgres/references/joins.ts +206 -0
  11. package/plugins/kysely-postgres/skills/kysely-postgres/references/json-arrays.ts +398 -0
  12. package/plugins/kysely-postgres/skills/kysely-postgres/references/mutations.ts +199 -0
  13. package/plugins/kysely-postgres/skills/kysely-postgres/references/orderby-pagination.ts +117 -0
  14. package/plugins/kysely-postgres/skills/kysely-postgres/references/relations.ts +176 -0
  15. package/plugins/kysely-postgres/skills/kysely-postgres/references/select-where.ts +146 -0
  16. package/plugins/linear/.claude-plugin/plugin.json +8 -0
  17. package/plugins/linear/skills/linear/SKILL.md +1040 -0
  18. package/plugins/linear/skills/linear/bin/linear.mjs +1228 -0
  19. package/plugins/linear/skills/linear/tech-stack.md +273 -0
  20. package/plugins/nitro-testing/.claude-plugin/plugin.json +8 -0
  21. package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +497 -0
  22. package/plugins/nitro-testing/skills/nitro-testing/async-testing.md +270 -0
  23. package/plugins/nitro-testing/skills/nitro-testing/ci-setup.md +226 -0
  24. package/plugins/nitro-testing/skills/nitro-testing/examples/global-setup.ts +90 -0
  25. package/plugins/nitro-testing/skills/nitro-testing/examples/handler.test.ts +167 -0
  26. package/plugins/nitro-testing/skills/nitro-testing/examples/setup.ts +29 -0
  27. package/plugins/nitro-testing/skills/nitro-testing/examples/test-utils-index.ts +297 -0
  28. package/plugins/nitro-testing/skills/nitro-testing/examples/vitest.config.ts +42 -0
  29. package/plugins/nitro-testing/skills/nitro-testing/factories.md +278 -0
  30. package/plugins/nitro-testing/skills/nitro-testing/frontend-testing.md +512 -0
  31. package/plugins/nitro-testing/skills/nitro-testing/test-utils.md +262 -0
  32. package/plugins/nitro-testing/skills/nitro-testing/transaction-rollback.md +183 -0
  33. package/plugins/nitro-testing/skills/nitro-testing/vitest-config.md +236 -0
  34. package/plugins/nuxt-nitro-api/.claude-plugin/plugin.json +8 -0
  35. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +260 -0
  36. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/auth-patterns.md +228 -0
  37. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +174 -0
  38. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/deep-linking.md +190 -0
  39. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-middleware.ts +32 -0
  40. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-utils.ts +51 -0
  41. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/deep-link-page.vue +61 -0
  42. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/service-util.ts +63 -0
  43. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/sse-endpoint.ts +59 -0
  44. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/validation-endpoint.ts +38 -0
  45. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/fetch-patterns.md +178 -0
  46. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/nitro-tasks.md +243 -0
  47. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/page-structure.md +162 -0
  48. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/server-services.md +238 -0
  49. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/sse.md +221 -0
  50. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/ssr-client.md +166 -0
  51. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/validation.md +131 -0
  52. package/scripts/link-skills.mjs +252 -0
@@ -0,0 +1,1040 @@
1
+ ---
2
+ name: linear
3
+ description: Create, triage, and manage Linear issues at Gallop Systems following the team's workflow conventions — cycle placement, issue templates, project/milestone hierarchy, and project refresh / cycle rebalance procedures. Use whenever the user asks for Linear work (creating issues, planning cycles, refreshing projects) on a Gallop client.
4
+ ---
5
+
6
+ # Linear Project Management — Team Workflow & CLI Guide
7
+
8
+ ## The CLI
9
+
10
+ The fallback tooling is a single zero-dependency Node script, `bin/linear.mjs`. It runs on bare `node` (v18.3+ — no `npm install`, no `tsx`, no build step) and uses symbolic names instead of raw UUIDs (`--state todo`, `--assignee frontend`, `--labels bug,frontend`, `--cycle current`).
11
+
12
+ Invoke it as `node <skill>/bin/linear.mjs <command> [args] [--flags]`. Examples below write `node linear.mjs` for brevity — use the full path to the file, or `cd` into the skill's `bin/` directory first. Run `node linear.mjs help` for the full command list.
13
+
14
+ ## First-time Setup (run once per user)
15
+
16
+ ### Check 1 — Workspace bootstrap config exists
17
+
18
+ Before running any `linear.mjs` command, verify that the per-user workspace config exists at `~/.config/linctl/workspace.json` (override path with `$LINCTL_WORKSPACE_FILE`). This file holds the team UUID, the Linear member UUIDs that play the Frontend/PM and Backend roles, and the workflow-state and label UUIDs the CLI resolves symbolic names against. Without it, every command that needs the team, members, states, or labels will refuse to run.
19
+
20
+ ```bash
21
+ [ -f "${LINCTL_WORKSPACE_FILE:-$HOME/.config/linctl/workspace.json}" ] && echo "ok" || echo "missing"
22
+ ```
23
+
24
+ **If missing,** instruct the user to run:
25
+
26
+ ```bash
27
+ node linear.mjs init
28
+ ```
29
+
30
+ `init` calls Linear's GraphQL API, lists the workspace's members, and prompts the user to designate (1) the Frontend/PM lead and (2) the Backend lead by number. It then writes `~/.config/linctl/workspace.json`. The config is read fresh on every invocation — no re-sourcing needed.
31
+
32
+ ### Check 2 — Linear MCP server installed and authorized
33
+
34
+ This skill routes most operations through `mcp__linear-server__*` tools. Before doing any Linear work, verify the MCP server is available:
35
+
36
+ - **Not installed:** if no `mcp__linear-server__*` tools appear in your toolset, stop and tell the user: *"This skill needs Linear's MCP server. Install it with `claude mcp add --transport sse linear https://mcp.linear.app/sse`, restart Claude Code, then tell me to continue."* Don't try to fall back to `linear.mjs` for everything — the CLI only covers a small subset of operations.
37
+ - **Installed but not authorized:** if a `mcp__linear-server__*` call returns an auth/OAuth error, tell the user: *"The Linear MCP server is installed but not authorized. The next call will open a browser to sign in — please complete OAuth, then tell me to continue."*
38
+
39
+ Don't silently skip these checks. A user who hits an MCP error mid-task without context will be confused.
40
+
41
+ ### Check 3 — `LINEAR_API_KEY` for the CLI
42
+
43
+ `linear.mjs` reads `LINEAR_API_KEY` from the environment. Before using any command, check whether it's set:
44
+
45
+ ```bash
46
+ [ -n "$LINEAR_API_KEY" ] && echo "set" || echo "missing"
47
+ ```
48
+
49
+ **If missing, onboard the user:**
50
+
51
+ 1. Tell them: *"I need a Linear personal API key to run the CLI. Create one at https://linear.app/settings/account/security (click 'New API key', name it 'Claude Code', copy the `lin_api_...` token), then paste it here in chat."*
52
+ 2. When they paste the key, install it into `~/.zshenv` so every future shell — including the ones Claude Code spawns — picks it up automatically:
53
+ ```bash
54
+ echo 'export LINEAR_API_KEY=lin_api_THEIR_KEY_HERE' >> ~/.zshenv
55
+ ```
56
+ (Use `~/.bashrc` instead if the user is on bash.)
57
+ 3. Export it in the current shell too so the next tool call works without restart:
58
+ ```bash
59
+ export LINEAR_API_KEY=lin_api_THEIR_KEY_HERE
60
+ ```
61
+ 4. Verify with a harmless call: `node linear.mjs list-members`.
62
+
63
+ **Never commit the key, never write it into `.env` or any project file** — `~/.zshenv` is the single source of truth.
64
+
65
+ ---
66
+
67
+ ## Team Overview
68
+
69
+ - **Workspace Team Key:** `GAL`
70
+ - **Team Size:** 2 members
71
+ - **Sprint Cycle:** 2 weeks
72
+ - **Stack:** Nuxt 4 (Vue frontend + Nitro backend)
73
+ - **Work Type:** Client/agency projects
74
+
75
+ ### Team Roles
76
+
77
+ | Role | Responsibilities |
78
+ |------|-----------------|
79
+ | **Frontend/PM Lead** | Frontend development, requirement gathering, project design, client communication, light backend (e.g., adding endpoints), issue triage, client IT coordination (DNS, infrastructure requests) |
80
+ | **Backend Lead** | Data modeling, database design, backend architecture, API logic |
81
+
82
+ The Frontend/PM lead triages incoming client requests and translates them into Linear issues.
83
+
84
+ On first run, `node linear.mjs init` binds these roles to specific Linear members; Claude reads `~/.config/linctl/workspace.json` to know who they are. Pass `--assignee frontend` or `--assignee backend` and the CLI resolves it to the corresponding Linear user UUID.
85
+
86
+ ---
87
+
88
+ ## Workflow Statuses
89
+
90
+ | Status | Meaning |
91
+ |--------|---------|
92
+ | **Backlog** | Captured but not yet planned for a cycle |
93
+ | **Todo** | Committed to the current or next cycle |
94
+ | **In Progress** | Actively being worked on |
95
+ | **In Review** | Code complete, awaiting review or client feedback |
96
+ | **Done** | Shipped and verified |
97
+ | **Canceled** | Dropped or no longer relevant |
98
+
99
+ > **Note:** "In Review" is a recommended addition to the default Linear statuses. It provides a clear handoff point for code review between the two team members and for client sign-off.
100
+
101
+ ---
102
+
103
+ ## Priority Levels
104
+
105
+ | Priority | When to Use |
106
+ |----------|-------------|
107
+ | **Urgent** | Production issues, client-blocking bugs, deadline-critical items |
108
+ | **High** | Current sprint commitments, important client deliverables |
109
+ | **Medium** | Planned work, non-blocking improvements |
110
+ | **Low** | Nice-to-haves, tech debt, internal tooling |
111
+
112
+ ---
113
+
114
+ ## Labels (Recommended)
115
+
116
+ ### By Type
117
+ - `bug` — Something is broken
118
+ - `feature` — New functionality
119
+ - `improvement` — Enhancement to existing functionality
120
+ - `chore` — Maintenance, config, devops, dependencies
121
+ - `spike` — Research or investigation task
122
+
123
+ ### By Domain
124
+ - `frontend` — UI/UX, Vue components, pages, styling
125
+ - `backend` — API, database, data modeling, server logic
126
+ - `fullstack` — Touches both frontend and backend
127
+
128
+ ---
129
+
130
+ ## Estimation (T-Shirt Sizes)
131
+
132
+ | Size | Meaning | Rough Effort |
133
+ |------|---------|-------------|
134
+ | **S** | Small, well-understood task | A few hours |
135
+ | **M** | Medium complexity, clear scope | Half a day to a full day |
136
+ | **L** | Large, may span multiple days | 2–3 days |
137
+ | **XL** | Very large — consider breaking down | 3+ days, likely needs subtasks |
138
+
139
+ If an issue is XL, break it into smaller sub-issues before starting work.
140
+
141
+ ---
142
+
143
+ ## Sprint Cycle Process
144
+
145
+ ### Cycle Start (Every 2 Weeks)
146
+ 1. Review **Backlog** — pull items into **Todo** for the cycle
147
+ 2. Assign issues to the appropriate team member based on domain (backend vs. frontend)
148
+ 3. Ensure each issue has: priority, estimate, label(s), and assignee
149
+ 4. Keep cycle scope realistic — a 2-person team should commit to what's achievable
150
+
151
+ ### During the Cycle
152
+ - Move issues to **In Progress** when you start working on them
153
+ - Move to **In Review** when code is ready for review or client feedback
154
+ - Move to **Done** when merged/deployed and verified
155
+ - If scope changes, add new issues to **Backlog** unless they're urgent
156
+
157
+ ### Cycle End
158
+ - Review what got done vs. what was planned
159
+ - Move incomplete **Todo** / **In Progress** items to the next cycle or back to **Backlog**
160
+ - Archive the completed cycle
161
+
162
+ ---
163
+
164
+ ## Linear Tooling — MCP First, `linear.mjs` as Fallback
165
+
166
+ **Default to the Linear MCP server (`mcp__linear__*` tools)** for all standard operations: creating/updating issues, listing projects/milestones/cycles/initiatives/labels/users, comments, etc. The MCP tools take strings directly — pass real markdown with real newlines, no JSON-escaping.
167
+
168
+ **Use `linear.mjs` only for things MCP doesn't expose:**
169
+ - `cycle-capacity` — velocity-based capacity % (used in cycle placement & rebalancing)
170
+ - `batch-move-to-cycle` / `batch-move-to-milestone` — rate-limit-aware bulk moves
171
+ - `add-dependency` / `remove-dependency` / `list-dependencies` — issue relations
172
+ - `add-initiative-link` — adding external links to initiatives
173
+ - `api` — raw GraphQL escape hatch
174
+
175
+ **Mapping** of common CLI → MCP equivalents lives in `MEMORY.md`. The sections below document the CLI for the fallback paths and for reference; prefer the MCP tool whenever one exists.
176
+
177
+ ### The CLI
178
+
179
+ `bin/linear.mjs` wraps the Linear GraphQL API. It runs on bare `node` (v18.3+, no install) and reads `LINEAR_API_KEY` from the environment plus the workspace config from `~/.config/linctl/workspace.json`. There is nothing to source — every invocation loads config fresh.
180
+
181
+ ```bash
182
+ node linear.mjs help # full command list
183
+ ```
184
+
185
+ ### Symbolic names (no UUIDs needed)
186
+
187
+ The CLI resolves friendly names against `workspace.json`, so you rarely need raw UUIDs:
188
+
189
+ ```
190
+ --state todo | backlog | "in progress" | "in review" | done | canceled (or a UUID)
191
+ --assignee frontend | backend (or a UUID)
192
+ --labels bug,frontend,feature (comma-separated label names) (or UUIDs)
193
+ --priority 0-4 or none | urgent | high | medium | low
194
+ --cycle current (the active cycle) (or a UUID)
195
+ ```
196
+
197
+ Any value that's already a UUID is passed through untouched. Project and milestone IDs are still UUIDs (pass them with `--project` / `--milestone`).
198
+
199
+ ### Creating Issues
200
+
201
+ > **Important:** When assigning an issue to a cycle, always set `--state todo`. Issues default to Backlog, which doesn't work with cycles — they must be in Todo status.
202
+ >
203
+ > **Required placement rule:** Never create an issue without both `--project` and `--milestone`. If the right project does not exist, create it first. If the project exists but the right milestone does not, create the milestone first. Do not leave issues unscoped or unmilestoned.
204
+
205
+ ```bash
206
+ # --state todo is required when using --cycle
207
+ # --project and --milestone are always required
208
+ node linear.mjs create-issue \
209
+ --title 'Add user profile page' \
210
+ --description 'Create /profile page with user info and settings' \
211
+ --priority high \
212
+ --state todo \
213
+ --assignee frontend \
214
+ --labels feature,frontend \
215
+ --estimate 3 \
216
+ --project 'project-uuid-here' \
217
+ --milestone 'milestone-uuid-here' \
218
+ --cycle current
219
+
220
+ # Create a bug report
221
+ node linear.mjs create-issue \
222
+ --title 'Fix: login redirect fails on Safari' \
223
+ --description 'Users on Safari not redirected after login. Reproduced on Safari 17.' \
224
+ --priority urgent \
225
+ --state todo \
226
+ --assignee frontend \
227
+ --labels bug,frontend \
228
+ --project 'project-uuid-here' \
229
+ --milestone 'milestone-uuid-here' \
230
+ --cycle current
231
+
232
+ # Long descriptions: pass a file instead of inline text (no shell-escaping)
233
+ node linear.mjs create-issue --title 'Investigate perf issue' --state todo \
234
+ --description-file ./issue-body.md \
235
+ --project 'project-uuid' --milestone 'milestone-uuid'
236
+
237
+ # If the project or milestone does not exist yet, create it before the issue
238
+ PROJECT_ID="$(node linear.mjs create-project --name '[CLIENT] Feature Area' --description 'Short description' | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectCreate.project.id))")"
239
+ MILESTONE_ID="$(node linear.mjs create-milestone "$PROJECT_ID" 'Phase 1' | node -e "process.stdin.once('data',d=>{const n=JSON.parse(d).data.projectMilestoneCreate.projectMilestone;console.log(n.id)})")"
240
+ node linear.mjs create-issue --title 'Investigate performance issue' --state todo \
241
+ --project "$PROJECT_ID" --milestone "$MILESTONE_ID" --cycle current
242
+ ```
243
+
244
+ ### Priority Values
245
+ - `0` = No priority
246
+ - `1` = Urgent
247
+ - `2` = High
248
+ - `3` = Medium
249
+ - `4` = Low
250
+
251
+ ### Listing & Filtering Issues
252
+ ```bash
253
+ # List all issues (pretty table by default)
254
+ node linear.mjs list-issues
255
+
256
+ # Filter by state type: backlog, unstarted, started, completed, canceled
257
+ node linear.mjs list-issues started
258
+
259
+ # List issues in current cycle
260
+ node linear.mjs list-cycle-issues
261
+
262
+ # Raw JSON output (for piping) — add --json to any list command
263
+ node linear.mjs list-issues --json
264
+ node linear.mjs list-issues started --json
265
+ ```
266
+
267
+ ### Updating Issues
268
+ ```bash
269
+ # Move issue by status name
270
+ node linear.mjs move-issue "issue-uuid" "In Progress"
271
+ node linear.mjs move-issue "issue-uuid" "Done"
272
+
273
+ # Assign to a team member by role
274
+ node linear.mjs assign-issue "issue-uuid" frontend
275
+ node linear.mjs assign-issue "issue-uuid" backend
276
+
277
+ # General update — symbolic flags
278
+ node linear.mjs update-issue "issue-uuid" --priority urgent --state todo
279
+
280
+ # Or merge arbitrary raw JSON input with --raw
281
+ node linear.mjs update-issue "issue-uuid" --raw '{"priority":1}'
282
+ ```
283
+
284
+ ### Issue Dependencies
285
+ ```bash
286
+ # Create a "blocks" dependency (backend blocks frontend)
287
+ node linear.mjs add-dependency "$BLOCKER_ISSUE_ID" "$BLOCKED_ISSUE_ID"
288
+
289
+ # List all dependencies for an issue (both directions)
290
+ node linear.mjs list-dependencies "$ISSUE_ID"
291
+
292
+ # Remove a dependency by relation UUID (get UUID from list-dependencies)
293
+ node linear.mjs remove-dependency "$RELATION_ID"
294
+ ```
295
+
296
+ ### Comments
297
+ ```bash
298
+ # Add a comment to an issue
299
+ node linear.mjs add-comment "$ISSUE_ID" --body "Comment body text here"
300
+
301
+ # Long comment from a file (no shell-escaping)
302
+ node linear.mjs add-comment "$ISSUE_ID" --body-file ./comment.md
303
+ ```
304
+
305
+ > **Note:** Always use `@` mentions when referring to team members in comments. Use the Linear `@` mention syntax with the team member's display name from `workspace.json`'s `roles` (e.g., `@<Frontend Lead Name>`, `@<Backend Lead Name>`) so they get properly notified.
306
+
307
+ ### Searching
308
+ ```bash
309
+ node linear.mjs search-issues "login bug"
310
+ ```
311
+
312
+ ### Projects & Milestones
313
+ ```bash
314
+ # Create a new project (linked to an initiative)
315
+ node linear.mjs create-project --name "[KEY] Project Name" --initiative "$INITIATIVE_ID" --description "Short description"
316
+
317
+ # List all projects (pretty table with initiative, state, progress)
318
+ node linear.mjs list-projects
319
+
320
+ # List milestones within a project
321
+ node linear.mjs list-milestones "$PROJECT_ID"
322
+
323
+ # List issues grouped by milestone within a project
324
+ node linear.mjs list-project-issues "$PROJECT_ID"
325
+
326
+ # Raw JSON variants (for piping) — add --json
327
+ node linear.mjs list-projects --json
328
+ node linear.mjs list-milestones "$PROJECT_ID" --json
329
+ node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
330
+
331
+ # Create issue within a project/milestone
332
+ node linear.mjs create-issue \
333
+ --title 'Add feature X' \
334
+ --project 'project-uuid' \
335
+ --milestone 'milestone-uuid' \
336
+ --priority high \
337
+ --assignee frontend \
338
+ --labels feature
339
+ ```
340
+
341
+ ### Initiatives
342
+ ```bash
343
+ # Create a new initiative (= new client)
344
+ node linear.mjs create-initiative --name "ClientName" --description "Short description"
345
+
346
+ # List all initiatives (pretty table with ID, status, description)
347
+ node linear.mjs list-initiatives
348
+
349
+ # Get full initiative detail by name (case-insensitive)
350
+ node linear.mjs get-initiative-by-name "Northwind"
351
+
352
+ # Get full initiative detail by ID
353
+ node linear.mjs get-initiative "$INITIATIVE_ID"
354
+
355
+ # Update initiative content (markdown) or description
356
+ node linear.mjs update-initiative "$INITIATIVE_ID" --content-file ./initiative-notes.md
357
+ node linear.mjs update-initiative "$INITIATIVE_ID" --description "Short description"
358
+
359
+ # Add an external link (e.g., repo) as a resource on the initiative
360
+ node linear.mjs add-initiative-link "$INITIATIVE_ID" "https://github.com/org/repo" "GitHub Repo"
361
+
362
+ # Raw JSON of all initiatives
363
+ node linear.mjs list-initiatives --json
364
+ ```
365
+
366
+ ### Info Commands
367
+ ```bash
368
+ node linear.mjs list-states # Workflow states
369
+ node linear.mjs list-members # Team members
370
+ node linear.mjs list-labels # Labels
371
+ node linear.mjs list-cycles # All cycles
372
+ node linear.mjs current-cycle-id # Current active cycle UUID
373
+ ```
374
+
375
+ ---
376
+
377
+ ## Issue Templates
378
+
379
+ > **Tech stack context:** All projects use Nuxt 4 + Nitro + Kysely + PostgreSQL + PrimeVue/Volt + Tailwind CSS v4. See `tech-stack.md` for full details.
380
+
381
+ ### Issue Title Conventions
382
+
383
+ - **No client prefix** (e.g., ~~[GBX]~~) — the project name already identifies the client.
384
+ - **No domain prefix** (e.g., ~~UI:~~, ~~API:~~) — labels (`frontend`, `backend`) already cover this.
385
+ - Titles should be concise and describe the feature/fix directly (e.g., "Add provider create form", "Fix login redirect on Safari").
386
+
387
+ ### Client Feature Request — Frontend
388
+
389
+ > **Important:** Do NOT guess which pages/components need updating. Check the client's repo (`app/pages/`, `app/components/`) to identify the correct files and routes. If the repo is not accessible, add a **## To Determine** section listing what needs to be verified before work begins (e.g., "Which page renders the jobs list? Check repo.").
390
+
391
+ ```
392
+ Title: Feature description
393
+ Priority: High (2) or Medium (3)
394
+ Labels: feature, frontend
395
+ Estimate: S/M/L/XL
396
+ Description:
397
+ ## Context
398
+ [Why does the client need this?]
399
+
400
+ ## Requirements
401
+ - [ ] Requirement 1
402
+ - [ ] Requirement 2
403
+
404
+ ## UI Notes
405
+ - Page/route: `/path` ← verified from repo, NOT guessed
406
+ - Components: [Which Volt components are relevant — VoltCard, VoltDataTable, etc.]
407
+ - Follow DESIGN_LANGUAGE.md (zinc palette, no decorative shadows)
408
+
409
+ ## To Determine (if repo not checked)
410
+ - [ ] Which page/route handles this feature?
411
+ - [ ] Which existing components need modification?
412
+
413
+ ## Acceptance Criteria
414
+ - [ ] What "done" looks like
415
+ ```
416
+
417
+ ### Client Feature Request — Backend / API
418
+
419
+ > **Note:** Backend issues should describe *what* functionality is needed, not *how* to implement it. The Backend lead knows which endpoints to create, how to structure handlers, and what validation to add. Focus the description on the functionality the backend needs to support and any business rules or constraints.
420
+
421
+ ```
422
+ Title: Feature description
423
+ Priority: High (2) or Medium (3)
424
+ Labels: feature, backend
425
+ Estimate: S/M/L/XL
426
+ Description:
427
+ ## Context
428
+ [Why does the client need this? What problem does it solve for the client?]
429
+
430
+ ## Functionality
431
+ - [What the backend needs to support — describe the behavior, not the implementation]
432
+ - [Business rules, constraints, edge cases]
433
+ - [What data needs to be stored, returned, or transformed]
434
+ - [Auth considerations if non-standard (e.g., public access, webhook)]
435
+
436
+ ## Acceptance Criteria
437
+ - [ ] What "done" looks like from a functionality perspective
438
+ - [ ] Tests written
439
+ ```
440
+
441
+ ### Fullstack Features — Split Into Separate Issues
442
+
443
+ When a feature requires both backend and frontend work, **always create separate issues** — one for backend and one for frontend. Link them using **Linear's dependency system** so the frontend issue is blocked by the backend issue.
444
+
445
+ > **Reminder:** For the frontend issue, verify affected pages/components from the client's repo. Don't guess file paths — check `app/pages/` and `app/components/` in the actual codebase.
446
+
447
+ This keeps issues focused, enables parallel assignment (the Backend lead on backend, the Frontend/PM lead on frontend), and makes progress tracking clearer. Using Linear dependencies (rather than just mentioning the dependency in the description) makes the blocking relationship visible in the UI, prevents the frontend issue from accidentally being started too early, and keeps the dependency machine-readable.
448
+
449
+ **Steps:**
450
+ 1. Create the **backend issue** using the "Client Feature Request — Backend / API" template above (labels: `feature`, `backend`)
451
+ 2. Create the **frontend issue** using the "Client Feature Request — Frontend" template above (labels: `feature`, `frontend`)
452
+ 3. **Create the Linear dependency:** use `add-dependency` so the backend issue blocks the frontend issue
453
+
454
+ ```bash
455
+ # After creating both issues, link them:
456
+ node linear.mjs add-dependency "$BACKEND_ISSUE_ID" "$FRONTEND_ISSUE_ID"
457
+ # Result: backend blocks frontend (frontend is blocked by backend)
458
+ ```
459
+
460
+ **Example:** "Add admin button to complete all job tasks"
461
+ - **Backend issue:** Support marking all tasks for a job as complete in a single operation; admin-only, should be atomic
462
+ - **Frontend issue:** Admin-only button on job page, confirmation dialog, API call, toast
463
+ - **Dependency:** `node linear.mjs add-dependency "$BACKEND_ID" "$FRONTEND_ID"`
464
+
465
+ > **Note:** If the feature is simple enough that the backend is trivial (e.g., a single straightforward CRUD endpoint), it's acceptable to create one combined issue assigned to the person doing both. Use your judgement.
466
+
467
+ ### Bug Report
468
+ ```
469
+ Title: Fix: brief description of the bug
470
+ Priority: Urgent (1) or High (2)
471
+ Labels: bug, frontend|backend
472
+ Description:
473
+ ## Bug
474
+ [What's happening vs. what should happen]
475
+
476
+ ## Steps to Reproduce
477
+ 1. Step 1
478
+ 2. Step 2
479
+
480
+ ## Environment
481
+ [Browser, OS, user account, etc.]
482
+
483
+ ## Likely Location
484
+ - [File path if known, e.g., server/api/users/[id].get.ts or app/pages/users.vue]
485
+ ```
486
+
487
+ ### Backend / Data Modeling Task
488
+
489
+ > **Note:** Focus on *what* data needs to be modeled and *why*, not on prescribing specific schema details or endpoint structures. Include business context and constraints so the Backend lead can make the right design decisions.
490
+
491
+ ```
492
+ Title: Description of the task
493
+ Priority: as appropriate
494
+ Labels: backend
495
+ Estimate: S/M/L/XL
496
+ Description:
497
+ ## Objective
498
+ [What data model or API change is needed and why]
499
+
500
+ ## Requirements
501
+ - [What data needs to be stored/tracked]
502
+ - [Relationships to existing data (e.g., "each job has many tasks")]
503
+ - [Business rules and constraints]
504
+ - [Any existing data that needs migrating]
505
+
506
+ ## Acceptance Criteria
507
+ - [ ] What "done" looks like
508
+ - [ ] Tests written
509
+ ```
510
+
511
+ ### Chore / Maintenance
512
+ ```
513
+ Title: Chore: description
514
+ Priority: Medium (3) or Low (4)
515
+ Labels: chore, frontend|backend
516
+ Estimate: S/M/L
517
+ Description:
518
+ ## What
519
+ [What needs to be done]
520
+
521
+ ## Why
522
+ [Why it matters — tech debt, performance, DX, etc.]
523
+
524
+ ## Files Affected
525
+ - [List key files/directories]
526
+ ```
527
+
528
+ ---
529
+
530
+ ## Assignment Guidelines
531
+
532
+ | Issue Type | Default Assignee |
533
+ |-----------|-----------------|
534
+ | Kysely migrations, schema design, complex DB queries | Backend lead |
535
+ | Complex Nitro API handlers (transactions, multi-table) | Backend lead |
536
+ | Vue pages, Volt components, Tailwind styling, UX | Frontend/PM lead |
537
+ | Simple CRUD API endpoint (single table, straightforward) | Either (Frontend/PM lead can handle) |
538
+ | Client requirement gathering, design | Frontend/PM lead |
539
+ | Bug — Kysely/DB/server middleware | Backend lead |
540
+ | Bug — Vue/PrimeVue/Tailwind/client-side | Frontend/PM lead |
541
+ | Bug — fullstack | Discuss, assign based on root cause |
542
+
543
+ ---
544
+
545
+ ## Post-Organization: Update Initiative in Linear
546
+
547
+ **After organizing issues for a client (creating, triaging, updating statuses, or completing a sprint review), always update the corresponding initiative's `content` field in Linear.**
548
+
549
+ ### What to Update
550
+
551
+ The initiative `content` field stores **client-level context only** — NOT data already tracked elsewhere in Linear. Update:
552
+
553
+ 1. **Overview** — Client description, domain context, business purpose
554
+ 2. **Repo structure** — Routes, components, API endpoints, key files
555
+ 3. **Tech stack deviations** — Anything different from the standard Gallop template
556
+ 4. **Domain concepts** — Key entities and business logic specific to the client
557
+ 5. **Notes** — High-level observations, architectural decisions, gotchas
558
+
559
+ **Do NOT put in initiative content:** Team members (already in Linear), repo links (use `add-initiative-link` instead), project listings, milestone details, issue counts, progress percentages, remaining work, or any data already tracked in Linear's project/milestone/issue hierarchy.
560
+
561
+ ### When to Update
562
+
563
+ - After creating a batch of new issues for a client
564
+ - After triaging/re-prioritizing a client's backlog
565
+ - After a sprint review or cycle close
566
+ - After marking significant issues as Done or Canceled
567
+ - Any time the initiative's content would be stale after your changes
568
+
569
+ ### How to Get Current Data
570
+
571
+ Use the Linear CLI to query the initiative and pull fresh issue data:
572
+ ```bash
573
+ # Get the initiative's current content
574
+ node linear.mjs get-initiative-by-name "ClientName"
575
+ # List all issues to see current statuses
576
+ node linear.mjs list-issues
577
+ # Or check cycle-specific progress
578
+ node linear.mjs list-cycle-issues
579
+ ```
580
+
581
+ Then update the initiative's content in Linear (use a file for the markdown body):
582
+ ```bash
583
+ node linear.mjs update-initiative "$INITIATIVE_ID" --content-file ./initiative-notes.md
584
+ ```
585
+
586
+ ---
587
+
588
+ ## Project & Milestone Hierarchy
589
+
590
+ Linear organizes work in a top-down hierarchy: **Initiative → Project → Milestone → Issue**. Here's how the Gallop team uses each level.
591
+
592
+ ### Initiative (= Client)
593
+
594
+ An **Initiative** represents a client engagement or internal program. Each client gets one initiative.
595
+
596
+ **The current client roster is not stored in this repo — fetch it live from Linear.** Initiatives are the source of truth for which clients exist, their descriptions, and their repo links:
597
+
598
+ - **List all clients:** `mcp__linear-server__list_initiatives`
599
+ - **Read a client's full details (overview, repo structure, domain notes):** `mcp__linear-server__get_initiative` — these live in the initiative's `content` field
600
+ - **Get a client's repo URL:** read the `links` array on the initiative
601
+
602
+ When you start any task that needs client context, query Linear instead of looking for a hardcoded list. This keeps the skill in sync as clients are added or removed without repo changes.
603
+
604
+ - One initiative can contain **multiple projects**
605
+
606
+ ### Project (= Product / Workstream)
607
+
608
+ A **Project** is a distinct product, app, or major workstream within a client initiative. It groups related issues that ship together.
609
+
610
+ **Examples:**
611
+ - `[ACME] Billing System` — one self-contained product
612
+ - `[ACME] Analytics Demo` — separate product under the same client
613
+ - `[CLIENT] Migration Workstream` — the single workstream for that client
614
+ - `[CLIENT] Scheduling Platform` — the main product for that client
615
+
616
+ **When to create a new project:**
617
+ - The work has its own deployment, repo, or codebase
618
+ - It could be described independently to a stakeholder
619
+ - It has a distinct "done" state separate from other work
620
+
621
+ **Naming convention:** `[CLIENT_KEY] Project Name`
622
+
623
+ ### Milestone (= Phase / Epic)
624
+
625
+ A **Milestone** is a phase or epic within a project — a meaningful chunk of progress that can be demoed or shipped incrementally.
626
+
627
+ **Examples within `[ACME] Billing System`:**
628
+ - `Core Billing` — create, edit, send invoices (done)
629
+ - `Quotes` — quote workflow, create/edit/convert to invoice
630
+ - `Payments` — payment methods, receipts, balance due display
631
+
632
+ **Examples within `[NW] Appointment Scheduling`:**
633
+ - `Providers Module` — list, create, edit, deactivate providers
634
+ - `Booking Requests` — request creation, accept/reject workflow
635
+ - `Scheduling & Calendar` — availability, scheduling UI
636
+
637
+ **When to create a milestone:**
638
+ - A logical group of 5–15 related issues
639
+ - Has a clear "phase complete" definition
640
+ - Can be reviewed/demoed as a unit
641
+ - Work within it is mostly sequential or tightly coupled
642
+
643
+ **Naming convention:** Short, descriptive noun phrase (no client key prefix needed since milestones live inside a project)
644
+
645
+ ### Issue (= Task)
646
+
647
+ Individual work items live at the bottom of the hierarchy. Every issue belongs to a project and a milestone.
648
+
649
+ ### Hierarchy in Practice
650
+
651
+ ```
652
+ Initiative: Northwind
653
+ └── Project: [NW] Appointment Scheduling
654
+ ├── Milestone: Providers Module
655
+ │ ├── ACME-101: Create providers list page
656
+ │ ├── ACME-102: Add provider create/edit form
657
+ │ └── ACME-103: Provider deactivation support
658
+ ├── Milestone: Booking Requests
659
+ │ ├── ACME-110: Request creation form
660
+ │ └── ACME-111: Accept/reject API endpoints
661
+ └── Milestone: Notifications
662
+ └── ACME-120: Set up email service
663
+ ```
664
+
665
+ ### Guidelines for the Team
666
+
667
+ 1. **Every issue must be placed into a cycle with Todo status.** **Do NOT default to the current/active cycle.** Follow this procedure: (a) Run `cycle-capacity` to see each cycle's capacity % (velocity-based, from last 3 completed cycles). (b) Starting from the earliest (current) cycle, find the first cycle that is **strictly under 100%** capacity. (c) If the current cycle is at or above 100%, **skip it** and use the next cycle with room. Assign the issue there via `--cycle`. **Always set `--state todo`** — issues in Backlog don't work with cycles. **Exception:** High priority or above (priority ≤ 2: Urgent, High) always go into the current active cycle regardless of capacity.
668
+ 2. **Every issue must belong to a project and a milestone.** Never create orphan issues and never leave an issue outside a milestone.
669
+ 3. **If the correct project does not exist, create it before creating the issue.** Do not park work in a generic team backlog while waiting to organize it later.
670
+ 4. **If the correct milestone does not exist, create it before creating the issue.** Milestone creation is part of issue intake, not optional cleanup.
671
+ 5. **Use milestones for sequencing.** Milestones can have target dates, making them useful for communicating delivery phases to clients.
672
+ 6. **Track progress in Linear.** After creating/updating projects or milestones, update the initiative's content in Linear to reflect the current structure (see "Post-Organization: Update Initiative in Linear" below).
673
+ 7. **When creating issues with the CLI**, use the `--project`, `--milestone`, and `--cycle` flags to place issues correctly in the hierarchy and cycle.
674
+
675
+ ### CLI Examples
676
+
677
+ ```bash
678
+ # List projects for the team
679
+ node linear.mjs list-projects
680
+
681
+ # List milestones within a project
682
+ node linear.mjs list-milestones "$PROJECT_ID"
683
+
684
+ # If needed, create the missing project or milestone before creating the issue
685
+ PROJECT_ID="$(node linear.mjs create-project --name "[CLIENT] Feature Area" --description "Short description" | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectCreate.project.id))")"
686
+ MILESTONE_ID="$(node linear.mjs create-milestone "$PROJECT_ID" "Phase 1" | node -e "process.stdin.once('data',d=>console.log(JSON.parse(d).data.projectMilestoneCreate.projectMilestone.id))")"
687
+
688
+ # Create an issue within a project and milestone (with cycle)
689
+ node linear.mjs create-issue \
690
+ --title 'Add provider create form' \
691
+ --description '...' \
692
+ --priority high \
693
+ --state todo \
694
+ --assignee frontend \
695
+ --labels feature,frontend \
696
+ --project 'project-uuid-here' \
697
+ --milestone 'milestone-uuid-here' \
698
+ --cycle current
699
+ ```
700
+
701
+ ---
702
+
703
+ ## Project Refresh — Milestone Restructuring
704
+
705
+ When a project's milestone structure becomes outdated (or was never set up), use the **project refresh** workflow to reorganize milestones without losing or changing any issues.
706
+
707
+ ### When to Refresh
708
+
709
+ - Project was created without milestones and has grown to 10+ issues
710
+ - Milestones were set up early but no longer match the actual work groupings
711
+ - A project pivot changed priorities and the old phases don't apply
712
+ - Too many issues are in "(No milestone)" and need proper grouping
713
+ - Milestones are too broad (30+ issues each) or too granular (1-2 issues each)
714
+
715
+ ### Refresh Workflow
716
+
717
+ **Step 1: Audit the current state**
718
+
719
+ ```bash
720
+ # Get the project ID
721
+ node linear.mjs list-projects
722
+
723
+ # See current milestones
724
+ node linear.mjs list-milestones "$PROJECT_ID"
725
+
726
+ # See all issues grouped by milestone (includes unmilestoned)
727
+ node linear.mjs list-project-issues "$PROJECT_ID" --limit 200
728
+
729
+ # Get raw JSON for scripting (includes issue UUIDs and milestone UUIDs)
730
+ node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
731
+ ```
732
+
733
+ Review:
734
+ - How many issues per milestone? (ideal: 5–15)
735
+ - Are milestones thematically coherent?
736
+ - Are there many unmilestoned issues?
737
+ - Do completed milestones still have open issues?
738
+ - Are milestone names clear and descriptive?
739
+
740
+ **Step 2: Propose new milestone structure**
741
+
742
+ Present the proposed changes to the user before making any modifications:
743
+ - Which milestones to **keep** (unchanged)
744
+ - Which milestones to **rename** (same issues, better name) — **never rename milestones with target dates**
745
+ - Which milestones to **merge** (combine two sparse milestones)
746
+ - Which milestones to **split** (break an overloaded milestone)
747
+ - Which milestones to **create** (for unmilestoned issues or new groupings)
748
+ - Which milestones to **delete** (empty after reshuffling) — **never delete milestones with target dates**
749
+ - For each issue, which milestone it should end up in
750
+
751
+ **Present this as a before/after table so the user can approve.**
752
+
753
+ **Step 3: Execute the changes (after user approval)**
754
+
755
+ Order of operations matters — follow this sequence:
756
+
757
+ 1. **Create new milestones** (need their IDs before moving issues)
758
+ ```bash
759
+ node linear.mjs create-milestone "$PROJECT_ID" "New Milestone Name" --target-date "2025-06-01"
760
+ ```
761
+
762
+ 2. **Rename existing milestones** (safe, doesn't affect issues)
763
+ ```bash
764
+ node linear.mjs update-milestone "$MILESTONE_ID" --name "Better Name"
765
+ ```
766
+
767
+ 3. **Move issues to their new milestones**
768
+ ```bash
769
+ # One at a time
770
+ node linear.mjs set-issue-milestone "$ISSUE_ID" "$NEW_MILESTONE_ID"
771
+
772
+ # Or batch move
773
+ node linear.mjs batch-move-to-milestone "$NEW_MILESTONE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
774
+ ```
775
+
776
+ 4. **Delete empty milestones** (only after all issues are moved out)
777
+ ```bash
778
+ node linear.mjs delete-milestone "$EMPTY_MILESTONE_ID"
779
+ ```
780
+
781
+ 5. **Verify the result**
782
+ ```bash
783
+ node linear.mjs list-project-issues "$PROJECT_ID" --limit 200
784
+ ```
785
+
786
+ **Step 4: Update the initiative in Linear**
787
+
788
+ After restructuring, update the initiative's `content` field in Linear to reflect the new milestone structure.
789
+
790
+ ### Safety Rules
791
+
792
+ - **No issue loss.** Every issue that existed before the refresh must exist after. Verify issue count before and after.
793
+ - **No status changes.** Don't change any issue's status, priority, assignee, labels, or estimate during a refresh. Only the milestone assignment changes.
794
+ - **No issue deletion.** Never delete or cancel issues as part of a refresh.
795
+ - **Delete milestones last.** Only delete a milestone after confirming it has zero issues.
796
+ - **Never delete or rename a dated milestone.** Milestones with target dates represent intentional commitments — they must stay intact (name and date unchanged). You may move issues out of them, but the milestone itself must not be deleted or renamed.
797
+ - **User approval required.** Always present the proposed restructuring plan and get explicit approval before executing any changes.
798
+
799
+ ### CLI Reference (Milestone Operations)
800
+
801
+ ```bash
802
+ # Create a milestone
803
+ node linear.mjs create-milestone "$PROJECT_ID" "Milestone Name" [--target-date YYYY-MM-DD]
804
+
805
+ # Rename / update a milestone
806
+ node linear.mjs update-milestone "$MILESTONE_ID" --name "New Name"
807
+ node linear.mjs update-milestone "$MILESTONE_ID" --target-date "2025-07-01"
808
+ node linear.mjs update-milestone "$MILESTONE_ID" --sort-order 5
809
+
810
+ # Delete a milestone (must be empty!)
811
+ node linear.mjs delete-milestone "$MILESTONE_ID"
812
+
813
+ # Move a single issue to a milestone
814
+ node linear.mjs set-issue-milestone "$ISSUE_ID" "$MILESTONE_ID"
815
+
816
+ # Remove issue from its milestone (set to unmilestoned)
817
+ node linear.mjs set-issue-milestone "$ISSUE_ID" none
818
+
819
+ # Batch move issues to a milestone
820
+ node linear.mjs batch-move-to-milestone "$MILESTONE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
821
+
822
+ # Get raw JSON with issue/milestone UUIDs (for scripting)
823
+ node linear.mjs list-project-issues "$PROJECT_ID" --limit 200 --json
824
+ ```
825
+
826
+ ---
827
+
828
+ ## Cycle Rebalance — Redistributing Issues Across Cycles
829
+
830
+ The **cycle rebalance** workflow redistributes issues so that cycles are filled **front-to-back**: the current cycle should be at **105% capacity**, overflow spills into the next cycle (also up to 105%), and so on. This applies in **both directions** — issues move later when a cycle is overloaded, and issues pull forward from later cycles when the current cycle has room.
831
+
832
+ **Capacity** is calculated using `cycle-capacity`: total estimate points in the cycle / average completed estimate points from the last 3 completed cycles (velocity).
833
+
834
+ ### When to Rebalance
835
+
836
+ - After a cycle ends with incomplete issues that rolled into the next cycle
837
+ - When a cycle is over or under capacity
838
+ - When the user says "rebalance cycles", "redistribute issues", or similar
839
+ - During sprint planning when upcoming cycles look uneven
840
+
841
+ ### Rebalance Workflow
842
+
843
+ **Step 1: Audit current cycle state**
844
+
845
+ ```bash
846
+ # Check velocity-based capacity for all cycles
847
+ node linear.mjs cycle-capacity
848
+
849
+ # Overview: all active/upcoming cycles with issues grouped by project
850
+ node linear.mjs rebalance
851
+
852
+ # Raw data for analysis
853
+ node linear.mjs rebalance --json
854
+ ```
855
+
856
+ Collect this data and analyze:
857
+ - **Velocity:** From `cycle-capacity` output (avg completed pts from last 3 cycles)
858
+ - **Capacity per cycle:** Each cycle's estimate points as a % of velocity
859
+ - **Target per cycle:** 105% of velocity (e.g., if velocity = 91, target = ~96 pts)
860
+ - **Which cycles are under 105%:** These need issues pulled forward from later cycles
861
+ - **Which cycles are over 105%:** These need issues pushed to later cycles
862
+
863
+ **Step 2: Analyze and plan the redistribution**
864
+
865
+ The goal is to **fill cycles front-to-back to 105%**:
866
+
867
+ 1. Start with the **current (active) cycle**. Calculate its capacity.
868
+ 2. If **under 105%** → pull movable issues forward from the next cycle(s) until at 105% (or no more movable issues exist).
869
+ 3. If **over 105%** → push lowest-priority movable issues to the next cycle until at 105%.
870
+ 4. Move to the **next cycle** and repeat.
871
+ 5. Continue until all cycles are processed. The last cycle absorbs whatever remains.
872
+
873
+ **When pulling issues forward**, prefer (in order):
874
+ 1. **Urgent/High priority** issues first — get important work done sooner
875
+ 2. Issues whose **dependencies are already satisfied** (blocker is Done or in an earlier/same cycle)
876
+ 3. Issues from **underrepresented clients** in the target cycle (balance client mix)
877
+ 4. Issues in the **same milestone** as other issues already in the target cycle
878
+
879
+ **When pushing issues later**, prefer (in order):
880
+ 1. **NEVER move High (2) or Urgent (1) priority issues to a later cycle** — they are time-sensitive and must stay in their current cycle or move earlier
881
+ 2. **NEVER move issues with a due date** — due dates represent commitments; these issues are pinned to their current cycle (or can move earlier, never later)
882
+ 3. **Low (4)** priority issues first — least impactful to delay
883
+ 4. **Medium (3)** priority next
884
+ 5. Issues with **no downstream dependents** (nothing blocked by them)
885
+ 6. Issues from **overrepresented clients** in the current cycle
886
+
887
+ Apply these heuristics throughout:
888
+
889
+ #### Heuristic 1: Respect status — never move active work
890
+ - **Never move** issues that are `In Progress` or `In Review` — they stay in their current cycle
891
+ - **Todo** issues are movable (both forward and backward)
892
+ - **Backlog** issues in a cycle are movable (but should be set to Todo after moving)
893
+
894
+ #### Heuristic 2: Respect dependencies
895
+ - **Hard rule: a blocking issue must NEVER be in a later cycle than the issue it blocks.** If issue A blocks issue B, A must be in the same cycle as B or an earlier one. This is inviolable — never move a blocker to a later cycle than its dependent.
896
+ - Check dependencies with `node linear.mjs list-dependencies "$ISSUE_ID"` for any issue you plan to move
897
+ - When pulling an issue forward, also pull forward any of its blockers that are in a later cycle (or leave both)
898
+ - When pushing an issue later, ensure none of the issues it blocks are in the current or an earlier cycle — if they are, you cannot push this issue. Either push the dependent issues too, or leave the blocker in place.
899
+
900
+ #### Heuristic 3: Balance client work per cycle
901
+ - Each cycle should have a **roughly proportional mix** of client work — avoid "all Globex" or "all Northwind" cycles
902
+ - When choosing which issues to pull forward or push later, use client balance as a tiebreaker
903
+ - This ensures progress across all clients every sprint
904
+
905
+ #### Heuristic 4: Keep milestones together
906
+ - Issues in the same milestone should stay in the same cycle when possible — they're often sequentially dependent even if not formally linked
907
+ - If a milestone spans cycles, keep the split clean: don't scatter milestone issues across 3+ cycles
908
+ - When pulling forward, prefer pulling entire milestone groups together
909
+
910
+ #### Heuristic 5: Balance assignee load
911
+ - Each cycle should have a reasonable split between the Frontend/PM lead and the Backend lead
912
+ - Don't create a cycle where one person has 80% of the work and the other has 20%
913
+ - Consider that backend issues (Backend lead) often block frontend issues (Frontend/PM lead) — schedule accordingly
914
+
915
+ #### Heuristic 6: Estimate-aware balancing
916
+ - Use estimate points (not just issue count) for capacity calculations via `cycle-capacity`
917
+ - A cycle with 3 XL issues is heavier than one with 8 S issues
918
+ - Unestimated issues don't count toward capacity — note this when presenting the plan
919
+
920
+ **Step 3: Present the rebalance plan**
921
+
922
+ Present a clear before/after comparison:
923
+
924
+ ```
925
+ VELOCITY: 91 pts (avg from C1=96, C2=80, C3=96)
926
+ TARGET PER CYCLE: ~96 pts (105%)
927
+
928
+ BEFORE:
929
+ Cycle 4 (active): 67 pts — 74% capacity
930
+ Cycle 5: 62 pts — 68% capacity
931
+ Cycle 6: 65 pts — 72% capacity
932
+
933
+ AFTER:
934
+ Cycle 4: 96 pts — 105% capacity (pulled 29 pts forward from C5)
935
+ Cycle 5: 96 pts — 105% capacity (lost 29 to C4, pulled 63 from C6)
936
+ Cycle 6: 2 pts — 2% capacity (pushed 63 to C5)
937
+
938
+ MOVES:
939
+ ← ACME-342 "Build customer profiles list page" (est 3, Northwind) → Cycle 5 → Cycle 4
940
+ ← ACME-441 "Show dependency indicators" (est 3, Globex) → Cycle 5 → Cycle 4
941
+ → ACME-278 "Display audit log" (est 3, Globex) → Cycle 6 → Cycle 5
942
+ ...
943
+ ```
944
+
945
+ Include:
946
+ - Direction arrow: `←` for pulling forward, `→` for pushing later
947
+ - Which issues move, with identifier, title, priority, estimate, and project
948
+ - Why each issue was chosen to move
949
+ - Client distribution per cycle (before and after)
950
+ - Assignee balance per cycle (before and after)
951
+ - Any issues you considered moving but kept in place, and why
952
+
953
+ **Get explicit user approval before executing.**
954
+
955
+ **Step 4: Execute the moves (after approval)**
956
+
957
+ > **Important: Rate limiting.** The Linear API silently drops rapid-fire mutations. The `batch-move-to-cycle` command already inserts a 0.5s delay between calls and validates each response (reporting `success`/`fail` counts). For large rebalances (50+ moves), still process in groups of ~9 and verify between groups, since responses may report `success: true` while the mutation is silently discarded.
958
+
959
+ ```bash
960
+ # Use the built-in batch command (includes delays and error reporting):
961
+ node linear.mjs batch-move-to-cycle "$TARGET_CYCLE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
962
+
963
+ # "current" resolves to the active cycle:
964
+ node linear.mjs batch-move-to-cycle current "$ISSUE_1" "$ISSUE_2"
965
+
966
+ # For one-off moves:
967
+ node linear.mjs move-issue-to-cycle "$ISSUE_ID" "$CYCLE_ID"
968
+ ```
969
+
970
+ **Step 5: Verify the result**
971
+
972
+ ```bash
973
+ # Confirm the new capacity distribution
974
+ node linear.mjs cycle-capacity
975
+
976
+ # Confirm issue-level details
977
+ node linear.mjs rebalance
978
+ ```
979
+
980
+ Review the output and confirm:
981
+ - Current cycle is at or near 105% (or as close as possible given movable issues)
982
+ - Each subsequent cycle is filled to 105% before spilling to the next
983
+ - No In Progress/In Review issues were moved
984
+ - Dependencies are still satisfied (blockers before blocked)
985
+ - Client mix is balanced across cycles
986
+
987
+ **Step 6: Update initiative in Linear if needed**
988
+
989
+ After rebalancing, update the initiative's content in Linear if cycle assignments or progress notes are tracked there.
990
+
991
+ ### Safety Rules
992
+
993
+ - **No status changes.** Only the cycle assignment changes — never touch status, priority, assignee, labels, estimate, milestone, or project.
994
+ - **No issue deletion.** Never delete or cancel issues during a rebalance.
995
+ - **Don't move active work.** Issues in `In Progress` or `In Review` are untouchable.
996
+ - **Never push High or Urgent issues later.** High (priority 2) and Urgent (priority 1) issues must never be moved to a farther-out cycle — they are time-sensitive by definition. They can only stay put or be pulled forward.
997
+ - **Never push due-dated issues later.** Issues with a due date are pinned to their current cycle (or earlier). Due dates represent commitments — never move these to a farther-out cycle.
998
+ - **Respect dependencies.** A blocking issue must NEVER end up in a later cycle than the issue it blocks. Before moving any issue, check its dependencies — if it blocks something in cycle N, it cannot move to cycle N+1 or later.
999
+ - **User approval required.** Always present the full rebalance plan and get explicit approval before executing any moves.
1000
+ - **105% target is a soft cap.** It's okay if a cycle lands at 103% or 107% because the next movable issue would overshoot. The goal is "each cycle as close to 105% as possible, filled front-to-back," not mathematical perfection.
1001
+
1002
+ ### CLI Reference (Cycle Rebalance Operations)
1003
+
1004
+ ```bash
1005
+ # Full overview of cycles with issues grouped by project
1006
+ node linear.mjs rebalance
1007
+
1008
+ # Raw JSON for all active/upcoming cycles with incomplete issues
1009
+ node linear.mjs rebalance --json
1010
+ # (or: node linear.mjs list-cycle-issues-all)
1011
+
1012
+ # Raw JSON for a specific cycle's incomplete issues
1013
+ node linear.mjs list-cycle-issues-by-id "$CYCLE_ID"
1014
+
1015
+ # Move a single issue to a different cycle
1016
+ node linear.mjs move-issue-to-cycle "$ISSUE_ID" "$CYCLE_ID"
1017
+
1018
+ # Batch move multiple issues to a cycle (includes 0.5s delay between calls)
1019
+ # Reports success/fail counts. Keep batches ≤9 for reliability.
1020
+ node linear.mjs batch-move-to-cycle "$CYCLE_ID" "$ISSUE_1" "$ISSUE_2" "$ISSUE_3"
1021
+
1022
+ # Check dependencies before moving
1023
+ node linear.mjs list-dependencies "$ISSUE_ID"
1024
+
1025
+ # Verify after rebalancing
1026
+ node linear.mjs rebalance
1027
+ ```
1028
+
1029
+ > **Rate limit note:** Linear's API can silently discard rapid mutations. The batch function includes a 0.5s delay between calls and validates each response. For large rebalances (50+ moves), process in groups of ~9 and verify between groups.
1030
+
1031
+ ---
1032
+
1033
+ ## Tips for a 2-Person Team
1034
+
1035
+ 1. **Keep issues small.** If it's XL, break it down. Small issues keep momentum and make reviews easier.
1036
+ 2. **Daily async check-in.** A quick message about what you're working on and if you're blocked.
1037
+ 3. **Use "In Review" status.** It signals to the other person that something needs their eyes.
1038
+ 4. **Don't overcommit cycles.** Leave ~20% buffer for bugs, client requests, and interruptions.
1039
+ 5. **Projects identify the client.** No need for client prefixes in issue titles or client labels — the project name (e.g., `[GBX] Portal`) already provides that context.
1040
+ 6. **Triage first.** New client requests go to Backlog, not straight into the sprint — unless truly urgent.