@opengsd/gsd-core 1.13.0 → 1.15.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 (441) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ja-JP.md +3 -3
  4. package/README.ko-KR.md +3 -3
  5. package/README.pt-BR.md +3 -3
  6. package/README.zh-CN.md +3 -3
  7. package/agents/gsd-advisor-researcher.compact.md +85 -0
  8. package/agents/gsd-ai-researcher.compact.md +96 -0
  9. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  10. package/agents/gsd-code-fixer.compact.md +459 -0
  11. package/agents/gsd-code-fixer.md +9 -8
  12. package/agents/gsd-code-reviewer.compact.md +269 -0
  13. package/agents/gsd-code-reviewer.md +15 -3
  14. package/agents/gsd-codebase-mapper.compact.md +760 -0
  15. package/agents/gsd-debug-session-manager.compact.md +360 -0
  16. package/agents/gsd-debug-session-manager.md +17 -2
  17. package/agents/gsd-debugger.md +2 -2
  18. package/agents/gsd-doc-classifier.compact.md +192 -0
  19. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  20. package/agents/gsd-doc-verifier.compact.md +143 -0
  21. package/agents/gsd-doc-writer.compact.md +440 -0
  22. package/agents/gsd-dom-verifier.compact.md +138 -0
  23. package/agents/gsd-domain-researcher.compact.md +141 -0
  24. package/agents/gsd-eval-auditor.compact.md +160 -0
  25. package/agents/gsd-eval-auditor.md +1 -1
  26. package/agents/gsd-eval-planner.compact.md +137 -0
  27. package/agents/gsd-executor.md +13 -8
  28. package/agents/gsd-framework-selector.compact.md +82 -0
  29. package/agents/gsd-integration-checker.compact.md +245 -0
  30. package/agents/gsd-intel-updater.compact.md +226 -0
  31. package/agents/gsd-intel-updater.md +1 -1
  32. package/agents/gsd-mempalace-curator.compact.md +45 -0
  33. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  34. package/agents/gsd-pattern-mapper.compact.md +275 -0
  35. package/agents/gsd-phase-researcher.md +19 -11
  36. package/agents/gsd-plan-checker.md +8 -7
  37. package/agents/gsd-planner.md +12 -8
  38. package/agents/gsd-project-researcher.compact.md +587 -0
  39. package/agents/gsd-project-researcher.md +1 -1
  40. package/agents/gsd-research-synthesizer.compact.md +212 -0
  41. package/agents/gsd-research-synthesizer.md +1 -1
  42. package/agents/gsd-roadmapper.compact.md +454 -0
  43. package/agents/gsd-roadmapper.md +13 -0
  44. package/agents/gsd-security-auditor.compact.md +162 -0
  45. package/agents/gsd-ui-auditor.compact.md +404 -0
  46. package/agents/gsd-ui-auditor.md +155 -17
  47. package/agents/gsd-ui-checker.compact.md +277 -0
  48. package/agents/gsd-ui-researcher.compact.md +282 -0
  49. package/agents/gsd-ui-researcher.md +1 -1
  50. package/agents/gsd-user-profiler.compact.md +108 -0
  51. package/agents/gsd-verifier.md +10 -9
  52. package/bin/install.js +848 -163
  53. package/commands/gsd/autonomous.md +2 -2
  54. package/commands/gsd/capture.md +1 -1
  55. package/commands/gsd/cleanup.md +1 -0
  56. package/commands/gsd/code-review.md +2 -1
  57. package/commands/gsd/complete-milestone.md +1 -0
  58. package/commands/gsd/config.md +1 -0
  59. package/commands/gsd/debug.md +1 -0
  60. package/commands/gsd/graphify.md +1 -0
  61. package/commands/gsd/health.md +1 -0
  62. package/commands/gsd/mempalace-capture.md +8 -3
  63. package/commands/gsd/mempalace-recall.md +1 -0
  64. package/commands/gsd/new-milestone.md +1 -0
  65. package/commands/gsd/new-project.md +1 -0
  66. package/commands/gsd/next.md +1 -0
  67. package/commands/gsd/pause-work.md +1 -0
  68. package/commands/gsd/phase.md +1 -0
  69. package/commands/gsd/plan-review-convergence.md +6 -6
  70. package/commands/gsd/pr-branch.md +1 -0
  71. package/commands/gsd/progress.md +1 -1
  72. package/commands/gsd/quick-batch.md +1 -1
  73. package/commands/gsd/resume-work.md +1 -0
  74. package/commands/gsd/review-backlog.md +1 -0
  75. package/commands/gsd/review.md +2 -3
  76. package/commands/gsd/settings.md +2 -1
  77. package/commands/gsd/stats.md +1 -0
  78. package/commands/gsd/thread.md +1 -0
  79. package/commands/gsd/workspace.md +1 -0
  80. package/commands/gsd/workstreams.md +1 -0
  81. package/gsd-core/bin/check-latest-version.cjs +8 -3
  82. package/gsd-core/bin/gsd-tools.cjs +672 -146
  83. package/gsd-core/bin/lib/adr-parser.cjs +4 -2
  84. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  85. package/gsd-core/bin/lib/audit.cjs +119 -34
  86. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  87. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  88. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  89. package/gsd-core/bin/lib/capability-registry.cjs +96 -189
  90. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  91. package/gsd-core/bin/lib/capability-validator.cjs +14 -2
  92. package/gsd-core/bin/lib/check-command-router.cjs +213 -49
  93. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  94. package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
  95. package/gsd-core/bin/lib/commands.cjs +823 -112
  96. package/gsd-core/bin/lib/config-loader.cjs +66 -4
  97. package/gsd-core/bin/lib/config.cjs +186 -45
  98. package/gsd-core/bin/lib/coverage.cjs +1 -1
  99. package/gsd-core/bin/lib/decisions.cjs +164 -45
  100. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  101. package/gsd-core/bin/lib/frontmatter.cjs +13 -0
  102. package/gsd-core/bin/lib/graphify.cjs +10 -2
  103. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  104. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
  105. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  106. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  107. package/gsd-core/bin/lib/init.cjs +614 -86
  108. package/gsd-core/bin/lib/install-engine.cjs +29 -3
  109. package/gsd-core/bin/lib/install-profiles.cjs +14 -0
  110. package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
  111. package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
  112. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  113. package/gsd-core/bin/lib/milestone.cjs +37 -13
  114. package/gsd-core/bin/lib/model-resolver.cjs +253 -53
  115. package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
  116. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  117. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  118. package/gsd-core/bin/lib/phase-id.cjs +268 -27
  119. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  120. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  121. package/gsd-core/bin/lib/phase.cjs +393 -88
  122. package/gsd-core/bin/lib/plan-document.cjs +49 -1
  123. package/gsd-core/bin/lib/planning-document.cjs +459 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
  126. package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
  127. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  128. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  129. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  130. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  131. package/gsd-core/bin/lib/project-root.cjs +41 -2
  132. package/gsd-core/bin/lib/quick-batch.cjs +1 -1
  133. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  134. package/gsd-core/bin/lib/research-store.cjs +11 -12
  135. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  136. package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
  137. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  138. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  139. package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
  140. package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
  141. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
  142. package/gsd-core/bin/lib/roadmap.cjs +356 -42
  143. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
  144. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
  145. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
  146. package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
  147. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
  148. package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
  149. package/gsd-core/bin/lib/security.cjs +126 -7
  150. package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
  151. package/gsd-core/bin/lib/state-document.cjs +130 -28
  152. package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
  153. package/gsd-core/bin/lib/state-transition.cjs +181 -30
  154. package/gsd-core/bin/lib/state.cjs +265 -27
  155. package/gsd-core/bin/lib/surface.cjs +77 -3
  156. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  157. package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
  158. package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
  159. package/gsd-core/bin/lib/uat.cjs +9 -1
  160. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  161. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
  162. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  163. package/gsd-core/bin/lib/update-context.cjs +30 -24
  164. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  165. package/gsd-core/bin/lib/verification.cjs +315 -30
  166. package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
  167. package/gsd-core/bin/lib/verify.cjs +320 -48
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
  169. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  170. package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
  171. package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
  172. package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
  173. package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
  174. package/gsd-core/references/checkpoints.md +5 -3
  175. package/gsd-core/references/compact-content-gate.md +66 -0
  176. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  177. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  178. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  179. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  180. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  181. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  182. package/gsd-core/references/edge-probe.md +195 -21
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
  184. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  185. package/gsd-core/references/gsd-run-resolver.md +1 -1
  186. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  187. package/gsd-core/references/model-profiles.md +13 -4
  188. package/gsd-core/references/phase-argument-parsing.md +9 -7
  189. package/gsd-core/references/phase-id-convention.md +28 -0
  190. package/gsd-core/references/planner-gap-closure.md +2 -0
  191. package/gsd-core/references/planner-load-graph-context.md +24 -13
  192. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  193. package/gsd-core/references/planning-config.md +14 -2
  194. package/gsd-core/references/tdd.md +30 -4
  195. package/gsd-core/references/thinking-models-planning.md +18 -2
  196. package/gsd-core/references/ui-consideration-probe.md +10 -5
  197. package/gsd-core/references/verification-patterns.md +17 -4
  198. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  199. package/gsd-core/references/worktree-path-safety.md +433 -2
  200. package/gsd-core/templates/README.md +7 -1
  201. package/gsd-core/templates/state.md +6 -3
  202. package/gsd-core/templates/summary.compact.md +212 -0
  203. package/gsd-core/templates/user-setup.compact.md +199 -0
  204. package/gsd-core/templates/user-setup.md +0 -9
  205. package/gsd-core/templates/verification-report.md +1 -1
  206. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  207. package/gsd-core/workflows/add-backlog.md +1 -1
  208. package/gsd-core/workflows/add-phase.md +1 -1
  209. package/gsd-core/workflows/add-tests.md +2 -2
  210. package/gsd-core/workflows/add-todo.md +6 -5
  211. package/gsd-core/workflows/ai-integration-phase.md +11 -3
  212. package/gsd-core/workflows/audit-fix.md +1 -1
  213. package/gsd-core/workflows/audit-milestone.md +1 -1
  214. package/gsd-core/workflows/audit-uat.md +1 -1
  215. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  216. package/gsd-core/workflows/autonomous.md +29 -16
  217. package/gsd-core/workflows/check-todos.md +6 -4
  218. package/gsd-core/workflows/cleanup.md +5 -3
  219. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  220. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
  221. package/gsd-core/workflows/code-review-fix.md +108 -22
  222. package/gsd-core/workflows/code-review.md +216 -73
  223. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  224. package/gsd-core/workflows/complete-milestone.md +41 -264
  225. package/gsd-core/workflows/debug.md +3 -3
  226. package/gsd-core/workflows/diagnose-issues.md +1 -1
  227. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  228. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  229. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  230. package/gsd-core/workflows/discuss-phase.md +1 -1
  231. package/gsd-core/workflows/do.md +2 -2
  232. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  233. package/gsd-core/workflows/docs-update.md +17 -158
  234. package/gsd-core/workflows/edit-phase.md +1 -1
  235. package/gsd-core/workflows/eval-review.md +10 -3
  236. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  237. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
  238. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
  239. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  240. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
  241. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  242. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  243. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  244. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  245. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  246. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  247. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  248. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  249. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  250. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  251. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
  252. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  253. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  254. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
  255. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  256. package/gsd-core/workflows/execute-phase.md +83 -172
  257. package/gsd-core/workflows/execute-plan.md +24 -10
  258. package/gsd-core/workflows/explore.md +3 -3
  259. package/gsd-core/workflows/extract-learnings.md +2 -1
  260. package/gsd-core/workflows/fast.md +1 -1
  261. package/gsd-core/workflows/forensics.md +1 -1
  262. package/gsd-core/workflows/graduation.md +1 -1
  263. package/gsd-core/workflows/health.md +2 -2
  264. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  265. package/gsd-core/workflows/help/modes/full.md +5 -5
  266. package/gsd-core/workflows/help/modes/topic.md +15 -5
  267. package/gsd-core/workflows/help.md +1 -1
  268. package/gsd-core/workflows/import.md +2 -2
  269. package/gsd-core/workflows/inbox.md +2 -2
  270. package/gsd-core/workflows/ingest-docs.md +3 -3
  271. package/gsd-core/workflows/insert-phase.md +1 -1
  272. package/gsd-core/workflows/list-seeds.md +1 -1
  273. package/gsd-core/workflows/list-workspaces.md +1 -1
  274. package/gsd-core/workflows/manager.md +2 -2
  275. package/gsd-core/workflows/map-codebase.md +52 -5
  276. package/gsd-core/workflows/milestone-summary.md +1 -1
  277. package/gsd-core/workflows/mvp-phase.md +1 -1
  278. package/gsd-core/workflows/new-milestone.md +56 -14
  279. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  280. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  281. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  282. package/gsd-core/workflows/new-project.md +39 -209
  283. package/gsd-core/workflows/new-workspace.md +2 -2
  284. package/gsd-core/workflows/next.md +1 -1
  285. package/gsd-core/workflows/note.md +1 -1
  286. package/gsd-core/workflows/onboard.md +1 -1
  287. package/gsd-core/workflows/pause-work.md +1 -1
  288. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  289. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  290. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  291. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  292. package/gsd-core/workflows/plan-phase.md +45 -187
  293. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  294. package/gsd-core/workflows/plant-seed.md +62 -20
  295. package/gsd-core/workflows/pr-branch.md +132 -20
  296. package/gsd-core/workflows/profile-user.md +2 -2
  297. package/gsd-core/workflows/progress.md +1 -1
  298. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
  299. package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
  300. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
  301. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  302. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  303. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  304. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  305. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  306. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  307. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
  308. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  309. package/gsd-core/workflows/quick-batch.md +1 -1
  310. package/gsd-core/workflows/quick.md +29 -10
  311. package/gsd-core/workflows/reapply-patches.md +86 -6
  312. package/gsd-core/workflows/remove-phase.md +1 -1
  313. package/gsd-core/workflows/remove-workspace.md +2 -2
  314. package/gsd-core/workflows/resume-project.md +1 -1
  315. package/gsd-core/workflows/review.md +31 -16
  316. package/gsd-core/workflows/scan.md +1 -1
  317. package/gsd-core/workflows/secure-phase.md +3 -2
  318. package/gsd-core/workflows/settings-advanced.md +30 -10
  319. package/gsd-core/workflows/settings-integrations.md +2 -3
  320. package/gsd-core/workflows/settings.md +22 -9
  321. package/gsd-core/workflows/ship.md +3 -2
  322. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  323. package/gsd-core/workflows/sketch.md +1 -1
  324. package/gsd-core/workflows/smart-entry.md +2 -2
  325. package/gsd-core/workflows/spec-phase.md +15 -5
  326. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  327. package/gsd-core/workflows/spike.md +1 -1
  328. package/gsd-core/workflows/stats.md +1 -1
  329. package/gsd-core/workflows/sync-skills.md +5 -5
  330. package/gsd-core/workflows/thread.md +1 -1
  331. package/gsd-core/workflows/transition.md +1 -1
  332. package/gsd-core/workflows/ui-phase.md +44 -8
  333. package/gsd-core/workflows/ui-review.md +18 -4
  334. package/gsd-core/workflows/ultraplan-phase.md +1 -1
  335. package/gsd-core/workflows/undo.md +339 -20
  336. package/gsd-core/workflows/update.md +14 -12
  337. package/gsd-core/workflows/validate-phase.md +3 -2
  338. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  339. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  340. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  341. package/gsd-core/workflows/verify-work.md +101 -196
  342. package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
  343. package/hooks/dist/gsd-context-monitor.js +88 -15
  344. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  345. package/hooks/dist/gsd-secret-read-guard.js +71 -19
  346. package/hooks/dist/gsd-statusline.js +81 -20
  347. package/hooks/dist/gsd-validate-commit.sh +97 -8
  348. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  349. package/hooks/dist/gsd-write-guard.js +46 -1
  350. package/hooks/dist/lib/dispatch-identity.js +187 -0
  351. package/hooks/dist/lib/filename-classification.js +64 -0
  352. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  353. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  354. package/hooks/gsd-agent-isolation-guard.js +66 -16
  355. package/hooks/gsd-context-monitor.js +88 -15
  356. package/hooks/gsd-cursor-subagent-start.js +34 -14
  357. package/hooks/gsd-secret-read-guard.js +71 -19
  358. package/hooks/gsd-statusline.js +81 -20
  359. package/hooks/gsd-validate-commit.sh +97 -8
  360. package/hooks/gsd-worktree-path-guard.js +25 -14
  361. package/hooks/gsd-write-guard.js +46 -1
  362. package/hooks/lib/dispatch-identity.js +187 -0
  363. package/hooks/lib/filename-classification.js +64 -0
  364. package/hooks/lib/isolation-deny-reason.js +53 -1
  365. package/hooks/lib/isolation-sentinel.js +58 -19
  366. package/package.json +11 -6
  367. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  368. package/scripts/benchmark-compact-content.cjs +368 -0
  369. package/scripts/build-hooks.js +15 -6
  370. package/scripts/check-contract-drift.cjs +131 -12
  371. package/scripts/check-env.cjs +36 -8
  372. package/scripts/check-glossary-refs.cjs +25 -21
  373. package/scripts/ci-next-health.cjs +271 -0
  374. package/scripts/ci-prepare-test-scope.cjs +7 -7
  375. package/scripts/ci-test-scope.cjs +126 -20
  376. package/scripts/ci-timeout-report.cjs +1 -1
  377. package/scripts/command-contract-helpers.cjs +3 -0
  378. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  379. package/scripts/docs-guard-registry.cjs +35 -2
  380. package/scripts/gen-adr-index.cjs +8 -2
  381. package/scripts/gen-inventory-manifest.cjs +12 -0
  382. package/scripts/gen-loop-host-contract.cjs +69 -0
  383. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  384. package/scripts/lib/drift-scan.cjs +1 -1
  385. package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
  386. package/scripts/lib/ndjson-reporter.cjs +3 -2
  387. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  388. package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
  389. package/scripts/lib/suite-detection.cjs +32 -0
  390. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  391. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
  392. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  393. package/scripts/lint-phase-id-drift.cjs +623 -13
  394. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  395. package/scripts/lint-response-language-coverage.cjs +9 -3
  396. package/scripts/lint-retired-runtime-name.cjs +619 -0
  397. package/scripts/lint-source-test-name-collision.cjs +1 -1
  398. package/scripts/lint-state-write-path-drift.cjs +93 -0
  399. package/scripts/lint-test-file-count.allowlist.json +29 -9
  400. package/scripts/lint-vendored-deps.cjs +128 -17
  401. package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
  402. package/scripts/prompt-injection-scan.sh +18 -0
  403. package/scripts/release-tarball-smoke.cjs +194 -1
  404. package/scripts/workflow-size.cjs +139 -0
  405. package/skills/gsd-autonomous/SKILL.md +2 -2
  406. package/skills/gsd-capture/SKILL.md +1 -1
  407. package/skills/gsd-cleanup/SKILL.md +1 -0
  408. package/skills/gsd-code-review/SKILL.md +2 -1
  409. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  410. package/skills/gsd-config/SKILL.md +1 -0
  411. package/skills/gsd-debug/SKILL.md +1 -0
  412. package/skills/gsd-graphify/SKILL.md +1 -0
  413. package/skills/gsd-health/SKILL.md +1 -0
  414. package/skills/gsd-mempalace-capture/SKILL.md +8 -3
  415. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  416. package/skills/gsd-new-milestone/SKILL.md +1 -0
  417. package/skills/gsd-new-project/SKILL.md +1 -0
  418. package/skills/gsd-next/SKILL.md +1 -0
  419. package/skills/gsd-pause-work/SKILL.md +1 -0
  420. package/skills/gsd-phase/SKILL.md +1 -0
  421. package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
  422. package/skills/gsd-pr-branch/SKILL.md +1 -0
  423. package/skills/gsd-progress/SKILL.md +1 -1
  424. package/skills/gsd-quick-batch/SKILL.md +1 -1
  425. package/skills/gsd-resume-work/SKILL.md +1 -0
  426. package/skills/gsd-review/SKILL.md +2 -3
  427. package/skills/gsd-review-backlog/SKILL.md +1 -0
  428. package/skills/gsd-settings/SKILL.md +2 -1
  429. package/skills/gsd-stats/SKILL.md +1 -0
  430. package/skills/gsd-thread/SKILL.md +1 -0
  431. package/skills/gsd-workspace/SKILL.md +1 -0
  432. package/skills/gsd-workstreams/SKILL.md +1 -0
  433. package/vscode/package.json +1 -1
  434. package/gsd-core/templates/claude-md.md +0 -145
  435. package/gsd-core/templates/codebase/concerns.md +0 -310
  436. package/gsd-core/templates/codebase/conventions.md +0 -307
  437. package/gsd-core/templates/codebase/integrations.md +0 -280
  438. package/gsd-core/templates/codebase/structure.md +0 -285
  439. package/gsd-core/templates/codebase/testing.md +0 -480
  440. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  441. package/gsd-core/templates/discovery.md +0 -146
@@ -123,11 +123,89 @@ function resolveTierEntry({ runtime, tier, overrides }) {
123
123
  */
124
124
  function _resolveRuntimeTier(config, tier) {
125
125
  return resolveTierEntry({
126
- runtime: config['runtime'],
126
+ // #4505/#4495: TIER SELECTION follows the runtime that is actually
127
+ // resolving, not whatever the config file happens to say. `config.runtime`
128
+ // is frequently absent (the runtime is normally known from GSD_RUNTIME or
129
+ // the per-install marker), and reading it raw made
130
+ // `model_profile_overrides.<runtime>.<tier>` silently inert for every
131
+ // install that did not also write the key by hand.
132
+ runtime: resolveActiveRuntime(config),
127
133
  tier,
128
134
  overrides: config['model_profile_overrides'],
129
135
  });
130
136
  }
137
+ /**
138
+ * #4192 — Resolve the claude-runtime TIER OVERRIDE model for (config, tier).
139
+ *
140
+ * Step 3's runtime-aware resolution deliberately skips the claude runtime to
141
+ * preserve the alias-native posture (#1156/#2297): with no user override, the
142
+ * resolver must keep returning bare tier aliases, and the builtin claude tier
143
+ * map (`opus → claude-opus-4-8`, …) must never force full-ID emission on every
144
+ * default install. But `model_profile_overrides.<runtime>.<tier>` is a
145
+ * documented override point (docs/CONFIGURATION.md § Runtime-Aware Profiles)
146
+ * that `workflows/settings-advanced.md` actively writes for claude-runtime
147
+ * users — and #4192 Finding 1 measured the key inert on this runtime.
148
+ *
149
+ * This helper reads ONLY the user's override entry for the effective claude
150
+ * runtime and tier — never the builtin claude tier map — so an install with no
151
+ * override is byte-identical to before the fix.
152
+ *
153
+ * #4505 CHANGED how the runtime reaches here. #4192 originally passed
154
+ * `config['runtime']` and recorded that the value policy "must key off the
155
+ * config the operator wrote, NOT resolveActiveRuntime". That reading turned out
156
+ * to defeat #4192's own stated principle — that an explicit pin must not be
157
+ * silently UNPINNED. With `config.runtime` absent and GSD_RUNTIME=opencode, the
158
+ * config-keyed read treated the session as claude and collapsed an explicit
159
+ * `claude-opus-4-8` pin to the bare alias `opus`, which is a Claude-only token
160
+ * the actual runtime cannot spawn. The runtime is now the ACTIVE one, so the
161
+ * claude value policy applies exactly when claude is what is running. No test
162
+ * pinned the previous behaviour; the ones covering #4192's pins and the
163
+ * alias-native posture all still pass.
164
+ *
165
+ * Value policy mirrors the model_overrides path (#2041/#4192): an override
166
+ * value that maps to a current tier alias collapses to that alias
167
+ * (byte-equivalent resolution, alias-form emission); anything else — a pinned
168
+ * older generation (`claude-opus-4-7`), a bare alias/tier repoint (`sonnet`),
169
+ * or a non-Claude vendor id (`openai/o3`) — is emitted verbatim as pinned.
170
+ * Malformed entries (no usable `model` string) return null so the caller falls
171
+ * through to normal alias resolution (ADR-443 D1: invalid values fall through).
172
+ */
173
+ function resolveClaudeTierOverrideModel(
174
+ // The ACTIVE runtime (#4505), not the raw config key — see docblock.
175
+ activeRuntime, tier, overrides) {
176
+ if (!tier || tier === 'inherit')
177
+ return null;
178
+ const effectiveRuntime = activeRuntime || 'claude';
179
+ if (effectiveRuntime !== 'claude')
180
+ return null; // non-claude runtimes resolve at step 3
181
+ const overridesMap = overrides;
182
+ if (!overridesMap || typeof overridesMap !== 'object')
183
+ return null;
184
+ // Own-property guards throughout: both levels are config-supplied plain
185
+ // objects, so a prototype-chain key ("constructor", "toString") must not
186
+ // resolve an inherited member instead of falling through (same hardening as
187
+ // every other config-keyed lookup in this module).
188
+ const runtimeEntry = Object.hasOwn(overridesMap, effectiveRuntime)
189
+ ? overridesMap[effectiveRuntime]
190
+ : undefined;
191
+ if (!runtimeEntry || typeof runtimeEntry !== 'object')
192
+ return null;
193
+ const userRaw = Object.hasOwn(runtimeEntry, tier) ? runtimeEntry[tier] : undefined;
194
+ if (userRaw === undefined || userRaw === null)
195
+ return null;
196
+ const entry = typeof userRaw === 'string'
197
+ ? { model: userRaw }
198
+ : userRaw;
199
+ if (!entry || typeof entry !== 'object')
200
+ return null;
201
+ const model = entry['model'];
202
+ if (typeof model !== 'string' || model.length === 0)
203
+ return null;
204
+ if (Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, model)) {
205
+ return CLAUDE_POLICY_ID_TO_ALIAS[model];
206
+ }
207
+ return model;
208
+ }
131
209
  // Reverse of the Claude tier-default IDs, plus the Fable alias which Claude
132
210
  // Code's Agent tool accepts but which is not a GSD model-profile tier (#1133).
133
211
  const CLAUDE_POLICY_ID_TO_ALIAS = {
@@ -154,7 +232,10 @@ function warnModelPolicyUnmappable(agentType, policyModel, tier) {
154
232
  function _resetModelPolicyWarningCacheForTests() {
155
233
  _modelPolicyUnmappableWarned.clear();
156
234
  }
157
- // Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041).
235
+ // Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041 /
236
+ // #4192). #2041 originally warned that such a value was being DROPPED to tier
237
+ // resolution; #4192 keeps the warn-once breadcrumb but changes the behavior to
238
+ // a verbatim pass-through, so the text now describes the pass-through.
158
239
  const _modelOverrideUnmappableWarned = new Set();
159
240
  function warnModelOverrideUnmappable(agentType, overrideValue) {
160
241
  const key = `${agentType}::${overrideValue}`;
@@ -165,8 +246,9 @@ function warnModelOverrideUnmappable(agentType, overrideValue) {
165
246
  // full to stderr/logs (#2041 security review). MUST go to stderr — resolve-
166
247
  // model's JSON result is parsed from stdout.
167
248
  const safe = overrideValue.length > 64 ? overrideValue.slice(0, 64) + '…' : overrideValue;
168
- process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} ` +
169
- `has no Claude agent alias; falling through to tier resolution.\n`);
249
+ process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} is a fully-qualified ` +
250
+ `Claude model ID with no tier alias; passing it through verbatim. Claude Code setups ` +
251
+ `whose Agent tool accepts only tier aliases will not honor it. (#4192)\n`);
170
252
  }
171
253
  // Test-only: reset the model_overrides warn-dedupe cache between cases (#2041).
172
254
  function _resetModelOverrideWarningCacheForTests() {
@@ -175,25 +257,42 @@ function _resetModelOverrideWarningCacheForTests() {
175
257
  /**
176
258
  * #2041 — Map a `model_overrides` value to its Claude Agent-tool alias on the
177
259
  * claude runtime, mirroring the `model_policy` path (#1144). Claude Code's
178
- * Agent tool `model` parameter documents only tier aliases (opus/sonnet/haiku/
179
- * fable); a full Claude model ID returned verbatim is silently dropped by the
180
- * spawner. Returns the value to return verbatim, or null to signal "fall
181
- * through to normal tier/dynamic-routing resolution" (used when a Claude full
182
- * ID has no alias — matches model_policy's warn-and-fall-through). Non-Claude
183
- * runtimes and non-Claude values always pass through verbatim.
260
+ * Agent tool `model` parameter documents tier aliases (opus/sonnet/haiku/
261
+ * fable) as the always-accepted form. Returns the value to emit verbatim, or
262
+ * null to signal "fall through to normal tier/dynamic-routing resolution".
263
+ * Non-Claude runtimes and non-Claude values always pass through verbatim.
264
+ *
265
+ * #4192 — an unmappable `claude-*` value (a pinned generation that is not the
266
+ * current catalog default, e.g. `claude-opus-4-7`) is now PASSED THROUGH
267
+ * VERBATIM with a warn-once stderr breadcrumb, instead of being dropped to
268
+ * tier resolution. #2041's drop was correct when the value was plausibly a
269
+ * mis-typed current default, but for an explicit pin it silently UNPINNED the
270
+ * operator's choice — the resolver would report a tier the config never asked
271
+ * for, the exact "profile can misrepresent what actually runs" defect #4192
272
+ * files. The documented contract ("any fully-qualified model ID",
273
+ * docs/CONFIGURATION.md § Per-Agent Overrides,
274
+ * gsd-core/references/model-profiles.md § Per-Agent Overrides) is restored:
275
+ * the pin is resolved as configured. Values that DO map to a current tier
276
+ * alias still collapse to that alias — byte-equivalent resolution, the #2041
277
+ * protection preserved — and a mappable pin never warns.
184
278
  *
185
279
  * Hardening (code+security review): a `typeof` guard preserves the pre-fix
186
280
  * no-crash behavior if a malformed config surfaces a non-string value, and an
187
281
  * `Object.hasOwn` lookup defeats `__proto__`/`constructor` lookups on the plain
188
282
  * object literal so those reserved keys cannot return a truthy non-string.
189
283
  */
190
- function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
284
+ function mapClaudeOverrideForRuntime(override,
285
+ // The ACTIVE runtime (#4505). Previously the raw `config['runtime']`, which
286
+ // made an explicit claude pin collapse to a bare alias whenever the runtime
287
+ // was declared via GSD_RUNTIME or the install marker rather than the config —
288
+ // handing a Claude-only token to a runtime that cannot spawn it.
289
+ activeRuntime, agentType) {
191
290
  // Defensive: model_overrides is typed Record<string,string> but a malformed
192
291
  // config could surface a non-string; pass through verbatim (preserving the
193
292
  // pre-fix no-crash behaviour) and let the downstream Agent tool reject it.
194
293
  if (typeof override !== 'string')
195
294
  return override;
196
- const onClaude = !configRuntime || configRuntime === 'claude';
295
+ const onClaude = !activeRuntime || activeRuntime === 'claude';
197
296
  if (!onClaude)
198
297
  return override;
199
298
  // Object.hasOwn guards against __proto__/constructor returning a truthy
@@ -204,8 +303,9 @@ function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
204
303
  if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(override))
205
304
  return override;
206
305
  if (override.startsWith('claude-')) {
306
+ // #4192: explicit generation pin — resolve as configured (see docblock).
207
307
  warnModelOverrideUnmappable(agentType, override);
208
- return null;
308
+ return override;
209
309
  }
210
310
  return override;
211
311
  }
@@ -402,7 +502,7 @@ function resolveModelInternal(cwd, agentType) {
402
502
  ? modelOverrides[agentType]
403
503
  : undefined;
404
504
  if (override) {
405
- const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
505
+ const mapped = mapClaudeOverrideForRuntime(override, resolveActiveRuntime(config), agentType);
406
506
  if (mapped !== null)
407
507
  return mapped;
408
508
  // Unmappable Claude ID — fall through to tier resolution (matches model_policy).
@@ -419,10 +519,19 @@ function resolveModelInternal(cwd, agentType) {
419
519
  const agentModels = Object.hasOwn(modelProfilesMapForModel, agentType) ? modelProfilesMapForModel[agentType] : undefined;
420
520
  const tier = computeProfileTier(config, agentType);
421
521
  // 2.5. model_policy preset (#49, #1133)
522
+ // `configRuntime` is retained ONLY as the EXPLICIT-OPT-IN signal for step 3's
523
+ // precedence over the omit gate (see the note there). Every actual runtime
524
+ // question — which tier map to read, which value policy applies — now uses
525
+ // the active runtime (#4505).
422
526
  const configRuntime = config['runtime'];
527
+ // #4505/#4495: GSD_RUNTIME -> config.runtime -> per-install marker -> 'claude'.
528
+ // Never undefined, so the "no runtime declared anywhere" case still resolves
529
+ // 'claude' and step 3's deliberate claude skip (#1156/#2297/#4192) is
530
+ // unchanged for every existing install.
531
+ const activeRuntime = resolveActiveRuntime(config);
423
532
  if (tier && tier !== 'inherit') {
424
- const onClaude = !configRuntime || configRuntime === 'claude';
425
- const effectiveRuntime = configRuntime || 'claude';
533
+ const onClaude = activeRuntime === 'claude';
534
+ const effectiveRuntime = activeRuntime;
426
535
  const mergedPolicy = config['model_policy']
427
536
  ? { ...config['model_policy'], runtime: effectiveRuntime }
428
537
  : null;
@@ -447,8 +556,49 @@ function resolveModelInternal(cwd, agentType) {
447
556
  warnModelPolicyUnmappable(agentType, policyModel, tier);
448
557
  }
449
558
  }
450
- // 3. Runtime-aware resolution (#2517)
451
- if (configRuntime && configRuntime !== 'claude' && tier && tier !== 'inherit') {
559
+ // #4505: the omit DECISION is computed here, ahead of step 3, though the
560
+ // return still happens at step 4 below.
561
+ //
562
+ // Step 3 was previously unreachable unless the operator had written `runtime`
563
+ // into the config. Keying it off the ACTIVE runtime — the #4495 fix — makes it
564
+ // reachable for env- and marker-declared runtimes too, which newly exposes an
565
+ // ordering the corpus already decided, in two places that only coexisted
566
+ // because step 3 could not fire:
567
+ //
568
+ // #2517 `runtime:"codex"` + resolve_model_ids:"omit" -> the codex tier
569
+ // model. "Explicit non-Claude opt-in wins" — the operator naming a
570
+ // runtime in the project config outranks an omit.
571
+ // #4717 (user-sanctioned decision a — supersedes #2297 acceptance #4):
572
+ // a DETECTED runtime now constitutes that opt-in too. The identity
573
+ // fill in config-loader materializes GSD_RUNTIME / the per-install
574
+ // marker into `config.runtime` when it is empty, so a genuinely
575
+ // installed non-Claude runtime resolves its own tier map instead of
576
+ // the omit's "". The shared "omit" remains a Claude protection:
577
+ // marker/env = claude still ignores it (native aliases), and
578
+ // garbage runtime values still fail safe to "" (guard below).
579
+ //
580
+ // So the opt-in signal is the `runtime` KEY — written by the operator or
581
+ // materialized by the #4717 fill: step 3 reads the active runtime's tier
582
+ // map, but only outranks the omit gate when that key canonicalizes to a
583
+ // recognised non-Claude runtime.
584
+ const omitApplies = config['resolve_model_ids'] === 'omit'
585
+ && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(activeRuntime));
586
+ // CANONICALIZED, not the raw field. Comparing the raw value against the literal
587
+ // 'claude' made every spelling that is not exactly that string count as a
588
+ // non-Claude opt-in and outrank the omit gate: `runtime:"Claude"`,
589
+ // `runtime:"claude-code"` and even `runtime:5` each emitted a model id where
590
+ // origin/next returned "". That is the #2297 acceptance-#4 case this very
591
+ // block claims to preserve, failing OPEN. (Security review of #4505.)
592
+ //
593
+ // null covers both "not a string" and "not a runtime we recognise", and both
594
+ // must read as NOT an opt-in: an unrecognised value is not evidence the
595
+ // operator deliberately chose a non-Claude runtime, so it must not buy an
596
+ // escalation past an explicit omit.
597
+ const configRuntimeCanonical = (0, runtime_name_policy_cjs_1.canonicalizeRuntimeName)(configRuntime);
598
+ const explicitNonClaudeOptIn = configRuntimeCanonical !== null && configRuntimeCanonical !== 'claude';
599
+ // 3. Runtime-aware resolution (#2517), keyed off the ACTIVE runtime (#4505).
600
+ if (activeRuntime !== 'claude' && tier && tier !== 'inherit'
601
+ && (explicitNonClaudeOptIn || !omitApplies)) {
452
602
  const entry = _resolveRuntimeTier(config, tier);
453
603
  if (entry?.model)
454
604
  return entry.model;
@@ -463,10 +613,42 @@ function resolveModelInternal(cwd, agentType) {
463
613
  // NOTE: a non-Claude runtime that HAS a populated runtime-tier map already
464
614
  // returned its own model id at step 3 above, before this gate — for those the
465
615
  // explicit-project-omit honoring here is moot (step 3 wins, by #2517 design).
466
- if (config['resolve_model_ids'] === 'omit'
467
- && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(resolveActiveRuntime(config)))) {
616
+ if (omitApplies) {
468
617
  return '';
469
618
  }
619
+ // 4.5. Claude-runtime tier override (#4192 Finding 1). Sits AFTER the omit
620
+ // gate so an explicit project `resolve_model_ids:"omit"` still wins (#2297:
621
+ // explicit project omit is honored regardless of runtime), and BEFORE the
622
+ // alias return so a pinned generation is not re-collapsed to a tier alias or
623
+ // re-materialized to the LATEST catalog id by step 5's
624
+ // `resolve_model_ids:true` path. Fires ONLY when the user wrote a
625
+ // `model_profile_overrides.claude.<tier>` entry for this tier — see
626
+ // resolveClaudeTierOverrideModel for why the builtin map stays out.
627
+ if (tier && tier !== 'inherit') {
628
+ const claudeOverrideModel = resolveClaudeTierOverrideModel(activeRuntime, tier, config['model_profile_overrides']);
629
+ if (claudeOverrideModel !== null)
630
+ return claudeOverrideModel;
631
+ }
632
+ // 4.75 dynamic_routing.tier_models (#4505 / #3024).
633
+ //
634
+ // Position is the documented composition, not a convenience:
635
+ // docs/features/dynamic-routing-with-failure-tier-escalation.md —
636
+ // "model_overrides always wins; dynamic_routing.tier_models[<tier>] resolves
637
+ // above models.<phase_type> and model_profile."
638
+ // So it sits BELOW model_overrides (step 1), the model_policy preset (2.5),
639
+ // the runtime tier map (3), the resolve_model_ids:"omit" gate (4) and the
640
+ // claude tier override (4.5) — and ABOVE the profile lookup (5).
641
+ //
642
+ // An earlier cut of this fix routed every call site through resolveModelForTier
643
+ // instead, which returns the tier model directly and therefore skipped steps 3,
644
+ // 4 and 4.5 entirely: with `resolve_model_ids:"omit"` and a non-Claude runtime
645
+ // it handed out a model id where the gate had returned "". Putting the step
646
+ // here keeps every higher-precedence layer reachable.
647
+ if (tier !== 'inherit') {
648
+ const routed = dynamicRoutingModel(config, agentType, 0);
649
+ if (routed !== null)
650
+ return routed;
651
+ }
470
652
  // 5. Profile lookup (Claude-native default).
471
653
  if (!agentModels) {
472
654
  return profile === 'quality' ? 'opus'
@@ -523,6 +705,50 @@ function assertValidGranularityOverride(override, fail) {
523
705
  fail(`invalid granularity '${override}' (valid: ${[...VALID_GRANULARITIES].join(', ')})`);
524
706
  }
525
707
  }
708
+ /**
709
+ * #4505 — the ONE implementation of `dynamic_routing.tier_models` lookup.
710
+ *
711
+ * Returns the configured model for this agent's routing tier at `attempt`, or
712
+ * null when dynamic routing does not apply (disabled, absent, no tier table, an
713
+ * agent with no default routing tier, or no entry for the tier). Both entry
714
+ * points call it, so the first-spawn value and the escalated value can never
715
+ * drift apart — the "Generative Fix Divergence" this repo names by name.
716
+ *
717
+ * `attempt` 0 means "no escalation yet", which is the documented FIRST-SPAWN
718
+ * case, NOT "skip dynamic routing":
719
+ * docs/features/dynamic-routing-with-failure-tier-escalation.md —
720
+ * "enabled: true — the resolver picks tier_models[default_tier] for the first
721
+ * spawn and escalates one tier up on orchestrator-detected soft failure."
722
+ */
723
+ function dynamicRoutingModel(config, agentType, attempt) {
724
+ const dr = config['dynamic_routing'];
725
+ if (!dr || typeof dr !== 'object' || dr['enabled'] !== true)
726
+ return null;
727
+ const tierModels = dr['tier_models'];
728
+ if (!tierModels || typeof tierModels !== 'object')
729
+ return null;
730
+ const defaultTier = (AGENT_DEFAULT_TIERS)[agentType];
731
+ if (!defaultTier || !(VALID_AGENT_TIERS).has(defaultTier))
732
+ return null;
733
+ const maxEscalations = Number.isInteger(dr['max_escalations']) && dr['max_escalations'] >= 0
734
+ ? dr['max_escalations']
735
+ : 1;
736
+ const escalationEnabled = dr['escalate_on_failure'] !== false;
737
+ const effectiveAttempt = escalationEnabled ? Math.min(attempt, maxEscalations) : 0;
738
+ let tier = defaultTier;
739
+ for (let i = 0; i < effectiveAttempt; i += 1) {
740
+ const next = (nextTier)(tier);
741
+ if (!next || next === tier)
742
+ break;
743
+ tier = next;
744
+ }
745
+ // Own-property guard: `tier_models` is a config-supplied plain object, so a
746
+ // prototype-chain key must not resolve an inherited member.
747
+ const alias = Object.hasOwn(tierModels, tier) ? tierModels[tier] : undefined;
748
+ if (typeof alias !== 'string' || alias.length === 0)
749
+ return null;
750
+ return alias;
751
+ }
526
752
  /**
527
753
  * #3024 — Resolve a model for a specific dynamic-routing attempt.
528
754
  */
@@ -538,45 +764,19 @@ function resolveModelForTier(cwd, agentType, attempt) {
538
764
  ? modelOverrides[agentType]
539
765
  : undefined;
540
766
  if (override) {
541
- const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
767
+ const mapped = mapClaudeOverrideForRuntime(override, resolveActiveRuntime(config), agentType);
542
768
  if (mapped !== null)
543
769
  return mapped;
544
770
  // Unmappable Claude ID — fall through to dynamic_routing / model_policy resolution.
545
771
  }
546
- if (config['model_policy'] && config['runtime'] && config['runtime'] !== 'claude') {
547
- return resolveModelInternal(cwd, agentType);
548
- }
549
- const dr = config['dynamic_routing'];
550
- if (!dr || typeof dr !== 'object' || dr['enabled'] !== true) {
551
- return resolveModelInternal(cwd, agentType);
552
- }
553
- const tierModels = dr['tier_models'];
554
- if (!tierModels || typeof tierModels !== 'object') {
772
+ // #4505: same active-runtime rule as resolveModelInternal step 3.
773
+ if (config['model_policy'] && resolveActiveRuntime(config) !== 'claude') {
555
774
  return resolveModelInternal(cwd, agentType);
556
775
  }
557
- const defaultTier = (AGENT_DEFAULT_TIERS)[agentType];
558
- if (!defaultTier || !(VALID_AGENT_TIERS).has(defaultTier)) {
559
- return resolveModelInternal(cwd, agentType);
560
- }
561
- const maxEscalations = Number.isInteger(dr['max_escalations']) && dr['max_escalations'] >= 0
562
- ? dr['max_escalations']
563
- : 1;
564
- const escalationEnabled = dr['escalate_on_failure'] !== false;
565
- const effectiveAttempt = escalationEnabled
566
- ? Math.min(attemptN, maxEscalations)
567
- : 0;
568
- let tier = defaultTier;
569
- for (let i = 0; i < effectiveAttempt; i += 1) {
570
- const next = (nextTier)(tier);
571
- if (!next || next === tier)
572
- break;
573
- tier = next;
574
- }
575
- const alias = tierModels[tier];
576
- if (typeof alias !== 'string' || alias.length === 0) {
577
- return resolveModelInternal(cwd, agentType);
578
- }
579
- return alias;
776
+ const alias = dynamicRoutingModel(config, agentType, attemptN);
777
+ if (alias !== null)
778
+ return alias;
779
+ return resolveModelInternal(cwd, agentType);
580
780
  }
581
781
  /**
582
782
  * Keep only usable model ids: non-empty strings. A malformed config can put
@@ -126,7 +126,13 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
126
126
  if (args.includes('--dry-run')) {
127
127
  return makeInvalidArgs('--dry-run', 'phase insert does not support --dry-run');
128
128
  }
129
- phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
129
+ // #4569: --sibling opts into joining afterPhase's parent decimal level
130
+ // instead of nesting one level deeper. Filtered out like other
131
+ // boolean flags (see `remove`'s --force handling above) so it never
132
+ // leaks into the free-text description.
133
+ const sibling = args.includes('--sibling');
134
+ const insertArgs = args.slice(2).filter(token => token !== '--sibling');
135
+ phase.cmdPhaseInsert(cwd, insertArgs[0], insertArgs.slice(1).join(' '), raw, sibling ? 'sibling' : 'nested');
130
136
  return { ok: true, data: null };
131
137
  },
132
138
  remove: (_ctx) => {
@@ -180,11 +186,16 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
180
186
  },
181
187
  'uat-passed': (_ctx) => {
182
188
  let requireVerification = false;
189
+ let uatOnly = false;
183
190
  const positional = [];
184
191
  for (const token of args.slice(2)) {
185
192
  if (token === '--require-verification') {
186
193
  requireVerification = true;
187
194
  }
195
+ else if (token === '--uat-only') {
196
+ // #4663: evaluate UAT rows only (verification-status blockers skipped).
197
+ uatOnly = true;
198
+ }
188
199
  else if (token === '--raw') {
189
200
  // --raw is handled by the outer CLI layer; accepted here silently
190
201
  }
@@ -195,7 +206,10 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
195
206
  positional.push(token);
196
207
  }
197
208
  }
198
- phase.cmdPhaseUatPassed(cwd, positional[0], raw, { policy: { requireVerification } });
209
+ if (requireVerification && uatOnly) {
210
+ return makeInvalidArgs('--uat-only', '--uat-only and --require-verification are mutually exclusive');
211
+ }
212
+ phase.cmdPhaseUatPassed(cwd, positional[0], raw, { policy: { requireVerification, uatOnly } });
199
213
  return { ok: true, data: null };
200
214
  },
201
215
  // #1437 — list plan files for a phase
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical bracket convention card (#3638 / ADR-612 PR-5).
4
+ *
5
+ * This module is the sole editable source of the compact grammar diagram.
6
+ * Render sites import phaseIdCard(); generated documentation is held to the
7
+ * same bytes by tests/phase-id-card.test.cjs. PR-6 can therefore inject the
8
+ * card broadly without inventing another copy.
9
+ */
10
+ const PHASE_ID_CARD = [
11
+ '[GSD.02] 05.03-01',
12
+ ' │ │ │ │ │',
13
+ ' │ │ │ │ └── plan 01',
14
+ ' │ │ │ └────── subphase 03',
15
+ ' │ │ └───────── phase 05',
16
+ ' │ └───────────── milestone 02',
17
+ ' └───────────────── project GSD',
18
+ ].join('\n');
19
+ const PHASE_ID_LEGEND = "milestone = bracket integer; dots = phase-levels; one hyphen = plan; "
20
+ + "no 'Phase' word, no vX.Y";
21
+ function phaseIdCard(options = {}) {
22
+ const parts = [];
23
+ if (options.title)
24
+ parts.push(options.title, '');
25
+ parts.push(PHASE_ID_CARD, '', PHASE_ID_LEGEND);
26
+ return parts.join('\n');
27
+ }
28
+ module.exports = {
29
+ phaseIdCard,
30
+ PHASE_ID_CARD,
31
+ PHASE_ID_LEGEND,
32
+ };
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * Phase-ID display adapters (#3638 / ADR-612 PR-5).
4
+ *
5
+ * STATE.md and milestone metadata still expose the legacy `vN.0` marker on
6
+ * this stacked base. Display surfaces need to translate that metadata into a
7
+ * bracket identity without growing another renderer. These pure adapters do
8
+ * only the boundary normalization, then delegate identity parsing/rendering to
9
+ * phase-id.cts's canonical pair.
10
+ */
11
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
12
+ const phaseId = require("./phase-id.cjs");
13
+ const { parsePhaseId, renderMilestoneId, renderPhaseId } = phaseId;
14
+ function canonicalNumeric(value) {
15
+ let offset = 0;
16
+ while (offset < value.length - 1 && value[offset] === '0')
17
+ offset++;
18
+ const stripped = value.slice(offset);
19
+ return stripped.length < 2 ? stripped.padStart(2, '0') : stripped;
20
+ }
21
+ function isDigits(value) {
22
+ return value.length > 0
23
+ && [...value].every(char => char >= '0' && char <= '9');
24
+ }
25
+ function milestoneToken(value) {
26
+ if (typeof value !== 'string' && typeof value !== 'number')
27
+ return null;
28
+ const raw = String(value).replace(/^v/i, '');
29
+ const parts = raw.split('.');
30
+ if (parts.length > 2 || parts.some(part => !isDigits(part)))
31
+ return null;
32
+ return canonicalNumeric(parts[0]);
33
+ }
34
+ function phaseToken(value) {
35
+ if (typeof value !== 'string' && typeof value !== 'number')
36
+ return null;
37
+ const raw = String(value);
38
+ const parts = raw.split('.');
39
+ if (parts.length > 2 || parts.some(part => !isDigits(part)))
40
+ return null;
41
+ return parts.map(canonicalNumeric).join('.');
42
+ }
43
+ /**
44
+ * Render a bracket phase display from legacy milestone metadata.
45
+ * Returns null for incomplete or invalid metadata so cosmetic callers can
46
+ * degrade without breaking a command/statusline render.
47
+ */
48
+ function renderBracketPhaseDisplay(milestone, phase, projectCode) {
49
+ const project = typeof projectCode === 'string' ? projectCode : '';
50
+ const mm = milestoneToken(milestone);
51
+ const pp = phaseToken(phase);
52
+ if (!project || mm === null || pp === null)
53
+ return null;
54
+ try {
55
+ return renderPhaseId(parsePhaseId(`${project}.${mm}-${pp}`));
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ /** Render only the bracket milestone label, still through canonical parsing. */
62
+ function renderBracketMilestoneDisplay(milestone, projectCode) {
63
+ const project = typeof projectCode === 'string' ? projectCode : '';
64
+ const mm = milestoneToken(milestone);
65
+ if (!project || mm === null)
66
+ return null;
67
+ try {
68
+ // The sentinel phase is a validation vehicle, not a rendering artifact.
69
+ return renderMilestoneId(parsePhaseId(`${project}.${mm}-00`));
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ module.exports = {
76
+ renderBracketPhaseDisplay,
77
+ renderBracketMilestoneDisplay,
78
+ };