@opengsd/gsd-core 1.19.0 → 1.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.compact.md +5 -5
  4. package/agents/gsd-code-fixer.md +9 -4
  5. package/agents/gsd-code-reviewer.compact.md +49 -8
  6. package/agents/gsd-code-reviewer.md +53 -14
  7. package/agents/gsd-executor.md +8 -4
  8. package/bin/install.js +36 -40
  9. package/commands/gsd/new-milestone.md +2 -2
  10. package/commands/gsd/new-project.md +2 -2
  11. package/commands/gsd/update.md +5 -2
  12. package/gsd-core/bin/gsd-tools.cjs +177 -93
  13. package/gsd-core/bin/lib/capability-lifecycle.cjs +1 -12
  14. package/gsd-core/bin/lib/capability-registry.cjs +284 -93
  15. package/gsd-core/bin/lib/capability-validator.cjs +15 -0
  16. package/gsd-core/bin/lib/commands.cjs +46 -17
  17. package/gsd-core/bin/lib/config-loader.cjs +3 -3
  18. package/gsd-core/bin/lib/core-utils.cjs +11 -0
  19. package/gsd-core/bin/lib/frontmatter.cjs +21 -3
  20. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
  21. package/gsd-core/bin/lib/gate-context-drift.cjs +1 -1
  22. package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
  23. package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
  24. package/gsd-core/bin/lib/git-info-exclude.cjs +1 -6
  25. package/gsd-core/bin/lib/gsd-tool-dir.cjs +134 -0
  26. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +13 -9
  27. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
  28. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -3
  29. package/gsd-core/bin/lib/init.cjs +181 -68
  30. package/gsd-core/bin/lib/install-engine.cjs +9 -9
  31. package/gsd-core/bin/lib/install-manifest-record.cjs +86 -0
  32. package/gsd-core/bin/lib/install-profiles.cjs +4 -2
  33. package/gsd-core/bin/lib/io.cjs +6 -0
  34. package/gsd-core/bin/lib/markdown-sectionizer.cjs +34 -1
  35. package/gsd-core/bin/lib/markdown-table.cjs +23 -0
  36. package/gsd-core/bin/lib/mcp-server.cjs +8 -5
  37. package/gsd-core/bin/lib/milestone.cjs +18 -50
  38. package/gsd-core/bin/lib/phase-entry-grammar.cjs +119 -0
  39. package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
  40. package/gsd-core/bin/lib/phase-id.cjs +627 -36
  41. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -74
  42. package/gsd-core/bin/lib/phase-locator.cjs +328 -19
  43. package/gsd-core/bin/lib/phase.cjs +3039 -666
  44. package/gsd-core/bin/lib/planning-document.cjs +26 -5
  45. package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
  46. package/gsd-core/bin/lib/planning-snapshot.cjs +12 -7
  47. package/gsd-core/bin/lib/planning-workspace.cjs +3 -3
  48. package/gsd-core/bin/lib/read-only-git-env.cjs +23 -2
  49. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
  50. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -0
  51. package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
  52. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
  53. package/gsd-core/bin/lib/roadmap-parser.cjs +759 -144
  54. package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
  55. package/gsd-core/bin/lib/roadmap-upgrade.cjs +8 -5
  56. package/gsd-core/bin/lib/roadmap.cjs +358 -106
  57. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -130
  58. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +6 -0
  59. package/gsd-core/bin/lib/shell-command-projection.cjs +36 -9
  60. package/gsd-core/bin/lib/smart-entry.cjs +22 -11
  61. package/gsd-core/bin/lib/state-contract.cjs +11 -7
  62. package/gsd-core/bin/lib/state-document.cjs +332 -23
  63. package/gsd-core/bin/lib/state-transition.cjs +51 -15
  64. package/gsd-core/bin/lib/state.cjs +499 -180
  65. package/gsd-core/bin/lib/template.cjs +2 -2
  66. package/gsd-core/bin/lib/validate.cjs +3 -2
  67. package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
  68. package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
  69. package/gsd-core/bin/lib/verify.cjs +2 -2
  70. package/gsd-core/bin/lib/workstream-inventory.cjs +27 -21
  71. package/gsd-core/bin/lib/worktree-base-ref.cjs +32 -2
  72. package/gsd-core/bin/verify-reapply-patches.cjs +411 -10
  73. package/gsd-core/references/cicd-review-patterns.md +45 -0
  74. package/gsd-core/references/necessity-review.md +48 -0
  75. package/gsd-core/workflows/code-review-fix.md +46 -5
  76. package/gsd-core/workflows/code-review.md +3 -0
  77. package/gsd-core/workflows/execute-phase.md +1 -1
  78. package/gsd-core/workflows/execute-plan.md +1 -1
  79. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
  80. package/gsd-core/workflows/plan-phase.md +1 -1
  81. package/gsd-core/workflows/pr-branch.md +2 -0
  82. package/gsd-core/workflows/reapply-patches.md +104 -0
  83. package/gsd-core/workflows/review.md +54 -141
  84. package/gsd-core/workflows/secure-phase.md +1 -1
  85. package/gsd-core/workflows/settings-advanced.md +57 -9
  86. package/gsd-core/workflows/ship.md +12 -0
  87. package/hooks/dist/gsd-agent-isolation-guard.js +1 -1
  88. package/hooks/dist/gsd-cursor-subagent-start.js +1 -1
  89. package/hooks/dist/gsd-graphify-update.sh +6 -0
  90. package/hooks/dist/gsd-statusline.js +3 -2
  91. package/hooks/dist/gsd-windsurf-pre-write.js +4 -4
  92. package/hooks/dist/gsd-workflow-guard.js +2 -1
  93. package/hooks/dist/gsd-worktree-path-guard.js +4 -4
  94. package/hooks/dist/lib/git-probe.js +16 -0
  95. package/hooks/dist/lib/isolation-sentinel.js +77 -2
  96. package/hooks/gsd-agent-isolation-guard.js +1 -1
  97. package/hooks/gsd-cursor-subagent-start.js +1 -1
  98. package/hooks/gsd-graphify-update.sh +6 -0
  99. package/hooks/gsd-statusline.js +3 -2
  100. package/hooks/gsd-windsurf-pre-write.js +4 -4
  101. package/hooks/gsd-workflow-guard.js +2 -1
  102. package/hooks/gsd-worktree-path-guard.js +4 -4
  103. package/hooks/lib/git-probe.js +16 -0
  104. package/hooks/lib/isolation-sentinel.js +77 -2
  105. package/package.json +3 -3
  106. package/pi/package-agents/gsd-code-fixer.md +9 -4
  107. package/pi/package-agents/gsd-code-reviewer.md +53 -14
  108. package/pi/package-agents/gsd-executor.md +8 -4
  109. package/pi/package-agents/manifest.json +3 -3
  110. package/pi/package-skills/gsd-new-milestone/SKILL.md +1 -1
  111. package/pi/package-skills/gsd-new-project/SKILL.md +1 -1
  112. package/pi/package-skills/gsd-update/SKILL.md +4 -1
  113. package/scripts/changeset/lint.cjs +28 -3
  114. package/scripts/ci-next-health.cjs +246 -23
  115. package/scripts/docs-guard-registry.cjs +2 -0
  116. package/scripts/gen-features.cjs +14 -2
  117. package/scripts/lib/drift-scan.cjs +27 -0
  118. package/scripts/lib/macos-conformance-tier.generated.cjs +10 -0
  119. package/scripts/lib/platform-conformance-tier.generated.cjs +15 -0
  120. package/scripts/lint-phase-enumeration-drift.cjs +8 -4
  121. package/scripts/lint-roadmap-progress-drift.cjs +421 -0
  122. package/scripts/merge-commit-pr.cjs +186 -0
  123. package/scripts/mutation-matrix.cjs +23 -1
  124. package/scripts/release-ancestry.cjs +86 -0
  125. package/scripts/release-pipeline-pr.cjs +92 -0
  126. package/scripts/require-issue-link-policy.cjs +18 -9
  127. package/scripts/sync-next-version.cjs +48 -7
  128. package/skills/gsd-new-milestone/SKILL.md +1 -1
  129. package/skills/gsd-new-project/SKILL.md +1 -1
  130. package/skills/gsd-update/SKILL.md +5 -2
  131. package/vscode/package.json +1 -1
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "gsd-core",
11
11
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
12
- "version": "1.19.0",
12
+ "version": "1.20.1",
13
13
  "source": "./",
14
14
  "author": {
15
15
  "name": "open-gsd",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gsd-core",
3
3
  "displayName": "GSD Core",
4
- "version": "1.19.0",
4
+ "version": "1.20.1",
5
5
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
6
6
  "author": {
7
7
  "name": "open-gsd",
@@ -256,17 +256,17 @@ Treat this as a finally-block obligation: even on early exit (config error, no f
256
256
 
257
257
  <step name="load_context">
258
258
  1. Read all `<required_reading>` files if present.
259
- 2. Parse `<config>` block: `phase_dir`, `padded_phase`, `review_path` (full path to REVIEW.md), `fix_scope` ("critical_warning" default, or "all" includes Info), `fix_report_path` (output REVIEW-FIX.md path).
259
+ 2. Parse `<config>` block: `phase_dir`, `padded_phase`, `review_path` (full path to REVIEW.md), `fix_scope` ("critical_warning" default, or "all" includes Info and necessity), `fix_report_path` (output REVIEW-FIX.md path).
260
260
  3. `cat {review_path}`.
261
261
  4. Parse frontmatter `status:`. If `"clean"` or `"skipped"`: exit with "No issues to fix -- REVIEW.md status is {status}." — do NOT create REVIEW-FIX.md, exit 0 (not an error).
262
262
  5. Load project context (`<project_context>`): CLAUDE.md, skills.
263
263
  </step>
264
264
 
265
265
  <step name="parse_findings">
266
- 1. Extract findings via `<finding_parser>` rules: `id`, `severity` (Critical CR-*/BL-*, Warning WR-*, Info IN-*), `title`, `file` (primary), `files` (all referenced, for multi-file fixes), `line` (or null), `issue`, `fix` (may be multi-line/code fences).
267
- 2. Filter by `fix_scope`: `critical_warning` → CR-*/BL-*/WR-* only; `all` → + IN-*.
268
- 3. Sort: Critical first, then Warning, then Info; same-severity keeps document order.
269
- 4. Record `findings_in_scope` count for frontmatter.
266
+ 1. Extract findings via `<finding_parser>` rules: `id`, `severity` (Critical CR-*/BL-*, Warning WR-*, Info IN-*), `category` (`necessity` when the finding carries a `**Category:** necessity` line, #4100 — unnecessary code, not broken code), `title`, `file` (primary), `files` (all referenced, for multi-file fixes), `line` (or null), `issue`, `fix` (may be multi-line/code fences).
267
+ 2. Filter by `fix_scope`: `critical_warning` → CR-*/BL-*/WR-* only, and `critical_warning` excludes necessity findings (removal is the human's scope call; the marker overrides the prefix, so a WR-* carrying `**Category:** necessity` is out of scope and skipped with no edit); `all` → + IN-*, and `all` includes necessity findings, ordered last — record each as `fixed: requires human verification`.
268
+ 3. Sort: Critical first, then Warning, then Info, then necessity; same-severity keeps document order.
269
+ 4. Record `findings_in_scope` count for frontmatter. If `findings_in_scope` is 0, apply nothing: write REVIEW-FIX.md with `fixed: 0`, `skipped: 0`, `status: all_fixed`, and a body stating no finding was in scope and the out-of-scope findings remain in REVIEW.md.
270
270
  </step>
271
271
 
272
272
  <step name="apply_fixes">
@@ -158,6 +158,8 @@ Each finding starts with:
158
158
 
159
159
  Where ID matches: `CR-\d+` or `BL-\d+` (Critical-tier-equivalent), `WR-\d+` (Warning), or `IN-\d+` (Info)
160
160
 
161
+ A finding whose body carries a `**Category:** necessity` line is a **necessity finding** (#4100): code the reviewer judged unnecessary, not broken. Its prefix still gives its severity.
162
+
161
163
  **Required Fields:**
162
164
 
163
165
  - **File:** line contains primary file path
@@ -439,7 +441,7 @@ This cleanup is unconditional when a worktree was created — register it mental
439
441
  - `phase_dir`: Path to phase directory (e.g., `.planning/phases/02-code-review-command`)
440
442
  - `padded_phase`: Zero-padded phase number (e.g., "02")
441
443
  - `review_path`: Full path to REVIEW.md (e.g., `.planning/phases/02-code-review-command/02-REVIEW.md`)
442
- - `fix_scope`: "critical_warning" (default) or "all" (includes Info findings)
444
+ - `fix_scope`: "critical_warning" (default) or "all" (includes Info and necessity findings)
443
445
  - `fix_report_path`: Full path for REVIEW-FIX.md output (e.g., `.planning/phases/02-code-review-command/02-REVIEW-FIX.md`)
444
446
 
445
447
  **3. Read REVIEW.md:**
@@ -465,6 +467,7 @@ Read `./CLAUDE.md` and check for `.claude/skills/` or `.agents/skills/` (as desc
465
467
  For each finding, extract:
466
468
  - `id`: Finding identifier (e.g., CR-01, WR-03, IN-12)
467
469
  - `severity`: Critical (CR-* or BL-*), Warning (WR-*), Info (IN-*)
470
+ - `category`: `necessity` when the finding carries a `**Category:** necessity` line, otherwise none
468
471
  - `title`: Issue title from `### ` heading
469
472
  - `file`: Primary file path from **File:** line
470
473
  - `files`: ALL file paths referenced in finding (including in Fix section) — for multi-file fixes
@@ -473,15 +476,17 @@ For each finding, extract:
473
476
  - `fix`: Full fix content from **Fix:** section (may be multi-line, may contain code fences)
474
477
 
475
478
  **2. Filter by fix_scope:**
476
- - If `fix_scope == "critical_warning"`: include only CR-*, BL-*, and WR-* findings
477
- - If `fix_scope == "all"`: include CR-*, BL-*, WR-*, and IN-* findings
479
+ - If `fix_scope == "critical_warning"`: include only CR-*, BL-*, and WR-* findings; `critical_warning` excludes necessity findings (removing code is a scope decision for the human, #4100). The marker overrides the prefix: a WR-* finding carrying `**Category:** necessity` is OUT of scope here, and is skipped without applying any edit
480
+ - If `fix_scope == "all"`: include CR-*, BL-*, WR-*, and IN-* findings; `all` includes necessity findings, ordered last. A necessity fix removes code, so record it as `fixed: requires human verification`
478
481
 
479
482
  **3. Sort findings by severity:**
480
- - Critical (CR-* and BL-*) first, then Warning, then Info
483
+ - Critical (CR-* and BL-*) first, then Warning, then Info, then necessity findings
481
484
  - Within same severity, maintain document order
482
485
 
483
486
  **4. Count findings in scope:**
484
487
  Record `findings_in_scope` for REVIEW-FIX.md frontmatter.
488
+
489
+ If `findings_in_scope` is 0, apply nothing: write REVIEW-FIX.md with `fixed: 0`, `skipped: 0`, `status: all_fixed`, and a body stating that no finding was in scope and the out-of-scope findings remain in REVIEW.md.
485
490
  </step>
486
491
 
487
492
  <step name="apply_fixes">
@@ -49,6 +49,8 @@ Read `./CLAUDE.md` if present — follow project guidelines, security requiremen
49
49
 
50
50
  **3. Code Quality** — dead code, unused imports/variables, poor naming, missing error handling, inconsistent patterns, overly complex functions (high cyclomatic complexity), code duplication, magic numbers, commented-out code
51
51
 
52
+ **4. Necessity** (#4100) — reachable code whose guarded-against problem cannot occur in this stack: impossible-state guard, already-defended-elsewhere, cargo-culted pattern, phantom requirement, generality without instantiation. At `standard`/`deep`, Read `~/.claude/gsd-core/references/necessity-review.md` before classifying; if unreadable, say so in the Summary and omit `necessity:`. `quick` does not assess necessity. Warning when provable from the repo, Info when judgment, Never Critical; cite the evidence the state cannot occur and add a `**Category:** necessity` line.
53
+
52
54
  **Out of Scope (v1):** performance issues (O(n²) algorithms, memory leaks, inefficient queries) — NOT in scope. Focus on correctness, security, maintainability.
53
55
 
54
56
  </review_scope>
@@ -56,10 +58,12 @@ Read `./CLAUDE.md` if present — follow project guidelines, security requiremen
56
58
  <depth_levels>
57
59
 
58
60
  **quick** — pattern-matching only, grep/regex scan for common anti-patterns, no full file reads. Target: <2 min.
59
- Patterns: hardcoded secrets `(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['"][^'"]+['"]`; dangerous fns `eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru`; debug artifacts `console\.log|debugger;|TODO|FIXME|XXX|HACK`; empty catch `catch\s*\([^)]*\)\s*\{\s*\}`; commented-out code `^\s*//.*[{};]|^\s*#.*:|^\s*/\*`.
61
+ Checks (commands in `review_by_depth`): Hardcoded secrets; Dangerous functions; Debug artifacts; Empty catch blocks; Commented-out code; Unpinned actions; Non-idempotent install; Missing job timeout.
62
+ The last three: GitHub Actions workflows only.
60
63
 
61
64
  **standard** (default) — Read each changed file, check bugs/security/quality in context, cross-reference imports/exports. Target: 5-15 min.
62
65
  Language-aware checks: **JS/TS** unchecked `.length`, missing `await`, unhandled promise rejection, `as any`, `==` vs `===`, null coalescing issues. **Python** bare `except:`, mutable default args, f-string injection, `eval()`, missing `with` for file ops. **Go** unchecked error returns, goroutine leaks, context not passed, `defer` in loops, race conditions. **C/C++** buffer overflow patterns, use-after-free, null pointer deref, missing bounds checks, memory leaks. **Shell** unquoted variables, `eval`, missing `set -e`, command injection via interpolation.
66
+ **CI/CD pipelines** (by path: `.github/workflows/*.yml`, `Dockerfile*`, `docker-compose*.yml`, deploy scripts): Read `~/.claude/gsd-core/references/cicd-review-patterns.md`; if it cannot be read, say so in the Summary and check: non-idempotent steps (`npm install` vs `npm ci`, unlocked `pip install`, unguarded `mkdir`/`rm`), no job `timeout-minutes`, deploy without `concurrency` + `cancel-in-progress`, per-environment rebuild (not build-once), no health check or rollback, load-bearing `continue-on-error`, static credentials where OIDC works, unpinned actions.
63
67
 
64
68
  **deep** — all of standard + cross-file analysis: trace call chains across imports, check type consistency at API boundaries (TS interfaces, API contracts), verify error propagation (thrown errors caught by callers), check state mutation consistency across modules, detect circular dependencies/coupling. Target: 15-30 min.
65
69
 
@@ -109,6 +113,7 @@ Use its `files`. A `status` of `unresolvable` (exit 69, JSON still printed), or
109
113
  NOTE: do NOT exclude all `.md` — commands, workflows, and agents are source code in this codebase.
110
114
 
111
115
  **2. Group by language/type:** JS/TS (`.js`,`.jsx`,`.ts`,`.tsx`), Python (`.py`), Go (`.go`), C/C++ (`.c`,`.cpp`,`.h`,`.hpp`), Shell (`.sh`,`.bash`), other → generic.
116
+ CI/CD (by path): `.github/workflows/*.yml`/`*.yaml`, `Dockerfile*`, `docker-compose*.yml`, `deploy*.sh` (also Shell).
112
117
 
113
118
  **3. Exit early if empty:** create REVIEW.md with `status: skipped`, all finding counts 0. Body: "No source files to review after filtering. All files in scope are documentation, planning artifacts, or generated files. Use `status: skipped` (not `clean`) because no actual review was performed."
114
119
 
@@ -116,14 +121,47 @@ NOTE: `status: clean` = reviewed, no issues. `status: skipped` = no reviewable f
116
121
  </step>
117
122
 
118
123
  <step name="review_by_depth">
119
- **depth=quick:** run grep patterns from `<depth_levels>` against all files:
124
+ **depth=quick:** run every check from `<depth_levels>`:
120
125
  ```bash
121
- grep -n -E "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"]\w+['\"]" file
122
- grep -n -E "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec" file
123
- grep -n -E "console\.log|debugger;|TODO|FIXME|XXX|HACK" file
124
- grep -n -E "catch\s*\([^)]*\)\s*\{\s*\}" file
126
+ # Set FILE per scoped path with: while IFS= read -r FILE (never paste a name into code).
127
+
128
+ # Hardcoded secrets
129
+ grep -n -E -- "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"][^'\"]+['\"]" "$FILE"
130
+
131
+ # Dangerous functions
132
+ grep -n -E -- "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru" "$FILE"
133
+
134
+ # Debug artifacts
135
+ grep -n -E -- "console\.log|debugger;|TODO|FIXME|XXX|HACK" "$FILE"
136
+
137
+ # Empty catch blocks
138
+ grep -n -E -- "catch\s*\([^)]*\)\s*\{\s*\}" "$FILE"
139
+
140
+ # Commented-out code
141
+ grep -n -E -- "^\s*//.*[{};]|^\s*#[[:space:]]*(if|for|while|def|class|elif|else|try|except|with)[[:space:](].*:[[:space:]]*$|^\s*/\*[^*].*[{};]" "$FILE"
142
+
143
+ # Unpinned actions
144
+ grep -n -E -- "^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]*['\"]?[^[:space:]'\"]+@(main|master|latest)['\"]?([[:space:]]|$)" "$FILE"
145
+
146
+ # Non-idempotent install
147
+ [ -f package-lock.json ] && grep -n -E -- "(^|[^[:alnum:]_-])npm[[:space:]]+(install|i)([[:space:]]|$)" "$FILE" | grep -v -E "^[0-9]+:[[:space:]]*#|[[:space:]](-g|--global)([[:space:]]|$)"
148
+
149
+ # Missing job timeout
150
+ awk '
151
+ function flush() { if (job != "" && runs && !timed) print line ": " job ": no job-level timeout-minutes"; job = "" }
152
+ { sub(/\r$/, "") }
153
+ /^[^[:space:]#]/ { flush(); injobs = ($0 ~ /^jobs:[[:space:]]*(#.*)?$/); jind = -1; next }
154
+ !injobs || /^[[:space:]]*(#.*)?$/ { next }
155
+ { ind = match($0, /[^ ]/) - 1 }
156
+ jind < 0 { jind = ind }
157
+ ind == jind { flush(); job = $0; sub(/^ +/, "", job); sub(/:.*/, "", job); line = NR; kind = -1; runs = 0; timed = 0; next }
158
+ kind < 0 { kind = ind }
159
+ ind == kind && /^ *runs-on:/ { runs = 1 }
160
+ ind == kind && /^ *timeout-minutes:/ { timed = 1 }
161
+ END { flush() }
162
+ ' "$FILE"
125
163
  ```
126
- Severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning.
164
+ Severity: secrets/dangerous=Critical, debug and commented-out code=Info, empty catch and the three CI/CD checks=Warning.
127
165
 
128
166
  **depth=standard:** per file — Read full content, apply language-specific checks, check for: functions >50 lines, deep nesting (>4 levels), missing error handling in async functions, hardcoded config values, type safety issues (TS `any`, loose Python typing). Record findings with file path, line number, description.
129
167
 
@@ -133,7 +171,7 @@ Severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning.
133
171
  <step name="classify_findings">
134
172
  **Critical** — security vulnerabilities, data loss, crashes, auth bypasses: SQL/command/path-traversal injection, hardcoded secrets in production code, null pointer derefs that crash, auth/authz bypasses, unsafe deserialization, buffer overflows.
135
173
 
136
- **Warning** — logic errors, unhandled edge cases, missing error handling, code smells that could cause bugs: unchecked array access, missing async error handling, off-by-one errors, `==` vs `===` coercion, unhandled promise rejections, dead code paths indicating logic errors.
174
+ **Warning** — logic errors, unhandled edge cases, missing error handling, code smells that could cause bugs: unchecked array access, missing async error handling, off-by-one errors, `==` vs `===` coercion, unhandled promise rejections, dead code paths indicating logic errors; CI/CD pipeline findings: every check in the CI/CD entry.
137
175
 
138
176
  **Info** — style, naming, dead code, unused imports, suggestions: unused imports/variables, poor naming (single letters except loop counters), commented-out code, TODO/FIXME, magic numbers, duplication.
139
177
 
@@ -158,10 +196,13 @@ findings:
158
196
  warning: N
159
197
  info: N
160
198
  total: N
199
+ necessity: N
161
200
  status: clean | issues_found
162
201
  ---
163
202
  ```
164
203
 
204
+ `necessity:` only at standard/deep: the necessity findings among `warning` + `info`, never added to `total`.
205
+
165
206
  **3. Body sections (required order):**
166
207
  1) `## Structural Findings (fallow)` — only if structural findings provided; normalized items first.
167
208
  2) `## Narrative Findings (AI reviewer)` — your adversarial findings, including any external claim independently verified (`(external: {slug})`).
@@ -61,6 +61,8 @@ This ensures project-specific patterns, conventions, and best practices are appl
61
61
 
62
62
  **3. Code Quality** — Dead code, unused imports/variables, poor naming conventions, missing error handling, inconsistent patterns, overly complex functions (high cyclomatic complexity), code duplication, magic numbers, commented-out code
63
63
 
64
+ **4. Necessity** (#4100) — reachable code whose guarded-against problem cannot occur in this stack: impossible-state guard, already-defended-elsewhere, cargo-culted pattern, phantom requirement, generality without instantiation. At `standard`/`deep`, Read `~/.claude/gsd-core/references/necessity-review.md` before classifying findings; if it cannot be read, say so in the Summary and omit `necessity:`. `quick` does not assess necessity. Warning when provable from the repo, Info when it rests on judgment, Never Critical; each finding cites the evidence that the state cannot occur and carries a `**Category:** necessity` line.
65
+
64
66
  **Out of Scope (v1):** Performance issues (O(n²) algorithms, memory leaks, inefficient queries) are NOT in scope for v1. Focus on correctness, security, and maintainability.
65
67
 
66
68
  </review_scope>
@@ -71,12 +73,17 @@ This ensures project-specific patterns, conventions, and best practices are appl
71
73
 
72
74
  **quick** — Pattern-matching only. Use grep/regex to scan for common anti-patterns without reading full file contents. Target: under 2 minutes.
73
75
 
74
- Patterns checked:
75
- - Hardcoded secrets: `(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['"][^'"]+['"]`
76
- - Dangerous functions: `eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru`
77
- - Debug artifacts: `console\.log|debugger;|TODO|FIXME|XXX|HACK`
78
- - Empty catch blocks: `catch\s*\([^)]*\)\s*\{\s*\}`
79
- - Commented-out code: `^\s*//.*[{};]|^\s*#.*:|^\s*/\*`
76
+ Checks (commands in `review_by_depth`):
77
+ - Hardcoded secrets
78
+ - Dangerous functions
79
+ - Debug artifacts
80
+ - Empty catch blocks
81
+ - Commented-out code
82
+ - Unpinned actions
83
+ - Non-idempotent install
84
+ - Missing job timeout
85
+
86
+ The last three: GitHub Actions workflows only.
80
87
 
81
88
  **standard** (default) — Read each changed file. Check for bugs, security issues, and quality problems in context. Cross-reference imports and exports. Target: 5-15 minutes.
82
89
 
@@ -86,6 +93,7 @@ Language-aware checks:
86
93
  - **Go**: Unchecked error returns, goroutine leaks, context not passed, `defer` in loops, race conditions
87
94
  - **C/C++**: Buffer overflow patterns, use-after-free indicators, null pointer dereferences, missing bounds checks, memory leaks
88
95
  - **Shell**: Unquoted variables, `eval` usage, missing `set -e`, command injection via interpolation
96
+ - **CI/CD pipelines** (by path: `.github/workflows/*.yml`, `Dockerfile*`, `docker-compose*.yml`, deploy scripts): Read `~/.claude/gsd-core/references/cicd-review-patterns.md`; if it cannot be read, say so in the Summary and check: non-idempotent steps (`npm install` vs `npm ci`, unlocked `pip install`, unguarded `mkdir`/`rm`), no job `timeout-minutes`, deploy without `concurrency` + `cancel-in-progress`, per-environment rebuild (not build-once), no health check or rollback, load-bearing `continue-on-error`, static credentials where OIDC works, unpinned actions
89
97
 
90
98
  **deep** — All of standard, plus cross-file analysis. Trace function call chains across imports. Target: 15-30 minutes.
91
99
 
@@ -174,6 +182,7 @@ NOTE: Do NOT exclude all `.md` files — commands, workflows, and agents are sou
174
182
  - Go: `.go`
175
183
  - C/C++: `.c`, `.cpp`, `.h`, `.hpp`
176
184
  - Shell: `.sh`, `.bash`
185
+ - CI/CD (by path): `.github/workflows/*.yml`/`*.yaml`, `Dockerfile*`, `docker-compose*.yml`, `deploy*.sh` (also Shell)
177
186
  - Other: Review generically
178
187
 
179
188
  **3. Exit early if empty:** If no source files remain after filtering, create REVIEW.md with:
@@ -194,22 +203,48 @@ NOTE: `status: clean` means "reviewed and found no issues." `status: skipped` me
194
203
  Branch on depth level:
195
204
 
196
205
  **For depth=quick:**
197
- Run grep patterns (from `<depth_levels>` quick section) against all files:
206
+ Run every check from `<depth_levels>` quick section:
198
207
  ```bash
208
+ # Set FILE per scoped path with: while IFS= read -r FILE (never paste a name into code).
209
+
199
210
  # Hardcoded secrets
200
- grep -n -E "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"]\w+['\"]" file
211
+ grep -n -E -- "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"][^'\"]+['\"]" "$FILE"
201
212
 
202
213
  # Dangerous functions
203
- grep -n -E "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec" file
214
+ grep -n -E -- "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru" "$FILE"
204
215
 
205
216
  # Debug artifacts
206
- grep -n -E "console\.log|debugger;|TODO|FIXME|XXX|HACK" file
207
-
208
- # Empty catch
209
- grep -n -E "catch\s*\([^)]*\)\s*\{\s*\}" file
217
+ grep -n -E -- "console\.log|debugger;|TODO|FIXME|XXX|HACK" "$FILE"
218
+
219
+ # Empty catch blocks
220
+ grep -n -E -- "catch\s*\([^)]*\)\s*\{\s*\}" "$FILE"
221
+
222
+ # Commented-out code
223
+ grep -n -E -- "^\s*//.*[{};]|^\s*#[[:space:]]*(if|for|while|def|class|elif|else|try|except|with)[[:space:](].*:[[:space:]]*$|^\s*/\*[^*].*[{};]" "$FILE"
224
+
225
+ # Unpinned actions
226
+ grep -n -E -- "^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]*['\"]?[^[:space:]'\"]+@(main|master|latest)['\"]?([[:space:]]|$)" "$FILE"
227
+
228
+ # Non-idempotent install
229
+ [ -f package-lock.json ] && grep -n -E -- "(^|[^[:alnum:]_-])npm[[:space:]]+(install|i)([[:space:]]|$)" "$FILE" | grep -v -E "^[0-9]+:[[:space:]]*#|[[:space:]](-g|--global)([[:space:]]|$)"
230
+
231
+ # Missing job timeout
232
+ awk '
233
+ function flush() { if (job != "" && runs && !timed) print line ": " job ": no job-level timeout-minutes"; job = "" }
234
+ { sub(/\r$/, "") }
235
+ /^[^[:space:]#]/ { flush(); injobs = ($0 ~ /^jobs:[[:space:]]*(#.*)?$/); jind = -1; next }
236
+ !injobs || /^[[:space:]]*(#.*)?$/ { next }
237
+ { ind = match($0, /[^ ]/) - 1 }
238
+ jind < 0 { jind = ind }
239
+ ind == jind { flush(); job = $0; sub(/^ +/, "", job); sub(/:.*/, "", job); line = NR; kind = -1; runs = 0; timed = 0; next }
240
+ kind < 0 { kind = ind }
241
+ ind == kind && /^ *runs-on:/ { runs = 1 }
242
+ ind == kind && /^ *timeout-minutes:/ { timed = 1 }
243
+ END { flush() }
244
+ ' "$FILE"
210
245
  ```
211
246
 
212
- Record findings with severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning
247
+ Record findings with severity: secrets/dangerous=Critical, debug and commented-out code=Info, empty catch and the three CI/CD checks=Warning
213
248
 
214
249
  **For depth=standard:**
215
250
  For each file:
@@ -253,6 +288,7 @@ For each finding, assign severity:
253
288
  - Type coercion issues (`==` vs `===`)
254
289
  - Unhandled promise rejections
255
290
  - Dead code paths that indicate logic errors
291
+ - CI/CD pipeline findings: every check in the CI/CD entry
256
292
 
257
293
  **Info** — Style issues, naming improvements, dead code, unused imports, suggestions:
258
294
  - Unused imports/variables
@@ -287,10 +323,13 @@ findings:
287
323
  warning: N
288
324
  info: N
289
325
  total: N
326
+ necessity: N
290
327
  status: clean | issues_found
291
328
  ---
292
329
  ```
293
330
 
331
+ Emit `necessity:` only at standard/deep depth. It counts the necessity findings among `warning` + `info` and is never added to `total`.
332
+
294
333
  **3. Body sections (required order):**
295
334
  1) `## Structural Findings (fallow)` — only when structural findings were provided; list normalized items first.
296
335
  2) `## Narrative Findings (AI reviewer)` — your adversarial findings from direct code review, including any external-reviewer claim you independently verified (`(external: {slug})`, see `load_context` step 5).
@@ -766,7 +766,14 @@ Do NOT skip. Do NOT proceed to state updates if self-check fails.
766
766
  </self_check>
767
767
 
768
768
  <state_updates>
769
- After SUMMARY.md, update STATE.md using `gsd-tools query` state handlers (named flags):
769
+ After SUMMARY.md, update ROADMAP.md progress FIRST, then STATE.md. The ROADMAP write is the step that can decline (`missing_phase_details`), and a decline stops the plan before any STATE.md write, so STATE.md never advances past a plan the ROADMAP did not record:
770
+
771
+ ```bash
772
+ # Update ROADMAP.md progress for this phase (plan counts, status). A decline stops here, before STATE.md moves.
773
+ PROGRESS=$(gsd_run query roadmap.update-plan-progress "${PHASE_NUMBER}") && REASON=$(jq -er '.reason//""' <<<"$PROGRESS") && [ "$REASON" != missing_phase_details ] || { echo "ERROR: ROADMAP update-plan-progress did not complete for ${PHASE_NUMBER}-${PLAN} (missing_phase_details decline, failed query, or jq not installed). STOP: skip this plan's completion steps." >&2; exit 1; }
774
+ ```
775
+
776
+ Then update STATE.md using `gsd-tools query` state handlers (named flags):
770
777
 
771
778
  ```bash
772
779
  # Advance plan counter (handles edge cases automatically)
@@ -793,9 +800,6 @@ gsd_run query state.record-session \
793
800
  ```
794
801
 
795
802
  ```bash
796
- # Update ROADMAP.md progress for this phase (plan counts, status)
797
- gsd_run query roadmap.update-plan-progress "${PHASE_NUMBER}"
798
-
799
803
  # Mark completed requirements from PLAN.md frontmatter
800
804
  # Extract the `requirements` array from the plan's frontmatter, then mark each complete
801
805
  gsd_run query requirements.mark-complete ${REQ_IDS}
package/bin/install.js CHANGED
@@ -2659,7 +2659,15 @@ function convertSlashCommandsToTraeSkillMentions(content) {
2659
2659
  });
2660
2660
  }
2661
2661
 
2662
- function convertClaudeToTraeMarkdown(content) {
2662
+ function convertClaudeToTraeMarkdown(content, pathPrefix) {
2663
+ return runtimeArtifactConversion._shieldEmittedPrefix(
2664
+ runtimeArtifactConversion._resolveTraeInstructionFileAtPrefix(content, pathPrefix),
2665
+ pathPrefix,
2666
+ convertClaudeToTraeMarkdownBody,
2667
+ );
2668
+ }
2669
+
2670
+ function convertClaudeToTraeMarkdownBody(content) {
2663
2671
  let converted = convertSlashCommandsToTraeSkillMentions(filterRuntimeNotesForTarget(content, 'trae'));
2664
2672
  converted = converted.replace(/\bBash\(/g, 'Shell(');
2665
2673
  converted = converted.replace(/\bEdit\(/g, 'StrReplace(');
@@ -2719,9 +2727,10 @@ function convertClaudeToTraeMarkdown(content) {
2719
2727
  converted = converted.replace(/\.\/\.claude\//g, './.trae/');
2720
2728
  converted = converted.replace(/\.claude\//g, '.trae/');
2721
2729
  // Bare forms (no trailing slash) — after slash forms to avoid double-rewrite.
2722
- // Use negative lookahead (?![\w-]) to preserve .claude-plugin and .claudeignore.
2723
- converted = converted.replace(/~\/\.claude(?![\w-])/g, '~/.trae');
2724
- converted = converted.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.trae');
2730
+ // Use negative lookahead (?![\w-]|\.\w) to preserve .claude-plugin, .claudeignore and .claude.json.
2731
+ // #5715: an emitted prefix is shielded by convertClaudeToTraeMarkdown; the skills path runs this before the prefix pass, and the profile maps the own-dir forms.
2732
+ converted = converted.replace(/~\/\.claude(?![\w-]|\.\w)/g, '~/.trae');
2733
+ converted = converted.replace(/\$HOME\/\.claude(?![\w-]|\.\w)/g, () => '$HOME/.trae');
2725
2734
  // Environment variable name rewrite
2726
2735
  converted = converted.replace(/\bCLAUDE_CONFIG_DIR\b/g, 'TRAE_CONFIG_DIR');
2727
2736
  converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
@@ -2765,8 +2774,8 @@ function convertClaudeCommandToTraeSkill(content, skillName) {
2765
2774
  return `${fm}\n${body}`;
2766
2775
  }
2767
2776
 
2768
- function convertClaudeAgentToTraeAgent(content) {
2769
- let converted = convertClaudeToTraeMarkdown(content);
2777
+ function convertClaudeAgentToTraeAgent(content, _isGlobal, meta) {
2778
+ let converted = convertClaudeToTraeMarkdown(content, meta && meta.pathPrefix);
2770
2779
 
2771
2780
  const { frontmatter, body } = extractFrontmatterAndBody(converted);
2772
2781
  if (!frontmatter) return converted;
@@ -7279,14 +7288,16 @@ function installCodexConfig(targetDir, agentsSrc, sandboxTier = 'codex-agent-san
7279
7288
  content = composeWorkflow(content, { sourcePath: agentTomlSourcePath });
7280
7289
  // Replace full .claude/gsd-core prefix so path resolves to the Codex
7281
7290
  // GSD install before generic .claude → .codex conversion rewrites it.
7282
- content = content.replace(/~\/\.claude\/gsd-core\//g, codexGsdPath);
7283
- content = content.replace(/\$HOME\/\.claude\/gsd-core\//g, codexGsdPath);
7291
+ // #5715: one pass, function replacement: a string replacement would expand `$&` in a
7292
+ // config dir, and a second pass would rescan the path the first emitted.
7293
+ content = content.replace(/(?:~|\$HOME)\/\.claude\/gsd-core\//g, () => codexGsdPath);
7284
7294
  // Route TOML emit through the same full Claude→Codex conversion pipeline
7285
7295
  // used on the `.md` emit path (#2639). Covers: slash-command rewrites,
7286
7296
  // $ARGUMENTS → {{GSD_ARGS}}, /clear removal, anchored and bare .claude/
7287
7297
  // paths, .claudeignore → .codexignore, and standalone "Claude" /
7288
7298
  // CLAUDE.md neutralization via neutralizeAgentReferences(..., 'AGENTS.md').
7289
- content = convertClaudeToCodexMarkdown(content);
7299
+ // #5715: the converter runs after the path above was emitted; shield it from the converter.
7300
+ content = runtimeArtifactConversion._shieldEmittedPrefix(content, codexGsdPath, convertClaudeToCodexMarkdown);
7290
7301
  const { frontmatter } = extractFrontmatterAndBody(content);
7291
7302
  // #3897 security review F1 (blocker, post-merge): this loop used to key
7292
7303
  // the sandbox/hold decision off ONLY the filename stem while the emitted
@@ -7949,7 +7960,9 @@ const RUNTIME_CONTENT_DISPATCH = {
7949
7960
  },
7950
7961
  },
7951
7962
  trae: {
7952
- md: (content) => convertClaudeToTraeMarkdown(content),
7963
+ // #5715: the Trae converter shields (and resolves) the emitted prefix itself.
7964
+ mdShieldsPrefix: true,
7965
+ md: (content, ctx) => convertClaudeToTraeMarkdown(content, ctx && ctx.pathPrefix),
7953
7966
  js: (content) => {
7954
7967
  content = content.replace(/\/gsd:([a-z0-9-]+)/g, (_, commandName) => {
7955
7968
  return `/gsd-${commandName}`;
@@ -8152,33 +8165,8 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
8152
8165
  // balanced masking grammar used by this path and the rewrite engine.
8153
8166
  const rewriteGenericPaths = (body) => {
8154
8167
  content = body;
8155
- const globalClaudeRegex = /~\/\.claude\//g;
8156
- const globalClaudeHomeRegex = /\$HOME\/\.claude\//g;
8157
- const localClaudeRegex = /\.\/\.claude\//g;
8158
- content = content.replace(globalClaudeRegex, pathPrefix);
8159
- content = content.replace(globalClaudeHomeRegex, pathPrefix);
8160
- content = content.replace(localClaudeRegex, `./${dirName}/`);
8161
- // #3544 review (Finding 1 fallout): guarded with the SAME
8162
- // negative-lookahead convention already used at ~:2859-2860 below
8163
- // ("preserve .claude-plugin and .claudeignore"). A naive `\b` here
8164
- // is satisfied by ANY non-word character, including '-' — so for a
8165
- // --config-dir whose name EXTENDS '.claude' (e.g. '.claude-work',
8166
- // pathPrefix '$HOME/.claude-work/'), this pass re-matched the
8167
- // '$HOME/.claude' PREFIX of its own slash-form output (lines above)
8168
- // and re-appended the full prefix, corrupting every emitted path to
8169
- // '$HOME/.claude-work-work/...'. Harmless no-op for the literal
8170
- // default '.claude' (self-replace with an identical string), which
8171
- // is why this went undetected until a non-default config-dir name
8172
- // was exercised.
8173
- content = content.replace(/~\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
8174
- content = content.replace(/\$HOME\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
8175
- content = content.replace(/\.\/\.claude\b/g, `./${dirName}`);
8176
- content = content.replace(/~\/\.qwen\//g, pathPrefix);
8177
- content = content.replace(/\$HOME\/\.qwen\//g, pathPrefix);
8178
- content = content.replace(/\.\/\.qwen\//g, `./${dirName}/`);
8179
- content = content.replace(/~\/\.hermes\//g, pathPrefix);
8180
- content = content.replace(/\$HOME\/\.hermes\//g, pathPrefix);
8181
- content = content.replace(/\.\/\.hermes\//g, `./${dirName}/`);
8168
+ // #5715: every generic source form in ONE pass (no rescan of the emitted prefix).
8169
+ content = runtimeArtifactConversion._rewriteSpecTreeConfigDirForms(content, pathPrefix, dirName);
8182
8170
  // #3544: restore @-file-reference lines to the tilde form Claude Code
8183
8171
  // actually expands — the SAME correction #3133 already applies to
8184
8172
  // skill/command bodies via _applyRuntimeRewrites's 'claude' case (see
@@ -8215,7 +8203,15 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
8215
8203
  content = normalizeAgentBodyForRuntime(content, runtime, readGsdCommandNames());
8216
8204
 
8217
8205
  // Apply per-runtime .md converter (if any)
8218
- if (dispatch.md) content = dispatch.md(content, ctx);
8206
+ // #5715: the generic pass above emitted `pathPrefix`; shield it from the converter
8207
+ // (a converter that skips the generic pass emitted nothing to shield).
8208
+ if (dispatch.md) {
8209
+ content = runtimeArtifactConversion._shieldEmittedPrefix(
8210
+ content,
8211
+ dispatch.mdSkipGenericRewrite || dispatch.mdShieldsPrefix ? undefined : pathPrefix,
8212
+ (shielded) => dispatch.md(shielded, ctx),
8213
+ );
8214
+ }
8219
8215
 
8220
8216
  // Re-run attribution after converter for runtimes that need it (copilot, antigravity)
8221
8217
  if (dispatch.mdReattributeAfter) content = processAttribution(content, getCommitAttribution(runtime));
@@ -10087,8 +10083,8 @@ const MANIFEST_NAME = 'gsd-file-manifest.json';
10087
10083
  * Compute SHA256 hash of file contents
10088
10084
  */
10089
10085
  function fileHash(filePath) {
10090
- const content = fs.readFileSync(filePath);
10091
- return crypto.createHash('sha256').update(content).digest('hex');
10086
+ // #5683: single owner of the per-file hash, shared with `effort sync`.
10087
+ return require('../gsd-core/bin/lib/install-manifest-record.cjs').fileHash(filePath);
10092
10088
  }
10093
10089
 
10094
10090
  /**
@@ -9,7 +9,7 @@ allowed-tools:
9
9
  - Grep
10
10
  - Agent
11
11
  - AskUserQuestion
12
- requires: [new-project, phase, plan-phase]
12
+ requires: [new-project, phase, plan-phase, discuss-phase, execute-phase]
13
13
  ---
14
14
 
15
15
  <arguments>$ARGUMENTS</arguments>
@@ -28,7 +28,7 @@ Brownfield equivalent of new-project. Project exists, PROJECT.md has history. Ga
28
28
  - `.planning/ROADMAP.md` — phase structure (continues numbering)
29
29
  - `.planning/STATE.md` — reset for new milestone
30
30
 
31
- **After:** `/gsd:plan-phase [N]` to start execution.
31
+ **After:** Run `/gsd:discuss-phase [N]` to clarify the approach (or `/gsd:plan-phase [N]` to plan directly); then `/gsd:execute-phase [N]` to execute.
32
32
  </objective>
33
33
 
34
34
  <execution_context>
@@ -9,7 +9,7 @@ allowed-tools:
9
9
  - Write
10
10
  - Agent
11
11
  - AskUserQuestion
12
- requires: [config, phase, plan-phase]
12
+ requires: [config, phase, plan-phase, discuss-phase, execute-phase]
13
13
  ---
14
14
 
15
15
  <arguments>$ARGUMENTS</arguments>
@@ -36,7 +36,7 @@ Initialize a new project through unified flow: questioning → research (optiona
36
36
  - `.planning/ROADMAP.md` — phase structure
37
37
  - `.planning/STATE.md` — project memory
38
38
 
39
- **After this command:** Run `/gsd:plan-phase 1` to start execution.
39
+ **After this command:** Run `/gsd:discuss-phase 1` to clarify the approach (or `/gsd:plan-phase 1` to plan directly); then `/gsd:execute-phase 1` to execute.
40
40
  </objective>
41
41
 
42
42
  <execution_context>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gsd:update
3
3
  description: Update GSD to latest version with changelog display
4
- argument-hint: "[--sync | --reapply | --next | --rc]"
4
+ argument-hint: "[--sync | --reapply | --next | --rc] [--interactive=exceptions|all]"
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Write
@@ -35,14 +35,17 @@ Routes to the update workflow which handles:
35
35
  <flags>
36
36
  - **--sync**: Sync managed GSD skills across runtime roots so multi-runtime users stay aligned after an update. Runs the sync-skills workflow (--from, --to, --dry-run, --apply flags supported).
37
37
  - **--reapply**: Reapply local modifications after a GSD update. Uses three-way comparison (pristine baseline, user-modified backup, newly installed version) to merge user customizations back. Runs the reapply-patches workflow.
38
+ - **--interactive[=exceptions|all]** (with `--reapply` only): show each user edit next to the stock text with a recommended disposition and wait for a keep / retire / rewrite decision per edit. `all` shows every edit; `exceptions` (bare `--interactive`) shows only edits upstream adopted, partly adopted, reworded around, or orphaned, and keeps the rest. Without it, `--reapply` behaves exactly as before.
38
39
  - **--next** (alias **--rc**): Target the `@next` RC dist-tag instead of `@latest` so you can install or refresh a release candidate (e.g. `1.4.0-rc.1`) through the normal update flow — scope/runtime detection, changelog preview, custom-file backup, and cache clearing all still apply. Omitting it keeps targeting `@latest` (no change). See ADR #660 for the RC channel.
39
40
  - **(no flag)**: Standard update — check for new version, show changelog, install.
40
41
  </flags>
41
42
 
42
43
  <process>
44
+ If the arguments contain `--interactive` (bare or with a value) but not `--reapply`, STOP with: `--interactive applies only to --reapply (for example: /gsd:update --reapply --interactive=all)`. Do nothing else.
45
+
43
46
  Parse the first token of the `<arguments>` block:
44
47
  - If it is `--sync`: strip the flag, execute the sync-skills workflow (passing remaining args for --from/--to/--dry-run/--apply).
45
- - If it is `--reapply`: strip the flag, execute the reapply-patches workflow.
48
+ - If it is `--reapply`: strip the flag, execute the reapply-patches workflow, passing the remaining arguments (for example `--interactive=all`) to it.
46
49
  - Otherwise (including `--next` / `--rc`): execute the update workflow end-to-end, passing the `<arguments>` block through so the workflow's parse_update_channel step can select the release channel.
47
50
 
48
51
  </process>