* 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
* fix(sdk): extractCurrentMilestone Backlog leak + state.begin-phase flag parsing
Closes#2422Closes#2420
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): skip stateVersion early-return for shipped milestones
When STATE.md has a stale `milestone: v1.0` entry but v1.0 is already
shipped (heading contains ✅ in ROADMAP.md), the stateVersion early-return
path in getMilestoneInfo was returning v1.0 instead of detecting the new
active milestone.
Two-part fix:
1. In the stateVersion block: skip the early-return when the matched
heading line includes ✅ (shipped marker). Fall through to normal
detection instead.
2. In the heading-format fallback regex: add a negative lookahead
`(?!.*✅)` so the regex never matches a ✅ heading regardless of
whether stateVersion was present. This handles the no-STATE.md case
and ensures fallthrough from part 1 actually finds the next milestone.
Adds two regression tests covering both ✅-suffix (`## v1.0 ✅ Name`)
and ✅-prefix (`## ✅ v1.0 Name`) heading formats.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(core): allow padded-and-unpadded phase headings in getRoadmapPhaseInternal
The zero-strip normalization (01→1) fixed the archived-phase guard but
broke lookup against ROADMAP headings that still use zero-padded numbers
like "Phase 01:". Change the regex to use 0*<normalized> so both formats
match, making the fix robust regardless of ROADMAP heading style.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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>
Prevents infinite config-get loops on Kimi K2.5 and other models that
re-execute bash tool calls when they encounter config-get subshell patterns.
Values are now bundled into the init plan-phase JSON so step 15 of
plan-phase.md can read them directly without separate shell calls.
Closes#2192
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
When a new milestone reuses a phase number that exists in an archived
milestone (e.g., v2.0 Phase 2 while v1.0-phases/02-old-feature exists),
findPhaseInternal falls through to the archive and returns the old
phase. init plan-phase and init execute-phase then emitted archived
values for phase_dir, phase_slug, has_context, has_research, and
*_path fields, while phase_req_ids came from the current ROADMAP —
producing a silent inconsistency that pointed downstream agents at a
shipped phase from a previous milestone.
cmdInitPhaseOp already guarded against this (see lines 617-642);
apply the same guard in cmdInitPlanPhase, cmdInitExecutePhase, and
cmdInitVerifyWork: if findPhaseInternal returns an archived match
and the current ROADMAP.md has the phase, discard the archived
phaseInfo so the ROADMAP fallback path produces clean values.
Adds three regression tests covering plan-phase, execute-phase, and
verify-work under the shared-number scenario.
* refactor(tests): standardize to node:assert/strict and t.after() per CONTRIBUTING.md
- Replace require('node:assert') with require('node:assert/strict') across
all 73 test files to enforce strict equality (no type coercion)
- Replace try/finally cleanup blocks with t.after() hooks in core.test.cjs
and hooks-opt-in.test.cjs per the test lifecycle standards
- Utility functions in codex-config and security-scan retain try/finally
as that is appropriate for per-function resource guards, not lifecycle hooks
Closes#1674
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* perf(tests): add --test-concurrency=4 to test runner for parallel file execution
Node.js --test-concurrency controls how many test files run as parallel child
processes. Set to 4 by default, configurable via TEST_CONCURRENCY env var.
Fixes tests at a known level rather than inheriting os.availableParallelism()
which varies across CI environments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(security): allowlist verify.test.cjs in prompt-injection scanner
tests/verify.test.cjs uses <human>...</human> as GSD phase task-type
XML (meaning "a human should verify this step"), which matches the
scanner's fake-message-boundary pattern for LLM APIs. This is a
false positive — add it to the allowlist alongside the other test files
that legitimately contain injection-adjacent patterns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
The map-codebase workflow was refactored to remove the explicit
"Runtimes with Task tool" line in favor of inline detection instructions.
Updated test to match the new workflow structure by checking the
"NOT available" condition line instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The code/package detection in cmdInitNewProject only recognized 7 file
extensions and 5 package files, missing Android (Kotlin + Gradle), Flutter
(Dart + pubspec.yaml), C/C++, C#, Ruby, PHP, Scala, and others. This caused
new-project to treat brownfield projects in those ecosystems as greenfield,
skipping the codebase mapping step.
Added 18 code extensions and 11 package/build files to the detection lists.
Fixes#1325
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenCode has a `task` tool that supports spawning subagents, but
map-codebase workflow incorrectly listed it under "Runtimes WITHOUT
Task tool". This caused the agent to skip parallel mapping and fall
back to sequential mode, wasting tokens when it self-corrected.
Move OpenCode to the "with Task tool" list and clarify that either
`Task` or `task` (case-insensitive) qualifies. Add regression test.
Fixes#1316
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test tried to fs.readFileSync on config.json which doesn't exist
in createTempProject() fixtures. Now gracefully creates the config
from scratch when the file is missing.
Co-Authored-By: GhadiSaab <GhadiSaab@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`workflow.text_mode: true` (or `--text` flag) now applies to
plan-phase, not just discuss-phase. Fixes#1313.
Changes:
- `init plan-phase` now exposes `text_mode` from config in its JSON output
- plan-phase workflow parses `--text` flag and resolves TEXT_MODE from
init JSON or flag, whichever is set
- All four AskUserQuestion call sites (no-context gate, research prompt,
UI design contract gate, requirements coverage gap) now conditionally
present as plain-text numbered lists when TEXT_MODE is active
- `--text` added to plan-phase command argument-hint and flags docs
- Tests added for init output and workflow references
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Test that has_reviews=true and reviews_path is set when REVIEWS.md exists
- Test that reviews_path is undefined and has_reviews=false when missing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmdInitPlanPhase, cmdInitExecutePhase, and cmdInitVerifyWork returned
phase_found: false when the phase existed in ROADMAP.md but no phase
directory had been created yet. This caused workflows to fail silently
after /gsd:new-project, producing directories named null-null.
cmdInitPhaseOp (used by discuss-phase) already had a ROADMAP fallback.
Applied the same pattern to the three missing commands: when
findPhaseInternal returns null, fall back to getRoadmapPhaseInternal
and construct phaseInfo from the ROADMAP entry.
Added 5 regression tests covering:
- plan-phase ROADMAP fallback
- execute-phase ROADMAP fallback
- verify-work ROADMAP fallback
- phase_found false when neither directory nor ROADMAP entry exists
- disk directory preferred over ROADMAP fallback
Add support for workspaces with multiple independent git repositories.
When configured, GSD routes commits to the correct sub-repo and ensures
.planning/ stays at the project root.
Core features:
- detectSubRepos(): scans child directories for .git to discover repos
- findProjectRoot(): walks up from CWD to find the project root that
owns .planning/, preventing orphaned .planning/ in sub-repos
- loadConfig auto-syncs sub_repos when repos are added or removed
- Migrates legacy "multiRepo: true" to sub_repos array automatically
- All init commands include project_root in output
- cmdCommitToSubrepo: groups files by sub-repo prefix, commits independently
Zero impact on single-repo workflows — sub_repos defaults to empty array.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quick task IDs were sequential integers (001, 002...) computed by reading
the local .planning/quick/ directory max value. Two users running /gsd:quick
simultaneously would get the same number, causing directory collisions on git.
Replace with a collision-resistant format: YYMMDD-xxx where xxx is the
number of 2-second blocks elapsed since midnight, encoded as 3 lowercase
Base36 characters (000–xbz). Practical collision window is ~2 seconds per
user — effectively zero for any realistic team workflow.
- init.cjs: remove nextNum scan logic, generate quickId from wall clock
- quick.md: rename all next_num refs to quick_id, update directory patterns
- init.test.cjs: rewrite cmdInitQuick tests for new ID format
Co-authored-by: yanbing <yanbing@corp.netease.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The source now outputs posix paths; update the test to match instead
of using path.join (which produces backslashes on Windows).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move 81 tests (18 describe blocks) from single monolithic test file
into 7 domain-specific test files under tests/ with shared helpers.
Test parity verified: 81/81 pass before and after split.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>