@opengsd/gsd-core 1.8.0 → 1.9.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 (177) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +31 -1
  4. package/agents/gsd-code-fixer.md +107 -34
  5. package/agents/gsd-codebase-mapper.md +1 -1
  6. package/agents/gsd-debug-session-manager.md +36 -0
  7. package/agents/gsd-executor.md +20 -7
  8. package/agents/gsd-intel-updater.md +3 -3
  9. package/agents/gsd-phase-researcher.md +4 -2
  10. package/agents/gsd-plan-checker.md +20 -0
  11. package/agents/gsd-planner.md +15 -23
  12. package/agents/gsd-project-researcher.md +2 -2
  13. package/agents/gsd-ui-auditor.md +0 -40
  14. package/bin/install.js +236 -107
  15. package/commands/gsd/plan-review-convergence.md +5 -1
  16. package/gsd-core/bin/gsd-tools.cjs +882 -4
  17. package/gsd-core/bin/lib/api-coverage.cjs +22 -8
  18. package/gsd-core/bin/lib/audit.cjs +8 -8
  19. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  20. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  21. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  22. package/gsd-core/bin/lib/capability-registry.cjs +1353 -132
  23. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  24. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  25. package/gsd-core/bin/lib/check-command-router.cjs +12 -2
  26. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  27. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +102 -12
  28. package/gsd-core/bin/lib/claude-orchestration.cjs +125 -22
  29. package/gsd-core/bin/lib/commands.cjs +246 -18
  30. package/gsd-core/bin/lib/config-loader.cjs +200 -28
  31. package/gsd-core/bin/lib/config.cjs +90 -5
  32. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  33. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  34. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  35. package/gsd-core/bin/lib/init.cjs +44 -19
  36. package/gsd-core/bin/lib/install-engine.cjs +1 -0
  37. package/gsd-core/bin/lib/milestone.cjs +36 -9
  38. package/gsd-core/bin/lib/model-catalog.cjs +51 -1
  39. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  40. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  41. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  42. package/gsd-core/bin/lib/phase-id.cjs +278 -5
  43. package/gsd-core/bin/lib/phase.cjs +61 -6
  44. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  45. package/gsd-core/bin/lib/plan-scan.cjs +1 -1
  46. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  47. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  48. package/gsd-core/bin/lib/project-root.cjs +48 -0
  49. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  50. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  51. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  52. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  53. package/gsd-core/bin/lib/roadmap-parser.cjs +54 -6
  54. package/gsd-core/bin/lib/roadmap.cjs +10 -4
  55. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +31 -4
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -1
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +140 -0
  58. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  59. package/gsd-core/bin/lib/smart-entry.cjs +1 -1
  60. package/gsd-core/bin/lib/state-document.cjs +164 -20
  61. package/gsd-core/bin/lib/state-transition.cjs +28 -10
  62. package/gsd-core/bin/lib/state.cjs +141 -21
  63. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  64. package/gsd-core/bin/lib/uat.cjs +9 -7
  65. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  66. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  67. package/gsd-core/bin/lib/validate.cjs +32 -0
  68. package/gsd-core/bin/lib/verification.cjs +51 -14
  69. package/gsd-core/bin/lib/verify.cjs +146 -22
  70. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  71. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  72. package/gsd-core/bin/shared/config-schema.manifest.json +1 -13
  73. package/gsd-core/bin/shared/model-catalog.json +5 -0
  74. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  75. package/gsd-core/references/context-budget.md +40 -0
  76. package/gsd-core/references/gate-prompts.md +6 -3
  77. package/gsd-core/references/model-profile-resolution.md +64 -13
  78. package/gsd-core/references/offer-next.md +88 -0
  79. package/gsd-core/references/planning-config.md +2 -1
  80. package/gsd-core/references/reviewer-instances.md +28 -21
  81. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  82. package/gsd-core/references/ui-consideration-probe.md +2 -2
  83. package/gsd-core/references/worktree-branch-check.md +4 -4
  84. package/gsd-core/templates/summary-minimal.md +4 -0
  85. package/gsd-core/templates/summary-standard.md +4 -0
  86. package/gsd-core/templates/summary.md +7 -0
  87. package/gsd-core/workflows/ai-integration-phase.md +4 -4
  88. package/gsd-core/workflows/audit-fix.md +4 -0
  89. package/gsd-core/workflows/audit-milestone.md +8 -0
  90. package/gsd-core/workflows/autonomous.md +19 -15
  91. package/gsd-core/workflows/check-todos.md +2 -2
  92. package/gsd-core/workflows/code-review-fix.md +14 -6
  93. package/gsd-core/workflows/code-review.md +93 -21
  94. package/gsd-core/workflows/debug.md +10 -2
  95. package/gsd-core/workflows/diagnose-issues.md +4 -0
  96. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  97. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  98. package/gsd-core/workflows/discuss-phase-assumptions.md +15 -9
  99. package/gsd-core/workflows/discuss-phase.md +2 -2
  100. package/gsd-core/workflows/docs-update.md +8 -0
  101. package/gsd-core/workflows/eval-review.md +1 -1
  102. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  103. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  104. package/gsd-core/workflows/execute-phase.md +85 -115
  105. package/gsd-core/workflows/execute-plan.md +5 -4
  106. package/gsd-core/workflows/explore.md +4 -0
  107. package/gsd-core/workflows/extract-learnings.md +21 -0
  108. package/gsd-core/workflows/help/modes/full.md +3 -3
  109. package/gsd-core/workflows/import.md +4 -1
  110. package/gsd-core/workflows/ingest-docs.md +4 -0
  111. package/gsd-core/workflows/map-codebase.md +13 -6
  112. package/gsd-core/workflows/new-milestone.md +10 -2
  113. package/gsd-core/workflows/new-project.md +11 -4
  114. package/gsd-core/workflows/next.md +5 -2
  115. package/gsd-core/workflows/plan-phase.md +42 -46
  116. package/gsd-core/workflows/plan-review-convergence.md +18 -14
  117. package/gsd-core/workflows/progress.md +1 -1
  118. package/gsd-core/workflows/quick.md +14 -3
  119. package/gsd-core/workflows/review.md +146 -575
  120. package/gsd-core/workflows/scan.md +9 -1
  121. package/gsd-core/workflows/secure-phase.md +10 -2
  122. package/gsd-core/workflows/ship.md +41 -11
  123. package/gsd-core/workflows/smart-entry.md +1 -1
  124. package/gsd-core/workflows/ui-phase.md +8 -1
  125. package/gsd-core/workflows/ui-review.md +8 -1
  126. package/gsd-core/workflows/update.md +104 -5
  127. package/gsd-core/workflows/validate-phase.md +10 -2
  128. package/gsd-core/workflows/verify-work.md +8 -1
  129. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  130. package/hooks/dist/gsd-cursor-stop.js +6 -2
  131. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  132. package/hooks/dist/gsd-graphify-update.sh +9 -0
  133. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  134. package/hooks/dist/gsd-prompt-guard.js +101 -2
  135. package/hooks/dist/gsd-read-guard.js +100 -2
  136. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  137. package/hooks/dist/gsd-statusline.js +9 -6
  138. package/hooks/dist/gsd-workflow-guard.js +110 -6
  139. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  140. package/hooks/dist/lib/cursor-workspace.js +74 -0
  141. package/hooks/gsd-cursor-session-start.js +6 -2
  142. package/hooks/gsd-cursor-stop.js +6 -2
  143. package/hooks/gsd-cursor-subagent-start.js +6 -2
  144. package/hooks/gsd-graphify-update.sh +9 -0
  145. package/hooks/gsd-phase-boundary.sh +14 -2
  146. package/hooks/gsd-prompt-guard.js +101 -2
  147. package/hooks/gsd-read-guard.js +100 -2
  148. package/hooks/gsd-read-injection-scanner.js +109 -2
  149. package/hooks/gsd-statusline.js +9 -6
  150. package/hooks/gsd-workflow-guard.js +110 -6
  151. package/hooks/gsd-worktree-path-guard.js +132 -8
  152. package/hooks/lib/cursor-workspace.js +74 -0
  153. package/package.json +7 -7
  154. package/pi/gsd.cjs +26 -1
  155. package/scripts/check-coverage-gate.cjs +51 -0
  156. package/scripts/check-glossary-refs.cjs +24 -0
  157. package/scripts/ci-test-scope.cjs +67 -17
  158. package/scripts/gen-adr-index.cjs +6 -4
  159. package/scripts/gen-capability-matrix.cjs +26 -2
  160. package/scripts/gen-capability-registry.cjs +132 -34
  161. package/scripts/gen-emitted-baseline.cjs +145 -0
  162. package/scripts/gen-registry.cjs +39 -15
  163. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  164. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  165. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  166. package/scripts/lint-resolution-provenance.cjs +9 -0
  167. package/scripts/mutation-matrix.cjs +4 -0
  168. package/scripts/prompt-injection-scan.sh +6 -0
  169. package/scripts/registry-schema.cjs +372 -94
  170. package/scripts/release-notes/conventional-title.cjs +19 -1
  171. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  172. package/scripts/validate-registry.cjs +10 -6
  173. package/scripts/workflow-size.cjs +16 -8
  174. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  175. package/vscode/package.json +1 -1
  176. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  177. package/scripts/update-size-baseline.cjs +0 -68
@@ -13,6 +13,10 @@ Execute all plans in a phase using wave-based parallel execution. Orchestrator s
13
13
  Orchestrator coordinates, not executes. Each subagent loads the full execute-plan context. Orchestrator: discover plans → analyze deps → group waves → spawn agents → handle checkpoints → collect results.
14
14
  </core_principle>
15
15
 
16
+ <!-- #2508 runtime-aware-dispatch -->
17
+
18
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
19
+
16
20
  <runtime_compatibility>
17
21
  **Subagent spawning is runtime-specific:**
18
22
  - **Claude Code:** Uses `Agent(subagent_type="gsd-executor", ...)` — blocks until complete, returns result
@@ -96,24 +100,14 @@ USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/nul
96
100
  EXECUTOR_STALL_INTERVAL_MINUTES=$(gsd_run query config-get executor.stall_detect_interval_minutes 2>/dev/null || echo "5")
97
101
  EXECUTOR_STALL_THRESHOLD_MINUTES=$(gsd_run query config-get executor.stall_threshold_minutes 2>/dev/null || echo "10")
98
102
 
99
- if [ "$RUNTIME" != "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
100
- echo "FATAL: git worktree isolation (isolation=\"worktree\") is unsupported on runtime '$RUNTIME' — it would run executor agents unisolated against the main checkout. Set workflow.use_worktrees=false." >&2
101
- exit 1
102
- fi
103
- # Sweep orphaned locked worktrees from prior crashed sessions before spawning executors (#3707).
104
- [ "$USE_WORKTREES" != "false" ] && gsd_run query worktree.reap-orphans 2>/dev/null || true
105
- # Auto-degrade to sequential if HEAD has diverged from the worktree fork base (#683).
106
- # Only applies to Claude Code (isolation="worktree" is Claude-Code-specific).
107
- if [ "$RUNTIME" = "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
108
- _SHOULD_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || true)
109
- if [ "$_SHOULD_DEGRADE" = "true" ]; then
110
- _DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
111
- [ -n "$_DEGRADE_MSG" ] && printf '%s\n' "$_DEGRADE_MSG" >&2
112
- USE_WORKTREES=false
113
- fi
114
- fi
103
+ # Resolve ISOLATION + apply its guards: read and execute the "Resolve ISOLATION"
104
+ # section of execute-phase/steps/executor-isolation-dispatch.md. It sets
105
+ # ISOLATION (harness-worktree|orchestrator-worktree|none), forces none when
106
+ # USE_WORKTREES=false, fails closed when a host has no primitive, sweeps orphans,
107
+ # and applies the #683 fork-base auto-degrade.
115
108
  ```
116
- `isolation="worktree"` is a Claude-Code-specific agent primitive; no other runtime can honor it (Codex maps subagents to `spawn_agent`, others prohibit or omit worktree binding). Failing closed prevents main-checkout edits while the workflow believes agents are isolated.
109
+
110
+ `ISOLATION` — not `RUNTIME` — is the ONLY fan-out branch point; **never add a `RUNTIME = "codex"` test here.** Per-host dispatch detail lives in `execute-phase/steps/executor-isolation-dispatch.md` (read from step 3).
117
111
 
118
112
  If the project uses git submodules, worktree isolation is unsafe **only when a plan touches a submodule path** — the executor commit protocol cannot correctly handle submodule commits inside isolated worktrees. Compute submodule paths once and intersect them per-plan with the plan's declared `files_modified` frontmatter.
119
113
 
@@ -129,9 +123,9 @@ fi
129
123
 
130
124
  `SUBMODULE_PATHS` is exported to the `execute_waves` step, where the per-plan decision happens (see "Per-plan worktree decision" sub-step inside `execute_waves`). The decision is per-plan because different plans in the same wave can touch different files — only plans whose paths intersect a submodule must drop worktree isolation; plans nowhere near a submodule keep parallel isolation.
131
125
 
132
- When `USE_WORKTREES` (project-level) is `false`, all executor agents run without `isolation="worktree"` — they execute sequentially on the main working tree instead of in parallel worktrees. The per-plan decision below has no effect when worktrees are project-disabled.
126
+ When `USE_WORKTREES` is `false`, `ISOLATION` is forced to `none`: executors run sequentially on the main working tree. The per-plan decision below has no effect when worktrees are project-disabled.
133
127
 
134
- `USE_WORKTREES` is also automatically set to `false` for the duration of a run when `worktree base-check` detects that the orchestrator HEAD has diverged from the worktree fork base (the #683 condition — e.g. an unmerged milestone or feature branch). This check runs only when `RUNTIME=claude` because `isolation="worktree"` is a Claude Code-specific feature; other runtimes do not use it. The auto-degrade prints a one-line warning to stderr and falls through to the sequential path so executors do not hit the exit-42 worktree-branch-check halt. To restore parallel worktree execution, set `worktree.baseRef:"head"` in `.claude/settings.local.json` (or run `gsd-tools worktree set-baseref`) — this makes the fork base track the live HEAD instead of a fixed remote ref. The `worktree-branch-check` exit-42 guard inside each executor remains in place as a backstop.
128
+ `USE_WORKTREES` and `ISOLATION` are also reset for the run when `worktree base-check` detects the orchestrator HEAD has diverged from the worktree fork base (#683 — e.g. an unmerged milestone branch). This runs for **any** isolated run, not only Claude: fork-base divergence is a property of the repository, so it degrades a GSD-created worktree exactly as a harness-created one. The auto-degrade prints a one-line warning to stderr and falls through to the sequential path so executors do not hit the exit-42 worktree-branch-check halt. To restore parallel worktree execution, set `worktree.baseRef:"head"` in `.claude/settings.local.json` (or run `gsd_run worktree set-baseref`) — this makes the fork base track the live HEAD instead of a fixed remote ref. The `worktree-branch-check` exit-42 guard inside each executor remains in place as a backstop.
135
129
 
136
130
  Read context window size for adaptive prompt enrichment:
137
131
 
@@ -307,10 +301,8 @@ else
307
301
  else
308
302
  git switch --quiet "$DEFAULT_BRANCH" 2>/dev/null && git merge --ff-only --quiet "origin/$DEFAULT_BRANCH" 2>/dev/null || true
309
303
  fi
310
- # Pinned base + fail-fast: on success HEAD is exactly at origin/$DEFAULT_BRANCH,
311
- # so a post-creation merge-base or "ahead-of" guard would be unreachable. The
312
- # explicit base argument here is the single source of correctness for #2916.
313
- git checkout -b "$BRANCH_NAME" "origin/$DEFAULT_BRANCH" \
304
+ # Pinned base (#2916); --no-track (#2498) so default autoSetupMerge doesn't wire upstream to origin/$DEFAULT_BRANCH.
305
+ git checkout -b "$BRANCH_NAME" "origin/$DEFAULT_BRANCH" --no-track \
314
306
  || { echo "ERROR: Could not create '$BRANCH_NAME' from origin/$DEFAULT_BRANCH (#2916)." >&2; exit 1; }
315
307
  fi
316
308
  ```
@@ -441,18 +433,63 @@ cwd inside an agent worktree (or a subdirectory of one). Every subsequent
441
433
  orchestrator-side git call would then target the wrong tree — this is how a wrong-base
442
434
  merge nearly shipped ~1000 files. Resolve the *worktree root* (so a subdirectory cwd
443
435
  cannot skew the check) and refuse if it is an agent worktree. The discriminator is the
444
- per-agent branch namespace `worktree-agent-*`, NOT the `.claude/worktrees/` path: the
436
+ per-agent branch namespace `agent-*` / `worktree-agent-*`, NOT the `.claude/worktrees/` path: the
445
437
  orchestrator may itself be legitimately invoked from a feature worktree under
446
438
  `.claude/worktrees/`, so a path-substring refusal would break legitimate runs. Do NOT
447
439
  pin to `git worktree list`'s first entry — that is the main worktree, the wrong target
448
440
  when the orchestrator legitimately runs from a feature worktree.
449
441
 
450
442
  ```bash
443
+ # gsd:guard=orchestrator-cwd-drift
451
444
  ORCHESTRATOR_WT=$(git rev-parse --show-toplevel 2>/dev/null) || {
452
445
  echo "FATAL: execute_waves entry is not inside a git worktree (#48)." >&2; exit 1; }
453
446
  ORCH_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
454
- if printf '%s' "$ORCH_BRANCH" | grep -Eq '^worktree-agent-'; then
447
+ if printf '%s' "$ORCH_BRANCH" | grep -Eq '^(worktree-)?agent-'; then
455
448
  echo "FATAL: orchestrator cwd is inside an agent worktree (branch '$ORCH_BRANCH', root '$ORCHESTRATOR_WT') — refusing to execute waves (#48). A prior isolation=\"worktree\" dispatch drifted the cwd; re-run from the orchestrator's own worktree." >&2
449
+ # #1856 handoff: the refusal above is correct, but on its own it is a dead end —
450
+ # this worktree may hold committed fixes AND uncommitted work, and "re-run from
451
+ # the orchestrator's worktree" silently means abandoning them. Report exactly
452
+ # what is stranded and how to integrate it. Every command here is DIAGNOSTIC:
453
+ # each is `|| true`-guarded so a failure degrades to the plain refusal above
454
+ # rather than crashing before the message prints.
455
+ _WT_BASE=""
456
+ for _ref in "$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null || true)" \
457
+ origin/next origin/main next main; do
458
+ [ -n "$_ref" ] || continue
459
+ if git rev-parse --verify --quiet "$_ref" >/dev/null 2>&1; then _WT_BASE="$_ref"; break; fi
460
+ done
461
+ _WT_AHEAD=""
462
+ [ -n "$_WT_BASE" ] && _WT_AHEAD=$(git rev-list --count "$_WT_BASE..HEAD" 2>/dev/null || true)
463
+ # Count BEFORE truncating, so a long list reports its true size rather than
464
+ # under-reporting what is stranded — which is the whole point of this report.
465
+ _WT_DIRTY_ALL=$(git status --porcelain 2>/dev/null || true)
466
+ _WT_DIRTY_N=0
467
+ [ -n "$_WT_DIRTY_ALL" ] && _WT_DIRTY_N=$(printf '%s\n' "$_WT_DIRTY_ALL" | wc -l | tr -d ' ')
468
+ _WT_HAS_COMMITS=0
469
+ [ -n "$_WT_AHEAD" ] && [ "$_WT_AHEAD" -gt 0 ] 2>/dev/null && _WT_HAS_COMMITS=1
470
+
471
+ echo "" >&2
472
+ echo "── Handoff: what is in this worktree (#1856) ──" >&2
473
+ if [ "$_WT_HAS_COMMITS" -eq 1 ]; then
474
+ echo " $_WT_AHEAD commit(s) on '$ORCH_BRANCH' not on '$_WT_BASE':" >&2
475
+ git log --oneline --no-decorate "$_WT_BASE..HEAD" 2>/dev/null | head -20 | sed 's/^/ /' >&2 || true
476
+ [ "$_WT_AHEAD" -gt 20 ] 2>/dev/null && echo " … and $((_WT_AHEAD - 20)) more" >&2
477
+ echo " These live ONLY on this branch. Switching away without integrating loses them." >&2
478
+ fi
479
+ if [ -n "$_WT_DIRTY_ALL" ]; then
480
+ echo " $_WT_DIRTY_N uncommitted change(s) still in this worktree:" >&2
481
+ printf '%s\n' "$_WT_DIRTY_ALL" | head -20 | sed 's/^/ /' >&2
482
+ [ "$_WT_DIRTY_N" -gt 20 ] 2>/dev/null && echo " … and $((_WT_DIRTY_N - 20)) more" >&2
483
+ fi
484
+ if [ "$_WT_HAS_COMMITS" -eq 1 ] || [ -n "$_WT_DIRTY_ALL" ]; then
485
+ echo "" >&2
486
+ echo " To integrate before continuing:" >&2
487
+ [ -n "$_WT_DIRTY_ALL" ] && echo " 1. git add -A && git commit -m 'wip: recover worktree state' # from THIS worktree" >&2
488
+ echo " 2. cd <orchestrator worktree> # a checkout whose branch is NOT agent-*/worktree-agent-*" >&2
489
+ echo " 3. git merge --no-ff $ORCH_BRANCH # or: git cherry-pick <sha>... for selected commits" >&2
490
+ echo " 4. re-run the phase from there" >&2
491
+ echo " Verify with: git log --oneline ${_WT_BASE:-HEAD}..$ORCH_BRANCH" >&2
492
+ fi
456
493
  exit 1
457
494
  fi
458
495
  # Pin to the worktree root; each later orchestrator-side block re-pins the same way
@@ -553,7 +590,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
553
590
 
554
591
  Read and execute `gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md` for each plan. It extracts `PLAN_FILES` from the plan's JSON, intersects against `SUBMODULE_PATHS` (with normalization, bidirectional matching, and glob-prefix handling), and sets `USE_WORKTREES_FOR_PLAN` to `false` when the plan touches a submodule path. Append `plan_id` to a `WAVE_WORKTREE_PLANS` accumulator when `USE_WORKTREES_FOR_PLAN != false`.
555
592
 
556
- The dispatch branches in step 3 below MUST gate on `USE_WORKTREES_FOR_PLAN` for the current plan, not on the project-level `USE_WORKTREES`.
593
+ The dispatch branches in step 3 gate on both `USE_WORKTREES` and `USE_WORKTREES_FOR_PLAN` (#2474).
557
594
 
558
595
  2.75. **Execute:wave:pre capability dispatch:**
559
596
 
@@ -574,14 +611,14 @@ increases monotonically across waves. `{status}` is `complete` (success),
574
611
  For 200k models, this keeps orchestrator context lean (~10-15%).
575
612
  For 1M+ models (Opus 4.6, Sonnet 4.6), richer context can be passed directly.
576
613
 
577
- **Worktree mode** (`USE_WORKTREES_FOR_PLAN` is not `false` — evaluated per-plan in step 2.5):
614
+ **Worktree mode** (`USE_WORKTREES` and `USE_WORKTREES_FOR_PLAN` not `false`):
578
615
 
579
616
  Before spawning, capture the current HEAD:
580
617
  ```bash
581
618
  EXPECTED_BASE=$(git rev-parse HEAD)
582
619
  DISPATCH_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
583
620
  EXPECTED_BRANCH=$(git rev-parse --abbrev-ref HEAD)
584
- if [ "${USE_WORKTREES_FOR_PLAN:-true}" != "false" ] && [ -z "${WAVE_WORKTREE_MANIFEST:-}" ]; then
621
+ if [ "${USE_WORKTREES:-true}" != "false" ] && [ "${USE_WORKTREES_FOR_PLAN:-true}" != "false" ] && [ -z "${WAVE_WORKTREE_MANIFEST:-}" ]; then
585
622
  M=$(mktemp "${TMPDIR:-/tmp}/gsd-worktree-wave-XXXXXX") && mv "$M" "$M.json" && WAVE_WORKTREE_MANIFEST="$M.json" || exit 1 # XXXXXX must be path-final on BSD/macOS (#1520)
586
623
  # Persist the dispatch-time orchestrator worktree root so wave-cleanup can pin back to the
587
624
  # orchestrator's OWN worktree — NOT `git worktree list`'s first entry (always the main
@@ -593,6 +630,8 @@ increases monotonically across waves. `{status}` is `complete` (success),
593
630
  fi
594
631
  ```
595
632
 
633
+ **Isolation model.** The block below is the **`harness-worktree`** path. For `orchestrator-worktree` use the dispatch below it; for `none` use sequential mode. Both are detailed in `execute-phase/steps/executor-isolation-dispatch.md`.
634
+
596
635
  **Sequential dispatch for parallel execution (waves with 2+ agents):**
597
636
  Dispatch each `Agent()` call **one at a time with `run_in_background: true`**. Do NOT
598
637
  send all Agent calls in a single message: simultaneous `git worktree add` calls race
@@ -611,7 +650,10 @@ increases monotonically across waves. `{status}` is `complete` (success),
611
650
  # When executor_model is "inherit", omit this parameter entirely so
612
651
  # Claude Code inherits the orchestrator model automatically.
613
652
  model="{executor_model}", # omit this line when executor_model == "inherit"
614
- isolation="worktree",
653
+ # The host's OWN declared isolation flag (`harnessFlag` from
654
+ # `dispatch-isolation --json`; see the isolation-dispatch fragment).
655
+ # Emit the declared token — do NOT hardcode a runtime's flag.
656
+ {harnessFlag},
615
657
  prompt="
616
658
  <objective>
617
659
  Execute plan {plan_number} of phase {phase_number}-{phase_name}.
@@ -695,6 +737,8 @@ increases monotonically across waves. `{status}` is `complete` (success),
695
737
 
696
738
  > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above to spawn executor agent(s), stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
697
739
 
740
+ **Orchestrator-managed worktree dispatch** (`ISOLATION=orchestrator-worktree`): read and execute `execute-phase/steps/executor-isolation-dispatch.md`. GSD creates each worktree (`worktree create`) and spawns the executor into it; the orchestrator performs every git operation. Merge-back and cleanup are the existing manifest-scoped gauntlet, unchanged.
741
+
698
742
  **Sequential mode** (`USE_WORKTREES_FOR_PLAN` is `false` — either project-level `USE_WORKTREES=false`, or per-plan submodule intersection forced it false in step 2.5):
699
743
 
700
744
  Omit `isolation="worktree"` from the Agent call. Replace the `<parallel_execution>` block with:
@@ -1511,7 +1555,7 @@ Copy failure must NOT block phase completion.
1511
1555
  <step name="close_phase_todos">
1512
1556
  **Auto-close pending todos tagged for this phase (#2433).**
1513
1557
 
1514
- This step runs AFTER `update_roadmap` marks the phase complete. It moves any pending todos that carry `resolves_phase: <current-phase-number>` to the completed directory.
1558
+ After `update_roadmap`, moves todos whose `resolves_phase` matches to `completed/`.
1515
1559
 
1516
1560
  ```bash
1517
1561
  PHASE_NUM="${PHASE_NUMBER}"
@@ -1519,12 +1563,19 @@ PENDING_DIR=".planning/todos/pending"
1519
1563
  COMPLETED_DIR=".planning/todos/completed"
1520
1564
  mkdir -p "$COMPLETED_DIR"
1521
1565
 
1566
+ # "05"=="5" (#2576).
1567
+ normalize_phase_num() {
1568
+ local p="${1//\"/}"; printf '%s' "$p" | sed 's/^0*\([0-9]\)/\1/'
1569
+ }
1570
+ PHASE_NUM_NORM=$(normalize_phase_num "$PHASE_NUM")
1571
+
1522
1572
  CLOSED=()
1523
1573
  for TODO_FILE in "$PENDING_DIR"/*.md; do
1524
1574
  [ -f "$TODO_FILE" ] || continue
1525
- # Extract resolves_phase from YAML frontmatter (first --- block only)
1575
+ # resolves_phase from first frontmatter block
1526
1576
  RP=$(awk '/^---/{c++;next} c==1 && /^resolves_phase:/{print $2;exit} c==2{exit}' "$TODO_FILE" 2>/dev/null || true)
1527
- if [ "$RP" = "$PHASE_NUM" ] || [ "$RP" = "\"$PHASE_NUM\"" ]; then
1577
+ RP_NORM=$(normalize_phase_num "$RP")
1578
+ if [ -n "$RP_NORM" ] && [ "$RP_NORM" = "$PHASE_NUM_NORM" ]; then
1528
1579
  mv "$TODO_FILE" "$COMPLETED_DIR/"
1529
1580
  CLOSED+=("$(basename "$TODO_FILE")")
1530
1581
  fi
@@ -1537,7 +1588,7 @@ if [ ${#CLOSED[@]} -gt 0 ]; then
1537
1588
  fi
1538
1589
  ```
1539
1590
 
1540
- **If no todos have `resolves_phase: <this-phase>`:** Skip silently — this step is always additive and never blocks phase completion.
1591
+ **No matches:** skip silently (always additive, non-blocking).
1541
1592
  </step>
1542
1593
 
1543
1594
  <step name="update_project_md">
@@ -1563,88 +1614,7 @@ gsd_run query commit "docs(phase-{X}): evolve PROJECT.md after phase completion"
1563
1614
  </step>
1564
1615
 
1565
1616
  <step name="offer_next">
1566
-
1567
- **Exception:** If `gaps_found`, the `verify_phase_goal` step already presents the gap-closure path (`/gsd:plan-phase {X} --gaps`). No additional routing needed — skip auto-advance.
1568
-
1569
- **No-transition check (spawned by auto-advance chain):**
1570
-
1571
- Parse `--no-transition` flag from $ARGUMENTS.
1572
-
1573
- **If `--no-transition` flag present:**
1574
-
1575
- Execute-phase was spawned by plan-phase's auto-advance. Do NOT run transition.md.
1576
- After verification passes and roadmap is updated, return completion status to parent:
1577
-
1578
- ```
1579
- ## PHASE COMPLETE
1580
-
1581
- Phase: ${PHASE_NUMBER} - ${PHASE_NAME}
1582
- Plans: ${completed_count}/${total_count}
1583
- Verification: {Passed | Gaps Found}
1584
-
1585
- [Include aggregate_results output]
1586
- ```
1587
-
1588
- STOP. Do not proceed to auto-advance or transition.
1589
-
1590
- **If `--no-transition` flag is NOT present:**
1591
-
1592
- **Auto-advance detection:**
1593
-
1594
- 1. Parse `--auto` flag from $ARGUMENTS
1595
- 2. Read consolidated auto-mode (`active` = chain flag OR user preference; chain flag already synced in init step):
1596
- ```bash
1597
- AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo "false")
1598
- ```
1599
-
1600
- **If `--auto` flag present OR `AUTO_MODE` is true (AND verification passed with no gaps):**
1601
-
1602
- ```
1603
- ╔══════════════════════════════════════════╗
1604
- ║ AUTO-ADVANCING → TRANSITION ║
1605
- ║ Phase {X} verified, continuing chain ║
1606
- ╚══════════════════════════════════════════╝
1607
- ```
1608
-
1609
- Execute the transition workflow inline (do NOT use Agent — orchestrator context is ~10-15%, transition needs phase completion data already in context):
1610
-
1611
- Read and follow `~/.claude/gsd-core/workflows/transition.md`, passing through the `--auto` flag so it propagates to the next phase invocation.
1612
-
1613
- **If neither `--auto` nor `AUTO_MODE` is true:**
1614
-
1615
- **STOP. Do not auto-advance. Do not execute transition. Do not plan next phase. Present options to the user and wait.**
1616
-
1617
- **IMPORTANT: There is NO `/gsd-transition` command. Never suggest it. The transition workflow is internal only.**
1618
-
1619
- Check whether CONTEXT.md already exists for the next phase:
1620
-
1621
- ```bash
1622
- ls .planning/phases/*{next}*/{next}-CONTEXT.md 2>/dev/null || echo "no-context"
1623
- ```
1624
-
1625
- If CONTEXT.md does **not** exist for the next phase, present:
1626
-
1627
- ```
1628
- ## ✓ Phase {X}: {Name} Complete
1629
-
1630
- /gsd:progress ${GSD_WS} — see updated roadmap
1631
- /gsd:discuss-phase {next} ${GSD_WS} — start here: discuss next phase before planning ← recommended
1632
- /gsd:plan-phase {next} ${GSD_WS} — plan next phase (skip discuss)
1633
- /gsd:execute-phase {next} ${GSD_WS} — execute next phase (skip discuss and plan)
1634
- ```
1635
-
1636
- If CONTEXT.md **exists** for the next phase, present:
1637
-
1638
- ```
1639
- ## ✓ Phase {X}: {Name} Complete
1640
-
1641
- /gsd:progress ${GSD_WS} — see updated roadmap
1642
- /gsd:plan-phase {next} ${GSD_WS} — start here: plan next phase (CONTEXT.md already present) ← recommended
1643
- /gsd:discuss-phase {next} ${GSD_WS} — re-discuss next phase
1644
- /gsd:execute-phase {next} ${GSD_WS} — execute next phase (skip planning)
1645
- ```
1646
-
1647
- Only suggest the commands listed above. Do not invent or hallucinate command names.
1617
+ @~/.claude/gsd-core/references/offer-next.md
1648
1618
  </step>
1649
1619
 
1650
1620
  </process>
@@ -109,7 +109,11 @@ Otherwise: Apply checkpoint-based routing below.
109
109
  | Verify-only | B (segmented) | Segments between checkpoints. After none/human-verify → SUBAGENT. After decision/human-action → MAIN |
110
110
  | Decision | C (main) | Execute entirely in main context |
111
111
 
112
- **Pattern A:** init_agent_tracking → capture `EXPECTED_BASE=$(git rev-parse HEAD)` → print `Spawning executor agent (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` → spawn Agent(subagent_type="gsd-executor", model=executor_model) with prompt: execute plan at [path], autonomous, all tasks + SUMMARY + commit, follow deviation/auth rules, report: plan name, tasks, SUMMARY path, commit hash → track agent_id → wait → update tracking → report. **Include `isolation="worktree"` only if `workflow.use_worktrees` is not `false`** (read via `config-get workflow.use_worktrees`). **When using `isolation="worktree"`, embed the `<worktree_branch_check>` block from `gsd-core/references/worktree-branch-check.md` into the prompt, substituting `{EXPECTED_BASE}` with the captured base SHA.** That guard is **verify-only and fail-closed** (#48): it asserts a per-agent `worktree-agent-*` branch and the exact base, forbids `git update-ref` self-recovery (#2924), and on any mismatch prints `FATAL:` and `exit 42` so the orchestrator can recover — the sub-agent never rewrites a worktree it did not create. This supersedes the former self-recovery (#2015), whose destructive base rewrite could fail silently under a deny rule; the base-drift it addressed affects all platforms, and base correction is now the orchestrator's responsibility.
112
+ <!-- #2508 runtime-aware-dispatch -->
113
+
114
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
115
+
116
+ **Pattern A:** init_agent_tracking → capture `EXPECTED_BASE=$(git rev-parse HEAD)` → print `Spawning executor agent (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` → spawn Agent(subagent_type="gsd-executor", model=executor_model) with prompt: execute plan at [path], autonomous, all tasks + SUMMARY + commit, follow deviation/auth rules, report: plan name, tasks, SUMMARY path, commit hash → track agent_id → wait → update tracking → report. **Include `isolation="worktree"` only if `workflow.use_worktrees` is not `false`** (read via `config-get workflow.use_worktrees`). **When using `isolation="worktree"`, embed the `<worktree_branch_check>` block from `gsd-core/references/worktree-branch-check.md` into the prompt, substituting `{EXPECTED_BASE}` with the captured base SHA.** That guard is **verify-only and fail-closed** (#48): it asserts a per-agent `agent-*` / `worktree-agent-*` branch and the exact base, forbids `git update-ref` self-recovery (#2924), and on any mismatch prints `FATAL:` and `exit 42` so the orchestrator can recover — the sub-agent never rewrites a worktree it did not create. This supersedes the former self-recovery (#2015), whose destructive base rewrite could fail silently under a deny rule; the base-drift it addressed affects all platforms, and base correction is now the orchestrator's responsibility.
113
117
 
114
118
  **Pattern B:** Execute segment-by-segment. Autonomous segments: spawn subagent for assigned tasks only (no SUMMARY/commit). Checkpoints: main context. After all segments: aggregate, create SUMMARY, commit. See segment_execution.
115
119
 
@@ -159,9 +163,6 @@ Pattern B only (verify-only checkpoints). Skip for A/C.
159
163
 
160
164
  **Known Claude Code bug (classifyHandoffIfNeeded):** If any segment agent reports "failed" with `classifyHandoffIfNeeded is not defined`, this is a Claude Code runtime bug — not a real failure. Run spot-checks; if they pass, treat as successful.
161
165
 
162
-
163
-
164
-
165
166
  </step>
166
167
 
167
168
  <step name="load_prompt">
@@ -60,6 +60,10 @@ This would take ~30 seconds and might surface useful context.
60
60
 
61
61
  If yes, spawn a research agent:
62
62
 
63
+ <!-- #2508 runtime-aware-dispatch -->
64
+
65
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
66
+
63
67
  Print: `◆ Spawning explorer... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
64
68
  ```
65
69
  Agent(
@@ -186,6 +186,27 @@ The body follows this structure:
186
186
  ```
187
187
  </step>
188
188
 
189
+ <step name="calibrate_estimates">
190
+ Rebuild the estimate-vs-actual calibration from every completed phase (#2632, ADR-2629).
191
+
192
+ ```bash
193
+ gsd_run query estimate-calibrate
194
+ ```
195
+
196
+ This pairs each phase's PLAN `estimate` with its SUMMARY `actuals`, writes
197
+ `.planning/estimation-calibration.json`, and reports the resulting correction factor.
198
+ The planner reads it on the next `/gsd:plan-phase`, so estimates improve for THIS project
199
+ over time.
200
+
201
+ Report the returned `factor`, `sample_count`, and `confidence` in the summary output.
202
+ `applied: false` means fewer than 3 phases carry both an estimate and actuals — that is
203
+ expected early and is not an error. The verb rebuilds from scratch each run, so it is safe
204
+ to re-run and never accumulates duplicates.
205
+
206
+ Phases missing either side are skipped rather than guessed: a fabricated sample would
207
+ steer every future estimate.
208
+ </step>
209
+
189
210
  <step name="update_state">
190
211
  Update STATE.md to reflect the learning extraction:
191
212
 
@@ -417,7 +417,7 @@ List pending todos and select one to work on.
417
417
  - Optional area filter (e.g., `/gsd:capture --list api`)
418
418
  - Loads full context for selected todo
419
419
  - Routes to appropriate action (work now, add to phase, brainstorm)
420
- - Moves todo to done/ when work begins
420
+ - Moves todo to completed/ when work begins
421
421
 
422
422
  Usage: `/gsd:capture --list`
423
423
  Usage: `/gsd:capture --list api`
@@ -624,7 +624,7 @@ The commands above cover the most common day-to-day flows. Every command listed
624
624
 
625
625
  - **`/gsd:mvp-phase <phase-number>`** — Plan a phase as a vertical MVP slice (user story + SPIDR splitting) before handing off to plan-phase. Same end-state as `/gsd:plan-phase --mvp`, with a guided MVP-shaping intro.
626
626
  - **`/gsd:ultraplan-phase [phase]`** — [BETA] Offload plan phase to Claude Code's ultraplan cloud; review in browser and import back.
627
- - **`/gsd:plan-review-convergence <phase> [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--all] [--text] [--ws <name>] [--max-cycles N]`** — Cross-AI plan convergence loop — replan with review feedback until no HIGH concerns remain. Supports both cloud reviewers (Codex/Gemini/Claude/OpenCode) and local model runtimes (Ollama, LM Studio, llama.cpp).
627
+ - **`/gsd:plan-review-convergence <phase> [--gemini] [--claude] [--codex] [--coderabbit] [--opencode] [--qwen] [--cursor] [--agy/--antigravity] [--ollama] [--lm-studio] [--llama-cpp] [--kimi-code] [--all] [--text] [--ws <name>] [--max-cycles N]`** — Cross-AI plan convergence loop — replan with review feedback until no HIGH concerns remain. Supports both cloud reviewers (Gemini/Claude/Codex/CodeRabbit/OpenCode/Qwen/Cursor/Antigravity/Kimi Code) and local model runtimes (Ollama, LM Studio, llama.cpp).
628
628
  - **`/gsd:autonomous [--from N] [--to N] [--only N] [--interactive] [--converge]`** — Run all remaining phases autonomously: discuss → plan → execute per phase. `--converge` routes planning through plan-review convergence; `--cross-ai` is an alias.
629
629
 
630
630
  ### Quality, Review & Verification
@@ -688,7 +688,7 @@ These six skills exist primarily for the model to perform two-stage hierarchical
688
688
  ├── config.json # Workflow mode & gates
689
689
  ├── todos/ # Captured ideas and tasks
690
690
  │ ├── pending/ # Todos waiting to be worked on
691
- │ └── done/ # Completed todos
691
+ │ └── completed/ # Completed todos
692
692
  ├── spikes/ # Spike experiments (/gsd:spike)
693
693
  │ ├── MANIFEST.md # Spike inventory and verdicts
694
694
  │ └── NNN-name/ # Individual spike directories
@@ -18,7 +18,6 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
18
18
 
19
19
  **If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
20
20
 
21
-
22
21
  ```
23
22
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
24
23
  GSD ► IMPORT
@@ -205,6 +204,10 @@ Delegate validation to gsd-plan-checker:
205
204
 
206
205
  Print: "Delegating to gsd-plan-checker (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)"
207
206
 
207
+ <!-- #2508 runtime-aware-dispatch -->
208
+
209
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
210
+
208
211
  ```
209
212
  Agent({
210
213
  subagent_type: "gsd-plan-checker",
@@ -188,6 +188,10 @@ Collect the one-line confirmations from each classifier. If any classifier error
188
188
 
189
189
  Spawn `gsd-doc-synthesizer` once (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
190
190
 
191
+ <!-- #2508 runtime-aware-dispatch -->
192
+
193
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
194
+
191
195
  ```
192
196
  Agent({
193
197
  subagent_type: "gsd-doc-synthesizer",
@@ -140,6 +140,14 @@ Before spawning agents, detect whether the current runtime supports the `Agent`
140
140
  <step name="spawn_agents" condition="Agent tool is available">
141
141
  Spawn 4 parallel gsd-codebase-mapper agents.
142
142
 
143
+ <!-- #2508 runtime-aware-dispatch -->
144
+
145
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
146
+
147
+ <!-- #2517 model-omit-on-inherit -->
148
+
149
+ > **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`mapper_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
150
+
143
151
  Use Agent tool with `subagent_type="gsd-codebase-mapper"`, `model="{mapper_model}"`, and `run_in_background=true` for parallel execution.
144
152
 
145
153
  **CRITICAL:** Use the dedicated `gsd-codebase-mapper` agent, NOT `Explore` or `browser_subagent`. The mapper agent writes documents directly.
@@ -163,7 +171,7 @@ Write these documents to {codebase_dir}/:
163
171
  - STACK.md - Languages, runtime, frameworks, dependencies, configuration
164
172
  - INTEGRATIONS.md - External APIs, databases, auth providers, webhooks
165
173
 
166
- IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
174
+ IMPORTANT: Set all date stamps (`**Analysis Date:**`, footer `*... analysis: ...*`, `<!-- refreshed: ... -->`) to {date}, overwriting any existing date.
167
175
 
168
176
  Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
169
177
 
@@ -189,7 +197,7 @@ Write these documents to {codebase_dir}/:
189
197
  - ARCHITECTURE.md - Pattern, layers, data flow, abstractions, entry points
190
198
  - STRUCTURE.md - Directory layout, key locations, naming conventions
191
199
 
192
- IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
200
+ IMPORTANT: Set all date stamps (`**Analysis Date:**`, footer `*... analysis: ...*`, `<!-- refreshed: ... -->`) to {date}, overwriting any existing date.
193
201
 
194
202
  Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
195
203
 
@@ -215,7 +223,7 @@ Write these documents to {codebase_dir}/:
215
223
  - CONVENTIONS.md - Code style, naming, patterns, error handling
216
224
  - TESTING.md - Framework, structure, mocking, coverage
217
225
 
218
- IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
226
+ IMPORTANT: Set all date stamps (`**Analysis Date:**`, footer `*... analysis: ...*`, `<!-- refreshed: ... -->`) to {date}, overwriting any existing date.
219
227
 
220
228
  Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
221
229
 
@@ -240,7 +248,7 @@ Analyze this codebase for technical debt, known issues, and areas of concern.
240
248
  Write this document to {codebase_dir}/:
241
249
  - CONCERNS.md - Tech debt, bugs, security, performance, fragile areas
242
250
 
243
- IMPORTANT: Use {date} for all [YYYY-MM-DD] date placeholders in documents.
251
+ IMPORTANT: Set all date stamps (`**Analysis Date:**`, footer `*... analysis: ...*`, `<!-- refreshed: ... -->`) to {date}, overwriting any existing date.
244
252
 
245
253
  Scope: ${PATH_SCOPE_HINT:-(full repo)} — when --paths is supplied, restrict exploration to those prefixes only.
246
254
 
@@ -293,7 +301,7 @@ When the `Agent` tool is unavailable, perform codebase mapping sequentially in t
293
301
 
294
302
  **IMPORTANT:** Do NOT use `browser_subagent`, `Explore`, or any browser-based tool. Use only file system tools (Read, Bash, Write, Grep, Glob, list_dir, view_file, grep_search, or equivalent tools available in your runtime).
295
303
 
296
- **IMPORTANT:** Use `{date}` from init context for all `[YYYY-MM-DD]` date placeholders in documents. NEVER guess the date.
304
+ **IMPORTANT:** Set all date stamps (`**Analysis Date:**`, footer, `<!-- refreshed -->`) to `{date}` from init context, overwriting any existing date — Update runs seed from files with concrete prior dates, so merely replacing `[YYYY-MM-DD]` placeholders is not sufficient. NEVER guess the date.
297
305
 
298
306
  **SCOPE:** When `${PATH_SCOPE_HINT}` is non-empty (i.e. `--paths` was supplied), restrict every pass below to the validated path prefixes in `${SCOPED_PATHS}`. Do NOT scan files outside those prefixes. When `${PATH_SCOPE_HINT}` is empty, perform a full-repo scan.
299
307
 
@@ -407,7 +415,6 @@ Created .planning/codebase/:
407
415
  - INTEGRATIONS.md ([N] lines) - External services and APIs
408
416
  - CONCERNS.md ([N] lines) - Technical debt and issues
409
417
 
410
-
411
418
  ---
412
419
 
413
420
  ## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
@@ -352,6 +352,10 @@ mkdir -p .planning/research
352
352
  Spawn 4 parallel gsd-project-researcher agents. Each uses this template with dimension-specific fields:
353
353
 
354
354
  **Common structure for all 4 researchers:**
355
+ <!-- #2517 model-omit-on-inherit -->
356
+
357
+ > **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`researcher_model`, `synthesizer_model`, `roadmapper_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
358
+
355
359
  ```text
356
360
  Agent(prompt="
357
361
  <research_type>Project Research — {DIMENSION} for [new features].</research_type>
@@ -374,6 +378,10 @@ ${AGENT_SKILLS_RESEARCHER}
374
378
 
375
379
  <quality_gate>{GATES}</quality_gate>
376
380
 
381
+ <!-- #2508 runtime-aware-dispatch -->
382
+
383
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
384
+
377
385
  <output>
378
386
  Write to: {research_dir}/{FILE}
379
387
  Use template: ~/.claude/gsd-core/templates/research-project/{FILE}
@@ -418,8 +426,8 @@ Commit after writing.
418
426
 
419
427
  **Synthesizer output self-heal (#222) — verify SUMMARY.md materialized:** The synthesizer's canonical output is `.planning/research/SUMMARY.md` on disk; its brief structured return (`## SYNTHESIS COMPLETE` plus a few `###` confirmation lines) is NOT the file content. A known LLM false-refusal (issue #222) sometimes makes the agent return the full SUMMARY.md document inline — fabricating a write restriction (e.g. "the runtime is blocking file writes") — instead of writing the file. Prompt hardening alone does not fully eliminate it, so the orchestrator MUST absorb the failure deterministically before spawning `gsd-roadmapper`:
420
428
 
421
- 1. Verify `.planning/research/SUMMARY.md` exists AND is substantive — non-empty, and free of any leftover `<!-- gsd:write-continue -->` continuation sentinel (which marks a truncated/incomplete write). You may validate with `gsd-tools verify-summary .planning/research/SUMMARY.md` — it exits 0 regardless, so check its JSON `passed` field (`"passed": false` means missing or invalid), not the process exit code. If it passes, continue normally.
422
- 2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `gsd-tools query commit "docs: complete project research" --files .planning/research/` unless they are already committed. Log `⚠ #222 self-heal: synthesizer returned SUMMARY.md inline without writing it; orchestrator persisted the file.`
429
+ 1. Verify `.planning/research/SUMMARY.md` exists AND is substantive — non-empty, and free of any leftover `<!-- gsd:write-continue -->` continuation sentinel (which marks a truncated/incomplete write). You may validate with `gsd_run verify-summary .planning/research/SUMMARY.md` — it exits 0 regardless, so check its JSON `passed` field (`"passed": false` means missing or invalid), not the process exit code. If it passes, continue normally.
430
+ 2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `gsd_run query commit "docs: complete project research" --files .planning/research/` unless they are already committed. Log `⚠ #222 self-heal: synthesizer returned SUMMARY.md inline without writing it; orchestrator persisted the file.`
423
431
  3. If it is MISSING or invalid AND the return is only a brief confirmation (no full SUMMARY document to recover), the synthesizer genuinely failed — surface the error and stop; do NOT spawn `gsd-roadmapper` against a missing or incomplete SUMMARY.md.
424
432
 
425
433
  This guarantees `gsd-roadmapper` (which lists SUMMARY.md as required reading) never runs against a missing or truncated SUMMARY.md.
@@ -111,7 +111,7 @@ elif [ -n "$OPENCODE_CONFIG_DIR" ] || [ -n "$OPENCODE_CONFIG" ]; then RUNTIME="o
111
111
  else RUNTIME="claude"; fi
112
112
  ```
113
113
 
114
- Set the instruction file variable via the shared runtime-name policy adapter (`gsd-tools query project-instruction-file`, backed by `getProjectInstructionFile` in `runtime-name-policy.cjs` — the single source of truth shared with `profile-output.cjs`):
114
+ Set the instruction file variable via the shared runtime-name policy adapter (`gsd_run query project-instruction-file`, backed by `getProjectInstructionFile` in `runtime-name-policy.cjs` — the single source of truth shared with `profile-output.cjs`):
115
115
  ```bash
116
116
  INSTRUCTION_FILE=$(gsd_run query project-instruction-file --runtime "$RUNTIME")
117
117
  ```
@@ -132,7 +132,6 @@ All subsequent references to the project instruction file use `$INSTRUCTION_FILE
132
132
 
133
133
  **If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
134
134
 
135
-
136
135
  **Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
137
136
  Use AskUserQuestion:
138
137
 
@@ -946,6 +945,10 @@ Display spawning indicator:
946
945
 
947
946
  Spawn 4 parallel gsd-project-researcher agents with path references:
948
947
 
948
+ <!-- #2517 model-omit-on-inherit -->
949
+
950
+ > **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`researcher_model`, `synthesizer_model`, `roadmapper_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
951
+
949
952
  ```text
950
953
  Agent(prompt="<research_type>
951
954
  Project Research — Stack dimension for [domain].
@@ -981,6 +984,10 @@ Your STACK.md feeds into roadmap creation. Be prescriptive:
981
984
  - [ ] Confidence levels assigned to each recommendation
982
985
  </quality_gate>
983
986
 
987
+ <!-- #2508 runtime-aware-dispatch -->
988
+
989
+ > **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
990
+
984
991
  <output>
985
992
  Write to: {research_dir}/STACK.md
986
993
  Use template: ~/.claude/gsd-core/templates/research-project/STACK.md
@@ -1139,8 +1146,8 @@ Commit after writing.
1139
1146
 
1140
1147
  **Synthesizer output self-heal (#222) — verify SUMMARY.md materialized:** The synthesizer's canonical output is `.planning/research/SUMMARY.md` on disk; its brief structured return (`## SYNTHESIS COMPLETE` plus a few `###` confirmation lines) is NOT the file content. A known LLM false-refusal (issue #222) sometimes makes the agent return the full SUMMARY.md document inline — fabricating a write restriction (e.g. "the runtime is blocking file writes") — instead of writing the file. Prompt hardening alone does not fully eliminate it, so the orchestrator MUST absorb the failure deterministically before spawning `gsd-roadmapper`:
1141
1148
 
1142
- 1. Verify `.planning/research/SUMMARY.md` exists AND is substantive — non-empty, and free of any leftover `<!-- gsd:write-continue -->` continuation sentinel (which marks a truncated/incomplete write). You may validate with `gsd-tools verify-summary .planning/research/SUMMARY.md` — it exits 0 regardless, so check its JSON `passed` field (`"passed": false` means missing or invalid), not the process exit code. If it passes, continue normally.
1143
- 2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `gsd-tools query commit "docs: complete project research" --files .planning/research/` unless they are already committed. Log `⚠ #222 self-heal: synthesizer returned SUMMARY.md inline without writing it; orchestrator persisted the file.`
1149
+ 1. Verify `.planning/research/SUMMARY.md` exists AND is substantive — non-empty, and free of any leftover `<!-- gsd:write-continue -->` continuation sentinel (which marks a truncated/incomplete write). You may validate with `gsd_run verify-summary .planning/research/SUMMARY.md` — it exits 0 regardless, so check its JSON `passed` field (`"passed": false` means missing or invalid), not the process exit code. If it passes, continue normally.
1150
+ 2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `gsd_run query commit "docs: complete project research" --files .planning/research/` unless they are already committed. Log `⚠ #222 self-heal: synthesizer returned SUMMARY.md inline without writing it; orchestrator persisted the file.`
1144
1151
  3. If it is MISSING or invalid AND the return is only a brief confirmation (no full SUMMARY document to recover), the synthesizer genuinely failed — surface the error and stop; do NOT spawn `gsd-roadmapper` against a missing or incomplete SUMMARY.md.
1145
1152
 
1146
1153
  This guarantees `gsd-roadmapper` (which lists SUMMARY.md as required reading) never runs against a missing or truncated SUMMARY.md.