fix(#2786): skip sentinel phase ids in phase-complete stage 2 heading scan (#3130)

* fix(#2786): skip sentinel phase ids in phase-complete stage 2 heading scan

Stage 2 of the next-phase cascade accepted any higher-numbered roadmap
heading without checking the 999.x backlog sentinel convention that stage 1
already checks. A Phase 999.1: Backlog Item heading was treated as the next
real phase, advancing STATE.md into the backlog and making the milestone
perpetually 'Ready to plan' instead of 'All phases complete'.

Added isSentinelPhaseId(pm[1]) guard (mirrors stage 1's /^999(?:\.|$)/ check)
so both sentinel ranges (0.x drafts, 999.x backlog) are skipped.

* chore(#2786): backfill changeset PR number 3130

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-06 18:42:39 -04:00
committed by GitHub
parent 1c9f6a08e3
commit 94bf32a2bb
2 changed files with 10 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3130
---
**`phase complete` no longer advances `next_phase` into 999.x backlog headings** — the roadmap heading scan (stage 2 of the next-phase cascade) accepted any higher-numbered heading without checking the sentinel convention, so a `Phase 999.1: Backlog Item` heading was treated as the next real phase. Sentinel phase ids (999.x backlog, 0.x drafts) are now skipped. (#2786)

View File

@@ -2576,6 +2576,11 @@ function cmdPhaseComplete(cwd: string, phaseNum: string, raw: boolean): void {
);
let pm: RegExpExecArray | null;
while ((pm = phasePattern.exec(roadmapForPhases)) !== null) {
// #2786: skip sentinel phase ids (999.x backlog, 0.x drafts) — stage 1
// already skips 999 dirs on disk; stage 2's heading scan must not
// advance into backlog headings. Mirrors the /^999(?:\.|$)/ guard
// stage 1 uses at line 2536, but via isSentinelPhaseId for both ranges.
if (isSentinelPhaseId(pm[1])) continue;
if (comparePhaseNum(pm[1], phaseNum) > 0) {
nextPhaseNum = pm[1];
nextPhaseName = pm[2]