fix(#1836): count prefixed milestone phase dirs (#1844)

* fix(#1836): count prefixed milestone phase dirs

* chore(#1836): add changeset fragment
This commit is contained in:
Tom Boucher
2026-06-30 20:43:28 -04:00
committed by GitHub
parent 9e32462dd8
commit 88a7500e91
3 changed files with 48 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 1844
---
**`init milestone-op` now counts project_code-prefixed phase directories correctly** — fully shipped milestones using the standard prefixed directory layout no longer report `completed_phases: 0` or stay falsely incomplete. (#1844)

View File

@@ -64,7 +64,7 @@ const {
extractCurrentMilestone,
} = roadmapParser;
const { pathExistsInternal, generateSlugInternal, toPosixPath } = coreUtils;
const { normalizePhaseName, phaseTokenMatches } = phaseId;
const { normalizePhaseName, phaseTokenMatches, stripProjectCodePrefix } = phaseId;
const { pruneOrphanedWorktrees } = worktreeSafety;
const {
@@ -1204,7 +1204,7 @@ function cmdInitMilestoneOp(cwd: string, raw: boolean): void {
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
for (const e of entries) {
if (!e.isDirectory()) continue;
const m = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/);
const m = stripProjectCodePrefix(e.name).match(/^(\d+[A-Z]?(?:\.\d+)*)/);
if (!m) continue;
diskPhaseDirs.set(canonicalizePhase(m[1]), e.name);
}

View File

@@ -967,6 +967,47 @@ describe('cmdInitMilestoneOp', () => {
assert.strictEqual(output.all_phases_complete, true);
});
test('project_code-prefixed phase directories count as completed milestone phases (#1836)', () => {
seedPhase(tmpDir, 'PROJ-01-setup', {
'PROJ-01-01-PLAN.md': '# Plan',
'PROJ-01-01-SUMMARY.md': '# Summary',
});
fs.writeFileSync(
path.join(tmpDir, '.planning', 'config.json'),
JSON.stringify({ project_code: 'PROJ' }, null, 2)
);
fs.writeFileSync(
path.join(tmpDir, '.planning', 'STATE.md'),
[
'---',
'gsd_state_version: 1.0',
'milestone: v1.0.0',
'milestone_name: Test Milestone',
'status: executing',
'---',
'',
].join('\n')
);
fs.writeFileSync(
path.join(tmpDir, '.planning', 'ROADMAP.md'),
[
'# Roadmap',
'',
'## 🚧 v1.0.0 Test Milestone',
'### Phase 1: Setup',
'',
].join('\n')
);
const result = runGsdTools('init milestone-op', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.phase_count, 1);
assert.strictEqual(output.completed_phases, 1);
assert.strictEqual(output.all_phases_complete, true);
});
test('archive directory scanning', () => {
fs.mkdirSync(path.join(tmpDir, '.planning', 'archive', 'v1.0'), { recursive: true });
fs.mkdirSync(path.join(tmpDir, '.planning', 'archive', 'v0.9'), { recursive: true });