fix(#2641): strip YAML quotes from STATE.md milestone version
Address CodeRabbit review on PR #3046: extractCurrentMilestone read the `milestone:` value from STATE.md frontmatter via `.trim()` only, while parseMilestoneFromState() and getMilestoneInfo() both also strip surrounding YAML quotes via `.replace(/^["']|["']$/g, '')`. For projects whose STATE.md uses quoted YAML (`milestone: "v0.9"`), `version` carried literal quotes, `escapedVersion` became `\"v0\.9\"`, and neither the markdown-heading regex nor the new <details><summary> fallback could match anything — falling through to stripShippedMilestones() and reintroducing the same archived-milestone misrouting this PR addresses. Strip quotes for parity. Three-line addition + one new test. All 41 roadmap.test.ts tests pass.
This commit is contained in:
@@ -437,6 +437,33 @@ describe('extractCurrentMilestone', () => {
|
||||
expect(result).not.toContain('Old phase');
|
||||
});
|
||||
|
||||
// ─── Bug #2641 (CodeRabbit follow-up): quoted YAML version normalization ───
|
||||
it('bug-2641: handles quoted YAML version (milestone: "v0.9") in STATE.md', async () => {
|
||||
// STATE.md may use quoted YAML (`milestone: "v0.9"`). Without quote-stripping,
|
||||
// version would carry literal quotes, escapedVersion would be `\"v0\.9\"`,
|
||||
// and neither the markdown-heading regex nor the <details><summary> fallback
|
||||
// would match — falling through to stripShippedMilestones and reintroducing
|
||||
// the archived-milestone misrouting this PR addresses. Parity with
|
||||
// parseMilestoneFromState() and getMilestoneInfo() (which both strip quotes).
|
||||
const roadmap = `# Roadmap
|
||||
|
||||
<details>
|
||||
<summary>v0.9 Local-First Bus (active) — Phase Details</summary>
|
||||
|
||||
### Phase 3: Polish
|
||||
**Goal:** Add polish.
|
||||
</details>
|
||||
`;
|
||||
const stateQuoted = `---\nmilestone: "v0.9"\n---\n# State\n`;
|
||||
await writeFile(join(tmpDir, '.planning', 'STATE.md'), stateQuoted);
|
||||
await writeFile(join(tmpDir, '.planning', 'ROADMAP.md'), roadmap);
|
||||
|
||||
const result = await extractCurrentMilestone(roadmap, tmpDir);
|
||||
|
||||
expect(result).toContain('### Phase 3: Polish');
|
||||
expect(result).toMatch(/^##\s+v0\.9 Local-First Bus/m);
|
||||
});
|
||||
|
||||
// ─── Bug #2641: tolerate attributes on <details> tag (e.g. <details open>) ───
|
||||
it('bug-2641: finds active milestone in <details open><summary>vX.Y …</summary>', async () => {
|
||||
// GitHub auto-renders <details open> for sections that should default to
|
||||
|
||||
@@ -153,13 +153,19 @@ export async function getMilestoneInfo(projectDir: string, workstream?: string):
|
||||
* @returns Content scoped to current milestone
|
||||
*/
|
||||
export async function extractCurrentMilestone(content: string, projectDir: string, workstream?: string): Promise<string> {
|
||||
// Get version from STATE.md frontmatter
|
||||
// Get version from STATE.md frontmatter.
|
||||
// Strip optional surrounding YAML quotes (e.g. `milestone: "v0.9"`) for parity
|
||||
// with parseMilestoneFromState() above and getMilestoneInfo()'s STATE.md path.
|
||||
// Without this, a quoted version yields `escapedVersion = '\\"v0\\.9\\"'`
|
||||
// which matches neither markdown headings nor <summary> text, falling
|
||||
// through to stripShippedMilestones() — and reintroducing the same archived-
|
||||
// milestone misrouting this fallback addresses.
|
||||
let version: string | null = null;
|
||||
try {
|
||||
const stateRaw = await readFile(planningPaths(projectDir, workstream).state, 'utf-8');
|
||||
const milestoneMatch = stateRaw.match(/^milestone:\s*(.+)/m);
|
||||
if (milestoneMatch) {
|
||||
version = milestoneMatch[1].trim();
|
||||
version = milestoneMatch[1].trim().replace(/^["']|["']$/g, '');
|
||||
}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user