fix(#1591): phase.complete recognizes checkbox-list phases in isLastPhase fallback (#1819)

* fix(#1591): phase.complete recognizes checkbox-list phases in the isLastPhase fallback

When the active milestone's phase checklist is written as `- [ ] Phase N:`
checkbox items inside a <details> block (the @Azd325 structure) and the next
phase has no directory yet, the disk-based next-phase resolver finds nothing
and phase.complete falls back to the roadmap-enumeration guard at the
isLastPhase site. That guard's phasePattern was heading-only
(/#{2,4}\s*Phase…/), so it never matched checklist items → is_last_phase=true
and next_phase=null on a mid-milestone phase, and STATE.md was wrongly marked
'Milestone complete' with total_phases decremented.

Broaden the marker alternation to match BOTH heading-style (### Phase N:) and
checkbox-list items (- [ ] Phase N: / - [x] Phase N:); the number/name
captures are unchanged. extractCurrentMilestone already surfaces the
<details>-wrapped checklist correctly, so no parser change is needed. The
heading-only sibling patterns elsewhere in phase.cts are left untouched
(scope discipline — only the reproduced isLastPhase fallback is changed).

Regression: a phase complete 36 on a <details>-wrapped v2.0 checklist
(Phases 36-38, only 36 has a dir) returns is_last_phase=false, next_phase=37,
and does NOT flip STATE.md to 'Milestone complete'.

* docs(#1591): add changeset fragment for phase.complete checkbox-list fix

* test(#1752): add total_phases-preservation regression for the #1591 follow-up

#1752 is the scoped follow-up to #1591 — same <details>-wrapped-checkbox
defect, with the additional emphasis on the total_phases decrement cascade.
The #1591 fix (is_last_phase=false) already resolves it: with all 8 phase
dirs on disk, phase.complete 36 on a v2.0 <details> checklist leaves
total_phases at 8 (not decremented to 7) and does not flip STATE.md to
'Milestone complete'. Verified manually before adding the test.

Add the #1752 regression case (8 phase dirs, curated total_phases: 8) to the
phase complete command block in tests/phase.test.cjs, and update the changeset
to reference both issues (#1591, #1752) since this is one user-facing change
resolving both.
This commit is contained in:
Tom Boucher
2026-06-28 22:41:57 -04:00
committed by GitHub
parent 38c2c1805e
commit 4f6fda852e
3 changed files with 197 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
---
type: Fixed
pr: 1819
---
**`phase.complete` no longer reports a false `is_last_phase` on a `<details>`-wrapped checkbox checklist (#1591, #1752)** — when the active milestone's phase checklist was written as `- [ ] Phase N:` checkbox items inside a `<details>` block and the next phase had no directory on disk yet (still in planning), `phase.complete`'s `isLastPhase` roadmap-enumeration fallback used a heading-only pattern (`/#{2,4}\s*Phase…/`) that never matched checkbox items. It returned `is_last_phase: true, next_phase: null` on a mid-milestone phase and — via the milestone-complete cascade — wrongly flipped STATE.md to `Milestone complete` and decremented `progress.total_phases` (e.g. 8 → 7). The pattern now matches both heading-style (`### Phase N:`) and checkbox-list phases (`- [ ] Phase N:` / `- [x] Phase N:`); `extractCurrentMilestone` already surfaces the `<details>`-wrapped checklist correctly, so no parser change was needed. Only the reproduced `phase.complete` fallback is changed; the heading-only sibling patterns elsewhere in `phase.cts` are untouched.