Two code-confirmed defects in `gsd-tools phase complete` (re-verified against next; the three severe corruption paths the issue filed are superseded by the ADR-1769 Transition Module migration + #2012, so this is the confirmed remainder). 1. Milestone-end mislabel (isLastPhase). The milestone-end determination only cleared isLastPhase when a HIGHER-numbered phase existed, so completing the numerically-highest phase out of order (e.g. Phase 10 before Phase 9) stamped STATE.md `Status: Milestone complete` while a lower phase was still outstanding. Added a lower-phase check: after the existing higher-phase scans, if any earlier phase in the current milestone has an unchecked roadmap checkbox (`[ ]`), isLastPhase becomes false AND next_phase/next_phase_name point at the LOWEST outstanding lower phase — so STATE.md advances to the real gap instead of parking on the just-completed phase. A completed phase always has `[x]` (phase.complete sets it), so all-lower-complete still reports milestone-end; heading-only roadmaps (no checkboxes) retain prior behavior. The checkbox regex mirrors the sibling phasePattern's anchoring (whitespace/bold + required `:`) so unrelated checklist lines mentioning "Phase N" don't match. 2. Workstream root-fallback (no guard). cmdPhaseComplete resolves every path via planningDir(cwd); with a `workstreams/` dir present but no active workstream and no --ws, that returns root `.planning`, so phase.complete wrote STATE.md/ ROADMAP.md (and the mislabel) into the shared root other workstreams read. Added the same #1912 fail-safe guard init.progress got: refuse (asking for `--ws`/active workstream) instead of silently writing root. Resolution itself was already wired globally (resolveActiveWorkstream: --ws > GSD_WORKSTREAM > pointer, set in bin/gsd-tools.cjs), so only the refusal guard was missing. The workstream-mode detection (`listAvailableWorkstreams`) is extracted into planning-workspace.cts as the single source of truth and consumed by BOTH init.progress and phase.complete, so the two fail-safe paths cannot drift. Tests (tests/phase.test.cjs, new #2028 describe): out-of-order completion becomes `Ready to plan` with is_last_phase=false, next_phase pointing at the outstanding phase and Current Phase advancing to it (not the completed phase); all-lower- complete still reports milestone-end; workstream-mode-no-active refuses with an `--ws` hint; `--ws` completes in the workstream leaving root untouched; flat mode unaffected. Fail-first verified locally via direct gsd-tools invocation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
884 B
type, pr
| type | pr |
|---|---|
| Fixed | 2066 |
phase complete no longer marks a milestone done out of order, nor silently writes root state in workstream mode. Completing the numerically-highest phase while an earlier phase was still outstanding wrongly flipped STATE.md to Status: Milestone complete (the milestone-end check only looked for higher-numbered phases, so an out-of-order completion — e.g. Phase 10 before Phase 9 — read as the end). It now reports milestone-end only when every lower-numbered phase in the milestone is checked complete. Separately, in workstream mode with no active workstream, phase complete previously fell back to root .planning and wrote STATE.md/ROADMAP.md (and the mislabel) into the shared root other workstreams read; it now fails safe — asking for --ws <name> or an active workstream — mirroring the existing init progress guard. (#2066)