From ba6a3efc3ee9d5243c596642f7fe489676aadf9e Mon Sep 17 00:00:00 2001 From: Ben Lamm Date: Sat, 2 May 2026 23:34:22 -0400 Subject: [PATCH] fix(#2641): strip YAML quotes from STATE.md milestone version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
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. --- sdk/src/query/roadmap.test.ts | 27 +++++++++++++++++++++++++++ sdk/src/query/roadmap.ts | 10 ++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/sdk/src/query/roadmap.test.ts b/sdk/src/query/roadmap.test.ts index 94ea62565..19e11fbbe 100644 --- a/sdk/src/query/roadmap.test.ts +++ b/sdk/src/query/roadmap.test.ts @@ -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
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 + +
+v0.9 Local-First Bus (active) — Phase Details + +### Phase 3: Polish +**Goal:** Add polish. +
+`; + 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
tag (e.g.
) ─── it('bug-2641: finds active milestone in
vX.Y …', async () => { // GitHub auto-renders
for sections that should default to diff --git a/sdk/src/query/roadmap.ts b/sdk/src/query/roadmap.ts index eae508938..73bcf3d1c 100644 --- a/sdk/src/query/roadmap.ts +++ b/sdk/src/query/roadmap.ts @@ -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 { - // 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 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 */ }