fix(#2135): anchor milestone heading regex + strip delimiter, widen preserve guard
getMilestoneInfo's `##` heading regex was unanchored (no `^`/`m`), so it matched a `##` quoted mid-line inside a Milestones bullet and captured a delimiter-led fragment into milestone_name — clobbering the curated name on every phase transition. roadmap-parser.cts (load-bearing): - Consult the 🚧 name-bearing marker FIRST (reorder; it already existed but was shadowed by a spuriously-successful heading match). - Anchor the `##` regex to line start (`^` + `m` flag) so a heading quoted in backticks/prose can no longer match. - stripLeadingDelimiter removes a leading em/en-dash/colon/hyphen run that .trim() cannot (the `## vX.Y — Name` convention). state.cts (defense in depth): - Widen the #948 preserve guard from 'derived equals placeholder' to 'derived does not look like a name' (non-empty, not placeholder, not punctuation-led), so a future bad derive preserves the curated name instead of silently overwriting it. Closes #2135
This commit is contained in:
@@ -1573,8 +1573,18 @@ function syncStateFrontmatter(content: string, cwd: string | undefined): string
|
||||
// existing frontmatter already holds; only an empty derived value falls through
|
||||
// to this guard (the primary #905 preserve path below handles that).
|
||||
const MILESTONE_NAME_PLACEHOLDER = 'milestone';
|
||||
// #2135: widen the preserve guard. A bad derive is not always the literal
|
||||
// placeholder — getMilestoneInfo can return a delimiter-led fragment
|
||||
// ("— Active Milestone") when the roadmap regex mis-binds. Preserve the
|
||||
// existing curated name unless the derived value actually looks like a name:
|
||||
// non-empty, not the placeholder, and not punctuation-led.
|
||||
const derivedName = derivedFm['milestone_name'];
|
||||
const derivedLooksLikeName = typeof derivedName === 'string'
|
||||
&& derivedName.length > 0
|
||||
&& derivedName !== MILESTONE_NAME_PLACEHOLDER
|
||||
&& !/^[\s—–:-]/.test(derivedName);
|
||||
if (
|
||||
derivedFm['milestone_name'] === MILESTONE_NAME_PLACEHOLDER &&
|
||||
!derivedLooksLikeName &&
|
||||
existingFm['milestone_name'] &&
|
||||
existingFm['milestone_name'] !== MILESTONE_NAME_PLACEHOLDER
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user