@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
@@ -35,9 +35,19 @@ const commandRoutingHub = require("./command-routing-hub.cjs");
35
35
  // eslint-disable-next-line @typescript-eslint/no-require-imports
36
36
  const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
37
37
  const security_cjs_1 = require("./security.cjs");
38
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
39
+ const nodeFs = require("node:fs");
40
+ // #5118: the writer set is owned by src/verification.cts.
41
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
42
+ const verificationMod = require("./verification.cjs");
38
43
  const { output, ERROR_REASON } = io;
44
+ const { VERIFIER_STATUSES } = verificationMod;
39
45
  const { makeInvalidArgs } = commandRoutingHub;
40
46
  const { routeHubCommandFamily } = cjsCommandRouterAdapter;
47
+ /** Default stdin reader for `parse-args --stdin` (fd 0, UTF-8, whole stream). */
48
+ function readProcessStdin() {
49
+ return nodeFs.readFileSync(0, 'utf8');
50
+ }
41
51
  // ─── Small arg-parsing helpers (local — no new shared convention needed) ────
42
52
  /** `--flag value` lookup; undefined when the flag is absent. */
43
53
  function argValue(args, flag) {
@@ -57,7 +67,7 @@ function parseJsonArg(raw, label) {
57
67
  return { ok: true, value: parsed.value };
58
68
  }
59
69
  // ─── Implementation ───────────────────────────────────────────────────────────
60
- function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatchDispatch }) {
70
+ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatchDispatch, _readStdin }) {
61
71
  const qb = _quickBatch ?? quickBatch;
62
72
  const dispatch = _quickBatchDispatch ?? quickBatchDispatch;
63
73
  /** Forward a `Result<T>` from either module straight to output()/error(). */
@@ -211,7 +221,7 @@ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatc
211
221
  // `quick-batch verification-routing --status <passed|gaps_found|human_needed>`
212
222
  'verification-routing': () => {
213
223
  const status = argValue(args, '--status');
214
- if (status !== 'passed' && status !== 'gaps_found' && status !== 'human_needed') {
224
+ if (typeof status !== 'string' || !VERIFIER_STATUSES.has(status)) {
215
225
  return makeInvalidArgs('--status', 'Usage: gsd-tools quick-batch verification-routing --status <passed|gaps_found|human_needed>', ERROR_REASON.USAGE);
216
226
  }
217
227
  output(dispatch.routeVerificationOutcome(status), raw);
@@ -252,17 +262,33 @@ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatc
252
262
  planContent,
253
263
  }), raw);
254
264
  },
255
- // `quick-batch parse-args --text "<raw $ARGUMENTS string>"` (preferred —
256
- // callers pass the ENTIRE, still-quoted $ARGUMENTS as ONE argv element;
257
- // this handler does the whitespace split itself, in Node, so shell
258
- // pathname expansion (globbing) on attacker-influenced task text never
259
- // happens before this parser sees it — quoting `"$ARGUMENTS"` at the
260
- // call site is what closes that off; splitting it here is what keeps
261
- // the caller from having to word-split it unsafely beforehand).
265
+ // `quick-batch parse-args --stdin` (preferred, #4780 — the caller feeds
266
+ // the typed text through a QUOTED heredoc, so the shell never parses it:
267
+ // quotes, `$(...)`, backticks and newlines in attacker-influenced task
268
+ // text cannot break out of any shell position, because there is no
269
+ // shell position for the text to occupy).
270
+ // `quick-batch parse-args --text "<raw string>"` (still supported for a
271
+ // caller that already holds the text as ONE real argv element with no
272
+ // shell quoting involved, e.g. a test harness; it is NOT safe to build
273
+ // that argument by splicing user text into a shell command line).
274
+ // In both forms this handler does the whitespace split itself, in Node,
275
+ // so shell pathname expansion (globbing) never happens.
262
276
  // `quick-batch parse-args -- <already-tokenized args>` (legacy/direct
263
277
  // form — still supported for a caller that already has a real argv
264
278
  // array with no shell splitting involved, e.g. a test harness).
265
279
  'parse-args': () => {
280
+ if (args.includes('--stdin')) {
281
+ let stdinText;
282
+ try {
283
+ stdinText = (_readStdin ?? readProcessStdin)();
284
+ }
285
+ catch (readError) {
286
+ return makeInvalidArgs('--stdin', `could not read standard input: ${readError instanceof Error ? readError.message : String(readError)}`, ERROR_REASON.USAGE);
287
+ }
288
+ const stdinTokens = stdinText.trim().length === 0 ? [] : stdinText.trim().split(/\s+/);
289
+ emit(dispatch.parseQuickBatchArgs(stdinTokens));
290
+ return;
291
+ }
266
292
  const textArg = argValue(args, '--text');
267
293
  if (textArg !== undefined) {
268
294
  const rawArgs = textArg.trim().length === 0 ? [] : textArg.trim().split(/\s+/);
@@ -140,6 +140,16 @@ function computeSpawnPlan(input) {
140
140
  }
141
141
  return { spawn, pending };
142
142
  }
143
+ /**
144
+ * #5118: one routing entry per writer-set status, keyed by the owner's type —
145
+ * no status literal is compared (local/no-verification-status-literal's
146
+ * type-aware arm), and `Record<VerifierStatus, …>` makes the table total.
147
+ */
148
+ const VERIFICATION_OUTCOME_ROUTES = Object.freeze({
149
+ passed: { action: 'complete' },
150
+ human_needed: { action: 'human_needed' },
151
+ gaps_found: { action: 'fail', failureReason: 'verification reported gaps_found (no automatic gap-fix retry in v1)' },
152
+ });
143
153
  /**
144
154
  * Route a verifier's status to an item action. `human_needed` is terminal
145
155
  * for the item — the caller must NOT call `completeQuickItem` (row 30, no
@@ -147,14 +157,7 @@ function computeSpawnPlan(input) {
147
157
  * WITHOUT an automatic gap-fix retry (row 31,34). `passed` completes it.
148
158
  */
149
159
  function routeVerificationOutcome(status) {
150
- switch (status) {
151
- case 'passed':
152
- return { action: 'complete' };
153
- case 'human_needed':
154
- return { action: 'human_needed' };
155
- case 'gaps_found':
156
- return { action: 'fail', failureReason: 'verification reported gaps_found (no automatic gap-fix retry in v1)' };
157
- }
160
+ return VERIFICATION_OUTCOME_ROUTES[status];
158
161
  }
159
162
  /**
160
163
  * Route a merge attempt's outcome to an item action. Both `merge_failed`
@@ -105,6 +105,14 @@ const SANDBOX_MARKER = 'GSD_TEST_HOME_SANDBOX';
105
105
  * `statSync` follows symlinks and reports the inode itself, so a case-variant
106
106
  * HOME, a symlinked HOME, and a bind-mounted HOME all compare equal.
107
107
  *
108
+ * `identify` calls `fs.statSync(p, { bigint: true })`, not the plain numeric
109
+ * form. Node documents that the default numeric `dev`/`ino` lose precision past
110
+ * `Number.MAX_SAFE_INTEGER` (2^53), and NTFS/ReFS file IDs routinely exceed
111
+ * that — so on Windows two DISTINCT directories can round to the same numeric
112
+ * `ino` and compare equal here, which is a fail-OPEN in a module whose whole
113
+ * job is refusing writes into the real home. `{ bigint: true }` reports the
114
+ * true 64-bit values with no precision loss.
115
+ *
108
116
  * Fails CLOSED, and "closed" here means returning FALSE. The sole caller is the
109
117
  * passwd-less marker branch, which READS a true as permission to proceed:
110
118
  *
@@ -130,7 +138,7 @@ function sameDirectory(a, b) {
130
138
  }
131
139
  function identify(p) {
132
140
  try {
133
- const st = node_fs_1.default.statSync(p);
141
+ const st = node_fs_1.default.statSync(p, { bigint: true });
134
142
  return { kind: 'ok', dev: st.dev, ino: st.ino };
135
143
  }
136
144
  catch (err) {
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestStatus = exports.ReportFormat = void 0;
4
+ exports.parseTestReport = parseTestReport;
5
+ /** Format adapters for RED evidence. Parsing never depends on the runner command. */
6
+ const tap_parser_cjs_1 = require("./vendor/tap-parser.cjs");
7
+ const saxes_cjs_1 = require("./vendor/saxes.cjs");
8
+ exports.ReportFormat = {
9
+ Tap: 'tap', Junit: 'junit', SwiftTesting: 'swift-testing', Unittest: 'unittest', Unknown: 'unknown',
10
+ };
11
+ exports.TestStatus = { Passed: 'passed', Failed: 'failed', Skipped: 'skipped', Todo: 'todo' };
12
+ function parseTap(output) {
13
+ const report = { format: exports.ReportFormat.Tap, valid: true, tests: [], issues: [] };
14
+ const parser = new tap_parser_cjs_1.Parser({ strict: true });
15
+ const assertions = [];
16
+ const openBuffered = new Map();
17
+ let completed = false;
18
+ const watch = (p) => {
19
+ p.on('child', (child) => {
20
+ if (child.buffered)
21
+ openBuffered.set(p, (openBuffered.get(p) ?? 0) + 1);
22
+ watch(child);
23
+ });
24
+ // tap-parser finalizes buffered children at EOF even without their closing
25
+ // brace. Track its child/line events so that truncation cannot prove RED.
26
+ p.on('line', (line) => {
27
+ if (line === '}\n')
28
+ openBuffered.set(p, (openBuffered.get(p) ?? 0) - 1);
29
+ });
30
+ p.on('assert', (result) => {
31
+ if (!result.closingTestPoint)
32
+ assertions.push({ result, owner: p });
33
+ if (result.buffered && !result.closingTestPoint)
34
+ report.issues.push('Missing buffered TAP subtest');
35
+ const diag = result.diag;
36
+ if (diag && ['cancelledByParent', 'hookFailed', 'testTimeoutFailure'].includes(String(diag['failureType']))) {
37
+ report.issues.push('Test run interrupted by cancellation, hook failure, or timeout');
38
+ }
39
+ });
40
+ p.on('extra', (extra) => { if (extra.trim())
41
+ report.issues.push('Non-TAP data in report'); });
42
+ p.on('complete', (summary) => {
43
+ if (p === parser)
44
+ completed = true;
45
+ const { start, end } = summary.plan;
46
+ if (start !== 1 || end === null || summary.count !== end)
47
+ report.issues.push('Missing or incomplete TAP plan');
48
+ if (summary.bailout)
49
+ report.issues.push('TAP bailout');
50
+ if (summary.failures.some((failure) => failure.tapError))
51
+ report.issues.push('Malformed TAP');
52
+ });
53
+ };
54
+ watch(parser);
55
+ parser.end(output);
56
+ if ([...openBuffered.values()].some((count) => count !== 0))
57
+ report.issues.push('Unclosed buffered TAP subtest');
58
+ for (const { result, owner } of assertions) {
59
+ let skipped = Boolean(result.skip);
60
+ let todo = Boolean(result.todo);
61
+ for (let parent = owner; parent; parent = parent.parent) {
62
+ skipped ||= Boolean(parent.closingTestPoint?.skip);
63
+ todo ||= Boolean(parent.closingTestPoint?.todo);
64
+ }
65
+ report.tests.push({
66
+ name: result.name,
67
+ identities: [...new Set([result.name, result.fullname])],
68
+ group: null,
69
+ groupIdentities: [],
70
+ status: skipped ? exports.TestStatus.Skipped : todo ? exports.TestStatus.Todo : result.ok ? exports.TestStatus.Passed : exports.TestStatus.Failed,
71
+ });
72
+ }
73
+ if (!completed)
74
+ report.issues.push('Incomplete TAP stream');
75
+ report.valid = report.issues.length === 0;
76
+ return report;
77
+ }
78
+ /** Surefire and Failsafe share the JUnit XML report shape. */
79
+ function parseJunit(output) {
80
+ const report = { format: exports.ReportFormat.Junit, valid: true, tests: [], issues: [] };
81
+ const parser = new saxes_cjs_1.SaxesParser({ xmlns: false });
82
+ const elements = [];
83
+ const suites = [];
84
+ let current = null;
85
+ parser.on('error', () => { report.issues.push('Malformed XML'); });
86
+ parser.on('doctype', () => { report.issues.push('JUnit reports must not declare a DTD'); });
87
+ parser.on('opentag', (tag) => {
88
+ const parent = elements.at(-1);
89
+ if (!parent && tag.name !== 'testsuite' && tag.name !== 'testsuites')
90
+ report.issues.push('Not a JUnit report');
91
+ if (tag.name === 'testsuite' || tag.name === 'testsuites') {
92
+ if (parent && parent !== 'testsuite' && parent !== 'testsuites')
93
+ report.issues.push('Misplaced test suite');
94
+ const count = tag.attributes['tests'];
95
+ const planned = count === undefined ? null : Number(count);
96
+ if (count !== undefined && (!/^\d+$/.test(count) || !Number.isSafeInteger(planned)))
97
+ report.issues.push('Invalid JUnit test count');
98
+ suites.push({ firstTest: report.tests.length, planned });
99
+ }
100
+ else if (tag.name === 'testcase') {
101
+ if (parent !== 'testsuite' || current)
102
+ report.issues.push('Misplaced test case');
103
+ const name = tag.attributes['name'] ?? '';
104
+ const className = tag.attributes['classname'] ?? '';
105
+ const qualified = className ? `${className}#${name}` : name;
106
+ if (!name)
107
+ report.issues.push('Unnamed test case');
108
+ current = {
109
+ name: qualified,
110
+ identities: [...new Set([name, qualified])],
111
+ group: className || null,
112
+ groupIdentities: className ? [className, className.split('.').at(-1)] : [],
113
+ status: exports.TestStatus.Passed,
114
+ };
115
+ }
116
+ else if (current && parent === 'testcase') {
117
+ if (tag.name === 'failure' || tag.name === 'error') {
118
+ if (current.status === exports.TestStatus.Skipped)
119
+ report.issues.push('Contradictory JUnit status');
120
+ current.status = exports.TestStatus.Failed;
121
+ }
122
+ if (tag.name === 'skipped') {
123
+ if (current.status === exports.TestStatus.Failed)
124
+ report.issues.push('Contradictory JUnit status');
125
+ current.status = exports.TestStatus.Skipped;
126
+ }
127
+ }
128
+ elements.push(tag.name);
129
+ });
130
+ parser.on('closetag', (tag) => {
131
+ if (tag.name === 'testcase' && current) {
132
+ report.tests.push(current);
133
+ current = null;
134
+ }
135
+ else if (tag.name === 'testsuite' || tag.name === 'testsuites') {
136
+ const suite = suites.pop();
137
+ if (!suite || (suite.planned !== null && suite.planned !== report.tests.length - suite.firstTest)) {
138
+ report.issues.push('Incomplete JUnit test suite');
139
+ }
140
+ }
141
+ elements.pop();
142
+ });
143
+ parser.write(output).close();
144
+ report.valid = report.issues.length === 0;
145
+ return report;
146
+ }
147
+ /**
148
+ * swift-testing console output (#4957). Only the anchored aggregate line marks
149
+ * the format; every declared test must have its own result line. The aggregate
150
+ * counts skipped tests too, and a parameterized test names its case count.
151
+ * A test is named by its quoted display name (with `(aka 'function()')` under
152
+ * --verbose) or, without one, by its bare function name. Windows consoles
153
+ * print √ × - in place of ✔ ✘ ━. swift-testing 6.1 omits the suite count from
154
+ * the aggregate and marks skipped tests with ✘ (later sources add the count
155
+ * and use ➜).
156
+ */
157
+ const SWIFT_AGGREGATE = /^[ \t]*[✘✔━×√-][ \t]*Test run with (\d+) tests?(?: in \d+ suites?)? (passed|failed)\b/gm;
158
+ const SWIFT_RESULT = /^[ \t]*[✘✔━➜×√-][ \t]*Test (?:"([^"]+)"(?: \(aka '([^']+)'\))?|([^\s"]\S*\)))(?: with \d+ test cases?)? (?:(failed|passed|was cancelled) after [\d.]+ seconds?(?: with \d+ [^\n]*?)?(?:\.|: "[^\n]*")|(skipped)(?:\.|: "[^\n]*"))$/gm;
159
+ function parseSwiftTesting(output) {
160
+ const report = { format: exports.ReportFormat.SwiftTesting, valid: true, tests: [], issues: [] };
161
+ let declared = 0;
162
+ let runPassed = true;
163
+ for (const match of output.matchAll(SWIFT_AGGREGATE)) {
164
+ declared += Number(match[1]);
165
+ runPassed &&= match[2] === 'passed';
166
+ }
167
+ for (const match of output.matchAll(SWIFT_RESULT)) {
168
+ const fn = match[2] ?? match[3];
169
+ report.tests.push({
170
+ name: match[1] ?? match[3],
171
+ identities: [...new Set([match[1], fn, fn?.replace(/\(.*\)$/, '')].filter((id) => Boolean(id)))],
172
+ group: null,
173
+ groupIdentities: [],
174
+ status: match[4] === 'failed' ? exports.TestStatus.Failed : match[4] === 'passed' ? exports.TestStatus.Passed : exports.TestStatus.Skipped,
175
+ });
176
+ }
177
+ if (report.tests.length !== declared)
178
+ report.issues.push('Incomplete swift-testing report');
179
+ if (runPassed && report.tests.some((test) => test.status === exports.TestStatus.Failed)) {
180
+ report.issues.push('Contradictory swift-testing status');
181
+ }
182
+ report.valid = report.issues.length === 0;
183
+ return report;
184
+ }
185
+ /**
186
+ * Python stdlib unittest text output (#4970). Passing tests are unnamed unless
187
+ * verbose, so they are counted from the summary; failures are named by their
188
+ * FAIL:/ERROR: headers, which must account for every counted failure. Each
189
+ * failing subTest repeats its method's header and counts as a failure, while
190
+ * Ran counts methods, so identical headers collapse into one failed test. An
191
+ * unexpected success fails the run but is a test that passed.
192
+ */
193
+ const UNITTEST_RAN = /^Ran (\d+) tests? in [\d.]+s$/gm;
194
+ const UNITTEST_HEADER = /^(?:FAIL|ERROR): (\S+)(?: \(([^)]*)\))?/gm;
195
+ function parseUnittest(output) {
196
+ const report = { format: exports.ReportFormat.Unittest, valid: true, tests: [], issues: [] };
197
+ const runs = [...output.matchAll(UNITTEST_RAN)];
198
+ if (runs.length !== 1) {
199
+ report.issues.push('Expected exactly one unittest summary');
200
+ report.valid = false;
201
+ return report;
202
+ }
203
+ const ran = Number(runs[0][1]);
204
+ const outcome = /^(OK|FAILED)(?: \(([^)]*)\))?$/m.exec(output.slice(runs[0].index + runs[0][0].length));
205
+ const counts = {};
206
+ for (const part of outcome?.[2]?.split(', ') ?? []) {
207
+ const [key, value] = part.split('=');
208
+ counts[key] = Number(value);
209
+ }
210
+ if (!outcome || Object.keys(counts).some((key) => !['failures', 'errors', 'skipped', 'expected failures', 'unexpected successes'].includes(key))) {
211
+ report.issues.push('Unsupported unittest outcome');
212
+ }
213
+ let headers = 0;
214
+ const failedIds = new Set();
215
+ for (const match of output.matchAll(UNITTEST_HEADER)) {
216
+ headers++;
217
+ const id = match[2] ?? '';
218
+ const key = `${match[1]} ${id}`;
219
+ if (failedIds.has(key))
220
+ continue;
221
+ failedIds.add(key);
222
+ // unittest synthesizes _FailedTest for an import/collection crash; its
223
+ // method name can equal the target, so it is a load failure, never RED.
224
+ if (id.includes('_FailedTest')) {
225
+ report.issues.push('unittest module failed to load');
226
+ continue;
227
+ }
228
+ report.tests.push({
229
+ name: match[1],
230
+ identities: [...new Set([match[1], id].filter(Boolean))],
231
+ group: null,
232
+ groupIdentities: [],
233
+ status: exports.TestStatus.Failed,
234
+ });
235
+ }
236
+ const failed = (counts['failures'] ?? 0) + (counts['errors'] ?? 0);
237
+ const skipped = counts['skipped'] ?? 0;
238
+ const runFailed = failed > 0 || (counts['unexpected successes'] ?? 0) > 0;
239
+ if (headers !== failed || (outcome?.[1] === 'FAILED') !== runFailed || failedIds.size + skipped > ran) {
240
+ report.issues.push('Incomplete unittest report');
241
+ }
242
+ const unnamed = (status) => ({ name: '', identities: [], group: null, groupIdentities: [], status });
243
+ for (let i = 0; i < skipped; i++)
244
+ report.tests.push(unnamed(exports.TestStatus.Skipped));
245
+ for (let i = Math.max(ran - failedIds.size - skipped, 0); i > 0; i--)
246
+ report.tests.push(unnamed(exports.TestStatus.Passed));
247
+ report.valid = report.issues.length === 0;
248
+ return report;
249
+ }
250
+ const adapters = [
251
+ { matches: (output) => /^(?:TAP version \d+|(?:not )?ok\b|1\.\.\d+|#)/.test(output.trimStart()), parse: parseTap },
252
+ { matches: (output) => output.trimStart().startsWith('<'), parse: parseJunit },
253
+ { matches: (output) => new RegExp(SWIFT_AGGREGATE.source, 'm').test(output), parse: parseSwiftTesting },
254
+ { matches: (output) => new RegExp(UNITTEST_RAN.source, 'm').test(output), parse: parseUnittest },
255
+ ];
256
+ /** Parse supported reports into one result contract; unknown/malformed input fails closed. */
257
+ function parseTestReport(output) {
258
+ const adapter = adapters.find((candidate) => candidate.matches(output));
259
+ if (!adapter)
260
+ return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Unsupported report format'] };
261
+ try {
262
+ const report = adapter.parse(output);
263
+ // One entry per problem: a long run of bad lines must not amplify the payload.
264
+ return { ...report, issues: [...new Set(report.issues)] };
265
+ }
266
+ catch {
267
+ return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Malformed report'] };
268
+ }
269
+ }
@@ -64,7 +64,7 @@ exports.checkReviewerDocsParity = checkReviewerDocsParity;
64
64
  * `args` is an argv TEMPLATE, not a prefix. The injected pieces — model, effort, output file,
65
65
  * argv-borne prompt — do not all go in the same place, and no positional rule expresses that:
66
66
  * `codex` injects the model in the MIDDLE (after the `exec --ephemeral` subcommand) and the output
67
- * file later still, while `gemini` injects the model first and five lanes end with a bare `-` that
67
+ * file later still, while `kimi-code` injects the model first and five lanes end with a bare `-` that
68
68
  * must stay last. Splicing by position silently produced
69
69
  * `codex --model M -o F exec --ephemeral …`, which is not a valid codex invocation.
70
70
  *
@@ -94,36 +94,16 @@ const SPAWN_STDIN_STDOUT = {
94
94
  outputChannel: 'stdout',
95
95
  };
96
96
  /**
97
- * The twelve declared lanes, in `write_reviews` order.
97
+ * The eleven declared lanes, in `write_reviews` order.
98
+ *
99
+ * The `gemini` lane was retired by #4709: Google sunset Gemini CLI on 2026-06-18 (the same
100
+ * sunset that removed the gemini RUNTIME in #1928/1.8.0), so the lane spawned a binary that no
101
+ * longer serves the free/Pro/Ultra tiers that are GSD's audience.
98
102
  *
99
103
  * `kimi-code` joined in Phase 5b (#2799, closes #2718) — ADR-2782's phase table lands it here
100
104
  * rather than in 5a precisely so it arrives together with the iteration that can invoke it.
101
105
  */
102
106
  exports.REVIEWER_LANES = Object.freeze([
103
- {
104
- slug: 'gemini',
105
- flags: ['--gemini'],
106
- transport: 'spawn',
107
- probe: { kind: 'command-exists', binary: 'gemini' },
108
- invoke: {
109
- binary: 'gemini',
110
- args: ['{{model}}', '-p', '-'],
111
- ...SPAWN_STDIN_STDOUT,
112
- modelArg: '-m',
113
- effortChannel: 'none',
114
- },
115
- timeoutFloorMs: 900_000,
116
- timeoutConfigKey: 'review.timeouts.gemini',
117
- emptyOutput: 'stub-with-stderr',
118
- reviewsSection: 'Gemini',
119
- evidenceClass: 'source-grounded',
120
- requiresBinaries: [],
121
- promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.gemini',
122
- modelConfigKey: 'review.models.gemini',
123
- effortConfigKey: null,
124
- defaultEffort: null,
125
- handler: null,
126
- },
127
107
  {
128
108
  // 1_200_000 rather than the 900_000 floor: headless Claude measured ~525 s
129
109
  // on a large plan set (review.md:304).
@@ -139,8 +119,8 @@ exports.REVIEWER_LANES = Object.freeze([
139
119
  effortChannel: 'argv',
140
120
  // #2483: without these the claude leg is the only reviewer that additionally inherits the
141
121
  // invoking user's global CLAUDE.md, the project CLAUDE.md, and Claude Code auto-memory —
142
- // a context asymmetry against the independent-review premise (gemini sees only the
143
- // assembled prompt; codex runs --ephemeral). Both flags, not just the first: CLAUDE.md
122
+ // a context asymmetry against the independent-review premise (codex sees only the
123
+ // assembled prompt and runs --ephemeral). Both flags, not just the first: CLAUDE.md
144
124
  // loading and auto-memory are independently-toggled mechanisms, and an environment
145
125
  // exporting CLAUDE_CODE_DISABLE_AUTO_MEMORY=0 forces auto-memory back ON — the explicit
146
126
  // pair is robust against that. Applies when /gsd:review runs from a non-Claude-Code host;
@@ -796,7 +776,7 @@ const NON_LANE_SIGNATURE_FLAGS = new Set(['--all']);
796
776
  /**
797
777
  * Is `flag` documented in `text` under one of the two structural shapes docs actually use?
798
778
  *
799
- * Backticked is the `COMMANDS.md` table-cell shape; bracketed (`[--gemini]`) is the
779
+ * Backticked is the `COMMANDS.md` table-cell shape; bracketed (`[--codex]`) is the
800
780
  * `FEATURES.md` signature shape. Requiring one of those two delimiters — rather than a bare
801
781
  * substring — is what keeps prose and fenced examples from satisfying the gate, and it bounds the
802
782
  * token for free: a backticked `--claude` demands its closing backtick, so a backticked
@@ -889,7 +869,7 @@ const BRACKETED_FLAG_RE = /\[(--[a-z0-9][a-z0-9-]*)\]/g;
889
869
  * cell 3. A file-scoped check is therefore satisfied by the forwarding row alone, so deleting a
890
870
  * lane's actual table row — the exact #2781 regression — passes. Keying on cell 1 separates the
891
871
  * two: `| `--agy` / `--antigravity` | … |` is one lane row declaring two flags, while
892
- * `| Reviewer flags | No | … `--gemini`, `--claude` … |` is not a lane row at all.
872
+ * `| Reviewer flags | No | … `--codex`, `--claude` … |` is not a lane row at all.
893
873
  */
894
874
  function flagsInFirstTableCell(lines, declared) {
895
875
  const found = new Set();
@@ -20,7 +20,7 @@
20
20
  * `reviewer` bodies in the capability registry, not a hand-maintained tail.
21
21
  * A capability of EITHER `role: "runtime"` (the six dual-purpose hosts —
22
22
  * antigravity/claude/codex/cursor/opencode/qwen) or the lane-only
23
- * `role: "reviewer"` (gemini/coderabbit/ollama/lm_studio/llama_cpp) may carry
23
+ * `role: "reviewer"` (coderabbit/ollama/lm_studio/llama_cpp) may carry
24
24
  * a `reviewer` body, and it is the body's `reviewer.slug` — NOT the capability
25
25
  * id — that becomes the roster entry: the two differ for `lm-studio` (id) /
26
26
  * `lm_studio` (slug) and `llama-cpp` (id) / `llama_cpp` (slug), ADR-2782's
@@ -229,7 +229,7 @@ function resolveReviewerSelection(input) {
229
229
  // ADR-2782 D4: absent-safe governs DISCOVERY, never explicit selection.
230
230
  // Not finding a lane nobody asked for is normal; failing to run a lane
231
231
  // somebody asked for is an error. Every miss used to be an `info`, so a
232
- // PARTIAL miss (`--gemini --qwen` with qwen absent) ran the review with a
232
+ // PARTIAL miss (`--codex --qwen` with qwen absent) ran the review with a
233
233
  // thinner reviewer set while present_results reported success — "a cross-AI
234
234
  // review that silently drops a lane is blind in one eye" (review.md).
235
235
  // A total miss already errored, but only as a side effect of the selected
@@ -13,12 +13,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const command_aliases_cjs_1 = require("./command-aliases.cjs");
16
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
16
17
  // eslint-disable-next-line @typescript-eslint/no-require-imports
17
18
  const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
18
19
  const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
19
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
20
21
  const roadmapUpgrade = require("./roadmap-upgrade.cjs");
21
22
  // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ const phaseIdCardMod = require("./phase-id-card.cjs");
24
+ const { phaseIdCard } = phaseIdCardMod;
25
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
22
26
  const planningWorkspace = require("./planning-workspace.cjs");
23
27
  const { planningDir } = planningWorkspace;
24
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -142,14 +146,13 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
142
146
  if (roadmapContent.trim() === '') {
143
147
  warnings.push({ code: 'V002', message: 'ROADMAP.md is empty' });
144
148
  }
145
- // Malformed frontmatter — a `---` opener with no matching closer.
146
- // Tolerate a leading BOM (#3057) before the fence.
147
- const contentAfterBom = roadmapContent.replace(/^\uFEFF/, '');
148
- if (contentAfterBom.startsWith('---')) {
149
- const closeMatch = contentAfterBom.slice(3).match(/\r?\n---\s*(\r?\n|$)/);
150
- if (!closeMatch) {
151
- warnings.push({ code: 'V003', message: 'ROADMAP.md frontmatter is malformed (unterminated --- fence)' });
152
- }
149
+ // Malformed frontmatter — a `---` opener with no matching closer, as the one fence
150
+ // owner reads it (a leading BOM, #3057, is tolerated there).
151
+ const roadmapFence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(roadmapContent);
152
+ // The milestone-window read below sees the document without its leading BOM (#3057).
153
+ const contentAfterBom = roadmapContent.charCodeAt(0) === 0xFEFF ? roadmapContent.slice(1) : roadmapContent;
154
+ if (roadmapFence && !roadmapFence.closed) {
155
+ warnings.push({ code: 'V003', message: 'ROADMAP.md frontmatter is malformed (unterminated --- fence)' });
153
156
  }
154
157
  // #3641: resolve phase_id_convention ONCE, ahead of every consumer in
155
158
  // this validate pass — V004's entry check and V005's scope classifier
@@ -166,13 +169,11 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
166
169
  convention = undefined;
167
170
  }
168
171
  if (convention === undefined || convention === null) {
169
- // Fallback: read from ROADMAP.md frontmatter. Bounded to match
170
- // cmdRoadmapMilestoneScope's copy exactly (#3641 review NEW-1: an
171
- // unbounded capture here read past 4KB frontmatters the probe's
172
- // bounded copy could not, diverging validate from the probe).
173
- const fmMatch = roadmapContent.match(/^---\r?\n([\s\S]{0,4000}?)\r?\n---/);
174
- if (fmMatch) {
175
- const kvMatch = fmMatch[1].match(/^phase_id_convention:\s*(.*)$/m);
172
+ // Fallback: read from ROADMAP.md frontmatter — the block the one fence owner finds,
173
+ // exactly as cmdRoadmapMilestoneScope reads it (#3641 review NEW-1: two copies of
174
+ // this read disagreed on long frontmatter, diverging validate from the probe).
175
+ if (roadmapFence?.closed) {
176
+ const kvMatch = roadmapContent.slice(roadmapFence.openEnd, roadmapFence.bodyEnd).match(/^phase_id_convention:\s*(.*)$/m);
176
177
  if (kvMatch) {
177
178
  const val = kvMatch[1].trim();
178
179
  if (val !== 'null' && val !== '') {
@@ -234,7 +235,7 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
234
235
  'upgrade': () => {
235
236
  const dryRun = !args.includes('--apply');
236
237
  // Parse `--convention <value>` and `--convention=<value>`. When the flag is
237
- // absent entirely, default to the only supported convention; when present
238
+ // absent entirely, default to the legacy supported convention; when present
238
239
  // with a missing/unsupported value, fall through to the rejection below
239
240
  // (fail-closed — never silently run a migration the user did not request).
240
241
  let convention = 'milestone-prefixed';
@@ -245,13 +246,18 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
245
246
  ? token.slice(token.indexOf('=') + 1)
246
247
  : (args[conventionFlagIdx + 1] ?? '');
247
248
  }
248
- if (convention !== 'milestone-prefixed') {
249
+ if (convention !== 'milestone-prefixed' && convention !== 'bracket') {
249
250
  // No-throw hub contract (ADR-0012): a hub-dispatched handler must not call
250
251
  // process.exit. Throw instead — the hub converts this to HandlerFailure and
251
252
  // the adapter routes it through the injected error() boundary.
252
- throw new Error('Only --convention milestone-prefixed is supported');
253
+ throw new Error('Only --convention milestone-prefixed or bracket is supported');
254
+ }
255
+ if (convention === 'bracket') {
256
+ // Keep stdout machine-readable for the dry-run JSON plan. The card is
257
+ // human guidance emitted at command start for both dry-run and apply.
258
+ process.stderr.write(`${phaseIdCard({ title: 'Bracket phase-ID convention' })}\n`);
253
259
  }
254
- const plan = roadmapUpgrade.computeMigrationPlan(cwd);
260
+ const plan = roadmapUpgrade.computeMigrationPlan(cwd, { convention });
255
261
  roadmapUpgrade.applyMigration(cwd, plan, { dryRun });
256
262
  },
257
263
  },