@opengsd/gsd-core 1.8.0 → 1.9.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 (174) 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 +1 -1
  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 +186 -55
  15. package/commands/gsd/plan-review-convergence.md +5 -1
  16. package/gsd-core/bin/gsd-tools.cjs +849 -2
  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 +5 -5
  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 +57 -5
  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/review-lane-descriptor.cjs +927 -0
  49. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  50. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  51. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  52. package/gsd-core/bin/lib/roadmap-parser.cjs +54 -6
  53. package/gsd-core/bin/lib/roadmap.cjs +10 -4
  54. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +31 -4
  55. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -1
  56. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +140 -0
  57. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  58. package/gsd-core/bin/lib/smart-entry.cjs +1 -1
  59. package/gsd-core/bin/lib/state-document.cjs +164 -20
  60. package/gsd-core/bin/lib/state-transition.cjs +28 -10
  61. package/gsd-core/bin/lib/state.cjs +141 -21
  62. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  63. package/gsd-core/bin/lib/uat.cjs +9 -7
  64. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  65. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  66. package/gsd-core/bin/lib/validate.cjs +32 -0
  67. package/gsd-core/bin/lib/verification.cjs +51 -14
  68. package/gsd-core/bin/lib/verify.cjs +128 -20
  69. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  70. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  71. package/gsd-core/bin/shared/config-schema.manifest.json +1 -13
  72. package/gsd-core/bin/shared/model-catalog.json +5 -0
  73. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  74. package/gsd-core/references/context-budget.md +40 -0
  75. package/gsd-core/references/gate-prompts.md +6 -3
  76. package/gsd-core/references/model-profile-resolution.md +64 -13
  77. package/gsd-core/references/offer-next.md +88 -0
  78. package/gsd-core/references/planning-config.md +2 -1
  79. package/gsd-core/references/reviewer-instances.md +28 -21
  80. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  81. package/gsd-core/references/ui-consideration-probe.md +2 -2
  82. package/gsd-core/references/worktree-branch-check.md +4 -4
  83. package/gsd-core/templates/summary-minimal.md +4 -0
  84. package/gsd-core/templates/summary-standard.md +4 -0
  85. package/gsd-core/templates/summary.md +7 -0
  86. package/gsd-core/workflows/ai-integration-phase.md +4 -4
  87. package/gsd-core/workflows/audit-fix.md +4 -0
  88. package/gsd-core/workflows/audit-milestone.md +8 -0
  89. package/gsd-core/workflows/autonomous.md +19 -15
  90. package/gsd-core/workflows/check-todos.md +2 -2
  91. package/gsd-core/workflows/code-review-fix.md +14 -6
  92. package/gsd-core/workflows/code-review.md +76 -19
  93. package/gsd-core/workflows/debug.md +10 -2
  94. package/gsd-core/workflows/diagnose-issues.md +4 -0
  95. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  96. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  97. package/gsd-core/workflows/discuss-phase-assumptions.md +15 -9
  98. package/gsd-core/workflows/discuss-phase.md +2 -2
  99. package/gsd-core/workflows/docs-update.md +8 -0
  100. package/gsd-core/workflows/eval-review.md +1 -1
  101. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  102. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  103. package/gsd-core/workflows/execute-phase.md +85 -115
  104. package/gsd-core/workflows/execute-plan.md +5 -4
  105. package/gsd-core/workflows/explore.md +4 -0
  106. package/gsd-core/workflows/extract-learnings.md +21 -0
  107. package/gsd-core/workflows/help/modes/full.md +3 -3
  108. package/gsd-core/workflows/import.md +4 -1
  109. package/gsd-core/workflows/ingest-docs.md +4 -0
  110. package/gsd-core/workflows/map-codebase.md +13 -6
  111. package/gsd-core/workflows/new-milestone.md +10 -2
  112. package/gsd-core/workflows/new-project.md +11 -4
  113. package/gsd-core/workflows/next.md +5 -2
  114. package/gsd-core/workflows/plan-phase.md +42 -46
  115. package/gsd-core/workflows/plan-review-convergence.md +18 -14
  116. package/gsd-core/workflows/progress.md +1 -1
  117. package/gsd-core/workflows/quick.md +14 -3
  118. package/gsd-core/workflows/review.md +146 -575
  119. package/gsd-core/workflows/scan.md +9 -1
  120. package/gsd-core/workflows/secure-phase.md +10 -2
  121. package/gsd-core/workflows/ship.md +41 -11
  122. package/gsd-core/workflows/smart-entry.md +1 -1
  123. package/gsd-core/workflows/ui-phase.md +8 -1
  124. package/gsd-core/workflows/ui-review.md +8 -1
  125. package/gsd-core/workflows/update.md +104 -5
  126. package/gsd-core/workflows/validate-phase.md +10 -2
  127. package/gsd-core/workflows/verify-work.md +8 -1
  128. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  129. package/hooks/dist/gsd-cursor-stop.js +6 -2
  130. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  131. package/hooks/dist/gsd-graphify-update.sh +9 -0
  132. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  133. package/hooks/dist/gsd-prompt-guard.js +101 -2
  134. package/hooks/dist/gsd-read-guard.js +100 -2
  135. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  136. package/hooks/dist/gsd-statusline.js +9 -6
  137. package/hooks/dist/gsd-workflow-guard.js +110 -6
  138. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  139. package/hooks/dist/lib/cursor-workspace.js +74 -0
  140. package/hooks/gsd-cursor-session-start.js +6 -2
  141. package/hooks/gsd-cursor-stop.js +6 -2
  142. package/hooks/gsd-cursor-subagent-start.js +6 -2
  143. package/hooks/gsd-graphify-update.sh +9 -0
  144. package/hooks/gsd-phase-boundary.sh +14 -2
  145. package/hooks/gsd-prompt-guard.js +101 -2
  146. package/hooks/gsd-read-guard.js +100 -2
  147. package/hooks/gsd-read-injection-scanner.js +109 -2
  148. package/hooks/gsd-statusline.js +9 -6
  149. package/hooks/gsd-workflow-guard.js +110 -6
  150. package/hooks/gsd-worktree-path-guard.js +132 -8
  151. package/hooks/lib/cursor-workspace.js +74 -0
  152. package/package.json +7 -7
  153. package/pi/gsd.cjs +26 -1
  154. package/scripts/check-coverage-gate.cjs +51 -0
  155. package/scripts/check-glossary-refs.cjs +24 -0
  156. package/scripts/ci-test-scope.cjs +67 -17
  157. package/scripts/gen-adr-index.cjs +6 -4
  158. package/scripts/gen-capability-matrix.cjs +26 -2
  159. package/scripts/gen-capability-registry.cjs +132 -34
  160. package/scripts/gen-emitted-baseline.cjs +145 -0
  161. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  162. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  163. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  164. package/scripts/lint-resolution-provenance.cjs +9 -0
  165. package/scripts/mutation-matrix.cjs +4 -0
  166. package/scripts/prompt-injection-scan.sh +6 -0
  167. package/scripts/registry-schema.cjs +57 -8
  168. package/scripts/release-notes/conventional-title.cjs +19 -1
  169. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  170. package/scripts/workflow-size.cjs +16 -8
  171. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  172. package/vscode/package.json +1 -1
  173. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  174. package/scripts/update-size-baseline.cjs +0 -68
@@ -0,0 +1,160 @@
1
+ # Executor isolation dispatch (ADR-1239 / #2584 Phase 3)
2
+
3
+ Read and follow this fragment from `execute-phase.md` step 3 when dispatching a wave.
4
+ It owns the per-host dispatch detail so the host workflow stays inside its
5
+ ADR-857 Phase 6 byte budget (#1168) — the host step keeps only the `ISOLATION`
6
+ resolution and its fail-closed guard.
7
+
8
+ ## Resolve ISOLATION
9
+
10
+ Run this in the config-gate step, right after `RUNTIME`/`USE_WORKTREES` are read.
11
+
12
+ ```bash
13
+ _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
14
+ # Isolation is a NEGOTIATED CAPABILITY, not a runtime id (#2584). Fail-closed to none.
15
+ ISOLATION=$(gsd_run query dispatch-isolation --raw 2>/dev/null || echo "none")
16
+ case "$ISOLATION" in
17
+ harness-worktree|orchestrator-worktree|none) ;;
18
+ *) ISOLATION=none ;;
19
+ esac
20
+
21
+ # Project-level opt-out wins on every host; a host with no primitive fails closed.
22
+ [ "$USE_WORKTREES" = "false" ] && ISOLATION=none
23
+ if [ "$ISOLATION" = "none" ] && [ "$USE_WORKTREES" != "false" ]; then
24
+ echo "FATAL: runtime '$RUNTIME' declares no executor-isolation primitive (dispatch.isolation=none) — executors would run unisolated against the main checkout. Set workflow.use_worktrees=false." >&2
25
+ exit 1
26
+ fi
27
+
28
+ # Sweep orphaned locked worktrees from prior crashed sessions (#3707).
29
+ [ "$ISOLATION" != "none" ] && gsd_run query worktree.reap-orphans 2>/dev/null || true
30
+ # Auto-degrade if HEAD diverged from the fork base (#683) — both isolation models.
31
+ if [ "$ISOLATION" != "none" ]; then
32
+ _SHOULD_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || true)
33
+ if [ "$_SHOULD_DEGRADE" = "true" ]; then
34
+ _DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
35
+ [ -n "$_DEGRADE_MSG" ] && printf '%s\n' "$_DEGRADE_MSG" >&2
36
+ USE_WORKTREES=false
37
+ ISOLATION=none
38
+ fi
39
+ fi
40
+ ```
41
+
42
+ `ISOLATION` — not `RUNTIME` — selects how the wave fans out. These three values are the only
43
+ branch points; **never add a `RUNTIME = "codex"` test to the scheduler.** The per-host
44
+ invocation detail is descriptor data, surfaced by `dispatch-isolation --json` as
45
+ `harnessFlag` / `exec`.
46
+
47
+ | `ISOLATION` | Fan-out | What the scheduler does |
48
+ |---|---|---|
49
+ | `harness-worktree` | host-driven | Pass the host's own declared isolation flag (`harnessFlag`) on each executor dispatch and let the harness create + bind the worktree. GSD runs no git. |
50
+ | `orchestrator-worktree` | GSD-driven | GSD creates the worktree (`worktree create`), then process-spawns the executor bound to it via the resolved `exec` argv/cwd. GSD performs all git operations. |
51
+ | `none` | none | Plans run inline, sequentially (unchanged). |
52
+
53
+ Fail-closed is the invariant: an undeclared, unknown, or unresolvable isolation declaration
54
+ degrades to `none`, never to an unsafe parallel path. A `harness-worktree` host with no
55
+ declared flag, and an `orchestrator-worktree` host whose exec descriptor does not resolve,
56
+ both degrade to `none` rather than dispatching executors that only believe they are isolated.
57
+
58
+ ## harness-worktree — pass the host flag
59
+
60
+ Read the flag once before dispatching; it is descriptor data, never hardcoded per runtime:
61
+
62
+ ```bash
63
+ HARNESS_FLAG=$(gsd_run query dispatch-isolation --json 2>/dev/null \
64
+ | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);process.stdout.write(j&&j.harnessFlag?j.harnessFlag:"")}catch{process.stdout.write("")}})')
65
+ [ -n "$HARNESS_FLAG" ] || { echo "FATAL: runtime declares dispatch.isolation=harness-worktree but no harnessIsolationFlag — refusing to dispatch executors that would believe they are isolated." >&2; exit 1; }
66
+ ```
67
+
68
+ Substitute `$HARNESS_FLAG`'s value for the `{harnessFlag}` placeholder in the `Agent()` dispatch
69
+ in `execute-phase.md` step 3 (on Claude Code it is literally `isolation="worktree"`).
70
+
71
+ ## orchestrator-worktree — GSD creates the worktree and spawns the executor
72
+
73
+ The host has no harness-native isolation primitive, so **GSD** creates each worktree and process-spawns the executor into it. Fan-out is OS-level (N processes), not the host's subagent tool. Per the Codex `workspace-write` sandbox constraint, **the orchestrator performs every git operation** — create, merge, cleanup; the spawned executor only edits files and commits inside its own worktree.
74
+
75
+ Run the loop below once per runnable plan in the wave, **one plan at a time** (`git worktree add` races on `.git/config.lock`).
76
+
77
+ **Before running the bash block, substitute the plan's identifiers into it** exactly as you do for the `Agent()` prompt on the harness path: replace `{plan_number}` and `{phase_number}` with this plan's values. They are template placeholders, not shell variables. `$ORCH_ROOT` and `$EXPECTED_BASE` are real shell variables, already assigned earlier in this step; `$WAVE_WORKTREE_MANIFEST` was initialized above.
78
+
79
+ First build the executor prompt. It is the **same prompt text the harness path's `Agent()` call uses**, with the harness-only framing removed — drop the `<worktree_branch_check>` build-time embed note and the `<parallel_execution>` harness block, keep `<objective>`, the execution context, and `<success_criteria>` verbatim. Assign it to a shell variable so it can be passed as one argument:
80
+
81
+ ```bash
82
+ # Compose the executor prompt for THIS plan. Single-quoted multi-line
83
+ # assignment (NOT a heredoc): these blocks are indented inside the workflow,
84
+ # and a heredoc terminator must sit at column 0 — `<<-` strips only tabs, not
85
+ # the leading spaces, so a heredoc here would never terminate. Single quotes
86
+ # also stop the shell expanding anything in the prompt body.
87
+ EXECUTOR_PROMPT='<objective>
88
+ Execute plan {plan_number} of phase {phase_number}-{phase_name}.
89
+ Commit each task atomically. Create SUMMARY.md.
90
+ Do NOT update STATE.md or ROADMAP.md — the orchestrator owns those writes after all worktree agents in the wave complete.
91
+ </objective>
92
+
93
+ <execution_context>
94
+ You are running as an executor in a git worktree GSD created for you. Your
95
+ working directory IS that worktree. Do not cd elsewhere, and do not run any
96
+ git command that targets the main checkout. Use normal git commits WITH hooks.
97
+ Do NOT use --no-verify.
98
+ REQUIRED ORDER: Write SUMMARY.md, commit, then any narration.
99
+ </execution_context>
100
+
101
+ <success_criteria>
102
+ - [ ] All tasks executed
103
+ - [ ] Each task committed individually
104
+ - [ ] SUMMARY.md created AND committed in the plan directory
105
+ </success_criteria>'
106
+ [ -n "$EXECUTOR_PROMPT" ] || { echo "FATAL: executor prompt is empty for plan {plan_number}." >&2; exit 1; }
107
+ ```
108
+
109
+ The prompt body must contain no single-quote character, since the assignment above is single-quoted; keep apostrophes out of it when editing.
110
+
111
+ Then create the worktree and resolve the spawn:
112
+
113
+ ```bash
114
+ # 1. Create the worktree. Bounded, manifest-recorded, fail-closed, and
115
+ # root-confined by the verb itself — never hand-roll `git worktree add`.
116
+ AGENT_ID="agent-p{plan_number}-$(date -u +%s)"
117
+ WT_BRANCH="worktree-${AGENT_ID}"
118
+ WT_PATH="${ORCH_ROOT}/.claude/worktrees/${AGENT_ID}"
119
+ CREATE_JSON=$(gsd_run query worktree.create \
120
+ --manifest "$WAVE_WORKTREE_MANIFEST" \
121
+ --agent-id "$AGENT_ID" \
122
+ --path "$WT_PATH" \
123
+ --branch "$WT_BRANCH" \
124
+ --base "$EXPECTED_BASE" \
125
+ --root "$ORCH_ROOT" 2>&1) || {
126
+ echo "FATAL: worktree create failed for plan {plan_number}: $CREATE_JSON" >&2
127
+ exit 1
128
+ }
129
+
130
+ # 2. Resolve the host's headless-exec argv for that worktree. Descriptor
131
+ # data — command, args, cwd flag and prompt flag all come from the
132
+ # capability descriptor, so no host is named here.
133
+ EXEC_JSON=$(gsd_run query dispatch-isolation --json \
134
+ --cwd-target "$WT_PATH" \
135
+ --prompt "$EXECUTOR_PROMPT")
136
+
137
+ # 3. MANDATORY fail-closed check. `dispatch-isolation` degrades to
138
+ # isolation:"none" / exec:null rather than exiting non-zero, so the
139
+ # command substitution above ALWAYS "succeeds" — the exit code proves
140
+ # nothing. A worktree already exists at this point (step 1 is a real side
141
+ # effect), so an unusable exec must NOT be spawned and must NOT be left
142
+ # behind as an orphan: tear it down through the manifest-scoped cleanup
143
+ # and halt rather than silently running the wave unisolated.
144
+ EXEC_OK=$(printf '%s' "$EXEC_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);process.stdout.write(j&&j.isolation==="orchestrator-worktree"&&j.exec&&j.exec.command?"true":"false")}catch{process.stdout.write("false")}})')
145
+ if [ "$EXEC_OK" != "true" ]; then
146
+ echo "FATAL: could not resolve an orchestrator-exec invocation for plan {plan_number} after its worktree was created. The wave is halted rather than run unisolated. Retained for inspection: $WT_PATH (branch $WT_BRANCH, recorded in $WAVE_WORKTREE_MANIFEST) — run 'gsd_run query worktree.cleanup-wave --manifest \"$WAVE_WORKTREE_MANIFEST\"' to merge/clean it." >&2
147
+ exit 1
148
+ fi
149
+ ```
150
+
151
+ `worktree create` records the entry in `$WAVE_WORKTREE_MANIFEST` itself, so **do not** call `worktree.record-agent` for these plans — that verb is the harness-path counterpart, used because the harness creates the worktree behind GSD's back. Double-recording is deduped by path+branch, but the create verb is the single writer here.
152
+
153
+ Spawn `EXEC_JSON`'s `command` + `args` as a background process with its working directory set to `EXEC_JSON.cwd`. The `cwd` is returned for **every** host, including those whose descriptor has no cwd flag (`cwdFlag: null`) and therefore bind through the process's own working directory — always set it, never assume the flag did the job. Wait for all spawned executors in the wave before merging.
154
+
155
+ The executor never touches `STATE.md`/`ROADMAP.md`, and that guard needs no new code — `execute-plan` auto-detects worktree mode via the `IS_WORKTREE` (`.git`-is-a-file) primitive, which a GSD-created worktree trips identically to a harness-created one.
156
+
157
+ Merge-back, validation, and cleanup are the **existing** gauntlet, unchanged: the serialized `worktree.cleanup-wave` merge loop that stops the wave and retains the worktree on conflict, and manifest-only cleanup (never glob-inferred). Because the manifest shape is identical, the orchestrator path reuses it verbatim.
158
+
159
+ > **Declared-scope conformance (#2596):** ADR-1239 specifies that *both* isolation adapters route their merge through a check that each plan branch's committed diff stayed inside its declared `files_modified` scope. That check does not exist yet for either adapter (it is tracked as #2596). When it lands it must be wired into this path **and** the harness path together.
160
+
@@ -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",