@chief-clancy/brief 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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/bin/brief.js CHANGED
@@ -39,6 +39,9 @@ const require = createRequire(import.meta.url);
39
39
  const pkg = require('../package.json');
40
40
 
41
41
  const briefRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
42
+ const scanRoot = join(
43
+ dirname(require.resolve('@chief-clancy/scan/package.json')),
44
+ );
42
45
 
43
46
  const sources = {
44
47
  commandsDir: join(briefRoot, 'src', 'commands'),
@@ -87,6 +90,15 @@ const ask = (label) => new Promise((resolve) => rl.question(label, resolve));
87
90
  const COMMAND_FILES = ['approve-brief.md', 'board-setup.md', 'brief.md'];
88
91
  const WORKFLOW_FILES = ['approve-brief.md', 'board-setup.md', 'brief.md'];
89
92
  const AGENT_FILES = ['devils-advocate.md'];
93
+ const SCAN_AGENT_FILES = [
94
+ 'arch-agent.md',
95
+ 'concerns-agent.md',
96
+ 'design-agent.md',
97
+ 'quality-agent.md',
98
+ 'tech-agent.md',
99
+ ];
100
+ const SCAN_COMMAND_FILES = ['map-codebase.md', 'update-docs.md'];
101
+ const SCAN_WORKFLOW_FILES = ['map-codebase.md', 'update-docs.md'];
90
102
 
91
103
  // ---------------------------------------------------------------------------
92
104
  // Installer
@@ -113,12 +125,13 @@ function copyChecked(src, dest) {
113
125
  function inlineWorkflows(commandsDest, workflowsDest) {
114
126
  const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
115
127
 
116
- COMMAND_FILES.forEach((file) => {
128
+ [...COMMAND_FILES, ...SCAN_COMMAND_FILES].forEach((file) => {
117
129
  const cmdPath = join(commandsDest, file);
118
130
  const content = readFileSync(cmdPath, 'utf8');
119
131
  const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
120
132
  const wfPath = join(workflowsDest, fileName);
121
133
  if (!existsSync(wfPath)) return match;
134
+ rejectSymlink(wfPath);
122
135
  return readFileSync(wfPath, 'utf8');
123
136
  });
124
137
 
@@ -156,39 +169,14 @@ async function chooseMode() {
156
169
  }
157
170
 
158
171
  // ---------------------------------------------------------------------------
159
- // Main
172
+ // Install helpers
160
173
  // ---------------------------------------------------------------------------
161
174
 
162
- async function main() {
163
- console.log('');
164
- console.log(blue(' Clancy Brief'));
165
- console.log(
166
- ` ${bold(`v${pkg.version}`)}${dim(' Strategic brief generator for Claude Code.')}`,
167
- );
168
- console.log('');
169
-
170
- const mode = flag ?? (await chooseMode());
175
+ /** Copy all brief + scan files and write version marker. */
176
+ function installFiles(dest, mode) {
177
+ const { commandsDest, workflowsDest, agentsDest } = dest;
171
178
 
172
- if (flag) {
173
- console.log(dim(` Mode: ${flag} (--${flag} flag)`));
174
- }
175
-
176
- const cwd = process.cwd();
177
- const baseDir =
178
- mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
179
-
180
- const commandsDest = join(baseDir, 'commands', 'clancy');
181
- const workflowsDest = join(baseDir, 'clancy', 'workflows');
182
- const agentsDest = join(baseDir, 'clancy', 'agents');
183
-
184
- console.log(dim(` Installing to: ${commandsDest}`));
185
-
186
- // Create directories
187
- mkdirSync(commandsDest, { recursive: true });
188
- mkdirSync(workflowsDest, { recursive: true });
189
- mkdirSync(agentsDest, { recursive: true });
190
-
191
- // Copy files
179
+ // Brief files
192
180
  COMMAND_FILES.forEach((f) =>
193
181
  copyChecked(join(sources.commandsDir, f), join(commandsDest, f)),
194
182
  );
@@ -199,17 +187,26 @@ async function main() {
199
187
  copyChecked(join(sources.agentsDir, f), join(agentsDest, f)),
200
188
  );
201
189
 
202
- // Inline workflows for global mode
203
- if (mode === 'global') {
204
- inlineWorkflows(commandsDest, workflowsDest);
205
- }
190
+ // Scan files (agents, commands, workflows from @chief-clancy/scan)
191
+ SCAN_AGENT_FILES.forEach((f) =>
192
+ copyChecked(join(scanRoot, 'src', 'agents', f), join(agentsDest, f)),
193
+ );
194
+ SCAN_COMMAND_FILES.forEach((f) =>
195
+ copyChecked(join(scanRoot, 'src', 'commands', f), join(commandsDest, f)),
196
+ );
197
+ SCAN_WORKFLOW_FILES.forEach((f) =>
198
+ copyChecked(join(scanRoot, 'src', 'workflows', f), join(workflowsDest, f)),
199
+ );
200
+
201
+ if (mode === 'global') inlineWorkflows(commandsDest, workflowsDest);
206
202
 
207
- // Write version marker
208
203
  const versionPath = join(commandsDest, 'VERSION.brief');
209
204
  rejectSymlink(versionPath);
210
205
  writeFileSync(versionPath, pkg.version);
206
+ }
211
207
 
212
- // Success
208
+ /** Print the install success output. */
209
+ function printSuccess() {
213
210
  console.log('');
214
211
  console.log(green(' ✓ Clancy Brief installed successfully.'));
215
212
  console.log('');
@@ -223,10 +220,19 @@ async function main() {
223
220
  console.log(
224
221
  ` ${cyan('/clancy:board-setup')} ${dim('Configure board credentials (optional)')}`,
225
222
  );
223
+ console.log(
224
+ ` ${cyan('/clancy:map-codebase')} ${dim('Scan codebase and generate .clancy/docs/')}`,
225
+ );
226
+ console.log(
227
+ ` ${cyan('/clancy:update-docs')} ${dim('Refresh .clancy/docs/ incrementally')}`,
228
+ );
226
229
  console.log('');
227
230
  console.log(' Next steps:');
228
231
  console.log(` 1. Open a project in Claude Code`);
229
- console.log(` 2. Run: ${cyan('/clancy:brief "Your feature idea"')}`);
232
+ console.log(
233
+ ` 2. Optional: ${cyan('/clancy:map-codebase')} ${dim('for richer briefs')}`,
234
+ );
235
+ console.log(` 3. Run: ${cyan('/clancy:brief "Your feature idea"')}`);
230
236
  console.log('');
231
237
  console.log(dim(' Want to brief from board tickets?'));
232
238
  console.log(dim(` Run: ${cyan('/clancy:board-setup')}`));
@@ -236,6 +242,42 @@ async function main() {
236
242
  );
237
243
  console.log(dim(` npx chief-clancy`));
238
244
  console.log('');
245
+ }
246
+
247
+ // ---------------------------------------------------------------------------
248
+ // Main
249
+ // ---------------------------------------------------------------------------
250
+
251
+ async function main() {
252
+ console.log('');
253
+ console.log(blue(' Clancy Brief'));
254
+ console.log(
255
+ ` ${bold(`v${pkg.version}`)}${dim(' Strategic brief generator for Claude Code.')}`,
256
+ );
257
+ console.log('');
258
+
259
+ const mode = flag ?? (await chooseMode());
260
+
261
+ if (flag) {
262
+ console.log(dim(` Mode: ${flag} (--${flag} flag)`));
263
+ }
264
+
265
+ const cwd = process.cwd();
266
+ const baseDir =
267
+ mode === 'global' ? join(homeDir, '.claude') : join(cwd, '.claude');
268
+
269
+ const commandsDest = join(baseDir, 'commands', 'clancy');
270
+ const workflowsDest = join(baseDir, 'clancy', 'workflows');
271
+ const agentsDest = join(baseDir, 'clancy', 'agents');
272
+
273
+ console.log(dim(` Installing to: ${commandsDest}`));
274
+
275
+ mkdirSync(commandsDest, { recursive: true });
276
+ mkdirSync(workflowsDest, { recursive: true });
277
+ mkdirSync(agentsDest, { recursive: true });
278
+
279
+ installFiles({ commandsDest, workflowsDest, agentsDest }, mode);
280
+ printSuccess();
239
281
 
240
282
  rl.close();
241
283
  }
@@ -11,6 +11,9 @@ type BriefInstallSources = {
11
11
  readonly commandsDir: string;
12
12
  readonly workflowsDir: string;
13
13
  readonly agentsDir: string;
14
+ readonly scanAgentsDir: string;
15
+ readonly scanCommandsDir: string;
16
+ readonly scanWorkflowsDir: string;
14
17
  };
15
18
  /**
16
19
  * File system operations the installer performs.
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"AAaA,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,iDAAiD;AACjD,KAAK,mBAAmB,GAAG;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AA8BF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,SAAS,MAAM,EAAE,KACtB,gBAAgB,GAAG,IAKrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,gBAAgB,EACtB,SAAS,MAAM,EACf,KAAK,MAAM,KACV,iBASF,CAAC;AA4EF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,IA6BjE,CAAC"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"AAaA,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,iDAAiD;AACjD,KAAK,mBAAmB,GAAG;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF;;;;GAIG;AACH,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AA6CF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,SAAS,MAAM,EAAE,KACtB,gBAAgB,GAAG,IAKrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GACnC,MAAM,gBAAgB,EACtB,SAAS,MAAM,EACf,KAAK,MAAM,KACV,iBASF,CAAC;AA+EF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,IA+CjE,CAAC"}
@@ -23,6 +23,18 @@ const WORKFLOW_FILES = [
23
23
  ];
24
24
  /** Agent files shipped with the brief package. */
25
25
  const AGENT_FILES = ['devils-advocate.md'];
26
+ /** Scan agent files from @chief-clancy/scan. */
27
+ const SCAN_AGENT_FILES = [
28
+ 'arch-agent.md',
29
+ 'concerns-agent.md',
30
+ 'design-agent.md',
31
+ 'quality-agent.md',
32
+ 'tech-agent.md',
33
+ ];
34
+ /** Scan command files from @chief-clancy/scan. */
35
+ const SCAN_COMMAND_FILES = ['map-codebase.md', 'update-docs.md'];
36
+ /** Scan workflow files from @chief-clancy/scan. */
37
+ const SCAN_WORKFLOW_FILES = ['map-codebase.md', 'update-docs.md'];
26
38
  /** Matches `@.claude/clancy/workflows/<filename>.md` on its own line. */
27
39
  const WORKFLOW_REF = /^@\.claude\/clancy\/workflows\/([^/\\]+\.md)\r?$/gm;
28
40
  // ---------------------------------------------------------------------------
@@ -69,6 +81,7 @@ const rejectSymlink = (path, isSymlink) => {
69
81
  /** Copy a list of files from src dir to dest dir with symlink protection. */
70
82
  const copyFiles = (options) => {
71
83
  const { files, srcDir, destDir, fs } = options;
84
+ rejectSymlink(destDir, fs.isSymlink);
72
85
  fs.mkdir(destDir);
73
86
  files.forEach((file) => {
74
87
  const src = join(srcDir, file);
@@ -88,12 +101,15 @@ const copyFiles = (options) => {
88
101
  * @-file resolution.
89
102
  */
90
103
  const inlineWorkflow = (commandsDest, workflowsDest, fs) => {
91
- COMMAND_FILES.forEach((file) => {
104
+ [...COMMAND_FILES, ...SCAN_COMMAND_FILES].forEach((file) => {
92
105
  const cmdPath = join(commandsDest, file);
93
106
  const content = fs.readFile(cmdPath);
94
107
  const resolved = content.replace(WORKFLOW_REF, (match, fileName) => {
95
108
  const wfPath = join(workflowsDest, fileName);
96
- return fs.exists(wfPath) ? fs.readFile(wfPath) : match;
109
+ rejectSymlink(wfPath, fs.isSymlink);
110
+ if (!fs.exists(wfPath))
111
+ return match;
112
+ return fs.readFile(wfPath);
97
113
  });
98
114
  if (resolved !== content) {
99
115
  rejectSymlink(cmdPath, fs.isSymlink);
@@ -133,6 +149,24 @@ export const runBriefInstall = (options) => {
133
149
  destDir: paths.agentsDest,
134
150
  fs,
135
151
  });
152
+ copyFiles({
153
+ files: SCAN_AGENT_FILES,
154
+ srcDir: sources.scanAgentsDir,
155
+ destDir: paths.agentsDest,
156
+ fs,
157
+ });
158
+ copyFiles({
159
+ files: SCAN_COMMAND_FILES,
160
+ srcDir: sources.scanCommandsDir,
161
+ destDir: paths.commandsDest,
162
+ fs,
163
+ });
164
+ copyFiles({
165
+ files: SCAN_WORKFLOW_FILES,
166
+ srcDir: sources.scanWorkflowsDir,
167
+ destDir: paths.workflowsDest,
168
+ fs,
169
+ });
136
170
  if (mode === 'global') {
137
171
  inlineWorkflow(paths.commandsDest, paths.workflowsDest, fs);
138
172
  }
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkDjC,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,oDAAoD;AACpD,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,gBAAgB;IAChB,UAAU;CACF,CAAC;AAEX,qDAAqD;AACrD,MAAM,cAAc,GAAG;IACrB,kBAAkB;IAClB,gBAAgB;IAChB,UAAU;CACF,CAAC;AAEX,kDAAkD;AAClD,MAAM,WAAW,GAAG,CAAC,oBAAoB,CAAU,CAAC;AAEpD,gDAAgD;AAChD,MAAM,gBAAgB,GAAG;IACvB,eAAe;IACf,mBAAmB;IACnB,iBAAiB;IACjB,kBAAkB;IAClB,eAAe;CACP,CAAC;AAEX,kDAAkD;AAClD,MAAM,kBAAkB,GAAG,CAAC,iBAAiB,EAAE,gBAAgB,CAAU,CAAC;AAE1E,mDAAmD;AACnD,MAAM,mBAAmB,GAAG,CAAC,iBAAiB,EAAE,gBAAgB,CAAU,CAAC;AAE3E,yEAAyE;AACzE,MAAM,YAAY,GAAG,oDAAoD,CAAC;AAE1E,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,IAAuB,EACE,EAAE;IAC3B,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/C,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,OAAO,CAAC;IAE7C,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,IAAsB,EACtB,OAAe,EACf,GAAW,EACQ,EAAE;IACrB,MAAM,OAAO,GACX,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAEtE,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC;QACjD,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;QACnD,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;KAC9C,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,kEAAkE;AAClE,MAAM,aAAa,GAAG,CACpB,IAAY,EACZ,SAAiC,EAC3B,EAAE;IACR,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC,CAAC;AASF,6EAA6E;AAC7E,MAAM,SAAS,GAAG,CAAC,OAAyB,EAAQ,EAAE;IACpD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC;IAC/C,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;IACrC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAElB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAEjC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;QAClC,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,CACrB,YAAoB,EACpB,aAAqB,EACrB,EAAoB,EACd,EAAE;IACR,CAAC,GAAG,aAAa,EAAE,GAAG,kBAAkB,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAC9B,YAAY,EACZ,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YAC7C,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;gBAAE,OAAO,KAAK,CAAC;YAErC,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC,CACF,CAAC;QAEF,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;YACrC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAA+B,EAAQ,EAAE;IACvE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC;IAEtD,SAAS,CAAC;QACR,KAAK,EAAE,aAAa;QACpB,MAAM,EAAE,OAAO,CAAC,WAAW;QAC3B,OAAO,EAAE,KAAK,CAAC,YAAY;QAC3B,EAAE;KACH,CAAC,CAAC;IACH,SAAS,CAAC;QACR,KAAK,EAAE,cAAc;QACrB,MAAM,EAAE,OAAO,CAAC,YAAY;QAC5B,OAAO,EAAE,KAAK,CAAC,aAAa;QAC5B,EAAE;KACH,CAAC,CAAC;IACH,SAAS,CAAC;QACR,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,OAAO,CAAC,SAAS;QACzB,OAAO,EAAE,KAAK,CAAC,UAAU;QACzB,EAAE;KACH,CAAC,CAAC;IACH,SAAS,CAAC;QACR,KAAK,EAAE,gBAAgB;QACvB,MAAM,EAAE,OAAO,CAAC,aAAa;QAC7B,OAAO,EAAE,KAAK,CAAC,UAAU;QACzB,EAAE;KACH,CAAC,CAAC;IACH,SAAS,CAAC;QACR,KAAK,EAAE,kBAAkB;QACzB,MAAM,EAAE,OAAO,CAAC,eAAe;QAC/B,OAAO,EAAE,KAAK,CAAC,YAAY;QAC3B,EAAE;KACH,CAAC,CAAC;IACH,SAAS,CAAC;QACR,KAAK,EAAE,mBAAmB;QAC1B,MAAM,EAAE,OAAO,CAAC,gBAAgB;QAChC,OAAO,EAAE,KAAK,CAAC,aAAa;QAC5B,EAAE;KACH,CAAC,CAAC;IAEH,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,cAAc,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAC9D,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;IACzC,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC,CAAC"}
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.1",
4
4
  "description": "Strategic brief generator for Claude Code — grill, decompose, and document feature ideas",
5
5
  "author": "Alex Clapperton",
6
6
  "license": "MIT",
@@ -39,6 +39,9 @@
39
39
  "src/workflows",
40
40
  "src/agents"
41
41
  ],
42
+ "dependencies": {
43
+ "@chief-clancy/scan": "0.2.0"
44
+ },
42
45
  "scripts": {
43
46
  "build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json",
44
47
  "test": "vitest run",
@@ -8,7 +8,7 @@ You receive 10-15 clarifying questions generated during the AI-grill phase of `/
8
8
 
9
9
  1. Work through each question one at a time. For every question, investigate before answering — never guess.
10
10
  2. Interrogate three sources in order of preference:
11
- - **Codebase**: use Glob, Grep, and Read to explore affected areas, check `.clancy/docs/`, read existing patterns. Use real file paths and code snippets as evidence.
11
+ - **Codebase**: use Glob, Grep, and Read to explore affected areas, check `.clancy/docs/` if available, read existing patterns. Use real file paths and code snippets as evidence.
12
12
  - **Board**: check the parent ticket, related tickets, and existing children for context. Look for conflicting requirements.
13
13
  - **Web**: when the question involves external technology, third-party integrations, or industry patterns, use WebSearch and WebFetch.
14
14
  3. Challenge your own answers. If the codebase says one thing but the ticket description says another, flag the conflict — do not silently pick one.
@@ -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
 
@@ -582,7 +582,7 @@ Same relentless energy as the human grill, but directed at the strategist itself
582
582
  - The path to the agent prompt: `.claude/clancy/agents/devils-advocate.md`
583
583
 
584
584
  3. The devil's advocate agent answers each question by INTERROGATING ITS SOURCES:
585
- - **Codebase:** explore affected areas, read `.clancy/docs/`, check existing patterns. Don't assume — look.
585
+ - **Codebase:** explore affected areas, read `.clancy/docs/` if available, check existing patterns. Don't assume — look.
586
586
  - **Board:** parent ticket, related tickets, existing children. Check for conflicting requirements.
587
587
  - **Web:** when the question involves external technology, patterns, or third-party integrations. Same trigger as Step 6: `--research` flag forces it, otherwise judgement-based.
588
588
 
@@ -695,7 +695,7 @@ Web research agent (adds 1 to the count above, max 4 total):
695
695
 
696
696
  ### What agents explore
697
697
 
698
- - `.clancy/docs/` — STACK.md, ARCHITECTURE.md, CONVENTIONS.md, TESTING.md, DESIGN-SYSTEM.md
698
+ - `.clancy/docs/` (if available) — STACK.md, ARCHITECTURE.md, CONVENTIONS.md, TESTING.md, DESIGN-SYSTEM.md
699
699
  - Affected code areas via Glob + Read
700
700
  - Board for duplicates/related tickets (text-match against title):
701
701
  - **GitHub:** `GET /repos/$GITHUB_REPO/issues?state=open&per_page=30` and text-match
@@ -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
  // ---------------------------------------------------------------------------