chief-clancy 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chief-clancy",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Autonomous, board-driven development for Claude Code — scaffolds docs, integrates Kanban boards, runs tickets in a loop.",
5
5
  "keywords": [
6
6
  "claude",
@@ -314,25 +314,29 @@ Could not update plan comment. The plan is still promoted to the description.
314
314
 
315
315
  ---
316
316
 
317
- ## Step 6 — Post-approval transition
317
+ ## Step 6 — Post-approval label transition
318
318
 
319
- Transition the ticket from the planning queue to the implementation queue. This is **best-effort** — warn on failure, continue.
319
+ Transition the ticket from the planning queue to the implementation queue via pipeline labels. This is **best-effort** — warn on failure, continue.
320
320
 
321
- ### GitHub (always, not configurable)
321
+ **Crash safety:** Add the new label BEFORE removing the old one. A ticket briefly has two labels (harmless) rather than zero labels (ticket lost).
322
322
 
323
- 1. **Remove planning label:**
323
+ Read `CLANCY_LABEL_BUILD` from `.clancy/.env` (default: `clancy:build`). Read `CLANCY_LABEL_PLAN` from `.clancy/.env` (default: `clancy:plan`, falls back to `CLANCY_PLAN_LABEL`).
324
+
325
+ ### GitHub
326
+
327
+ 1. **Add build label** (ensure it exists first):
324
328
  ```bash
329
+ # Ensure label exists (ignore 422 = already exists)
325
330
  curl -s \
326
331
  -H "Authorization: Bearer $GITHUB_TOKEN" \
327
332
  -H "Accept: application/vnd.github+json" \
328
333
  -H "X-GitHub-Api-Version: 2022-11-28" \
329
- -X DELETE \
330
- "https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels/$CLANCY_PLAN_LABEL"
331
- ```
332
- `CLANCY_PLAN_LABEL` defaults to `needs-refinement`. URL-encode the label name. Ignore 404 (label not on issue). If `CLANCY_PLAN_LABEL` is not set, use the default `needs-refinement`.
334
+ -H "Content-Type: application/json" \
335
+ -X POST \
336
+ "https://api.github.com/repos/$GITHUB_REPO/labels" \
337
+ -d '{"name": "$CLANCY_LABEL_BUILD", "color": "0075ca"}'
333
338
 
334
- 2. **Add implementation label** (only if `CLANCY_LABEL` is set — skip if unset):
335
- ```bash
339
+ # Add to issue
336
340
  curl -s \
337
341
  -H "Authorization: Bearer $GITHUB_TOKEN" \
338
342
  -H "Accept: application/vnd.github+json" \
@@ -340,38 +344,68 @@ Transition the ticket from the planning queue to the implementation queue. This
340
344
  -H "Content-Type: application/json" \
341
345
  -X POST \
342
346
  "https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels" \
343
- -d '{"labels": ["$CLANCY_LABEL"]}'
347
+ -d '{"labels": ["$CLANCY_LABEL_BUILD"]}'
344
348
  ```
345
- If `CLANCY_LABEL` is not set, skip this step entirely (only the plan label removal is done). `CLANCY_LABEL` has no default — it is fully optional. When set (e.g. `clancy`), use its value. If the label does not exist on the repo, attempt to create it:
349
+
350
+ 2. **Remove plan label:**
346
351
  ```bash
347
352
  curl -s \
348
353
  -H "Authorization: Bearer $GITHUB_TOKEN" \
349
354
  -H "Accept: application/vnd.github+json" \
355
+ -H "X-GitHub-Api-Version: 2022-11-28" \
356
+ -X DELETE \
357
+ "https://api.github.com/repos/$GITHUB_REPO/issues/$ISSUE_NUMBER/labels/$(echo $CLANCY_LABEL_PLAN | jq -Rr @uri)"
358
+ ```
359
+ Ignore 404 (label not on issue).
360
+
361
+ ### Jira
362
+
363
+ 1. **Add build label** (Jira auto-creates labels):
364
+ ```bash
365
+ # Fetch current labels
366
+ CURRENT_LABELS=$(curl -s \
367
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
368
+ -H "Accept: application/json" \
369
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=labels" | jq -r '.fields.labels')
370
+
371
+ # Add build label
372
+ UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg build "$CLANCY_LABEL_BUILD" '. + [$build] | unique')
373
+
374
+ curl -s \
375
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
376
+ -X PUT \
350
377
  -H "Content-Type: application/json" \
351
- -X POST \
352
- "https://api.github.com/repos/$GITHUB_REPO/labels" \
353
- -d '{"name": "$CLANCY_LABEL", "color": "0075ca"}'
378
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY" \
379
+ -d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
354
380
  ```
355
- If 403 (no admin access) or 422 (invalid name): warn, continue without label.
356
381
 
357
- ### Jira (only if `CLANCY_STATUS_PLANNED` is set)
382
+ 2. **Remove plan label:**
383
+ ```bash
384
+ # Re-fetch labels (may have changed), remove plan label
385
+ CURRENT_LABELS=$(curl -s \
386
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
387
+ -H "Accept: application/json" \
388
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY?fields=labels" | jq -r '.fields.labels')
358
389
 
359
- If `CLANCY_STATUS_PLANNED` is not set: skip transition, no warning needed.
390
+ UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg plan "$CLANCY_LABEL_PLAN" '[.[] | select(. != $plan)]')
360
391
 
361
- If `CLANCY_STATUS_PLANNED` is set:
392
+ curl -s \
393
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
394
+ -X PUT \
395
+ -H "Content-Type: application/json" \
396
+ "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY" \
397
+ -d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
398
+ ```
362
399
 
363
- 1. **Fetch transitions:**
400
+ 3. **Status transition** (only if `CLANCY_STATUS_PLANNED` is set — skip if unset):
364
401
  ```bash
402
+ # Fetch transitions
365
403
  curl -s \
366
404
  -u "$JIRA_USER:$JIRA_API_TOKEN" \
367
405
  -H "Accept: application/json" \
368
406
  "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_KEY/transitions"
369
- ```
370
407
 
371
- 2. **Find matching transition:** Look for a transition where `.name` matches `CLANCY_STATUS_PLANNED` (case-insensitive). This matches the pattern used in the runtime Jira module.
372
-
373
- 3. **Execute transition:**
374
- ```bash
408
+ # Find matching transition and execute
375
409
  curl -s \
376
410
  -u "$JIRA_USER:$JIRA_API_TOKEN" \
377
411
  -X POST \
@@ -385,23 +419,50 @@ On failure:
385
419
  Could not transition ticket. Move it manually to your implementation queue.
386
420
  ```
387
421
 
388
- ### Linear (always)
422
+ ### Linear
423
+
424
+ 1. **Add build label** (ensure it exists, then add):
425
+ ```graphql
426
+ # Ensure label exists — check team labels, workspace labels, create if missing
427
+ mutation {
428
+ issueLabelCreate(input: {
429
+ teamId: "$LINEAR_TEAM_ID"
430
+ name: "$CLANCY_LABEL_BUILD"
431
+ color: "#0075ca"
432
+ }) { success issueLabel { id } }
433
+ }
434
+
435
+ # Fetch current label IDs on the issue, add build label ID
436
+ mutation {
437
+ issueUpdate(
438
+ id: "$ISSUE_UUID"
439
+ input: { labelIds: [...currentLabelIds, buildLabelId] }
440
+ ) { success }
441
+ }
442
+ ```
443
+
444
+ 2. **Remove plan label:**
445
+ ```graphql
446
+ # Fetch current label IDs, filter out plan label ID
447
+ mutation {
448
+ issueUpdate(
449
+ id: "$ISSUE_UUID"
450
+ input: { labelIds: [currentLabelIds without planLabelId] }
451
+ ) { success }
452
+ }
453
+ ```
389
454
 
390
- 1. **Resolve "unstarted" state:**
455
+ 3. **State transition** (always):
391
456
  ```graphql
457
+ # Resolve "unstarted" state
392
458
  query {
393
459
  workflowStates(filter: {
394
460
  team: { id: { eq: "$LINEAR_TEAM_ID" } }
395
461
  type: { eq: "unstarted" }
396
- }) {
397
- nodes { id name }
398
- }
462
+ }) { nodes { id name } }
399
463
  }
400
- ```
401
- Use `nodes[0].id` as the target state. If nodes is empty: warn `No 'unstarted' state found for team.` Skip transition.
402
464
 
403
- 2. **Transition:**
404
- ```graphql
465
+ # Transition
405
466
  mutation {
406
467
  issueUpdate(
407
468
  id: "$ISSUE_UUID"
@@ -409,6 +470,7 @@ Could not transition ticket. Move it manually to your implementation queue.
409
470
  ) { success }
410
471
  }
411
472
  ```
473
+ If no `unstarted` state found: warn, skip transition.
412
474
 
413
475
  On failure:
414
476
  ```
@@ -421,20 +483,13 @@ Could not transition ticket. Move it manually to your implementation queue.
421
483
 
422
484
  On success, display a board-specific message:
423
485
 
424
- **GitHub (with CLANCY_LABEL set):**
486
+ **GitHub:**
425
487
  ```
426
- Plan promoted. Label swapped: {plan_label} → {impl_label}. Ready for /clancy:once.
488
+ Plan promoted. Label swapped: {CLANCY_LABEL_PLAN} → {CLANCY_LABEL_BUILD}. Ready for /clancy:once.
427
489
 
428
490
  "Book 'em, Lou." — The ticket is ready for /clancy:once.
429
491
  ```
430
492
 
431
- **GitHub (without CLANCY_LABEL):**
432
- ```
433
- Plan promoted. Label removed: {plan_label}. Add your implementation label manually, or run /clancy:once.
434
-
435
- "Book 'em, Lou."
436
- ```
437
-
438
493
  **Jira (with transition):**
439
494
  ```
440
495
  Plan promoted. Ticket transitioned to {CLANCY_STATUS_PLANNED}.
@@ -136,9 +136,9 @@ Then skip to Step 3b with this single ticket.
136
136
  Build the JQL using planning-specific env vars:
137
137
  - `CLANCY_PLAN_STATUS` defaults to `Backlog` if not set
138
138
  - Sprint clause: include `AND sprint in openSprints()` if `CLANCY_JQL_SPRINT` is set
139
- - Label clause: include `AND labels = "$CLANCY_LABEL"` if `CLANCY_LABEL` is set
139
+ - Label clause: include `AND labels = "$CLANCY_LABEL_PLAN"` if `CLANCY_LABEL_PLAN` is set (falls back to `CLANCY_PLAN_LABEL` if `CLANCY_LABEL_PLAN` is not set). If neither is set, include `AND labels = "$CLANCY_LABEL"` if `CLANCY_LABEL` is set.
140
140
 
141
- Full JQL: `project=$JIRA_PROJECT_KEY [AND sprint in openSprints()] [AND labels = "$CLANCY_LABEL"] AND assignee=currentUser() AND status="$CLANCY_PLAN_STATUS" ORDER BY priority ASC`
141
+ Full JQL: `project=$JIRA_PROJECT_KEY [AND sprint in openSprints()] [AND labels = "$CLANCY_LABEL_PLAN"] AND assignee=currentUser() AND status="$CLANCY_PLAN_STATUS" ORDER BY priority ASC`
142
142
 
143
143
  ```bash
144
144
  RESPONSE=$(curl -s \
@@ -164,16 +164,16 @@ Then fetch issues:
164
164
  RESPONSE=$(curl -s \
165
165
  -H "Authorization: Bearer $GITHUB_TOKEN" \
166
166
  -H "X-GitHub-Api-Version: 2022-11-28" \
167
- "https://api.github.com/repos/$GITHUB_REPO/issues?state=open&assignee=$GITHUB_USERNAME&labels=$CLANCY_PLAN_LABEL&per_page=<N>")
167
+ "https://api.github.com/repos/$GITHUB_REPO/issues?state=open&assignee=$GITHUB_USERNAME&labels=$CLANCY_LABEL_PLAN&per_page=<N>")
168
168
  ```
169
169
 
170
- - `CLANCY_PLAN_LABEL` defaults to `needs-refinement` if not set
170
+ - `CLANCY_LABEL_PLAN` is the pipeline label for the planning queue (default: `clancy:plan`). Falls back to `CLANCY_PLAN_LABEL` if `CLANCY_LABEL_PLAN` is not set. If neither is set, defaults to `needs-refinement`.
171
171
  - Filter out PRs (entries with `pull_request` key)
172
172
  - For each issue, fetch comments: `GET /repos/$GITHUB_REPO/issues/{number}/comments`
173
173
 
174
174
  #### Linear
175
175
 
176
- Build the filter using `CLANCY_PLAN_STATE_TYPE` (defaults to `backlog` if not set):
176
+ Build the filter using `CLANCY_PLAN_STATE_TYPE` (defaults to `backlog` if not set). If `CLANCY_LABEL_PLAN` is set (falls back to `CLANCY_PLAN_LABEL`), add a label filter to the query:
177
177
 
178
178
  ```graphql
179
179
  query {
@@ -182,6 +182,7 @@ query {
182
182
  filter: {
183
183
  state: { type: { eq: "$CLANCY_PLAN_STATE_TYPE" } }
184
184
  team: { id: { eq: "$LINEAR_TEAM_ID" } }
185
+ labels: { name: { eq: "$CLANCY_LABEL_PLAN" } } # Only if CLANCY_LABEL_PLAN is set
185
186
  }
186
187
  first: $N
187
188
  orderBy: priority
@@ -214,7 +215,7 @@ If no tickets found:
214
215
 
215
216
  Then display board-specific guidance:
216
217
 
217
- - **GitHub:** `For GitHub: planning uses the "$CLANCY_PLAN_LABEL" label (default: needs-refinement), not "clancy". Apply that label to issues you want planned.`
218
+ - **GitHub:** `For GitHub: planning uses the "$CLANCY_LABEL_PLAN" label (default: clancy:plan, fallback: $CLANCY_PLAN_LABEL or needs-refinement). Apply that label to issues you want planned.`
218
219
  - **Jira:** `Check that CLANCY_PLAN_STATUS (currently: "$CLANCY_PLAN_STATUS") matches a status in your Jira project, and that tickets in that status are assigned to you.`
219
220
  - **Linear:** `Check that CLANCY_PLAN_STATE_TYPE (currently: "$CLANCY_PLAN_STATE_TYPE") is a valid Linear state type (backlog, unstarted, started, completed, canceled, triage), and that tickets in that state are assigned to you in team $LINEAR_TEAM_ID.`
220
221
 
@@ -592,6 +592,74 @@ Note: as more roles are added in future versions, they appear as additional numb
592
592
 
593
593
  ---
594
594
 
595
+ ## Step 4c-2 — Pipeline labels (conditional)
596
+
597
+ Only ask this if any optional role was enabled in Step 4c. If neither Planner nor Strategist was selected, skip this section entirely. If `CLANCY_LABEL` or `CLANCY_PLAN_LABEL` are already set in `.clancy/.env`, show:
598
+
599
+ ```
600
+ Note: CLANCY_LABEL and CLANCY_PLAN_LABEL are deprecated.
601
+ Use CLANCY_LABEL_BUILD and CLANCY_LABEL_PLAN instead.
602
+ Your existing values will continue to work as fallbacks.
603
+ ```
604
+
605
+ **If the user enabled Strategist (or both Strategist + Planner):**
606
+
607
+ Output:
608
+
609
+ ```
610
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
611
+ Pipeline Labels
612
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
613
+
614
+ Clancy uses labels to move tickets through pipeline stages:
615
+ brief → plan → build
616
+
617
+ Each label marks which queue a ticket belongs to.
618
+ ```
619
+
620
+ Then ask each label in order:
621
+
622
+ ```
623
+ What label marks tickets that have been briefed (awaiting approval)?
624
+ [clancy:brief]
625
+ ```
626
+
627
+ If a value is entered: store as `CLANCY_LABEL_BRIEF` in `.clancy/.env`. Wrap in double quotes.
628
+ If enter is pressed: use default — store `CLANCY_LABEL_BRIEF="clancy:brief"` in `.clancy/.env`.
629
+
630
+ ```
631
+ What label marks tickets that need planning?
632
+ [clancy:plan]
633
+ ```
634
+
635
+ If a value is entered: store as `CLANCY_LABEL_PLAN` in `.clancy/.env`. Wrap in double quotes.
636
+ If enter is pressed: use default — store `CLANCY_LABEL_PLAN="clancy:plan"` in `.clancy/.env`.
637
+
638
+ ```
639
+ What label marks tickets ready to build?
640
+ [clancy:build]
641
+ ```
642
+
643
+ If a value is entered: store as `CLANCY_LABEL_BUILD` in `.clancy/.env`. Wrap in double quotes.
644
+ If enter is pressed: use default — store `CLANCY_LABEL_BUILD="clancy:build"` in `.clancy/.env`.
645
+
646
+ **If the user enabled Planner only (no Strategist):**
647
+
648
+ Skip `CLANCY_LABEL_BRIEF` (no `/clancy:brief` command). Ask only:
649
+
650
+ ```
651
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
652
+ Pipeline Labels
653
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
654
+
655
+ Clancy uses labels to move tickets through pipeline stages:
656
+ plan → build
657
+ ```
658
+
659
+ Then ask `CLANCY_LABEL_PLAN` and `CLANCY_LABEL_BUILD` using the same prompts and defaults as above.
660
+
661
+ ---
662
+
595
663
  ## Step 4d (if Planner role selected): Planning queue config
596
664
 
597
665
  Only ask this if the user selected Planner in Step 4c above (or if re-running init and `CLANCY_ROLES` already includes `planner`).
@@ -396,6 +396,16 @@ MAX_ITERATIONS=5
396
396
  # CLANCY_GIT_PLATFORM=gitlab # override auto-detection (github/gitlab/bitbucket)
397
397
  # CLANCY_GIT_API_URL=https://gitlab.example.com/api/v4 # self-hosted git API base URL
398
398
 
399
+ # ─── Optional: Pipeline labels ────────────────────────────────────────────────
400
+ # Labels that control ticket flow through pipeline stages.
401
+ # CLANCY_LABEL_BRIEF marks tickets that have been briefed (awaiting approval).
402
+ # CLANCY_LABEL_PLAN marks tickets that need planning.
403
+ # CLANCY_LABEL_BUILD marks tickets ready for implementation.
404
+ # Deprecated: CLANCY_LABEL (use CLANCY_LABEL_BUILD), CLANCY_PLAN_LABEL (use CLANCY_LABEL_PLAN)
405
+ # CLANCY_LABEL_BRIEF="clancy:brief"
406
+ # CLANCY_LABEL_PLAN="clancy:plan"
407
+ # CLANCY_LABEL_BUILD="clancy:build"
408
+
399
409
  # ─── Optional: Rework loop ──────────────────────────────────────────────────
400
410
  # PR-based rework is automatic — when a reviewer leaves inline comments or
401
411
  # a conversation comment prefixed with "Rework:", Clancy picks it up on the
@@ -502,6 +512,16 @@ MAX_ITERATIONS=20
502
512
  # PLAYWRIGHT_STORYBOOK_PORT=6006
503
513
  # PLAYWRIGHT_STARTUP_WAIT=15
504
514
 
515
+ # ─── Optional: Pipeline labels ────────────────────────────────────────────────
516
+ # Labels that control ticket flow through pipeline stages.
517
+ # CLANCY_LABEL_BRIEF marks tickets that have been briefed (awaiting approval).
518
+ # CLANCY_LABEL_PLAN marks tickets that need planning.
519
+ # CLANCY_LABEL_BUILD marks tickets ready for implementation.
520
+ # Deprecated: CLANCY_LABEL (use CLANCY_LABEL_BUILD), CLANCY_PLAN_LABEL (use CLANCY_LABEL_PLAN)
521
+ # CLANCY_LABEL_BRIEF="clancy:brief"
522
+ # CLANCY_LABEL_PLAN="clancy:plan"
523
+ # CLANCY_LABEL_BUILD="clancy:build"
524
+
505
525
  # ─── Optional: Rework loop ──────────────────────────────────────────────────
506
526
  # PR-based rework is automatic — when a reviewer leaves inline comments or
507
527
  # a conversation comment prefixed with "Rework:", Clancy picks it up on the
@@ -636,6 +656,16 @@ MAX_ITERATIONS=20
636
656
  # CLANCY_GIT_PLATFORM=gitlab # override auto-detection (github/gitlab/bitbucket)
637
657
  # CLANCY_GIT_API_URL=https://gitlab.example.com/api/v4 # self-hosted git API base URL
638
658
 
659
+ # ─── Optional: Pipeline labels ────────────────────────────────────────────────
660
+ # Labels that control ticket flow through pipeline stages.
661
+ # CLANCY_LABEL_BRIEF marks tickets that have been briefed (awaiting approval).
662
+ # CLANCY_LABEL_PLAN marks tickets that need planning.
663
+ # CLANCY_LABEL_BUILD marks tickets ready for implementation.
664
+ # Deprecated: CLANCY_LABEL (use CLANCY_LABEL_BUILD), CLANCY_PLAN_LABEL (use CLANCY_LABEL_PLAN)
665
+ # CLANCY_LABEL_BRIEF="clancy:brief"
666
+ # CLANCY_LABEL_PLAN="clancy:plan"
667
+ # CLANCY_LABEL_BUILD="clancy:build"
668
+
639
669
  # ─── Optional: Reliable autonomous mode ───────────────────────────────────────
640
670
  # Max self-healing attempts after verification failure (default: 2, range 0-5)
641
671
  # CLANCY_FIX_RETRIES=2
@@ -86,6 +86,14 @@ Strategist
86
86
  [T2] Issue type {CLANCY_BRIEF_ISSUE_TYPE:-Task}
87
87
  [T3] Component {CLANCY_COMPONENT if set, else "off"}
88
88
 
89
+ {If Planner or Strategist enabled:}
90
+ Pipeline Labels
91
+ [L1] Brief label {CLANCY_LABEL_BRIEF if set, else "clancy:brief"} (Strategist only)
92
+ [L2] Plan label {CLANCY_LABEL_PLAN if set, else "clancy:plan"}
93
+ [L3] Build label {CLANCY_LABEL_BUILD if set, else "clancy:build"}
94
+ {If CLANCY_LABEL or CLANCY_PLAN_LABEL set:}
95
+ ⚠ CLANCY_LABEL and CLANCY_PLAN_LABEL are deprecated. Use CLANCY_LABEL_BUILD and CLANCY_LABEL_PLAN.
96
+
89
97
  {If Planner enabled:}
90
98
  Planner
91
99
  {If Jira:}
@@ -539,6 +547,60 @@ If [2]: remove `CLANCY_COMPONENT` from `.clancy/.env`.
539
547
 
540
548
  ---
541
549
 
550
+ ### [L1] Brief label
551
+
552
+ Only shown when Strategist is enabled.
553
+
554
+ ```
555
+ Brief label — current: {value or "clancy:brief"}
556
+ Label applied to tickets after /clancy:brief. Removed when the brief is approved.
557
+
558
+ [1] clancy:brief (default)
559
+ [2] Enter a different value
560
+ [3] Cancel
561
+ ```
562
+
563
+ If [1]: remove `CLANCY_LABEL_BRIEF` from `.clancy/.env` (uses default).
564
+ If [2]: prompt `What label should /clancy:brief apply?` then write `CLANCY_LABEL_BRIEF=<value>` to `.clancy/.env`. Wrap in double quotes.
565
+
566
+ ---
567
+
568
+ ### [L2] Plan label
569
+
570
+ Only shown when Planner or Strategist is enabled.
571
+
572
+ ```
573
+ Plan label — current: {value or "clancy:plan"}
574
+ Label applied to tickets that need planning. Removed when the plan is approved.
575
+
576
+ [1] clancy:plan (default)
577
+ [2] Enter a different value
578
+ [3] Cancel
579
+ ```
580
+
581
+ If [1]: remove `CLANCY_LABEL_PLAN` from `.clancy/.env` (uses default).
582
+ If [2]: prompt `What label should mark tickets needing planning?` then write `CLANCY_LABEL_PLAN=<value>` to `.clancy/.env`. Wrap in double quotes.
583
+
584
+ ---
585
+
586
+ ### [L3] Build label
587
+
588
+ Only shown when Planner or Strategist is enabled.
589
+
590
+ ```
591
+ Build label — current: {value or "clancy:build"}
592
+ Label applied to tickets ready for implementation. Used by /clancy:once and /clancy:run to filter the queue.
593
+
594
+ [1] clancy:build (default)
595
+ [2] Enter a different value
596
+ [3] Cancel
597
+ ```
598
+
599
+ If [1]: remove `CLANCY_LABEL_BUILD` from `.clancy/.env` (uses default).
600
+ If [2]: prompt `What label should mark tickets ready to build?` then write `CLANCY_LABEL_BUILD=<value>` to `.clancy/.env`. Wrap in double quotes.
601
+
602
+ ---
603
+
542
604
  ### [P1] Plan queue status (Jira only)
543
605
 
544
606
  ```
@@ -218,13 +218,19 @@ Platform-specific pre-creation lookups.
218
218
 
219
219
  ### GitHub
220
220
 
221
+ **Pipeline label for children:** Determine the pipeline label based on planner role and flags:
222
+ - `--skip-plan` flag → `CLANCY_LABEL_BUILD` (default: `clancy:build`)
223
+ - Planner role enabled (`CLANCY_ROLES` includes `planner`) → `CLANCY_LABEL_PLAN` (default: `clancy:plan`)
224
+ - Planner role NOT enabled → `CLANCY_LABEL_BUILD` (default: `clancy:build`)
225
+
221
226
  **Labels to apply per ticket:**
222
- - `CLANCY_PLAN_LABEL` (default: `needs-refinement`) — planning queue
223
- - `CLANCY_LABEL` (if set) — Clancy identifier
227
+ - The pipeline label determined above (`CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD`) — replaces `CLANCY_LABEL` on children
224
228
  - `component:{CLANCY_COMPONENT}` (if `CLANCY_COMPONENT` set)
225
229
  - `size:{S|M|L}` — from decomposition table
226
230
  - `clancy:afk` or `clancy:hitl` — from Mode column
227
231
 
232
+ 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.
233
+
228
234
  **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.
229
235
 
230
236
  ```bash
@@ -266,7 +272,9 @@ Set CLANCY_BRIEF_ISSUE_TYPE in .clancy/.env.
266
272
  ```
267
273
  Stop.
268
274
 
269
- **Labels:** Jira auto-creates labelsno pre-creation needed. Apply: `CLANCY_LABEL` (if set), `clancy:afk` or `clancy:hitl`.
275
+ **Pipeline label for children:** Same logic as GitHub determine the pipeline label from `--skip-plan` flag and planner role.
276
+
277
+ **Labels:** Jira auto-creates labels — no pre-creation needed. Apply: the pipeline label (`CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD`), `clancy:afk` or `clancy:hitl`. `CLANCY_LABEL` is NOT applied to children when pipeline labels are active.
270
278
 
271
279
  **Components:** If `CLANCY_COMPONENT` is set, it maps to the Jira `components` field.
272
280
 
@@ -297,7 +305,9 @@ query {
297
305
  }
298
306
  ```
299
307
 
300
- For each required label (`CLANCY_LABEL`, `component:{CLANCY_COMPONENT}`, `clancy:afk`, `clancy:hitl`): search by exact name. If not found in team labels, check workspace labels:
308
+ **Pipeline label for children:** Same logic as GitHub/Jira determine the pipeline label from `--skip-plan` flag and planner role.
309
+
310
+ 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:
301
311
 
302
312
  ```graphql
303
313
  query {
@@ -370,7 +380,7 @@ curl -s \
370
380
  -d '{
371
381
  "title": "{ticket title}",
372
382
  "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.*",
373
- "labels": ["{CLANCY_PLAN_LABEL}", "size:{size}", "clancy:{mode}", ...],
383
+ "labels": ["{PIPELINE_LABEL}", "size:{size}", "clancy:{mode}", ...],
374
384
  "assignees": ["{resolved_username}"]
375
385
  }'
376
386
  ```
@@ -412,7 +422,7 @@ curl -s \
412
422
  },
413
423
  "issuetype": { "name": "{CLANCY_BRIEF_ISSUE_TYPE or Task}" },
414
424
  "parent": { "key": "{PARENT_KEY}" },
415
- "labels": ["{CLANCY_LABEL}", "clancy:{mode}"]
425
+ "labels": ["{PIPELINE_LABEL}", "clancy:{mode}"]
416
426
  }
417
427
  }'
418
428
  ```
@@ -631,6 +641,60 @@ Created by Clancy on {YYYY-MM-DD}.
631
641
 
632
642
  ---
633
643
 
644
+ ## Step 11a — Remove brief label from parent
645
+
646
+ Only if a parent ticket exists AND all tickets were created successfully. Remove `CLANCY_LABEL_BRIEF` from the parent ticket. Best-effort — warn on failure, never stop.
647
+
648
+ Read `CLANCY_LABEL_BRIEF` from `.clancy/.env`. Default: `clancy:brief`.
649
+
650
+ ### GitHub
651
+
652
+ ```bash
653
+ curl -s \
654
+ -H "Authorization: Bearer $GITHUB_TOKEN" \
655
+ -H "Accept: application/vnd.github+json" \
656
+ -H "X-GitHub-Api-Version: 2022-11-28" \
657
+ -X DELETE \
658
+ "https://api.github.com/repos/$GITHUB_REPO/issues/$PARENT_NUMBER/labels/$(echo $CLANCY_LABEL_BRIEF | jq -Rr @uri)"
659
+ ```
660
+
661
+ Ignore 404 (label may not be present on the parent).
662
+
663
+ ### Jira
664
+
665
+ ```bash
666
+ CURRENT_LABELS=$(curl -s \
667
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
668
+ -H "Accept: application/json" \
669
+ "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY?fields=labels" | jq -r '.fields.labels')
670
+
671
+ UPDATED_LABELS=$(echo "$CURRENT_LABELS" | jq --arg brief "$CLANCY_LABEL_BRIEF" '[.[] | select(. != $brief)]')
672
+
673
+ curl -s \
674
+ -u "$JIRA_USER:$JIRA_API_TOKEN" \
675
+ -X PUT \
676
+ -H "Content-Type: application/json" \
677
+ "$JIRA_BASE_URL/rest/api/3/issue/$PARENT_KEY" \
678
+ -d "{\"fields\": {\"labels\": $UPDATED_LABELS}}"
679
+ ```
680
+
681
+ ### Linear
682
+
683
+ ```bash
684
+ # Fetch current label IDs on the parent, remove the brief label ID, issueUpdate
685
+ # Use the same pattern as brief.md Step 10a — query labels, filter, update
686
+ ```
687
+
688
+ ### On failure
689
+
690
+ ```
691
+ ⚠️ Could not remove brief label from {PARENT_KEY}. Remove it manually if needed.
692
+ ```
693
+
694
+ Continue — do not stop.
695
+
696
+ ---
697
+
634
698
  ## Step 12 — Display summary
635
699
 
636
700
  Show the final result: