@opengsd/gsd-core 1.9.1 → 1.10.0

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 (219) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +27 -3
  5. package/agents/gsd-debugger.md +12 -246
  6. package/agents/gsd-executor.md +7 -5
  7. package/agents/gsd-integration-checker.md +3 -0
  8. package/agents/gsd-plan-checker.md +9 -0
  9. package/agents/gsd-planner.md +5 -8
  10. package/agents/gsd-roadmapper.md +21 -3
  11. package/agents/gsd-verifier.md +14 -70
  12. package/bin/install.js +453 -289
  13. package/commands/gsd/mempalace-capture.md +1 -1
  14. package/commands/gsd/new-milestone.md +1 -1
  15. package/commands/gsd/plan-phase.md +1 -1
  16. package/gsd-core/bin/gsd-tools.cjs +579 -66
  17. package/gsd-core/bin/lib/active-workstream-store.cjs +25 -0
  18. package/gsd-core/bin/lib/agent-install-check.cjs +38 -6
  19. package/gsd-core/bin/lib/api-coverage.cjs +120 -0
  20. package/gsd-core/bin/lib/audit.cjs +89 -1
  21. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  22. package/gsd-core/bin/lib/capability-registry.cjs +96 -110
  23. package/gsd-core/bin/lib/capability-validator.cjs +12 -2
  24. package/gsd-core/bin/lib/check-command-router.cjs +43 -1
  25. package/gsd-core/bin/lib/command-aliases.cjs +72 -0
  26. package/gsd-core/bin/lib/commands.cjs +26 -25
  27. package/gsd-core/bin/lib/commonjs-marker.cjs +136 -0
  28. package/gsd-core/bin/lib/config-loader.cjs +1 -0
  29. package/gsd-core/bin/lib/config.cjs +12 -1
  30. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  31. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  32. package/gsd-core/bin/lib/core-utils.cjs +91 -12
  33. package/gsd-core/bin/lib/docs.cjs +3 -2
  34. package/gsd-core/bin/lib/external-job.cjs +19 -4
  35. package/gsd-core/bin/lib/frontmatter.cjs +84 -12
  36. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  37. package/gsd-core/bin/lib/git-base-branch.cjs +58 -15
  38. package/gsd-core/bin/lib/graphify.cjs +142 -27
  39. package/gsd-core/bin/lib/gsd2-import.cjs +27 -4
  40. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  41. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  42. package/gsd-core/bin/lib/init.cjs +1021 -57
  43. package/gsd-core/bin/lib/install-engine.cjs +64 -10
  44. package/gsd-core/bin/lib/install-profiles.cjs +27 -1
  45. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  46. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  47. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  48. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  49. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  50. package/gsd-core/bin/lib/installer-migrations.cjs +87 -1
  51. package/gsd-core/bin/lib/io.cjs +28 -3
  52. package/gsd-core/bin/lib/markdown-sectionizer.cjs +6 -0
  53. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  54. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  55. package/gsd-core/bin/lib/milestone.cjs +75 -47
  56. package/gsd-core/bin/lib/phase-id.cjs +63 -0
  57. package/gsd-core/bin/lib/phase-locator.cjs +138 -45
  58. package/gsd-core/bin/lib/phase.cjs +260 -28
  59. package/gsd-core/bin/lib/plan-dependency-graph.cjs +232 -0
  60. package/gsd-core/bin/lib/planning-workspace.cjs +4 -0
  61. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  62. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +80 -0
  63. package/gsd-core/bin/lib/review-lane-descriptor.cjs +99 -0
  64. package/gsd-core/bin/lib/review-lane-runner.cjs +30 -6
  65. package/gsd-core/bin/lib/roadmap-command-router.cjs +42 -9
  66. package/gsd-core/bin/lib/roadmap-parser.cjs +100 -18
  67. package/gsd-core/bin/lib/roadmap.cjs +37 -7
  68. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +195 -62
  69. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +15 -3
  70. package/gsd-core/bin/lib/runtime-homes.cjs +154 -41
  71. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +105 -41
  72. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  73. package/gsd-core/bin/lib/shell-command-projection.cjs +113 -27
  74. package/gsd-core/bin/lib/smart-entry.cjs +12 -0
  75. package/gsd-core/bin/lib/state-transition.cjs +73 -8
  76. package/gsd-core/bin/lib/state.cjs +151 -62
  77. package/gsd-core/bin/lib/surface.cjs +12 -1
  78. package/gsd-core/bin/lib/uat-predicate.cjs +11 -1
  79. package/gsd-core/bin/lib/uat.cjs +320 -21
  80. package/gsd-core/bin/lib/unusable-input.cjs +9 -0
  81. package/gsd-core/bin/lib/verification.cjs +29 -12
  82. package/gsd-core/bin/lib/verify.cjs +10 -2
  83. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  84. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +181 -18
  85. package/gsd-core/bin/lib/workstream-inventory.cjs +519 -27
  86. package/gsd-core/bin/lib/workstream.cjs +6 -0
  87. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  88. package/gsd-core/bin/lib/worktree-safety.cjs +276 -118
  89. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  90. package/gsd-core/references/artifact-types.md +10 -3
  91. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  92. package/gsd-core/references/debugger-techniques.md +255 -0
  93. package/gsd-core/references/research-documentation-lookup.md +5 -3
  94. package/gsd-core/references/specless-probe-fallback.md +7 -6
  95. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  96. package/gsd-core/references/worktree-branch-check.md +2 -2
  97. package/gsd-core/templates/summary-complex.md +2 -0
  98. package/gsd-core/templates/summary-minimal.md +2 -0
  99. package/gsd-core/templates/summary-standard.md +2 -0
  100. package/gsd-core/templates/summary.md +2 -0
  101. package/gsd-core/workflows/audit-milestone.md +3 -0
  102. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  103. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  104. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  105. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  106. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  107. package/gsd-core/workflows/autonomous.md +32 -69
  108. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  109. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +83 -0
  110. package/gsd-core/workflows/code-review.md +42 -160
  111. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  112. package/gsd-core/workflows/complete-milestone.md +23 -81
  113. package/gsd-core/workflows/debug.md +9 -12
  114. package/gsd-core/workflows/diagnose-issues.md +22 -0
  115. package/gsd-core/workflows/discovery-phase.md +4 -4
  116. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  117. package/gsd-core/workflows/discuss-phase-assumptions.md +5 -16
  118. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  119. package/gsd-core/workflows/docs-update.md +8 -51
  120. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +34 -2
  121. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  122. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  123. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +19 -0
  124. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  125. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  126. package/gsd-core/workflows/execute-phase.md +65 -137
  127. package/gsd-core/workflows/execute-plan.md +1 -1
  128. package/gsd-core/workflows/help/modes/full.md +6 -1
  129. package/gsd-core/workflows/ingest-docs.md +2 -1
  130. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  131. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  132. package/gsd-core/workflows/new-milestone.md +21 -38
  133. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  134. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  135. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  136. package/gsd-core/workflows/new-project.md +13 -226
  137. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  138. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  139. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  140. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  141. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  142. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  143. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  144. package/gsd-core/workflows/plan-phase.md +49 -193
  145. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  146. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  147. package/gsd-core/workflows/progress.md +11 -153
  148. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  149. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  150. package/gsd-core/workflows/quick/steps/quick-verification.md +46 -0
  151. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  152. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  153. package/gsd-core/workflows/quick.md +20 -390
  154. package/gsd-core/workflows/resume-project.md +3 -0
  155. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  156. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  157. package/gsd-core/workflows/review.md +15 -8
  158. package/gsd-core/workflows/section-manifest.json +219 -0
  159. package/gsd-core/workflows/sketch.md +1 -1
  160. package/gsd-core/workflows/spec-phase.md +17 -14
  161. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  162. package/gsd-core/workflows/spike.md +50 -16
  163. package/gsd-core/workflows/sync-skills.md +49 -11
  164. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  165. package/gsd-core/workflows/transition.md +8 -21
  166. package/gsd-core/workflows/ui-phase.md +8 -7
  167. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  168. package/gsd-core/workflows/update.md +18 -7
  169. package/gsd-core/workflows/verify-phase.md +4 -7
  170. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  171. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  172. package/gsd-core/workflows/verify-work.md +8 -58
  173. package/hooks/dist/gsd-agent-isolation-guard.js +428 -0
  174. package/hooks/dist/gsd-check-update-worker.js +14 -5
  175. package/hooks/dist/gsd-cursor-subagent-start.js +532 -26
  176. package/hooks/dist/gsd-read-injection-scanner.js +7 -0
  177. package/hooks/dist/gsd-statusline.js +72 -6
  178. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  179. package/hooks/dist/gsd-write-guard.js +359 -0
  180. package/hooks/dist/lib/isolation-sentinel.js +268 -0
  181. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  182. package/hooks/gsd-agent-isolation-guard.js +428 -0
  183. package/hooks/gsd-check-update-worker.js +14 -5
  184. package/hooks/gsd-cursor-subagent-start.js +532 -26
  185. package/hooks/gsd-read-injection-scanner.js +7 -0
  186. package/hooks/gsd-statusline.js +72 -6
  187. package/hooks/gsd-worktree-path-guard.js +2 -1
  188. package/hooks/gsd-write-guard.js +359 -0
  189. package/hooks/hooks.json +12 -0
  190. package/hooks/lib/isolation-sentinel.js +268 -0
  191. package/hooks/managed-hooks-registry.cjs +2 -0
  192. package/package.json +14 -5
  193. package/pi/gsd.cjs +57 -12
  194. package/scripts/build-hooks.js +9 -0
  195. package/scripts/changeset/lint.cjs +9 -2
  196. package/scripts/changeset/serialize.cjs +5 -1
  197. package/scripts/gen-capability-matrix.cjs +1 -1
  198. package/scripts/gen-context-index.cjs +448 -0
  199. package/scripts/gen-inventory-manifest.cjs +101 -1
  200. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  201. package/scripts/gen-section-manifest.cjs +638 -0
  202. package/scripts/generate-package-identity.cjs +4 -2
  203. package/scripts/lint-allow-test-rule-refs.allowlist.json +17 -31
  204. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  205. package/scripts/lint-docs-command-form.cjs +195 -0
  206. package/scripts/lint-docs-required.cjs +9 -1
  207. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  208. package/scripts/lint-example-parser-parity.cjs +395 -0
  209. package/scripts/lint-test-file-count.allowlist.json +27 -1
  210. package/scripts/mutation-matrix.cjs +13 -0
  211. package/scripts/prompt-injection-scan.sh +27 -6
  212. package/scripts/run-tests.cjs +3 -2
  213. package/skills/gsd-autonomous/SKILL.md +1 -1
  214. package/skills/gsd-execute-phase/SKILL.md +1 -1
  215. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  216. package/skills/gsd-new-milestone/SKILL.md +1 -1
  217. package/skills/gsd-plan-phase/SKILL.md +2 -2
  218. package/vscode/package.json +1 -1
  219. package/scripts/gen-emitted-baseline.cjs +0 -145
@@ -44,6 +44,15 @@ PLAN_STRATEGY="local"
44
44
  if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-(converge|cross-ai)([[:space:]]|$)'; then
45
45
  PLAN_STRATEGY="converge"
46
46
  fi
47
+
48
+ CONVERGE_PARAM=""
49
+ if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-converge([[:space:]]|$)'; then
50
+ CONVERGE_PARAM="--converge"
51
+ fi
52
+ CROSS_AI_PARAM=""
53
+ if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-cross-ai([[:space:]]|$)'; then
54
+ CROSS_AI_PARAM="--cross-ai"
55
+ fi
47
56
  ```
48
57
 
49
58
  When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
@@ -58,8 +67,12 @@ Bootstrap via milestone-level init:
58
67
  _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
59
68
  INIT=$(gsd_run query init.milestone-op)
60
69
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
70
+ INIT_AUTONOMOUS=$(gsd_run query init.autonomous $CONVERGE_PARAM $CROSS_AI_PARAM)
71
+ if [[ "$INIT_AUTONOMOUS" == @file:* ]]; then INIT_AUTONOMOUS=$(cat "${INIT_AUTONOMOUS#@file:}"); fi
61
72
  ```
62
73
 
74
+ Extract `section_manifest` from `INIT_AUTONOMOUS` (used by the `converge-*` sections below and in step 3).
75
+
63
76
  If `PLAN_STRATEGY` is `converge`, fail fast unless the existing convergence feature gate is enabled:
64
77
 
65
78
  ```bash
@@ -79,23 +92,12 @@ if echo "$ARGUMENTS" | grep -qE '\-\-max-cycles\s+[0-9]+'; then
79
92
  MAX_CYCLES_ARG=$(echo "$ARGUMENTS" | grep -oE '\-\-max-cycles\s+[0-9]+' | awk '{print $2}')
80
93
  CONVERGENCE_ARGS="${CONVERGENCE_ARGS} --max-cycles ${MAX_CYCLES_ARG}"
81
94
  fi
82
-
83
- if [ "$PLAN_STRATEGY" = "converge" ]; then
84
- CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
85
- if [ "$CONVERGENCE_ENABLED" != "true" ]; then
86
- printf '%s\n' \
87
- 'gsd-autonomous --converge is disabled (workflow.plan_review_convergence=false).' \
88
- '' \
89
- 'Enable plan convergence with:' \
90
- '' \
91
- ' gsd config-set workflow.plan_review_convergence true' \
92
- '' \
93
- 'Then re-run the autonomous command with --converge.'
94
- exit 1
95
- fi
96
- fi
97
95
  ```
98
96
 
97
+ <!-- gsd:section id="converge-fail-fast" when="state:plan-strategy-converge" -->
98
+ If `section_manifest` is `null` or `"converge-fail-fast"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-fail-fast.md`. Otherwise skip — do not read the file.
99
+ <!-- /gsd:section -->
100
+
99
101
  Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`.
100
102
 
101
103
  **If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd:new-milestone` first."
@@ -116,7 +118,9 @@ If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
116
118
  Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
117
119
  If `TO_PHASE` is set, display: `Stopping after phase ${TO_PHASE}`
118
120
  If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute inline — background on Codex only)`
119
- If `PLAN_STRATEGY` is `converge`, display: `Planning: Plan-review convergence enabled`
121
+ <!-- gsd:section id="converge-banner" when="state:plan-strategy-converge" -->
122
+ If `section_manifest` is `null` or `"converge-banner"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-banner.md`. Otherwise skip — do not read the file.
123
+ <!-- /gsd:section -->
120
124
 
121
125
  **Agent skills (delegated agents self-load):** This workflow delegates plan/execute/review via flat `Skill()` invocations rather than resolving `agent_skills` itself. Each consumer agent (`gsd-planner`, `gsd-executor`, `gsd-plan-checker`, `gsd-verifier`, …) self-loads its configured `.planning/config.json` `agent_skills` in its own mandatory init step per `@~/.claude/gsd-core/references/agent-skills-bootstrap.md`. This is the durable path that works on every runtime — including Cursor, where `Skill()`-delegated workflow bash init does not reliably execute. No per-delegation injection is needed here. See open-gsd/gsd-core#1866.
122
126
 
@@ -332,42 +336,11 @@ Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHA
332
336
 
333
337
  **3a.5. UI Design Contract (Frontend Phases)**
334
338
 
335
- Resolve active `plan:pre` hooks:
336
-
337
- ```bash
338
- UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
339
- HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
340
- ```
341
-
342
- Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline). **Compute the active UI step hooks** = entries from `activeHooks` where `kind == "step"` and `ref.skill` is set. **If there are NO active step hooks → skip silently to 3b.** (This covers `workflow.ui_phase=false` — including configurations where only a gate-only entry is present, e.g. `ui_phase=false` + `ui_safety_gate=true` produces `activeHooks=[{kind:"gate"}]`. Autonomous never runs the plan:pre gate — it is always pipeline mode — so a gate-only active set is equivalent to no active step and is silently skipped here. This matches OLD §3a.5 behaviour.)
343
-
344
- (At least one active step hook ⇒ `workflow.ui_phase` is on.) Run the UI-SPEC gate:
345
-
346
- ```bash
347
- GATE=$(gsd_run check ui-plan-gate "${PHASE_NUM}" --raw)
348
- ```
349
-
350
- Read `frontend` and `hasUiSpec` from `GATE` (in-context).
351
-
352
- **If `frontend` is false:** Skip silently to 3b.
353
-
354
- **If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3b.
355
-
356
- **Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
339
+ > Full instructions are in `gsd-core/references/autonomous-ui-design-contract.md`. Read that file now and follow it exactly.
357
340
 
358
- ```
359
- Skill(skill="gsd-${ref.skill}", args="${PHASE_NUM}")
360
- ```
341
+ **Inputs:** `PHASE_NUM`, `PHASE_DIR` from execute_phase. Resolves whether the phase needs a UI-SPEC.md generated before planning via active `plan:pre` step hooks. Always non-blocking — proceeds to 3b regardless of outcome.
361
342
 
362
- (Prepend `gsd-` to `ref.skill` — so `ui-phase` → `gsd-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are silently ignored — autonomous is always pipeline mode, there is no blocking gate here.
363
-
364
- After all step hooks return, re-read:
365
-
366
- ```bash
367
- UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
368
- ```
369
-
370
- **If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3b. NON-BLOCKING.
343
+ Read and execute: `$HOME/.claude/gsd-core/references/autonomous-ui-design-contract.md`
371
344
 
372
345
  **3b. Plan**
373
346
 
@@ -379,15 +352,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
379
352
 
380
353
  - **If `FLATTEN` is `false`:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
381
354
 
382
- - If `PLAN_STRATEGY=converge`, print: `◆ Spawning background plan-convergence loop for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
383
-
384
- ```
385
- Agent(
386
- description="Plan convergence phase ${PHASE_NUM}: ${PHASE_NAME}",
387
- run_in_background=true,
388
- prompt="Run plan convergence for phase ${PHASE_NUM}: Skill(skill=\"gsd-plan-review-convergence\", args=\"${PHASE_NUM} ${CONVERGENCE_ARGS}\")"
389
- )
390
- ```
355
+ <!-- gsd:section id="converge-dispatch-bg" when="state:plan-strategy-converge" -->
356
+ If `section_manifest` is `null` or `"converge-dispatch-bg"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md`. Otherwise skip — do not read the file.
357
+ <!-- /gsd:section -->
391
358
 
392
359
  - Otherwise, print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
393
360
 
@@ -403,11 +370,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
403
370
 
404
371
  - **Otherwise (`FLATTEN` is `true` — run inline):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
405
372
 
406
- - If `PLAN_STRATEGY=converge`:
407
-
408
- ```
409
- Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
410
- ```
373
+ <!-- gsd:section id="converge-dispatch-inline" when="state:plan-strategy-converge" -->
374
+ If `section_manifest` is `null` or `"converge-dispatch-inline"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md`. Otherwise skip — do not read the file.
375
+ <!-- /gsd:section -->
411
376
 
412
377
  - Otherwise (local planning):
413
378
 
@@ -417,11 +382,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
417
382
 
418
383
  **If `INTERACTIVE` is NOT set (default):** Run plan inline.
419
384
 
420
- If `PLAN_STRATEGY=converge`, run the convergence loop:
421
-
422
- ```
423
- Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
424
- ```
385
+ <!-- gsd:section id="converge-loop" when="state:plan-strategy-converge" -->
386
+ If `section_manifest` is `null` or `"converge-loop"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-loop.md`. Otherwise skip — do not read the file.
387
+ <!-- /gsd:section -->
425
388
 
426
389
  If `PLAN_STRATEGY=local`, run the regular planner:
427
390
 
@@ -0,0 +1,39 @@
1
+ <step name="dispatch_fix">
2
+ If the `--fix` flag was passed (`FIX_FLAG=true`), delegate to the `code-review-fix.md` workflow
3
+ to auto-apply findings from the REVIEW.md that was just written (or that already existed).
4
+
5
+ This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
6
+ is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
7
+ workflow handles the missing-review error and exits cleanly.
8
+
9
+ ```bash
10
+ if [ "$FIX_FLAG" = "true" ]; then
11
+ echo ""
12
+ echo "─────────────────────────────────────────────────────────────────"
13
+ echo " --fix: delegating to code-review-fix.md"
14
+ echo "─────────────────────────────────────────────────────────────────"
15
+ echo ""
16
+
17
+ # Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
18
+ FIX_ARGS="${PHASE_ARG}"
19
+ if [ "$FIX_ALL" = "true" ]; then
20
+ FIX_ARGS="${FIX_ARGS} --all"
21
+ fi
22
+ if [ "$FIX_AUTO" = "true" ]; then
23
+ FIX_ARGS="${FIX_ARGS} --auto"
24
+ fi
25
+
26
+ # Load and execute the code-review-fix workflow.
27
+ # The fix workflow is the canonical implementation for all fix logic:
28
+ # gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
29
+ # and result presentation. Do not duplicate that logic here.
30
+ Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
31
+
32
+ # Exit after fix workflow completes — present_results is for review-only output.
33
+ # The fix workflow has its own present_results step.
34
+ # Exit workflow.
35
+ fi
36
+ ```
37
+
38
+ If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
39
+ </step>
@@ -0,0 +1,83 @@
1
+ When `FALLOW_ENABLED=true`:
2
+
3
+ 1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
4
+ ```bash
5
+ FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
6
+ const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
7
+ const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
8
+ if (resolved) process.stdout.write(resolved);
9
+ ")
10
+ ```
11
+
12
+ 2) If binary is missing, fail with actionable message:
13
+ ```bash
14
+ if [ -z \"$FALLOW_BIN\" ]; then
15
+ echo \"Error: fallow is enabled but no binary was found.\"
16
+ echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
17
+ # Exit workflow
18
+ fi
19
+ ```
20
+
21
+ 3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
22
+ ```bash
23
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
24
+ FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
25
+ FALLOW_STDERR_TMP=$(mktemp)
26
+
27
+ # Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
28
+ # Derive the phase base commit; if none is found, fall back to repo scope (fallow
29
+ # auto-detects the base branch).
30
+ FALLOW_SCOPE_ARGS=()
31
+ if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
32
+ FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
33
+ if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
34
+ FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
35
+ FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
36
+ fi
37
+ fi
38
+
39
+ gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
40
+ FALLOW_EXIT=$?
41
+
42
+ # fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
43
+ # valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
44
+ # Decide success by whether the output parses as a fallow report, not by exit code.
45
+ FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
46
+ try {
47
+ const fs = require('fs');
48
+ const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
49
+ const o = JSON.parse(txt);
50
+ process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
51
+ } catch { process.stdout.write('0'); }
52
+ \")
53
+ if [ \"$FALLOW_OK\" != \"1\" ]; then
54
+ FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
55
+ rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
56
+ # #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
57
+ # but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
58
+ # 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
59
+ # CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
60
+ # not found. A non-zero exit here with a resolved binary means fallow is
61
+ # installed but did not produce a report — surface that loudly so a Windows
62
+ # user does not mistake it for "fallow absent".
63
+ case \"$FALLOW_EXIT\" in
64
+ 124) FALLOW_FAIL_KIND=\"timed out\" ;;
65
+ 2) FALLOW_FAIL_KIND=\"usage error\" ;;
66
+ 125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
67
+ 126) FALLOW_FAIL_KIND=\"not executable\" ;;
68
+ 127) FALLOW_FAIL_KIND=\"not found\" ;;
69
+ *) FALLOW_FAIL_KIND=\"crashed\" ;;
70
+ esac
71
+ echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
72
+ FALLOW_JSON_PATH=\"\"
73
+ else
74
+ mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
75
+ rm -f \"$FALLOW_STDERR_TMP\"
76
+ fi
77
+ ```
78
+
79
+ On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
80
+
81
+ 4) Optional MCP bridge path (runtime-dependent):
82
+ - If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
83
+ - Otherwise pass static JSON findings from `FALLOW.json`.
@@ -19,7 +19,33 @@ Parse arguments and load project state:
19
19
  ```bash
20
20
  _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
21
21
  PHASE_ARG="${1}"
22
- INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
22
+
23
+ # Parse all code-review flags into a structured IR via code-review-flags.cjs.
24
+ # This is the canonical flag-parsing surface — do not replicate inline bash parsing
25
+ # for --fix/--all/--auto here; the module handles all flag extraction and implication
26
+ # logic (e.g., --all and --auto imply --fix). Resolved BEFORE the init call below so
27
+ # the section-manifest gate forwards the RESOLVED (post-implication) fix decision, not
28
+ # just a literal --fix token check.
29
+ FLAGS_JSON=$(node -e "
30
+ const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
31
+ const flags = parseCodeReviewFlags(process.argv.slice(1));
32
+ process.stdout.write(JSON.stringify(flags));
33
+ " -- "$@" 2>/dev/null)
34
+
35
+ # Extract individual flag values from the IR
36
+ FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
37
+ FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
38
+ FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
39
+ DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
40
+ FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
41
+
42
+ # Forward the resolved fix decision (--fix itself, or --all/--auto implying it) to
43
+ # init.code-review so section_manifest's flag:--fix gating (dispatch-fix section)
44
+ # matches code-review-flags.cjs's own implication logic rather than a raw token scan.
45
+ FIX_PARAM=""
46
+ if [ "$FIX_FLAG" = "true" ]; then FIX_PARAM="--fix"; fi
47
+
48
+ INIT=$(gsd_run query init.code-review "${PHASE_ARG}" $FIX_PARAM)
23
49
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
24
50
  AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
25
51
  # #2072: resolve the routed model so model_overrides / models.verification are honored
@@ -27,7 +53,7 @@ AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
27
53
  REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
28
54
  ```
29
55
 
30
- Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
56
+ Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`, `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap`.
31
57
 
32
58
  **Input sanitization (defense-in-depth):**
33
59
  ```bash
@@ -46,27 +72,6 @@ Error: Phase ${PHASE_ARG} not found. Run /gsd:progress to see available phases.
46
72
 
47
73
  This runs BEFORE config gate check so user errors are surfaced immediately regardless of config state.
48
74
 
49
- Parse optional flags from $ARGUMENTS using the typed flag parser:
50
-
51
- ```bash
52
- # Parse all code-review flags into a structured IR via code-review-flags.cjs.
53
- # This is the canonical flag-parsing surface — do not replicate inline bash parsing
54
- # for --fix/--all/--auto here; the module handles all flag extraction and implication
55
- # logic (e.g., --all and --auto imply --fix).
56
- FLAGS_JSON=$(node -e "
57
- const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
58
- const flags = parseCodeReviewFlags(process.argv.slice(1));
59
- process.stdout.write(JSON.stringify(flags));
60
- " -- "$@" 2>/dev/null)
61
-
62
- # Extract individual flag values from the IR
63
- FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
64
- FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
65
- FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
66
- DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
67
- FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
68
- ```
69
-
70
75
  If FILES_OVERRIDE is set, split by comma into array:
71
76
  ```bash
72
77
  if [ -n "$FILES_OVERRIDE" ]; then
@@ -231,8 +236,13 @@ Additionally, whenever a reliable diff base is available, cross-check the SUMMAR
231
236
  against the diff and warn about (then add) any changed files the SUMMARY extractor did not
232
237
  surface — so a partial SUMMARY result can no longer silently mask the rest of the phase.
233
238
  ```bash
234
- # Compute diff base from phase commits — fail closed if no reliable base found
235
- PHASE_COMMITS=$(git log --oneline --all --grep="${PADDED_PHASE}" --format="%H" 2>/dev/null)
239
+ # Compute diff base from phase commits — fail closed if no reliable base found.
240
+ # #2989: anchor the grep to the phase-mention convention ("Phase N" / "phase N"
241
+ # with a word boundary) so a bare digit substring doesn't match version strings,
242
+ # dates, issue refs, or other phases' numbers. With --extended-regexp, \b is
243
+ # a word boundary. When no commit genuinely references the phase, this yields
244
+ # empty and the fail-closed warning below actually fires.
245
+ PHASE_COMMITS=$(git log --oneline --all --grep="[Pp]hase ${PADDED_PHASE}\b" --extended-regexp --format="%H" 2>/dev/null)
236
246
  DIFF_BASE=""
237
247
  if [ -n "$PHASE_COMMITS" ]; then
238
248
  DIFF_BASE=$(echo "$PHASE_COMMITS" | tail -1)^
@@ -389,20 +399,7 @@ Exit workflow. Do NOT spawn agent or create REVIEW.md.
389
399
  <step name="structural_pre_pass">
390
400
  Optional structural cross-module pass powered by fallow.
391
401
 
392
- Read fallow config gates:
393
- ```bash
394
- FALLOW_ENABLED=$(gsd_run query config-get code_quality.fallow.enabled 2>/dev/null || echo "false")
395
- FALLOW_SCOPE=$(gsd_run query config-get code_quality.fallow.scope 2>/dev/null || echo "phase")
396
- FALLOW_PROFILE=$(gsd_run query config-get code_quality.fallow.profile 2>/dev/null || echo "standard")
397
- FALLOW_MCP=$(gsd_run query config-get code_quality.fallow.mcp 2>/dev/null || echo "false")
398
- # profile maps to a --max-crap threshold since fallow has no native profile concept.
399
- # minimal=50 (more lenient), standard=30 (default), strict=15 (tighter).
400
- case "$FALLOW_PROFILE" in
401
- minimal) FALLOW_MAX_CRAP=50 ;;
402
- strict) FALLOW_MAX_CRAP=15 ;;
403
- *) FALLOW_MAX_CRAP=30 ;; # standard (default)
404
- esac
405
- ```
402
+ Parse `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap` from the init JSON as `FALLOW_ENABLED`, `FALLOW_SCOPE`, `FALLOW_PROFILE`, `FALLOW_MCP`, `FALLOW_MAX_CRAP`. These are resolved once by `init.code-review` at init time — consuming the pre-resolved values here (instead of a `config-get` call inside this step) avoids gating this section's own inclusion on a fact its own body would otherwise compute (see `state:fallow-enabled` in docs/reference/workflow-fragments.md).
406
403
 
407
404
  Defaults are fail-closed and opt-in:
408
405
  - `enabled=false` (skip entirely)
@@ -410,88 +407,9 @@ Defaults are fail-closed and opt-in:
410
407
  - `profile=standard` (maps to `--max-crap 30`; minimal=50, standard=30, strict=15 — fallow has no native profile concept)
411
408
  - `mcp=false`
412
409
 
413
- When `FALLOW_ENABLED=true`:
414
-
415
- 1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
416
- ```bash
417
- FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
418
- const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
419
- const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
420
- if (resolved) process.stdout.write(resolved);
421
- ")
422
- ```
423
-
424
- 2) If binary is missing, fail with actionable message:
425
- ```bash
426
- if [ -z \"$FALLOW_BIN\" ]; then
427
- echo \"Error: fallow is enabled but no binary was found.\"
428
- echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
429
- # Exit workflow
430
- fi
431
- ```
432
-
433
- 3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
434
- ```bash
435
- FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
436
- FALLOW_STDERR_TMP=$(mktemp)
437
-
438
- # Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
439
- # Derive the phase base commit; if none is found, fall back to repo scope (fallow
440
- # auto-detects the base branch).
441
- FALLOW_SCOPE_ARGS=()
442
- if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
443
- FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
444
- if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
445
- FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
446
- FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
447
- fi
448
- fi
449
-
450
- gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
451
- FALLOW_EXIT=$?
452
-
453
- # fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
454
- # valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
455
- # Decide success by whether the output parses as a fallow report, not by exit code.
456
- FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
457
- try {
458
- const fs = require('fs');
459
- const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
460
- const o = JSON.parse(txt);
461
- process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
462
- } catch { process.stdout.write('0'); }
463
- \")
464
- if [ \"$FALLOW_OK\" != \"1\" ]; then
465
- FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
466
- rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
467
- # #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
468
- # but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
469
- # 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
470
- # CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
471
- # not found. A non-zero exit here with a resolved binary means fallow is
472
- # installed but did not produce a report — surface that loudly so a Windows
473
- # user does not mistake it for "fallow absent".
474
- case \"$FALLOW_EXIT\" in
475
- 124) FALLOW_FAIL_KIND=\"timed out\" ;;
476
- 2) FALLOW_FAIL_KIND=\"usage error\" ;;
477
- 125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
478
- 126) FALLOW_FAIL_KIND=\"not executable\" ;;
479
- 127) FALLOW_FAIL_KIND=\"not found\" ;;
480
- *) FALLOW_FAIL_KIND=\"crashed\" ;;
481
- esac
482
- echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
483
- FALLOW_JSON_PATH=\"\"
484
- else
485
- mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
486
- rm -f \"$FALLOW_STDERR_TMP\"
487
- fi
488
- ```
489
-
490
- On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
491
-
492
- 4) Optional MCP bridge path (runtime-dependent):
493
- - If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
494
- - Otherwise pass static JSON findings from `FALLOW.json`.
410
+ <!-- gsd:section id="structural-pre-pass" when="state:fallow-enabled" -->
411
+ If `section_manifest` is `null` or `"structural-pre-pass"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/structural-pre-pass.md`. Otherwise skip — do not read the file.
412
+ <!-- /gsd:section -->
495
413
 
496
414
  When disabled, set:
497
415
  ```bash
@@ -636,45 +554,9 @@ fi
636
554
  ```
637
555
  </step>
638
556
 
639
- <step name="dispatch_fix">
640
- If the `--fix` flag was passed (`FIX_FLAG=true`), delegate to the `code-review-fix.md` workflow
641
- to auto-apply findings from the REVIEW.md that was just written (or that already existed).
642
-
643
- This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
644
- is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
645
- workflow handles the missing-review error and exits cleanly.
646
-
647
- ```bash
648
- if [ "$FIX_FLAG" = "true" ]; then
649
- echo ""
650
- echo "─────────────────────────────────────────────────────────────────"
651
- echo " --fix: delegating to code-review-fix.md"
652
- echo "─────────────────────────────────────────────────────────────────"
653
- echo ""
654
-
655
- # Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
656
- FIX_ARGS="${PHASE_ARG}"
657
- if [ "$FIX_ALL" = "true" ]; then
658
- FIX_ARGS="${FIX_ARGS} --all"
659
- fi
660
- if [ "$FIX_AUTO" = "true" ]; then
661
- FIX_ARGS="${FIX_ARGS} --auto"
662
- fi
663
-
664
- # Load and execute the code-review-fix workflow.
665
- # The fix workflow is the canonical implementation for all fix logic:
666
- # gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
667
- # and result presentation. Do not duplicate that logic here.
668
- Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
669
-
670
- # Exit after fix workflow completes — present_results is for review-only output.
671
- # The fix workflow has its own present_results step.
672
- # Exit workflow.
673
- fi
674
- ```
675
-
676
- If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
677
- </step>
557
+ <!-- gsd:section id="dispatch-fix" when="flag:--fix" -->
558
+ If `section_manifest` is `null` or `"dispatch-fix"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/dispatch-fix.md`. Otherwise skip — do not read the file; proceed to `present_results`.
559
+ <!-- /gsd:section -->
678
560
 
679
561
  <step name="present_results">
680
562
  Read the REVIEW.md YAML frontmatter to extract finding counts.
@@ -0,0 +1,29 @@
1
+ <step name="git_tag">
2
+
3
+ Create git tag:
4
+
5
+ ```bash
6
+ # Pre-check: skip if tag already exists (prevents silent failure on retry)
7
+ if git rev-parse "v[X.Y]" >/dev/null 2>&1; then echo "Tag v[X.Y] already exists, skipping"; exit 0; fi
8
+ git tag -a v[X.Y] -m "v[X.Y] [Name]
9
+
10
+ Delivered: [One sentence]
11
+
12
+ Key accomplishments:
13
+ - [Item 1]
14
+ - [Item 2]
15
+ - [Item 3]
16
+
17
+ See .planning/MILESTONES.md for full details."
18
+ ```
19
+
20
+ Confirm: "Tagged: v[X.Y]"
21
+
22
+ Ask: "Push tag to remote? (y/n)"
23
+
24
+ If yes:
25
+ ```bash
26
+ git push origin v[X.Y]
27
+ ```
28
+
29
+ </step>