@chief-clancy/brief 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -52,15 +52,36 @@ Briefs are saved to `.clancy/briefs/` in your project.
52
52
 
53
53
  ## Board ticket mode (optional)
54
54
 
55
- To brief from board tickets and push approved briefs back to the board without installing the full pipeline:
55
+ To brief from board tickets without installing the full pipeline:
56
56
 
57
57
  1. Run `/clancy:board-setup` in Claude Code
58
58
  2. Follow the prompts to configure your board credentials
59
- 3. Run `/clancy:brief #42` (or your board's ticket format) to brief from an existing ticket
60
- 4. Run `/clancy:approve-brief <slug>` to convert an approved brief into child tickets on your board (creates one ticket per row in the brief's decomposition table, links dependencies, and posts a tracking summary)
59
+ 3. Run `/clancy:brief #42` (or your board's ticket format)
61
60
 
62
61
  Credentials are stored in `.clancy/.env` and are per-project (not global).
63
62
 
63
+ Supported boards: Jira, GitHub Issues, Linear, Shortcut, Notion, Azure DevOps.
64
+
65
+ ## Approving briefs
66
+
67
+ The brief package ships `/clancy:approve-brief` so the approval gate works without the full pipeline. The behaviour depends on the install mode.
68
+
69
+ ### Standalone (no board)
70
+
71
+ `/clancy:approve-brief` requires board credentials. Approve-brief's job is to create child tickets ON the board, so without a board there is nothing for it to do. Run `/clancy:board-setup` first to configure your board, then re-run `/clancy:approve-brief`.
72
+
73
+ ### Standalone+board (board credentials but no full pipeline)
74
+
75
+ `/clancy:approve-brief <slug>` walks the brief's decomposition table, creates one child ticket per row on the board (in topological/dependency order), links dependencies, and posts a tracking summary as a comment on the parent ticket. Each child ticket gets a pipeline label so downstream queue commands (`/clancy:plan`, `/clancy:implement`) know which queue picks it up.
76
+
77
+ In standalone+board mode, child tickets default to `CLANCY_LABEL_PLAN` (default `clancy:plan`), even though `CLANCY_ROLES` is unset — passing `--skip-plan` overrides this and forces `CLANCY_LABEL_BUILD` (default `clancy:build`) instead. This is the **single-source-of-truth pipeline label rule**: standalone+board users have installed both `@chief-clancy/brief` and `@chief-clancy/plan` as standalone packages and clearly intend to use plan, so the workflow defaults to the planning queue rather than the build queue.
78
+
79
+ Partial failures stop immediately. The brief file's approve marker tracks which rows already shipped to the board, so re-running `/clancy:approve-brief` resumes from where the previous run stopped — it never duplicates a ticket.
80
+
81
+ ### Terminal mode (full pipeline)
82
+
83
+ Existing behaviour, unchanged. The pipeline label respects `CLANCY_ROLES`: if `planner` is enabled (or `CLANCY_ROLES` is unset, indicating a global install), child tickets get `CLANCY_LABEL_PLAN`; if `planner` is explicitly excluded, child tickets get `CLANCY_LABEL_BUILD` (the terminal user has opted out of the planning queue). The `--skip-plan` flag overrides both and forces the build label.
84
+
64
85
  ## Full pipeline
65
86
 
66
87
  `@chief-clancy/brief` covers brief generation and ticket creation from briefs. For planning and the full development pipeline (autopilot, implementation, review), install the complete Clancy package:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chief-clancy/brief",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Strategic brief generator for Claude Code — grill, decompose, and document feature ideas",
5
5
  "author": "Alex Clapperton",
6
6
  "license": "MIT",
@@ -8,39 +8,60 @@ Approve a reviewed strategic brief by creating child tickets on the board, linki
8
8
 
9
9
  ## Step 1 — Preflight checks
10
10
 
11
- 1. Check `.clancy/` exists and `.clancy/.env` is present. If not:
11
+ ### 1. Detect installation context
12
12
 
13
- ```
14
- .clancy/ not found. Run /clancy:init to set up Clancy first.
15
- ```
13
+ Check for `.clancy/.env`:
16
14
 
17
- Stop.
15
+ - **Absent** → **standalone mode**. The brief package has been installed via `npx @chief-clancy/brief` but board credentials have never been configured. There is nothing approve-brief can do without a board (its job is to create tickets ON the board), so stop with:
18
16
 
19
- 2. Source `.clancy/.env` and check board credentials are present.
17
+ ```
18
+ No board credentials found. Run /clancy:board-setup first to configure your board, then re-run /clancy:approve-brief.
19
+ ```
20
20
 
21
- 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`:
21
+ Stop.
22
22
 
23
- ```
24
- The Strategist role is not enabled. Add "strategist" to CLANCY_ROLES in .clancy/.env or run /clancy:settings.
25
- ```
23
+ - **Present** → continue to `.clancy/clancy-implement.js` check below.
26
24
 
27
- Stop.
25
+ If `.clancy/.env` is present, check for `.clancy/clancy-implement.js`:
28
26
 
29
- 4. Branch freshness check:
27
+ - **Present** **terminal mode**. Full Clancy pipeline installed.
28
+ - **Absent** → **standalone+board mode**. Board credentials available via `/clancy:board-setup`. Brief and plan are installed as standalone packages but the full pipeline (`clancy-implement.js`) is not.
30
29
 
31
- ```bash
32
- git fetch origin
33
- ```
30
+ The detected install mode is captured for Step 6's pipeline label decision.
34
31
 
35
- Compare HEAD with `origin/$CLANCY_BASE_BRANCH`. If behind:
32
+ ### 2. Terminal-mode preflight (skip in standalone+board mode)
36
33
 
37
- **AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): auto-pull without prompting.
34
+ If in **terminal mode** (`.clancy/.env` present AND `.clancy/clancy-implement.js` present):
38
35
 
39
- **Interactive mode:**
36
+ a. Source `.clancy/.env` and check board credentials are present.
40
37
 
41
- ```
42
- Behind by N commits. [1] Pull latest [2] Continue [3] Abort
43
- ```
38
+ b. 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`:
39
+
40
+ ```
41
+ The Strategist role is not enabled. Add "strategist" to CLANCY_ROLES in .clancy/.env or run /clancy:settings.
42
+ ```
43
+
44
+ Stop.
45
+
46
+ ### 3. Standalone+board preflight (only in standalone+board mode)
47
+
48
+ If in **standalone+board mode**, source `.clancy/.env` and check board credentials are present (same presence check as the terminal-mode preflight above — an empty or partial `.clancy/.env` should fail loudly here, not later in Step 6/7). Standalone+board users came in via `npx @chief-clancy/brief` and `/clancy:board-setup`, so they have no `CLANCY_ROLES` configured and the strategist role check above does not apply.
49
+
50
+ ### 4. Branch freshness check (all modes)
51
+
52
+ ```bash
53
+ git fetch origin
54
+ ```
55
+
56
+ Compare HEAD with `origin/$CLANCY_BASE_BRANCH`. If behind:
57
+
58
+ **AFK mode** (`--afk` flag or `CLANCY_MODE=afk`): auto-pull without prompting.
59
+
60
+ **Interactive mode:**
61
+
62
+ ```
63
+ Behind by N commits. [1] Pull latest [2] Continue [3] Abort
64
+ ```
44
65
 
45
66
  ---
46
67
 
@@ -298,14 +319,20 @@ Continue — omit parent fields from creation payloads. No tracking comment will
298
319
 
299
320
  Platform-specific pre-creation lookups.
300
321
 
301
- ### GitHub
322
+ ### Pipeline label selection rule (applies to all six platforms below)
323
+
324
+ Every child ticket gets exactly one pipeline label. The label determines which queue picks the ticket up downstream (`/clancy:plan` for the planning queue, `/clancy:implement` for the build queue). The selection rule, in order of precedence:
302
325
 
303
- **Pipeline label for children this is mandatory, always apply a pipeline label to every child ticket.** Determine which label to use:
326
+ 1. `--skip-plan` flag is set use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`. The user has explicitly opted out of the planning queue for this run.
327
+ 2. Install mode is **standalone+board** (Step 1 detected this) → use `CLANCY_LABEL_PLAN` from `.clancy/.env` if set, otherwise `clancy:plan`. Standalone+board users have installed both `@chief-clancy/brief` and `@chief-clancy/plan` as standalone packages and clearly intend to use plan, even though they have no `CLANCY_ROLES` configured.
328
+ 3. Install mode is **terminal** AND `CLANCY_ROLES` includes `planner` (or `CLANCY_ROLES` is unset, indicating a global install where all roles are available) → use `CLANCY_LABEL_PLAN` from `.clancy/.env` if set, otherwise `clancy:plan`.
329
+ 4. Install mode is **terminal** AND `CLANCY_ROLES` is set but does NOT include `planner` → use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`. The terminal user has explicitly opted out of the planning queue by not enabling the planner role.
330
+
331
+ This rule replaces the per-platform fallthrough that used to live in each of the six platform subsections below. Each subsection now applies the rule rather than re-enumerating it. Apply the chosen label to each child ticket, creating it first only on platforms that require explicit label creation (the per-platform subsections below document which platforms auto-create vs require pre-creation).
332
+
333
+ ### GitHub
304
334
 
305
- - `--skip-plan` flag use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`
306
- - Planner role enabled (`CLANCY_ROLES` includes `planner`) → use `CLANCY_LABEL_PLAN` from `.clancy/.env` if set, otherwise `clancy:plan`
307
- - Planner role NOT enabled → use `CLANCY_LABEL_BUILD` from `.clancy/.env` if set, otherwise `clancy:build`
308
- Ensure the label exists on the board (create it if missing), then add it to each child ticket.
335
+ **Pipeline label for children mandatory.** Apply the pipeline label per the rule above to every child ticket.
309
336
 
310
337
  **Labels to apply per ticket:**
311
338
 
@@ -362,7 +389,7 @@ Set CLANCY_BRIEF_ISSUE_TYPE in .clancy/.env.
362
389
 
363
390
  Stop.
364
391
 
365
- **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.
392
+ **Pipeline label for children — mandatory.** Apply the pipeline label per the rule at the top of Step 6. Defaults: `clancy:plan` / `clancy:build`. Always apply to every child ticket.
366
393
 
367
394
  **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.
368
395
 
@@ -404,7 +431,7 @@ query {
404
431
  }
405
432
  ```
406
433
 
407
- **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.
434
+ **Pipeline label for children — mandatory.** Apply the pipeline label per the rule at the top of Step 6. Defaults: `clancy:plan` / `clancy:build`. Always apply to every child ticket.
408
435
 
409
436
  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:
410
437
 
@@ -448,7 +475,7 @@ curl -s \
448
475
 
449
476
  If the specified type is not found, stop with the available types listed.
450
477
 
451
- **Pipeline tag for children — mandatory, same logic as other boards.** Use `CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD` from `.clancy/.env` (defaults: `clancy:plan` / `clancy:build`). Tags are applied via the `System.Tags` field (semicolon-delimited).
478
+ **Pipeline tag for children — mandatory.** Apply the pipeline tag determined by the rule at the top of Step 6. Defaults: `clancy:plan` / `clancy:build`. Tags are applied via the `System.Tags` field (semicolon-delimited).
452
479
 
453
480
  **Tags:** Azure DevOps auto-creates tags — no pre-creation needed. Apply: the pipeline tag, `clancy:afk` or `clancy:hitl`. `CLANCY_LABEL` is NOT applied to children when pipeline tags are active.
454
481
 
@@ -467,7 +494,7 @@ Use `authenticatedUser.providerDisplayName` for the `System.AssignedTo` field.
467
494
 
468
495
  **Story type:** Shortcut creates stories by default. Use `story_type` field if needed (`feature`, `bug`, `chore` — default: `feature`).
469
496
 
470
- **Pipeline label for children — mandatory, same logic as other boards.** Use `CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD` from `.clancy/.env` (defaults: `clancy:plan` / `clancy:build`).
497
+ **Pipeline label for children — mandatory.** Apply the pipeline label per the rule at the top of Step 6. Defaults: `clancy:plan` / `clancy:build`.
471
498
 
472
499
  **Labels:** Resolve label IDs via `GET /api/v3/labels`. If the required label does not exist, create it:
473
500
 
@@ -494,7 +521,7 @@ Use `id` as the `owner_ids` value for story creation.
494
521
 
495
522
  ### Notion
496
523
 
497
- **Pipeline label for children — mandatory, same logic as other boards.** Use `CLANCY_LABEL_PLAN` or `CLANCY_LABEL_BUILD` from `.clancy/.env` (defaults: `clancy:plan` / `clancy:build`). Labels are applied via multi-select properties.
524
+ **Pipeline label for children — mandatory.** Apply the pipeline label per the rule at the top of Step 6. Defaults: `clancy:plan` / `clancy:build`. Labels are applied via multi-select properties.
498
525
 
499
526
  **Labels:** Notion auto-creates multi-select options when first used — no pre-creation needed. Apply: the pipeline label, `clancy:afk` or `clancy:hitl`. `CLANCY_LABEL` is NOT applied to children when pipeline labels are active.
500
527
 
@@ -55,6 +55,204 @@ describe('workflows directory structure', () => {
55
55
  });
56
56
  });
57
57
 
58
+ // ---------------------------------------------------------------------------
59
+ // approve-brief Step 1 install-mode preflight assertions
60
+ // ---------------------------------------------------------------------------
61
+
62
+ describe('approve-brief Step 1 install-mode preflight', () => {
63
+ const content = readFileSync(
64
+ new URL('approve-brief.md', import.meta.url),
65
+ 'utf8',
66
+ );
67
+
68
+ it('detects three installation states', () => {
69
+ expect(content).toContain('standalone mode');
70
+ expect(content).toContain('standalone+board mode');
71
+ expect(content).toContain('terminal mode');
72
+ });
73
+
74
+ it('uses the same env-var probes as approve-plan and plan workflows', () => {
75
+ // Schema-pair contract: must mirror approve-plan.md and plan.md exactly.
76
+ expect(content).toContain('.clancy/.env');
77
+ expect(content).toContain('.clancy/clancy-implement.js');
78
+ });
79
+
80
+ it('hard-stops in standalone mode with the board-setup message', () => {
81
+ expect(content).toContain('No board credentials found');
82
+ expect(content).toContain('Run /clancy:board-setup first');
83
+ });
84
+
85
+ it('does NOT use the old /clancy:init standalone hard-stop', () => {
86
+ expect(content).not.toContain('Run /clancy:init to set up Clancy first');
87
+ });
88
+
89
+ it('terminal-mode preflight gates the strategist role check', () => {
90
+ expect(content).toContain(
91
+ '### 2. Terminal-mode preflight (skip in standalone+board mode)',
92
+ );
93
+ expect(content).toContain('CLANCY_ROLES` includes `strategist`');
94
+ });
95
+
96
+ it('standalone+board preflight notes the strategist role check does not apply', () => {
97
+ expect(content).toContain(
98
+ '### 3. Standalone+board preflight (only in standalone+board mode)',
99
+ );
100
+ expect(content).toContain('strategist role check above does not apply');
101
+ });
102
+
103
+ it('captures the install mode for Step 6 to read', () => {
104
+ expect(content).toContain(
105
+ "The detected install mode is captured for Step 6's pipeline label decision",
106
+ );
107
+ });
108
+ });
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // approve-brief Step 6 pipeline label selection rule
112
+ // ---------------------------------------------------------------------------
113
+
114
+ describe('approve-brief Step 6 pipeline label selection rule', () => {
115
+ const content = readFileSync(
116
+ new URL('approve-brief.md', import.meta.url),
117
+ 'utf8',
118
+ );
119
+
120
+ it('has the preamble heading at the top of Step 6', () => {
121
+ expect(content).toContain(
122
+ '### Pipeline label selection rule (applies to all six platforms below)',
123
+ );
124
+ });
125
+
126
+ it('preamble enumerates rule 1 — --skip-plan flag uses build label', () => {
127
+ expect(content).toMatch(
128
+ /1\. `--skip-plan` flag is set → use `CLANCY_LABEL_BUILD`/,
129
+ );
130
+ });
131
+
132
+ it('preamble enumerates rule 2 — standalone+board uses plan label', () => {
133
+ expect(content).toMatch(
134
+ /2\. Install mode is \*\*standalone\+board\*\*[^\n]*→ use `CLANCY_LABEL_PLAN`/,
135
+ );
136
+ });
137
+
138
+ it('preamble enumerates rule 3 — terminal + planner enabled uses plan label', () => {
139
+ expect(content).toMatch(
140
+ /3\. Install mode is \*\*terminal\*\* AND `CLANCY_ROLES` includes `planner`[^\n]*→ use `CLANCY_LABEL_PLAN`/,
141
+ );
142
+ });
143
+
144
+ it('preamble enumerates rule 4 — terminal + planner not enabled uses build label', () => {
145
+ expect(content).toMatch(
146
+ /4\. Install mode is \*\*terminal\*\* AND `CLANCY_ROLES` is set but does NOT include `planner` → use `CLANCY_LABEL_BUILD`/,
147
+ );
148
+ });
149
+
150
+ it('GitHub subsection delegates to the preamble (no inline 3-rule fallthrough)', () => {
151
+ expect(content).toContain(
152
+ 'Apply the pipeline label per the rule above to every child ticket',
153
+ );
154
+ // The old per-platform fallthrough must be gone — no leftover
155
+ // "Planner role enabled" / "Planner role NOT enabled" lines anywhere.
156
+ expect(content).not.toContain('Planner role enabled');
157
+ expect(content).not.toContain('Planner role NOT enabled');
158
+ });
159
+
160
+ it('all five non-GitHub platform subsections delegate to the preamble', () => {
161
+ // Each non-GitHub platform must reference the preamble explicitly
162
+ // and must NOT use the old "same logic as GitHub/other boards" wording.
163
+ expect(content).not.toContain('same logic as GitHub');
164
+ expect(content).not.toContain('same logic as other boards');
165
+
166
+ // GitHub uses "above"; the other 5 use "at the top of Step 6". Of those
167
+ // 5, four are "pipeline label per the rule" (Jira, Linear, Shortcut,
168
+ // Notion) and one is "pipeline tag determined by the rule" (Azure
169
+ // DevOps, which uses System.Tags rather than labels).
170
+ const labelDelegations = content.match(
171
+ /Apply the pipeline label per the rule at the top of Step 6/g,
172
+ );
173
+ const tagDelegations = content.match(
174
+ /Apply the pipeline tag determined by the rule at the top of Step 6/g,
175
+ );
176
+
177
+ expect(labelDelegations).not.toBeNull();
178
+ expect(labelDelegations?.length).toBe(4);
179
+ expect(tagDelegations).not.toBeNull();
180
+ expect(tagDelegations?.length).toBe(1);
181
+ });
182
+ });
183
+
184
+ // ---------------------------------------------------------------------------
185
+ // approve-brief test permissiveness audit
186
+ // ---------------------------------------------------------------------------
187
+
188
+ describe('approve-brief test regex permissiveness audit', () => {
189
+ // These tests guard against the `\\?d` class of trap from
190
+ // feedback_workflow_md_gotchas.md by walking through the simplest wrong
191
+ // inputs that the regexes above SHOULD reject.
192
+ const content = readFileSync(
193
+ new URL('approve-brief.md', import.meta.url),
194
+ 'utf8',
195
+ );
196
+
197
+ /**
198
+ * Slice the workflow content between two literal markers, asserting both
199
+ * markers are present AND the end marker comes after the start marker.
200
+ * Without these guards a missing marker would return -1 from indexOf,
201
+ * which slice() interprets as a negative index — that produces a
202
+ * substring counted from the END of the file and silently passes
203
+ * (or silently fails) the body assertions for the wrong reason. Per
204
+ * Copilot finding on PR #222 + the test permissiveness audit discipline.
205
+ */
206
+ const sliceBetween = (start: string, end: string): string => {
207
+ const startIdx = content.indexOf(start);
208
+ const endIdx = content.indexOf(end);
209
+
210
+ expect(startIdx, `start marker not found: ${start}`).toBeGreaterThanOrEqual(
211
+ 0,
212
+ );
213
+ expect(endIdx, `end marker not found: ${end}`).toBeGreaterThanOrEqual(0);
214
+ expect(endIdx, `end marker before start marker`).toBeGreaterThan(startIdx);
215
+
216
+ return content.slice(startIdx, endIdx);
217
+ };
218
+
219
+ it('rule-2 body does NOT contain a swapped label', () => {
220
+ // Sanity check: rule 2 must reference CLANCY_LABEL_PLAN, not _BUILD.
221
+ // If someone accidentally swapped the labels in the preamble, the
222
+ // existing rule-2 assertion above would still match the heading; this
223
+ // assertion makes the swap fail loudly.
224
+ const rule2ToRule3 = sliceBetween(
225
+ '2. Install mode is **standalone+board**',
226
+ '3. Install mode is **terminal** AND `CLANCY_ROLES` includes `planner`',
227
+ );
228
+
229
+ expect(rule2ToRule3).not.toContain('CLANCY_LABEL_BUILD');
230
+ });
231
+
232
+ it('rule-3 body does NOT contain a swapped label', () => {
233
+ // Symmetric to rule-2 / rule-4. Rule 3 must reference CLANCY_LABEL_PLAN.
234
+ const rule3ToRule4 = sliceBetween(
235
+ '3. Install mode is **terminal** AND `CLANCY_ROLES` includes `planner`',
236
+ '4. Install mode is **terminal** AND `CLANCY_ROLES` is set but does NOT include `planner`',
237
+ );
238
+
239
+ expect(rule3ToRule4).not.toContain('CLANCY_LABEL_BUILD');
240
+ });
241
+
242
+ it('rule-4 body does NOT contain a swapped label', () => {
243
+ // Symmetric to the rule-2 check: rule 4 must reference
244
+ // CLANCY_LABEL_BUILD, not _PLAN. Slice from rule-4 to the rule-block
245
+ // terminator paragraph (the "This rule replaces..." line that follows
246
+ // rule 4 in the preamble).
247
+ const rule4ToTerminator = sliceBetween(
248
+ '4. Install mode is **terminal** AND `CLANCY_ROLES` is set but does NOT include `planner`',
249
+ 'This rule replaces the per-platform fallthrough',
250
+ );
251
+
252
+ expect(rule4ToTerminator).not.toContain('CLANCY_LABEL_PLAN');
253
+ });
254
+ });
255
+
58
256
  // ---------------------------------------------------------------------------
59
257
  // board-setup.md content assertions
60
258
  // ---------------------------------------------------------------------------