fix: surface unmapped active requirements when all milestones complete (#1805)

This commit is contained in:
TÂCHES 2026-03-21 11:45:44 -06:00 committed by GitHub
parent 8fecf6a3ef
commit 28a3387e2b
2 changed files with 88 additions and 1 deletions

View file

@ -471,6 +471,10 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
}
// All milestones complete
const lastEntry = registry[registry.length - 1];
const activeReqs = requirements.active ?? 0;
const completionNote = activeReqs > 0
? `All milestones complete. ${activeReqs} active requirement${activeReqs === 1 ? '' : 's'} in REQUIREMENTS.md ${activeReqs === 1 ? 'has' : 'have'} not been mapped to a milestone.`
: 'All milestones complete.';
return {
activeMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
activeSlice: null,
@ -478,7 +482,7 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
phase: 'complete',
recentDecisions: [],
blockers: [],
nextAction: 'All milestones complete.',
nextAction: completionNote,
registry,
requirements,
progress: {

View file

@ -319,6 +319,89 @@ Continue from step 2.
}
}
// ─── Test 7b: complete with active requirements → surfaces unmapped reqs ──
console.log('\n=== complete with active requirements → surfaces unmapped reqs ===');
{
const base = createFixtureBase();
try {
writeRoadmap(base, 'M001', `# M001: Test Milestone
**Vision:** Test complete phase with unmapped requirements.
## Slices
- [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
> After this: Done.
`);
writeMilestoneValidation(base, 'M001');
writeMilestoneSummary(base, 'M001', `# M001 Summary\n\nMilestone complete.`);
writeRequirements(base, `# Requirements
## Active
### REQ01 First active requirement
- Status: active
### REQ02 Second active requirement
- Status: active
## Validated
### REQ03 Validated requirement
- Status: validated
`);
const state = await deriveState(base);
assertEq(state.phase, 'complete', 'complete-with-reqs: phase is complete');
assertTrue(
state.nextAction.includes('2 active requirements'),
'complete-with-reqs: nextAction mentions 2 active requirements'
);
assertTrue(
state.nextAction.includes('REQUIREMENTS.md'),
'complete-with-reqs: nextAction mentions REQUIREMENTS.md'
);
} finally {
cleanup(base);
}
}
// ─── Test 7c: complete with no active requirements → standard message ──
console.log('\n=== complete with no active requirements → standard message ===');
{
const base = createFixtureBase();
try {
writeRoadmap(base, 'M001', `# M001: Test Milestone
**Vision:** Test complete phase with all requirements validated.
## Slices
- [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
> After this: Done.
`);
writeMilestoneValidation(base, 'M001');
writeMilestoneSummary(base, 'M001', `# M001 Summary\n\nMilestone complete.`);
writeRequirements(base, `# Requirements
## Validated
### REQ01 Validated requirement
- Status: validated
`);
const state = await deriveState(base);
assertEq(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
assertEq(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
} finally {
cleanup(base);
}
}
// ─── Test 8: blocked dependencies ──────────────────────────────────────
console.log('\n=== blocked dependencies ===');
{