Merge pull request #1447 from rahuljordashe/fix/phase-complete-roadmap-rollup
fix: cmdPhaseComplete updates Plans column and plan checkboxes
This commit is contained in:
@@ -707,10 +707,12 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|||||||
const cells = fullRow.split('|').slice(1, -1);
|
const cells = fullRow.split('|').slice(1, -1);
|
||||||
if (cells.length === 5) {
|
if (cells.length === 5) {
|
||||||
// 5-col: Phase | Milestone | Plans | Status | Completed
|
// 5-col: Phase | Milestone | Plans | Status | Completed
|
||||||
|
cells[2] = ` ${summaryCount}/${planCount} `;
|
||||||
cells[3] = ' Complete ';
|
cells[3] = ' Complete ';
|
||||||
cells[4] = ` ${today} `;
|
cells[4] = ` ${today} `;
|
||||||
} else if (cells.length === 4) {
|
} else if (cells.length === 4) {
|
||||||
// 4-col: Phase | Plans | Status | Completed
|
// 4-col: Phase | Plans | Status | Completed
|
||||||
|
cells[1] = ` ${summaryCount}/${planCount} `;
|
||||||
cells[2] = ' Complete ';
|
cells[2] = ' Complete ';
|
||||||
cells[3] = ` ${today} `;
|
cells[3] = ` ${today} `;
|
||||||
}
|
}
|
||||||
@@ -727,6 +729,18 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|||||||
`$1${summaryCount}/${planCount} plans complete`
|
`$1${summaryCount}/${planCount} plans complete`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Mark completed plan checkboxes (safety net for missed per-plan updates)
|
||||||
|
for (const summaryFile of phaseInfo.summaries) {
|
||||||
|
const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
|
||||||
|
if (!planId) continue;
|
||||||
|
const planEscaped = escapeRegex(planId);
|
||||||
|
const planCheckboxPattern = new RegExp(
|
||||||
|
`(-\\s*\\[) (\\]\\s*${planEscaped})`,
|
||||||
|
'i'
|
||||||
|
);
|
||||||
|
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
||||||
|
}
|
||||||
|
|
||||||
fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
|
fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
|
||||||
|
|
||||||
// Update REQUIREMENTS.md traceability for this phase's requirements
|
// Update REQUIREMENTS.md traceability for this phase's requirements
|
||||||
|
|||||||
@@ -1619,6 +1619,130 @@ describe('phase complete command', () => {
|
|||||||
// Verify compound format preserved
|
// Verify compound format preserved
|
||||||
assert.ok(state.match(/Phase:.*of\s+1/), 'should preserve "of N" in compound Phase format');
|
assert.ok(state.match(/Phase:.*of\s+1/), 'should preserve "of N" in compound Phase format');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('updates Plans Complete column in 4-column progress table', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
- [ ] Phase 1: Foundation
|
||||||
|
- [ ] Phase 2: API
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
**Plans:** 1 plans
|
||||||
|
|
||||||
|
### Phase 2: API
|
||||||
|
**Goal:** Build API
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|
|-------|----------------|--------|-----------|
|
||||||
|
| 1. Foundation | 0/1 | Not started | - |
|
||||||
|
| 2. API | 0/1 | Not started | - |
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 01\n**Status:** In progress\n**Current Plan:** 01-01\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-api'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('phase complete 1', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
const rowMatch = roadmap.match(/^\|[^\n]*1\. Foundation[^\n]*$/m);
|
||||||
|
assert.ok(rowMatch, 'table row should exist');
|
||||||
|
const cells = rowMatch[0].split('|').slice(1, -1).map(c => c.trim());
|
||||||
|
assert.strictEqual(cells.length, 4, 'should have 4 columns');
|
||||||
|
assert.strictEqual(cells[1], '1/1', 'Plans Complete column should be updated to 1/1');
|
||||||
|
assert.ok(cells[2].includes('Complete'), 'Status column should be Complete');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates Plans Complete column in 5-column progress table', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
- [ ] Phase 1: Foundation
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
**Plans:** 1 plans
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
| Phase | Milestone | Plans Complete | Status | Completed |
|
||||||
|
|-------|-----------|----------------|--------|-----------|
|
||||||
|
| 1. Foundation | v1.0 | 0/1 | Planned | |
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 01\n**Status:** In progress\n**Current Plan:** 01-01\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
|
||||||
|
const result = runGsdTools('phase complete 1', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
const rowMatch = roadmap.match(/^\|[^\n]*1\. Foundation[^\n]*$/m);
|
||||||
|
assert.ok(rowMatch, 'table row should exist');
|
||||||
|
const cells = rowMatch[0].split('|').slice(1, -1).map(c => c.trim());
|
||||||
|
assert.strictEqual(cells.length, 5, 'should have 5 columns');
|
||||||
|
assert.strictEqual(cells[2], '1/1', 'Plans Complete column should be updated to 1/1');
|
||||||
|
assert.ok(cells[3].includes('Complete'), 'Status column should be Complete');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('marks plan-level checkboxes on phase complete', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
- [ ] Phase 1: Foundation
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
**Plans:** 2 plans
|
||||||
|
|
||||||
|
Plans:
|
||||||
|
- [ ] 01-01-PLAN.md \u2014 Schema migration
|
||||||
|
- [ ] 01-02-PLAN.md \u2014 Auth setup
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 01\n**Status:** In progress\n**Current Plan:** 01-02\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-02-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-02-SUMMARY.md'), '# Summary');
|
||||||
|
|
||||||
|
const result = runGsdTools('phase complete 1', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
assert.ok(roadmap.includes('[x] 01-01-PLAN.md'), 'plan 01-01 checkbox should be checked');
|
||||||
|
assert.ok(roadmap.includes('[x] 01-02-PLAN.md'), 'plan 01-02 checkbox should be checked');
|
||||||
|
assert.ok(!roadmap.includes('[ ] 01-01-PLAN.md'), 'plan 01-01 should not remain unchecked');
|
||||||
|
assert.ok(!roadmap.includes('[ ] 01-02-PLAN.md'), 'plan 01-02 should not remain unchecked');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user