* feat(#2527): add settings layers to /gsd:settings (Group A toggles)
Expand /gsd:settings from 14 to 22 settings, grouped into six visual
sections: Planning, Execution, Docs & Output, Features, Model & Pipeline,
Misc. Adds 8 new toggles:
workflow.pattern_mapper, workflow.tdd_mode, workflow.code_review,
workflow.code_review_depth (conditional on code_review=on),
workflow.ui_review, commit_docs, intel.enabled, graphify.enabled
All 8 keys already existed in VALID_CONFIG_KEYS and docs/CONFIGURATION.md;
this wires them into the interactive flow, update_config write step,
~/.gsd/defaults.json persistence, and confirmation table.
Closes#2527
* test(#2527): tighten leaf-collision and rename mismatched negative test
Addresses CodeRabbit findings on PR #2602:
- comment 3127100796: leaf-only matching collapsed `intel.enabled` and
`graphify.enabled` to a single `enabled` token, so one occurrence
could satisfy both assertions. Replace with hasPathLike(), which
requires each dotted segment to appear in order within a bounded
window. Applied to both update_config and save_as_defaults blocks.
- comment 3127100798: the negative-test description claimed to verify
invalid `code_review_depth` value rejection but actually exercised an
unknown key path. Split into two suites with accurate names: one
asserts settings.md constrains the depth options, the other asserts
config-set rejects an unknown key path.
* docs(#2527): clarify resolved config path for /gsd-settings
Addresses CodeRabbit comment 3127100790 on PR #2602: the original line
implied a single `.planning/config.json` target, but settings updates
route to `.planning/workstreams/<active>/config.json` when a workstream
is active. Document both resolved paths so the merge target is
unambiguous.
resolveQueryArgv only expanded `init.execute-phase` → `init execute-phase`
when the tokens array had length 1. Argv like `init.execute-phase 1` has
length 2, skipped the expansion, and resolved to no registered handler.
All 50+ workflow files use the dotted form with arguments, so this broke
every non-argless query route (`init.execute-phase`, `state.update`,
`phase.add`, `milestone.complete`, etc.) at runtime.
Rename `expandSingleDottedToken` → `expandFirstDottedToken`: split only
the first token on its dots (guarding against `--` flags) and preserve
the tail as positional args. Identity comparison at the call site still
detects "no expansion" since we return the input array unchanged.
Adds regression tests for the three failure patterns reported:
`init.execute-phase 1`, `state.update status X`, `phase.add desc`.
Closes#2597
* feat(#2473): ship refuses to open PR when HANDOFF.json declares in-progress work
Add a preflight step to /gsd-ship that parses .planning/HANDOFF.json and
refuses to run git push + gh pr create when any remaining_tasks[].status
is not in the terminal set {done, cancelled, deferred_to_backend, wont_fix}.
Refusal names each blocking task and lists four resolutions (finish, mark
terminal, delete stale file, --force). Missing HANDOFF.json is a no-op so
projects that do not use /gsd-pause-work see no behavior change.
Also documents the terminal-statuses contract in references/artifact-types.md
and adds tests/ship-handoff-preflight.test.cjs to lock in the contract.
Closes#2473
* fix(#2473): capture node exit from $() so malformed HANDOFF.json hard-stops
Command substitution BLOCKING=$(node -e "...") discards the inner process
exit code, so a corrupted HANDOFF.json that fails JSON.parse would yield
empty BLOCKING and fall through silently to push_branch — the opposite of
what preflight is supposed to do.
Capture node's exit into HANDOFF_EXIT via $? immediately after the
assignment and branch on it. A non-zero exit is now a hard refusal with
the parser error printed on the preceding stderr line. --force does not
bypass this branch: if the file exists and can't be parsed, something is
wrong and the user should fix it (option 3 in the refusal message —
"Delete HANDOFF.json if it's stale" — still applies).
Verified with a tmp-dir simulation: captured exit 2, hard-stop fires
correctly on malformed JSON. Added a test case asserting the capture
($?) + branch (-ne 0) + parser exit (process.exit(2)) are all present,
so a future refactor can't silently reintroduce the bug.
Reported by @coderabbitai on PR #2553.
* test(#2519): add regression test verifying sdk/package.json has files + prepublishOnly
Guards the sdk/package.json fix for #2519 (tarball shipped without dist/)
so future edits can't silently drop either the `files` whitelist or the
`prepublishOnly` build hook. Asserts:
- `files` is a non-empty array
- `files` includes "dist" (so compiled CLI ships in tarball)
- `scripts.prepublishOnly` runs a build (npm run build / tsc)
- `bin` target lives under dist/ (sanity tie-in)
Closes#2519
* test(#2519): accept valid npm glob variants for dist in files matcher
Addresses CodeRabbit nitpick: the previous equality check on 'dist' / 'dist/' /
'dist/**' would false-fail on other valid npm packaging forms like './dist',
'dist/**/*', or backslash-separated paths. Normalize each entry and use a
regex that accepts all common dist path variants.
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>
Adds a `statusline.show_last_command` config toggle (default: false) that
appends ` │ last: /<cmd>` to the statusline, showing the most recently
invoked slash command in the current session.
The suffix is derived by tailing the active Claude Code transcript
(provided as transcript_path in the hook input) and extracting the last
<command-name> tag. Reads only the final 256 KiB to stay cheap per render.
Graceful degradation: missing transcript, no recorded command, unreadable
config, or parse errors all silently omit the suffix without breaking the
statusline.
Closes#2538
The Copilot content converter only replaced `~/.claude/` and
`$HOME/.claude/` when followed by a literal `/`. Bare references
(e.g. `configDir = ~/.claude` at end of line) slipped through and
triggered the post-install "Found N unreplaced .claude path reference(s)"
warning, since the leak scanner uses `(?:~|$HOME)/\.claude\b`.
Switched both replacements to a `(\/|\b)` capture group so trailing-slash
and bare forms are handled in a single pass — matching the pattern
already used by Antigravity, OpenCode, Kilo, and Codex converters.
Closes#2545
The gsd-phase-researcher and gsd-project-researcher agents instructed
WebSearch queries to always include 'current year' (e.g., 2024). As
time passes, a hardcoded year biases search results toward stale
dated content — users saw 2024-tagged queries producing stale blog
references in 2026.
Remove the year-injection guidance. Instead, rely on checking
publication dates on the returned sources. Query templates and
success criteria updated accordingly.
Closes#2559
#2549: load_prior_context was reading every prior *-CONTEXT.md file,
growing linearly with project phase count. Cap to the 3 most recent
phases. If .planning/DECISIONS-INDEX.md exists, read that instead.
#2550: scout_codebase claimed to select maps "based on phase type" but
had no classifier — agents read all 7 maps. Replace with an explicit
phase-type-to-maps table (2–3 maps per phase type) with a Mixed fallback.
#2552: Add explicit instruction not to split-read the same file at two
different offsets. Split reads break prompt cache reuse and cost more
than a single full read.
Closes#2549Closes#2550Closes#2552
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
\$CLAUDE_PROJECT_DIR is Claude Code-specific. Gemini CLI doesn't set it, and on
Windows its path-join logic doubled the value producing unresolvable paths like
D:\Projects\GSD\'D:\Projects\GSD'. Gemini runs project hooks with project root
as cwd, so bare relative paths (e.g. node .gemini/hooks/gsd-check-update.js)
are cross-platform and correct. Claude Code and others still use the env var.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The configGet query handler previously threw GSDError with
ErrorClassification.Validation, which maps to exit code 10. Callers
using `if ! gsd-sdk query config-get key; then fallback; fi` could
not detect missing keys through the exit code alone, because exit 10
is still truthy-failure but the intent (and documented UNIX
convention — cf. `git config --get`) is exit 1 for absent key.
Change the classification for the two 'Key not found' throw sites to
ErrorClassification.Execution so the CLI exits 1 on missing key.
Usage/schema errors (no key argument, malformed JSON, missing
config.json) remain Validation.
Closes#2544
The SDK query handler `agent-skills` previously scanned every skill
directory on the filesystem and returned a flat JSON list, ignoring
`config.agent_skills[agentType]` entirely. Workflows that interpolate
$(gsd-sdk query agent-skills <type>) into Task() prompts got a JSON
dump of all skills instead of the documented <agent_skills> block.
Port `buildAgentSkillsBlock` semantics from
get-shit-done/bin/lib/init.cjs into the SDK handler:
- Read config.agent_skills[agentType] via loadConfig()
- Support single-string and array forms
- Validate each project-relative path stays inside the project root
(symlink-aware, mirrors security.cjs#validatePath)
- Support `global:<name>` prefix for ~/.claude/skills/<name>/
- Skip entries whose SKILL.md is missing, with a stderr warning
- Return the exact string block workflows embed:
<agent_skills>\nRead these user-configured skills:\n- @.../SKILL.md\n</agent_skills>
- Empty string when no agent type, no config, or nothing valid — matches
gsd-tools.cjs cmdAgentSkills output.
The normalization `replace(/^0+/, '')` over-stripped decimal phase IDs:
`"00.1"` collapsed to `".1"`, while the disk-side extractor yielded
`"0.1"` from `"00.1-<slug>"`. Set membership failed and inserted decimal
phases were silently excluded from every disk scan inside
`buildStateFrontmatter`, causing `state update` to rewind progress
counters.
Strip leading zeros only when followed by a digit
(`replace(/^0+(?=\d)/, '')`), preserving the zero before the decimal
point while keeping existing behavior for zero-padded integer IDs.
Closes#2554
* fix(#2530-2535): correct VALID_CONFIG_KEYS set — remove internal state key, add missing public keys, add migration hints
- Remove workflow._auto_chain_active from VALID_CONFIG_KEYS (internal runtime state, not user-settable) (#2530)
- Add hooks.workflow_guard to VALID_CONFIG_KEYS (read by gsd-workflow-guard.js hook, already documented) (#2531)
- Add workflow.ui_review to VALID_CONFIG_KEYS (read in autonomous.md via config-get) (#2532)
- Add workflow.max_discuss_passes to VALID_CONFIG_KEYS (read in discuss-phase.md via config-get) (#2533)
- Add CONFIG_KEY_SUGGESTIONS entries for sub_repos → planning.sub_repos and plan_checker → workflow.plan_check (#2535)
- Document workflow.ui_review and workflow.max_discuss_passes in docs/CONFIGURATION.md
- Clear INTERNAL_KEYS exemption in parity test (workflow._auto_chain_active removed from schema entirely)
- Add regression test file tests/bug-2530-valid-config-keys.test.cjs covering all 6 bugs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: align SDK VALID_CONFIG_KEYS with CJS — remove internal key, add missing public keys
- Remove workflow._auto_chain_active from SDK (internal runtime state, not user-settable)
- Add workflow.ui_review, workflow.max_discuss_passes, hooks.workflow_guard to SDK
- Add ui_review and max_discuss_passes to Full Schema example in CONFIGURATION.md
Resolves CodeRabbit review on #2561.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(hooks): detect Claude Code via stdin session_id, not filtered env (#2520)
The #2344 fix assumed `CLAUDECODE` would propagate to hook subprocesses.
On Claude Code v2.1.116 it doesn't — Claude Code applies a separate env
filter to PreToolUse hook commands that drops bare CLAUDECODE and
CLAUDE_SESSION_ID, keeping only CLAUDE_CODE_*-prefixed vars plus
CLAUDE_PROJECT_DIR. As a result every Edit/Write on an existing file
produced a redundant READ-BEFORE-EDIT advisory inside Claude Code.
Use `data.session_id` from the hook's stdin JSON as the primary Claude
Code signal (it's part of Claude Code's documented PreToolUse hook-input
schema). Keep CLAUDE_CODE_ENTRYPOINT / CLAUDE_CODE_SSE_PORT env checks
as propagation-verified fallbacks, and keep the legacy
CLAUDE_SESSION_ID / CLAUDECODE checks for back-compat and
future-proofing.
Add tests/bug-2520-read-guard-hook-subprocess-env.test.cjs, which spawns
the hook with an env mirroring the actual Claude Code hook-subprocess
filter. Extend the legacy test harnesses to also strip the
propagation-verified CLAUDE_CODE_* vars so positive-path tests keep
passing when the suite itself runs inside a Claude Code session (same
class of leak as #2370 / PR #2375, now covering the new detection
signals).
Non-Claude-host behavior (OpenCode / MiniMax) is unchanged: with no
`session_id` on stdin and no CLAUDE_CODE_* env var, the advisory still
fires.
Closes#2520
* test(2520): isolate session_id signal from env fallbacks in regression test
Per reviewer feedback (Copilot + CodeRabbit on #2521): the session_id
isolation test used the helper's default CLAUDE_CODE_ENTRYPOINT /
CLAUDE_CODE_SSE_PORT values, so the env fallback would rescue the skip
even if the primary `data.session_id` check regressed. Pass an explicit
env override that clears those fallbacks, so only the stdin `session_id`
signal can trigger the skip.
Other cases (env-only fallback, negative / non-Claude host) already
override env appropriately.
---------
Co-authored-by: forfrossen <forfrossensvart@gmail.com>
* feat(sdk): add queued_phases to init.manager (closes#2497)
Surfaces the milestone immediately AFTER the active one so the
/gsd-manager dashboard can preview upcoming phases without mixing
them into the active phases grid.
Changes:
- roadmap.ts: exports two new helpers
- extractPhasesFromSection(section): parses phase number / name /
goal / depends_on using the same pattern initManager uses for
the active milestone, so queued phases have identical shape.
- extractNextMilestoneSection(content, projectDir): resolves the
current milestone via the STATE-first path (matching upstream
PR #2508) then scans for the next ## milestone heading. Shipped
milestones are stripped first so they can't shadow the real
next. Returns null when the active milestone is the last one.
- init-complex.ts: initManager now exposes
- queued_phases: Array<{ number, name, display_name, goal,
depends_on, dep_phases, deps_display }>
- queued_milestone_version: string | null
- queued_milestone_name: string | null
Existing phases array is unchanged — callers that only care about
the active milestone see no behavior difference.
Scope note: PR #2508 (merged upstream 2026-04-21) superseded the
#2495 + #2496 portions of this branch's original submission. This
commit is the rebased remainder contributing only #2497 on top of
upstream's new helpers.
Test coverage (7 new tests, all passing):
- roadmap.test.ts: +5 tests
- extractPhasesFromSection parses multiple phases with goal + deps
- extractPhasesFromSection returns [] when no phase headings
- extractNextMilestoneSection returns the milestone after the
STATE-resolved active one
- extractNextMilestoneSection returns null when active is last
- extractNextMilestoneSection returns null when no version found
- init-complex.test.ts: +4 tests under `queued_phases (#2497)`
- surfaces next milestone with version + name metadata
- queued entries carry name / deps_display / display_name
- queued phases are NOT mixed into active phases list
- returns [] + nulls when active is the last milestone
All 51 tests in roadmap.test.ts + init-complex.test.ts pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(workflows): render queued_phases section in /gsd-manager dashboard
Surfaces the new `queued_phases` / `queued_milestone_version` /
`queued_milestone_name` fields from init.manager (SDK #2497) in a
compact preview section directly below the main active-milestone
table.
Changes to workflows/manager.md:
- Initialize step: parse the optional trio
(queued_milestone_version, queued_milestone_name, queued_phases)
alongside the existing init.manager fields. Treat missing as
empty for backward compatibility with older SDK versions.
- Dashboard step: new "Queued section (next milestone preview)"
rendered between the main active-milestone grid and the
Recommendations section. Renders only when queued_phases is
non-empty; skipped entirely when absent or empty (e.g. active
milestone is the last one).
- Queued rows render without D/P/E columns since the phases haven't
been discussed yet — just number, display_name, deps_display,
and a fixed "· Queued" status.
- Success criterion added: queued section renders when non-empty
and is skipped when absent.
Queued phases are deliberately NOT eligible for the Continue action
menu; they live in a future milestone. The preview exists for
situational awareness only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When model_profile is "inherit", execute-phase was passing the literal string
"inherit" to Task(model=), causing fallback to the default model. The workflow
now documents that executor_model=="inherit" requires omitting the model= parameter
entirely so Claude Code inherits the orchestrator model automatically.
Closes#2516
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
Use process.platform !== 'win32' guard in catch instead of a comment, and add
regression test for bug #2525 (gsd-sdk bin symlink points at non-executable file).
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 8 file list omitted deferred-items.md, leaving executor out-of-scope
findings untracked after final commit even with commit_docs: true.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): forward --ws workstream flag through query dispatch (closes#2524)
- cli.ts: pass args.ws as workstream to registry.dispatch()
- registry.ts: add workstream? param to dispatch(), thread to handler
- utils.ts: add optional workstream? to QueryHandler type signature
- helpers.ts: planningPaths() accepts workstream? and uses relPlanningPath()
- All ~26 query handlers updated to receive and pass workstream to planningPaths()
- Config/commit/intel handlers use _workstream (project-global, not scoped)
- Add failing-then-passing test: tests/bug-2524-sdk-query-ws-flag.test.cjs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): forward workstream to all downstream query helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): rewrite #2524 test as static source assertions — no sdk/dist build in CI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Flips the bias in step 8b: build a simple HTML page/web UI by default,
fall back to stdout only for pure fact-checking (binary yes/no, benchmarks).
Mirrors upstream spike-idea skill constraint #3 update.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spike workflow:
- Add frontier mode (no-arg or "frontier" proposes integration + frontier spikes)
- Add depth-over-speed principle — follow surprising findings, test edge cases,
document investigation trail not just verdict
- Add CONVENTIONS.md awareness — follow established patterns, update after session
- Add Requirements section in MANIFEST — track design decisions as they emerge
- Add re-ground step before each spike to prevent drift in long sessions
- Add Investigation Trail section to README template
- Restructured prior context loading with priority ordering
- Research step now runs per-spike with briefing and approach comparison table
Sketch workflow:
- Add frontier mode (no-arg or "frontier" proposes consistency + frontier sketches)
- Add spike context loading — ground mockups in real data shapes, requirements,
and conventions from spike findings
Spike wrap-up workflow:
- Add CONVENTIONS.md generation step (recurring stack/structure/pattern choices)
- Reference files now use implementation blueprint format (Requirements, How to
Build It, What to Avoid, Constraints)
- SKILL.md now includes requirements section from MANIFEST
- Next-steps route to /gsd-spike frontier mode instead of inline analysis
Sketch wrap-up workflow:
- Next-steps route to /gsd-sketch frontier mode
Commands updated with frontier mode in descriptions and argument hints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spike workflow:
- Add frontier mode (no-arg or "frontier" proposes integration + frontier spikes)
- Add depth-over-speed principle — follow surprising findings, test edge cases,
document investigation trail not just verdict
- Add CONVENTIONS.md awareness — follow established patterns, update after session
- Add Requirements section in MANIFEST — track design decisions as they emerge
- Add re-ground step before each spike to prevent drift in long sessions
- Add Investigation Trail section to README template
- Restructured prior context loading with priority ordering
- Research step now runs per-spike with briefing and approach comparison table
Sketch workflow:
- Add frontier mode (no-arg or "frontier" proposes consistency + frontier sketches)
- Add spike context loading — ground mockups in real data shapes, requirements,
and conventions from spike findings
Spike wrap-up workflow:
- Add CONVENTIONS.md generation step (recurring stack/structure/pattern choices)
- Reference files now use implementation blueprint format (Requirements, How to
Build It, What to Avoid, Constraints)
- SKILL.md now includes requirements section from MANIFEST
- Next-steps route to /gsd-spike frontier mode instead of inline analysis
Sketch wrap-up workflow:
- Next-steps route to /gsd-sketch frontier mode
Commands updated with frontier mode in descriptions and argument hints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(sdk): stripShippedMilestones handles inline SHIPPED headings; getMilestoneInfo prefers STATE.md
Fixes two compounding bugs:
- #2496: stripShippedMilestones only stripped <details> blocks, ignoring
'## Heading — ✅ SHIPPED ...' inline markers. Shipped milestone sections
were leaking into downstream parsers.
- #2495: getMilestoneInfo checked STATE.md frontmatter only as a last-resort
fallback, so it returned the first heading match (often a leaked shipped
milestone) rather than the current milestone. Moved STATE.md check to
priority 1, consistent with extractCurrentMilestone.
Closes#2495Closes#2496
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(roadmap): handle ### SHIPPED headings and STATE.md version-only case
Two follow-up fixes from CodeRabbit review of #2508:
1. stripShippedMilestones only split on ## boundaries; ### headings marked
✅ SHIPPED were not stripped, leaking into fallback parsers. Expanded
the split/filter regex to #{2,3} to align with extractCurrentMilestone.
2. getMilestoneInfo's early-return on parseMilestoneFromState discarded the
real milestone name from ROADMAP.md when STATE.md had only `milestone:`
(no `milestone_name:`), returning the placeholder name 'milestone'.
Now only short-circuits when STATE.md provides a real name; otherwise
falls through to ROADMAP for the name while using stateVersion to
override the version in every ROADMAP-derived return path.
Tests: +2 new cases (### SHIPPED heading, version-only STATE.md).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(insert-phase): update STATE.md next-phase recommendation after inserting a phase
Closes#2502
* fix(insert-phase): update all STATE.md pointers; tighten test scope
Two follow-up fixes from CodeRabbit review of #2509:
1. The update_project_state instruction only said to find "the line" for
the next-phase recommendation. STATE.md can have multiple pointers
(structured current_phase: field AND prose recommendation text).
Updated wording to explicitly require updating all of them in the same
edit.
2. The regression test for the next-phase pointer update scanned the
entire file, so a match anywhere would pass even if update_project_state
itself was missing the instruction. Scoped the assertion to only the
content inside <step name="update_project_state"> to prevent false
positives.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(detect-custom-files): exclude skills and command dirs not wiped by installer (closes#2505)
GSD_MANAGED_DIRS included 'skills' and 'command' directories, but the
installer never wipes those paths. Users with third-party skills installed
(40+ files, none in GSD's manifest) had every skill flagged as a "custom
file" requiring backup, producing noisy false-positive reports on every
/gsd-update run.
Removes 'skills' and 'command' from both gsd-tools.cjs and the SDK's
detect-custom-files.ts. Adds two regression tests confirming neither
directory is scanned.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(settings): warn that model profiles are no-ops on non-Claude runtimes (closes#2506)
settings.md presented Quality/Balanced/Budget model profiles without any
indication that these tiers map to Claude models (Opus/Sonnet/Haiku) and
have no effect on non-Claude runtimes (Codex, Gemini CLI, OpenRouter).
Users on Codex saw the profile chooser as if it would meaningfully select
models, but all agents silently used the runtime default regardless.
Adds a non-Claude runtime note before the profile question (shown in
TEXT_MODE, the path all non-Claude runtimes take) explaining the profiles
are no-ops and directing users to either choose Inherit or configure
model_overrides manually. Also updates the Inherit option description to
explicitly name the runtimes where it is the correct choice.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The guard at the worktree-merge resurrection block was inverting the
intended logic: it deleted any .planning/ file absent from PRE_MERGE_FILES,
which includes brand-new files (e.g. SUMMARY.md just created by the
executor). A genuine resurrection is a file that was previously tracked on
main, deliberately removed, and then re-introduced by the merge. Detecting
that requires a git history check — not just tree membership.
Fix: replace the PRE_MERGE_FILES grep guard with a `git log --follow
--diff-filter=D` check that only removes the file if it has a deletion
event in main's ancestry.
Closes#2501
* feat(plan-phase): chunked mode + filesystem fallback for Windows stdio hang (#2310)
Addresses the 2026-04-16 Windows incident where gsd-planner wrote all 5
PLAN.md files to disk but Task() never returned, hanging the orchestrator
for 30+ minutes. Two mitigations:
1. Filesystem fallback (steps 9a, 11a): when Task() returns with an
empty/truncated response but PLAN.md files exist on disk, surface a
recoverable prompt (Accept plans / Retry planner / Stop) instead of
silently failing. Directly addresses the post-restart recovery path.
2. Chunked mode (--chunked flag / workflow.plan_chunked config): splits the
single long-lived planner Task into a short outline Task (~2 min) followed
by N short per-plan Tasks (~3-5 min each). Each plan is committed
individually for crash resilience. A hang loses one plan, not all of them.
Resume detection skips plans already on disk on re-run.
RCA confirmed: task state mtime 14:29 vs PLAN.md writes 14:32-14:52 =
subagent completed normally, IPC return was dropped by Windows stdio deadlock.
Neither mitigation fixes the root cause (requires upstream Task() timeout
support); both bound damage and enable recovery.
New reference file planner-chunked.md keeps OUTLINE COMPLETE / PLAN COMPLETE
return formats out of gsd-planner.md (which sits at 46K near its size limit).
Closes#2310
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plan-phase): address CodeRabbit review comments on #2499
- docs/CONFIGURATION.md: add workflow.plan_chunked to full JSON schema example
- plan-phase.md step 8.5.1: validate PLAN-OUTLINE.md with grep for OUTLINE
COMPLETE marker before reusing (not just file existence)
- plan-phase.md step 8.5.2: validate per-plan PLAN.md has YAML frontmatter
(head -1 grep for ---) before skipping in resume path
- plan-phase.md: add language tags (text/javascript/bash) to bare fenced
code blocks in steps 8.5, 9a, 11a (markdownlint MD040)
- Rejected: commit_docs gate on per-plan commits (gsd-sdk query commit
already respects commit_docs internally — comment was a false positive)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(plan-phase): route Accept-plans through step 9 PLANNING COMPLETE handling
Honors --skip-verify / plan_checker_enabled=false in 9a fallback path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(execute-phase): post-merge deletion audit for bulk file deletions (closes#2384)
Two data-loss incidents were caused by worktree merges bringing in bulk
file deletions silently. The pre-merge check (HEAD...WT_BRANCH) catches
deletions on the worktree branch, but files deleted during the merge
itself (e.g., from merge conflict resolution or stale branch state) were
not audited post-merge.
Adds a post-merge audit immediately after git merge --no-ff succeeds:
- Counts files deleted outside .planning/ in the merge commit
- If count > 5 and ALLOW_BULK_DELETE!=1: reverts the merge with
git reset --hard HEAD~1 and continues to the next worktree
- Logs the full file list and an escape-hatch instruction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): tighten post-merge deletion audit assertions (CodeRabbit #2483)
Replace loose substring checks with exact regex assertions:
- assert.match against 'git diff --diff-filter=D --name-only HEAD~1 HEAD'
- assert.match against threshold gate + ALLOW_BULK_DELETE override condition
- assert.match against git reset --hard HEAD~1 revert
- assert.match against MERGE_DEL_COUNT grep -vc for non-.planning count
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(inventory): update workflow count to 81 (graduation.md added in #2490)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(install): remove bare ~/.claude reference in update.md (closes#2470)
The installer's copyWithPathReplacement() replaces ~/\.claude\/ (with
trailing slash) but not ~/\.claude (bare, no trailing slash). A comment
on line 398 of update.md used the bare form, which scanForLeakedPaths()
correctly flagged for every non-Claude runtime install.
Replaced the example in the comment with a non-Claude runtime path so
the file passes the scanner for all runtimes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): align regex with installer's word-boundary semantics (CodeRabbit #2482)
Replace negative lookahead (?!\/) with \b word boundary to match the
installer's scanForLeakedPaths() pattern. The lookahead would incorrectly
flag ~/.claude_suffix whereas \b correctly excludes it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): revert \b regex — (?!\/) was intentionally scoped to bare refs
The installer's scanForLeakedPaths uses \b but the test is specifically
checking for bare ~/.claude without trailing slash that the replacer misses.
~/.claude/ (with slash) at line 359 of update.md is expected and handled.
\b would flag it as a false positive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(inventory): update workflow count to 81 (graduation.md added in #2490)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(assembly): add link mode for CLAUDE.md @-reference sections (#2415)
Adds `claude_md_assembly.mode: "link"` config option that writes
`@.planning/<source>` instead of inlining content between GSD markers,
reducing typical CLAUDE.md size by ~65%. Per-block overrides available
via `claude_md_assembly.blocks.<section>`. Falls back to embed for
sections without a real source file (workflow, fallbacks).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): add positive assertion for embedded workflow content (CodeRabbit #2484)
The negative assertion only confirmed @GSD defaults wasn't written.
Add assert.ok(content.includes('GSD Workflow Enforcement')) to verify
the workflow section is actually embedded inline when link mode falls back.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds step 10.5 to gsd-new-milestone that scans pending todos against the
approved roadmap and tags matches with `resolves_phase: N` in their YAML
frontmatter. Adds a `close_phase_todos` step to execute-phase that moves
tagged todos to `completed/` when the phase completes — closing the loop
automatically with no manual cleanup.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): update 5 source-text tests to read config-schema.cjs
VALID_CONFIG_KEYS moved from config.cjs to config-schema.cjs in the
drift-prevention companion PR. Tests that read config.cjs source text
and checked for key literal includes() now point to the correct file.
Closes#2480
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(cli): add /gsd-sync-skills for cross-runtime managed skill sync (#2380)
Adds /gsd-sync-skills command so multi-runtime users can keep gsd-* skill
directories aligned across runtime roots after updating one runtime with gsd-update.
Changes:
- bin/install.js: add --skills-root <runtime> flag that prints the skills root
path for any supported runtime, reusing the existing getGlobalDir() table.
Banner is suppressed when --skills-root is used (machine-readable output).
- commands/gsd/sync-skills.md: slash command definition
- get-shit-done/workflows/sync-skills.md: full workflow spec covering argument
parsing, path resolution via --skills-root, diff computation (CREATE/UPDATE/
REMOVE/SKIP), dry-run report (default), apply execution, idempotency guarantee,
and safety rules (only gsd-* touched, dry-run performs no writes).
Safety rules: only gsd-* directories are ever created/updated/removed; non-GSD
skills in destination roots are never touched; --dry-run is the default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): update 5 source-text tests to read config-schema.cjs
VALID_CONFIG_KEYS moved from config.cjs to config-schema.cjs in the
drift-prevention companion PR. Tests that read config.cjs source text
and checked for key literal includes() now point to the correct file.
Closes#2480
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(workflows): close LEARNINGS.md consumption-and-graduation loop (#2430)
Part A — Consumption: extend plan-phase.md cross-phase context load to include
LEARNINGS.md files from the 3 most recent prior phases (same recency gate as
CONTEXT.md + SUMMARY.md: CONTEXT_WINDOW >= 500000 only). Also loads LEARNINGS.md
from any phases in the Depends-on chain. Silent skip if absent; 15% context
budget cap with oldest-first truncation; [from Phase N LEARNINGS] attribution.
Part B — Graduation: add graduation_scan step to transition.md (after
evolve_project) that delegates to new graduation.md helper workflow. The helper
clusters recurring items across the last N phases (default window=5, threshold=3)
using Jaccard lexical similarity, surfaces HITL Promote/Defer/Dismiss prompts,
routes promotions to PROJECT.md or PATTERNS.md by category, annotates graduated
items with `graduated:` field, and persists dismissed/deferred clusters in
STATE.md graduation_backlog. Always non-blocking; silently no-ops on first phase
or when data is insufficient.
Also: adds optional `graduated:` annotation docs to extract_learnings.md schema.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(graduation): address CodeRabbit review findings on PR #2490
- graduation.md: unify insufficient-data guard to silent-skip (remove
contradictory [no-op] print path)
- graduation.md: add TEXT_MODE fallback for HITL cluster prompts
- graduation.md: add A (defer-all) to accepted actions [P/D/X/A]
- graduation.md: tag untyped code fences with text language (MD040)
- transition.md: tag untyped graduation.md fence with text language
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(graduation): rephrase TEXT_MODE line to avoid prompt-injection scanner false positive
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds artifacts.cjs with canonical .planning/ root file names, W019 warning
in gsd-health that flags unrecognized .md files at the .planning/ root, and
templates/README.md as the authoritative artifact index for agents and humans.
Closes#2448
* fix(tests): update 5 source-text tests to read config-schema.cjs
VALID_CONFIG_KEYS moved from config.cjs to config-schema.cjs in the
drift-prevention companion PR. Tests that read config.cjs source text
and checked for key literal includes() now point to the correct file.
Closes#2480
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(agents): sycophancy hardening for 9 audit-class agents (#2427)
Add adversarial reviewer posture to gsd-plan-checker, gsd-code-reviewer,
gsd-security-auditor, gsd-verifier, gsd-eval-auditor, gsd-nyquist-auditor,
gsd-ui-auditor, gsd-integration-checker, and gsd-doc-verifier.
Four changes per agent:
- Third-person framing: <role> opens with submission framing, not "You are a GSD X"
- FORCE stance: explicit starting hypothesis that the submission is flawed
- Failure modes: agent-specific list of how each reviewer type goes soft
- BLOCKER/WARNING classification: every finding must carry an explicit severity
Also applies to sdk/prompts/agents variants of gsd-plan-checker and gsd-verifier.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(roadmap): surface wave dependencies and cross-cutting constraints (#2447)
Adds roadmap.annotate-dependencies command that post-processes a phase's
ROADMAP plan list to insert wave dependency notes and surface must_haves.truths
entries shared across 2+ plans as cross-cutting constraints. Operation is
idempotent and purely derived from existing PLAN frontmatter.
Closes#2447
* fix(roadmap): address CodeRabbit review findings on PR #2487
- roadmap.cjs: expand idempotency guard to also check for existing
cross-cutting constraints header, preventing duplicate injection on
re-runs; add content equality check before writing to preserve
true idempotency for single-wave phases
- plan-phase.md: move ROADMAP annotation (13d) before docs commit (13c)
so annotated ROADMAP.md is included in the commit rather than left dirty;
include .planning/ROADMAP.md in committed files list
- sdk/src/query/index.ts: add annotate-dependencies aliases to
QUERY_MUTATION_COMMANDS so the mutation is properly event-wired
- sdk/src/query/roadmap.ts: add timeout (15s) and maxBuffer to spawnSync;
check result.error before result.status to handle spawn/timeout failures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds W018 warning when .planning/milestones/vX.Y-ROADMAP.md snapshots
exist without a corresponding entry in MILESTONES.md. Introduces
--backfill flag to synthesize missing entries from snapshot titles.
Closes#2446
* fix(tests): clear CLAUDECODE env var in read-guard test runner
The hook skips its advisory on two env vars: CLAUDE_SESSION_ID and
CLAUDECODE. runHook() cleared CLAUDE_SESSION_ID but inherited CLAUDECODE
from process.env, so tests run inside a Claude Code session silently
no-oped and produced no stdout, causing JSON.parse to throw.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): update ARCHITECTURE.md counts and add TEXT_MODE fallback to sketch workflow
Four new spike/sketch files were added in 1.37.0 but two housekeeping
items were missed: ARCHITECTURE.md component counts (75→79 commands,
72→76 workflows) and the required TEXT_MODE fallback in sketch.md for
non-Claude runtimes (#2012).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): update directory-tree slash command count in ARCHITECTURE.md
Missed the second count in the directory tree (# 75 slash commands → 79).
The prose "Total commands" was updated but the tree annotation was not,
causing command-count-sync.test.cjs to fail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>