@opengsd/gsd-core 1.14.0 → 1.16.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 (551) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +85 -5
  4. package/README.ja-JP.md +3 -3
  5. package/README.ko-KR.md +3 -3
  6. package/README.pt-BR.md +3 -3
  7. package/README.zh-CN.md +3 -3
  8. package/agents/gsd-code-fixer.compact.md +7 -6
  9. package/agents/gsd-code-fixer.md +9 -8
  10. package/agents/gsd-code-reviewer.compact.md +5 -3
  11. package/agents/gsd-code-reviewer.md +8 -6
  12. package/agents/gsd-debug-session-manager.compact.md +17 -2
  13. package/agents/gsd-debug-session-manager.md +17 -2
  14. package/agents/gsd-debugger.md +3 -3
  15. package/agents/gsd-eval-auditor.compact.md +1 -1
  16. package/agents/gsd-eval-auditor.md +1 -1
  17. package/agents/gsd-executor.md +17 -12
  18. package/agents/gsd-intel-updater.compact.md +1 -1
  19. package/agents/gsd-intel-updater.md +1 -1
  20. package/agents/gsd-mempalace-curator.md +2 -2
  21. package/agents/gsd-phase-researcher.md +19 -11
  22. package/agents/gsd-plan-checker.md +15 -9
  23. package/agents/gsd-planner.md +15 -11
  24. package/agents/gsd-project-researcher.compact.md +1 -1
  25. package/agents/gsd-project-researcher.md +1 -1
  26. package/agents/gsd-research-synthesizer.compact.md +1 -1
  27. package/agents/gsd-research-synthesizer.md +1 -1
  28. package/agents/gsd-ui-auditor.compact.md +21 -30
  29. package/agents/gsd-ui-auditor.md +166 -37
  30. package/agents/gsd-ui-researcher.compact.md +1 -1
  31. package/agents/gsd-ui-researcher.md +1 -1
  32. package/agents/gsd-verifier.md +37 -14
  33. package/bin/install.js +764 -287
  34. package/commands/gsd/add-tests.md +6 -1
  35. package/commands/gsd/ai-integration-phase.md +6 -1
  36. package/commands/gsd/audit-fix.md +5 -0
  37. package/commands/gsd/audit-milestone.md +6 -1
  38. package/commands/gsd/autonomous.md +7 -2
  39. package/commands/gsd/capture.md +9 -5
  40. package/commands/gsd/code-review.md +7 -2
  41. package/commands/gsd/complete-milestone.md +4 -0
  42. package/commands/gsd/config.md +7 -3
  43. package/commands/gsd/debug.md +11 -7
  44. package/commands/gsd/discuss-phase.md +7 -3
  45. package/commands/gsd/docs-update.md +12 -7
  46. package/commands/gsd/eval-review.md +6 -1
  47. package/commands/gsd/execute-phase.md +12 -7
  48. package/commands/gsd/extract-learnings.md +5 -0
  49. package/commands/gsd/fast.md +4 -0
  50. package/commands/gsd/forensics.md +5 -1
  51. package/commands/gsd/graphify.md +10 -6
  52. package/commands/gsd/health.md +5 -0
  53. package/commands/gsd/help.md +7 -2
  54. package/commands/gsd/import.md +7 -3
  55. package/commands/gsd/inbox.md +5 -0
  56. package/commands/gsd/ingest-docs.md +5 -1
  57. package/commands/gsd/manager.md +6 -1
  58. package/commands/gsd/map-codebase.md +7 -3
  59. package/commands/gsd/mempalace-capture.md +12 -4
  60. package/commands/gsd/mempalace-recall.md +5 -1
  61. package/commands/gsd/milestone-summary.md +5 -1
  62. package/commands/gsd/mvp-phase.md +8 -3
  63. package/commands/gsd/new-milestone.md +6 -1
  64. package/commands/gsd/new-project.md +5 -0
  65. package/commands/gsd/next.md +6 -1
  66. package/commands/gsd/ns-context.md +4 -0
  67. package/commands/gsd/ns-ideate.md +4 -0
  68. package/commands/gsd/ns-manage.md +4 -0
  69. package/commands/gsd/ns-project.md +4 -0
  70. package/commands/gsd/ns-review.md +4 -0
  71. package/commands/gsd/ns-workflow.md +4 -0
  72. package/commands/gsd/onboard.md +6 -1
  73. package/commands/gsd/pause-work.md +5 -1
  74. package/commands/gsd/phase.md +8 -4
  75. package/commands/gsd/plan-phase.md +6 -1
  76. package/commands/gsd/plan-review-convergence.md +11 -7
  77. package/commands/gsd/pr-branch.md +4 -0
  78. package/commands/gsd/profile-user.md +5 -1
  79. package/commands/gsd/progress.md +7 -2
  80. package/commands/gsd/quick-batch.md +21 -9
  81. package/commands/gsd/quick.md +12 -7
  82. package/commands/gsd/review.md +7 -4
  83. package/commands/gsd/secure-phase.md +6 -1
  84. package/commands/gsd/ship.md +5 -0
  85. package/commands/gsd/sketch.md +7 -2
  86. package/commands/gsd/spec-phase.md +5 -1
  87. package/commands/gsd/spike.md +8 -3
  88. package/commands/gsd/surface.md +5 -1
  89. package/commands/gsd/thread.md +4 -0
  90. package/commands/gsd/ui-phase.md +6 -1
  91. package/commands/gsd/ui-review.md +6 -1
  92. package/commands/gsd/ultraplan-phase.md +5 -1
  93. package/commands/gsd/undo.md +5 -1
  94. package/commands/gsd/update.md +6 -2
  95. package/commands/gsd/validate-phase.md +6 -1
  96. package/commands/gsd/verify-work.md +6 -1
  97. package/commands/gsd/workspace.md +7 -3
  98. package/gsd-core/bin/gsd-tools.cjs +477 -78
  99. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  100. package/gsd-core/bin/lib/adr-parser.cjs +3 -1
  101. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  102. package/gsd-core/bin/lib/audit.cjs +144 -42
  103. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  104. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  105. package/gsd-core/bin/lib/capability-registry.cjs +197 -222
  106. package/gsd-core/bin/lib/capability-validator.cjs +16 -1
  107. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  108. package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
  109. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  110. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  111. package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
  112. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  113. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  114. package/gsd-core/bin/lib/commands.cjs +343 -207
  115. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  116. package/gsd-core/bin/lib/config-loader.cjs +65 -4
  117. package/gsd-core/bin/lib/config.cjs +76 -19
  118. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  119. package/gsd-core/bin/lib/coverage.cjs +4 -8
  120. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  121. package/gsd-core/bin/lib/decisions.cjs +30 -14
  122. package/gsd-core/bin/lib/drift.cjs +177 -42
  123. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  124. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  125. package/gsd-core/bin/lib/frontmatter.cjs +426 -234
  126. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  127. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  128. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  129. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  130. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  131. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  132. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  133. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  134. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  135. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  136. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  137. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  138. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  139. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  140. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  141. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  142. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  143. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  144. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  145. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  146. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  147. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  148. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  149. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  150. package/gsd-core/bin/lib/graphify.cjs +10 -2
  151. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  152. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  153. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  154. package/gsd-core/bin/lib/init.cjs +364 -132
  155. package/gsd-core/bin/lib/install-engine.cjs +30 -33
  156. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  157. package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
  158. package/gsd-core/bin/lib/io.cjs +122 -3
  159. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  160. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  161. package/gsd-core/bin/lib/milestone.cjs +37 -6
  162. package/gsd-core/bin/lib/model-resolver.cjs +171 -62
  163. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  164. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  165. package/gsd-core/bin/lib/pattern.cjs +10 -0
  166. package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
  167. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  168. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  169. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  170. package/gsd-core/bin/lib/phase-id.cjs +110 -8
  171. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  172. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  173. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  174. package/gsd-core/bin/lib/phase.cjs +489 -88
  175. package/gsd-core/bin/lib/plan-document.cjs +142 -20
  176. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  177. package/gsd-core/bin/lib/planning-document.cjs +692 -0
  178. package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
  179. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  180. package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
  181. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  182. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  183. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  184. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  185. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  186. package/gsd-core/bin/lib/project-root.cjs +41 -2
  187. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  188. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  189. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  190. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  191. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  192. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  193. package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
  194. package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
  195. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
  196. package/gsd-core/bin/lib/roadmap.cjs +405 -88
  197. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
  198. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
  199. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  200. package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
  201. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
  202. package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
  203. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  204. package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
  205. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  206. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  207. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  208. package/gsd-core/bin/lib/state-document.cjs +100 -22
  209. package/gsd-core/bin/lib/state-transition.cjs +39 -2
  210. package/gsd-core/bin/lib/state.cjs +256 -105
  211. package/gsd-core/bin/lib/surface.cjs +19 -2
  212. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
  213. package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
  214. package/gsd-core/bin/lib/uat.cjs +432 -7
  215. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  216. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
  217. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  218. package/gsd-core/bin/lib/vendor/README.md +31 -9
  219. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  220. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  221. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  222. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  223. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  224. package/gsd-core/bin/lib/verification.cjs +1378 -274
  225. package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
  226. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  227. package/gsd-core/bin/lib/verify.cjs +357 -531
  228. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  229. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  230. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  231. package/gsd-core/bin/lib/workstream.cjs +11 -2
  232. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  233. package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
  234. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
  235. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  236. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  237. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  238. package/gsd-core/references/checkpoints.md +5 -3
  239. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  240. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  241. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  242. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  243. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  244. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  245. package/gsd-core/references/edge-probe.md +195 -21
  246. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  247. package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
  248. package/gsd-core/references/execute-phase-response-language.md +1 -1
  249. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  250. package/gsd-core/references/gsd-run-resolver.md +1 -1
  251. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  252. package/gsd-core/references/model-profiles.md +1 -1
  253. package/gsd-core/references/offer-next.md +1 -1
  254. package/gsd-core/references/phase-argument-parsing.md +9 -7
  255. package/gsd-core/references/phase-id-convention.md +28 -0
  256. package/gsd-core/references/planner-gap-closure.md +2 -0
  257. package/gsd-core/references/planner-load-graph-context.md +24 -13
  258. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  259. package/gsd-core/references/planning-config.md +12 -3
  260. package/gsd-core/references/spidr-splitting.md +1 -1
  261. package/gsd-core/references/tdd.md +37 -8
  262. package/gsd-core/references/ui-consideration-probe.md +10 -5
  263. package/gsd-core/references/verifier-phase-gates.md +5 -2
  264. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  265. package/gsd-core/references/verify-mvp-mode.md +2 -2
  266. package/gsd-core/references/workstream-flag.md +33 -3
  267. package/gsd-core/references/worktree-path-safety.md +321 -0
  268. package/gsd-core/templates/README.md +1 -1
  269. package/gsd-core/templates/UAT.md +17 -1
  270. package/gsd-core/templates/config.json +2 -11
  271. package/gsd-core/templates/verification-report.md +1 -1
  272. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  273. package/gsd-core/workflows/add-backlog.md +1 -1
  274. package/gsd-core/workflows/add-phase.md +8 -7
  275. package/gsd-core/workflows/add-tests.md +4 -3
  276. package/gsd-core/workflows/add-todo.md +6 -5
  277. package/gsd-core/workflows/ai-integration-phase.md +13 -4
  278. package/gsd-core/workflows/audit-fix.md +1 -1
  279. package/gsd-core/workflows/audit-milestone.md +4 -3
  280. package/gsd-core/workflows/audit-uat.md +1 -1
  281. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  282. package/gsd-core/workflows/autonomous.md +43 -23
  283. package/gsd-core/workflows/check-todos.md +7 -6
  284. package/gsd-core/workflows/cleanup.md +2 -2
  285. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  286. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  287. package/gsd-core/workflows/code-review-fix.md +112 -25
  288. package/gsd-core/workflows/code-review.md +146 -135
  289. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  290. package/gsd-core/workflows/complete-milestone.md +13 -8
  291. package/gsd-core/workflows/debug.md +32 -7
  292. package/gsd-core/workflows/diagnose-issues.md +3 -2
  293. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  294. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  295. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  296. package/gsd-core/workflows/discuss-phase.md +4 -3
  297. package/gsd-core/workflows/do.md +2 -2
  298. package/gsd-core/workflows/docs-update.md +6 -5
  299. package/gsd-core/workflows/edit-phase.md +4 -3
  300. package/gsd-core/workflows/eval-review.md +14 -5
  301. package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
  302. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
  303. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  304. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  305. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
  306. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  307. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  308. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  309. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  310. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  311. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  312. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  313. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  314. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  315. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  316. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  317. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  318. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  319. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  320. package/gsd-core/workflows/execute-phase.md +96 -178
  321. package/gsd-core/workflows/execute-plan.md +18 -23
  322. package/gsd-core/workflows/explore.md +4 -4
  323. package/gsd-core/workflows/extract-learnings.md +4 -2
  324. package/gsd-core/workflows/fast.md +1 -1
  325. package/gsd-core/workflows/forensics.md +1 -1
  326. package/gsd-core/workflows/graduation.md +1 -1
  327. package/gsd-core/workflows/health.md +3 -2
  328. package/gsd-core/workflows/help/modes/full.compact.md +3 -3
  329. package/gsd-core/workflows/help/modes/full.md +5 -5
  330. package/gsd-core/workflows/help/modes/topic.md +15 -5
  331. package/gsd-core/workflows/import.md +4 -3
  332. package/gsd-core/workflows/inbox.md +2 -2
  333. package/gsd-core/workflows/ingest-docs.md +3 -3
  334. package/gsd-core/workflows/insert-phase.md +4 -3
  335. package/gsd-core/workflows/list-seeds.md +1 -1
  336. package/gsd-core/workflows/list-workspaces.md +1 -1
  337. package/gsd-core/workflows/manager.md +6 -4
  338. package/gsd-core/workflows/map-codebase.md +5 -4
  339. package/gsd-core/workflows/milestone-summary.md +3 -2
  340. package/gsd-core/workflows/mvp-phase.md +14 -14
  341. package/gsd-core/workflows/new-milestone.md +11 -11
  342. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  343. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  344. package/gsd-core/workflows/new-project.md +7 -7
  345. package/gsd-core/workflows/new-workspace.md +2 -2
  346. package/gsd-core/workflows/next.md +1 -1
  347. package/gsd-core/workflows/note.md +1 -1
  348. package/gsd-core/workflows/onboard.md +1 -1
  349. package/gsd-core/workflows/pause-work.md +2 -2
  350. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  351. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  352. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  353. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  354. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  355. package/gsd-core/workflows/plan-phase.md +50 -24
  356. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  357. package/gsd-core/workflows/plant-seed.md +62 -20
  358. package/gsd-core/workflows/pr-branch.md +113 -13
  359. package/gsd-core/workflows/profile-user.md +2 -2
  360. package/gsd-core/workflows/progress.md +19 -49
  361. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
  362. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  363. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  364. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  365. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  366. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  367. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  368. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  369. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  370. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  371. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  372. package/gsd-core/workflows/quick-batch.md +15 -10
  373. package/gsd-core/workflows/quick.md +62 -39
  374. package/gsd-core/workflows/reapply-patches.md +9 -3
  375. package/gsd-core/workflows/remove-phase.md +3 -2
  376. package/gsd-core/workflows/remove-workspace.md +2 -2
  377. package/gsd-core/workflows/resume-project.md +3 -2
  378. package/gsd-core/workflows/review.md +33 -17
  379. package/gsd-core/workflows/scan.md +3 -2
  380. package/gsd-core/workflows/secure-phase.md +13 -13
  381. package/gsd-core/workflows/settings-advanced.md +30 -10
  382. package/gsd-core/workflows/settings-integrations.md +2 -3
  383. package/gsd-core/workflows/settings.md +4 -4
  384. package/gsd-core/workflows/ship.md +14 -13
  385. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  386. package/gsd-core/workflows/sketch.md +1 -1
  387. package/gsd-core/workflows/smart-entry.md +2 -2
  388. package/gsd-core/workflows/spec-phase.md +15 -5
  389. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  390. package/gsd-core/workflows/spike.md +1 -1
  391. package/gsd-core/workflows/stats.md +1 -1
  392. package/gsd-core/workflows/sync-skills.md +5 -5
  393. package/gsd-core/workflows/thread.md +2 -2
  394. package/gsd-core/workflows/transition.md +13 -23
  395. package/gsd-core/workflows/ui-phase.md +48 -11
  396. package/gsd-core/workflows/ui-review.md +21 -6
  397. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  398. package/gsd-core/workflows/undo.md +339 -20
  399. package/gsd-core/workflows/update.md +7 -7
  400. package/gsd-core/workflows/validate-phase.md +12 -13
  401. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  402. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  403. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  404. package/gsd-core/workflows/verify-work.md +129 -55
  405. package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
  406. package/hooks/dist/gsd-check-update-worker.js +8 -0
  407. package/hooks/dist/gsd-check-update.js +8 -0
  408. package/hooks/dist/gsd-context-monitor.js +31 -8
  409. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  410. package/hooks/dist/gsd-secret-read-guard.js +161 -4
  411. package/hooks/dist/gsd-statusline.js +85 -20
  412. package/hooks/dist/gsd-update-banner.js +8 -0
  413. package/hooks/dist/gsd-validate-commit.sh +63 -4
  414. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  415. package/hooks/dist/gsd-workflow-guard.js +5 -4
  416. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  417. package/hooks/dist/lib/cli-exit.js +12 -0
  418. package/hooks/dist/lib/git-probe.js +17 -1
  419. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  420. package/hooks/gsd-agent-isolation-guard.js +32 -0
  421. package/hooks/gsd-check-update-worker.js +8 -0
  422. package/hooks/gsd-check-update.js +8 -0
  423. package/hooks/gsd-context-monitor.js +31 -8
  424. package/hooks/gsd-cursor-subagent-start.js +8 -0
  425. package/hooks/gsd-secret-read-guard.js +161 -4
  426. package/hooks/gsd-statusline.js +85 -20
  427. package/hooks/gsd-update-banner.js +8 -0
  428. package/hooks/gsd-validate-commit.sh +63 -4
  429. package/hooks/gsd-windsurf-pre-write.js +11 -2
  430. package/hooks/gsd-workflow-guard.js +5 -4
  431. package/hooks/gsd-worktree-path-guard.js +6 -2
  432. package/hooks/hooks.json +5 -5
  433. package/hooks/lib/cli-exit.js +12 -0
  434. package/hooks/lib/git-probe.js +17 -1
  435. package/hooks/lib/isolation-sentinel.js +2 -2
  436. package/package.json +22 -4
  437. package/scripts/build-hooks.js +15 -6
  438. package/scripts/changeset/parse.cjs +52 -4
  439. package/scripts/check-contract-drift.cjs +127 -11
  440. package/scripts/ci-timeout-report.cjs +770 -4
  441. package/scripts/command-contract-helpers.cjs +15 -8
  442. package/scripts/docs-guard-registry.cjs +34 -0
  443. package/scripts/gen-features.cjs +13 -8
  444. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  445. package/scripts/gen-loop-host-contract.cjs +79 -1
  446. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  447. package/scripts/gen-plugin-skills.cjs +87 -1
  448. package/scripts/gen-research-agents.cjs +24 -31
  449. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  450. package/scripts/gen-test-timings.cjs +32 -7
  451. package/scripts/lib/cli-exit.cjs +12 -0
  452. package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
  453. package/scripts/lib/ndjson-reporter.cjs +31 -5
  454. package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
  455. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  456. package/scripts/lib/vendor-bundle.cjs +59 -0
  457. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  458. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  459. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  460. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  461. package/scripts/lint-descriptions.cjs +7 -3
  462. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
  463. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  464. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  465. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  466. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  467. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  468. package/scripts/lint-phase-id-drift.cjs +319 -5
  469. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  470. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  471. package/scripts/lint-response-language-coverage.cjs +3 -0
  472. package/scripts/lint-retired-runtime-name.cjs +619 -0
  473. package/scripts/lint-skill-deps.cjs +7 -3
  474. package/scripts/lint-state-write-path-drift.cjs +93 -0
  475. package/scripts/lint-test-file-count.allowlist.json +38 -9
  476. package/scripts/lint-test-file-count.cjs +34 -1
  477. package/scripts/lint-vendored-deps.cjs +41 -5
  478. package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
  479. package/scripts/mutation-matrix.cjs +50 -5
  480. package/scripts/prompt-injection-scan.sh +4 -0
  481. package/scripts/release-tarball-smoke.cjs +194 -1
  482. package/scripts/require-issue-link-policy.cjs +6 -2
  483. package/scripts/sync-runtime-launcher.cjs +184 -2
  484. package/scripts/verify-npm-publish.cjs +76 -20
  485. package/skills/gsd-add-tests/SKILL.md +6 -1
  486. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  487. package/skills/gsd-audit-fix/SKILL.md +5 -0
  488. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  489. package/skills/gsd-autonomous/SKILL.md +7 -2
  490. package/skills/gsd-capture/SKILL.md +9 -5
  491. package/skills/gsd-code-review/SKILL.md +7 -2
  492. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  493. package/skills/gsd-config/SKILL.md +8 -4
  494. package/skills/gsd-debug/SKILL.md +11 -7
  495. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  496. package/skills/gsd-docs-update/SKILL.md +12 -7
  497. package/skills/gsd-eval-review/SKILL.md +6 -1
  498. package/skills/gsd-execute-phase/SKILL.md +12 -7
  499. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  500. package/skills/gsd-fast/SKILL.md +4 -0
  501. package/skills/gsd-forensics/SKILL.md +5 -1
  502. package/skills/gsd-graphify/SKILL.md +10 -6
  503. package/skills/gsd-health/SKILL.md +5 -0
  504. package/skills/gsd-help/SKILL.md +7 -2
  505. package/skills/gsd-import/SKILL.md +7 -3
  506. package/skills/gsd-inbox/SKILL.md +5 -0
  507. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  508. package/skills/gsd-manager/SKILL.md +6 -1
  509. package/skills/gsd-map-codebase/SKILL.md +7 -3
  510. package/skills/gsd-mempalace-capture/SKILL.md +12 -4
  511. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  512. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  513. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  514. package/skills/gsd-new-milestone/SKILL.md +6 -1
  515. package/skills/gsd-new-project/SKILL.md +5 -0
  516. package/skills/gsd-next/SKILL.md +6 -1
  517. package/skills/gsd-ns-context/SKILL.md +4 -0
  518. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  519. package/skills/gsd-ns-manage/SKILL.md +4 -0
  520. package/skills/gsd-ns-project/SKILL.md +4 -0
  521. package/skills/gsd-ns-review/SKILL.md +4 -0
  522. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  523. package/skills/gsd-onboard/SKILL.md +6 -1
  524. package/skills/gsd-pause-work/SKILL.md +5 -1
  525. package/skills/gsd-phase/SKILL.md +8 -4
  526. package/skills/gsd-plan-phase/SKILL.md +6 -1
  527. package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
  528. package/skills/gsd-pr-branch/SKILL.md +4 -0
  529. package/skills/gsd-profile-user/SKILL.md +5 -1
  530. package/skills/gsd-progress/SKILL.md +7 -2
  531. package/skills/gsd-quick/SKILL.md +16 -10
  532. package/skills/gsd-quick-batch/SKILL.md +21 -9
  533. package/skills/gsd-review/SKILL.md +7 -4
  534. package/skills/gsd-review-backlog/SKILL.md +3 -2
  535. package/skills/gsd-secure-phase/SKILL.md +6 -1
  536. package/skills/gsd-ship/SKILL.md +5 -0
  537. package/skills/gsd-sketch/SKILL.md +7 -2
  538. package/skills/gsd-spec-phase/SKILL.md +5 -1
  539. package/skills/gsd-spike/SKILL.md +8 -3
  540. package/skills/gsd-surface/SKILL.md +5 -1
  541. package/skills/gsd-thread/SKILL.md +4 -0
  542. package/skills/gsd-ui-phase/SKILL.md +6 -1
  543. package/skills/gsd-ui-review/SKILL.md +6 -1
  544. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  545. package/skills/gsd-undo/SKILL.md +5 -1
  546. package/skills/gsd-update/SKILL.md +6 -2
  547. package/skills/gsd-validate-phase/SKILL.md +6 -1
  548. package/skills/gsd-verify-work/SKILL.md +6 -1
  549. package/skills/gsd-workspace/SKILL.md +7 -3
  550. package/skills/gsd-workstreams/SKILL.md +6 -6
  551. package/vscode/package.json +1 -1
@@ -38,7 +38,7 @@ const stateContract = require("./state-contract.cjs");
38
38
  const { publishStateContract } = stateContract;
39
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports
40
40
  const phaseIdMod = require("./phase-id.cjs");
41
- const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId, isSentinelPhaseDir } = phaseIdMod;
41
+ const { normalizePhaseName, matchPhaseDirs, isSentinelPhaseId, isSentinelPhaseDir, buildPhaseHeadingScanRegex, PHASE_HEADING_BASELINE } = phaseIdMod;
42
42
  const pattern_cjs_1 = require("./pattern.cjs");
43
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports
44
44
  const roadmapParserMod = require("./roadmap-parser.cjs");
@@ -63,7 +63,7 @@ const { extractFrontmatter } = frontmatterMod;
63
63
  // divergence signal). Routed through the single write-seam composition
64
64
  // (`syncAndPreserveStateMd`) instead, under `withStateLock` — see
65
65
  // `cmdMilestoneComplete`'s own STATE.md-update block for the full rationale.
66
- const { syncAndPreserveStateMd, withStateLock, readModifyWriteStateMd } = stateMod;
66
+ const { syncAndPreserveStateMd, withStateLock, readModifyWriteStateMd, assertVerificationReportsReadable } = stateMod;
67
67
  // #2288 security: a milestone version label becomes a filesystem directory
68
68
  // component (`milestones/<label>-phases/`) into which phase directories are
69
69
  // MOVED. Any label used as a path segment must be a safe version token —
@@ -642,8 +642,21 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
642
642
  // windows converge to the same value regardless of which version drove
643
643
  // the lookup.
644
644
  const scopedContent = sliceMilestoneWindow(roadmapContent, version) ?? extractCurrentMilestone(roadmapContent, cwd);
645
- // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
646
- const phasePattern = new RegExp(`#{2,4}\\s*Phase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:\\s*([^\\n]+)`, 'gi');
645
+ // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag, owned
646
+ // by buildPhaseHeadingScanRegex (phase-id.cts) so this guard also
647
+ // recognizes bracket-convention headings instead of hand-rolling a
648
+ // literal `Phase\s+`.
649
+ // #4984 fix: resolved ONCE and threaded into BOTH the heading scan and
650
+ // the disk-side matchPhaseDirs check below — the heading scan alone
651
+ // recognizing `[GSD.02] 01:` and extracting the bare phase-number token
652
+ // "01" is only half the fix. Without also passing this convention to
653
+ // matchPhaseDirs, its bracket-qualified match arm never fires, "01"
654
+ // matches no bracket directory name ("GSD.02-01-setup"), and every
655
+ // properly-scaffolded bracket phase reads as disk_status: 'no_directory'
656
+ // — turning a real bracket project's own phases into false "ROADMAP
657
+ // lists N unstarted phase(s)" failures on `milestone complete`.
658
+ const convention = resolvePhaseIdConvention(cwd);
659
+ const { regex: phasePattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, convention);
647
660
  const noDirectoryPhases = [];
648
661
  let pm;
649
662
  const phaseDirEntries = (() => {
@@ -658,7 +671,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
658
671
  }
659
672
  })();
660
673
  while ((pm = phasePattern.exec(scopedContent)) !== null) {
661
- const phaseNum = pm[1];
674
+ const phaseNum = pm[phaseNumGroup];
662
675
  // Phase 0 (pre-milestone) and Phase 999 (backlog) are sentinels, not
663
676
  // real phases — they legitimately have no directory and must not block
664
677
  // milestone completion. Mirrors the engine-wide sentinel convention
@@ -672,7 +685,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
672
685
  // with a matching token exists on disk. Use the same matchPhaseDirs
673
686
  // owner that roadmap.analyze uses to avoid false positives on decimal
674
687
  // (2.1) and letter-suffix (12A) phase IDs. (#2528)
675
- const hasDirectory = matchPhaseDirs(phaseDirEntries, normalized).matches.length > 0;
688
+ const hasDirectory = matchPhaseDirs(phaseDirEntries, normalized, convention).matches.length > 0;
676
689
  if (!hasDirectory) {
677
690
  noDirectoryPhases.push(phaseNum);
678
691
  }
@@ -856,6 +869,15 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
856
869
  output(dryRunResult, raw);
857
870
  return;
858
871
  }
872
+ // #5118 (no write before the error): the STATE.md update further down
873
+ // rebuilds the frontmatter from every phase's report and throws on a
874
+ // `status` outside the closed set — AFTER the archive directory, the
875
+ // archived ROADMAP/REQUIREMENTS copies, MILESTONES.md and the moved audit
876
+ // and quick-task files. Validate first, so the refusal leaves the tree
877
+ // untouched.
878
+ if (node_fs_1.default.existsSync(statePath)) {
879
+ assertVerificationReportsReadable((0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '', cwd);
880
+ }
859
881
  // Ensure archive directory exists. Deliberately placed AFTER the dry-run
860
882
  // early return and every refusal/guard above (missingExplicitVersion, the
861
883
  // scope refusal, the unstarted-phase guard) — #3184 review finding: this
@@ -1689,6 +1711,15 @@ function cmdQuickArchive(cwd, version, options, raw) {
1689
1711
  }, raw);
1690
1712
  return;
1691
1713
  }
1714
+ // #5118 (no write before the error): the STATE.md reset below goes through
1715
+ // `readModifyWriteStateMd`, whose frontmatter rebuild reads every phase's
1716
+ // report and throws on a `status` outside the closed set — AFTER the quick
1717
+ // task directories have been MOVED. Validate first, so the refusal leaves the
1718
+ // tree untouched. Gated on there being anything to archive: a run that would
1719
+ // move nothing never reaches that rebuild.
1720
+ if (node_fs_1.default.existsSync(statePath) && listQuickTaskDirsForArchive(cwd).length > 0) {
1721
+ assertVerificationReportsReadable(node_fs_1.default.readFileSync(statePath, 'utf-8'), cwd);
1722
+ }
1692
1723
  const quickArchiveResult = archiveQuickTaskDirectories(cwd, version);
1693
1724
  const warnings = [];
1694
1725
  let stateUpdated = false;
@@ -55,24 +55,24 @@ const { planningDir } = planningWorkspaceMod;
55
55
  // <install>/gsd-core/bin/lib). Precedence for the gate: project config.runtime →
56
56
  // GSD_RUNTIME env (manual/CI override + test seam) → install marker → 'claude'.
57
57
  //
58
- // `claude` is currently the ONLY runtime with nativeModelAliases:true; a
59
- // registry-parity test guards this set so a future alias-capable runtime fails
60
- // loudly here instead of silently omitting.
61
- const RUNTIMES_WITH_NATIVE_ALIASES = new Set(['claude']);
58
+ // Whether a runtime resolves tier aliases natively is a runtime-descriptor fact
59
+ // (`hostBehaviors.nativeModelAliases`, #5169) — `claude` is currently the ONLY
60
+ // runtime that declares it — not a hand-kept set in this module. A label that is
61
+ // not a registered runtime (a future runtime named by env/config) has none.
62
+ function hasNativeModelAliases(runtime) {
63
+ return (0, runtime_name_policy_cjs_1.hostBehaviorsFor)(runtime).nativeModelAliases === true;
64
+ }
62
65
  // #3897 rung 2: the marker reader + its cache and test seams were promoted to
63
66
  // the canonical owner, `runtime-slash.cts` (imported above) — this module now
64
67
  // consumes that single implementation instead of holding its own copy. N5:
65
68
  // behaviour and the seam contract are unchanged by the move; the re-exports
66
69
  // below (`export =` at the bottom of this file) preserve every existing
67
70
  // caller's `require('./model-resolver.cjs')` surface byte-for-behaviour.
68
- // The runtime whose install is actually resolving, canonicalized so an alias or
69
- // case variant (e.g. "claude-code"/"Claude") cannot defeat the native-alias
70
- // check below (#2297 review). Precedence mirrors resolveRuntime()
71
- // (runtime-slash.cts): GSD_RUNTIME env → project config.runtime → per-install
72
- // .gsd-runtime marker → 'claude'.
73
- function resolveActiveRuntime(config) {
74
- return (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], config['runtime'], (0, runtime_slash_cjs_1.readInstallRuntimeMarker)()) || 'claude';
75
- }
71
+ // The runtime whose install is actually resolving — one chain, owned by
72
+ // runtime-slash.cts's `resolveActiveRuntime` (#5169): GSD_RUNTIME env → project
73
+ // config.runtime → per-install .gsd-runtime marker → 'claude', canonicalized so
74
+ // an alias or case variant (e.g. "claude-code"/"Claude") cannot defeat the
75
+ // native-alias check below (#2297 review).
76
76
  // Did the PROJECT's own config (root `.planning/config.json` or the active
77
77
  // workstream/project override) explicitly set resolve_model_ids to "omit"?
78
78
  // Project config takes precedence over the shared ~/.gsd/defaults.json (#2297
@@ -123,7 +123,13 @@ 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: (0, runtime_slash_cjs_1.resolveActiveRuntime)(config),
127
133
  tier,
128
134
  overrides: config['model_profile_overrides'],
129
135
  });
@@ -142,10 +148,19 @@ function _resolveRuntimeTier(config, tier) {
142
148
  *
143
149
  * This helper reads ONLY the user's override entry for the effective claude
144
150
  * runtime and tier — never the builtin claude tier map — so an install with no
145
- * override is byte-identical to before the fix. The runtime is resolved the
146
- * same way steps 1-3 resolve it (config['runtime'], defaulting to 'claude'),
147
- * NOT via resolveActiveRuntime (GSD_RUNTIME/marker): the value policy must
148
- * key off the config the operator wrote, matching mapClaudeOverrideForRuntime.
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.
149
164
  *
150
165
  * Value policy mirrors the model_overrides path (#2041/#4192): an override
151
166
  * value that maps to a current tier alias collapses to that alias
@@ -155,10 +170,12 @@ function _resolveRuntimeTier(config, tier) {
155
170
  * Malformed entries (no usable `model` string) return null so the caller falls
156
171
  * through to normal alias resolution (ADR-443 D1: invalid values fall through).
157
172
  */
158
- function resolveClaudeTierOverrideModel(configRuntime, tier, overrides) {
173
+ function resolveClaudeTierOverrideModel(
174
+ // The ACTIVE runtime (#4505), not the raw config key — see docblock.
175
+ activeRuntime, tier, overrides) {
159
176
  if (!tier || tier === 'inherit')
160
177
  return null;
161
- const effectiveRuntime = configRuntime || 'claude';
178
+ const effectiveRuntime = activeRuntime || 'claude';
162
179
  if (effectiveRuntime !== 'claude')
163
180
  return null; // non-claude runtimes resolve at step 3
164
181
  const overridesMap = overrides;
@@ -264,13 +281,18 @@ function _resetModelOverrideWarningCacheForTests() {
264
281
  * `Object.hasOwn` lookup defeats `__proto__`/`constructor` lookups on the plain
265
282
  * object literal so those reserved keys cannot return a truthy non-string.
266
283
  */
267
- 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) {
268
290
  // Defensive: model_overrides is typed Record<string,string> but a malformed
269
291
  // config could surface a non-string; pass through verbatim (preserving the
270
292
  // pre-fix no-crash behaviour) and let the downstream Agent tool reject it.
271
293
  if (typeof override !== 'string')
272
294
  return override;
273
- const onClaude = !configRuntime || configRuntime === 'claude';
295
+ const onClaude = !activeRuntime || activeRuntime === 'claude';
274
296
  if (!onClaude)
275
297
  return override;
276
298
  // Object.hasOwn guards against __proto__/constructor returning a truthy
@@ -480,7 +502,7 @@ function resolveModelInternal(cwd, agentType) {
480
502
  ? modelOverrides[agentType]
481
503
  : undefined;
482
504
  if (override) {
483
- const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
505
+ const mapped = mapClaudeOverrideForRuntime(override, (0, runtime_slash_cjs_1.resolveActiveRuntime)(config), agentType);
484
506
  if (mapped !== null)
485
507
  return mapped;
486
508
  // Unmappable Claude ID — fall through to tier resolution (matches model_policy).
@@ -497,10 +519,19 @@ function resolveModelInternal(cwd, agentType) {
497
519
  const agentModels = Object.hasOwn(modelProfilesMapForModel, agentType) ? modelProfilesMapForModel[agentType] : undefined;
498
520
  const tier = computeProfileTier(config, agentType);
499
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).
500
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 = (0, runtime_slash_cjs_1.resolveActiveRuntime)(config);
501
532
  if (tier && tier !== 'inherit') {
502
- const onClaude = !configRuntime || configRuntime === 'claude';
503
- const effectiveRuntime = configRuntime || 'claude';
533
+ const onClaude = activeRuntime === 'claude';
534
+ const effectiveRuntime = activeRuntime;
504
535
  const mergedPolicy = config['model_policy']
505
536
  ? { ...config['model_policy'], runtime: effectiveRuntime }
506
537
  : null;
@@ -525,8 +556,49 @@ function resolveModelInternal(cwd, agentType) {
525
556
  warnModelPolicyUnmappable(agentType, policyModel, tier);
526
557
  }
527
558
  }
528
- // 3. Runtime-aware resolution (#2517)
529
- 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) || !hasNativeModelAliases(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)) {
530
602
  const entry = _resolveRuntimeTier(config, tier);
531
603
  if (entry?.model)
532
604
  return entry.model;
@@ -541,8 +613,7 @@ function resolveModelInternal(cwd, agentType) {
541
613
  // NOTE: a non-Claude runtime that HAS a populated runtime-tier map already
542
614
  // returned its own model id at step 3 above, before this gate — for those the
543
615
  // explicit-project-omit honoring here is moot (step 3 wins, by #2517 design).
544
- if (config['resolve_model_ids'] === 'omit'
545
- && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(resolveActiveRuntime(config)))) {
616
+ if (omitApplies) {
546
617
  return '';
547
618
  }
548
619
  // 4.5. Claude-runtime tier override (#4192 Finding 1). Sits AFTER the omit
@@ -554,10 +625,30 @@ function resolveModelInternal(cwd, agentType) {
554
625
  // `model_profile_overrides.claude.<tier>` entry for this tier — see
555
626
  // resolveClaudeTierOverrideModel for why the builtin map stays out.
556
627
  if (tier && tier !== 'inherit') {
557
- const claudeOverrideModel = resolveClaudeTierOverrideModel(configRuntime, tier, config['model_profile_overrides']);
628
+ const claudeOverrideModel = resolveClaudeTierOverrideModel(activeRuntime, tier, config['model_profile_overrides']);
558
629
  if (claudeOverrideModel !== null)
559
630
  return claudeOverrideModel;
560
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
+ }
561
652
  // 5. Profile lookup (Claude-native default).
562
653
  if (!agentModels) {
563
654
  return profile === 'quality' ? 'opus'
@@ -614,6 +705,50 @@ function assertValidGranularityOverride(override, fail) {
614
705
  fail(`invalid granularity '${override}' (valid: ${[...VALID_GRANULARITIES].join(', ')})`);
615
706
  }
616
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
+ }
617
752
  /**
618
753
  * #3024 — Resolve a model for a specific dynamic-routing attempt.
619
754
  */
@@ -629,45 +764,19 @@ function resolveModelForTier(cwd, agentType, attempt) {
629
764
  ? modelOverrides[agentType]
630
765
  : undefined;
631
766
  if (override) {
632
- const mapped = mapClaudeOverrideForRuntime(override, config['runtime'], agentType);
767
+ const mapped = mapClaudeOverrideForRuntime(override, (0, runtime_slash_cjs_1.resolveActiveRuntime)(config), agentType);
633
768
  if (mapped !== null)
634
769
  return mapped;
635
770
  // Unmappable Claude ID — fall through to dynamic_routing / model_policy resolution.
636
771
  }
637
- if (config['model_policy'] && config['runtime'] && config['runtime'] !== 'claude') {
638
- return resolveModelInternal(cwd, agentType);
639
- }
640
- const dr = config['dynamic_routing'];
641
- if (!dr || typeof dr !== 'object' || dr['enabled'] !== true) {
642
- return resolveModelInternal(cwd, agentType);
643
- }
644
- const tierModels = dr['tier_models'];
645
- if (!tierModels || typeof tierModels !== 'object') {
646
- return resolveModelInternal(cwd, agentType);
647
- }
648
- const defaultTier = (AGENT_DEFAULT_TIERS)[agentType];
649
- if (!defaultTier || !(VALID_AGENT_TIERS).has(defaultTier)) {
650
- return resolveModelInternal(cwd, agentType);
651
- }
652
- const maxEscalations = Number.isInteger(dr['max_escalations']) && dr['max_escalations'] >= 0
653
- ? dr['max_escalations']
654
- : 1;
655
- const escalationEnabled = dr['escalate_on_failure'] !== false;
656
- const effectiveAttempt = escalationEnabled
657
- ? Math.min(attemptN, maxEscalations)
658
- : 0;
659
- let tier = defaultTier;
660
- for (let i = 0; i < effectiveAttempt; i += 1) {
661
- const next = (nextTier)(tier);
662
- if (!next || next === tier)
663
- break;
664
- tier = next;
665
- }
666
- const alias = tierModels[tier];
667
- if (typeof alias !== 'string' || alias.length === 0) {
772
+ // #4505: same active-runtime rule as resolveModelInternal step 3.
773
+ if (config['model_policy'] && (0, runtime_slash_cjs_1.resolveActiveRuntime)(config) !== 'claude') {
668
774
  return resolveModelInternal(cwd, agentType);
669
775
  }
670
- return alias;
776
+ const alias = dynamicRoutingModel(config, agentType, attemptN);
777
+ if (alias !== null)
778
+ return alias;
779
+ return resolveModelInternal(cwd, agentType);
671
780
  }
672
781
  /**
673
782
  * Keep only usable model ids: non-empty strings. A malformed config can put
@@ -16,7 +16,7 @@
16
16
  * (RFC 4122); invalid values are silently coerced to undefined.
17
17
  * command: string — the dispatched verb
18
18
  * args?: unknown — only present when includeArgs === true
19
- * result: { kind: 'ok' | 'UnknownCommand' | 'InvalidArgs' | 'HandlerRefusal' | 'HandlerFailure', ...payload }
19
+ * result: { kind: 'ok' | 'UnknownCommand' | 'InvalidArgs' | 'HandlerRefusal' | 'HandlerFailure' | 'VerificationStatusInvalid', ...payload }
20
20
  * timestamp: string — ISO 8601
21
21
  */
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -16,6 +16,11 @@
16
16
  * No-op logger (createNoOpLogger):
17
17
  * Silent on all events. Used as the Hub default when no logger is injected.
18
18
  *
19
+ * Live-seam gate (resolveDispatchLogger, #4975):
20
+ * The one opt-in decision both live createHub() seams share. Resolves
21
+ * `audit.enabled` for the seam's cwd and returns the default logger, or
22
+ * undefined (inject nothing) when observability is off.
23
+ *
19
24
  * ADR-457 build-at-publish: the hand-written bin/lib/observability/logger.cjs
20
25
  * collapsed to a TypeScript source of truth. Behaviour is preserved
21
26
  * byte-for-behaviour from the prior hand-written .cjs; only types are added.
@@ -26,6 +31,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
31
  const node_fs_1 = __importDefault(require("node:fs"));
27
32
  const node_path_1 = __importDefault(require("node:path"));
28
33
  const redaction_cjs_1 = require("./redaction.cjs");
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const planningWorkspace = require("../planning-workspace.cjs");
36
+ const { readScopedConfigValue } = planningWorkspace;
29
37
  const AUDIT_FILE_NAME = '.gsd-trace.jsonl';
30
38
  const PLANNING_DIR = '.planning';
31
39
  // ─── helpers ─────────────────────────────────────────────────────────────────
@@ -47,6 +55,8 @@ function _safeStringify(value) {
47
55
  * inject nothing (the Hub stays byte-for-byte silent, preserving the default
48
56
  * dispatch output contract, incl. --json-errors); when on, the caller injects
49
57
  * createDefaultLogger and gets the stderr-on-error line + opt-in file audit.
58
+ * The live seams reach it through resolveDispatchLogger, which supplies the
59
+ * project config — called bare, only the env var is seen (#4975).
50
60
  */
51
61
  function _isAuditEnabled(config) {
52
62
  if (process.env['GSD_AUDIT'] === '1')
@@ -148,4 +158,39 @@ function createDefaultLogger({ cwd = process.cwd(), config } = {}) {
148
158
  },
149
159
  };
150
160
  }
151
- module.exports = { createDefaultLogger, createNoOpLogger, isAuditEnabled: _isAuditEnabled };
161
+ /**
162
+ * Resolve `audit.enabled` for `cwd` as the value `config-get audit.enabled`
163
+ * reports, through planning-workspace's readScopedConfigValue — the one
164
+ * scope-aware ladder worktreesOptedOut (#3972) reads too, so the two gates
165
+ * can never resolve the same config differently. Strict `=== true`, never
166
+ * coerced.
167
+ *
168
+ * Direct file reads, deliberately NOT loadConfig: this runs on every live
169
+ * dispatch, and loadConfig can rewrite config.json, spawn git, scan the
170
+ * capability registry, and print warnings — none of which a dispatch that
171
+ * never opted in may do (the default dispatch output is a stable contract,
172
+ * ADR-2619). Never throws. An unreadable or unparseable config file sets
173
+ * nothing, so the ladder moves on: under GSD_WORKSTREAM a broken workstream
174
+ * config inherits the root's `audit.enabled` (where config-get fails with
175
+ * CONFIG_PARSE_FAILED); otherwise the GSD_AUDIT env var alone decides.
176
+ */
177
+ function _readAuditConfig(cwd) {
178
+ const { present, value } = readScopedConfigValue(cwd, ['audit', 'enabled']);
179
+ return { audit: { enabled: present && value === true } };
180
+ }
181
+ /**
182
+ * The DispatchLogger a live createHub() seam injects (#4975). Either
183
+ * GSD_AUDIT=1 or `audit.enabled: true` in the project config turns the opt-in
184
+ * audit trail (and the structured stderr line on error) on; neither turns the
185
+ * other off. When observability is off this returns undefined so the Hub keeps
186
+ * its no-op fallback and the default dispatch output — including the
187
+ * --json-errors envelope — stays byte-for-byte unchanged (#2620).
188
+ *
189
+ * `cwd` defaults exactly as createDefaultLogger's does, so the config that
190
+ * decides and the directory that receives the trail are always the same.
191
+ */
192
+ function resolveDispatchLogger(cwd = process.cwd()) {
193
+ const config = _readAuditConfig(cwd);
194
+ return _isAuditEnabled(config) ? createDefaultLogger({ cwd, config }) : undefined;
195
+ }
196
+ module.exports = { createDefaultLogger, createNoOpLogger, isAuditEnabled: _isAuditEnabled, resolveDispatchLogger };
@@ -44,6 +44,7 @@
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.MAX_USER_PATTERN_LEN = void 0;
46
46
  exports.escapeRegex = escapeRegex;
47
+ exports.escapeEre = escapeEre;
47
48
  exports.literalPattern = literalPattern;
48
49
  exports.compileUserPattern = compileUserPattern;
49
50
  // re2js is vendored, not an npm dependency at runtime: gsd-core/bin/** is
@@ -66,6 +67,15 @@ const escapeMetachars = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
67
  function escapeRegex(value) {
67
68
  return (escapeBuiltin ?? escapeMetachars)(value);
68
69
  }
70
+ /**
71
+ * Escape `value` for a POSIX extended regular expression (e.g. `git log --extended-regexp
72
+ * --grep=…`). Always the plain backslash-escape of every ERE metacharacter, never
73
+ * `RegExp.escape`: that built-in writes a leading digit as `\x31`, which ERE does not read as a
74
+ * digit. #5139 (a plan id interpolated into a git `--grep` pattern).
75
+ */
76
+ function escapeEre(value) {
77
+ return escapeMetachars(value);
78
+ }
69
79
  function literalPattern(value, flags) {
70
80
  return new RegExp(escapeRegex(value), flags);
71
81
  }
@@ -22,10 +22,12 @@ const commandRoutingHub = require("./command-routing-hub.cjs");
22
22
  const { createHub, ERROR_KINDS, makeInvalidArgs } = commandRoutingHub;
23
23
  // #2620 (ADR-0174 §6): inject the reference DispatchLogger on the live phase
24
24
  // dispatch path, but only when observability is opt-in enabled; otherwise the
25
- // Hub stays byte-for-byte silent via its no-op fallback.
25
+ // Hub stays byte-for-byte silent via its no-op fallback. #4975: the opt-in is
26
+ // GSD_AUDIT=1 or `audit.enabled` in the project config — resolveDispatchLogger
27
+ // owns that whole decision for every live seam.
26
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
27
29
  const observabilityLogger = require("./observability/logger.cjs");
28
- const { createDefaultLogger, isAuditEnabled } = observabilityLogger;
30
+ const { resolveDispatchLogger } = observabilityLogger;
29
31
  // ─── Implementation ───────────────────────────────────────────────────────────
30
32
  function routePhaseCommand({ phase, args, cwd, raw, error }) {
31
33
  // ── Unsupported subcommands ─────────────────────────────────────────────────
@@ -186,11 +188,16 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
186
188
  },
187
189
  'uat-passed': (_ctx) => {
188
190
  let requireVerification = false;
191
+ let uatOnly = false;
189
192
  const positional = [];
190
193
  for (const token of args.slice(2)) {
191
194
  if (token === '--require-verification') {
192
195
  requireVerification = true;
193
196
  }
197
+ else if (token === '--uat-only') {
198
+ // #4663: evaluate UAT rows only (verification-status blockers skipped).
199
+ uatOnly = true;
200
+ }
194
201
  else if (token === '--raw') {
195
202
  // --raw is handled by the outer CLI layer; accepted here silently
196
203
  }
@@ -201,7 +208,10 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
201
208
  positional.push(token);
202
209
  }
203
210
  }
204
- phase.cmdPhaseUatPassed(cwd, positional[0], raw, { policy: { requireVerification } });
211
+ if (requireVerification && uatOnly) {
212
+ return makeInvalidArgs('--uat-only', '--uat-only and --require-verification are mutually exclusive');
213
+ }
214
+ phase.cmdPhaseUatPassed(cwd, positional[0], raw, { policy: { requireVerification, uatOnly } });
205
215
  return { ok: true, data: null };
206
216
  },
207
217
  // #1437 — list plan files for a phase
@@ -250,8 +260,8 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
250
260
  // #175: Hub is CJS-only — no mode param, no sdkLoader.
251
261
  // #2620: wire the reference logger (ADR-0174 §6) only when observability is
252
262
  // opt-in enabled; otherwise leave it unset so the Hub stays byte-for-byte
253
- // silent via its no-op fallback.
254
- const hub = createHub({ cjsRegistry, manifest, logger: isAuditEnabled() ? createDefaultLogger({ cwd }) : undefined });
263
+ // silent via its no-op fallback. #4975: GSD_AUDIT=1 or `audit.enabled`.
264
+ const hub = createHub({ cjsRegistry, manifest, logger: resolveDispatchLogger(cwd) });
255
265
  // ── Dispatch ────────────────────────────────────────────────────────────────
256
266
  const result = hub.dispatch({
257
267
  family: 'phase',
@@ -274,6 +284,11 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
274
284
  error(result.reason);
275
285
  return;
276
286
  }
287
+ if (result.kind === ERROR_KINDS.VerificationStatusInvalid) {
288
+ // #5118: typed reason survives to the CLI surface
289
+ error(result.message, result.reason);
290
+ return;
291
+ }
277
292
  // HandlerFailure: message field
278
293
  error(result.message);
279
294
  return;
@@ -47,6 +47,7 @@ exports.parseCalibrationDocument = parseCalibrationDocument;
47
47
  exports.renderCalibrationDocument = renderCalibrationDocument;
48
48
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- prompt-budget.cjs is an export= CommonJS module
49
49
  const promptBudget = require("./prompt-budget.cjs");
50
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
50
51
  const { estimateTokens } = promptBudget;
51
52
  /**
52
53
  * Assert that a bare number is an UNCORRECTED projection.
@@ -239,11 +240,11 @@ function applyCalibration(rawTokens, factor) {
239
240
  function extractFrontmatterBlock(text, key) {
240
241
  if (typeof text !== 'string')
241
242
  return null;
242
- // Anchor at byte 0 — CRLF-tolerant.
243
- const fm = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|\r?$)/.exec(text);
244
- if (fm === null)
243
+ // The block is the one the one fence owner finds (`locateFrontmatterFence`).
244
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(text);
245
+ if (!fence?.closed)
245
246
  return null;
246
- const lines = fm[1].split(/\r?\n/);
247
+ const lines = text.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/);
247
248
  const startIdx = lines.findIndex((l) => l === `${key}:` || l.startsWith(`${key}:`));
248
249
  if (startIdx === -1)
249
250
  return null;