* fix(phase): parse remove --force regardless of position
* chore(changeset): add fragment for phase-remove flag fix
* fix(phase): fail when phase.remove target is missing
* refactor(query): extract phase lifecycle policy module
* refactor(query): extract phase fs and roadmap mutation adapters
* fix(sdk): propagate non-ENOENT readdir errors in phase-filesystem-adapter (CR finding 1)
Swallow only ENOENT in listDirectories; rethrow EACCES, EIO, and other
unexpected errors so callers surface real failures rather than silently
treating a permission-denied phases dir as empty.
Also adds regression test: EACCES from readdir now propagates as thrown
error instead of returning [].
* fix(sdk): propagate non-ENOENT readFile errors in phase-roadmap-mutation (CR finding 4)
readModifyWriteRoadmapMd now falls back to empty content only on ENOENT;
EACCES, EIO, and other errors are rethrown so a subsequent write cannot
clobber real roadmap content that is temporarily unreadable.
Regression tests: EACCES propagates; absent ROADMAP.md still starts empty.
* fix(sdk): omit Depends on: Phase 0 for first sequential phase; align prefix grammar (CR findings 2+3)
Finding 2: buildPhaseRoadmapEntry now omits the "Depends on" line when
phaseId == 1 (prevPhase would be 0, which is not a valid predecessor).
The guard is `prevPhase < 1` so future phase-0 configs are also safe.
Finding 3: collectDecimalSuffixesFromDirNames regex prefix pattern
updated from `[A-Z]{1,6}` to `[A-Z][A-Z0-9]*` (case-insensitive flag
added), matching the grammar used by scanSequentialMaxPhaseFromDirs.
Prevents k014 parity drift for alphanumeric project-code prefixes longer
than six characters or containing digits.
Regression tests for both fixes included.
* fix(3259): non-mutating --help guard for native query handlers; reject --help as milestone version
Adds a dispatcher-level guard in query-dispatch.ts that short-circuits
to a non-mutating help stub whenever --help/-h appears in args destined
for a native mutating handler (fail-closed by default). Adds defense-
in-depth in milestoneComplete to reject --help/-h as a version value
before any disk write. Regression tests cover: per-handler --help guard,
registry-driven invariant across all mutating commands, handler-level
GSDError for both flags, and preservation of the #3019 CJS fallback
contract.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset fragment for #3272
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: reproduce phase.add dry-run + flag validation gaps (#3226)
Add failing tests for:
- --dry-run silently absorbed into description (symptom A)
- Unknown --flag should return validation error (symptom C)
- ### Phase N: ROADMAP heading scan verification (symptom B)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): phase.add honors --dry-run; rejects unknown flags (#3226)
- Add flag parser to phaseAdd: strip recognized flags (--dry-run) from
args before positional parsing so they never silently become description
or customId values
- --dry-run computes the next phase number and roadmap_entry string but
skips mkdir, writeFile, and readModifyWriteRoadmapMd; returns
{ dry_run: true, roadmap_entry } alongside normal fields
- Any unrecognized --flag throws a Validation GSDError naming the flag
- ROADMAP ### Phase N: heading scan for numbering (symptom B) was already
correct; verified with new regression test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* changeset: pr=3246 for #3226
* fix(sdk): phase.add scans disk AND roadmap (union, not fallback)
Address CodeRabbit finding: the conditional `if (maxPhase === 0)` guard
around the filesystem scan meant that if ROADMAP had any phases but disk
was ahead (e.g. ROADMAP max=10, dirs include 12-*), phase.add would
pick 11 and collide with the existing directory.
Remove the guard: always scan on-disk phase directories and take the
max across both ROADMAP and filesystem (union semantics).
All 57 phase-lifecycle tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: reproduce phase.add concurrent ID collision (CR finding)
Two concurrent phase.add calls against the same project observe
maxPhase before the lock is held, producing duplicate phase IDs.
Adds a Promise.all regression test that asserts both calls succeed
with distinct phase numbers {11, 12}.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): compute phase number under roadmap lock (CR finding)
Move maxPhase/newPhaseId/dirName computation inside the
readModifyWriteRoadmapMd callback so the entire read → compute → write
cycle is serialised under the lock. Previously, two concurrent
phase.add calls could both observe maxPhase=N before either acquired
the lock, then both write with phase ID N+1 — producing duplicate IDs.
In dry-run mode (no write, no race) the computation still happens
outside the lock to avoid unnecessary contention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a case where the active milestone is the last <details> block but
footer text (--- / *Last updated*) follows </details>, triggering the
fast-path to replace in the footer instead of inside the block.
Closes#2743
Filesystem fallback regex /^(\d+)-/ missed directories like CK-45-foundation
when project_code is configured. Updated to /^(?:[A-Z][A-Z0-9]*-)?(\d+)-/i.
Addresses coderabbit finding on PR #2737.
phaseAdd's phase-number regex only matched heading format (## Phase N:),
missing bullet checklist (- [x] Phase N:) and bold (**Phase N:**) entries.
When zero regex matches, newPhaseId defaulted to 1.
Fix: broaden regex to match all three formats, and add filesystem fallback
scanning .planning/phases/ when ROADMAP scan finds nothing.
Closes#2726
When **Plans:** appears on its own line with no inline value, the
planCountPattern regex crosses the newline and destroys the first
plan checkbox line by replacing it with the literal "N/N plans complete"
string.
This test documents the expected correct behavior and will fail until
the planCountPattern regex is fixed.
* feat(sdk): add typed query foundation and gsd-sdk query (Phase 1)
Add sdk/src/query registry and handlers with tests, GSDQueryError, CLI query wiring, and supporting type/tool-scoping hooks. Update CHANGELOG. Vitest 4 constructor mock fixes in milestone-runner tests.
Made-with: Cursor
* fix(2137): skip worktree isolation when .gitmodules detected
When a project contains git submodules, worktree isolation cannot
correctly handle submodule commits — three separate gaps exist in
worktree setup, executor commit protocol, and merge-back. Rather
than patch each gap individually, detect .gitmodules at phase start
and fall back to sequential execution, which handles submodules
transparently (Option B).
Affected workflows: execute-phase.md, quick.md
---------
Co-authored-by: David Sienkowski <dave@sienkowski.com>