chief-clancy 0.8.22 → 0.9.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 (94) hide show
  1. package/bin/clancy.js +153 -0
  2. package/package.json +8 -88
  3. package/README.md +0 -292
  4. package/dist/bundle/clancy-afk.js +0 -6
  5. package/dist/bundle/clancy-once.js +0 -239
  6. package/dist/installer/file-ops/file-ops.d.ts +0 -32
  7. package/dist/installer/file-ops/file-ops.d.ts.map +0 -1
  8. package/dist/installer/file-ops/file-ops.js +0 -58
  9. package/dist/installer/file-ops/file-ops.js.map +0 -1
  10. package/dist/installer/hook-installer/hook-installer.d.ts +0 -31
  11. package/dist/installer/hook-installer/hook-installer.d.ts.map +0 -1
  12. package/dist/installer/hook-installer/hook-installer.js +0 -137
  13. package/dist/installer/hook-installer/hook-installer.js.map +0 -1
  14. package/dist/installer/install.d.ts +0 -3
  15. package/dist/installer/install.d.ts.map +0 -1
  16. package/dist/installer/install.js +0 -270
  17. package/dist/installer/install.js.map +0 -1
  18. package/dist/installer/manifest/manifest.d.ts +0 -41
  19. package/dist/installer/manifest/manifest.d.ts.map +0 -1
  20. package/dist/installer/manifest/manifest.js +0 -97
  21. package/dist/installer/manifest/manifest.js.map +0 -1
  22. package/dist/installer/prompts/prompts.d.ts +0 -33
  23. package/dist/installer/prompts/prompts.d.ts.map +0 -1
  24. package/dist/installer/prompts/prompts.js +0 -55
  25. package/dist/installer/prompts/prompts.js.map +0 -1
  26. package/dist/installer/role-filter/role-filter.d.ts +0 -15
  27. package/dist/installer/role-filter/role-filter.d.ts.map +0 -1
  28. package/dist/installer/role-filter/role-filter.js +0 -48
  29. package/dist/installer/role-filter/role-filter.js.map +0 -1
  30. package/dist/installer/ui/ui.d.ts +0 -9
  31. package/dist/installer/ui/ui.d.ts.map +0 -1
  32. package/dist/installer/ui/ui.js +0 -94
  33. package/dist/installer/ui/ui.js.map +0 -1
  34. package/dist/scripts/shared/env-parser/env-parser.d.ts +0 -30
  35. package/dist/scripts/shared/env-parser/env-parser.d.ts.map +0 -1
  36. package/dist/scripts/shared/env-parser/env-parser.js +0 -64
  37. package/dist/scripts/shared/env-parser/env-parser.js.map +0 -1
  38. package/dist/utils/ansi/ansi.d.ts +0 -55
  39. package/dist/utils/ansi/ansi.d.ts.map +0 -1
  40. package/dist/utils/ansi/ansi.js +0 -55
  41. package/dist/utils/ansi/ansi.js.map +0 -1
  42. package/hooks/clancy-branch-guard.js +0 -128
  43. package/hooks/clancy-check-update.js +0 -114
  44. package/hooks/clancy-context-monitor.js +0 -189
  45. package/hooks/clancy-credential-guard.js +0 -120
  46. package/hooks/clancy-drift-detector.js +0 -96
  47. package/hooks/clancy-notification.js +0 -105
  48. package/hooks/clancy-post-compact.js +0 -53
  49. package/hooks/clancy-statusline.js +0 -82
  50. package/hooks/package.json +0 -3
  51. package/registry/boards.json +0 -44
  52. package/src/agents/arch-agent.md +0 -72
  53. package/src/agents/concerns-agent.md +0 -89
  54. package/src/agents/design-agent.md +0 -130
  55. package/src/agents/devils-advocate.md +0 -53
  56. package/src/agents/quality-agent.md +0 -161
  57. package/src/agents/tech-agent.md +0 -92
  58. package/src/agents/verification-gate.md +0 -128
  59. package/src/roles/implementer/commands/dry-run.md +0 -14
  60. package/src/roles/implementer/commands/once.md +0 -17
  61. package/src/roles/implementer/commands/run.md +0 -11
  62. package/src/roles/implementer/workflows/once.md +0 -146
  63. package/src/roles/implementer/workflows/run.md +0 -127
  64. package/src/roles/planner/commands/approve-plan.md +0 -10
  65. package/src/roles/planner/commands/plan.md +0 -20
  66. package/src/roles/planner/workflows/approve-plan.md +0 -535
  67. package/src/roles/planner/workflows/plan.md +0 -536
  68. package/src/roles/reviewer/commands/logs.md +0 -7
  69. package/src/roles/reviewer/commands/review.md +0 -9
  70. package/src/roles/reviewer/commands/status.md +0 -9
  71. package/src/roles/reviewer/workflows/logs.md +0 -104
  72. package/src/roles/reviewer/workflows/review.md +0 -186
  73. package/src/roles/reviewer/workflows/status.md +0 -134
  74. package/src/roles/setup/commands/doctor.md +0 -7
  75. package/src/roles/setup/commands/help.md +0 -80
  76. package/src/roles/setup/commands/init.md +0 -7
  77. package/src/roles/setup/commands/map-codebase.md +0 -16
  78. package/src/roles/setup/commands/settings.md +0 -7
  79. package/src/roles/setup/commands/uninstall.md +0 -5
  80. package/src/roles/setup/commands/update-docs.md +0 -9
  81. package/src/roles/setup/commands/update.md +0 -12
  82. package/src/roles/setup/workflows/doctor.md +0 -124
  83. package/src/roles/setup/workflows/init.md +0 -1073
  84. package/src/roles/setup/workflows/map-codebase.md +0 -125
  85. package/src/roles/setup/workflows/scaffold.md +0 -845
  86. package/src/roles/setup/workflows/settings.md +0 -944
  87. package/src/roles/setup/workflows/uninstall.md +0 -161
  88. package/src/roles/setup/workflows/update-docs.md +0 -92
  89. package/src/roles/setup/workflows/update.md +0 -277
  90. package/src/roles/strategist/commands/approve-brief.md +0 -21
  91. package/src/roles/strategist/commands/brief.md +0 -27
  92. package/src/roles/strategist/workflows/approve-brief.md +0 -834
  93. package/src/roles/strategist/workflows/brief.md +0 -890
  94. package/src/templates/CLAUDE.md +0 -87
@@ -1,834 +0,0 @@
1
- # Clancy Approve Brief Workflow
2
-
3
- ## Overview
4
-
5
- Approve a reviewed strategic brief by creating child tickets on the board, linking dependencies, and posting a tracking summary on the parent ticket. Tickets are created sequentially in topological (dependency) order. Partial failures stop immediately — re-run to resume.
6
-
7
- ---
8
-
9
- ## Step 1 — Preflight checks
10
-
11
- 1. Check `.clancy/` exists and `.clancy/.env` is present. If not:
12
- ```
13
- .clancy/ not found. Run /clancy:init to set up Clancy first.
14
- ```
15
- Stop.
16
-
17
- 2. Source `.clancy/.env` and check board credentials are present.
18
-
19
- 3. Check `CLANCY_ROLES` includes `strategist` (or env var is unset, which indicates a global install where all roles are available). If `CLANCY_ROLES` is set but does not include `strategist`:
20
- ```
21
- The Strategist role is not enabled. Add "strategist" to CLANCY_ROLES in .clancy/.env or run /clancy:settings.
22
- ```
23
- Stop.
24
-
25
- 4. Branch freshness check:
26
- ```bash
27
- git fetch origin
28
- ```
29
- Compare HEAD with `origin/$CLANCY_BASE_BRANCH`. If behind:
30
-
31
- **AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): auto-pull without prompting.
32
-
33
- **Interactive mode:**
34
- ```
35
- Behind by N commits. [1] Pull latest [2] Continue [3] Abort
36
- ```
37
-
38
- ---
39
-
40
- ## Step 2 — Load brief
41
-
42
- Scan `.clancy/briefs/` for unapproved files — `.md` files WITHOUT a corresponding `.md.approved` marker file.
43
-
44
- ### Selection logic
45
-
46
- Parse the argument (if any) against the unapproved list:
47
-
48
- **No argument + 0 unapproved briefs:**
49
- ```
50
- No unapproved briefs found. Run /clancy:brief to generate one.
51
- ```
52
- Stop.
53
-
54
- **No argument + 1 unapproved brief:**
55
- Auto-select the single brief. Continue.
56
-
57
- **No argument + 2+ unapproved briefs:**
58
- Display numbered list:
59
- ```
60
- Multiple unapproved briefs found:
61
-
62
- [1] {slug-a} ({date}) — {N} tickets — Source: {source}
63
- [2] {slug-b} ({date}) — {N} tickets — Source: {source}
64
-
65
- Which brief to approve? [1-N]
66
- ```
67
-
68
- **Argument is a positive integer (e.g. `2`):**
69
- Select the Nth unapproved brief by index. If out of range: `Index out of range.` Stop.
70
-
71
- **Argument matches a ticket identifier (`#\d+`, `[A-Z]+-\d+`):**
72
- Scan unapproved brief files for a `**Source:**` line containing the identifier. If 0 matches: show available briefs and stop. If 1 match: load it. If 2+ matches: show numbered list and ask.
73
-
74
- **Argument is other text:**
75
- Match by slug (filename contains the argument). If 0 matches: `No brief matching "{arg}" found.` Stop. If 1 match: load it. If 2+ matches: list and ask.
76
-
77
- ### Already-approved guard
78
-
79
- After loading, verify the `.md.approved` marker does not exist. If it does:
80
- ```
81
- Brief "{slug}" is already approved. No action needed.
82
- ```
83
- Stop.
84
-
85
- ---
86
-
87
- ## Step 3 — Parse decomposition table
88
-
89
- Read the `## Ticket Decomposition` table from the brief file. Extract each row:
90
-
91
- | Column | Field |
92
- |--------|-------|
93
- | `#` | Sequence number |
94
- | `Title` | Ticket title |
95
- | `Description` | 1-2 sentence description |
96
- | `Size` | S / M / L |
97
- | `Dependencies` | References like `#1`, `#3` |
98
- | `Mode` | AFK or HITL |
99
- | `Ticket` | Board key (if partially created) |
100
-
101
- ### Validation
102
-
103
- - **0 tickets:** `No tickets found in the decomposition table. Add at least one ticket to the brief before approving.` Stop.
104
- - **>10 tickets:** Warn: `Brief has {N} tickets (max recommended: 10). Large decompositions may indicate the idea should be split further.` Continue — advisory only.
105
- - **Tickets already in Ticket column:** These were created in a prior partial run. Mark them for skipping during creation.
106
- - **Circular dependencies:** Run cycle detection on the dependency graph. If a cycle exists: `Circular dependency detected between #{N} and #{M}. Edit the brief to resolve, then re-run.` Stop.
107
-
108
- ### Topological sort
109
-
110
- Order tickets by their dependency graph so blockers are created before dependents. This ensures blocking relationships can be linked immediately after creation.
111
-
112
- ---
113
-
114
- ## Step 4 — Confirm with user
115
-
116
- Display the ticket list in dependency order:
117
-
118
- ```
119
- Clancy — Approve Brief
120
-
121
- Brief: {slug}
122
- Parent: {ticket key or "none (standalone)"}
123
-
124
- Tickets to create (dependency order):
125
- #1 [S] [AFK] Title — No deps
126
- #2 [M] [HITL] Title — No deps
127
- #3 [M] [AFK] Title — After #2
128
- #4 [S] [AFK] Title — After #1, #3
129
-
130
- Labels: {list of labels to apply}
131
- Issue type: {type} (Jira only)
132
- AFK-ready: {X} | Needs human: {Y}
133
-
134
- Create {N} tickets? [Y/n]
135
- ```
136
-
137
- **AFK mode:** If running in AFK mode (`--afk` flag OR `CLANCY_MODE=afk`), skip the confirmation prompt and auto-confirm. Display the ticket list for logging purposes but proceed without waiting for input.
138
-
139
- If user declines (interactive only): `Cancelled. No changes made.` Stop.
140
-
141
- ---
142
-
143
- ## Step 4a — Dry-run check
144
-
145
- If `--dry-run` flag is set:
146
- ```
147
- Dry run complete. No tickets created.
148
- ```
149
- Stop. No API calls beyond preflight.
150
-
151
- ---
152
-
153
- ## Step 5 — Resolve parent
154
-
155
- The parent ticket is where child tickets are linked. Determine the parent from one of these sources (in priority order):
156
-
157
- ### Source 1 — Board-sourced brief
158
-
159
- If the brief's `**Source:**` line contains a board ticket identifier (e.g. `[#50]`, `[PROJ-200]`, `[ENG-42]`), that ticket is the parent. Fetch it to validate:
160
-
161
- #### GitHub
162
- ```bash
163
- curl -s \
164
- -H "Authorization: Bearer $GITHUB_TOKEN" \
165
- -H "Accept: application/vnd.github+json" \
166
- -H "X-GitHub-Api-Version: 2022-11-28" \
167
- "https://api.github.com/repos/$GITHUB_REPO/issues/$PARENT_NUMBER"
168
- ```
169
-
170
- Check: `pull_request` present? -> `Parent #{N} is a PR, not an issue.` Stop. `state == "closed"`? -> Warn, ask `[y/N]`.
171
-
172
- #### Jira
173
- ```bash
174
- curl -s \
175
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
176
- -H "Accept: application/json" \
177
- "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY?fields=summary,status,issuetype,priority"
178
- ```
179
-
180
- Check: `status.statusCategory.key == "done"`? -> Warn, ask `[y/N]`. Note if `issuetype.name == "Epic"` (ideal case for parent linking).
181
-
182
- #### Linear
183
- ```graphql
184
- query {
185
- issues(filter: { identifier: { eq: "$PARENT_KEY" } }) {
186
- nodes {
187
- id identifier title
188
- state { type }
189
- team { id key }
190
- children { nodes { id identifier title } }
191
- }
192
- }
193
- }
194
- ```
195
-
196
- Check: `state.type == "completed"` or `"canceled"`? -> Warn, ask `[y/N]`. `team.id != LINEAR_TEAM_ID`? -> Warn about cross-team children, ask `[Y/n]`.
197
-
198
- ### Source 2 — `--epic` flag
199
-
200
- If `--epic` is provided (e.g. `--epic PROJ-200`, `--epic #100`, `--epic ENG-42`), validate the target using the same checks as Source 1. If not found or is Done: stop.
201
-
202
- Note: `--epic` is ignored for board-sourced briefs (the source ticket IS the parent).
203
-
204
- ### Source 3 — `CLANCY_BRIEF_EPIC` default
205
-
206
- If no board source and no `--epic` flag, check `.clancy/.env` for `CLANCY_BRIEF_EPIC`. If set, validate and use it as the parent.
207
-
208
- ### Source 4 — No parent (standalone)
209
-
210
- If none of the above apply:
211
- ```
212
- No parent specified. Tickets will be created as standalone.
213
- Use --epic {KEY} to attach to a parent.
214
- ```
215
- Continue — omit parent fields from creation payloads. No tracking comment will be posted.
216
-
217
- ---
218
-
219
- ## Step 6 — Look up queue state / labels
220
-
221
- Platform-specific pre-creation lookups.
222
-
223
- ### GitHub
224
-
225
- **Pipeline label for children — this is mandatory, always apply a pipeline label to every child ticket.** Determine which label to use:
226
- - `--skip-plan` flag → use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`
227
- - Planner role enabled (`CLANCY_ROLES` includes `planner`) → use `CLANCY_LABEL_PLAN` from `.clancy/.env` if set, otherwise `clancy:plan`
228
- - Planner role NOT enabled → use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`
229
- Ensure the label exists on the board (create it if missing), then add it to each child ticket.
230
-
231
- **Labels to apply per ticket:**
232
- - The pipeline label determined above (`CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD`) — replaces `CLANCY_LABEL` on children
233
- - `component:{CLANCY_COMPONENT}` (if `CLANCY_COMPONENT` set)
234
- - `size:{S|M|L}` — from decomposition table
235
- - `clancy:afk` or `clancy:hitl` — from Mode column
236
-
237
- Note: `CLANCY_LABEL` is NOT applied to child tickets when pipeline labels are active. The pipeline label (`clancy:plan` or `clancy:build`) serves as the queue identifier.
238
-
239
- **Label pre-creation:** For each unique label, attempt to create it on the repo. If it already exists, GitHub returns 422 — ignore that error. If 403 (no admin access), note the label as unavailable.
240
-
241
- ```bash
242
- curl -s \
243
- -H "Authorization: Bearer $GITHUB_TOKEN" \
244
- -H "Accept: application/vnd.github+json" \
245
- -H "X-GitHub-Api-Version: 2022-11-28" \
246
- -H "Content-Type: application/json" \
247
- -X POST \
248
- "https://api.github.com/repos/$GITHUB_REPO/labels" \
249
- -d '{"name": "$LABEL_NAME", "color": "d4c5f9"}'
250
- ```
251
-
252
- If label creation fails with 403: warn, continue without that label.
253
-
254
- **Resolve username** for assignee:
255
- ```bash
256
- curl -s \
257
- -H "Authorization: Bearer $GITHUB_TOKEN" \
258
- -H "Accept: application/vnd.github+json" \
259
- "https://api.github.com/user"
260
- ```
261
- Cache the `login` field for all ticket creations.
262
-
263
- ### Jira
264
-
265
- **Validate issue type exists in project:**
266
- ```bash
267
- curl -s \
268
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
269
- -H "Accept: application/json" \
270
- "$JIRA_BASE_URL/rest/api/3/issue/createmeta/$JIRA_PROJECT_KEY/issuetypes"
271
- ```
272
-
273
- Look for `CLANCY_BRIEF_ISSUE_TYPE` (default: `Task`). If not found:
274
- ```
275
- Issue type "{type}" not available in project {PROJ}. Available types: {list}.
276
- Set CLANCY_BRIEF_ISSUE_TYPE in .clancy/.env.
277
- ```
278
- Stop.
279
-
280
- **Pipeline label for children — mandatory, same logic as GitHub.** Use `CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD` from `.clancy/.env` (defaults: `clancy:plan` / `clancy:build`). Always apply to every child ticket.
281
-
282
- **Labels:** Jira auto-creates labels — no pre-creation needed. Apply: the pipeline label determined above, `clancy:afk` or `clancy:hitl`. `CLANCY_LABEL` is NOT applied to children when pipeline labels are active.
283
-
284
- **Components:** If `CLANCY_COMPONENT` is set, it maps to the Jira `components` field.
285
-
286
- **Priority:** Inherit from parent ticket if available. Otherwise omit (Jira uses project default).
287
-
288
- ### Linear
289
-
290
- **Look up backlog state UUID:**
291
- ```graphql
292
- query {
293
- workflowStates(filter: {
294
- team: { id: { eq: "$LINEAR_TEAM_ID" } }
295
- type: { eq: "backlog" }
296
- }) {
297
- nodes { id name }
298
- }
299
- }
300
- ```
301
-
302
- Use `nodes[0].id`. If empty, fall back to `triage` type, then `unstarted`, then any first state. If truly nothing found, warn and use the team default.
303
-
304
- **Look up label UUIDs:**
305
- ```graphql
306
- query {
307
- team(id: "$LINEAR_TEAM_ID") {
308
- labels { nodes { id name } }
309
- }
310
- }
311
- ```
312
-
313
- **Pipeline label for children — mandatory, same logic as GitHub/Jira.** Use `CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD` from `.clancy/.env` (defaults: `clancy:plan` / `clancy:build`). Always apply to every child ticket.
314
-
315
- For each required label (the pipeline label, `component:{CLANCY_COMPONENT}`, `clancy:afk`, `clancy:hitl`): search by exact name. `CLANCY_LABEL` is NOT applied to children when pipeline labels are active. If not found in team labels, check workspace labels:
316
-
317
- ```graphql
318
- query {
319
- issueLabels(filter: { name: { eq: "$LABEL_NAME" } }) {
320
- nodes { id name }
321
- }
322
- }
323
- ```
324
-
325
- If still not found, auto-create at team level:
326
- ```graphql
327
- mutation {
328
- issueLabelCreate(input: { teamId: "$LINEAR_TEAM_ID", name: "$LABEL_NAME" }) {
329
- success
330
- issueLabel { id name }
331
- }
332
- }
333
- ```
334
-
335
- On failure to create label: warn, continue without it.
336
-
337
- ---
338
-
339
- ## Step 6a — Pre-creation race check
340
-
341
- Create the `.approved` marker file with exclusive create (O_EXCL) to prevent concurrent approval:
342
-
343
- ```
344
- Marker path: .clancy/briefs/{filename}.approved
345
- ```
346
-
347
- If the file already exists (`EEXIST`):
348
- ```
349
- Brief is being approved by another process.
350
- ```
351
- Stop.
352
-
353
- If creation succeeds: the marker acts as a lock. **If ticket creation fails later, delete this marker** (partial failure = not approved).
354
-
355
- ---
356
-
357
- ## Step 7 — Create child tickets
358
-
359
- Create tickets **sequentially** in topological (dependency) order. Wait **500ms** between each creation to respect rate limits.
360
-
361
- For each ticket in dependency order:
362
-
363
- 1. **Check if already created:** If the Ticket column has a value from a prior partial run, skip this ticket. Display: `[{i}/{N}] skip {KEY} — already exists`
364
-
365
- 2. **Build the creation payload** (platform-specific — see below).
366
-
367
- 3. **Send the API request.**
368
-
369
- 4. **On success:** Record the created key/number. Map the decomposition `#N` to the board key for dependency linking. Display: `[{i}/{N}] + {KEY} — {Title} [{Mode}]`
370
-
371
- 5. **On failure:** STOP immediately. Do NOT attempt remaining tickets. See partial failure handling in Step 10.
372
-
373
- 6. **Wait 500ms** before the next creation.
374
-
375
- ### GitHub — POST /repos/{owner}/{repo}/issues
376
-
377
- ```bash
378
- curl -s \
379
- -H "Authorization: Bearer $GITHUB_TOKEN" \
380
- -H "Accept: application/vnd.github+json" \
381
- -H "X-GitHub-Api-Version: 2022-11-28" \
382
- -H "Content-Type: application/json" \
383
- -X POST \
384
- "https://api.github.com/repos/$GITHUB_REPO/issues" \
385
- -d '{
386
- "title": "{ticket title}",
387
- "body": "Epic: #{parent_number}\n\n## {Title}\n\n{Description}\n\n---\n\n**Parent:** #{parent_number}\n**Brief:** {slug}\n**Size:** {S|M|L}\n\n### Dependencies\n\n{Depends on #NN lines or None}\n\n---\n\n*Created by Clancy from strategic brief.*",
388
- "labels": ["{PIPELINE_LABEL}", "size:{size}", "clancy:{mode}", ...],
389
- "assignees": ["{resolved_username}"]
390
- }'
391
- ```
392
-
393
- The `Epic: #{parent_number}` line is always the FIRST line of the body — this enables cross-platform epic completion detection.
394
-
395
- Dependencies in the body use resolved issue numbers: `Depends on #51` (not decomposition indices).
396
-
397
- If no parent: omit `Epic:` and `Parent:` lines, omit `assignees` if not resolvable.
398
-
399
- **On 422 (label validation error):** Retry without the invalid label(s). Warn: `Label "{name}" does not exist. Created issue without it.`
400
-
401
- **On 429 (rate limited):** Check `X-RateLimit-Reset` header. Wait until reset, retry once. If still limited: stop, enter partial failure flow.
402
-
403
- ### Jira — POST /rest/api/3/issue
404
-
405
- ```bash
406
- curl -s \
407
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
408
- -H "Content-Type: application/json" \
409
- -H "Accept: application/json" \
410
- -X POST \
411
- "$JIRA_BASE_URL/rest/api/3/issue" \
412
- -d '{
413
- "fields": {
414
- "project": { "key": "$JIRA_PROJECT_KEY" },
415
- "summary": "{ticket title}",
416
- "description": {
417
- "version": 1,
418
- "type": "doc",
419
- "content": [
420
- {
421
- "type": "paragraph",
422
- "content": [
423
- { "type": "text", "text": "Epic: {PARENT_KEY}\n\n{Description}" }
424
- ]
425
- }
426
- ]
427
- },
428
- "issuetype": { "name": "{CLANCY_BRIEF_ISSUE_TYPE or Task}" },
429
- "parent": { "key": "{PARENT_KEY}" },
430
- "labels": ["{PIPELINE_LABEL}", "clancy:{mode}"]
431
- }
432
- }'
433
- ```
434
-
435
- **Conditional fields (include only when set):**
436
- - `CLANCY_COMPONENT` -> `"components": [{ "name": "{value}" }]`
437
- - Parent has priority -> `"priority": { "name": "{priority}" }`
438
-
439
- **Parent field fallback (classic projects):**
440
- If the API returns 400 with an error mentioning the `parent` field, retry with `customfield_10014` instead:
441
- ```json
442
- "customfield_10014": "{PARENT_KEY}"
443
- ```
444
- Cache which field works for the remaining tickets in this batch.
445
-
446
- If both `parent` and `customfield_10014` fail:
447
- ```
448
- Could not set parent. Continue creating tickets without parent? [y/N]
449
- ```
450
- Default: N (stop). If Y: create remaining tickets without parent field.
451
-
452
- **On 400 (component not found):** Retry without `components` field. Warn.
453
-
454
- **On 429 (rate limited):** Honour `Retry-After` header. Wait, retry once. If still 429: stop, enter partial failure flow.
455
-
456
- ### Linear — issueCreate mutation
457
-
458
- ```graphql
459
- mutation {
460
- issueCreate(input: {
461
- teamId: "$LINEAR_TEAM_ID"
462
- title: "{ticket title}"
463
- description: "Epic: {PARENT_IDENTIFIER}\n\n{Description}"
464
- parentId: "{PARENT_UUID}"
465
- stateId: "{BACKLOG_STATE_UUID}"
466
- labelIds: ["{label UUIDs}"]
467
- priority: 0
468
- }) {
469
- success
470
- issue {
471
- id
472
- identifier
473
- title
474
- url
475
- }
476
- }
477
- }
478
- ```
479
-
480
- If no parent: omit `parentId`.
481
-
482
- **On rate limit (RATELIMITED error code):** Wait 60s, retry once. If still limited: stop, enter partial failure flow.
483
-
484
- ---
485
-
486
- ## Step 8 — Link dependencies
487
-
488
- For each ticket with dependencies, create blocking relationships on the board. This is **best-effort** — warn on failure, do not stop.
489
-
490
- Skip links involving uncreated tickets (from partial failures).
491
-
492
- ### GitHub
493
-
494
- No separate API call needed. Dependencies are embedded in the issue body as `Depends on #{number}` text. GitHub auto-creates cross-references from the `#N` mentions.
495
-
496
- ### Jira — POST /rest/api/3/issueLink
497
-
498
- For each dependency (e.g. ticket #3 depends on #2):
499
-
500
- ```bash
501
- curl -s \
502
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
503
- -H "Content-Type: application/json" \
504
- -H "Accept: application/json" \
505
- -X POST \
506
- "$JIRA_BASE_URL/rest/api/3/issueLink" \
507
- -d '{
508
- "type": { "name": "Blocks" },
509
- "inwardIssue": { "key": "{BLOCKER_KEY}" },
510
- "outwardIssue": { "key": "{DEPENDENT_KEY}" }
511
- }'
512
- ```
513
-
514
- `inwardIssue` = the blocker, `outwardIssue` = the dependent ticket.
515
-
516
- Wait **200ms** between each link creation.
517
-
518
- On failure: `Could not link {DEPENDENT} -> {BLOCKER} (dependency). Link manually if needed.`
519
-
520
- ### Linear — issueRelationCreate mutation
521
-
522
- ```graphql
523
- mutation {
524
- issueRelationCreate(input: {
525
- issueId: "{DEPENDENT_UUID}"
526
- relatedIssueId: "{BLOCKER_UUID}"
527
- type: blockedBy
528
- }) {
529
- success
530
- }
531
- }
532
- ```
533
-
534
- `issueId` = the dependent ticket, `relatedIssueId` = the blocker.
535
-
536
- Wait **200ms** between each relation creation.
537
-
538
- On failure: `Could not link {DEPENDENT} -> {BLOCKER}. Add manually in Linear.`
539
-
540
- ---
541
-
542
- ## Step 9 — Update brief file
543
-
544
- After creating tickets, update the local brief file:
545
-
546
- 1. **Add ticket keys** to the `Ticket` column in the decomposition table:
547
- ```markdown
548
- | # | Title | Description | Size | Dependencies | Mode | Ticket |
549
- |---|-------|-------------|------|--------------|------|--------|
550
- | 1 | Set up route structure | ... | S | None | AFK | PROJ-201 |
551
- | 2 | SSO login flow | ... | M | None | HITL | PROJ-202 |
552
- ```
553
-
554
- 2. **Update status** from `Draft` to `Approved` (in the `**Status:**` line if present).
555
-
556
- ---
557
-
558
- ## Step 10 — Mark approved
559
-
560
- Check whether all tickets were created successfully:
561
-
562
- **All created:**
563
- - The `.approved` marker file (created in Step 6a) stays in place.
564
-
565
- **Partial failure (some tickets failed):**
566
- - **DELETE** the `.approved` marker file. Partial failure = not approved.
567
- - Update the brief file with the tickets that WERE created (Step 9 still applies).
568
- - Display partial failure summary:
569
- ```
570
- Partial: {M} of {N} tickets created
571
-
572
- PROJ-201 [S] Set up route structure
573
- PROJ-202 [M] SSO login flow
574
- X #3 Role-based access control — FAILED ({error})
575
- - #4-#{N} not attempted
576
-
577
- Created tickets are live on the board. To complete:
578
- 1. Fix the issue (check board status/permissions)
579
- 2. Re-run /clancy:approve-brief to resume creating remaining tickets
580
- ```
581
- - Log: `YYYY-MM-DD HH:MM | APPROVE_BRIEF | {slug} | PARTIAL — {M} of {N} created`
582
- - Stop (do not post tracking comment for partial failures).
583
-
584
- ---
585
-
586
- ## Step 11 — Post tracking summary on parent
587
-
588
- Only if a parent ticket exists AND all tickets were created successfully.
589
-
590
- Post a comment on the parent ticket listing all created children.
591
-
592
- ### GitHub
593
-
594
- ```bash
595
- curl -s \
596
- -H "Authorization: Bearer $GITHUB_TOKEN" \
597
- -H "Accept: application/vnd.github+json" \
598
- -H "X-GitHub-Api-Version: 2022-11-28" \
599
- -H "Content-Type: application/json" \
600
- -X POST \
601
- "https://api.github.com/repos/$GITHUB_REPO/issues/$PARENT_NUMBER/comments" \
602
- -d '{"body": "<tracking comment markdown>"}'
603
- ```
604
-
605
- ### Jira
606
-
607
- ```bash
608
- curl -s \
609
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
610
- -H "Content-Type: application/json" \
611
- -H "Accept: application/json" \
612
- -X POST \
613
- "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY/comment" \
614
- -d '{"body": { "version": 1, "type": "doc", "content": [/* ADF table */] }}'
615
- ```
616
-
617
- ### Linear
618
-
619
- ```graphql
620
- mutation {
621
- commentCreate(input: {
622
- issueId: "$PARENT_UUID"
623
- body: "<tracking comment markdown>"
624
- }) {
625
- success
626
- }
627
- }
628
- ```
629
-
630
- ### Tracking comment format
631
-
632
- ```markdown
633
- ## Clancy — Approved Tickets
634
-
635
- | # | Ticket | Title | Size | Mode |
636
- |---|--------|-------|------|------|
637
- | 1 | {KEY} | {Title} | S | AFK |
638
- | 2 | {KEY} | {Title} | M | HITL |
639
- | 3 | {KEY} | {Title} | M | AFK |
640
-
641
- Dependencies linked: {N}
642
- Created by Clancy on {YYYY-MM-DD}.
643
- ```
644
-
645
- **On failure:** Warn: `Could not post tracking summary on {PARENT}. Tickets are created regardless.` Continue — best-effort.
646
-
647
- ---
648
-
649
- ## Step 11a — Remove brief label from parent
650
-
651
- Only if a parent ticket exists AND all tickets were created successfully. Remove the brief label from the parent ticket. Best-effort — warn on failure, never stop.
652
-
653
- Use `CLANCY_LABEL_BRIEF` from `.clancy/.env` if set, otherwise `clancy:brief`.
654
-
655
- ### GitHub
656
-
657
- ```bash
658
- curl -s \
659
- -H "Authorization: Bearer $GITHUB_TOKEN" \
660
- -H "Accept: application/vnd.github+json" \
661
- -H "X-GitHub-Api-Version: 2022-11-28" \
662
- -X DELETE \
663
- "https://api.github.com/repos/$GITHUB_REPO/issues/$PARENT_NUMBER/labels/$(echo $CLANCY_LABEL_BRIEF | jq -Rr @uri)"
664
- ```
665
-
666
- Ignore 404 (label may not be present on the parent).
667
-
668
- ### Jira
669
-
670
- ```bash
671
- CURRENT_LABELS=$(curl -s \
672
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
673
- -H "Accept: application/json" \
674
- "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY?fields=labels" | jq -r '.fields.labels')
675
-
676
- UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg brief "$CLANCY_LABEL_BRIEF" '[.[] | select(. != $brief)]')
677
-
678
- curl -s \
679
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
680
- -X PUT \
681
- -H "Content-Type: application/json" \
682
- "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY" \
683
- -d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
684
- ```
685
-
686
- ### Linear
687
-
688
- ```bash
689
- # Fetch current label IDs on the parent, remove the brief label ID, issueUpdate
690
- # Use the same pattern as brief.md Step 10a — query labels, filter, update
691
- ```
692
-
693
- ### On failure
694
-
695
- ```
696
- ⚠️ Could not remove brief label from {PARENT_KEY}. Remove it manually if needed.
697
- ```
698
-
699
- Continue — do not stop.
700
-
701
- ---
702
-
703
- ## Step 12 — Display summary
704
-
705
- Show the final result:
706
-
707
- ```
708
- {N} tickets created under {PARENT_KEY}
709
-
710
- {KEY} [{Size}] [{Mode}] {Title}
711
- {KEY} [{Size}] [{Mode}] {Title}
712
- {KEY} [{Size}] [{Mode}] {Title}
713
-
714
- Dependencies linked: {N}
715
- AFK-ready: {X} | Needs human: {Y}
716
- Epic branch: epic/{parent-key-lowercase}
717
-
718
- Next: run /clancy:plan to generate implementation plans.
719
-
720
- "We've got ourselves a case."
721
- ```
722
-
723
- If no parent:
724
- ```
725
- {N} standalone tickets created.
726
- ...
727
- Next: run /clancy:plan to generate implementation plans.
728
-
729
- "We've got ourselves a case."
730
- ```
731
-
732
- ---
733
-
734
- ## Step 13 — Log
735
-
736
- Append to `.clancy/progress.txt`:
737
-
738
- ```
739
- YYYY-MM-DD HH:MM | APPROVE_BRIEF | {slug} | {N} tickets created
740
- ```
741
-
742
- ---
743
-
744
- ## Error handling reference
745
-
746
- ### Rate limiting
747
-
748
- | Platform | Detection | Response |
749
- |----------|-----------|----------|
750
- | GitHub | 403 + `X-RateLimit-Remaining: 0` | Wait until `X-RateLimit-Reset`, retry once |
751
- | Jira | 429 + `Retry-After` header | Wait the specified seconds, retry once |
752
- | Linear | `RATELIMITED` error code | Wait 60s, retry once |
753
-
754
- If retry also fails: stop, enter partial failure flow.
755
-
756
- ### Auth failure mid-batch
757
-
758
- If a 401/403 occurs during ticket creation (token expired or revoked after preflight):
759
- ```
760
- Auth failed during ticket creation. {M} of {N} created before failure.
761
- Check credentials and re-run to resume.
762
- ```
763
- Enter partial failure flow (Step 10).
764
-
765
- ### Timeout
766
-
767
- | Platform | Timeout |
768
- |----------|---------|
769
- | GitHub | 15s per API call |
770
- | Jira | 30s per API call |
771
- | Linear | 30s per GraphQL call |
772
-
773
- On timeout: `Request timed out. Ticket may have been created server-side. Check board before re-run.` Enter partial failure flow.
774
-
775
- ### Duplicate guard (on resume)
776
-
777
- When resuming from a partial failure, before creating each ticket check the brief's decomposition table for an existing Ticket column entry. Additionally, scan the board for existing children of the parent with matching titles (case-insensitive exact match):
778
-
779
- #### Jira
780
- ```bash
781
- curl -s \
782
- -u "$JIRA_USER:$JIRA_API_TOKEN" \
783
- -H "Content-Type: application/json" \
784
- -H "Accept: application/json" \
785
- -X POST \
786
- "$JIRA_BASE_URL/rest/api/3/search/jql" \
787
- -d '{"jql": "parent = $PARENT_KEY", "maxResults": 50, "fields": ["summary"]}'
788
- ```
789
-
790
- #### GitHub
791
- ```bash
792
- curl -s \
793
- -H "Authorization: Bearer $GITHUB_TOKEN" \
794
- -H "Accept: application/vnd.github+json" \
795
- -H "X-GitHub-Api-Version: 2022-11-28" \
796
- "https://api.github.com/repos/$GITHUB_REPO/issues?state=open&per_page=30"
797
- ```
798
- Filter for issues containing `Epic: #{parent_number}` in the body.
799
-
800
- #### Linear
801
- ```graphql
802
- query {
803
- issues(filter: { identifier: { eq: "$PARENT_KEY" } }) {
804
- nodes {
805
- children { nodes { id identifier title } }
806
- }
807
- }
808
- }
809
- ```
810
-
811
- If matching children found:
812
- ```
813
- {PARENT} already has children with similar titles:
814
- - {KEY}: "{Title}" (matches proposed ticket #{N})
815
-
816
- This brief may have already been approved. Continue anyway? [y/N]
817
- ```
818
- Default: N (don't create duplicates).
819
-
820
- ---
821
-
822
- ## Notes
823
-
824
- - The `Epic: {key}` convention is always the first line of every child ticket description across all platforms — this enables cross-platform epic completion detection by `fetchChildrenStatus`
825
- - Mode labels (`clancy:afk` / `clancy:hitl`) are used by `/clancy:run` to decide whether to pick up a ticket autonomously or skip it for human attention
826
- - Jira ADF construction: if complex content fails, wrap in a `codeBlock` node as fallback (matches the pattern used by `/clancy:approve-plan`)
827
- - The `.approved` marker filename is the full brief filename with `.approved` appended (e.g. `.clancy/briefs/2026-03-14-auth-rework.md.approved`)
828
- - Tickets are created sequentially (not in parallel) to maintain dependency ordering and respect rate limits
829
- - The 500ms delay between ticket creations is sufficient for all platforms under normal rate limit conditions
830
- - Dependency links use "Blocks" for Jira, `blockedBy` for Linear, and body text cross-references for GitHub
831
- - Labels on Jira are auto-created by the platform; on GitHub they must be pre-created or the 422 fallback handles it; on Linear they are looked up and auto-created if missing
832
- - Sprint/milestone assignment is deliberately not set — this is a team planning decision
833
- - Linear `priority: 0` means "No priority" — the team triages after creation
834
- - Jira priority is inherited from the parent if available; Linear and GitHub do not inherit priority