Commit Graph

56 Commits

Author SHA1 Message Date
Tom Boucher
8d5f509edf fix(3266): preserve wave 0 and bucket plans by depends_on DAG in phase-plan-index (#3276)
* fix(3266): preserve wave 0 and bucket plans by depends_on DAG in phase-plan-index

Fixes two cooperating bugs in the phase-plan-index builder:

1. Wave 0 collapse: `parseInt(...) || 1` coerced parsed value `0` to `1` due to
   JS falsy default. Fixed with `Number.isNaN` guard.
2. depends_on ignored: wave-bucketing used only the `wave:` frontmatter field.
   Now replaced with Kahn's topological-level algorithm over `depends_on`:
   source nodes (no in-phase deps) → lowest level; each plan's level = max(deps'
   levels) + 1.  Declared `wave:` that disagrees with computed level emits a
   non-fatal warning on the result.  Cycle detection throws GSDError.

`PlanInfo` gains `depends_on: string[]`. `PhasePlanIndex` gains `warnings?: string[]`.
Both TS (`sdk/src/query/phase.ts`) and CJS twin (`get-shit-done/bin/lib/phase.cjs`)
fixed identically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add changeset for #3276

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(phase): resolve depends_on against canonical plan id (#3276 CR)

Build a secondary `canonicalToId` index alongside `planMap` so that a
dependency declared as '03-01' resolves to a descriptive plan stored
under '03-01-auth-hardening', preventing silent wave-ordering failures.
Applied at both DAG construction sites in phase.cjs and the SDK's
phase.ts (k014 parity). Regression test added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:25:05 -04:00
Tom Boucher
99b2bddd14 fix(milestone-archive): expose searched roots and parse canonical STATE milestone 2026-05-06 15:38:53 -04:00
Tom Boucher
56737c057b fix(verify): use active milestone phase roots for consistency checks 2026-05-06 15:30:45 -04:00
Tom Boucher
4847277082 fix(gsd-tools): support .planning/milestones/v*-phases/ layout in validators and find-phase
Fixes #3164

Validators and find-phase hardcoded phasesDir = .planning/phases/, so projects
using the milestone-archive layout (.planning/milestones/v*-phases/) had an empty
diskPhases set, triggering W006 for every active phase and find-phase returning
found:false.

Add collectDiskPhases(planBase) helper that scans both flat layout and all
.planning/milestones/v*-phases/ subdirs. Wire it into cmdValidateConsistency,
cmdValidateHealth (both the Check 4 validPhases set and Check 8 diskPhases),
and refactor cmdFindPhase to iterate candidate search dirs so it also searches
milestone-archive dirs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 15:10:15 -04:00
Tom Boucher
61773332d6 Merge pull request #3125 from gsd-build/fix/3098-phase-insert-and-init-phase-op-disagree-
fix: make phase insert placeholder/dry-run preconditions explicit
2026-05-04 23:54:44 -04:00
Tom Boucher
59fd17251a fix(phase): clarify insert preconditions and reject unsupported dry-run flag 2026-05-04 23:22:20 -04:00
Tom Boucher
c7886415c3 fix(phase): canonicalize plan-summary matching for suffixless summaries 2026-05-04 22:51:15 -04:00
Tom Boucher
abb2cb63f6 refactor: extract planning-workspace seam from core.cjs (#2901)
* refactor: extract planning workspace seam from core

* docs: document planning-workspace module and inventory updates

* fix: harden planning lock timeout and preserve workstream set contract

---------

Co-authored-by: Tom Boucher <thomas.boucher@sas.com>
2026-04-30 11:38:13 -04:00
Tom Boucher
951d5bf7c0 fix(#2893): surface non-canonical plan filenames instead of silently returning zero plans (#2896)
* fix(#2893): surface non-canonical plan filenames instead of silently returning zero plans

Reporter saw `plan_count: 0` from `/gsd:execute-phase` even though five
plan files existed on disk. Investigation showed the planner had written
files like `01-PLAN-01-foundation.md`, while `phase-plan-index`'s strict
filter (`f.endsWith('-PLAN.md') || f === 'PLAN.md'`) rejected them
silently — collapsing two distinct states into the same `plans: []`
return:

  - directory truly has no plans (legit empty)
  - directory has plans but the filter rejected them (user/agent error)

The canonical contract is documented in three places:
  - `agents/gsd-planner.md` write_phase_prompt step (lines 1063-1080)
  - `commands/gsd/plan-phase.md`
  - `references/universal-anti-patterns.md` (rule 26)

It mandates `{padded_phase}-{NN}-PLAN.md` and explicitly forbids
`PLAN-NN.md` / `01-PLAN-01.md` / `plan-NN.md` etc. The strict filter is
correct per that contract. The bug is that the executor never tells the
user when the contract was violated — they just see `plan_count: 0`
with no signal.

Fix: add a diagnostic helper `describeNonCanonicalPlans()` that scans
the phase directory for files matching `*PLAN*.md` (the diagnostic net)
that the canonical filter rejected, excluding legit derivatives like
`*-PLAN-OUTLINE.md` and `*-PLAN.pre-bounce.md`. When offenders exist,
return a `warning` field naming each one and citing the canonical
pattern so the user knows what to rename to.

Wired into the three filter sites:
  - `phase-plan-index` (the executor's main entry point)
  - `phases list --type plans`
  - `find-phase`

The strict filter itself is unchanged — existing canonical plans behave
identically. This is purely a diagnostic that converts silent-empty
into loud-with-actionable-error.

Tests:
  - `phase-plan-index returns warning for reporter's exact filename
    pattern (`01-PLAN-01-foundation.md`)`
  - `truly empty dir does not emit a warning`
  - `canonical plans + outline + pre-bounce files do not emit a warning`

Closes #2893

* test(#2893): add parity tests for find-phase and phases list --type plans warnings

CodeRabbit's only finding on the prior commit: I wired the warning into
three filter sites (`phase-plan-index`, `find-phase`,
`phases list --type plans`) but only `phase-plan-index` had test
coverage for the warning shape. The other two paths could silently
diverge during future refactors — exactly the silent-drift class of bug
this fix exists to prevent.

Add four parity tests mirroring the existing two:

  - find-phase: non-canonical filenames produce a warning naming each
    offender + citing the canonical pattern.
  - find-phase: canonical plan + derivative files (PLAN-OUTLINE,
    pre-bounce) produce no warning.
  - phases list --type plans: same non-canonical case, but assert the
    warning is prefixed with `${dir}: ` (this path aggregates across
    phase directories so each offender is tagged with its dir).
  - phases list --type plans: canonical case, no warning.

`node --test tests/phase.test.cjs`: 98/98 pass (was 94, +4 new).
2026-04-30 10:49:13 -04:00
Tom Boucher
936cf26706 fix(#2769): tolerate Requirements header with colon inside bold delimiters (#2782)
* fix(#2769): tolerate Requirements header with colon inside bold delimiters

extractReqIds in sdk/src/query/init.ts and the legacy init.cjs port only
matched `**Requirements**:` (colon outside bold), so phases declared with
the equally-valid markdown form `**Requirements:**` (colon inside bold,
which is what the project's own templates emit) returned phase_req_ids:
null for both `init plan-phase` and `init execute-phase`.

The mirror-image bug in `phase complete`'s REQUIREMENTS.md traceability
sweep at get-shit-done/bin/lib/phase.cjs:871 only matched the inside-bold
form, silently skipping the REQ-ID checkbox flips for any roadmap that
used the outside-bold form. Both parsers now share the same canonical
regex that accepts all three rendered-identical variants:

  **Requirements:**     (colon inside bold)
  **Requirements**:     (colon outside bold)
  **Requirements** :    (space before outside colon)

Tests:
- tests/init.test.cjs — parameterized over the three header variants for
  both init plan-phase and init execute-phase (6 new behavioral cases).
- sdk/src/query/init.test.ts — describe.each over the same variants
  exercising initPlanPhase through the SDK.
- tests/bug-2769-requirements-header-variants.test.cjs — phase complete
  flips REQ-001 in REQUIREMENTS.md across all three header variants.

Closes #2769

* refactor(#2769): centralize REQUIREMENTS_HEADER_RE constant per CodeRabbit
2026-04-27 12:31:49 -04:00
Tom Boucher
b1a670e662 fix(#2697): replace retired /gsd: prefix with /gsd- in all user-facing text (#2699)
All workflow, command, reference, template, and tool-output files that
surfaced /gsd:<cmd> as a user-typed slash command have been updated to
use /gsd-<cmd>, matching the Claude Code skill directory name.

Closes #2697

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 10:59:33 -04:00
Tom Boucher
73c1af5168 fix(#2543): replace legacy /gsd-<cmd> syntax with /gsd:<cmd> across all source files (#2595)
Commands are now installed as commands/gsd/<name>.md and invoked as
/gsd:<name> in Claude Code. The old hyphen form /gsd-<name> was still
hardcoded in hundreds of places across workflows, references, templates,
lib modules, and command files — causing "Unknown command" errors
whenever GSD suggested a command to the user.

Replace all /gsd-<cmd> occurrences where <cmd> is a known command name
(derived at runtime from commands/gsd/*.md) using a targeted Node.js
script. Agent names, tool names (gsd-sdk, gsd-tools), directory names,
and path fragments are not touched.

Adds regression test tests/bug-2543-gsd-slash-namespace.test.cjs that
enforces zero legacy occurrences going forward. Removes inverted
tests/stale-colon-refs.test.cjs (bug #1748) which enforced the now-obsolete
hyphen form; the new bug-2543 test supersedes it. Updates 5 assertion
tests that hardcoded the old hyphen form to accept the new colon form.

Closes #2543

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:04:25 -04:00
Tom Boucher
98d92d7570 fix(#2526): warn about REQ-IDs in body missing from Traceability table (#2539)
Scan REQUIREMENTS.md body for all **REQ-ID** patterns during phase
complete and emit a warning for any IDs absent from the Traceability
table, regardless of whether the roadmap has a Requirements: line.

Closes #2526
2026-04-21 21:18:58 -04:00
Tom Boucher
4cd890b252 fix(phase): guard backlog dirs and YYYY-MM dates in integer phase removal (#2466)
* fix(phase): guard backlog dirs and YYYY-MM dates in integer phase removal

Closes #2435
Closes #2434

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(phase): extend date-collision guard to hyphen-adjacent context

The lookbehind `(?<!\d)` in renameIntegerPhases only excluded
digit-prefixed matches; a YYYY-MM-DD date like 2026-05-14 has a hyphen
before the month digits, which passed the original guard and caused
date corruption when renumbering a phase whose zero-padded number
matched the month. Replace with `(?<![0-9-])` lookbehind and
`(?![0-9-])` lookahead to exclude both digit- and hyphen-adjacent
contexts. Adds a regression test for the hyphen-adjacent case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 10:08:52 -04:00
Tom Boucher
55877d372f feat(handoffs): include project identity in all Next Up blocks (#1948) (#2287)
* feat(handoffs): include project identity in all Next Up blocks

Adds project_code and project_title to withProjectRoot() and updates
all 30 Next Up headings across 18 workflow files to include
[PROJECT_CODE] PROJECT_TITLE suffix for multi-project clarity.

Closes #1948

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(review): add withProjectRoot tests and fix placeholder syntax (#1951)

Address code review feedback:
- Add 4 tests for project_code/project_title injection in withProjectRoot()
- Fix inconsistent placeholder syntax in continuation-format.md canonical
  template (bare-brace → shell-dollar to match variant examples)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(phase): preserve zero-padded prefix in renameDecimalPhases

Captures the zero-padded prefix (e.g. "06" from "06.3-slug") with
(0*${baseInt}) so renamed directories keep their original format
(06.2-slug) instead of stripping padding (6.2-slug).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Higgins <brandonscotthiggins@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:43:02 -04:00
Cocoon-Break
d9a4e5bf40 fix: parse baseInt to integer in renameDecimalPhases call to fix regex mismatch (Closes #2197) (#2198) 2026-04-15 14:59:38 -04:00
Tom Boucher
4bf3b02bec fix: add phase add-batch command to prevent duplicate phase numbers on parallel invocations (#2165) (#2170)
Parallel `phase add` invocations each read disk state before any write
completes, causing all processes to calculate the same next phase number
and produce duplicate directories and ROADMAP entries.

The new `add-batch` subcommand accepts a JSON array of phase descriptions
and performs all directory creation and ROADMAP appends within a single
`withPlanningLock()` call, incrementing `maxPhase` within the lock for
each entry. This guarantees sequential numbering regardless of call
concurrency patterns.

Closes #2165

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 15:52:33 -04:00
Tom Boucher
eb03ba3dd8 fix(2129): exclude 999.x backlog phases from next-phase and all_complete (#2135)
* test(2129): add failing tests for 999.x backlog phase exclusion

Bug A: phase complete reports 999.1 as next phase instead of 3
Bug B: init manager returns all_complete:false when only 999.x is incomplete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(2129): exclude 999.x backlog phases from next-phase scan and all_complete check

In cmdPhaseComplete, backlog phases (999.x) on disk were picked as the
next phase when intervening milestone phases had no directory yet. Now
the filesystem scan skips any directory whose phase number starts with 999.

In cmdInitManager, all_complete compared completed count against the full
phase list including 999.x stubs, making it impossible to reach true when
backlog items existed. Now the check uses only non-backlog phases.

Closes #2129

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 23:50:25 -04:00
Tom Boucher
805696bd03 feat(state): add metrics table pruning and auto-prune on phase complete (#2087) (#2120)
- Extend cmdStatePrune to prune Performance Metrics table rows older than cutoff
- Add workflow.auto_prune_state config key (default: false)
- Call cmdStatePrune automatically in cmdPhaseComplete when enabled
- Document workflow.auto_prune_state in planning-config.md reference
- Add silent option to cmdStatePrune for programmatic use without stdout

Closes #2087

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 15:02:55 -04:00
Tibsfox
cad40fff8b fix(core): extend atomicWriteFileSync to milestone, phase, and frontmatter (#1972)
Replace 11 fs.writeFileSync calls with atomicWriteFileSync in three
files that write to .planning/ artifacts (ROADMAP.md, REQUIREMENTS.md,
MILESTONES.md, and frontmatter updates). This prevents partial writes
from corrupting planning files on crash or power loss.

Skipped low-risk writes: .gitkeep (empty files) and archive directory
writes (new files, not read-modify-write).

Files changed:
- milestone.cjs: 5 sites (REQUIREMENTS.md, MILESTONES.md)
- phase.cjs: 5 sites (ROADMAP.md, REQUIREMENTS.md)
- frontmatter.cjs: 2 sites (arbitrary .planning/ files)

Closes #1972

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 03:25:06 -07:00
Tom Boucher
7f11362952 fix(phase): scan .planning/phases/ for orphan dirs in phase add (#2034)
cmdPhaseAdd computed maxPhase from ROADMAP.md only, allowing orphan
directories on disk (untracked in roadmap) to silently collide with
newly added phases. The new phase's mkdirSync succeeded against the
existing directory, contaminating it with fresh content.

Fix: take max(roadmapMax, diskMax) where diskMax scans
.planning/phases/ and strips optional project_code prefix before
parsing the leading integer. Backlog orphans (>=999) are skipped.

Adds 3 regression tests covering:
- orphan dir with number higher than roadmap max
- prefixed orphan dirs (project_code-NN-slug)
- no collision when orphan number is lower than roadmap max

Fixes #2026

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 12:04:33 -04:00
Tom Boucher
d858f51a68 fix(phase): update plan count when current milestone is inside <details> (#2005) (#2029)
replaceInCurrentMilestone() locates content by finding the last </details>
in the ROADMAP and only operates on text after that boundary. When the
current (in-progress) milestone section is itself wrapped in a <details>
block (the standard /gsd-new-project layout), the phase section's
**Plans:** counter lives INSIDE that block. The replacement target ends up
in the empty space after the block's closing </details>, so the regex never
matches and the plan count stays at 0/N permanently.

Fix: switch the plan count update to use direct .replace() on the full
roadmapContent, consistent with the checkbox and progress table updates
that already use this pattern. The phase-scoped heading regex
(### Phase N: ...) is specific enough to avoid matching archived phases.

Adds two regression tests covering: (1) plan count updates inside a
<details>-wrapped current milestone, and (2) phase 2 plan count is not
corrupted when completing phase 1.
2026-04-10 11:15:59 -04:00
Tibsfox
04e9bd5e76 fix(phase): update overview bullet checkbox on phase complete (#1998) (#2000)
cmdPhaseComplete used replaceInCurrentMilestone() to update the overview
bullet checkbox (- [ ] → - [x]), but that function scopes replacements
to content after the last </details> tag. The current milestone's
overview bullets appear before any <details> blocks, so the replacement
never matched.

Switch to direct .replace() which correctly finds and updates the first
matching unchecked checkbox. This is safe because unchecked checkboxes
([ ]) only exist in the current milestone — archived phases have [x].

Closes #1998

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 10:50:17 -04:00
Tom Boucher
e6d2dc3be6 fix(phase): skip 999.x backlog phases in phase-add numbering (#1950)
Backlog phases use 999.x numbering and should not be counted when
calculating the next sequential phase ID. Without this fix, having
backlog phases causes the next phase to be numbered 1000+.

Co-authored-by: gg <grgbrasil@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:40:47 -04:00
Tibsfox
7bc6668504 fix(phase): use readModifyWriteStateMd for atomic STATE.md updates in phase transitions (#1936)
cmdPhaseComplete and cmdPhasesRemove read STATE.md outside the lock
then wrote inside. A crash between the ROADMAP update (locked) and
the STATE write left them inconsistent. Wrap both STATE.md updates in
readModifyWriteStateMd to hold the lock across read-modify-write.

Also exports readModifyWriteStateMd from state.cjs for cross-module use.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:26:26 -04:00
Tibsfox
a452c4a03b fix(phase): scan ROADMAP.md entries in next-decimal to prevent collisions (#1877)
next-decimal and insert-phase only scanned directory names in
.planning/phases/ when calculating the next available decimal number.
When agents added backlog items by writing ROADMAP.md entries and
creating directories without calling next-decimal, the function would
not see those entries and return a number that was already in use.

Both functions now union directory names AND ROADMAP.md phase headers
(e.g. ### Phase 999.3: ...) before computing max + 1. This follows the
same pattern already used by cmdPhaseComplete (lines 791-834) which
scans ROADMAP.md as a fallback for phases defined but not yet
scaffolded to disk.

Additional hardening:
- Use escapeRegex() on normalized phase names in regex construction
- Support optional project-code prefix in directory pattern matching
- Handle edge cases: missing ROADMAP.md, empty/missing phases dir,
  leading-zero padded phase numbers in ROADMAP.md

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:13:46 -04:00
Tom Boucher
f26e1e1141 feat(state): add programmatic gates for STATE.md consistency (#1647)
* feat(state): add programmatic gates for STATE.md consistency

Adds four enforcement gates to prevent STATE.md drift:
- `state validate`: detects drift between STATE.md and filesystem
- `state sync`: reconstructs STATE.md from actual project state
- `state planned-phase`: records state after plan-phase completes
- Performance Metrics update in `phase complete`

Also fixes ghost `state update-position` command reference in
execute-phase.md (command didn't exist in CLI dispatcher).

Closes #1627

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(state): By Phase table regex ate next section when table body was empty

The lazy [\s\S]*? with a $ lookahead in byPhaseTablePattern would
match past blank lines and capture the next ## section header as table
body when no data rows existed. Replaced with a precise row-matching
pattern ((?:[ \t]*\|[^\n]*\n)*) that only captures pipe-delimited
lines. Added regression assertion to verify row placement.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 08:01:39 -04:00
Quang Do
d4767ac2e0 fix: replace /gsd: slash command format with /gsd- skill format in all user-facing content (#1579)
* fix: replace /gsd: command format with /gsd- skill format in all suggestions

All next-step suggestions shown to users were still using the old colon
format (/gsd:xxx) which cannot be copy-pasted as skills. Migrated all
occurrences across agents/, commands/, get-shit-done/, docs/, README files,
bin/install.js (hardcoded defaults for claude runtime), and
get-shit-done/bin/lib/*.cjs (generate-claude-md templates and error messages).
Updated tests to assert new hyphen format instead of old colon format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: migrate remaining /gsd: format to /gsd- in hooks, workflows, and sdk

Addresses remaining user-facing occurrences missed in the initial migration:

- hooks/: fix 4 user-facing messages (pause-work, update, fast, quick)
  and 2 comments in gsd-workflow-guard.js
- get-shit-done/workflows/: fix 21 Skill() literal calls that Claude
  executes directly (installer does not transform workflow content)
- sdk/prompt-sanitizer.ts: update regex to strip /gsd- format in addition
  to legacy /gsd: format; update JSDoc comment
- tests/: update autonomous-ui-steps, prompt-sanitizer to assert new format

Note: commands/gsd/*.md frontmatter (name: gsd:xxx) intentionally unchanged
— installer derives skillName from directory path, not the name field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(plan-phase): preserve --chain flag in auto-advance sync and handle ui-phase gate in chain mode

Bug 1: step 15 sync-flag check only guarded against --auto, causing
_auto_chain_active to be cleared when plan-phase is invoked without
--auto in ARGUMENTS even though a --chain pipeline was active. Added
--chain to the guard condition, matching discuss-phase behaviour.

Bug 2: UI Design Contract gate (step 5.6) always exited the workflow
when UI-SPEC was missing, breaking the discuss --chain pipeline
silently. When _auto_chain_active is true, the gate now auto-invokes
gsd-ui-phase --auto via Skill() and continues to step 6 without
prompting. Manual invocations retain the existing AskUserQuestion flow.

* fix: remove <sub>/clear</sub> pattern and duplicate old-format command in discuss-phase.md

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 07:24:31 -04:00
Tom Boucher
70d8bbcd17 fix(phase-resolution): use exact token matching instead of prefix matches (#1639)
* fix(phase-resolution): use exact token matching instead of prefix matches

Closes #1635

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(phase-resolution): add case-insensitive flag to project-code strip regex

The strip regex in phaseTokenMatches lacked the `i` flag, so lowercase
project-code prefixes (e.g. `ck-01-name`) were not stripped during the
fallback comparison. This made `phaseTokenMatches('ck-01-name', '01')`
return false when it should return true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 07:14:55 -04:00
Tom Boucher
00e0446b99 fix: four bug fixes — bold plan checkboxes, BACKLOG recommendations, executor_model regression tests, session_id path traversal
- fix(#1572): phase complete now marks bold-wrapped plan checkboxes in ROADMAP.md
  (`- [ ] **01-01**` format) by allowing optional `**` around plan IDs in the
  planCheckboxPattern regex in both phase.cjs and roadmap.cjs

- fix(#1569): manager init no longer recommends 999.x (BACKLOG) phases as next
  actions; add guard in cmdManagerInit that skips phases matching /^999(?:\.|$)/

- fix(#1568): add regression tests confirming init execute-phase respects
  model_overrides for executor_model, including when resolve_model_ids is 'omit'

- fix(#1533): reject session_id values containing path traversal sequences
  (../,  /, \) in gsd-context-monitor and gsd-statusline before constructing
  /tmp file paths; add security tests covering both hooks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 11:30:08 -04:00
Tibsfox
d4859220e2 fix(core): add concurrency safety and atomic state writes
Wire withPlanningLock into all ROADMAP.md write paths (phase add/insert/complete/remove, roadmap update-plan-progress) to prevent concurrent corruption when parallel agents modify planning files.

Extract acquireStateLock/releaseStateLock from writeStateMd and add readModifyWriteStateMd helper that holds the lock across the entire read-modify-write cycle, preventing lost updates.

Replace O(n^2) normalizeMd fence detection with single-pass O(n) pre-computed fence state array.

Warn on must_haves parse failure and stateReplaceFieldWithFallback field miss.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 04:28:06 -07:00
Tom Boucher
5eed6973b8 Merge pull request #1447 from rahuljordashe/fix/phase-complete-roadmap-rollup
fix: cmdPhaseComplete updates Plans column and plan checkboxes
2026-04-01 17:20:14 -04:00
Rahul Jordashe
32b2c52729 fix: cmdPhaseComplete now updates Plans column and plan checkboxes in ROADMAP.md
cmdPhaseComplete updated Status and Completed columns in the progress
table but skipped the Plans Complete column and plan-level checkboxes.
If update-plan-progress was missed for any plan, the phase completion
safety net didn't catch it, leaving ROADMAP.md inconsistent.

Fixes #1446

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:46:42 +05:30
Brandon Higgins
1f34965717 feat: add project_code config for phase directory prefixing
When project_code is set (e.g., "CK"), phase directories are prefixed
with the code: CK-01-foundation, CK-02-api, etc. This disambiguates
phases across multiple GSD projects in the same session.

Changes:
- Add project_code to VALID_CONFIG_KEYS and buildNewProjectConfig defaults
- Add project_code to loadConfig in core.cjs
- Prepend prefix in cmdPhaseAdd and cmdPhaseInsert
- Update searchPhaseInDir, cmdFindPhase, comparePhaseNum, and
  normalizePhaseName to strip prefix before matching/sorting
- Support {project} placeholder in git.phase_branch_template
- Add 4 tests covering prefixed add, null code, find, and sort

Closes #1019

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 08:43:05 -06:00
SalesTeamToolbox
5f95fea4d7 refactor: extract shared utilities to reduce duplication across codebase
- core.cjs: add filterPlanFiles, filterSummaryFiles, getPhaseFileStats,
  readSubdirectories helpers; use them in searchPhaseInDir and getArchivedPhaseDirs
- gsd-tools.cjs: add parseNamedArgs() helper; replace ~50 repetitive indexOf/ternary
  patterns in state record-metric, add-decision, add-blocker, record-session,
  begin-phase, signal-waiting, template fill, and frontmatter subcommands
- phase.cjs: decompose 250-line cmdPhaseRemove into renameDecimalPhases(),
  renameIntegerPhases(), and updateRoadmapAfterPhaseRemoval(); import readSubdirectories
- workstream.cjs: import stateExtractField from state.cjs and shared helpers from
  core.cjs; replace all inline regex state parsing and readdirSync+filter+map patterns

All 1062 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
SalesTeamToolbox
c83b69bbb5 feat: workstream namespacing for parallel milestone work
Enable multiple Claude Code instances to work on the same codebase
simultaneously by scoping .planning/ state into workstreams.

Core changes:
- planningDir(cwd, ws?) and planningPaths(cwd, ws?) are now workstream-aware
  via GSD_WORKSTREAM env var (auto-detected from --ws flag or active-workstream file)
- All bin/lib modules use planningDir(cwd) for scoped paths (STATE.md, ROADMAP.md,
  phases/, REQUIREMENTS.md) and planningRoot(cwd) for shared paths (milestones/,
  PROJECT.md, config.json, codebase/)
- New workstream.cjs module: create, list, status, complete, set, get, progress
- gsd-tools.cjs: --ws flag parsing with priority chain
  (--ws > GSD_WORKSTREAM env > active-workstream file > flat mode)
- Collision detection: transition.md checks for other active workstreams before
  suggesting next-milestone continuation (prevents WS A from trampling WS B)
- ${GSD_WS} routing propagation across all 9 workflow files ensures workstream
  scope chains automatically through the workflow lifecycle

New files:
- get-shit-done/bin/lib/workstream.cjs (CRUD + collision detection)
- get-shit-done/commands/gsd/workstreams.md (slash command)
- get-shit-done/references/workstream-flag.md (documentation)
- tests/workstream.test.cjs (20 tests covering CRUD, env var routing, --ws flag)

All 1062 tests passing (1042 existing + 20 new workstream tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:21:13 -07:00
Tom Boucher
3e2c85e6fd refactor: consolidate STATE.md field helpers, fix command injection in isGitIgnored
Refactoring:
- Extract stateReplaceFieldWithFallback() to state.cjs as single source of truth
  for the try-primary-then-fallback pattern that was duplicated inline across
  phase.cjs, milestone.cjs, and state.cjs
- Replace all inline bold-only regex patterns in cmdPhaseComplete with shared
  stateReplaceField/stateExtractField helpers — now supports both **Bold:**
  and plain Field: STATE.md formats (fixes the same bug as #924)
- Replace inline bold-only regex patterns in cmdMilestoneComplete with shared helpers
- Replace inline bold-only regex for Completed Phases/Total Phases/Progress
  counters in cmdPhaseComplete with stateExtractField/stateReplaceField
- Replace inline bold-only Total Phases regex in cmdPhaseRemove with shared helpers

Security:
- Fix command injection surface in isGitIgnored (core.cjs): replace execSync with
  string concatenation with execFileSync using array arguments — prevents shell
  interpretation of special characters in file paths

Tests (7 new):
- 5 tests for stateReplaceFieldWithFallback: primary field, fallback, neither,
  preference, and plain format
- 1 regression test: phase complete with plain-format STATE.md fields
- 1 regression test: milestone complete with plain-format STATE.md fields

854 tests pass (was 847). No behavioral regressions.
2026-03-19 15:05:20 -04:00
j2h4u
e3bc614eb7 fix(roadmap): handle 5-column progress tables with Milestone column
The regex-based table parser captured a fixed number of columns,
so 5-column tables (Phase | Milestone | Plans | Status | Completed)
had the Milestone column eaten and Status/Date written to wrong cells.

Replaced regex with cell-based `split('|')` parsing that detects
column count (4 or 5) and updates the correct cells by index.
Affects both `cmdRoadmapUpdatePlanProgress` and `cmdPhaseComplete`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:30:41 +05:00
Tom Boucher
f9434f7ffc Merge pull request #1212 from jecanore/feat/audit-uat
feat: add verification debt tracking and /gsd:audit-uat command
2026-03-19 12:05:36 -04:00
jecanore
60a76ae06e feat: add verification debt tracking and /gsd:audit-uat command
Prevent silent loss of UAT/verification items when projects advance.
Surfaces outstanding items across all prior phases so nothing is forgotten.

New command:
- /gsd:audit-uat — cross-phase audit with categorized report and test plan

New capabilities:
- Cross-phase health check in /gsd:progress (Step 1.6)
- status: partial for incomplete UAT sessions
- result: blocked with blocked_by tag for dependency-gated tests
- human_needed items persisted as trackable HUMAN-UAT.md files
- Phase completion and transition warnings for verification debt

Files: 4 new, 14 modified (9 feature + 5 docs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:05:05 -05:00
Tom Boucher
a4da216523 feat: support ticket-based phase identifiers for team workflows (#1019)
Teams sharing a GSD repo collide on sequential phase numbers when multiple
people create phases concurrently. This adds support for arbitrary string
IDs (e.g. PROJ-42, AUTH-101) as phase identifiers.

New config option:
  { "phase_naming": "custom" }

Changes:
- core.cjs: normalizePhaseName() handles non-numeric IDs gracefully
- core.cjs: comparePhaseNum() falls back to string comparison for custom IDs
- core.cjs: searchPhaseInDir() matches custom ID prefixes case-insensitively
- core.cjs: getMilestonePhaseFilter() recognizes custom ID directory names
- core.cjs: getRoadmapPhaseInternal() matches Phase headers with any word chars
- core.cjs: loadConfig() adds phase_naming option (default: 'sequential')
- phase.cjs: cmdPhaseAdd() accepts --id flag for custom phase IDs
- verify.cjs: health check skips sequential numbering validation in custom mode
- gsd-tools.cjs: phase add --id PROJ-42 "description" wired up

Usage:
  # Sequential mode (default, backward compatible)
  gsd-tools phase add "User Authentication"  # → Phase 3

  # Custom mode (config: phase_naming: custom)
  gsd-tools phase add --id PROJ-42 "User Authentication"
  # Creates: .planning/phases/PROJ-42-user-authentication/
  # ROADMAP: ### Phase PROJ-42: User Authentication

All 755 existing tests pass.

Fixes #1019
2026-03-18 20:27:12 -04:00
Tom Boucher
c7954d1ad7 refactor: deduplicate code, add planningPaths helper, annotate empty catches
Code quality refactoring across 9 lib modules:

1. state.cjs: Remove duplicate stateExtractField() definition (lines 12
   vs 184). Replace 3 inline regex escape patterns with escapeRegex()
   from core.cjs. Add getStatePath() local helper for the 15 repeated
   statePath declarations. Import planningPaths from core.

2. core.cjs: Add planningPaths(cwd) helper returning an object with all
   common .planning paths (state, roadmap, project, config, phases,
   requirements). Add planningDir(cwd) shorthand. Export both.

3. init.cjs: Replace Unix-only execSync('find . | grep | head') with
   cross-platform fs.readdirSync recursive walk for code file detection.
   Remove unused child_process import. Fixes Windows compatibility.

4. All lib modules: Annotate 48 empty catch blocks with
   /* intentionally empty */ to distinguish deliberate error swallowing
   from accidental missing handlers. Affected: commands.cjs (8),
   config.cjs (1), core.cjs (4), init.cjs (13), milestone.cjs (3),
   phase.cjs (6), roadmap.cjs (1), state.cjs (3), verify.cjs (9).

All 755 tests pass.
2026-03-18 12:31:20 -04:00
Tom Boucher
7101ddcb9c fix: prevent PROJECT.md drift and fix phase completion counters (#956) (#1130)
Two gaps in the standard workflow cycle caused planning document drift:

1. PROJECT.md was never updated during discuss → plan → execute → verify.
   Only transition.md (optional) and complete-milestone evolved it.
   Added an 'update_project_md' step to execute-phase.md that evolves
   PROJECT.md after phase completion: moves requirements to Validated,
   updates Current State, bumps Last Updated timestamp.

2. cmdPhaseComplete() in phase.cjs advanced 'Current Phase' but never
   incremented 'Completed Phases' counter or recalculated 'percent'.
   Added counter increment and percentage recalculation based on
   completed/total phases ratio.

Addresses the workflow-level gaps identified in #956:
- PROJECT.md evolution in execute-phase (gap #2)
- completed_phases counter not incremented (gap #1 table row 3)
- percent never recalculated (gap #1 table row 4)

Fixes #956
2026-03-18 10:00:24 -06:00
Tom Boucher
26d742c548 fix(core): replace negative-heuristic stripShippedMilestones with positive milestone lookup (#1145) (#1146)
stripShippedMilestones() uses a negative heuristic: strip all <details>
blocks, assume what remains is the current milestone. This breaks when
agents accidentally wrap the current milestone in <details> for
collapsibility — all downstream consumers then see an empty milestone.

Observed failure: cmdPhaseComplete() returns is_last_phase: true and
next_phase: null for non-final phases because the current milestone's
phases were stripped along with shipped ones.

Added extractCurrentMilestone(content, cwd) — a positive lookup that:
1. Reads the current milestone version from STATE.md frontmatter
2. Falls back to 🚧 in-progress marker in ROADMAP.md
3. Finds the section heading matching that version
4. Returns only that section's content
5. Falls back to stripShippedMilestones() if version can't be determined

Updated 12 call sites across 6 files to use extractCurrentMilestone:
- core.cjs: getRoadmapPhaseInternal(), getMilestonePhaseFilter()
- phase.cjs: cmdPhaseAdd(), cmdPhaseInsert(), cmdPhaseComplete() (2 sites)
- roadmap.cjs: cmdRoadmapGetPhase(), cmdRoadmapAnalyze()
- commands.cjs: stats/progress display
- verify.cjs: phase verification (2 sites)
- init.cjs: project initialization

Kept stripShippedMilestones() for:
- getMilestoneInfo() — determines the version itself, can't use positive lookup
- replaceInCurrentMilestone() — write operations, conservative boundary
- extractCurrentMilestone() fallback — when no version available

All 755 tests pass.

Fixes #1145
2026-03-18 09:57:31 -06:00
Shard
ca4ae7b3b8 fix: scope ROADMAP.md searches to current milestone (#1059)
Phase numbers reset per milestone (v1.0 Phase 1-26, v1.1 Phase 1-6,
v1.2 Phase 1-6). Functions that searched ROADMAP.md for phase headings
or checkboxes would match archived milestone entries inside <details>
blocks before reaching the current milestone's entries.

This caused:
- roadmap_complete: true for phases that aren't complete (checkbox
  regex matched the archived [x] Phase N from a previous milestone)
- phase-complete marking the wrong checkbox (archived instead of current)
- phase-add computing wrong maxPhase from archived headings
- progress not showing phases defined in ROADMAP but not yet on disk
2026-03-15 11:39:59 -06:00
ashanuoc
c82c0a76b9 fix(phase-complete): improve REQUIREMENTS.md traceability updates (closes #848)
- Scope phase section regex to prevent cross-phase boundary matching
- Handle 'In Progress' status in traceability table (not just 'Pending')
- Add requirements_updated field to phase complete result object
- Add 3 new tests: result field, In Progress status, cross-boundary safety

Co-authored-by: ashanuoc <ashanuoc@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 21:57:48 -06:00
Tibsfox
15226feb18 fix(core): complete toPosixPath coverage for Windows output paths
Three files emit path.join or path.relative results directly into
JSON output without normalizing backslashes on Windows. Wrap these
with toPosixPath (already used in core.cjs and init.cjs) so agents
receive consistent forward-slash paths on all platforms.

Files changed:
- phase.cjs: wrap directory path in cmdPhasesFind
- commands.cjs: wrap todo path in cmdListTodos, relPath in cmdScaffold
- template.cjs: wrap relPath in cmdTemplateFill (both exists and
  created branches)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:01:54 -06:00
TÂCHES
43c0bde6d0 Merge pull request #822 from Tibsfox/fix/core-lifecycle-state
fix(core): correct phase lifecycle, milestone detection, state parsing, and CLI commit messages
2026-03-02 16:20:03 -06:00
j2h4u
b863ed6de5 fix(state): scope phase counting to current milestone
Extract getMilestonePhaseFilter() from milestone.cjs closure into core.cjs
as a shared helper. Apply it in buildStateFrontmatter and cmdPhaseComplete
so multi-milestone projects count only current milestone's phases instead
of all directories on disk.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:07:49 -06:00
Tibsfox
4e00c50022 fix(phase): add ROADMAP.md fallback to cmdPhaseComplete next-phase scan
cmdPhaseComplete determines is_last_phase and next_phase by scanning
.planning/phases/ directories on disk. Phases defined in ROADMAP.md
but not yet planned (no directory created) are invisible to this scan,
causing premature is_last_phase:true when only the first phase has
been scaffolded.

Add a fallback that parses ROADMAP.md phase headings when the
filesystem scan finds no next phase. Uses the existing comparePhaseNum
utility for consistent ordering with letter suffixes and decimals.

Closes #709

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 04:48:04 -08:00