@opengsd/gsd-core 1.10.0 → 1.12.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 (544) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -10,7 +10,7 @@ const capabilities = {
10
10
  "ai-integration": {
11
11
  "id": "ai-integration",
12
12
  "role": "feature",
13
- "version": "1.10.0",
13
+ "version": "1.12.0",
14
14
  "title": "AI design contract",
15
15
  "description": "AI-SPEC design contract workflow for phases that build AI systems; owns the AI integration command, agents, and workflow.ai_integration_phase activation key.",
16
16
  "tier": "full",
@@ -68,7 +68,7 @@ const capabilities = {
68
68
  "into": "planner",
69
69
  "fragment": {
70
70
  "path": "fragments/api-coverage-plan-pre.md",
71
- "inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[]}')\n```\n\nRead `API_COVERAGE_JSON.detected`. Act on it only — do **not** pattern-match the\nprose yourself.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
71
+ "inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null) || true\n[ -n \"$API_COVERAGE_JSON\" ] || API_COVERAGE_JSON='{\"skipped\":true,\"reason\":\"probe_unavailable\"}'\n```\n\nThe `|| true` neutralizes the assignment's status without discarding the\ndetector's own payload: the detector exits **1** for a real \"no integration\"\nverdict, so treating any non-zero exit as failure would throw away a correct\nanswer. Emptiness — not exit status — is what proves the probe never ran, and\nthe second line is the only place the fragment manufactures a payload of its\nown — one that records the *absence* of a verdict rather than asserting one.\n\nThe detector's exit code and `--json` payload now distinguish a real negative\nfrom an unexamined input (ADR-3889 Phase 3, #3907): empty/whitespace-only\n`$SCOPE` or a stdin read failure emit `{\"skipped\":true,\"reason\":\"no_input\"|\n\"stdin_error\"}` — no `detected` key at all. **Check for `skipped` before\nreading `detected`**: a `skipped` payload is not a confirmed \"no API\nintegration\" verdict, it means the detector never examined real input. Do not\ntreat it as `detected:false`. Read `API_COVERAGE_JSON.detected` only when\n`skipped` is absent — act on it only, do **not** pattern-match the prose\nyourself.\n\n**If `skipped` is `true`:** the detector could not establish a scope (empty\n`$SCOPE`) or failed to run (stdin read error). Skip the checkpoint for this\nrun rather than asserting a verdict about input that was never examined; do\nnot raise it with the user.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
72
72
  },
73
73
  "produces": [
74
74
  "COVERAGE.md"
@@ -95,9 +95,9 @@ const capabilities = {
95
95
  "antigravity": {
96
96
  "id": "antigravity",
97
97
  "role": "runtime",
98
- "version": "1.10.0",
98
+ "version": "1.12.0",
99
99
  "title": "Antigravity",
100
- "description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat skill layout; tier-1 support.",
100
+ "description": "Google Antigravity IDE — config/settings home nested under ~/.gemini/antigravity (probed across 1.x and 2.x layouts); global skills/agents install under ~/.gemini/config, the dir AGY scans for global discovery (#3738); Gemini hook event dialect; flat skill layout; tier-1 support.",
101
101
  "tier": "core",
102
102
  "requires": [],
103
103
  "engines": {
@@ -128,7 +128,8 @@ const capabilities = {
128
128
  "prefix": "gsd-",
129
129
  "nesting": "flat",
130
130
  "recursive": false,
131
- "converter": "convertClaudeCommandToAntigravitySkill"
131
+ "converter": "convertClaudeCommandToAntigravitySkill",
132
+ "home": ".gemini/config"
132
133
  },
133
134
  {
134
135
  "kind": "agents",
@@ -136,7 +137,8 @@ const capabilities = {
136
137
  "prefix": "gsd-",
137
138
  "nesting": "flat",
138
139
  "recursive": false,
139
- "converter": "convertClaudeAgentToAntigravityAgent"
140
+ "converter": "convertClaudeAgentToAntigravityAgent",
141
+ "home": ".gemini/config"
140
142
  }
141
143
  ],
142
144
  "local": [
@@ -158,6 +160,10 @@ const capabilities = {
158
160
  }
159
161
  ]
160
162
  },
163
+ "triggerPrecedence": [
164
+ "skills",
165
+ "commands"
166
+ ],
161
167
  "commandStyle": "slash-hyphen",
162
168
  "hooksSurface": "settings-json",
163
169
  "hookEvents": "gemini",
@@ -187,7 +193,6 @@ const capabilities = {
187
193
  "effortSurface": "undocumented"
188
194
  },
189
195
  "hostBehaviors": {
190
- "reviewerCli": true,
191
196
  "projectInstructionFile": "GEMINI.md",
192
197
  "noPathRewrite": true,
193
198
  "hookPathStyle": "raw",
@@ -224,7 +229,7 @@ const capabilities = {
224
229
  "reviewsSection": "Antigravity",
225
230
  "evidenceClass": "source-grounded",
226
231
  "requiresBinaries": [],
227
- "promptBudgetKey": null,
232
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.antigravity",
228
233
  "modelConfigKey": "review.models.agy",
229
234
  "handler": "antigravity"
230
235
  },
@@ -233,13 +238,18 @@ const capabilities = {
233
238
  "type": "string",
234
239
  "default": "",
235
240
  "description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
241
+ },
242
+ "review.max_prompt_tokens_per_reviewer.antigravity": {
243
+ "type": "number",
244
+ "default": -1,
245
+ "description": "Prompt-token budget for the Antigravity reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\". Keyed on the reviewer slug `antigravity`, not the `agy` binary alias used by review.models.agy."
236
246
  }
237
247
  }
238
248
  },
239
249
  "assumption-delta": {
240
250
  "id": "assumption-delta",
241
251
  "role": "feature",
242
- "version": "1.10.0",
252
+ "version": "1.12.0",
243
253
  "title": "Assumption-delta architecture checkpoint",
244
254
  "description": "Rarely-firing advisory checkpoint that triggers when a phase makes something plural, optional, or chosen that used to be singular, required, or derived. Surfaces one identity-model question (promote the new general representation to primary, or add it alongside?) so a silent primary-key drift does not accumulate into a later user-facing bug. Non-blocking; fires only on a detected signal.",
245
255
  "tier": "full",
@@ -270,7 +280,7 @@ const capabilities = {
270
280
  "into": "planner",
271
281
  "fragment": {
272
282
  "path": "fragments/plan-pre.md",
273
- "inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[],\"terms\":{}}')\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase), the query emits `{ \"detected\": false, ... }` — the checkpoint does not fire. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
283
+ "inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null) || true\n[ -n \"$ASSUMPTION_DELTA_JSON\" ] || ASSUMPTION_DELTA_JSON='{\"skipped\":true,\"reason\":\"probe_unavailable\"}'\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase, or a section with no body), the query emits `{ \"skipped\": true, \"reason\": \"phase_unresolved\" }` — **not** `detected:false`. A probe that never had input does not get to assert that this phase changes no core assumption. The checkpoint does not fire either way; the difference is that a skip is now distinguishable from a real negative. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `skipped` is `true`:** the detector never examined a phase section — it could not resolve one (`phase_unresolved`) or could not run at all (`probe_unavailable`). Skip the checkpoint for this run rather than asserting a verdict about input that was never examined; do not raise it with the user. **Check for `skipped` before reading `detected`** — a skipped payload carries no `detected` key, and treating its absence as `false` re-creates the fabrication this branch exists to prevent.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
274
284
  },
275
285
  "produces": [],
276
286
  "consumes": [
@@ -285,7 +295,7 @@ const capabilities = {
285
295
  "audit": {
286
296
  "id": "audit",
287
297
  "role": "feature",
288
- "version": "1.10.0",
298
+ "version": "1.12.0",
289
299
  "title": "Audit",
290
300
  "description": "Open-artifact audit and UAT-gap audit for milestone close gates; exposes `gsd-tools audit-uat` (cross-phase UAT outstanding items) and `gsd-tools audit-open` (structured open-artifact scan across debug, tasks, threads, todos, seeds, UAT, verification, context-questions).",
291
301
  "tier": "full",
@@ -322,7 +332,7 @@ const capabilities = {
322
332
  "augment": {
323
333
  "id": "augment",
324
334
  "role": "runtime",
325
- "version": "1.10.0",
335
+ "version": "1.12.0",
326
336
  "title": "Augment Code",
327
337
  "description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
328
338
  "tier": "core",
@@ -394,6 +404,10 @@ const capabilities = {
394
404
  }
395
405
  ]
396
406
  },
407
+ "triggerPrecedence": [
408
+ "skills",
409
+ "commands"
410
+ ],
397
411
  "commandStyle": "slash-hyphen",
398
412
  "hooksSurface": "settings-json",
399
413
  "hookEvents": "claude",
@@ -431,7 +445,7 @@ const capabilities = {
431
445
  "broken-windows": {
432
446
  "id": "broken-windows",
433
447
  "role": "feature",
434
- "version": "1.10.0",
448
+ "version": "1.12.0",
435
449
  "title": "Broken-windows ledger",
436
450
  "description": "Cross-phase defect register accumulating stubs, TODOs, skipped tests, unrun verifies, and unmet truths into .planning/WINDOWS.md. When enforcement is enabled, it blocks /gsd-ship while any window is open unless explicitly waived with a recorded reason. Operationalizes GSD's no-defer discipline as a tracked artifact (issue #1950).",
437
451
  "tier": "full",
@@ -477,7 +491,7 @@ const capabilities = {
477
491
  "claude": {
478
492
  "id": "claude",
479
493
  "role": "runtime",
480
- "version": "1.10.0",
494
+ "version": "1.12.0",
481
495
  "title": "Claude Code",
482
496
  "description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
483
497
  "tier": "core",
@@ -504,6 +518,14 @@ const capabilities = {
504
518
  "nesting": "flat",
505
519
  "recursive": false,
506
520
  "converter": "convertClaudeCommandToClaudeSkill"
521
+ },
522
+ {
523
+ "kind": "agents",
524
+ "destSubpath": "agents",
525
+ "prefix": "gsd-",
526
+ "nesting": "flat",
527
+ "recursive": false,
528
+ "converter": null
507
529
  }
508
530
  ],
509
531
  "local": [
@@ -525,6 +547,10 @@ const capabilities = {
525
547
  }
526
548
  ]
527
549
  },
550
+ "triggerPrecedence": [
551
+ "skills",
552
+ "commands"
553
+ ],
528
554
  "commandStyle": "slash-hyphen",
529
555
  "hooksSurface": "settings-json",
530
556
  "hookEvents": "claude",
@@ -577,8 +603,7 @@ const capabilities = {
577
603
  "skillsGlobalOnboarding": true,
578
604
  "legacyCommandsGsdInstallMigration": true,
579
605
  "legacyCommandsGsdUninstall": "global",
580
- "hyphenNameAgentBody": true,
581
- "reviewerCli": true
606
+ "hyphenNameAgentBody": true
582
607
  }
583
608
  },
584
609
  "reviewer": {
@@ -602,14 +627,18 @@ const capabilities = {
602
627
  "promptChannel": "stdin",
603
628
  "outputChannel": "stdout",
604
629
  "modelArg": "--model",
605
- "effortChannel": "argv"
630
+ "effortChannel": "argv",
631
+ "env": {
632
+ "CLAUDE_CODE_DISABLE_CLAUDE_MDS": "1",
633
+ "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1"
634
+ }
606
635
  },
607
636
  "timeoutFloorMs": 1200000,
608
637
  "emptyOutput": "stub-with-stderr",
609
638
  "reviewsSection": "Claude",
610
639
  "evidenceClass": "source-grounded",
611
640
  "requiresBinaries": [],
612
- "promptBudgetKey": null,
641
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.claude",
613
642
  "modelConfigKey": "review.models.claude",
614
643
  "handler": null
615
644
  },
@@ -618,13 +647,18 @@ const capabilities = {
618
647
  "type": "string",
619
648
  "default": "",
620
649
  "description": "Model passed to the Claude reviewer lane."
650
+ },
651
+ "review.max_prompt_tokens_per_reviewer.claude": {
652
+ "type": "number",
653
+ "default": -1,
654
+ "description": "Prompt-token budget for the Claude reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
621
655
  }
622
656
  }
623
657
  },
624
658
  "claude-orchestration": {
625
659
  "id": "claude-orchestration",
626
660
  "role": "feature",
627
- "version": "1.10.0",
661
+ "version": "1.12.0",
628
662
  "title": "Claude orchestration (Workflow backend)",
629
663
  "description": "Default-off, BETA, claude-only capability that adopts Claude Code's Workflow tool (the engine behind /effort ultracode) as an optional parallel-execution backend for the GSD loop. When the runtime exposes the Workflow tool and claude_orchestration.execution_backend resolves to 'workflow', execute-phase emits a generated Workflow script (waves -> parallel() barriers, plans -> agent({ agentType: 'gsd-executor', isolation: 'worktree' }), files_modified overlap -> separate sequential stages, resumeFromRunId wired to the phase run id, shared token budget) that composes the SAME gsd-executor agent and worktree isolation the inline path uses, restoring the wave parallelism the #853 backgrounded-agent nesting limitation forces inline on Claude Code. (The plan-checker and verifier remain inline until separately wired — this capability delivers the parallel-execution backend, not those gates.) Also folds the ultraplan plan-offload under one runtime gate (plan:* surface). On any runtime lacking the Workflow tool, or when the capability is disabled, behaviour is byte-identical to today (inline/manual dispatch). Detection + emission live in gsd-core/bin/lib/claude-orchestration.cjs (pure, fail-closed). Mirrors the existing gsd-ultraplan-phase BETA-isolation posture.",
630
664
  "tier": "full",
@@ -683,7 +717,7 @@ const capabilities = {
683
717
  "into": "executor",
684
718
  "fragment": {
685
719
  "path": "fragments/execute-wave-pre.md",
686
- "inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<files_to_read>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\nThe orchestrator still runs steps 4–5.8 (wait for completion, worktree cleanup,\npost-merge gate, tracking update) exactly as it does for inline dispatch — the\nWorkflow backend only replaces HOW agents are spawned for this wave, not what\nhappens after they return.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
720
+ "inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<required_reading>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\n### After the run: manifest bridge into the merge chain (#3302)\n\nThe single Workflow tool call replaces step 3's per-plan `Agent()` loop — which also\nmeans step 3's manifest bookkeeping (creation + per-agent recording) does NOT happen on\nthis path. The orchestrator MUST bridge the run's per-agent results into the SAME\nmanifest-scoped merge chain inline dispatch uses, before steps 4–5.8, which then run\nunchanged:\n\n1. **Create the manifest BEFORE invoking the tool** (this is step 3's creation block,\n which this path skips). When ANY plan in the wave has `use_worktree` not `false`:\n\n ```bash\n if [ -z \"${WAVE_WORKTREE_MANIFEST:-}\" ]; then\n 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)\n # Persist the dispatch-time orchestrator worktree root so wave-cleanup pins back\n # to the orchestrator's OWN worktree (#630), exactly as inline dispatch does.\n ORCH_ROOT=$(git rev-parse --show-toplevel)\n ORCH_ROOT=\"$ORCH_ROOT\" MANIFEST=\"$WAVE_WORKTREE_MANIFEST\" node -e 'const fs=require(\"fs\");fs.writeFileSync(process.env.MANIFEST,JSON.stringify({orchestrator_root:process.env.ORCH_ROOT||null,worktrees:[]})+\"\\n\")'\n export WAVE_WORKTREE_MANIFEST\n fi\n ```\n\n2. **Invoke the Workflow tool with the emitted script and\n `resumeFromRunId: summary.resumeRunId`.** The script top-level `return`s one entry\n per dispatched plan: `{ plan, expects_worktree, metadata }`. `metadata` is that\n plan's executor `<worktree_metadata>` JSON (`{agent_id, worktree_path, branch,\n expected_base}` — captured by the executor itself per\n `agents/gsd-executor.md`), or `null` when the agent's result carried none\n (interrupted agent, resumed-from-cache plan, or a non-worktree plan).\n\n3. **Record every worktree plan** exactly as inline dispatch does at step 3's\n \"After each `Agent()` returns\" — one `worktree.record-agent` per returned entry\n with `expects_worktree: true` and complete metadata:\n\n ```bash\n gsd_run query worktree.record-agent --manifest \"$WAVE_WORKTREE_MANIFEST\" \\\n --agent-id \"<metadata.agent_id>\" --path \"<metadata.worktree_path>\" \\\n --branch \"<metadata.branch>\" --base \"<metadata.expected_base>\" \\\n --files \"<plan files_modified, space-separated>\" \\\n --deletions \"<plan files_deleted, space-separated>\"\n ```\n\n `--deletions` (#3003) carries the plan's declared `files_deleted` so a plan that scoped a file\n removal merges through `cleanup-wave` instead of being blocked. Unlike `--files` it is not\n advisory: omitting it leaves the deletions guard blocking on any deletion at all, so this\n dispatch path must pass it or plans declaring a removal fail to merge here while succeeding on\n the inline path.\n\n The verb's write-strict validation applies as inline: on a non-zero exit or any\n missing field, stop and ask for recovery — do not append an under-populated entry.\n\n4. **HALT on uncapturable metadata — never a silently-empty manifest (#3302).**\n After recording, the manifest must hold one entry per `expects_worktree: true`\n outcome (`summary.worktreePlans` from `resolve-wave-dispatch` is the expected\n count). Any shortfall — a `null` `metadata`, a missing/empty field, or a count\n mismatch — means commits are stranded on their `worktree-wf_*` branches and\n `worktree.cleanup-wave` would merge nothing while the phase looks green. STOP the\n phase with the failing plan id and the recovery hint below; do NOT run\n `worktree.cleanup-wave` and do NOT proceed to step 4.\n\n **Recovery hint:** the unmerged `worktree-wf_*` branch still holds the work. Recover\n the missing metadata from the run's per-agent result journal (`journal.jsonl` — one\n `{\"type\":\"result\",…}` line per agent — in the Workflow run's transcript dir), re-run\n `worktree.record-agent` by hand, then re-run cleanup. If the journal cannot be\n recovered either, merge the branch manually after review — never discard it.\n\n5. **Resume (`resumeFromRunId`).** Cached/resumed agents do not re-emit their final\n messages, so a previously-completed plan can return with `metadata: null`. Recover\n that plan's metadata from the ORIGINAL run's journal (same hint as above). If it\n cannot be recovered, fail loudly per rule 4 — a resumed run must never report\n success over silently-dropped agent work.\n\n6. **Non-worktree plans** (`expects_worktree: false` — `use_worktree: false` in the\n manifest): they ran without isolation; their commits are already on the main working\n tree. No record-agent entry, no manifest write.\n\nWith the manifest populated, steps 4–5.8 (wait/completion bookkeeping, step 5.5's\nmanifest-scoped `worktree.cleanup-wave`, post-merge gate, tracking update) run\nUNCHANGED — the Workflow backend replaces HOW agents are spawned and returns their\nmetadata; the merge chain itself is the inline path's own, now with real input.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
687
721
  },
688
722
  "produces": [],
689
723
  "consumes": [
@@ -712,7 +746,7 @@ const capabilities = {
712
746
  "cline": {
713
747
  "id": "cline",
714
748
  "role": "runtime",
715
- "version": "1.10.0",
749
+ "version": "1.12.0",
716
750
  "title": "Cline",
717
751
  "description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
718
752
  "tier": "core",
@@ -739,10 +773,31 @@ const capabilities = {
739
773
  "nesting": "nested",
740
774
  "recursive": false,
741
775
  "converter": "convertClaudeCommandToClineSkill"
776
+ },
777
+ {
778
+ "kind": "agents",
779
+ "destSubpath": "agents",
780
+ "prefix": "gsd-",
781
+ "nesting": "flat",
782
+ "recursive": false,
783
+ "converter": "convertClaudeAgentToClineAgent"
742
784
  }
743
785
  ],
744
- "local": []
786
+ "local": [
787
+ {
788
+ "kind": "agents",
789
+ "destSubpath": "agents",
790
+ "prefix": "gsd-",
791
+ "nesting": "flat",
792
+ "recursive": false,
793
+ "converter": "convertClaudeAgentToClineAgent"
794
+ }
795
+ ]
745
796
  },
797
+ "triggerPrecedence": [
798
+ "skills",
799
+ "commands"
800
+ ],
746
801
  "commandStyle": "slash-hyphen",
747
802
  "hooksSurface": "cline-rules",
748
803
  "sandboxTier": "none",
@@ -783,7 +838,7 @@ const capabilities = {
783
838
  "code-review": {
784
839
  "id": "code-review",
785
840
  "role": "feature",
786
- "version": "1.10.0",
841
+ "version": "1.12.0",
787
842
  "title": "Code review",
788
843
  "description": "Source-file code review and review-fix workflow support for completed execution work.",
789
844
  "tier": "full",
@@ -844,7 +899,7 @@ const capabilities = {
844
899
  "codebuddy": {
845
900
  "id": "codebuddy",
846
901
  "role": "runtime",
847
- "version": "1.10.0",
902
+ "version": "1.12.0",
848
903
  "title": "CodeBuddy",
849
904
  "description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
850
905
  "tier": "core",
@@ -916,6 +971,10 @@ const capabilities = {
916
971
  }
917
972
  ]
918
973
  },
974
+ "triggerPrecedence": [
975
+ "skills",
976
+ "commands"
977
+ ],
919
978
  "commandStyle": "slash-hyphen",
920
979
  "hooksSurface": "settings-json",
921
980
  "hookEvents": "claude",
@@ -957,7 +1016,7 @@ const capabilities = {
957
1016
  "coderabbit": {
958
1017
  "id": "coderabbit",
959
1018
  "role": "reviewer",
960
- "version": "1.10.0",
1019
+ "version": "1.12.0",
961
1020
  "title": "CodeRabbit",
962
1021
  "description": "CodeRabbit CLI — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). Reviews the working-tree diff (`coderabbit review --prompt-only`), not the source tree, and accepts neither a prompt nor a model flag; findings are down-weighted in consensus (evidenceClass: diff-only).",
963
1022
  "tier": "full",
@@ -991,15 +1050,22 @@ const capabilities = {
991
1050
  "reviewsSection": "CodeRabbit",
992
1051
  "evidenceClass": "diff-only",
993
1052
  "requiresBinaries": [],
994
- "promptBudgetKey": null,
1053
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.coderabbit",
995
1054
  "modelConfigKey": null,
996
1055
  "handler": null
1056
+ },
1057
+ "config": {
1058
+ "review.max_prompt_tokens_per_reviewer.coderabbit": {
1059
+ "type": "number",
1060
+ "default": -1,
1061
+ "description": "Prompt-token budget for the CodeRabbit reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
1062
+ }
997
1063
  }
998
1064
  },
999
1065
  "codex": {
1000
1066
  "id": "codex",
1001
1067
  "role": "runtime",
1002
- "version": "1.10.0",
1068
+ "version": "1.12.0",
1003
1069
  "title": "OpenAI Codex CLI",
1004
1070
  "description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
1005
1071
  "tier": "core",
@@ -1027,6 +1093,14 @@ const capabilities = {
1027
1093
  "recursive": false,
1028
1094
  "converter": "convertClaudeCommandToCodexSkill",
1029
1095
  "home": ".agents"
1096
+ },
1097
+ {
1098
+ "kind": "agents",
1099
+ "destSubpath": "agents",
1100
+ "prefix": "gsd-",
1101
+ "nesting": "flat",
1102
+ "recursive": false,
1103
+ "converter": "convertClaudeAgentToCodexAgent"
1030
1104
  }
1031
1105
  ],
1032
1106
  "local": [
@@ -1037,9 +1111,21 @@ const capabilities = {
1037
1111
  "nesting": "flat",
1038
1112
  "recursive": false,
1039
1113
  "converter": "convertClaudeCommandToCodexSkill"
1114
+ },
1115
+ {
1116
+ "kind": "agents",
1117
+ "destSubpath": "agents",
1118
+ "prefix": "gsd-",
1119
+ "nesting": "flat",
1120
+ "recursive": false,
1121
+ "converter": "convertClaudeAgentToCodexAgent"
1040
1122
  }
1041
1123
  ]
1042
1124
  },
1125
+ "triggerPrecedence": [
1126
+ "skills",
1127
+ "commands"
1128
+ ],
1043
1129
  "commandStyle": "shell-var",
1044
1130
  "hooksSurface": "codex-hooks-json",
1045
1131
  "hookEvents": "claude",
@@ -1078,15 +1164,15 @@ const capabilities = {
1078
1164
  "exec"
1079
1165
  ],
1080
1166
  "cwdFlag": "--cd",
1081
- "promptFlag": null
1167
+ "promptFlag": null,
1168
+ "modelFlag": "--model"
1082
1169
  },
1083
1170
  "hostBehaviors": {
1084
1171
  "reapplyCommand": "$gsd-update --reapply",
1085
1172
  "tomlConfigInstall": true,
1086
1173
  "cleanupSkillSidecars": true,
1087
1174
  "agentTomlFiles": true,
1088
- "frontmatterDialect": "codex",
1089
- "reviewerCli": true
1175
+ "frontmatterDialect": "codex"
1090
1176
  }
1091
1177
  },
1092
1178
  "reviewer": {
@@ -1121,7 +1207,7 @@ const capabilities = {
1121
1207
  "reviewsSection": "Codex",
1122
1208
  "evidenceClass": "source-grounded",
1123
1209
  "requiresBinaries": [],
1124
- "promptBudgetKey": null,
1210
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.codex",
1125
1211
  "modelConfigKey": "review.models.codex",
1126
1212
  "handler": null
1127
1213
  },
@@ -1130,13 +1216,18 @@ const capabilities = {
1130
1216
  "type": "string",
1131
1217
  "default": "",
1132
1218
  "description": "Model passed to the Codex reviewer lane."
1219
+ },
1220
+ "review.max_prompt_tokens_per_reviewer.codex": {
1221
+ "type": "number",
1222
+ "default": -1,
1223
+ "description": "Prompt-token budget for the Codex reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
1133
1224
  }
1134
1225
  }
1135
1226
  },
1136
1227
  "copilot": {
1137
1228
  "id": "copilot",
1138
1229
  "role": "runtime",
1139
- "version": "1.10.0",
1230
+ "version": "1.12.0",
1140
1231
  "title": "GitHub Copilot",
1141
1232
  "description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
1142
1233
  "tier": "core",
@@ -1193,6 +1284,10 @@ const capabilities = {
1193
1284
  }
1194
1285
  ]
1195
1286
  },
1287
+ "triggerPrecedence": [
1288
+ "skills",
1289
+ "commands"
1290
+ ],
1196
1291
  "commandStyle": "slash-hyphen",
1197
1292
  "hooksSurface": "copilot-inline",
1198
1293
  "sandboxTier": "none",
@@ -1231,7 +1326,7 @@ const capabilities = {
1231
1326
  "cursor": {
1232
1327
  "id": "cursor",
1233
1328
  "role": "runtime",
1234
- "version": "1.10.0",
1329
+ "version": "1.12.0",
1235
1330
  "title": "Cursor",
1236
1331
  "description": "Cursor IDE — skills-only workflow surface; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
1237
1332
  "tier": "core",
@@ -1287,6 +1382,10 @@ const capabilities = {
1287
1382
  }
1288
1383
  ]
1289
1384
  },
1385
+ "triggerPrecedence": [
1386
+ "skills",
1387
+ "commands"
1388
+ ],
1290
1389
  "commandStyle": "slash-hyphen",
1291
1390
  "hooksSurface": "cursor-hooks-json",
1292
1391
  "hookEvents": "claude",
@@ -1337,8 +1436,7 @@ const capabilities = {
1337
1436
  "stop",
1338
1437
  "subagentStart",
1339
1438
  "subagentStop"
1340
- ],
1341
- "reviewerCli": true
1439
+ ]
1342
1440
  }
1343
1441
  },
1344
1442
  "reviewer": {
@@ -1372,15 +1470,22 @@ const capabilities = {
1372
1470
  "reviewsSection": "Cursor",
1373
1471
  "evidenceClass": "source-grounded",
1374
1472
  "requiresBinaries": [],
1375
- "promptBudgetKey": null,
1473
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.cursor",
1376
1474
  "modelConfigKey": null,
1377
1475
  "handler": null
1476
+ },
1477
+ "config": {
1478
+ "review.max_prompt_tokens_per_reviewer.cursor": {
1479
+ "type": "number",
1480
+ "default": -1,
1481
+ "description": "Prompt-token budget for the Cursor reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
1482
+ }
1378
1483
  }
1379
1484
  },
1380
1485
  "drift": {
1381
1486
  "id": "drift",
1382
1487
  "role": "feature",
1383
- "version": "1.10.0",
1488
+ "version": "1.12.0",
1384
1489
  "title": "Drift detection gates",
1385
1490
  "description": "Drift detection gates for the planning loop. At execute:wave:post: a blocking schema drift gate (detects schema files changed without a database push) and a non-blocking codebase drift gate (detects structural additions not reflected in STRUCTURE.md). At plan:pre: a non-blocking, warn-only codebase drift gate (gated on workflow.plan_drift_precheck) that flags a stale codebase map before planning, so plans are authored against a fresh STRUCTURE.md instead of discovering drift mid-execution.",
1386
1491
  "tier": "full",
@@ -1458,7 +1563,7 @@ const capabilities = {
1458
1563
  "external-job": {
1459
1564
  "id": "external-job",
1460
1565
  "role": "feature",
1461
- "version": "1.10.0",
1566
+ "version": "1.12.0",
1462
1567
  "title": "Async external-job scheduler adapter",
1463
1568
  "description": "Default-off producer of the async external-job manifest (#1164). At execute:wave:post an executor can externalize long-running compute (SLURM first, scheduler-pluggable), commit a .planning/async-jobs/<job>.json manifest, defer SUMMARY.md, and return external_job_waiting. The core loop (#1165) consumes the manifest; this capability is the only thing that writes it. NOTE on contribution point: #1164 specifies execute:wave:pre, but execute-phase.md only dispatches execute:wave:post today (wave:pre is declared in the loop host contract but not rendered); wiring wave:pre dispatch is a core-loop change #1164 explicitly puts out of scope, so this capability registers at wave:post and the executor honors the runtime_budget classification guidance before running any tagged task. The adapter (scripts/slurm-adapter.cjs) reads external_job.submit_timeout_ms / poll_timeout_ms / artifact_dir through the canonical capability-config seam (env override > config > registry default).",
1464
1569
  "tier": "full",
@@ -1541,7 +1646,7 @@ const capabilities = {
1541
1646
  "gap-analysis": {
1542
1647
  "id": "gap-analysis",
1543
1648
  "role": "feature",
1544
- "version": "1.10.0",
1649
+ "version": "1.12.0",
1545
1650
  "title": "Post-planning gap analysis",
1546
1651
  "description": "Proactive, non-blocking post-planning coverage report. After all PLAN.md files are generated, cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md against plan bodies. Emits a Source | Item | Status table. Does not block phase advancement.",
1547
1652
  "tier": "standard",
@@ -1582,7 +1687,7 @@ const capabilities = {
1582
1687
  "gemini": {
1583
1688
  "id": "gemini",
1584
1689
  "role": "reviewer",
1585
- "version": "1.10.0",
1690
+ "version": "1.12.0",
1586
1691
  "title": "Gemini CLI",
1587
1692
  "description": "Google Gemini CLI — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). Spawned as `gemini -p - -m <model>` with the plan piped on stdin.",
1588
1693
  "tier": "full",
@@ -1617,7 +1722,7 @@ const capabilities = {
1617
1722
  "reviewsSection": "Gemini",
1618
1723
  "evidenceClass": "source-grounded",
1619
1724
  "requiresBinaries": [],
1620
- "promptBudgetKey": null,
1725
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.gemini",
1621
1726
  "modelConfigKey": "review.models.gemini",
1622
1727
  "handler": null
1623
1728
  },
@@ -1626,13 +1731,18 @@ const capabilities = {
1626
1731
  "type": "string",
1627
1732
  "default": "",
1628
1733
  "description": "Model passed to the Gemini reviewer lane."
1734
+ },
1735
+ "review.max_prompt_tokens_per_reviewer.gemini": {
1736
+ "type": "number",
1737
+ "default": -1,
1738
+ "description": "Prompt-token budget for the Gemini reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
1629
1739
  }
1630
1740
  }
1631
1741
  },
1632
1742
  "graphify": {
1633
1743
  "id": "graphify",
1634
1744
  "role": "feature",
1635
- "version": "1.10.0",
1745
+ "version": "1.12.0",
1636
1746
  "title": "Knowledge graph",
1637
1747
  "description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
1638
1748
  "tier": "full",
@@ -1673,7 +1783,7 @@ const capabilities = {
1673
1783
  "hermes": {
1674
1784
  "id": "hermes",
1675
1785
  "role": "runtime",
1676
- "version": "1.10.0",
1786
+ "version": "1.12.0",
1677
1787
  "title": "Hermes Agent",
1678
1788
  "description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
1679
1789
  "tier": "core",
@@ -1700,6 +1810,14 @@ const capabilities = {
1700
1810
  "nesting": "nested",
1701
1811
  "recursive": false,
1702
1812
  "converter": "convertClaudeCommandToClaudeSkill"
1813
+ },
1814
+ {
1815
+ "kind": "agents",
1816
+ "destSubpath": "agents",
1817
+ "prefix": "gsd-",
1818
+ "nesting": "flat",
1819
+ "recursive": false,
1820
+ "converter": "convertClaudeAgentToHermesAgent"
1703
1821
  }
1704
1822
  ],
1705
1823
  "local": [
@@ -1710,9 +1828,21 @@ const capabilities = {
1710
1828
  "nesting": "nested",
1711
1829
  "recursive": false,
1712
1830
  "converter": "convertClaudeCommandToClaudeSkill"
1831
+ },
1832
+ {
1833
+ "kind": "agents",
1834
+ "destSubpath": "agents",
1835
+ "prefix": "gsd-",
1836
+ "nesting": "flat",
1837
+ "recursive": false,
1838
+ "converter": "convertClaudeAgentToHermesAgent"
1713
1839
  }
1714
1840
  ]
1715
1841
  },
1842
+ "triggerPrecedence": [
1843
+ "skills",
1844
+ "commands"
1845
+ ],
1716
1846
  "commandStyle": "slash-hyphen",
1717
1847
  "hooksSurface": "settings-json",
1718
1848
  "hookEvents": "claude",
@@ -1764,7 +1894,7 @@ const capabilities = {
1764
1894
  "intel": {
1765
1895
  "id": "intel",
1766
1896
  "role": "feature",
1767
- "version": "1.10.0",
1897
+ "version": "1.12.0",
1768
1898
  "title": "Codebase intelligence",
1769
1899
  "description": "Code-intelligence store for codebase querying, diff, snapshot, and API-surface extraction; exposes `gsd-tools intel` subcommands (query, status, update, diff, snapshot, patch-meta, validate, extract-exports, api-surface) and backs `/gsd-map-codebase` and `gsd-intel-updater`.",
1770
1900
  "tier": "full",
@@ -1816,7 +1946,7 @@ const capabilities = {
1816
1946
  "kilo": {
1817
1947
  "id": "kilo",
1818
1948
  "role": "runtime",
1819
- "version": "1.10.0",
1949
+ "version": "1.12.0",
1820
1950
  "title": "Kilo Code",
1821
1951
  "description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
1822
1952
  "tier": "core",
@@ -1858,6 +1988,14 @@ const capabilities = {
1858
1988
  "nesting": "flat",
1859
1989
  "recursive": true,
1860
1990
  "converter": "convertClaudeCommandToKiloSkill"
1991
+ },
1992
+ {
1993
+ "kind": "agents",
1994
+ "destSubpath": "agents",
1995
+ "prefix": "gsd-",
1996
+ "nesting": "flat",
1997
+ "recursive": false,
1998
+ "converter": "convertClaudeToKiloFrontmatter"
1861
1999
  }
1862
2000
  ],
1863
2001
  "local": [
@@ -1876,9 +2014,21 @@ const capabilities = {
1876
2014
  "nesting": "flat",
1877
2015
  "recursive": true,
1878
2016
  "converter": "convertClaudeCommandToKiloSkill"
2017
+ },
2018
+ {
2019
+ "kind": "agents",
2020
+ "destSubpath": "agents",
2021
+ "prefix": "gsd-",
2022
+ "nesting": "flat",
2023
+ "recursive": false,
2024
+ "converter": "convertClaudeToKiloFrontmatter"
1879
2025
  }
1880
2026
  ]
1881
2027
  },
2028
+ "triggerPrecedence": [
2029
+ "skills",
2030
+ "commands"
2031
+ ],
1882
2032
  "commandStyle": "slash-hyphen",
1883
2033
  "hooksSurface": "none",
1884
2034
  "extensionEvents": "kilo",
@@ -1925,7 +2075,7 @@ const capabilities = {
1925
2075
  "kimi": {
1926
2076
  "id": "kimi",
1927
2077
  "role": "runtime",
1928
- "version": "1.10.0",
2078
+ "version": "1.12.0",
1929
2079
  "title": "Kimi CLI",
1930
2080
  "description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; tier-2 support.",
1931
2081
  "tier": "core",
@@ -1969,6 +2119,10 @@ const capabilities = {
1969
2119
  ],
1970
2120
  "local": []
1971
2121
  },
2122
+ "triggerPrecedence": [
2123
+ "skills",
2124
+ "commands"
2125
+ ],
1972
2126
  "commandStyle": "slash-hyphen",
1973
2127
  "hooksSurface": "kimi-hooks-toml",
1974
2128
  "hookEvents": "claude",
@@ -2023,7 +2177,7 @@ const capabilities = {
2023
2177
  "kimi-code": {
2024
2178
  "id": "kimi-code",
2025
2179
  "role": "runtime",
2026
- "version": "1.10.0",
2180
+ "version": "1.12.0",
2027
2181
  "title": "Kimi Code CLI",
2028
2182
  "description": "Kimi Code CLI (Moonshot AI, Node) — Agent Skills auto-discovered at ~/.kimi-code/skills; global AGENTS.md at ~/.kimi-code/AGENTS.md; native config.toml + [[hooks]] bus; three built-in subagents (coder/explore/plan), NO custom named subagents; background dispatch; tier-2 support. Distinct from Python kimi-cli (the 'kimi' capability) per ADR-1239 EoS — Kimi Code cannot dispatch named subagents so the kimi-agents YAML layout does NOT apply; persona injection rides the existing ${AGENT_SKILLS_*} workflow fallback. Install-layout, agent-install-check, and install-time decision (kimi vs kimi-code) land in follow-up PRs; this descriptor is the EoS foundation.",
2029
2183
  "tier": "core",
@@ -2054,10 +2208,31 @@ const capabilities = {
2054
2208
  "nesting": "flat",
2055
2209
  "recursive": false,
2056
2210
  "converter": "convertClaudeCommandToKimiCodeSkill"
2211
+ },
2212
+ {
2213
+ "kind": "agents",
2214
+ "destSubpath": "agents",
2215
+ "prefix": "gsd-",
2216
+ "nesting": "flat",
2217
+ "recursive": false,
2218
+ "converter": null
2057
2219
  }
2058
2220
  ],
2059
- "local": []
2221
+ "local": [
2222
+ {
2223
+ "kind": "agents",
2224
+ "destSubpath": "agents",
2225
+ "prefix": "gsd-",
2226
+ "nesting": "flat",
2227
+ "recursive": false,
2228
+ "converter": null
2229
+ }
2230
+ ]
2060
2231
  },
2232
+ "triggerPrecedence": [
2233
+ "skills",
2234
+ "commands"
2235
+ ],
2061
2236
  "commandStyle": "slash-hyphen",
2062
2237
  "hooksSurface": "kimi-hooks-toml",
2063
2238
  "hookEvents": "claude",
@@ -2140,7 +2315,7 @@ const capabilities = {
2140
2315
  "reviewsSection": "Kimi Code",
2141
2316
  "evidenceClass": "source-grounded",
2142
2317
  "requiresBinaries": [],
2143
- "promptBudgetKey": null,
2318
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.kimi-code",
2144
2319
  "modelConfigKey": "review.models.kimi-code",
2145
2320
  "handler": null
2146
2321
  },
@@ -2149,13 +2324,71 @@ const capabilities = {
2149
2324
  "type": "string",
2150
2325
  "default": "",
2151
2326
  "description": "Model passed to the Kimi Code reviewer lane."
2327
+ },
2328
+ "review.max_prompt_tokens_per_reviewer.kimi-code": {
2329
+ "type": "number",
2330
+ "default": -1,
2331
+ "description": "Prompt-token budget for the Kimi Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
2152
2332
  }
2153
2333
  }
2154
2334
  },
2335
+ "live-dom-uat": {
2336
+ "id": "live-dom-uat",
2337
+ "role": "feature",
2338
+ "version": "1.12.0",
2339
+ "title": "Live-DOM UAT",
2340
+ "description": "Default-off live-DOM verification (#2856). Confines browser MCP reach to one purpose-built agent (gsd-dom-verifier) that carries the browser globs in its own tools: line, registered as an additive step hook at execute:wave:post. agents/gsd-executor.md is deliberately NOT widened: for a first-party agent the static tool list is the only control that exists, no capability can grant tools to one (ADR-1244 D2), no hook kind grants tool permissions (ADR-857 D4), and there is no per-dispatch tool override. Gated by activationKey workflow.live_dom_uat (default false), so with the key off the capability resolves inactive and the hook does not render at all. NOTE on the browser profile lock: chrome-devtools-mcp holds an exclusive lock on $HOME/.cache/chrome-devtools-mcp/chrome-profile, and --isolated is a flag on the user's own MCP-server registration that GSD cannot pass. Concurrent execution waves sharing one profile will therefore collide; the step tolerates and reports that (onError: skip, never blocking) rather than pretending to coordinate a resource it does not own.",
2341
+ "tier": "full",
2342
+ "requires": [],
2343
+ "engines": {
2344
+ "gsd": ">=1.11.0"
2345
+ },
2346
+ "runtimeCompat": {
2347
+ "supported": [
2348
+ "*"
2349
+ ],
2350
+ "unsupported": []
2351
+ },
2352
+ "skills": [],
2353
+ "agents": [
2354
+ "gsd-dom-verifier"
2355
+ ],
2356
+ "activationKey": "workflow.live_dom_uat",
2357
+ "config": {
2358
+ "workflow.live_dom_uat": {
2359
+ "type": "boolean",
2360
+ "default": false,
2361
+ "description": "Enable live-DOM verification. Default-off: browser MCP reach is opt-in per project. When on, the orchestrator's automated UI verification may additionally use mcp__chrome-devtools__* / mcp__claude-in-chrome__* when present, and a gsd-dom-verifier step runs after each execution wave. When off, neither surface reaches a browser and the pre-existing mcp__playwright__* path is unchanged."
2362
+ }
2363
+ },
2364
+ "hooks": [],
2365
+ "steps": [
2366
+ {
2367
+ "point": "execute:wave:post",
2368
+ "ref": {
2369
+ "agent": "gsd-dom-verifier"
2370
+ },
2371
+ "fragment": {
2372
+ "path": "fragments/execute-wave-post.md",
2373
+ "inline": "<objective>\nVerify the live-DOM acceptance criteria for the execution wave that just completed.\nAnswer: \"of this wave's stated UI acceptance criteria, which can I observe in a live DOM\nright now, and which could I not look at?\"\n\nThis step is ADDITIVE. It never halts the wave, never fails the phase, and never rewrites\nSUMMARY.md. If you cannot look, say so and finish.\n</objective>\n\n<required_reading>\n- {phase_dir}/{phase_num}-PLAN.md (the wave's tasks and their acceptance criteria)\n- {phase_dir}/{phase_num}-UI-SPEC.md if it exists (the design contract, when the phase has one)\n</required_reading>\n\n<browser_surface>\nYou carry exactly two browser MCP families: `mcp__chrome-devtools__*` and\n`mcp__claude-in-chrome__*`. Use whichever responds. Do not assume they expose the same\ntool names — probe, then use what is there. Do not paper over differences between them.\n\nYou do NOT carry the Playwright MCP family. That path belongs to the orchestrator's\nown verification step and is not yours.\n</browser_surface>\n\n<profile_lock>\n`chrome-devtools-mcp` holds an exclusive lock on its browser profile\n(`$HOME/.cache/chrome-devtools-mcp/chrome-profile`). A second concurrent instance fails with:\n\n```\nThe browser is already running for <dir>. Use --isolated to run multiple browser instances.\n```\n\nIf you see that, or any equivalent lock error:\n\n1. Record `outcome: could_not_look` and `reason: profile_locked`.\n2. Name `--isolated` in the notes, so the operator knows the remedy is a flag on THEIR MCP\n server registration.\n3. **Stop.** Do not retry, do not loop, do not wait for the lock. GSD cannot pass\n `--isolated` — it is not GSD's flag — and a retry loop here just holds up the wave.\n\nParallel execution waves sharing one profile WILL hit this. It is an expected condition,\nnot a defect, and it is not a reason to fail anything.\n</profile_lock>\n\n<method>\nFor each UI acceptance criterion you can identify in the wave's plan:\n\n1. Resolve its target URL. If no dev server or target is reachable, that criterion is\n `could_not_look` / `target_unreachable` — not a failure.\n2. Open it with the browser family that responded.\n3. Observe the DOM for the specific, stated condition. Assert on structure and content —\n an element's presence, its text, its attributes, its computed state.\n4. Record `passed` when the stated condition is observably true, `needs_review` when it is\n ambiguous or requires human judgement (subjective aesthetics, content accuracy).\n\nScope limit for this version: DOM observation against stated criteria only. No screenshot\ndiffing, no accessibility audit, no performance tracing. If a criterion needs one of those,\nmark it `needs_review` and say which.\n\nNever invent a criterion. If the plan states no UI acceptance criteria, that is\n`outcome: nothing_to_report` / `reason: no_criteria`, and it is a perfectly good result.\n</method>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-DOM-VERIFY.md\n\nFrontmatter carries scalars only, so a reader can get the verdict without parsing prose:\n\n```\n---\nschema_version: 1\nwave: {wave_number}\noutcome: verified | nothing_to_report | could_not_look\nreason: ok | no_criteria | no_browser_mcp | profile_locked | target_unreachable\nchecked: <integer>\npassed: <integer>\nneeds_review: <integer>\n---\n```\n\nThen a short body: one line per criterion with its verdict, and — when `outcome` is\n`could_not_look` — exactly what stopped you and what the operator would change.\n\n**`nothing_to_report` and `could_not_look` are different outcomes and must never be\nconflated.** \"There were no UI criteria in this wave\" and \"there were criteria but I had no\nbrowser\" look identical in a summary that collapses them, and that ambiguity is the reported\nproblem this capability exists to remove.\n</output>\n"
2374
+ },
2375
+ "produces": [
2376
+ "DOM-VERIFY.md"
2377
+ ],
2378
+ "consumes": [
2379
+ "PLAN.md"
2380
+ ],
2381
+ "when": "workflow.live_dom_uat",
2382
+ "onError": "skip"
2383
+ }
2384
+ ],
2385
+ "contributions": [],
2386
+ "gates": []
2387
+ },
2155
2388
  "llama-cpp": {
2156
2389
  "id": "llama-cpp",
2157
2390
  "role": "reviewer",
2158
- "version": "1.10.0",
2391
+ "version": "1.12.0",
2159
2392
  "title": "llama.cpp",
2160
2393
  "description": "llama.cpp server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.llama_cpp_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq. Capability id/folder are kebab (`llama-cpp`, required by KEBAB_RE); `reviewer.slug` stays snake (`llama_cpp`) to match the shipped roster and the `review.llama_cpp_host` config key (ADR-2782's three-namespace trap).",
2161
2394
  "tier": "full",
@@ -2213,7 +2446,7 @@ const capabilities = {
2213
2446
  "lm-studio": {
2214
2447
  "id": "lm-studio",
2215
2448
  "role": "reviewer",
2216
- "version": "1.10.0",
2449
+ "version": "1.12.0",
2217
2450
  "title": "LM Studio",
2218
2451
  "description": "LM Studio local model server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.lm_studio_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq. Capability id/folder are kebab (`lm-studio`, required by KEBAB_RE); `reviewer.slug` stays snake (`lm_studio`) to match the shipped roster and the `review.lm_studio_host` config key (ADR-2782's three-namespace trap).",
2219
2452
  "tier": "full",
@@ -2271,7 +2504,7 @@ const capabilities = {
2271
2504
  "mempalace": {
2272
2505
  "id": "mempalace",
2273
2506
  "role": "feature",
2274
- "version": "1.10.0",
2507
+ "version": "1.12.0",
2275
2508
  "title": "MemPalace memory",
2276
2509
  "description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
2277
2510
  "tier": "full",
@@ -2445,7 +2678,7 @@ const capabilities = {
2445
2678
  "nyquist": {
2446
2679
  "id": "nyquist",
2447
2680
  "role": "feature",
2448
- "version": "1.10.0",
2681
+ "version": "1.12.0",
2449
2682
  "title": "Nyquist validation",
2450
2683
  "description": "Validation coverage audit that maps executed work back to tests and manual-only evidence.",
2451
2684
  "tier": "full",
@@ -2495,7 +2728,7 @@ const capabilities = {
2495
2728
  "ollama": {
2496
2729
  "id": "ollama",
2497
2730
  "role": "reviewer",
2498
- "version": "1.10.0",
2731
+ "version": "1.12.0",
2499
2732
  "title": "Ollama",
2500
2733
  "description": "Ollama local model server — cross-AI /gsd:review reviewer lane only; not a GSD install target (no runtime body, no artifacts). OpenAI-compatible HTTP transport against a user-configured `review.ollama_host` (POST /v1/chat/completions); model discovered via GET /v1/models piped through jq.",
2501
2734
  "tier": "full",
@@ -2553,7 +2786,7 @@ const capabilities = {
2553
2786
  "opencode": {
2554
2787
  "id": "opencode",
2555
2788
  "role": "runtime",
2556
- "version": "1.10.0",
2789
+ "version": "1.12.0",
2557
2790
  "title": "OpenCode",
2558
2791
  "description": "OpenCode — XDG-based config dir; flat commands/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
2559
2792
  "tier": "core",
@@ -2590,6 +2823,14 @@ const capabilities = {
2590
2823
  "nesting": "flat",
2591
2824
  "recursive": true,
2592
2825
  "converter": "convertClaudeCommandToOpencodeSkill"
2826
+ },
2827
+ {
2828
+ "kind": "agents",
2829
+ "destSubpath": "agents",
2830
+ "prefix": "gsd-",
2831
+ "nesting": "flat",
2832
+ "recursive": false,
2833
+ "converter": "convertClaudeToOpencodeFrontmatter"
2593
2834
  }
2594
2835
  ],
2595
2836
  "local": [
@@ -2608,9 +2849,21 @@ const capabilities = {
2608
2849
  "nesting": "flat",
2609
2850
  "recursive": true,
2610
2851
  "converter": "convertClaudeCommandToOpencodeSkill"
2852
+ },
2853
+ {
2854
+ "kind": "agents",
2855
+ "destSubpath": "agents",
2856
+ "prefix": "gsd-",
2857
+ "nesting": "flat",
2858
+ "recursive": false,
2859
+ "converter": "convertClaudeToOpencodeFrontmatter"
2611
2860
  }
2612
2861
  ]
2613
2862
  },
2863
+ "triggerPrecedence": [
2864
+ "skills",
2865
+ "commands"
2866
+ ],
2614
2867
  "commandStyle": "slash-hyphen",
2615
2868
  "hooksSurface": "none",
2616
2869
  "extensionEvents": "opencode",
@@ -2661,8 +2914,7 @@ const capabilities = {
2661
2914
  "skipHomePrefixSubstitution": true,
2662
2915
  "skipSettingsUi": true,
2663
2916
  "skipUpdateBannerCommand": true,
2664
- "skipCodexSkillsManifest": true,
2665
- "reviewerCli": true
2917
+ "skipCodexSkillsManifest": true
2666
2918
  }
2667
2919
  },
2668
2920
  "reviewer": {
@@ -2695,7 +2947,7 @@ const capabilities = {
2695
2947
  "reviewsSection": "OpenCode",
2696
2948
  "evidenceClass": "source-grounded",
2697
2949
  "requiresBinaries": [],
2698
- "promptBudgetKey": null,
2950
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.opencode",
2699
2951
  "modelConfigKey": "review.models.opencode",
2700
2952
  "handler": "opencode"
2701
2953
  },
@@ -2704,13 +2956,18 @@ const capabilities = {
2704
2956
  "type": "string",
2705
2957
  "default": "",
2706
2958
  "description": "Model passed to the OpenCode reviewer lane."
2959
+ },
2960
+ "review.max_prompt_tokens_per_reviewer.opencode": {
2961
+ "type": "number",
2962
+ "default": -1,
2963
+ "description": "Prompt-token budget for the OpenCode reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
2707
2964
  }
2708
2965
  }
2709
2966
  },
2710
2967
  "pattern-mapper": {
2711
2968
  "id": "pattern-mapper",
2712
2969
  "role": "feature",
2713
- "version": "1.10.0",
2970
+ "version": "1.12.0",
2714
2971
  "title": "Pattern mapping",
2715
2972
  "description": "Optional codebase-pattern mapping before planning; owns the pattern mapper agent and workflow.pattern_mapper activation key.",
2716
2973
  "tier": "full",
@@ -2746,7 +3003,7 @@ const capabilities = {
2746
3003
  },
2747
3004
  "fragment": {
2748
3005
  "path": "fragments/plan-pre.md",
2749
- "inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</files_to_read>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
3006
+ "inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<required_reading>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</required_reading>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
2750
3007
  },
2751
3008
  "produces": [
2752
3009
  "PATTERNS.md"
@@ -2764,7 +3021,7 @@ const capabilities = {
2764
3021
  "pi": {
2765
3022
  "id": "pi",
2766
3023
  "role": "runtime",
2767
- "version": "1.10.0",
3024
+ "version": "1.12.0",
2768
3025
  "title": "pi",
2769
3026
  "description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.js (.js, not .cjs — pi's extension auto-discovery accepts only .ts/.js, #2470); no shared-settings hook surface; tier-2 support.",
2770
3027
  "tier": "core",
@@ -2787,6 +3044,10 @@ const capabilities = {
2787
3044
  "global": [],
2788
3045
  "local": []
2789
3046
  },
3047
+ "triggerPrecedence": [
3048
+ "skills",
3049
+ "commands"
3050
+ ],
2790
3051
  "commandStyle": "slash-hyphen",
2791
3052
  "hooksSurface": "none",
2792
3053
  "extensionEvents": "pi",
@@ -2829,7 +3090,7 @@ const capabilities = {
2829
3090
  "profile-pipeline": {
2830
3091
  "id": "profile-pipeline",
2831
3092
  "role": "feature",
2832
- "version": "1.10.0",
3093
+ "version": "1.12.0",
2833
3094
  "title": "Developer profiling pipeline",
2834
3095
  "description": "Developer behavioral profiling from Claude Code session history; scans session JSONL files, extracts and samples user messages, and generates profile artifacts (USER-PROFILE.md, dev-preferences.md, CLAUDE.md sections). Exposes eight `gsd-tools` commands: scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase). Backs the /gsd-profile-user skill and gsd-user-profiler agent.",
2835
3096
  "tier": "full",
@@ -2906,7 +3167,7 @@ const capabilities = {
2906
3167
  "qwen": {
2907
3168
  "id": "qwen",
2908
3169
  "role": "runtime",
2909
- "version": "1.10.0",
3170
+ "version": "1.12.0",
2910
3171
  "title": "Qwen Code",
2911
3172
  "description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
2912
3173
  "tier": "core",
@@ -2962,6 +3223,10 @@ const capabilities = {
2962
3223
  }
2963
3224
  ]
2964
3225
  },
3226
+ "triggerPrecedence": [
3227
+ "skills",
3228
+ "commands"
3229
+ ],
2965
3230
  "commandStyle": "slash-hyphen",
2966
3231
  "hooksSurface": "settings-json",
2967
3232
  "hookEvents": "claude",
@@ -3005,8 +3270,7 @@ const capabilities = {
3005
3270
  "legacyCommandsGsdCleanup": true,
3006
3271
  "legacyCommandsGsdInstallMigration": true,
3007
3272
  "legacyCommandsGsdUninstall": true,
3008
- "hyphenNameAgentBody": true,
3009
- "reviewerCli": true
3273
+ "hyphenNameAgentBody": true
3010
3274
  }
3011
3275
  },
3012
3276
  "reviewer": {
@@ -3034,15 +3298,89 @@ const capabilities = {
3034
3298
  "reviewsSection": "Qwen",
3035
3299
  "evidenceClass": "source-grounded",
3036
3300
  "requiresBinaries": [],
3037
- "promptBudgetKey": null,
3301
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.qwen",
3038
3302
  "modelConfigKey": null,
3039
3303
  "handler": null
3304
+ },
3305
+ "config": {
3306
+ "review.max_prompt_tokens_per_reviewer.qwen": {
3307
+ "type": "number",
3308
+ "default": -1,
3309
+ "description": "Prompt-token budget for the Qwen Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
3310
+ }
3040
3311
  }
3041
3312
  },
3313
+ "refactor-trigger": {
3314
+ "id": "refactor-trigger",
3315
+ "role": "feature",
3316
+ "version": "1.12.0",
3317
+ "title": "Complexity-triggered refactor",
3318
+ "description": "Measures the complexity of the code a phase touched and, when a function crosses a configured threshold or jumps past its recorded anchor, surfaces a scoped refactor proposal at .planning/phases/<N>/<NN>-REFACTOR.md. Advisory by default — it never edits code and never blocks. Opt-in strict mode blocks /gsd-ship while a proposal is untriaged; a declined proposal is recorded in the broken-windows ledger when that capability is present. Operationalizes 'refactor early, refactor often' as continuous pressure instead of a thing you have to remember (issue #1953).",
3319
+ "tier": "full",
3320
+ "requires": [],
3321
+ "engines": {
3322
+ "gsd": ">=1.10.0"
3323
+ },
3324
+ "runtimeCompat": {
3325
+ "supported": [
3326
+ "*"
3327
+ ],
3328
+ "unsupported": []
3329
+ },
3330
+ "skills": [],
3331
+ "agents": [],
3332
+ "activationKey": "refactor.trigger_enabled",
3333
+ "config": {
3334
+ "refactor.trigger_enabled": {
3335
+ "type": "boolean",
3336
+ "default": false,
3337
+ "description": "Enable the complexity-triggered refactor hook. When true, an execute:post step evaluates the complexity of the files the phase touched and writes a scoped refactor proposal if a function crosses refactor.complexity_threshold or jumps past refactor.complexity_jump_delta. Opt-in; when false the hook never runs. Issue #1953."
3338
+ },
3339
+ "refactor.complexity_threshold": {
3340
+ "type": "number",
3341
+ "default": 15,
3342
+ "description": "Absolute per-function complexity above which a refactor proposal is surfaced. Semantics match ESLint's `complexity: {max: N}` — the trigger is STRICTLY GREATER, so a score of exactly N does not trigger. Default 15 follows SonarSource's default; ESLint's own default is 20 and radon's rank C begins at 11. Raise it if proposals feel like noise."
3343
+ },
3344
+ "refactor.complexity_jump_delta": {
3345
+ "type": "number",
3346
+ "default": 5,
3347
+ "description": "Complexity growth above which a refactor proposal is surfaced even when the absolute threshold is not reached. Measured against the function's anchor — the score recorded the last time the function was consciously dispositioned — so it accumulates across phases and catches slow creep the absolute threshold would miss. Strictly greater, as with the threshold."
3348
+ },
3349
+ "refactor.trigger_strict": {
3350
+ "type": "boolean",
3351
+ "default": false,
3352
+ "description": "Record an untriaged refactor proposal as an open `deviation` entry in the broken-windows ledger, so it becomes a tracked task that must be resolved before ship. Off by default and deliberately so: a blocking complexity number is a metric an executor can satisfy by splitting one coherent function into two incoherent ones, so the entry clears on the proposal being DISPOSITIONED (gsd-tools refactor accept|decline), never on the score improving. Ship blocking is the broken-windows capability's existing ship:pre gate — enable it with workflow.windows_enforce. With broken-windows absent, strict mode still records the proposal locally and says so; it cannot block on its own. Advisory mode (the default) surfaces the same proposal and tracks nothing."
3353
+ }
3354
+ },
3355
+ "commands": [
3356
+ {
3357
+ "family": "refactor",
3358
+ "module": "refactor-trigger-command-router.cjs",
3359
+ "router": "routeRefactorTriggerCommand"
3360
+ }
3361
+ ],
3362
+ "hooks": [],
3363
+ "steps": [
3364
+ {
3365
+ "point": "execute:post",
3366
+ "ref": {
3367
+ "command": "refactor evaluate"
3368
+ },
3369
+ "produces": [
3370
+ "REFACTOR.md"
3371
+ ],
3372
+ "consumes": [],
3373
+ "when": "refactor.trigger_enabled",
3374
+ "onError": "skip"
3375
+ }
3376
+ ],
3377
+ "contributions": [],
3378
+ "gates": []
3379
+ },
3042
3380
  "research": {
3043
3381
  "id": "research",
3044
3382
  "role": "feature",
3045
- "version": "1.10.0",
3383
+ "version": "1.12.0",
3046
3384
  "title": "Phase research",
3047
3385
  "description": "Optional phase research before planning; owns the phase researcher agent and workflow.research activation key.",
3048
3386
  "tier": "standard",
@@ -3076,7 +3414,7 @@ const capabilities = {
3076
3414
  },
3077
3415
  "fragment": {
3078
3416
  "path": "fragments/plan-pre.md",
3079
- "inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</files_to_read>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
3417
+ "inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<required_reading>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</required_reading>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
3080
3418
  },
3081
3419
  "produces": [
3082
3420
  "RESEARCH.md"
@@ -3094,7 +3432,7 @@ const capabilities = {
3094
3432
  "schema-gate": {
3095
3433
  "id": "schema-gate",
3096
3434
  "role": "feature",
3097
- "version": "1.10.0",
3435
+ "version": "1.12.0",
3098
3436
  "title": "Schema push detection gate",
3099
3437
  "description": "Detects ORM schema-relevant files in the phase scope during planning and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.",
3100
3438
  "tier": "full",
@@ -3140,7 +3478,7 @@ const capabilities = {
3140
3478
  "security": {
3141
3479
  "id": "security",
3142
3480
  "role": "feature",
3143
- "version": "1.10.0",
3481
+ "version": "1.12.0",
3144
3482
  "title": "Security enforcement",
3145
3483
  "description": "Threat mitigation verification and ship-time security blocking for phases with security enforcement enabled.",
3146
3484
  "tier": "full",
@@ -3239,7 +3577,7 @@ const capabilities = {
3239
3577
  "tdd": {
3240
3578
  "id": "tdd",
3241
3579
  "role": "feature",
3242
- "version": "1.10.0",
3580
+ "version": "1.12.0",
3243
3581
  "title": "Test-driven development",
3244
3582
  "description": "Injects TDD heuristics into the planner and enforces RED/GREEN gate compliance on type:tdd plans after execution. Owns workflow.tdd_mode; the --tdd CLI flag is the ephemeral override.",
3245
3583
  "tier": "full",
@@ -3292,7 +3630,7 @@ const capabilities = {
3292
3630
  "trae": {
3293
3631
  "id": "trae",
3294
3632
  "role": "runtime",
3295
- "version": "1.10.0",
3633
+ "version": "1.12.0",
3296
3634
  "title": "Trae IDE",
3297
3635
  "description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
3298
3636
  "tier": "core",
@@ -3348,6 +3686,10 @@ const capabilities = {
3348
3686
  }
3349
3687
  ]
3350
3688
  },
3689
+ "triggerPrecedence": [
3690
+ "skills",
3691
+ "commands"
3692
+ ],
3351
3693
  "commandStyle": "slash-hyphen",
3352
3694
  "hooksSurface": "none",
3353
3695
  "sandboxTier": "none",
@@ -3385,7 +3727,7 @@ const capabilities = {
3385
3727
  "ui": {
3386
3728
  "id": "ui",
3387
3729
  "role": "feature",
3388
- "version": "1.10.0",
3730
+ "version": "1.12.0",
3389
3731
  "title": "UI design contracts",
3390
3732
  "description": "UI-SPEC design contract + retrospective UI audit for frontend phases.",
3391
3733
  "tier": "full",
@@ -3480,7 +3822,7 @@ const capabilities = {
3480
3822
  "vscode": {
3481
3823
  "id": "vscode",
3482
3824
  "role": "runtime",
3483
- "version": "1.10.0",
3825
+ "version": "1.12.0",
3484
3826
  "title": "VS Code",
3485
3827
  "description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
3486
3828
  "tier": "core",
@@ -3500,6 +3842,10 @@ const capabilities = {
3500
3842
  "global": [],
3501
3843
  "local": []
3502
3844
  },
3845
+ "triggerPrecedence": [
3846
+ "skills",
3847
+ "commands"
3848
+ ],
3503
3849
  "commandStyle": "slash-hyphen",
3504
3850
  "hooksSurface": "none",
3505
3851
  "extensionEvents": "none",
@@ -3533,7 +3879,7 @@ const capabilities = {
3533
3879
  "windsurf": {
3534
3880
  "id": "windsurf",
3535
3881
  "role": "runtime",
3536
- "version": "1.10.0",
3882
+ "version": "1.12.0",
3537
3883
  "title": "Windsurf",
3538
3884
  "description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); tier-2 support.",
3539
3885
  "tier": "core",
@@ -3582,6 +3928,10 @@ const capabilities = {
3582
3928
  }
3583
3929
  ]
3584
3930
  },
3931
+ "triggerPrecedence": [
3932
+ "skills",
3933
+ "commands"
3934
+ ],
3585
3935
  "commandStyle": "slash-hyphen",
3586
3936
  "hooksSurface": "windsurf-hooks-json",
3587
3937
  "sandboxTier": "none",
@@ -3620,7 +3970,7 @@ const capabilities = {
3620
3970
  "zcode": {
3621
3971
  "id": "zcode",
3622
3972
  "role": "runtime",
3623
- "version": "1.10.0",
3973
+ "version": "1.12.0",
3624
3974
  "title": "ZCode",
3625
3975
  "description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
3626
3976
  "tier": "core",
@@ -3662,7 +4012,7 @@ const capabilities = {
3662
4012
  "prefix": "gsd-",
3663
4013
  "nesting": "flat",
3664
4014
  "recursive": false,
3665
- "converter": null
4015
+ "converter": "convertClaudeAgentToZcodeAgent"
3666
4016
  }
3667
4017
  ],
3668
4018
  "local": [
@@ -3688,10 +4038,14 @@ const capabilities = {
3688
4038
  "prefix": "gsd-",
3689
4039
  "nesting": "flat",
3690
4040
  "recursive": false,
3691
- "converter": null
4041
+ "converter": "convertClaudeAgentToZcodeAgent"
3692
4042
  }
3693
4043
  ]
3694
4044
  },
4045
+ "triggerPrecedence": [
4046
+ "skills",
4047
+ "commands"
4048
+ ],
3695
4049
  "commandStyle": "slash-hyphen",
3696
4050
  "hooksSurface": "none",
3697
4051
  "sandboxTier": "none",
@@ -3746,6 +4100,7 @@ const byAgent = {
3746
4100
  "gsd-eval-planner": "ai-integration",
3747
4101
  "gsd-code-reviewer": "code-review",
3748
4102
  "gsd-code-fixer": "code-review",
4103
+ "gsd-dom-verifier": "live-dom-uat",
3749
4104
  "gsd-mempalace-curator": "mempalace",
3750
4105
  "gsd-nyquist-auditor": "nyquist",
3751
4106
  "gsd-pattern-mapper": "pattern-mapper",
@@ -3848,7 +4203,7 @@ const byLoopPoint = {
3848
4203
  },
3849
4204
  "fragment": {
3850
4205
  "path": "fragments/plan-pre.md",
3851
- "inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</files_to_read>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
4206
+ "inline": "<objective>\nResearch how to implement Phase {phase_number}: {phase_name}\nAnswer: \"What do I need to know to PLAN this phase well?\"\n</objective>\n\n<required_reading>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {requirements_path} (Project requirements)\n- {state_path} (Project decisions and history)\n</required_reading>\n\n${AGENT_SKILLS_RESEARCHER}\n\n<additional_context>\n**Phase description:** {phase_description}\n**Phase requirement IDs (MUST address):** {phase_req_ids}\n\n**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists; follow project-specific guidelines.\n**Project skills:** Check .claude/skills/ or .agents/skills/ directory if either exists. Read SKILL.md files and account for project skill patterns.\n</additional_context>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-RESEARCH.md\n</output>\n"
3852
4207
  },
3853
4208
  "produces": [
3854
4209
  "RESEARCH.md"
@@ -3882,7 +4237,7 @@ const byLoopPoint = {
3882
4237
  },
3883
4238
  "fragment": {
3884
4239
  "path": "fragments/plan-pre.md",
3885
- "inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<files_to_read>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</files_to_read>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
4240
+ "inline": "<pattern_mapping_context>\n**Phase:** {phase_number} - {phase_name}\n**Phase directory:** {phase_dir}\n**Padded phase:** {padded_phase}\n\n<required_reading>\n- {context_path} (USER DECISIONS from /gsd:discuss-phase)\n- {research_path} (Technical Research)\n</required_reading>\n\n**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md\n\nExtract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.\n</pattern_mapping_context>\n"
3886
4241
  },
3887
4242
  "produces": [
3888
4243
  "PATTERNS.md"
@@ -3901,7 +4256,7 @@ const byLoopPoint = {
3901
4256
  "into": "planner",
3902
4257
  "fragment": {
3903
4258
  "path": "fragments/api-coverage-plan-pre.md",
3904
- "inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[]}')\n```\n\nRead `API_COVERAGE_JSON.detected`. Act on it only — do **not** pattern-match the\nprose yourself.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
4259
+ "inline": "# API Coverage Decision Checkpoint\n\n> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase\n> integrates an external API / SDK / service. Most non-API phases will not fire\n> it — that is the point.\n\n## Why this exists\n\n\"We integrated the API\" too often silently means \"we integrated whatever the\nfirst use case exercised.\" Every un-built capability is then an invisible hole,\ndiscovered later by a user who reasonably expected it to work. The phase sealed\ngreen because its tasks completed; nobody decided the gaps were acceptable,\nbecause nobody enumerated them. This checkpoint makes the surface **visible and\ndecided** before the phase can seal.\n\n## Detect whether this phase integrates an external API\n\nThe detector is a deterministic scan over the phase scope. It strips fenced\ncode blocks first, so a trigger term inside a code snippet does not fire. It\nreturns a typed result: `{ detected, signals[], terms }`. Run it on the phase\nscope (the concatenation of this phase's ROADMAP section + the PLAN body):\n\n```bash\nSCOPE=\"$(cat \"${PHASE_DIR}\"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase \"${PHASE}\" 2>/dev/null || true)\"\nAPI_COVERAGE_JSON=$(printf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null) || true\n[ -n \"$API_COVERAGE_JSON\" ] || API_COVERAGE_JSON='{\"skipped\":true,\"reason\":\"probe_unavailable\"}'\n```\n\nThe `|| true` neutralizes the assignment's status without discarding the\ndetector's own payload: the detector exits **1** for a real \"no integration\"\nverdict, so treating any non-zero exit as failure would throw away a correct\nanswer. Emptiness — not exit status — is what proves the probe never ran, and\nthe second line is the only place the fragment manufactures a payload of its\nown — one that records the *absence* of a verdict rather than asserting one.\n\nThe detector's exit code and `--json` payload now distinguish a real negative\nfrom an unexamined input (ADR-3889 Phase 3, #3907): empty/whitespace-only\n`$SCOPE` or a stdin read failure emit `{\"skipped\":true,\"reason\":\"no_input\"|\n\"stdin_error\"}` — no `detected` key at all. **Check for `skipped` before\nreading `detected`**: a `skipped` payload is not a confirmed \"no API\nintegration\" verdict, it means the detector never examined real input. Do not\ntreat it as `detected:false`. Read `API_COVERAGE_JSON.detected` only when\n`skipped` is absent — act on it only, do **not** pattern-match the prose\nyourself.\n\n**If `skipped` is `true`:** the detector could not establish a scope (empty\n`$SCOPE`) or failed to run (stdin read error). Skip the checkpoint for this\nrun rather than asserting a verdict about input that was never examined; do\nnot raise it with the user.\n\n**If `detected` is `false`:** this phase does not integrate an external API. Skip\nthe checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** an external-API integration is in scope. You MUST\nproduce a **coverage matrix** before the plan is finalized.\n\n**If `detected` is `true` but the phase genuinely integrates no external API**\n(the detector is deterministic, not infallible — confirm by re-reading the phase\nscope, not by preference): do NOT fabricate a matrix row for a capability that\ndoes not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`\ninstead:\n\n```markdown\nNo external API integration: <one-line reason — what the phase touches instead>.\n```\n\nThe reason is required, exactly like an `OPT-OUT` reason. The seal-time gate\naccepts this declaration in place of a matrix.\n\n## Produce the coverage matrix\n\nEnumerate the external API's full **capability surface** — the verb/endpoint/method\nlist (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,\n`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability\nrecord a decision, starting from **full coverage** as the default:\n\n| capability | decision | reason |\n|---|---|---|\n| `<capability-id>` | `INTEGRATE` \\| `OPT-OUT` | `<one-line reason if OPT-OUT>` |\n\nRules:\n\n- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the\n matrix is the *subtraction record*.\n- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed\n yet`, `explicitly out of scope`, …). An opt-out without a reason is an\n un-decided hole — the exact failure mode this gate exists to close.\n- **A second integration against the same need** (e.g. a second platform for the\n same capability) starts from the **same full-coverage baseline** as the first.\n Do not carry over the first integration's opt-outs silently — re-decide each\n capability for the new surface, so a first-class/fallback asymmetry cannot\n accumulate.\n\nWrite the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):\n\n```markdown\n# API Coverage — <service>\n\n> Full coverage by default. Opt-outs are explicit, reasoned decisions.\n\n| capability | decision | reason |\n|---|---|---|\n| search | INTEGRATE | |\n| playlists | INTEGRATE | |\n| skip | OPT-OUT | not needed yet — tracked for follow-up phase |\n```\n\nA fenced ` ```coverage ` JSON block is also accepted for machine-generated\nmatrices; the markdown table is preferred (human-editable, diff-friendly).\n\n## The seal-time gate\n\nThis checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate\nruns `check api-coverage.verify-pre <phase-dir>`:\n\n- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and\n every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A\n reasoned `No external API integration: …` declaration (and no rows) passes.\n- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a\n strong external-API-integration signal is found, the seal is **blocked** until a\n matrix is produced. If no signal is found, the phase is treated as a non-API\n phase and the seal proceeds.\n\nSo: an API-integrating phase cannot seal without a decided matrix. Produce it at\nplan time; do not leave it for seal time.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in\n`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it\nfor a project, override at the call site:\n\n```bash\nprintf '%s' \"$SCOPE\" | node gsd-core/bin/lib/api-coverage.cjs --json \\\n --verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin\n```\n\nThe whole checkpoint is toggleable via `workflow.api_coverage_gate` in\n`.planning/config.json`.\n"
3905
4260
  },
3906
4261
  "produces": [
3907
4262
  "COVERAGE.md"
@@ -3918,7 +4273,7 @@ const byLoopPoint = {
3918
4273
  "into": "planner",
3919
4274
  "fragment": {
3920
4275
  "path": "fragments/plan-pre.md",
3921
- "inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null || echo '{\"detected\":false,\"signals\":[],\"terms\":{}}')\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase), the query emits `{ \"detected\": false, ... }` — the checkpoint does not fire. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
4276
+ "inline": "# Assumption-Delta Architecture Checkpoint\n\n> Advisory, non-blocking. Fires **only** when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.\n\n## Why this exists\n\nMost quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the *seam*: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.\n\n## Run the detector\n\nThe detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: `{ detected, signals[], terms }`. Resolve it through the `assumption-delta scan` query (same phase-section resolver as `roadmap.get-phase`):\n\n```bash\nASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan \"${PHASE}\" --json 2>/dev/null) || true\n[ -n \"$ASSUMPTION_DELTA_JSON\" ] || ASSUMPTION_DELTA_JSON='{\"skipped\":true,\"reason\":\"probe_unavailable\"}'\n```\n\n> If the phase section cannot be resolved (no `ROADMAP.md` / unknown phase, or a section with no body), the query emits `{ \"skipped\": true, \"reason\": \"phase_unresolved\" }` — **not** `detected:false`. A probe that never had input does not get to assert that this phase changes no core assumption. The checkpoint does not fire either way; the difference is that a skip is now distinguishable from a real negative. Do not block on it.\n>\n> Optional tuning — pass `--terms <comma-list>` to replace the curated pluralization cues for this project (the `optional`/`chosen` cues keep their defaults): `gsd_run query assumption-delta scan \"${PHASE}\" --json --terms second,alternative,fallback`.\n\n## Decision branch\n\nRead `ASSUMPTION_DELTA_JSON`. Act on `detected` only — do **not** pattern-match the human prose.\n\n**If `skipped` is `true`:** the detector never examined a phase section — it could not resolve one (`phase_unresolved`) or could not run at all (`probe_unavailable`). Skip the checkpoint for this run rather than asserting a verdict about input that was never examined; do not raise it with the user. **Check for `skipped` before reading `detected`** — a skipped payload carries no `detected` key, and treating its absence as `false` re-creates the fabrication this branch exists to prevent.\n\n**If `detected` is `false`:** this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.\n\n**If `detected` is `true`:** a core assumption may have lost its monopoly. The `signals[]` array tells you which family fired:\n\n| `kind` | What changed | The question to answer |\n|---|---|---|\n| `pluralization` | A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) | Does the current primary key / identity model still name the right noun? |\n| `optional` | A required / `only` field became optional | Is the field still the right anchor, or has the anchor moved? |\n| `chosen` | A derived value became chosen, or a constant became a parameter | Has a configuration decision become a modeling decision? |\n\nBefore finalizing the plan, answer this for the user and record the decision explicitly:\n\n> **Promote vs. add-alongside.** The usual correct move when a generalization occurs is to **promote** the new general representation to the primary and **demote** the old specific one to a detail of one variant — *not* to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).\n\nRecord the outcome in the PLAN.md front matter / a `<assumption_delta_decision>` block:\n\n- The **noun** that is now primary (the generalized identity).\n- The **decision**: `promote` | `add-alongside` | `no-change`, with a one-line rationale.\n- If `add-alongside`: call it out as accepted debt and note what would force a later promote.\n\n## Optional companion: an invariant test\n\nWhen `detected` is `true`, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. *\"every confirmed default round-trips through the primary use-path, for every supported variant.\"* That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.\n\n## Tuning the vocabulary (optional)\n\nThe trigger vocabulary is a curated, additive-only set in `gsd-core/bin/lib/assumption-delta.cjs` (`DEFAULT_ASSUMPTION_DELTA_TERMS`). Bare \"or\" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with `--terms <comma-list>` (replaces the pluralization cues; `optional`/`chosen` keep defaults). The whole checkpoint is toggleable via `workflow.assumption_delta` in `.planning/config.json`.\n\nThis checkpoint is advisory: it informs and records; it never blocks the phase.\n"
3922
4277
  },
3923
4278
  "produces": [],
3924
4279
  "consumes": [
@@ -4068,7 +4423,7 @@ const byLoopPoint = {
4068
4423
  "into": "executor",
4069
4424
  "fragment": {
4070
4425
  "path": "fragments/execute-wave-pre.md",
4071
- "inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<files_to_read>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\nThe orchestrator still runs steps 4–5.8 (wait for completion, worktree cleanup,\npost-merge gate, tracking update) exactly as it does for inline dispatch — the\nWorkflow backend only replaces HOW agents are spawned for this wave, not what\nhappens after they return.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
4426
+ "inline": "# Claude orchestration — Workflow execution backend (BETA)\n\n> Injected at `execute:wave:pre` `into: executor` only when\n> `claude_orchestration.enabled` is true. Default-off; `onError: skip`.\n\n## When this contribution is active\n\nThe Claude orchestration capability is **default-off and BETA**. It activates only\nwhen ALL of the following hold:\n\n1. `claude_orchestration.enabled` is `true` in `.planning/config.json`, AND\n2. the active runtime is **Claude Code** (the Workflow tool is Claude / Agent\n SDK-specific), AND\n3. `claude_orchestration.execution_backend` resolves to `workflow` — either\n explicitly, or via `auto` — **and** the Agent SDK version is\n `>= claude_orchestration.min_agent_sdk_version` (default `0.3.149`). The SDK\n floor applies in both `auto` and `workflow` modes (fail-closed: a pre-release\n or older SDK never activates the preview backend).\n\nDetection is fail-closed: any miss degrades to **inline, manual, one-agent-per-\nmessage dispatch** — exactly today's behaviour. On a non-Claude runtime this\ncontribution is a no-op.\n\n## Why `execute:wave:pre` (not `execute:wave:post`)\n\nThis is a **dispatch-backend selector** — it decides HOW a wave's executor agents\nare spawned. That decision has to be made BEFORE the wave's `Agent()` calls in\n`execute-phase.md` step 3, not after the wave has already finished (#2285). The\ncapability previously registered at `execute:wave:post`, which fires only after\nworktree merge/post-merge tests/tracking updates — by then the wave was already\ndispatched inline, so the contribution was structurally unable to change how\ndispatch happened. This fragment is injected at the point that actually precedes\ndispatch.\n\n## What the orchestrator does when the Workflow backend is active\n\nBefore spawning executor agents for the current wave (execute-phase.md step 3),\nresolve the dispatch backend through the single composed CLI seam:\n\n```bash\ngsd-tools claude-orchestration resolve-wave-dispatch \\\n --waves \"$WAVE_MANIFEST_PATH\" --run-id \"$PHASE_RUN_ID\" \\\n --runtime \"$RUNTIME\" \\\n --phase-dir \"$PHASE_DIR\" --raw\n```\n\n`--agent-sdk-version` is no longer passed here (#2590). The router resolves the\ninstalled Agent SDK version itself; see **Agent SDK version** below. The former\n`${AGENT_SDK_VERSION:+--agent-sdk-version \"$AGENT_SDK_VERSION\"}` line was also\n**shell-dependent**: zsh does not word-split unquoted parameter expansions, so it\ncollapsed to a SINGLE argv element there, `argValue()` never matched, and the run\nfailed into `agent_sdk_version_unknown` — indistinguishable from genuinely\nunknown. Pass `--agent-sdk-version <ver>` explicitly only to pin a version.\n\nThis composes `detectWorkflowBackend` (the gate ladder above) with\n`emitWorkflowScript` (the wave→plan mapping below) in ONE call — the pure\nfunction backing it is `resolveWaveDispatch` in\n`gsd-core/bin/lib/claude-orchestration.cjs`. Response shape:\n`{ backend: 'inline'|'workflow', reason, script?, summary? }`.\n\n### Manifest construction (`$WAVE_MANIFEST_PATH`, `$PHASE_RUN_ID`, `$PHASE_DIR`)\n\nThese are NOT pre-existing execute-phase.md variables — the orchestrator builds\nthem at this step, from data it already has in-context from `discover_and_group_plans`\n(the `PLAN_INDEX` JSON) and step 2.5 (the per-plan `USE_WORKTREES_FOR_PLAN` decision):\n\n1. **`$PHASE_DIR`** — reuse `{phase_dir}` from the `INIT` bundle (already loaded\n in the `initialize` step). No new value needed.\n\n2. **`$PHASE_RUN_ID`** — a stable identifier for THIS phase-execution attempt, so\n `resumeFromRunId` can resume an interrupted run without re-dispatching plans\n the Workflow tool already completed. Construct it deterministically —\n `execute-{phase_number}-{phase_slug}` — from `INIT`'s `phase_number`/`phase_slug`\n (both are already validated identifiers used elsewhere in this workflow, so\n they satisfy `emitWorkflowScript`'s `isScriptableIdentifier` check). Do NOT\n mint a new random id per wave — the SAME `$PHASE_RUN_ID` is reused for every\n wave in the phase so the Workflow tool can correctly track cross-wave resume\n state.\n\n3. **`$WAVE_MANIFEST_PATH`** — a fresh temp file for THIS wave's manifest (one\n wave = one `waves` array with a single entry, matching the wave-by-wave\n dispatch loop; do not batch multiple waves into one manifest — waves are\n dispatched in wave order, not all at once):\n\n ```bash\n WAVE_MANIFEST_PATH=$(mktemp \"${TMPDIR:-/tmp}/gsd-wave-dispatch-XXXXXX\") && mv \"$WAVE_MANIFEST_PATH\" \"$WAVE_MANIFEST_PATH.json\" && WAVE_MANIFEST_PATH=\"$WAVE_MANIFEST_PATH.json\"\n ```\n\n Then **use the Write tool** (not a bash/jq pipeline — the orchestrator already\n has every field parsed in-context) to write the manifest JSON to\n `$WAVE_MANIFEST_PATH`:\n\n ```json\n {\n \"waves\": [\n {\n \"id\": \"wave-{N}\",\n \"plans\": [\n {\n \"id\": \"{plan_id}\",\n \"brief\": \"{the SAME <objective>...<success_criteria> prompt block step 3 builds for this plan's inline Agent() call}\",\n \"files_modified\": [\"{from PLAN_INDEX.plans[].files_modified for this plan}\"],\n \"use_worktree\": {true unless step 2.5 set USE_WORKTREES_FOR_PLAN=false for this plan}\n }\n ]\n }\n ]\n }\n ```\n\n - **`id`** — the plan id from `PLAN_INDEX`, e.g. `\"01-01\"`.\n - **`brief`** — MUST carry the same task content as step 3's inline `Agent()`\n prompt (the `<objective>`/`<execution_context>`/`<required_reading>`/\n `<success_criteria>` block, with `{plan_number}`/`{phase_number}`/\n `{phase_name}` substituted) — a short summary here would NOT reproduce\n step 3's behavior and would violate the \"identical artifacts\" contract.\n - **`files_modified`** — copy verbatim from the plan's `PLAN_INDEX` entry.\n - **`use_worktree`** — `true` for every plan UNLESS step 2.5's per-plan\n worktree gate (`execute-phase/steps/per-plan-worktree-gate.md`) set\n `USE_WORKTREES_FOR_PLAN=false` for that plan (submodule-touching plan, or\n project-level `USE_WORKTREES=false`) — in which case pass `false` here so\n `emitWorkflowScript` omits `isolation: \"worktree\"` for that plan (#2772 /\n #2285 finding 1). **Never** hardcode `true` — that would force worktree\n isolation on a plan the inline path explicitly keeps out of worktrees.\n\n4. **`$AGENT_SDK_VERSION`** — no longer built here; the router resolves it.\n\n**Agent SDK version:** the orchestrator has no *bash-computable* way to\nintrospect the live Agent SDK version — but the router runs in Node, so it\nresolves the version itself (#2590), in this order:\n\n1. an explicit `--agent-sdk-version <ver>` (pin a version),\n2. `GSD_AGENT_SDK_VERSION`,\n3. the **installed** `@anthropic-ai/claude-agent-sdk` package version, read from\n its `package.json` on disk by walking `node_modules` up the tree. (Read\n directly rather than via `require.resolve`: the SDK's `exports` map does not\n expose `./package.json`, so `require.resolve` throws\n `ERR_PACKAGE_PATH_NOT_EXPORTED`.)\n\nPreviously nothing computed this at all, so gate 5 returned\n`agent_sdk_version_unknown` on **every** automated run and the Workflow backend\ncould never activate — while `gsd-tools capability state` still reported the\ncapability `active: true`. Fail-closed is preserved: when no version can be\nresolved, gate 5 still declines to `inline`. What changed is that a resolvable\nversion is now actually found, so a genuinely-too-old SDK reports\n`agent_sdk_version_below_floor` — the truthful reason — instead of `unknown`.\n\n**If `backend == \"workflow\"`:** run the emitted `script` via the Workflow tool\nfor THIS wave instead of the per-message `Agent()` loop in step 3. The script\ncomposes the SAME `gsd-executor` agent type the inline path uses, with\nworktree isolation applied PER PLAN from the manifest's `use_worktree` field\n(see `emitWorkflowScript`):\n\n- **waves → one or more sequential `parallel()` barriers** — each wave is a\n barrier group; when plans within a wave share `files_modified`, they are split\n into separate sequential stages within that wave's barrier.\n- **plans → `agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })`**\n when `use_worktree` is not `false`, or `agent(brief, { agentType: 'gsd-executor' })`\n (no isolation) when it is — so the produced `SUMMARY.md` and commits are\n identical to inline dispatch, INCLUDING the inline path's submodule safety\n gate (#2772 / #2285 finding 1).\n- **`files_modified` overlap → separate sequential stages** — the same overlap\n rule execute-phase already applies inline (step 1 of the wave loop).\n- **`resumeFromRunId`** — **pass `summary.resumeRunId` as the Workflow tool's\n `resumeFromRunId` INPUT when you invoke the tool.** It is a tool parameter,\n not a script function; the script deliberately does not call it (#2590 — doing\n so threw \"resumeFromRunId is not defined\" and rejected the entire script).\n Omitting it from the tool invocation silently regresses phase-resume to a\n no-op: an interrupted phase re-runs completed plans.\n\n### After the run: manifest bridge into the merge chain (#3302)\n\nThe single Workflow tool call replaces step 3's per-plan `Agent()` loop — which also\nmeans step 3's manifest bookkeeping (creation + per-agent recording) does NOT happen on\nthis path. The orchestrator MUST bridge the run's per-agent results into the SAME\nmanifest-scoped merge chain inline dispatch uses, before steps 4–5.8, which then run\nunchanged:\n\n1. **Create the manifest BEFORE invoking the tool** (this is step 3's creation block,\n which this path skips). When ANY plan in the wave has `use_worktree` not `false`:\n\n ```bash\n if [ -z \"${WAVE_WORKTREE_MANIFEST:-}\" ]; then\n 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)\n # Persist the dispatch-time orchestrator worktree root so wave-cleanup pins back\n # to the orchestrator's OWN worktree (#630), exactly as inline dispatch does.\n ORCH_ROOT=$(git rev-parse --show-toplevel)\n ORCH_ROOT=\"$ORCH_ROOT\" MANIFEST=\"$WAVE_WORKTREE_MANIFEST\" node -e 'const fs=require(\"fs\");fs.writeFileSync(process.env.MANIFEST,JSON.stringify({orchestrator_root:process.env.ORCH_ROOT||null,worktrees:[]})+\"\\n\")'\n export WAVE_WORKTREE_MANIFEST\n fi\n ```\n\n2. **Invoke the Workflow tool with the emitted script and\n `resumeFromRunId: summary.resumeRunId`.** The script top-level `return`s one entry\n per dispatched plan: `{ plan, expects_worktree, metadata }`. `metadata` is that\n plan's executor `<worktree_metadata>` JSON (`{agent_id, worktree_path, branch,\n expected_base}` — captured by the executor itself per\n `agents/gsd-executor.md`), or `null` when the agent's result carried none\n (interrupted agent, resumed-from-cache plan, or a non-worktree plan).\n\n3. **Record every worktree plan** exactly as inline dispatch does at step 3's\n \"After each `Agent()` returns\" — one `worktree.record-agent` per returned entry\n with `expects_worktree: true` and complete metadata:\n\n ```bash\n gsd_run query worktree.record-agent --manifest \"$WAVE_WORKTREE_MANIFEST\" \\\n --agent-id \"<metadata.agent_id>\" --path \"<metadata.worktree_path>\" \\\n --branch \"<metadata.branch>\" --base \"<metadata.expected_base>\" \\\n --files \"<plan files_modified, space-separated>\" \\\n --deletions \"<plan files_deleted, space-separated>\"\n ```\n\n `--deletions` (#3003) carries the plan's declared `files_deleted` so a plan that scoped a file\n removal merges through `cleanup-wave` instead of being blocked. Unlike `--files` it is not\n advisory: omitting it leaves the deletions guard blocking on any deletion at all, so this\n dispatch path must pass it or plans declaring a removal fail to merge here while succeeding on\n the inline path.\n\n The verb's write-strict validation applies as inline: on a non-zero exit or any\n missing field, stop and ask for recovery — do not append an under-populated entry.\n\n4. **HALT on uncapturable metadata — never a silently-empty manifest (#3302).**\n After recording, the manifest must hold one entry per `expects_worktree: true`\n outcome (`summary.worktreePlans` from `resolve-wave-dispatch` is the expected\n count). Any shortfall — a `null` `metadata`, a missing/empty field, or a count\n mismatch — means commits are stranded on their `worktree-wf_*` branches and\n `worktree.cleanup-wave` would merge nothing while the phase looks green. STOP the\n phase with the failing plan id and the recovery hint below; do NOT run\n `worktree.cleanup-wave` and do NOT proceed to step 4.\n\n **Recovery hint:** the unmerged `worktree-wf_*` branch still holds the work. Recover\n the missing metadata from the run's per-agent result journal (`journal.jsonl` — one\n `{\"type\":\"result\",…}` line per agent — in the Workflow run's transcript dir), re-run\n `worktree.record-agent` by hand, then re-run cleanup. If the journal cannot be\n recovered either, merge the branch manually after review — never discard it.\n\n5. **Resume (`resumeFromRunId`).** Cached/resumed agents do not re-emit their final\n messages, so a previously-completed plan can return with `metadata: null`. Recover\n that plan's metadata from the ORIGINAL run's journal (same hint as above). If it\n cannot be recovered, fail loudly per rule 4 — a resumed run must never report\n success over silently-dropped agent work.\n\n6. **Non-worktree plans** (`expects_worktree: false` — `use_worktree: false` in the\n manifest): they ran without isolation; their commits are already on the main working\n tree. No record-agent entry, no manifest write.\n\nWith the manifest populated, steps 4–5.8 (wait/completion bookkeeping, step 5.5's\nmanifest-scoped `worktree.cleanup-wave`, post-merge gate, tracking update) run\nUNCHANGED — the Workflow backend replaces HOW agents are spawned and returns their\nmetadata; the merge chain itself is the inline path's own, now with real input.\n\n**If `backend == \"inline\"`** (any gate miss, or `resolve-wave-dispatch` itself\nunavailable/erroring): proceed to step 3's standard per-message `Agent()`\ndispatch — the default, byte-identical-to-today path. `onError: skip` on this\ncontribution means a `resolve-wave-dispatch` command failure is treated exactly\nlike an `inline` result, never as a fatal wave error.\n\n## Fallback contract\n\nDetection is fail-closed end-to-end: capability disabled, non-Claude runtime,\n`execution_backend:\"inline\"`, missing/incapable host descriptor, unknown or\nbelow-floor Agent SDK version, or an `emitWorkflowScript` failure on a malformed\nwave manifest — ANY of these degrades to `backend:\"inline\"` and execute-phase's\nstandard inline dispatch (step 3) runs unmodified. The Workflow backend never\npartially activates; the executor MUST NOT assume parallelism, a shared budget,\nor resume-from-run-id semantics when `backend == \"inline\"`.\n"
4072
4427
  },
4073
4428
  "produces": [],
4074
4429
  "consumes": [
@@ -4081,7 +4436,27 @@ const byLoopPoint = {
4081
4436
  "gates": []
4082
4437
  },
4083
4438
  "execute:wave:post": {
4084
- "steps": [],
4439
+ "steps": [
4440
+ {
4441
+ "capId": "live-dom-uat",
4442
+ "point": "execute:wave:post",
4443
+ "ref": {
4444
+ "agent": "gsd-dom-verifier"
4445
+ },
4446
+ "fragment": {
4447
+ "path": "fragments/execute-wave-post.md",
4448
+ "inline": "<objective>\nVerify the live-DOM acceptance criteria for the execution wave that just completed.\nAnswer: \"of this wave's stated UI acceptance criteria, which can I observe in a live DOM\nright now, and which could I not look at?\"\n\nThis step is ADDITIVE. It never halts the wave, never fails the phase, and never rewrites\nSUMMARY.md. If you cannot look, say so and finish.\n</objective>\n\n<required_reading>\n- {phase_dir}/{phase_num}-PLAN.md (the wave's tasks and their acceptance criteria)\n- {phase_dir}/{phase_num}-UI-SPEC.md if it exists (the design contract, when the phase has one)\n</required_reading>\n\n<browser_surface>\nYou carry exactly two browser MCP families: `mcp__chrome-devtools__*` and\n`mcp__claude-in-chrome__*`. Use whichever responds. Do not assume they expose the same\ntool names — probe, then use what is there. Do not paper over differences between them.\n\nYou do NOT carry the Playwright MCP family. That path belongs to the orchestrator's\nown verification step and is not yours.\n</browser_surface>\n\n<profile_lock>\n`chrome-devtools-mcp` holds an exclusive lock on its browser profile\n(`$HOME/.cache/chrome-devtools-mcp/chrome-profile`). A second concurrent instance fails with:\n\n```\nThe browser is already running for <dir>. Use --isolated to run multiple browser instances.\n```\n\nIf you see that, or any equivalent lock error:\n\n1. Record `outcome: could_not_look` and `reason: profile_locked`.\n2. Name `--isolated` in the notes, so the operator knows the remedy is a flag on THEIR MCP\n server registration.\n3. **Stop.** Do not retry, do not loop, do not wait for the lock. GSD cannot pass\n `--isolated` — it is not GSD's flag — and a retry loop here just holds up the wave.\n\nParallel execution waves sharing one profile WILL hit this. It is an expected condition,\nnot a defect, and it is not a reason to fail anything.\n</profile_lock>\n\n<method>\nFor each UI acceptance criterion you can identify in the wave's plan:\n\n1. Resolve its target URL. If no dev server or target is reachable, that criterion is\n `could_not_look` / `target_unreachable` — not a failure.\n2. Open it with the browser family that responded.\n3. Observe the DOM for the specific, stated condition. Assert on structure and content —\n an element's presence, its text, its attributes, its computed state.\n4. Record `passed` when the stated condition is observably true, `needs_review` when it is\n ambiguous or requires human judgement (subjective aesthetics, content accuracy).\n\nScope limit for this version: DOM observation against stated criteria only. No screenshot\ndiffing, no accessibility audit, no performance tracing. If a criterion needs one of those,\nmark it `needs_review` and say which.\n\nNever invent a criterion. If the plan states no UI acceptance criteria, that is\n`outcome: nothing_to_report` / `reason: no_criteria`, and it is a perfectly good result.\n</method>\n\n<output>\nWrite to: {phase_dir}/{phase_num}-DOM-VERIFY.md\n\nFrontmatter carries scalars only, so a reader can get the verdict without parsing prose:\n\n```\n---\nschema_version: 1\nwave: {wave_number}\noutcome: verified | nothing_to_report | could_not_look\nreason: ok | no_criteria | no_browser_mcp | profile_locked | target_unreachable\nchecked: <integer>\npassed: <integer>\nneeds_review: <integer>\n---\n```\n\nThen a short body: one line per criterion with its verdict, and — when `outcome` is\n`could_not_look` — exactly what stopped you and what the operator would change.\n\n**`nothing_to_report` and `could_not_look` are different outcomes and must never be\nconflated.** \"There were no UI criteria in this wave\" and \"there were criteria but I had no\nbrowser\" look identical in a summary that collapses them, and that ambiguity is the reported\nproblem this capability exists to remove.\n</output>\n"
4449
+ },
4450
+ "produces": [
4451
+ "DOM-VERIFY.md"
4452
+ ],
4453
+ "consumes": [
4454
+ "PLAN.md"
4455
+ ],
4456
+ "when": "workflow.live_dom_uat",
4457
+ "onError": "skip"
4458
+ }
4459
+ ],
4085
4460
  "contributions": [
4086
4461
  {
4087
4462
  "capId": "external-job",
@@ -4163,6 +4538,19 @@ const byLoopPoint = {
4163
4538
  ],
4164
4539
  "when": "workflow.code_review",
4165
4540
  "onError": "skip"
4541
+ },
4542
+ {
4543
+ "capId": "refactor-trigger",
4544
+ "point": "execute:post",
4545
+ "ref": {
4546
+ "command": "refactor evaluate"
4547
+ },
4548
+ "produces": [
4549
+ "REFACTOR.md"
4550
+ ],
4551
+ "consumes": [],
4552
+ "when": "refactor.trigger_enabled",
4553
+ "onError": "skip"
4166
4554
  }
4167
4555
  ],
4168
4556
  "contributions": [],
@@ -4320,15 +4708,20 @@ const configKeys = {
4320
4708
  "workflow.ai_integration_phase": "ai-integration",
4321
4709
  "workflow.api_coverage_gate": "ai-integration",
4322
4710
  "review.models.agy": "antigravity",
4711
+ "review.max_prompt_tokens_per_reviewer.antigravity": "antigravity",
4323
4712
  "workflow.assumption_delta": "assumption-delta",
4324
4713
  "workflow.windows_enforce": "broken-windows",
4325
4714
  "review.models.claude": "claude",
4715
+ "review.max_prompt_tokens_per_reviewer.claude": "claude",
4326
4716
  "claude_orchestration.enabled": "claude-orchestration",
4327
4717
  "claude_orchestration.execution_backend": "claude-orchestration",
4328
4718
  "claude_orchestration.min_agent_sdk_version": "claude-orchestration",
4329
4719
  "workflow.code_review": "code-review",
4330
4720
  "workflow.code_review_depth": "code-review",
4721
+ "review.max_prompt_tokens_per_reviewer.coderabbit": "coderabbit",
4331
4722
  "review.models.codex": "codex",
4723
+ "review.max_prompt_tokens_per_reviewer.codex": "codex",
4724
+ "review.max_prompt_tokens_per_reviewer.cursor": "cursor",
4332
4725
  "workflow.drift_threshold": "drift",
4333
4726
  "workflow.drift_action": "drift",
4334
4727
  "workflow.schema_drift_gate": "drift",
@@ -4340,9 +4733,12 @@ const configKeys = {
4340
4733
  "external_job.poll_timeout_ms": "external-job",
4341
4734
  "workflow.post_planning_gaps": "gap-analysis",
4342
4735
  "review.models.gemini": "gemini",
4736
+ "review.max_prompt_tokens_per_reviewer.gemini": "gemini",
4343
4737
  "graphify.enabled": "graphify",
4344
4738
  "intel.enabled": "intel",
4345
4739
  "review.models.kimi-code": "kimi-code",
4740
+ "review.max_prompt_tokens_per_reviewer.kimi-code": "kimi-code",
4741
+ "workflow.live_dom_uat": "live-dom-uat",
4346
4742
  "review.models.llama_cpp": "llama-cpp",
4347
4743
  "review.llama_cpp_host": "llama-cpp",
4348
4744
  "review.max_prompt_tokens_per_reviewer.llama_cpp": "llama-cpp",
@@ -4364,8 +4760,14 @@ const configKeys = {
4364
4760
  "review.ollama_host": "ollama",
4365
4761
  "review.max_prompt_tokens_per_reviewer.ollama": "ollama",
4366
4762
  "review.models.opencode": "opencode",
4763
+ "review.max_prompt_tokens_per_reviewer.opencode": "opencode",
4367
4764
  "workflow.pattern_mapper": "pattern-mapper",
4368
4765
  "profile-pipeline.enabled": "profile-pipeline",
4766
+ "review.max_prompt_tokens_per_reviewer.qwen": "qwen",
4767
+ "refactor.trigger_enabled": "refactor-trigger",
4768
+ "refactor.complexity_threshold": "refactor-trigger",
4769
+ "refactor.complexity_jump_delta": "refactor-trigger",
4770
+ "refactor.trigger_strict": "refactor-trigger",
4369
4771
  "workflow.research": "research",
4370
4772
  "workflow.schema_push_detection": "schema-gate",
4371
4773
  "workflow.security_enforcement": "security",
@@ -4396,6 +4798,12 @@ const configSchema = {
4396
4798
  "default": "",
4397
4799
  "description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
4398
4800
  },
4801
+ "review.max_prompt_tokens_per_reviewer.antigravity": {
4802
+ "owner": "antigravity",
4803
+ "type": "number",
4804
+ "default": -1,
4805
+ "description": "Prompt-token budget for the Antigravity reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\". Keyed on the reviewer slug `antigravity`, not the `agy` binary alias used by review.models.agy."
4806
+ },
4399
4807
  "workflow.assumption_delta": {
4400
4808
  "owner": "assumption-delta",
4401
4809
  "type": "boolean",
@@ -4414,6 +4822,12 @@ const configSchema = {
4414
4822
  "default": "",
4415
4823
  "description": "Model passed to the Claude reviewer lane."
4416
4824
  },
4825
+ "review.max_prompt_tokens_per_reviewer.claude": {
4826
+ "owner": "claude",
4827
+ "type": "number",
4828
+ "default": -1,
4829
+ "description": "Prompt-token budget for the Claude reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4830
+ },
4417
4831
  "claude_orchestration.enabled": {
4418
4832
  "owner": "claude-orchestration",
4419
4833
  "type": "boolean",
@@ -4454,12 +4868,30 @@ const configSchema = {
4454
4868
  "deep"
4455
4869
  ]
4456
4870
  },
4871
+ "review.max_prompt_tokens_per_reviewer.coderabbit": {
4872
+ "owner": "coderabbit",
4873
+ "type": "number",
4874
+ "default": -1,
4875
+ "description": "Prompt-token budget for the CodeRabbit reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4876
+ },
4457
4877
  "review.models.codex": {
4458
4878
  "owner": "codex",
4459
4879
  "type": "string",
4460
4880
  "default": "",
4461
4881
  "description": "Model passed to the Codex reviewer lane."
4462
4882
  },
4883
+ "review.max_prompt_tokens_per_reviewer.codex": {
4884
+ "owner": "codex",
4885
+ "type": "number",
4886
+ "default": -1,
4887
+ "description": "Prompt-token budget for the Codex reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4888
+ },
4889
+ "review.max_prompt_tokens_per_reviewer.cursor": {
4890
+ "owner": "cursor",
4891
+ "type": "number",
4892
+ "default": -1,
4893
+ "description": "Prompt-token budget for the Cursor reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4894
+ },
4463
4895
  "workflow.drift_threshold": {
4464
4896
  "owner": "drift",
4465
4897
  "type": "number",
@@ -4533,6 +4965,12 @@ const configSchema = {
4533
4965
  "default": "",
4534
4966
  "description": "Model passed to the Gemini reviewer lane."
4535
4967
  },
4968
+ "review.max_prompt_tokens_per_reviewer.gemini": {
4969
+ "owner": "gemini",
4970
+ "type": "number",
4971
+ "default": -1,
4972
+ "description": "Prompt-token budget for the Gemini reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4973
+ },
4536
4974
  "graphify.enabled": {
4537
4975
  "owner": "graphify",
4538
4976
  "type": "boolean",
@@ -4551,6 +4989,18 @@ const configSchema = {
4551
4989
  "default": "",
4552
4990
  "description": "Model passed to the Kimi Code reviewer lane."
4553
4991
  },
4992
+ "review.max_prompt_tokens_per_reviewer.kimi-code": {
4993
+ "owner": "kimi-code",
4994
+ "type": "number",
4995
+ "default": -1,
4996
+ "description": "Prompt-token budget for the Kimi Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
4997
+ },
4998
+ "workflow.live_dom_uat": {
4999
+ "owner": "live-dom-uat",
5000
+ "type": "boolean",
5001
+ "default": false,
5002
+ "description": "Enable live-DOM verification. Default-off: browser MCP reach is opt-in per project. When on, the orchestrator's automated UI verification may additionally use mcp__chrome-devtools__* / mcp__claude-in-chrome__* when present, and a gsd-dom-verifier step runs after each execution wave. When off, neither surface reaches a browser and the pre-existing mcp__playwright__* path is unchanged."
5003
+ },
4554
5004
  "review.models.llama_cpp": {
4555
5005
  "owner": "llama-cpp",
4556
5006
  "type": "string",
@@ -4682,6 +5132,12 @@ const configSchema = {
4682
5132
  "default": "",
4683
5133
  "description": "Model passed to the OpenCode reviewer lane."
4684
5134
  },
5135
+ "review.max_prompt_tokens_per_reviewer.opencode": {
5136
+ "owner": "opencode",
5137
+ "type": "number",
5138
+ "default": -1,
5139
+ "description": "Prompt-token budget for the OpenCode reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
5140
+ },
4685
5141
  "workflow.pattern_mapper": {
4686
5142
  "owner": "pattern-mapper",
4687
5143
  "type": "boolean",
@@ -4694,6 +5150,36 @@ const configSchema = {
4694
5150
  "default": false,
4695
5151
  "description": "Enable the developer profiling pipeline commands (scan-sessions, extract-messages, profile-sample, write-profile, etc.)."
4696
5152
  },
5153
+ "review.max_prompt_tokens_per_reviewer.qwen": {
5154
+ "owner": "qwen",
5155
+ "type": "number",
5156
+ "default": -1,
5157
+ "description": "Prompt-token budget for the Qwen Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
5158
+ },
5159
+ "refactor.trigger_enabled": {
5160
+ "owner": "refactor-trigger",
5161
+ "type": "boolean",
5162
+ "default": false,
5163
+ "description": "Enable the complexity-triggered refactor hook. When true, an execute:post step evaluates the complexity of the files the phase touched and writes a scoped refactor proposal if a function crosses refactor.complexity_threshold or jumps past refactor.complexity_jump_delta. Opt-in; when false the hook never runs. Issue #1953."
5164
+ },
5165
+ "refactor.complexity_threshold": {
5166
+ "owner": "refactor-trigger",
5167
+ "type": "number",
5168
+ "default": 15,
5169
+ "description": "Absolute per-function complexity above which a refactor proposal is surfaced. Semantics match ESLint's `complexity: {max: N}` — the trigger is STRICTLY GREATER, so a score of exactly N does not trigger. Default 15 follows SonarSource's default; ESLint's own default is 20 and radon's rank C begins at 11. Raise it if proposals feel like noise."
5170
+ },
5171
+ "refactor.complexity_jump_delta": {
5172
+ "owner": "refactor-trigger",
5173
+ "type": "number",
5174
+ "default": 5,
5175
+ "description": "Complexity growth above which a refactor proposal is surfaced even when the absolute threshold is not reached. Measured against the function's anchor — the score recorded the last time the function was consciously dispositioned — so it accumulates across phases and catches slow creep the absolute threshold would miss. Strictly greater, as with the threshold."
5176
+ },
5177
+ "refactor.trigger_strict": {
5178
+ "owner": "refactor-trigger",
5179
+ "type": "boolean",
5180
+ "default": false,
5181
+ "description": "Record an untriaged refactor proposal as an open `deviation` entry in the broken-windows ledger, so it becomes a tracked task that must be resolved before ship. Off by default and deliberately so: a blocking complexity number is a metric an executor can satisfy by splitting one coherent function into two incoherent ones, so the entry clears on the proposal being DISPOSITIONED (gsd-tools refactor accept|decline), never on the score improving. Ship blocking is the broken-windows capability's existing ship:pre gate — enable it with workflow.windows_enforce. With broken-windows absent, strict mode still records the proposal locally and says so; it cannot block on its own. Advisory mode (the default) surfaces the same proposal and tracks nothing."
5182
+ },
4697
5183
  "workflow.research": {
4698
5184
  "owner": "research",
4699
5185
  "type": "boolean",
@@ -4761,9 +5247,9 @@ const runtimes = {
4761
5247
  "antigravity": {
4762
5248
  "id": "antigravity",
4763
5249
  "role": "runtime",
4764
- "version": "1.10.0",
5250
+ "version": "1.12.0",
4765
5251
  "title": "Antigravity",
4766
- "description": "Google Antigravity IDE — nested under ~/.gemini/antigravity; probed across 1.x and 2.x layouts; Gemini hook event dialect; flat skill layout; tier-1 support.",
5252
+ "description": "Google Antigravity IDE — config/settings home nested under ~/.gemini/antigravity (probed across 1.x and 2.x layouts); global skills/agents install under ~/.gemini/config, the dir AGY scans for global discovery (#3738); Gemini hook event dialect; flat skill layout; tier-1 support.",
4767
5253
  "tier": "core",
4768
5254
  "requires": [],
4769
5255
  "engines": {
@@ -4794,7 +5280,8 @@ const runtimes = {
4794
5280
  "prefix": "gsd-",
4795
5281
  "nesting": "flat",
4796
5282
  "recursive": false,
4797
- "converter": "convertClaudeCommandToAntigravitySkill"
5283
+ "converter": "convertClaudeCommandToAntigravitySkill",
5284
+ "home": ".gemini/config"
4798
5285
  },
4799
5286
  {
4800
5287
  "kind": "agents",
@@ -4802,7 +5289,8 @@ const runtimes = {
4802
5289
  "prefix": "gsd-",
4803
5290
  "nesting": "flat",
4804
5291
  "recursive": false,
4805
- "converter": "convertClaudeAgentToAntigravityAgent"
5292
+ "converter": "convertClaudeAgentToAntigravityAgent",
5293
+ "home": ".gemini/config"
4806
5294
  }
4807
5295
  ],
4808
5296
  "local": [
@@ -4824,6 +5312,10 @@ const runtimes = {
4824
5312
  }
4825
5313
  ]
4826
5314
  },
5315
+ "triggerPrecedence": [
5316
+ "skills",
5317
+ "commands"
5318
+ ],
4827
5319
  "commandStyle": "slash-hyphen",
4828
5320
  "hooksSurface": "settings-json",
4829
5321
  "hookEvents": "gemini",
@@ -4853,7 +5345,6 @@ const runtimes = {
4853
5345
  "effortSurface": "undocumented"
4854
5346
  },
4855
5347
  "hostBehaviors": {
4856
- "reviewerCli": true,
4857
5348
  "projectInstructionFile": "GEMINI.md",
4858
5349
  "noPathRewrite": true,
4859
5350
  "hookPathStyle": "raw",
@@ -4890,7 +5381,7 @@ const runtimes = {
4890
5381
  "reviewsSection": "Antigravity",
4891
5382
  "evidenceClass": "source-grounded",
4892
5383
  "requiresBinaries": [],
4893
- "promptBudgetKey": null,
5384
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.antigravity",
4894
5385
  "modelConfigKey": "review.models.agy",
4895
5386
  "handler": "antigravity"
4896
5387
  },
@@ -4899,13 +5390,18 @@ const runtimes = {
4899
5390
  "type": "string",
4900
5391
  "default": "",
4901
5392
  "description": "Model passed to the Antigravity reviewer lane. The key suffix is the lane binary/flag alias `agy`, not the slug `antigravity` — preserved verbatim so existing .planning/config.json files keep working."
5393
+ },
5394
+ "review.max_prompt_tokens_per_reviewer.antigravity": {
5395
+ "type": "number",
5396
+ "default": -1,
5397
+ "description": "Prompt-token budget for the Antigravity reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\". Keyed on the reviewer slug `antigravity`, not the `agy` binary alias used by review.models.agy."
4902
5398
  }
4903
5399
  }
4904
5400
  },
4905
5401
  "augment": {
4906
5402
  "id": "augment",
4907
5403
  "role": "runtime",
4908
- "version": "1.10.0",
5404
+ "version": "1.12.0",
4909
5405
  "title": "Augment Code",
4910
5406
  "description": "Augment Code CLI — commands + nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
4911
5407
  "tier": "core",
@@ -4977,6 +5473,10 @@ const runtimes = {
4977
5473
  }
4978
5474
  ]
4979
5475
  },
5476
+ "triggerPrecedence": [
5477
+ "skills",
5478
+ "commands"
5479
+ ],
4980
5480
  "commandStyle": "slash-hyphen",
4981
5481
  "hooksSurface": "settings-json",
4982
5482
  "hookEvents": "claude",
@@ -5014,7 +5514,7 @@ const runtimes = {
5014
5514
  "claude": {
5015
5515
  "id": "claude",
5016
5516
  "role": "runtime",
5017
- "version": "1.10.0",
5517
+ "version": "1.12.0",
5018
5518
  "title": "Claude Code",
5019
5519
  "description": "Anthropic Claude Code — primary development runtime; tier-1 support with full hook surface and skills-based global install.",
5020
5520
  "tier": "core",
@@ -5041,6 +5541,14 @@ const runtimes = {
5041
5541
  "nesting": "flat",
5042
5542
  "recursive": false,
5043
5543
  "converter": "convertClaudeCommandToClaudeSkill"
5544
+ },
5545
+ {
5546
+ "kind": "agents",
5547
+ "destSubpath": "agents",
5548
+ "prefix": "gsd-",
5549
+ "nesting": "flat",
5550
+ "recursive": false,
5551
+ "converter": null
5044
5552
  }
5045
5553
  ],
5046
5554
  "local": [
@@ -5062,6 +5570,10 @@ const runtimes = {
5062
5570
  }
5063
5571
  ]
5064
5572
  },
5573
+ "triggerPrecedence": [
5574
+ "skills",
5575
+ "commands"
5576
+ ],
5065
5577
  "commandStyle": "slash-hyphen",
5066
5578
  "hooksSurface": "settings-json",
5067
5579
  "hookEvents": "claude",
@@ -5114,8 +5626,7 @@ const runtimes = {
5114
5626
  "skillsGlobalOnboarding": true,
5115
5627
  "legacyCommandsGsdInstallMigration": true,
5116
5628
  "legacyCommandsGsdUninstall": "global",
5117
- "hyphenNameAgentBody": true,
5118
- "reviewerCli": true
5629
+ "hyphenNameAgentBody": true
5119
5630
  }
5120
5631
  },
5121
5632
  "reviewer": {
@@ -5139,14 +5650,18 @@ const runtimes = {
5139
5650
  "promptChannel": "stdin",
5140
5651
  "outputChannel": "stdout",
5141
5652
  "modelArg": "--model",
5142
- "effortChannel": "argv"
5653
+ "effortChannel": "argv",
5654
+ "env": {
5655
+ "CLAUDE_CODE_DISABLE_CLAUDE_MDS": "1",
5656
+ "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1"
5657
+ }
5143
5658
  },
5144
5659
  "timeoutFloorMs": 1200000,
5145
5660
  "emptyOutput": "stub-with-stderr",
5146
5661
  "reviewsSection": "Claude",
5147
5662
  "evidenceClass": "source-grounded",
5148
5663
  "requiresBinaries": [],
5149
- "promptBudgetKey": null,
5664
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.claude",
5150
5665
  "modelConfigKey": "review.models.claude",
5151
5666
  "handler": null
5152
5667
  },
@@ -5155,13 +5670,18 @@ const runtimes = {
5155
5670
  "type": "string",
5156
5671
  "default": "",
5157
5672
  "description": "Model passed to the Claude reviewer lane."
5673
+ },
5674
+ "review.max_prompt_tokens_per_reviewer.claude": {
5675
+ "type": "number",
5676
+ "default": -1,
5677
+ "description": "Prompt-token budget for the Claude reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
5158
5678
  }
5159
5679
  }
5160
5680
  },
5161
5681
  "cline": {
5162
5682
  "id": "cline",
5163
5683
  "role": "runtime",
5164
- "version": "1.10.0",
5684
+ "version": "1.12.0",
5165
5685
  "title": "Cline",
5166
5686
  "description": "Cline (VS Code extension) — global-only nested-skill layout; cline-rules hook surface (.clinerules); no hook events emitted; tier-2 support.",
5167
5687
  "tier": "core",
@@ -5188,10 +5708,31 @@ const runtimes = {
5188
5708
  "nesting": "nested",
5189
5709
  "recursive": false,
5190
5710
  "converter": "convertClaudeCommandToClineSkill"
5711
+ },
5712
+ {
5713
+ "kind": "agents",
5714
+ "destSubpath": "agents",
5715
+ "prefix": "gsd-",
5716
+ "nesting": "flat",
5717
+ "recursive": false,
5718
+ "converter": "convertClaudeAgentToClineAgent"
5191
5719
  }
5192
5720
  ],
5193
- "local": []
5721
+ "local": [
5722
+ {
5723
+ "kind": "agents",
5724
+ "destSubpath": "agents",
5725
+ "prefix": "gsd-",
5726
+ "nesting": "flat",
5727
+ "recursive": false,
5728
+ "converter": "convertClaudeAgentToClineAgent"
5729
+ }
5730
+ ]
5194
5731
  },
5732
+ "triggerPrecedence": [
5733
+ "skills",
5734
+ "commands"
5735
+ ],
5195
5736
  "commandStyle": "slash-hyphen",
5196
5737
  "hooksSurface": "cline-rules",
5197
5738
  "sandboxTier": "none",
@@ -5232,7 +5773,7 @@ const runtimes = {
5232
5773
  "codebuddy": {
5233
5774
  "id": "codebuddy",
5234
5775
  "role": "runtime",
5235
- "version": "1.10.0",
5776
+ "version": "1.12.0",
5236
5777
  "title": "CodeBuddy",
5237
5778
  "description": "CodeBuddy (Tencent) — converted commands + skills artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
5238
5779
  "tier": "core",
@@ -5304,6 +5845,10 @@ const runtimes = {
5304
5845
  }
5305
5846
  ]
5306
5847
  },
5848
+ "triggerPrecedence": [
5849
+ "skills",
5850
+ "commands"
5851
+ ],
5307
5852
  "commandStyle": "slash-hyphen",
5308
5853
  "hooksSurface": "settings-json",
5309
5854
  "hookEvents": "claude",
@@ -5345,7 +5890,7 @@ const runtimes = {
5345
5890
  "codex": {
5346
5891
  "id": "codex",
5347
5892
  "role": "runtime",
5348
- "version": "1.10.0",
5893
+ "version": "1.12.0",
5349
5894
  "title": "OpenAI Codex CLI",
5350
5895
  "description": "OpenAI Codex CLI — shell-var command style; per-agent sandbox tiers; config.toml + hooks.json hook surface; tier-1 support.",
5351
5896
  "tier": "core",
@@ -5373,6 +5918,14 @@ const runtimes = {
5373
5918
  "recursive": false,
5374
5919
  "converter": "convertClaudeCommandToCodexSkill",
5375
5920
  "home": ".agents"
5921
+ },
5922
+ {
5923
+ "kind": "agents",
5924
+ "destSubpath": "agents",
5925
+ "prefix": "gsd-",
5926
+ "nesting": "flat",
5927
+ "recursive": false,
5928
+ "converter": "convertClaudeAgentToCodexAgent"
5376
5929
  }
5377
5930
  ],
5378
5931
  "local": [
@@ -5383,9 +5936,21 @@ const runtimes = {
5383
5936
  "nesting": "flat",
5384
5937
  "recursive": false,
5385
5938
  "converter": "convertClaudeCommandToCodexSkill"
5939
+ },
5940
+ {
5941
+ "kind": "agents",
5942
+ "destSubpath": "agents",
5943
+ "prefix": "gsd-",
5944
+ "nesting": "flat",
5945
+ "recursive": false,
5946
+ "converter": "convertClaudeAgentToCodexAgent"
5386
5947
  }
5387
5948
  ]
5388
5949
  },
5950
+ "triggerPrecedence": [
5951
+ "skills",
5952
+ "commands"
5953
+ ],
5389
5954
  "commandStyle": "shell-var",
5390
5955
  "hooksSurface": "codex-hooks-json",
5391
5956
  "hookEvents": "claude",
@@ -5424,15 +5989,15 @@ const runtimes = {
5424
5989
  "exec"
5425
5990
  ],
5426
5991
  "cwdFlag": "--cd",
5427
- "promptFlag": null
5992
+ "promptFlag": null,
5993
+ "modelFlag": "--model"
5428
5994
  },
5429
5995
  "hostBehaviors": {
5430
5996
  "reapplyCommand": "$gsd-update --reapply",
5431
5997
  "tomlConfigInstall": true,
5432
5998
  "cleanupSkillSidecars": true,
5433
5999
  "agentTomlFiles": true,
5434
- "frontmatterDialect": "codex",
5435
- "reviewerCli": true
6000
+ "frontmatterDialect": "codex"
5436
6001
  }
5437
6002
  },
5438
6003
  "reviewer": {
@@ -5467,7 +6032,7 @@ const runtimes = {
5467
6032
  "reviewsSection": "Codex",
5468
6033
  "evidenceClass": "source-grounded",
5469
6034
  "requiresBinaries": [],
5470
- "promptBudgetKey": null,
6035
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.codex",
5471
6036
  "modelConfigKey": "review.models.codex",
5472
6037
  "handler": null
5473
6038
  },
@@ -5476,13 +6041,18 @@ const runtimes = {
5476
6041
  "type": "string",
5477
6042
  "default": "",
5478
6043
  "description": "Model passed to the Codex reviewer lane."
6044
+ },
6045
+ "review.max_prompt_tokens_per_reviewer.codex": {
6046
+ "type": "number",
6047
+ "default": -1,
6048
+ "description": "Prompt-token budget for the Codex reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
5479
6049
  }
5480
6050
  }
5481
6051
  },
5482
6052
  "copilot": {
5483
6053
  "id": "copilot",
5484
6054
  "role": "runtime",
5485
- "version": "1.10.0",
6055
+ "version": "1.12.0",
5486
6056
  "title": "GitHub Copilot",
5487
6057
  "description": "GitHub Copilot (VS Code) — markdown config format; copilot-inline hook surface; no hook events emitted; flat skill nesting (unconfirmed recursive loader); tier-2 support.",
5488
6058
  "tier": "core",
@@ -5539,6 +6109,10 @@ const runtimes = {
5539
6109
  }
5540
6110
  ]
5541
6111
  },
6112
+ "triggerPrecedence": [
6113
+ "skills",
6114
+ "commands"
6115
+ ],
5542
6116
  "commandStyle": "slash-hyphen",
5543
6117
  "hooksSurface": "copilot-inline",
5544
6118
  "sandboxTier": "none",
@@ -5577,7 +6151,7 @@ const runtimes = {
5577
6151
  "cursor": {
5578
6152
  "id": "cursor",
5579
6153
  "role": "runtime",
5580
- "version": "1.10.0",
6154
+ "version": "1.12.0",
5581
6155
  "title": "Cursor",
5582
6156
  "description": "Cursor IDE — skills-only workflow surface; hooks.json surface; Claude hook event dialect; recursive skill loader (flat nesting); tier-2 support.",
5583
6157
  "tier": "core",
@@ -5633,6 +6207,10 @@ const runtimes = {
5633
6207
  }
5634
6208
  ]
5635
6209
  },
6210
+ "triggerPrecedence": [
6211
+ "skills",
6212
+ "commands"
6213
+ ],
5636
6214
  "commandStyle": "slash-hyphen",
5637
6215
  "hooksSurface": "cursor-hooks-json",
5638
6216
  "hookEvents": "claude",
@@ -5683,8 +6261,7 @@ const runtimes = {
5683
6261
  "stop",
5684
6262
  "subagentStart",
5685
6263
  "subagentStop"
5686
- ],
5687
- "reviewerCli": true
6264
+ ]
5688
6265
  }
5689
6266
  },
5690
6267
  "reviewer": {
@@ -5718,15 +6295,22 @@ const runtimes = {
5718
6295
  "reviewsSection": "Cursor",
5719
6296
  "evidenceClass": "source-grounded",
5720
6297
  "requiresBinaries": [],
5721
- "promptBudgetKey": null,
6298
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.cursor",
5722
6299
  "modelConfigKey": null,
5723
6300
  "handler": null
6301
+ },
6302
+ "config": {
6303
+ "review.max_prompt_tokens_per_reviewer.cursor": {
6304
+ "type": "number",
6305
+ "default": -1,
6306
+ "description": "Prompt-token budget for the Cursor reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
6307
+ }
5724
6308
  }
5725
6309
  },
5726
6310
  "hermes": {
5727
6311
  "id": "hermes",
5728
6312
  "role": "runtime",
5729
- "version": "1.10.0",
6313
+ "version": "1.12.0",
5730
6314
  "title": "Hermes Agent",
5731
6315
  "description": "Hermes Agent (NousResearch) — skills nest under skills/gsd/ category bucket; nested skill layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
5732
6316
  "tier": "core",
@@ -5753,6 +6337,14 @@ const runtimes = {
5753
6337
  "nesting": "nested",
5754
6338
  "recursive": false,
5755
6339
  "converter": "convertClaudeCommandToClaudeSkill"
6340
+ },
6341
+ {
6342
+ "kind": "agents",
6343
+ "destSubpath": "agents",
6344
+ "prefix": "gsd-",
6345
+ "nesting": "flat",
6346
+ "recursive": false,
6347
+ "converter": "convertClaudeAgentToHermesAgent"
5756
6348
  }
5757
6349
  ],
5758
6350
  "local": [
@@ -5763,9 +6355,21 @@ const runtimes = {
5763
6355
  "nesting": "nested",
5764
6356
  "recursive": false,
5765
6357
  "converter": "convertClaudeCommandToClaudeSkill"
6358
+ },
6359
+ {
6360
+ "kind": "agents",
6361
+ "destSubpath": "agents",
6362
+ "prefix": "gsd-",
6363
+ "nesting": "flat",
6364
+ "recursive": false,
6365
+ "converter": "convertClaudeAgentToHermesAgent"
5766
6366
  }
5767
6367
  ]
5768
6368
  },
6369
+ "triggerPrecedence": [
6370
+ "skills",
6371
+ "commands"
6372
+ ],
5769
6373
  "commandStyle": "slash-hyphen",
5770
6374
  "hooksSurface": "settings-json",
5771
6375
  "hookEvents": "claude",
@@ -5817,7 +6421,7 @@ const runtimes = {
5817
6421
  "kilo": {
5818
6422
  "id": "kilo",
5819
6423
  "role": "runtime",
5820
- "version": "1.10.0",
6424
+ "version": "1.12.0",
5821
6425
  "title": "Kilo Code",
5822
6426
  "description": "Kilo Code — XDG-based config dir; global skills at ~/.kilo/skills (separate from XDG config); flat command/ + skills artifact layout; no lifecycle hook registration; tier-2 support.",
5823
6427
  "tier": "core",
@@ -5859,6 +6463,14 @@ const runtimes = {
5859
6463
  "nesting": "flat",
5860
6464
  "recursive": true,
5861
6465
  "converter": "convertClaudeCommandToKiloSkill"
6466
+ },
6467
+ {
6468
+ "kind": "agents",
6469
+ "destSubpath": "agents",
6470
+ "prefix": "gsd-",
6471
+ "nesting": "flat",
6472
+ "recursive": false,
6473
+ "converter": "convertClaudeToKiloFrontmatter"
5862
6474
  }
5863
6475
  ],
5864
6476
  "local": [
@@ -5877,9 +6489,21 @@ const runtimes = {
5877
6489
  "nesting": "flat",
5878
6490
  "recursive": true,
5879
6491
  "converter": "convertClaudeCommandToKiloSkill"
6492
+ },
6493
+ {
6494
+ "kind": "agents",
6495
+ "destSubpath": "agents",
6496
+ "prefix": "gsd-",
6497
+ "nesting": "flat",
6498
+ "recursive": false,
6499
+ "converter": "convertClaudeToKiloFrontmatter"
5880
6500
  }
5881
6501
  ]
5882
6502
  },
6503
+ "triggerPrecedence": [
6504
+ "skills",
6505
+ "commands"
6506
+ ],
5883
6507
  "commandStyle": "slash-hyphen",
5884
6508
  "hooksSurface": "none",
5885
6509
  "extensionEvents": "kilo",
@@ -5926,7 +6550,7 @@ const runtimes = {
5926
6550
  "kimi": {
5927
6551
  "id": "kimi",
5928
6552
  "role": "runtime",
5929
- "version": "1.10.0",
6553
+ "version": "1.12.0",
5930
6554
  "title": "Kimi CLI",
5931
6555
  "description": "Kimi CLI (Moonshot AI) — generic agents root at ~/.config/agents; skills + kimi-agents artifact layout; native config.toml [[hooks]] bus at ~/.kimi/config.toml; background dispatch; tier-2 support.",
5932
6556
  "tier": "core",
@@ -5970,6 +6594,10 @@ const runtimes = {
5970
6594
  ],
5971
6595
  "local": []
5972
6596
  },
6597
+ "triggerPrecedence": [
6598
+ "skills",
6599
+ "commands"
6600
+ ],
5973
6601
  "commandStyle": "slash-hyphen",
5974
6602
  "hooksSurface": "kimi-hooks-toml",
5975
6603
  "hookEvents": "claude",
@@ -6024,7 +6652,7 @@ const runtimes = {
6024
6652
  "kimi-code": {
6025
6653
  "id": "kimi-code",
6026
6654
  "role": "runtime",
6027
- "version": "1.10.0",
6655
+ "version": "1.12.0",
6028
6656
  "title": "Kimi Code CLI",
6029
6657
  "description": "Kimi Code CLI (Moonshot AI, Node) — Agent Skills auto-discovered at ~/.kimi-code/skills; global AGENTS.md at ~/.kimi-code/AGENTS.md; native config.toml + [[hooks]] bus; three built-in subagents (coder/explore/plan), NO custom named subagents; background dispatch; tier-2 support. Distinct from Python kimi-cli (the 'kimi' capability) per ADR-1239 EoS — Kimi Code cannot dispatch named subagents so the kimi-agents YAML layout does NOT apply; persona injection rides the existing ${AGENT_SKILLS_*} workflow fallback. Install-layout, agent-install-check, and install-time decision (kimi vs kimi-code) land in follow-up PRs; this descriptor is the EoS foundation.",
6030
6658
  "tier": "core",
@@ -6055,10 +6683,31 @@ const runtimes = {
6055
6683
  "nesting": "flat",
6056
6684
  "recursive": false,
6057
6685
  "converter": "convertClaudeCommandToKimiCodeSkill"
6686
+ },
6687
+ {
6688
+ "kind": "agents",
6689
+ "destSubpath": "agents",
6690
+ "prefix": "gsd-",
6691
+ "nesting": "flat",
6692
+ "recursive": false,
6693
+ "converter": null
6058
6694
  }
6059
6695
  ],
6060
- "local": []
6696
+ "local": [
6697
+ {
6698
+ "kind": "agents",
6699
+ "destSubpath": "agents",
6700
+ "prefix": "gsd-",
6701
+ "nesting": "flat",
6702
+ "recursive": false,
6703
+ "converter": null
6704
+ }
6705
+ ]
6061
6706
  },
6707
+ "triggerPrecedence": [
6708
+ "skills",
6709
+ "commands"
6710
+ ],
6062
6711
  "commandStyle": "slash-hyphen",
6063
6712
  "hooksSurface": "kimi-hooks-toml",
6064
6713
  "hookEvents": "claude",
@@ -6141,7 +6790,7 @@ const runtimes = {
6141
6790
  "reviewsSection": "Kimi Code",
6142
6791
  "evidenceClass": "source-grounded",
6143
6792
  "requiresBinaries": [],
6144
- "promptBudgetKey": null,
6793
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.kimi-code",
6145
6794
  "modelConfigKey": "review.models.kimi-code",
6146
6795
  "handler": null
6147
6796
  },
@@ -6150,13 +6799,18 @@ const runtimes = {
6150
6799
  "type": "string",
6151
6800
  "default": "",
6152
6801
  "description": "Model passed to the Kimi Code reviewer lane."
6802
+ },
6803
+ "review.max_prompt_tokens_per_reviewer.kimi-code": {
6804
+ "type": "number",
6805
+ "default": -1,
6806
+ "description": "Prompt-token budget for the Kimi Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
6153
6807
  }
6154
6808
  }
6155
6809
  },
6156
6810
  "opencode": {
6157
6811
  "id": "opencode",
6158
6812
  "role": "runtime",
6159
- "version": "1.10.0",
6813
+ "version": "1.12.0",
6160
6814
  "title": "OpenCode",
6161
6815
  "description": "OpenCode — XDG-based config dir; flat commands/ + skills artifact layout; settings-json config format; no lifecycle hook registration; tier-2 support.",
6162
6816
  "tier": "core",
@@ -6193,6 +6847,14 @@ const runtimes = {
6193
6847
  "nesting": "flat",
6194
6848
  "recursive": true,
6195
6849
  "converter": "convertClaudeCommandToOpencodeSkill"
6850
+ },
6851
+ {
6852
+ "kind": "agents",
6853
+ "destSubpath": "agents",
6854
+ "prefix": "gsd-",
6855
+ "nesting": "flat",
6856
+ "recursive": false,
6857
+ "converter": "convertClaudeToOpencodeFrontmatter"
6196
6858
  }
6197
6859
  ],
6198
6860
  "local": [
@@ -6211,9 +6873,21 @@ const runtimes = {
6211
6873
  "nesting": "flat",
6212
6874
  "recursive": true,
6213
6875
  "converter": "convertClaudeCommandToOpencodeSkill"
6876
+ },
6877
+ {
6878
+ "kind": "agents",
6879
+ "destSubpath": "agents",
6880
+ "prefix": "gsd-",
6881
+ "nesting": "flat",
6882
+ "recursive": false,
6883
+ "converter": "convertClaudeToOpencodeFrontmatter"
6214
6884
  }
6215
6885
  ]
6216
6886
  },
6887
+ "triggerPrecedence": [
6888
+ "skills",
6889
+ "commands"
6890
+ ],
6217
6891
  "commandStyle": "slash-hyphen",
6218
6892
  "hooksSurface": "none",
6219
6893
  "extensionEvents": "opencode",
@@ -6264,8 +6938,7 @@ const runtimes = {
6264
6938
  "skipHomePrefixSubstitution": true,
6265
6939
  "skipSettingsUi": true,
6266
6940
  "skipUpdateBannerCommand": true,
6267
- "skipCodexSkillsManifest": true,
6268
- "reviewerCli": true
6941
+ "skipCodexSkillsManifest": true
6269
6942
  }
6270
6943
  },
6271
6944
  "reviewer": {
@@ -6298,7 +6971,7 @@ const runtimes = {
6298
6971
  "reviewsSection": "OpenCode",
6299
6972
  "evidenceClass": "source-grounded",
6300
6973
  "requiresBinaries": [],
6301
- "promptBudgetKey": null,
6974
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.opencode",
6302
6975
  "modelConfigKey": "review.models.opencode",
6303
6976
  "handler": "opencode"
6304
6977
  },
@@ -6307,13 +6980,18 @@ const runtimes = {
6307
6980
  "type": "string",
6308
6981
  "default": "",
6309
6982
  "description": "Model passed to the OpenCode reviewer lane."
6983
+ },
6984
+ "review.max_prompt_tokens_per_reviewer.opencode": {
6985
+ "type": "number",
6986
+ "default": -1,
6987
+ "description": "Prompt-token budget for the OpenCode reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
6310
6988
  }
6311
6989
  }
6312
6990
  },
6313
6991
  "pi": {
6314
6992
  "id": "pi",
6315
6993
  "role": "runtime",
6316
- "version": "1.10.0",
6994
+ "version": "1.12.0",
6317
6995
  "title": "pi",
6318
6996
  "description": "pi (pi.dev) — bun-runtime programmatic-CLI; TS ExtensionAPI (registerCommand/registerTool/registerProvider/pi.on); single native-extension file at ~/.pi/agent/extensions/gsd.js (.js, not .cjs — pi's extension auto-discovery accepts only .ts/.js, #2470); no shared-settings hook surface; tier-2 support.",
6319
6997
  "tier": "core",
@@ -6336,6 +7014,10 @@ const runtimes = {
6336
7014
  "global": [],
6337
7015
  "local": []
6338
7016
  },
7017
+ "triggerPrecedence": [
7018
+ "skills",
7019
+ "commands"
7020
+ ],
6339
7021
  "commandStyle": "slash-hyphen",
6340
7022
  "hooksSurface": "none",
6341
7023
  "extensionEvents": "pi",
@@ -6378,7 +7060,7 @@ const runtimes = {
6378
7060
  "qwen": {
6379
7061
  "id": "qwen",
6380
7062
  "role": "runtime",
6381
- "version": "1.10.0",
7063
+ "version": "1.12.0",
6382
7064
  "title": "Qwen Code",
6383
7065
  "description": "Qwen Code (Alibaba) — nested-skill artifact layout; settings-json hook surface; Claude hook event dialect; tier-2 support.",
6384
7066
  "tier": "core",
@@ -6434,6 +7116,10 @@ const runtimes = {
6434
7116
  }
6435
7117
  ]
6436
7118
  },
7119
+ "triggerPrecedence": [
7120
+ "skills",
7121
+ "commands"
7122
+ ],
6437
7123
  "commandStyle": "slash-hyphen",
6438
7124
  "hooksSurface": "settings-json",
6439
7125
  "hookEvents": "claude",
@@ -6477,8 +7163,7 @@ const runtimes = {
6477
7163
  "legacyCommandsGsdCleanup": true,
6478
7164
  "legacyCommandsGsdInstallMigration": true,
6479
7165
  "legacyCommandsGsdUninstall": true,
6480
- "hyphenNameAgentBody": true,
6481
- "reviewerCli": true
7166
+ "hyphenNameAgentBody": true
6482
7167
  }
6483
7168
  },
6484
7169
  "reviewer": {
@@ -6506,15 +7191,22 @@ const runtimes = {
6506
7191
  "reviewsSection": "Qwen",
6507
7192
  "evidenceClass": "source-grounded",
6508
7193
  "requiresBinaries": [],
6509
- "promptBudgetKey": null,
7194
+ "promptBudgetKey": "review.max_prompt_tokens_per_reviewer.qwen",
6510
7195
  "modelConfigKey": null,
6511
7196
  "handler": null
7197
+ },
7198
+ "config": {
7199
+ "review.max_prompt_tokens_per_reviewer.qwen": {
7200
+ "type": "number",
7201
+ "default": -1,
7202
+ "description": "Prompt-token budget for the Qwen Code reviewer lane. Unset is -1, a sentinel: 0 is a legitimate value meaning \"do not trim this lane\", so it cannot double as \"not configured\"."
7203
+ }
6512
7204
  }
6513
7205
  },
6514
7206
  "trae": {
6515
7207
  "id": "trae",
6516
7208
  "role": "runtime",
6517
- "version": "1.10.0",
7209
+ "version": "1.12.0",
6518
7210
  "title": "Trae IDE",
6519
7211
  "description": "Trae IDE — nested-skill artifact layout; no hook surface (profile-marker-only config); tier-2 support.",
6520
7212
  "tier": "core",
@@ -6570,6 +7262,10 @@ const runtimes = {
6570
7262
  }
6571
7263
  ]
6572
7264
  },
7265
+ "triggerPrecedence": [
7266
+ "skills",
7267
+ "commands"
7268
+ ],
6573
7269
  "commandStyle": "slash-hyphen",
6574
7270
  "hooksSurface": "none",
6575
7271
  "sandboxTier": "none",
@@ -6607,7 +7303,7 @@ const runtimes = {
6607
7303
  "vscode": {
6608
7304
  "id": "vscode",
6609
7305
  "role": "runtime",
6610
- "version": "1.10.0",
7306
+ "version": "1.12.0",
6611
7307
  "title": "VS Code",
6612
7308
  "description": "VS Code — Marketplace/VSIX extension; no file-projected config directory; IDE-profile reference host (active vscode.lm model, engine-owned hook bus, sandboxed globalState/workspaceState stateIO).",
6613
7309
  "tier": "core",
@@ -6627,6 +7323,10 @@ const runtimes = {
6627
7323
  "global": [],
6628
7324
  "local": []
6629
7325
  },
7326
+ "triggerPrecedence": [
7327
+ "skills",
7328
+ "commands"
7329
+ ],
6630
7330
  "commandStyle": "slash-hyphen",
6631
7331
  "hooksSurface": "none",
6632
7332
  "extensionEvents": "none",
@@ -6660,7 +7360,7 @@ const runtimes = {
6660
7360
  "windsurf": {
6661
7361
  "id": "windsurf",
6662
7362
  "role": "runtime",
6663
- "version": "1.10.0",
7363
+ "version": "1.12.0",
6664
7364
  "title": "Windsurf",
6665
7365
  "description": "Windsurf (Codeium) — workspace workflow artifact layout for slash commands; Cascade native hooks.json blocking hook bus (pre_write_code, pre_run_command); tier-2 support.",
6666
7366
  "tier": "core",
@@ -6709,6 +7409,10 @@ const runtimes = {
6709
7409
  }
6710
7410
  ]
6711
7411
  },
7412
+ "triggerPrecedence": [
7413
+ "skills",
7414
+ "commands"
7415
+ ],
6712
7416
  "commandStyle": "slash-hyphen",
6713
7417
  "hooksSurface": "windsurf-hooks-json",
6714
7418
  "sandboxTier": "none",
@@ -6747,7 +7451,7 @@ const runtimes = {
6747
7451
  "zcode": {
6748
7452
  "id": "zcode",
6749
7453
  "role": "runtime",
6750
- "version": "1.10.0",
7454
+ "version": "1.12.0",
6751
7455
  "title": "ZCode",
6752
7456
  "description": "ZCode (Z.ai) — desktop Agentic Development Environment for GLM-5.2; Claude-shaped nested skills at ~/.zcode/skills/<name>/SKILL.md, slash commands, named subagents, native MCP; declarative plugin surface; profile-marker install; tier-2 community support.",
6753
7457
  "tier": "core",
@@ -6789,7 +7493,7 @@ const runtimes = {
6789
7493
  "prefix": "gsd-",
6790
7494
  "nesting": "flat",
6791
7495
  "recursive": false,
6792
- "converter": null
7496
+ "converter": "convertClaudeAgentToZcodeAgent"
6793
7497
  }
6794
7498
  ],
6795
7499
  "local": [
@@ -6815,10 +7519,14 @@ const runtimes = {
6815
7519
  "prefix": "gsd-",
6816
7520
  "nesting": "flat",
6817
7521
  "recursive": false,
6818
- "converter": null
7522
+ "converter": "convertClaudeAgentToZcodeAgent"
6819
7523
  }
6820
7524
  ]
6821
7525
  },
7526
+ "triggerPrecedence": [
7527
+ "skills",
7528
+ "commands"
7529
+ ],
6822
7530
  "commandStyle": "slash-hyphen",
6823
7531
  "hooksSurface": "none",
6824
7532
  "sandboxTier": "none",
@@ -6909,6 +7617,11 @@ const commandFamilies = {
6909
7617
  "module": "profile-pipeline-command-router.cjs",
6910
7618
  "router": "routeProfileSample"
6911
7619
  },
7620
+ "refactor": {
7621
+ "capId": "refactor-trigger",
7622
+ "module": "refactor-trigger-command-router.cjs",
7623
+ "router": "routeRefactorTriggerCommand"
7624
+ },
6912
7625
  "scan-sessions": {
6913
7626
  "capId": "profile-pipeline",
6914
7627
  "module": "profile-pipeline-command-router.cjs",
@@ -7027,6 +7740,7 @@ const _requiresGraph = {
7027
7740
  "kilo": [],
7028
7741
  "kimi": [],
7029
7742
  "kimi-code": [],
7743
+ "live-dom-uat": [],
7030
7744
  "llama-cpp": [],
7031
7745
  "lm-studio": [],
7032
7746
  "mempalace": [],
@@ -7039,6 +7753,7 @@ const _requiresGraph = {
7039
7753
  "pi": [],
7040
7754
  "profile-pipeline": [],
7041
7755
  "qwen": [],
7756
+ "refactor-trigger": [],
7042
7757
  "research": [],
7043
7758
  "schema-gate": [],
7044
7759
  "security": [],