fix(#2641): inject normalized ## heading from <details><summary> capture
Address CodeRabbit review on PR #3046: the prior commit returned only the body inside <details>...</details>, which fixed the `roadmapGetPhase` miss but left `roadmapAnalyze`'s downstream `data.milestones` scan (`/##\s*(.*v(\d+(?:\.\d+)+)[^(\n]*)/gi` at the bottom of roadmap.ts) without an active-milestone anchor in the returned slice. Now capture the <summary> text and prepend it as a synthesized `##` heading on the returned slice. This makes both `data.phases` (the original bug) AND `data.milestones` (the downstream consumer) surface the active milestone correctly for <details>-wrapped ROADMAPs. Also widened the inner tag to `<summary\b[^>]*>` for symmetry with the outer `<details\b[^>]*>` — both now tolerate attributes. Verified end-to-end against FAMP's v0.9 ROADMAP: - Before this commit (after PR #3046 base): milestones: [{heading: '# Phase 1: ... (v0.5.2 atomic bump)', version: 'v0.5.2'}] - After this commit: milestones: [{heading: 'v0.9 Local-First Bus', version: 'v0.9'}, {heading: '# Phase 1: ... (v0.5.2 ...)', version: 'v0.5.2'}] (The v0.5.2 entry is pre-existing noise from the loose `##\s*` regex matching the `### Phase 1: famp-bus (v0.5.2 atomic bump)` body heading; unrelated to this fix and out of scope for this PR.) Tests: - Updated the two `<details><summary>` tests to assert the synthesized `## v0.9 Local-First Bus` heading is present on the returned slice. - Added a 4th regression test (`roadmapAnalyze`) confirming `data.milestones` now contains the active milestone for <details>-wrapped ROADMAPs. - All 40 roadmap.test.ts tests pass.
This commit is contained in:
@@ -208,17 +208,28 @@ export async function extractCurrentMilestone(content: string, projectDir: strin
|
||||
// route phase lookups into archived milestones.
|
||||
//
|
||||
// <details\b[^>]*> tolerates attributes like <details open> and
|
||||
// <details class="...">. The lazy [\s\S]*? terminates on the first
|
||||
// </details>; nested <details> inside the active milestone are not
|
||||
// expected and would mis-anchor (acceptable; FAMP-style ROADMAPs do not
|
||||
// nest, and any project that does will fall through to the existing
|
||||
// stripShippedMilestones path with no regression vs. today's behavior).
|
||||
// <details class="...">. <summary\b[^>]*> tolerates the same on the
|
||||
// <summary> tag. The lazy [\s\S]*? terminates on the first </details>;
|
||||
// nested <details> inside the active milestone are not expected and would
|
||||
// mis-anchor (acceptable; FAMP-style ROADMAPs do not nest, and any project
|
||||
// that does will fall through to the existing stripShippedMilestones path
|
||||
// with no regression vs. today's behavior).
|
||||
//
|
||||
// We capture the <summary> text and prepend it as a normalized `##`
|
||||
// milestone heading on the returned slice. This keeps downstream consumers
|
||||
// that scan for `##` milestone headings (e.g. roadmapAnalyze's
|
||||
// data.milestones loop later in this file) producing a meaningful entry
|
||||
// for the active milestone instead of seeing an unanchored body.
|
||||
const detailsPattern = new RegExp(
|
||||
`<details\\b[^>]*>\\s*<summary>[^<]*${escapedVersion}[^<]*</summary>([\\s\\S]*?)</details>`,
|
||||
`<details\\b[^>]*>\\s*<summary\\b[^>]*>([^<]*${escapedVersion}[^<]*)</summary>([\\s\\S]*?)</details>`,
|
||||
'i'
|
||||
);
|
||||
const detailsMatch = content.match(detailsPattern);
|
||||
if (detailsMatch) return detailsMatch[1];
|
||||
if (detailsMatch) {
|
||||
const summary = detailsMatch[1].trim();
|
||||
const body = detailsMatch[2];
|
||||
return `## ${summary}\n${body}`;
|
||||
}
|
||||
return stripShippedMilestones(content);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user