@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 +24 -3
- package/bin/brief.js +80 -38
- package/dist/installer/install.d.ts +3 -0
- package/dist/installer/install.d.ts.map +1 -1
- package/dist/installer/install.js +36 -2
- package/dist/installer/install.js.map +1 -1
- package/package.json +4 -1
- package/src/agents/devils-advocate.md +1 -1
- package/src/workflows/approve-brief.md +59 -32
- package/src/workflows/brief.md +2 -2
- package/src/workflows/workflows.test.ts +198 -0
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
|
|
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)
|
|
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
|
-
//
|
|
172
|
+
// Install helpers
|
|
160
173
|
// ---------------------------------------------------------------------------
|
|
161
174
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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(
|
|
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;
|
|
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
|
-
|
|
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":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/installer/install.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chief-clancy/brief",
|
|
3
|
-
"version": "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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
+
If `.clancy/.env` is present, check for `.clancy/clancy-implement.js`:
|
|
28
26
|
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
git fetch origin
|
|
33
|
-
```
|
|
30
|
+
The detected install mode is captured for Step 6's pipeline label decision.
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
### 2. Terminal-mode preflight (skip in standalone+board mode)
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
If in **terminal mode** (`.clancy/.env` present AND `.clancy/clancy-implement.js` present):
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
a. Source `.clancy/.env` and check board credentials are present.
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
package/src/workflows/brief.md
CHANGED
|
@@ -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
|
|
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
|
// ---------------------------------------------------------------------------
|