brain-dev 2.3.1 → 2.5.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 +7 -5
- package/bin/lib/agents.cjs +16 -2
- package/bin/lib/commands/execute.cjs +8 -9
- package/bin/lib/commands/new-task.cjs +3 -2
- package/bin/lib/commands/quick.cjs +15 -3
- package/bin/lib/commands/story.cjs +214 -27
- package/bin/lib/commands/verify.cjs +12 -1
- package/bin/lib/config.cjs +6 -2
- package/bin/lib/story-helpers.cjs +69 -0
- package/bin/templates/executor.md +7 -0
- package/bin/templates/requirements-generator.md +115 -0
- package/bin/templates/roadmap-architect.md +104 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ Brain adds structure without adding friction:
|
|
|
30
30
|
/brain:discuss → Captures architectural decisions before coding
|
|
31
31
|
/brain:plan → Creates verified execution plans with TDD
|
|
32
32
|
/brain:execute → Builds with per-task commits and deviation handling
|
|
33
|
+
/brain:review → Mandatory code review before verification
|
|
33
34
|
/brain:verify → 3-level verification against must-haves
|
|
34
35
|
/brain:complete → Advances to next phase with audit trail
|
|
35
36
|
```
|
|
@@ -56,7 +57,7 @@ Brain provides four levels of work, from lightweight to comprehensive:
|
|
|
56
57
|
|
|
57
58
|
```
|
|
58
59
|
quick → plan → execute → commit (minutes)
|
|
59
|
-
new-task → discuss → plan → execute → verify
|
|
60
|
+
new-task → discuss → plan → execute → review → verify (hours)
|
|
60
61
|
story → research → requirements → roadmap → phases (days/weeks)
|
|
61
62
|
new-project → detect → map → suggest next step (one-time setup)
|
|
62
63
|
```
|
|
@@ -94,7 +95,7 @@ For features that need more than a quick fix but less than a full story:
|
|
|
94
95
|
/brain:new-task "add payment integration with Stripe"
|
|
95
96
|
```
|
|
96
97
|
|
|
97
|
-
Brain runs the full pipeline: discuss → plan → execute → verify → complete.
|
|
98
|
+
Brain runs the full pipeline: discuss → plan → execute → review → verify → complete.
|
|
98
99
|
|
|
99
100
|
```bash
|
|
100
101
|
/brain:new-task --research "add rate limiting" # With light research
|
|
@@ -150,7 +151,7 @@ No configuration needed — Brain detects your stack and injects expertise into
|
|
|
150
151
|
## Lifecycle
|
|
151
152
|
|
|
152
153
|
```
|
|
153
|
-
init → new-project → story → discuss → plan → execute → verify → complete
|
|
154
|
+
init → new-project → story → discuss → plan → execute → review → verify → complete
|
|
154
155
|
↑ │
|
|
155
156
|
└──────────── next story/phase ───────────────┘
|
|
156
157
|
```
|
|
@@ -173,14 +174,14 @@ Run phases autonomously without human intervention:
|
|
|
173
174
|
/brain:execute --auto --stop # Stop running auto session
|
|
174
175
|
```
|
|
175
176
|
|
|
176
|
-
Auto mode chains: discuss → plan → execute → verify → complete → next phase, with budget limits, stuck detection, and crash recovery built in.
|
|
177
|
+
Auto mode chains: discuss → plan → execute → review → verify → complete → next phase, with budget limits, stuck detection, and crash recovery built in.
|
|
177
178
|
|
|
178
179
|
## State Machine
|
|
179
180
|
|
|
180
181
|
Brain tracks progress through a deterministic state machine:
|
|
181
182
|
|
|
182
183
|
```
|
|
183
|
-
pending → discussing → discussed → planning → executing → executed → verifying → verified → complete
|
|
184
|
+
pending → discussing → discussed → planning → executing → executed → reviewing → reviewed → verifying → verified → complete
|
|
184
185
|
```
|
|
185
186
|
|
|
186
187
|
Every command knows what comes next. `brain-dev progress` always tells you the right next step.
|
|
@@ -223,6 +224,7 @@ No supply chain risk. No transitive vulnerabilities. No `node_modules` bloat.
|
|
|
223
224
|
| `/brain:discuss` | Capture architectural decisions |
|
|
224
225
|
| `/brain:plan` | Create verified execution plans |
|
|
225
226
|
| `/brain:execute` | Build according to plans with TDD |
|
|
227
|
+
| `/brain:review` | Mandatory code review before verification |
|
|
226
228
|
| `/brain:verify` | 3-level verification against must-haves |
|
|
227
229
|
| `/brain:complete` | Mark phase done, advance to next |
|
|
228
230
|
| `/brain:quick` | Quick task — skip the ceremony |
|
package/bin/lib/agents.cjs
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Agent registry for brain orchestration.
|
|
5
|
-
* Defines the
|
|
5
|
+
* Defines the 11 core agents and their metadata.
|
|
6
6
|
* Constant registry with discovery and validation functions.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const MAX_AGENTS =
|
|
9
|
+
const MAX_AGENTS = 11;
|
|
10
10
|
|
|
11
11
|
const AGENTS = {
|
|
12
12
|
researcher: {
|
|
@@ -73,6 +73,20 @@ const AGENTS = {
|
|
|
73
73
|
model: 'inherit',
|
|
74
74
|
description: 'Maps codebase across focus areas producing structured Markdown documentation',
|
|
75
75
|
focus: ['tech', 'arch', 'quality', 'concerns']
|
|
76
|
+
},
|
|
77
|
+
'requirements-generator': {
|
|
78
|
+
template: 'requirements-generator',
|
|
79
|
+
inputs: ['project_md', 'summary_content', 'stack_expertise', 'codebase_context'],
|
|
80
|
+
outputs: ['REQUIREMENTS.md'],
|
|
81
|
+
model: 'inherit',
|
|
82
|
+
description: 'Generates structured requirements with acceptance criteria from research findings and project context'
|
|
83
|
+
},
|
|
84
|
+
'roadmap-architect': {
|
|
85
|
+
template: 'roadmap-architect',
|
|
86
|
+
inputs: ['requirements_content', 'summary_content', 'project_md', 'stack_expertise'],
|
|
87
|
+
outputs: ['ROADMAP.md'],
|
|
88
|
+
model: 'inherit',
|
|
89
|
+
description: 'Creates strategically-phased roadmap with technical dependency analysis from requirements and research'
|
|
76
90
|
}
|
|
77
91
|
};
|
|
78
92
|
|
|
@@ -6,7 +6,7 @@ const { readState, writeState } = require('../state.cjs');
|
|
|
6
6
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
7
7
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
9
|
-
const { output, error } = require('../core.cjs');
|
|
9
|
+
const { output, error, pipelineGate } = require('../core.cjs');
|
|
10
10
|
const { recordInvocation, estimateTokens } = require('../cost.cjs');
|
|
11
11
|
const { acquireLock, releaseLock } = require('../lock.cjs');
|
|
12
12
|
const { generateExpertise, getDetectedFramework } = require('../stack-expert.cjs');
|
|
@@ -306,9 +306,9 @@ async function run(args = [], opts = {}) {
|
|
|
306
306
|
if (!targetPlan) {
|
|
307
307
|
state.phase.status = 'executed';
|
|
308
308
|
writeState(brainDir, state);
|
|
309
|
-
const msg = "All plans executed. Run /brain:
|
|
310
|
-
output({ action: 'all-executed', message: msg, nextAction: '/brain:
|
|
311
|
-
return { action: 'all-executed', message: msg, nextAction: '/brain:
|
|
309
|
+
const msg = "All plans executed. Run /brain:review before verify.";
|
|
310
|
+
output({ action: 'all-executed', message: msg, nextAction: '/brain:review' }, `[brain] ${msg}\n${pipelineGate('npx brain-dev review --phase ' + phaseNumber)}`);
|
|
311
|
+
return { action: 'all-executed', message: msg, nextAction: '/brain:review' };
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// Read plan content
|
|
@@ -409,10 +409,8 @@ async function run(args = [], opts = {}) {
|
|
|
409
409
|
buildDebuggerSpawnInstructions(taskSlug, errorCtx, taskCtx, attemptedFixes, state)
|
|
410
410
|
};
|
|
411
411
|
|
|
412
|
-
// Build
|
|
413
|
-
const
|
|
414
|
-
? `brain-dev verify --phase ${phaseNumber} --auto-recover`
|
|
415
|
-
: `brain-dev verify --phase ${phaseNumber}`;
|
|
412
|
+
// Build review command (review is mandatory before verify)
|
|
413
|
+
const reviewCmd = `brain-dev review --phase ${phaseNumber}`;
|
|
416
414
|
|
|
417
415
|
const humanLines = [
|
|
418
416
|
`[brain] Executor instructions generated for Plan ${padded} in Phase ${phaseNumber}`,
|
|
@@ -425,7 +423,8 @@ async function run(args = [], opts = {}) {
|
|
|
425
423
|
if (autoRecover) {
|
|
426
424
|
humanLines.push('[brain] Auto-recovery: enabled (verify will auto-diagnose failures)');
|
|
427
425
|
}
|
|
428
|
-
humanLines.push(`[brain]
|
|
426
|
+
humanLines.push(`[brain] After execution, review with: ${reviewCmd}`);
|
|
427
|
+
humanLines.push(pipelineGate(`npx brain-dev review --phase ${phaseNumber}`));
|
|
429
428
|
humanLines.push('');
|
|
430
429
|
humanLines.push(fullPrompt);
|
|
431
430
|
output(result, humanLines.join('\n'));
|
|
@@ -5,7 +5,7 @@ const path = require('node:path');
|
|
|
5
5
|
const { parseArgs } = require('node:util');
|
|
6
6
|
const { readState, writeState } = require('../state.cjs');
|
|
7
7
|
const { logEvent } = require('../logger.cjs');
|
|
8
|
-
const { output, error, prefix } = require('../core.cjs');
|
|
8
|
+
const { output, error, prefix, pipelineGate } = require('../core.cjs');
|
|
9
9
|
const { recordInvocation, estimateTokens } = require('../cost.cjs');
|
|
10
10
|
|
|
11
11
|
async function run(args = [], opts = {}) {
|
|
@@ -364,7 +364,8 @@ function handleContinue(brainDir, state) {
|
|
|
364
364
|
prefix(`Status: ${newStatus}`),
|
|
365
365
|
prefix(`Next step: ${nextStep}`),
|
|
366
366
|
'',
|
|
367
|
-
steps[nextStep]
|
|
367
|
+
steps[nextStep],
|
|
368
|
+
nextStep === 'review' ? pipelineGate('npx brain-dev review') : ''
|
|
368
369
|
].join('\n');
|
|
369
370
|
|
|
370
371
|
output({
|
|
@@ -6,7 +6,7 @@ const { readState, writeState } = require('../state.cjs');
|
|
|
6
6
|
const { loadTemplate, interpolate } = require('../templates.cjs');
|
|
7
7
|
const { resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
9
|
-
const { output, error, success } = require('../core.cjs');
|
|
9
|
+
const { output, error, success, pipelineGate } = require('../core.cjs');
|
|
10
10
|
const { generateExpertise } = require('../stack-expert.cjs');
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -265,7 +265,7 @@ function handleExecute(args, brainDir, state) {
|
|
|
265
265
|
} catch { /* ignore */ }
|
|
266
266
|
|
|
267
267
|
const nextCmd = isFull
|
|
268
|
-
? `npx brain-dev
|
|
268
|
+
? `npx brain-dev review`
|
|
269
269
|
: `npx brain-dev quick --complete --task ${taskNum}`;
|
|
270
270
|
|
|
271
271
|
logEvent(brainDir, 0, { type: 'quick-execute', task: taskNum });
|
|
@@ -279,9 +279,10 @@ function handleExecute(args, brainDir, state) {
|
|
|
279
279
|
'Do NOT execute the tasks yourself.',
|
|
280
280
|
'',
|
|
281
281
|
`After executor completes, run: ${nextCmd}`,
|
|
282
|
+
isFull ? pipelineGate('npx brain-dev review') : '',
|
|
282
283
|
'',
|
|
283
284
|
prompt
|
|
284
|
-
].join('\n');
|
|
285
|
+
].filter(Boolean).join('\n');
|
|
285
286
|
|
|
286
287
|
const result = {
|
|
287
288
|
action: 'spawn-quick-executor',
|
|
@@ -313,6 +314,17 @@ function handleVerify(args, brainDir, state) {
|
|
|
313
314
|
return { error: 'task-not-found' };
|
|
314
315
|
}
|
|
315
316
|
|
|
317
|
+
// Hard gate: review must be completed before verify
|
|
318
|
+
const reviewPath = path.join(taskDir, 'REVIEW.md');
|
|
319
|
+
if (!fs.existsSync(reviewPath)) {
|
|
320
|
+
error('Review is mandatory before verify. Run: /brain:review first.');
|
|
321
|
+
output(
|
|
322
|
+
{ error: 'review-required', nextAction: '/brain:review' },
|
|
323
|
+
`[brain] BLOCKED: Run /brain:review before quick --verify\n${pipelineGate('npx brain-dev review')}`
|
|
324
|
+
);
|
|
325
|
+
return { error: 'review-required' };
|
|
326
|
+
}
|
|
327
|
+
|
|
316
328
|
const planPath = path.join(taskDir, 'PLAN-1.md');
|
|
317
329
|
const verificationPath = path.join(taskDir, 'VERIFICATION-1.md');
|
|
318
330
|
|
|
@@ -6,14 +6,15 @@ const { parseArgs } = require('node:util');
|
|
|
6
6
|
const { readState, writeState } = require('../state.cjs');
|
|
7
7
|
const { output, error, prefix } = require('../core.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
9
|
-
const { loadTemplate, interpolate } = require('../templates.cjs');
|
|
9
|
+
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
10
10
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
11
11
|
const { generateExpertise } = require('../stack-expert.cjs');
|
|
12
12
|
const {
|
|
13
13
|
RESEARCH_AREAS, CORE_QUESTIONS, buildBrownfieldQuestions,
|
|
14
14
|
readDetection, buildCodebaseContext, generateProjectMd,
|
|
15
|
-
generateRequirementsMd, extractFeatures, extractSection
|
|
15
|
+
generateRequirementsMd, validateRequirementsMd, extractFeatures, extractSection
|
|
16
16
|
} = require('../story-helpers.cjs');
|
|
17
|
+
const { getDetectedFramework } = require('../stack-expert.cjs');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Get research areas with optional framework-specific additions.
|
|
@@ -318,10 +319,55 @@ function handleContinue(brainDir, state, values) {
|
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
if (!hasRequirementsMd) {
|
|
322
|
+
if (storyMeta.status === 'requirements-generating') {
|
|
323
|
+
const humanLines = prefix('Requirements agent is generating... Run --continue after it finishes.');
|
|
324
|
+
output({ action: 'requirements-pending' }, humanLines);
|
|
325
|
+
return { action: 'requirements-pending' };
|
|
326
|
+
}
|
|
321
327
|
return stepRequirements(brainDir, storyDir, storyMeta, state);
|
|
322
328
|
}
|
|
323
329
|
|
|
330
|
+
// Validate requirements quality before proceeding to roadmap.
|
|
331
|
+
// Only validate agent-generated requirements (hasSummaryMd).
|
|
332
|
+
// Programmatic fallback (--no-research) skips validation because its output
|
|
333
|
+
// format intentionally lacks acceptance criteria.
|
|
334
|
+
if (hasRequirementsMd && !hasRoadmapMd && !storyMeta.requirementsValidated && hasSummaryMd) {
|
|
335
|
+
const reqContent = fs.readFileSync(path.join(storyDir, 'REQUIREMENTS.md'), 'utf8');
|
|
336
|
+
const validation = validateRequirementsMd(reqContent);
|
|
337
|
+
|
|
338
|
+
if (!validation.valid && (storyMeta.requirementsRetries || 0) < 2) {
|
|
339
|
+
fs.unlinkSync(path.join(storyDir, 'REQUIREMENTS.md'));
|
|
340
|
+
storyMeta.requirementsRetries = (storyMeta.requirementsRetries || 0) + 1;
|
|
341
|
+
storyMeta.requirementsFeedback = validation.issues;
|
|
342
|
+
|
|
343
|
+
logEvent(brainDir, 0, { type: 'requirements-retry', story: storyMeta.num, attempt: storyMeta.requirementsRetries, issues: validation.issues.length });
|
|
344
|
+
|
|
345
|
+
const humanLines = [
|
|
346
|
+
prefix(`Requirements quality check failed (attempt ${storyMeta.requirementsRetries}/2):`),
|
|
347
|
+
...validation.issues.map(i => prefix(` - ${i}`)),
|
|
348
|
+
'',
|
|
349
|
+
prefix('Re-spawning requirements agent with feedback...')
|
|
350
|
+
].join('\n');
|
|
351
|
+
output({ action: 'requirements-retry', issues: validation.issues, stats: validation.stats }, humanLines);
|
|
352
|
+
|
|
353
|
+
return stepRequirements(brainDir, storyDir, storyMeta, state);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
storyMeta.requirementsValidated = true;
|
|
357
|
+
fs.writeFileSync(path.join(storyDir, 'story.json'), JSON.stringify(storyMeta, null, 2));
|
|
358
|
+
|
|
359
|
+
if (!validation.valid) {
|
|
360
|
+
const humanLines = prefix(`Warning: REQUIREMENTS.md has ${validation.issues.length} quality issues after ${storyMeta.requirementsRetries} retries. Proceeding anyway.`);
|
|
361
|
+
output({ action: 'requirements-quality-warning', issues: validation.issues, stats: validation.stats }, humanLines);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
324
365
|
if (!hasRoadmapMd) {
|
|
366
|
+
if (storyMeta.status === 'roadmap-generating') {
|
|
367
|
+
const humanLines = prefix('Roadmap agent is generating... Run --continue after it finishes.');
|
|
368
|
+
output({ action: 'roadmap-pending' }, humanLines);
|
|
369
|
+
return { action: 'roadmap-pending' };
|
|
370
|
+
}
|
|
325
371
|
return stepRoadmap(brainDir, storyDir, storyMeta, state);
|
|
326
372
|
}
|
|
327
373
|
|
|
@@ -526,9 +572,8 @@ function stepSynthesize(brainDir, storyDir, storyMeta, state) {
|
|
|
526
572
|
// ---------------------------------------------------------------------------
|
|
527
573
|
|
|
528
574
|
function stepRequirements(brainDir, storyDir, storyMeta, state) {
|
|
529
|
-
// Read PROJECT.md
|
|
575
|
+
// Read PROJECT.md
|
|
530
576
|
const projectMd = fs.readFileSync(path.join(storyDir, 'PROJECT.md'), 'utf8');
|
|
531
|
-
const features = extractFeatures(projectMd);
|
|
532
577
|
|
|
533
578
|
// Read research summary if available
|
|
534
579
|
const summaryPath = path.join(storyDir, 'research', 'SUMMARY.md');
|
|
@@ -537,36 +582,107 @@ function stepRequirements(brainDir, storyDir, storyMeta, state) {
|
|
|
537
582
|
summaryContent = fs.readFileSync(summaryPath, 'utf8');
|
|
538
583
|
}
|
|
539
584
|
|
|
540
|
-
//
|
|
541
|
-
const
|
|
542
|
-
|
|
585
|
+
// No research → use programmatic fallback
|
|
586
|
+
const skipResearch = storyMeta.noResearch === true || !summaryContent;
|
|
587
|
+
if (skipResearch) {
|
|
588
|
+
const features = extractFeatures(projectMd);
|
|
589
|
+
const { requirementsMd, categories } = generateRequirementsMd(features, summaryContent);
|
|
590
|
+
fs.writeFileSync(path.join(storyDir, 'REQUIREMENTS.md'), requirementsMd, 'utf8');
|
|
591
|
+
|
|
592
|
+
storyMeta.status = 'requirements';
|
|
593
|
+
fs.writeFileSync(path.join(storyDir, 'story.json'), JSON.stringify(storyMeta, null, 2));
|
|
594
|
+
|
|
595
|
+
const activeIdx = (state.stories.active || []).findIndex(s => s.dirName === storyMeta.dirName);
|
|
596
|
+
if (activeIdx >= 0) state.stories.active[activeIdx].status = 'requirements';
|
|
597
|
+
writeState(brainDir, state);
|
|
598
|
+
|
|
599
|
+
logEvent(brainDir, 0, { type: 'story-requirements', story: storyMeta.num, categories: categories.length });
|
|
600
|
+
|
|
601
|
+
const humanLines = [
|
|
602
|
+
prefix(`Story: ${storyMeta.version} ${storyMeta.title}`),
|
|
603
|
+
prefix('Step: Requirements Generated (programmatic fallback)'),
|
|
604
|
+
'',
|
|
605
|
+
prefix(`REQUIREMENTS.md written with ${categories.length} categories:`),
|
|
606
|
+
...categories.map(c => prefix(` - ${c.name} (${c.items.length} requirements)`)),
|
|
607
|
+
'',
|
|
608
|
+
prefix('Review REQUIREMENTS.md and confirm.'),
|
|
609
|
+
prefix('Then run: brain-dev story --continue')
|
|
610
|
+
].join('\n');
|
|
611
|
+
|
|
612
|
+
const result = {
|
|
613
|
+
action: 'requirements-generated',
|
|
614
|
+
content: requirementsMd,
|
|
615
|
+
categories,
|
|
616
|
+
instruction: 'Review and confirm'
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
output(result, humanLines);
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Research exists → spawn requirements-generator agent
|
|
624
|
+
const outputPath = path.join(storyDir, 'REQUIREMENTS.md');
|
|
625
|
+
const framework = getDetectedFramework(brainDir);
|
|
626
|
+
const template = loadTemplateWithOverlay('requirements-generator', framework);
|
|
627
|
+
const stackExpertise = generateExpertise(brainDir, 'general');
|
|
628
|
+
const detection = readDetection(brainDir);
|
|
629
|
+
const codebaseContext = detection ? buildCodebaseContext(detection) : '';
|
|
630
|
+
|
|
631
|
+
// Build feedback section if retrying
|
|
632
|
+
let feedbackSection = '';
|
|
633
|
+
if (storyMeta.requirementsFeedback && storyMeta.requirementsFeedback.length > 0) {
|
|
634
|
+
feedbackSection = '\n\n## PREVIOUS ATTEMPT FEEDBACK\n\n' +
|
|
635
|
+
'Your previous output was rejected for these quality issues:\n' +
|
|
636
|
+
storyMeta.requirementsFeedback.map(i => `- ${i}`).join('\n') +
|
|
637
|
+
'\n\nFix ALL these issues in your next attempt.';
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const prompt = interpolate(template, {
|
|
641
|
+
project_md: projectMd,
|
|
642
|
+
summary_content: summaryContent,
|
|
643
|
+
stack_expertise: stackExpertise || '',
|
|
644
|
+
codebase_context: codebaseContext || '_No codebase context available._',
|
|
645
|
+
output_path: outputPath
|
|
646
|
+
}) + feedbackSection;
|
|
647
|
+
|
|
648
|
+
const model = resolveModel('requirements-generator', state);
|
|
543
649
|
|
|
544
650
|
// Update status
|
|
545
|
-
storyMeta.status = 'requirements';
|
|
651
|
+
storyMeta.status = 'requirements-generating';
|
|
546
652
|
fs.writeFileSync(path.join(storyDir, 'story.json'), JSON.stringify(storyMeta, null, 2));
|
|
547
653
|
|
|
548
654
|
const activeIdx = (state.stories.active || []).findIndex(s => s.dirName === storyMeta.dirName);
|
|
549
|
-
if (activeIdx >= 0) state.stories.active[activeIdx].status = 'requirements';
|
|
655
|
+
if (activeIdx >= 0) state.stories.active[activeIdx].status = 'requirements-generating';
|
|
550
656
|
writeState(brainDir, state);
|
|
551
657
|
|
|
552
|
-
logEvent(brainDir, 0, { type: 'story-requirements', story: storyMeta.num,
|
|
658
|
+
logEvent(brainDir, 0, { type: 'story-requirements-spawn', story: storyMeta.num, retry: storyMeta.requirementsRetries || 0 });
|
|
553
659
|
|
|
554
660
|
const humanLines = [
|
|
555
661
|
prefix(`Story: ${storyMeta.version} ${storyMeta.title}`),
|
|
556
|
-
prefix('Step: Requirements
|
|
662
|
+
prefix('Step: Spawning Requirements Generator Agent'),
|
|
663
|
+
storyMeta.requirementsRetries ? prefix(`Retry: ${storyMeta.requirementsRetries}/2`) : '',
|
|
557
664
|
'',
|
|
558
|
-
prefix(
|
|
559
|
-
|
|
665
|
+
prefix('The agent will read your research summary and project context'),
|
|
666
|
+
prefix('to generate detailed, specific requirements with acceptance criteria.'),
|
|
560
667
|
'',
|
|
561
|
-
prefix(
|
|
562
|
-
prefix(
|
|
563
|
-
|
|
668
|
+
prefix(`Output: ${outputPath}`),
|
|
669
|
+
prefix(`Model: ${model}`),
|
|
670
|
+
'',
|
|
671
|
+
'IMPORTANT: Use the Agent tool to spawn the requirements-generator agent.',
|
|
672
|
+
'Do NOT generate requirements yourself.',
|
|
673
|
+
'',
|
|
674
|
+
'After agent completes, run: brain-dev story --continue',
|
|
675
|
+
'',
|
|
676
|
+
prompt
|
|
677
|
+
].filter(Boolean).join('\n');
|
|
564
678
|
|
|
565
679
|
const result = {
|
|
566
|
-
action: 'requirements-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
680
|
+
action: 'spawn-requirements-agent',
|
|
681
|
+
prompt,
|
|
682
|
+
model,
|
|
683
|
+
outputPath,
|
|
684
|
+
retry: storyMeta.requirementsRetries || 0,
|
|
685
|
+
instruction: 'Spawn requirements-generator agent, then run --continue'
|
|
570
686
|
};
|
|
571
687
|
|
|
572
688
|
output(result, humanLines);
|
|
@@ -582,6 +698,82 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
582
698
|
const reqPath = path.join(storyDir, 'REQUIREMENTS.md');
|
|
583
699
|
const reqContent = fs.readFileSync(reqPath, 'utf8');
|
|
584
700
|
|
|
701
|
+
// Read research summary if available
|
|
702
|
+
const summaryPath = path.join(storyDir, 'research', 'SUMMARY.md');
|
|
703
|
+
let summaryContent = '';
|
|
704
|
+
if (fs.existsSync(summaryPath)) {
|
|
705
|
+
summaryContent = fs.readFileSync(summaryPath, 'utf8');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// No research → use programmatic fallback
|
|
709
|
+
const skipResearch = storyMeta.noResearch === true || !summaryContent;
|
|
710
|
+
if (skipResearch) {
|
|
711
|
+
return stepRoadmapProgrammatic(brainDir, storyDir, storyMeta, state, reqContent);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Research exists → spawn roadmap-architect agent
|
|
715
|
+
const projectMd = fs.readFileSync(path.join(storyDir, 'PROJECT.md'), 'utf8');
|
|
716
|
+
const outputPath = path.join(storyDir, 'ROADMAP.md');
|
|
717
|
+
const framework = getDetectedFramework(brainDir);
|
|
718
|
+
const template = loadTemplateWithOverlay('roadmap-architect', framework);
|
|
719
|
+
const stackExpertise = generateExpertise(brainDir, 'general');
|
|
720
|
+
|
|
721
|
+
const prompt = interpolate(template, {
|
|
722
|
+
requirements_content: reqContent,
|
|
723
|
+
summary_content: summaryContent,
|
|
724
|
+
project_md: projectMd,
|
|
725
|
+
stack_expertise: stackExpertise || '',
|
|
726
|
+
output_path: outputPath,
|
|
727
|
+
story_version: storyMeta.version || '',
|
|
728
|
+
story_title: storyMeta.title || ''
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
const model = resolveModel('roadmap-architect', state);
|
|
732
|
+
|
|
733
|
+
// Update status
|
|
734
|
+
storyMeta.status = 'roadmap-generating';
|
|
735
|
+
fs.writeFileSync(path.join(storyDir, 'story.json'), JSON.stringify(storyMeta, null, 2));
|
|
736
|
+
|
|
737
|
+
const activeIdx = (state.stories.active || []).findIndex(s => s.dirName === storyMeta.dirName);
|
|
738
|
+
if (activeIdx >= 0) state.stories.active[activeIdx].status = 'roadmap-generating';
|
|
739
|
+
writeState(brainDir, state);
|
|
740
|
+
|
|
741
|
+
logEvent(brainDir, 0, { type: 'story-roadmap-spawn', story: storyMeta.num });
|
|
742
|
+
|
|
743
|
+
const humanLines = [
|
|
744
|
+
prefix(`Story: ${storyMeta.version} ${storyMeta.title}`),
|
|
745
|
+
prefix('Step: Spawning Roadmap Architect Agent'),
|
|
746
|
+
'',
|
|
747
|
+
prefix('The agent will analyze requirements and research findings'),
|
|
748
|
+
prefix('to create a strategically-phased roadmap with real dependencies.'),
|
|
749
|
+
'',
|
|
750
|
+
prefix(`Output: ${outputPath}`),
|
|
751
|
+
prefix(`Model: ${model}`),
|
|
752
|
+
'',
|
|
753
|
+
'IMPORTANT: Use the Agent tool to spawn the roadmap-architect agent.',
|
|
754
|
+
'Do NOT generate the roadmap yourself.',
|
|
755
|
+
'',
|
|
756
|
+
'After agent completes, run: brain-dev story --continue',
|
|
757
|
+
'',
|
|
758
|
+
prompt
|
|
759
|
+
].join('\n');
|
|
760
|
+
|
|
761
|
+
const result = {
|
|
762
|
+
action: 'spawn-roadmap-agent',
|
|
763
|
+
prompt,
|
|
764
|
+
model,
|
|
765
|
+
outputPath,
|
|
766
|
+
instruction: 'Spawn roadmap-architect agent, then run --continue'
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
output(result, humanLines);
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Programmatic fallback for roadmap generation (no research).
|
|
775
|
+
*/
|
|
776
|
+
function stepRoadmapProgrammatic(brainDir, storyDir, storyMeta, state, reqContent) {
|
|
585
777
|
// Parse categories from REQUIREMENTS.md headings
|
|
586
778
|
const categoryRegex = /## (.+)\n([\s\S]*?)(?=\n## |$)/g;
|
|
587
779
|
const phases = [];
|
|
@@ -591,8 +783,8 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
591
783
|
while ((catMatch = categoryRegex.exec(reqContent)) !== null) {
|
|
592
784
|
const catName = catMatch[1].trim();
|
|
593
785
|
if (catName === 'Requirements' || catName.toLowerCase() === 'requirements') continue;
|
|
786
|
+
if (catName === 'Traceability' || catName.toLowerCase() === 'traceability') continue;
|
|
594
787
|
|
|
595
|
-
// Extract requirement IDs from this category
|
|
596
788
|
const catBody = catMatch[2];
|
|
597
789
|
const reqIds = [];
|
|
598
790
|
const reqLineRegex = /- \*\*(\d+\.\d+)\*\*/g;
|
|
@@ -601,7 +793,6 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
601
793
|
reqIds.push(reqMatch[1]);
|
|
602
794
|
}
|
|
603
795
|
|
|
604
|
-
// Dependencies: each phase depends on the previous one (except phase 1)
|
|
605
796
|
const dependsOn = phaseNum > 1 ? [phaseNum - 1] : [];
|
|
606
797
|
|
|
607
798
|
phases.push({
|
|
@@ -617,7 +808,6 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
617
808
|
phaseNum++;
|
|
618
809
|
}
|
|
619
810
|
|
|
620
|
-
// Fallback: if no phases extracted, create a single phase
|
|
621
811
|
if (phases.length === 0) {
|
|
622
812
|
phases.push({
|
|
623
813
|
number: 1,
|
|
@@ -630,7 +820,6 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
630
820
|
});
|
|
631
821
|
}
|
|
632
822
|
|
|
633
|
-
// Generate ROADMAP.md
|
|
634
823
|
const roadmapLines = [
|
|
635
824
|
'# Roadmap',
|
|
636
825
|
'',
|
|
@@ -650,7 +839,6 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
650
839
|
roadmapLines.push('');
|
|
651
840
|
}
|
|
652
841
|
|
|
653
|
-
// Progress table
|
|
654
842
|
roadmapLines.push('## Progress');
|
|
655
843
|
roadmapLines.push('');
|
|
656
844
|
roadmapLines.push('| Phase | Status | Plans |');
|
|
@@ -663,7 +851,6 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
663
851
|
const roadmapMd = roadmapLines.join('\n');
|
|
664
852
|
fs.writeFileSync(path.join(storyDir, 'ROADMAP.md'), roadmapMd, 'utf8');
|
|
665
853
|
|
|
666
|
-
// Update status
|
|
667
854
|
storyMeta.status = 'roadmap';
|
|
668
855
|
fs.writeFileSync(path.join(storyDir, 'story.json'), JSON.stringify(storyMeta, null, 2));
|
|
669
856
|
|
|
@@ -675,7 +862,7 @@ function stepRoadmap(brainDir, storyDir, storyMeta, state) {
|
|
|
675
862
|
|
|
676
863
|
const humanLines = [
|
|
677
864
|
prefix(`Story: ${storyMeta.version} ${storyMeta.title}`),
|
|
678
|
-
prefix('Step: Roadmap Generated'),
|
|
865
|
+
prefix('Step: Roadmap Generated (programmatic fallback)'),
|
|
679
866
|
'',
|
|
680
867
|
prefix(`ROADMAP.md written with ${phases.length} phases:`),
|
|
681
868
|
...phases.map(p => prefix(` Phase ${p.number}: ${p.name} (depends on: ${p.dependsOn.length === 0 ? 'none' : p.dependsOn.join(', ')})`)),
|
|
@@ -6,7 +6,7 @@ const { readState, writeState, atomicWriteSync } = require('../state.cjs');
|
|
|
6
6
|
const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
|
|
7
7
|
const { getAgent, resolveModel } = require('../agents.cjs');
|
|
8
8
|
const { logEvent } = require('../logger.cjs');
|
|
9
|
-
const { output, error, success } = require('../core.cjs');
|
|
9
|
+
const { output, error, success, pipelineGate } = require('../core.cjs');
|
|
10
10
|
const antiPatterns = require('../anti-patterns.cjs');
|
|
11
11
|
const { buildDebuggerSpawnInstructions } = require('./execute.cjs');
|
|
12
12
|
const { isPathWithinRoot } = require('../security.cjs');
|
|
@@ -203,6 +203,17 @@ async function run(args = [], opts = {}) {
|
|
|
203
203
|
return { error: 'phase-not-found' };
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
// Hard gate: review must be completed before verify
|
|
207
|
+
const reviewPath = path.join(phaseDir, 'REVIEW.md');
|
|
208
|
+
if (!fs.existsSync(reviewPath)) {
|
|
209
|
+
error('Review is mandatory before verify. Run: /brain:review first.');
|
|
210
|
+
output(
|
|
211
|
+
{ error: 'review-required', nextAction: '/brain:review' },
|
|
212
|
+
`[brain] BLOCKED: Run /brain:review before /brain:verify\n${pipelineGate('npx brain-dev review --phase ' + phaseNumber)}`
|
|
213
|
+
);
|
|
214
|
+
return { error: 'review-required' };
|
|
215
|
+
}
|
|
216
|
+
|
|
206
217
|
// Scan for PLAN-*.md files and extract must_haves from each
|
|
207
218
|
const files = fs.readdirSync(phaseDir);
|
|
208
219
|
const planFiles = files.filter(f => /^PLAN-\d+\.md$/.test(f)).sort();
|
package/bin/lib/config.cjs
CHANGED
|
@@ -56,7 +56,9 @@ const PROFILES = {
|
|
|
56
56
|
models: {
|
|
57
57
|
'plan-checker': 'claude-sonnet-4-20250514',
|
|
58
58
|
verifier: 'claude-sonnet-4-20250514',
|
|
59
|
-
researcher: 'claude-sonnet-4-20250514'
|
|
59
|
+
researcher: 'claude-sonnet-4-20250514',
|
|
60
|
+
'requirements-generator': 'claude-sonnet-4-20250514',
|
|
61
|
+
'roadmap-architect': 'claude-sonnet-4-20250514'
|
|
60
62
|
}
|
|
61
63
|
},
|
|
62
64
|
budget: {
|
|
@@ -67,7 +69,9 @@ const PROFILES = {
|
|
|
67
69
|
'plan-checker': 'claude-haiku-4-20250514',
|
|
68
70
|
verifier: 'claude-haiku-4-20250514',
|
|
69
71
|
debugger: 'claude-haiku-4-20250514',
|
|
70
|
-
mapper: 'claude-haiku-4-20250514'
|
|
72
|
+
mapper: 'claude-haiku-4-20250514',
|
|
73
|
+
'requirements-generator': 'claude-sonnet-4-20250514',
|
|
74
|
+
'roadmap-architect': 'claude-haiku-4-20250514'
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
77
|
};
|
|
@@ -426,6 +426,74 @@ function extractSection(content, heading) {
|
|
|
426
426
|
return match ? match[1].trim() : '';
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Validate REQUIREMENTS.md quality after LLM generation.
|
|
431
|
+
* Checks for acceptance criteria, description length, banned phrases, and structure.
|
|
432
|
+
* @param {string} content - REQUIREMENTS.md content
|
|
433
|
+
* @returns {{ valid: boolean, issues: string[], stats: { requirements: number, withAcceptance: number, categories: number, acceptanceRate: number } }}
|
|
434
|
+
*/
|
|
435
|
+
function validateRequirementsMd(content) {
|
|
436
|
+
const issues = [];
|
|
437
|
+
|
|
438
|
+
// Parse requirements (checkbox format: - [ ] **N.M**: Description)
|
|
439
|
+
const reqRegex = /- \[[ x]\] \*\*(\S+)\*\*:?\s*(.+)/g;
|
|
440
|
+
let reqCount = 0;
|
|
441
|
+
let withAcceptance = 0;
|
|
442
|
+
let match;
|
|
443
|
+
|
|
444
|
+
while ((match = reqRegex.exec(content)) !== null) {
|
|
445
|
+
reqCount++;
|
|
446
|
+
const reqId = match[1];
|
|
447
|
+
const reqText = match[2];
|
|
448
|
+
|
|
449
|
+
// Check for acceptance criterion following this requirement
|
|
450
|
+
const reqIdx = content.indexOf(match[0]);
|
|
451
|
+
const nextLines = content.slice(reqIdx + match[0].length).split('\n').slice(0, 4);
|
|
452
|
+
const hasAcceptance = nextLines.some(l =>
|
|
453
|
+
l.trim().startsWith('- Acceptance:') || l.trim().startsWith('Acceptance:'));
|
|
454
|
+
|
|
455
|
+
if (hasAcceptance) withAcceptance++;
|
|
456
|
+
else issues.push(`${reqId}: missing acceptance criterion`);
|
|
457
|
+
|
|
458
|
+
// Check description length
|
|
459
|
+
if (reqText.split(/\s+/).length < 5) {
|
|
460
|
+
issues.push(`${reqId}: description too short (< 5 words)`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Check for banned vague phrases
|
|
464
|
+
const banned = [
|
|
465
|
+
'implement properly', 'handle correctly', 'make it work',
|
|
466
|
+
'ensure quality', 'integrate well', 'setup recommended'
|
|
467
|
+
];
|
|
468
|
+
for (const phrase of banned) {
|
|
469
|
+
if (reqText.toLowerCase().includes(phrase)) {
|
|
470
|
+
issues.push(`${reqId}: contains banned vague phrase "${phrase}"`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Check category count (exclude special sections like Traceability, Requirements)
|
|
476
|
+
const allHeadings = content.match(/^## [A-Z].*/gm) || [];
|
|
477
|
+
const catCount = allHeadings.filter(h =>
|
|
478
|
+
!h.match(/^## (Traceability|Requirements|REQUIREMENTS)/)).length;
|
|
479
|
+
if (catCount < 2) issues.push('Too few categories (expected at least 2)');
|
|
480
|
+
|
|
481
|
+
// Check minimum requirement count
|
|
482
|
+
if (reqCount < 3) issues.push(`Only ${reqCount} requirements found (expected at least 3)`);
|
|
483
|
+
|
|
484
|
+
const valid = issues.length === 0;
|
|
485
|
+
return {
|
|
486
|
+
valid,
|
|
487
|
+
issues,
|
|
488
|
+
stats: {
|
|
489
|
+
requirements: reqCount,
|
|
490
|
+
withAcceptance,
|
|
491
|
+
categories: catCount,
|
|
492
|
+
acceptanceRate: reqCount > 0 ? Math.round(withAcceptance / reqCount * 100) : 0
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
429
497
|
module.exports = {
|
|
430
498
|
RESEARCH_AREAS,
|
|
431
499
|
CORE_QUESTIONS,
|
|
@@ -434,6 +502,7 @@ module.exports = {
|
|
|
434
502
|
buildCodebaseContext,
|
|
435
503
|
generateProjectMd,
|
|
436
504
|
generateRequirementsMd,
|
|
505
|
+
validateRequirementsMd,
|
|
437
506
|
extractFeatures,
|
|
438
507
|
extractSection
|
|
439
508
|
};
|
|
@@ -243,6 +243,13 @@ Include sections:
|
|
|
243
243
|
- On failure after retry: `## EXECUTION FAILED`
|
|
244
244
|
- On checkpoint (user input needed): `## CHECKPOINT REACHED`
|
|
245
245
|
|
|
246
|
+
## After Execution Completes
|
|
247
|
+
|
|
248
|
+
When you output EXECUTION COMPLETE:
|
|
249
|
+
- The orchestrator will run /brain:review BEFORE /brain:verify
|
|
250
|
+
- Review is MANDATORY — it cannot be skipped for any reason
|
|
251
|
+
- Do NOT suggest running verify directly after execution
|
|
252
|
+
|
|
246
253
|
## Pipeline Enforcement Reminders
|
|
247
254
|
|
|
248
255
|
**EXECUTION FAILED:** When you output this marker:
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Requirements Generator Agent
|
|
2
|
+
|
|
3
|
+
You are a requirements engineering specialist. Your job is to analyze research findings and project context to produce specific, measurable, testable requirements.
|
|
4
|
+
|
|
5
|
+
## Technology Context
|
|
6
|
+
{{stack_expertise}}
|
|
7
|
+
|
|
8
|
+
## Project Context
|
|
9
|
+
{{project_md}}
|
|
10
|
+
|
|
11
|
+
## Research Summary
|
|
12
|
+
{{summary_content}}
|
|
13
|
+
|
|
14
|
+
## Codebase Context
|
|
15
|
+
{{codebase_context}}
|
|
16
|
+
|
|
17
|
+
## Your Task
|
|
18
|
+
|
|
19
|
+
Generate a structured REQUIREMENTS.md by analyzing the research summary and project context above. Every requirement must be **grounded in research findings** — no generic filler.
|
|
20
|
+
|
|
21
|
+
## Output Format
|
|
22
|
+
|
|
23
|
+
Write the file to: `{{output_path}}`
|
|
24
|
+
|
|
25
|
+
Use this EXACT format:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
# Requirements
|
|
29
|
+
|
|
30
|
+
## [Category Name]
|
|
31
|
+
|
|
32
|
+
Goal: [1-sentence goal grounded in specific research findings]
|
|
33
|
+
|
|
34
|
+
- [ ] **N.M**: [Specific, measurable requirement — minimum 5 words]
|
|
35
|
+
- Acceptance: [Testable criterion that can become an assert/expect statement]
|
|
36
|
+
- Source: [Which research area identified this need]
|
|
37
|
+
- Priority: P0|P1|P2
|
|
38
|
+
|
|
39
|
+
## Traceability
|
|
40
|
+
|
|
41
|
+
| Requirement | Category | Priority | Source |
|
|
42
|
+
|-------------|----------|----------|--------|
|
|
43
|
+
| N.M | Category | P0/P1/P2 | Research area |
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Category Rules
|
|
47
|
+
- Derive categories from ACTUAL research themes and project structure
|
|
48
|
+
- Do NOT use hardcoded generic names like "Infrastructure" or "Quality & Security"
|
|
49
|
+
- Each category needs a Goal line grounded in specific research findings
|
|
50
|
+
- Minimum 2 categories, maximum 8
|
|
51
|
+
|
|
52
|
+
### Requirement Rules
|
|
53
|
+
1. **Checkbox format required**: `- [ ] **N.M**: Description`
|
|
54
|
+
- N = category number (1, 2, 3...)
|
|
55
|
+
- M = requirement within category (1, 2, 3...)
|
|
56
|
+
2. **Every requirement MUST have an Acceptance line** — testable criterion
|
|
57
|
+
3. **Description minimum 5 words** — be specific about WHAT, not just "implement X"
|
|
58
|
+
4. **Source must reference research** — which research finding drives this requirement
|
|
59
|
+
5. **Priority**: P0 = must have, P1 = should have, P2 = nice to have
|
|
60
|
+
|
|
61
|
+
### Banned Vague Phrases (DO NOT USE)
|
|
62
|
+
- "implement properly"
|
|
63
|
+
- "handle correctly"
|
|
64
|
+
- "make it work"
|
|
65
|
+
- "ensure quality"
|
|
66
|
+
- "integrate well"
|
|
67
|
+
- "setup recommended technology stack"
|
|
68
|
+
- "implement testing strategy"
|
|
69
|
+
- "implement security requirements"
|
|
70
|
+
- "implement recommended architecture patterns"
|
|
71
|
+
|
|
72
|
+
### Bad vs Good Examples
|
|
73
|
+
|
|
74
|
+
**BAD:**
|
|
75
|
+
```
|
|
76
|
+
- [ ] **1.1**: Setup recommended technology stack
|
|
77
|
+
- [ ] **2.1**: Implement security requirements
|
|
78
|
+
- [ ] **3.1**: Handle errors properly
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**GOOD:**
|
|
82
|
+
```
|
|
83
|
+
- [ ] **1.1**: User authentication via JWT with RS256 signing, 24h token expiration
|
|
84
|
+
- Acceptance: POST /auth/login with valid credentials returns 200 with JWT containing user_id and exp claims; expired tokens return 401
|
|
85
|
+
- Source: Security research — session management recommendations
|
|
86
|
+
- Priority: P0
|
|
87
|
+
|
|
88
|
+
- [ ] **1.2**: PostgreSQL schema with users table including email uniqueness constraint and bcrypt password hashing
|
|
89
|
+
- Acceptance: Migration creates users table with UNIQUE index on email column; passwords stored as bcrypt hashes with cost factor 12
|
|
90
|
+
- Source: Stack research — database recommendations
|
|
91
|
+
- Priority: P0
|
|
92
|
+
|
|
93
|
+
- [ ] **2.1**: Rate limiting on authentication endpoints at 5 requests per minute per IP
|
|
94
|
+
- Acceptance: 6th login attempt within 60 seconds returns 429 with Retry-After header
|
|
95
|
+
- Source: Security research — brute force protection
|
|
96
|
+
- Priority: P1
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Cross-Cutting Concerns
|
|
100
|
+
Include requirements from ALL research areas, not just features:
|
|
101
|
+
- Security findings → security requirements
|
|
102
|
+
- Performance findings → performance requirements
|
|
103
|
+
- Testing findings → testing requirements
|
|
104
|
+
- Architecture findings → structural requirements
|
|
105
|
+
- Pitfalls findings → defensive requirements
|
|
106
|
+
|
|
107
|
+
## Output Marker
|
|
108
|
+
|
|
109
|
+
When complete, output:
|
|
110
|
+
```
|
|
111
|
+
## REQUIREMENTS COMPLETE
|
|
112
|
+
Categories: [count]
|
|
113
|
+
Requirements: [total count]
|
|
114
|
+
P0: [count] | P1: [count] | P2: [count]
|
|
115
|
+
```
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Roadmap Architect Agent
|
|
2
|
+
|
|
3
|
+
You are a technical roadmap architect. Your job is to create a strategically-phased development plan with REAL technical dependencies — not a linear chain of categories.
|
|
4
|
+
|
|
5
|
+
## Technology Context
|
|
6
|
+
{{stack_expertise}}
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
{{requirements_content}}
|
|
10
|
+
|
|
11
|
+
## Research Summary
|
|
12
|
+
{{summary_content}}
|
|
13
|
+
|
|
14
|
+
## Project Context
|
|
15
|
+
{{project_md}}
|
|
16
|
+
|
|
17
|
+
## Your Task
|
|
18
|
+
|
|
19
|
+
Analyze requirements and research to create a phased ROADMAP.md with technical dependency analysis, risk assessment, and strategic sequencing.
|
|
20
|
+
|
|
21
|
+
## Dependency Analysis Rules
|
|
22
|
+
|
|
23
|
+
1. **Identify TECHNICAL dependencies** between requirements:
|
|
24
|
+
- Database schema must exist before CRUD endpoints
|
|
25
|
+
- Auth infrastructure before protected routes
|
|
26
|
+
- Shared models/types before their consumers
|
|
27
|
+
- Configuration/environment setup before feature code
|
|
28
|
+
- Core utilities before features that use them
|
|
29
|
+
|
|
30
|
+
2. **Group by TECHNICAL AFFINITY**, not by requirement category:
|
|
31
|
+
- Requirements sharing the same domain model belong together
|
|
32
|
+
- Infrastructure requirements precede feature requirements
|
|
33
|
+
- Integration requirements follow their component requirements
|
|
34
|
+
- Testing setup can parallel with early implementation
|
|
35
|
+
|
|
36
|
+
3. **Dependency graph must be a DAG**:
|
|
37
|
+
- No circular dependencies
|
|
38
|
+
- Phase N can only depend on phases < N
|
|
39
|
+
- Multiple phases CAN share dependencies (fan-in)
|
|
40
|
+
- A phase CAN enable multiple later phases (fan-out)
|
|
41
|
+
- NOT all phases need to be sequential — identify parallelizable work
|
|
42
|
+
|
|
43
|
+
## Output Format
|
|
44
|
+
|
|
45
|
+
Write the file to: `{{output_path}}`
|
|
46
|
+
|
|
47
|
+
Use this EXACT format:
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
# Roadmap
|
|
51
|
+
|
|
52
|
+
Story: {{story_version}} {{story_title}}
|
|
53
|
+
|
|
54
|
+
## Phases
|
|
55
|
+
|
|
56
|
+
### Phase N: [Descriptive Name — NOT category name]
|
|
57
|
+
|
|
58
|
+
- **Goal:** [Specific goal grounded in requirements — NOT "Implement X requirements"]
|
|
59
|
+
- **Depends on:** [Comma-separated phase numbers based on TECHNICAL analysis, or None]
|
|
60
|
+
- **Requirements:** [Comma-separated requirement IDs from REQUIREMENTS.md]
|
|
61
|
+
- **Risk:** [Primary risk from research for this phase]
|
|
62
|
+
- **Estimated complexity:** Low|Medium|High
|
|
63
|
+
- **Status:** Pending
|
|
64
|
+
|
|
65
|
+
## Progress
|
|
66
|
+
|
|
67
|
+
| Phase | Status | Plans |
|
|
68
|
+
|-------|--------|-------|
|
|
69
|
+
| N | Pending | 0/0 |
|
|
70
|
+
|
|
71
|
+
## Phase Rationale
|
|
72
|
+
|
|
73
|
+
[2-3 sentences explaining WHY phases are ordered this way, citing specific technical dependencies discovered in research]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Phase Goal Anti-Patterns (DO NOT USE)
|
|
77
|
+
|
|
78
|
+
- "Implement core features requirements" — circular, says nothing
|
|
79
|
+
- "Setup infrastructure" — too vague
|
|
80
|
+
- "Implement X requirements" — restates the category name
|
|
81
|
+
- "Build the system" — meaningless
|
|
82
|
+
|
|
83
|
+
## Good Phase Goal Examples
|
|
84
|
+
|
|
85
|
+
- "Establish database schema, authentication flow, and API foundation that all feature phases depend on"
|
|
86
|
+
- "Build real-time notification pipeline using WebSocket connections with Redis pub/sub backing"
|
|
87
|
+
- "Add rate limiting, input validation, and CSRF protection across all public endpoints"
|
|
88
|
+
|
|
89
|
+
## Phase Sizing Guidelines
|
|
90
|
+
|
|
91
|
+
- Each phase should have 3-8 requirements
|
|
92
|
+
- If a phase has 10+ requirements, it's too large — split it
|
|
93
|
+
- If a phase has 1-2 requirements, merge with a related phase
|
|
94
|
+
- Aim for 3-6 phases total for most projects
|
|
95
|
+
|
|
96
|
+
## Output Marker
|
|
97
|
+
|
|
98
|
+
When complete, output:
|
|
99
|
+
```
|
|
100
|
+
## ROADMAP COMPLETE
|
|
101
|
+
Phases: [count]
|
|
102
|
+
Dependency depth: [max chain length]
|
|
103
|
+
Parallelizable: [phases with same/no dependencies that could run concurrently]
|
|
104
|
+
```
|