fix(roadmap): prevent milestone version substring matches

This commit is contained in:
Tom Boucher
2026-05-06 20:37:18 -04:00
parent b093301d7d
commit ca148036d2
2 changed files with 26 additions and 1 deletions

View File

@@ -509,6 +509,31 @@ describe('extractCurrentMilestone', () => {
expect(result).toMatch(/^##\s+v0\.9 Local-First Bus/m);
});
it('bug-2641: v0.1 must not substring-match v0.10 in markdown heading anchor path', async () => {
const roadmap = `# Roadmap
## v0.10 Future Milestone
### Phase 7: Wrong Phase
**Goal:** This is from v0.10, not v0.1.
## v0.1 Active Milestone
### Phase 1: Right Phase
**Goal:** This is the active milestone.
`;
const state = `---\nmilestone: v0.1\n---\n# State\n`;
await writeFile(join(tmpDir, '.planning', 'STATE.md'), state);
await writeFile(join(tmpDir, '.planning', 'ROADMAP.md'), roadmap);
const result = await extractCurrentMilestone(roadmap, tmpDir);
expect(result).toContain('### Phase 1: Right Phase');
expect(result).toContain('This is the active milestone');
expect(result).not.toContain('Phase 7: Wrong Phase');
expect(result).not.toContain('This is from v0.10');
});
// ─── Bug #2641 (review hardening): substring-version trap ───
it('bug-2641: v0.1 must not substring-match <summary>v0.10 …</summary>', async () => {
// The fallback regex anchors on `escapedVersion` inside `<summary>` text.

View File

@@ -201,7 +201,7 @@ export async function extractCurrentMilestone(content: string, projectDir: strin
// Find section matching this version
const escapedVersion = escapeRegex(version);
const sectionPattern = new RegExp(
`(^#{1,3}\\s+.*${escapedVersion}[^\\n]*)`,
`(^#{1,3}\\s+.*${escapedVersion}(?![\\d.])[^\\n]*)`,
'mi'
);
const sectionMatch = content.match(sectionPattern);