@opengsd/gsd-core 1.11.0 → 1.13.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 (498) 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 +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -23,6 +23,7 @@ exports.findTableBySchema = findTableBySchema;
23
23
  exports.findTableWithColumns = findTableWithColumns;
24
24
  exports.escapeCell = escapeCell;
25
25
  exports.appendQuickTaskRow = appendQuickTaskRow;
26
+ exports.migrateQuickTasksTable = migrateQuickTasksTable;
26
27
  exports.resetQuickTaskRows = resetQuickTaskRows;
27
28
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
28
29
  // ─── Schema registry ──────────────────────────────────────────────────────────
@@ -657,6 +658,54 @@ function escapeCell(value) {
657
658
  * Raw Text Matching").
658
659
  */
659
660
  exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
661
+ /**
662
+ * #3860: heading predicate for STATE.md's Quick Tasks Completed section(s).
663
+ * The heading is a section LABEL, not data — milestone-scoped files
664
+ * legitimately carry suffixed headings (`### Quick Tasks Completed (v1.1+)`
665
+ * beside an archived `(v1.0)`), so the match is prefix-anchored with a word
666
+ * boundary, never exact: `Quick Tasks Completedness` must NOT match. Hoisted
667
+ * beside QUICK_TASKS_SECTION_ABSENT so `appendQuickTaskRow` and
668
+ * `resetQuickTaskRows` cannot drift apart again.
669
+ */
670
+ const isQuickTasksHeading = (h) => /^quick tasks completed\b/i.test(h.text.trim());
671
+ /**
672
+ * #3860: among ALL heading-matching sections, pick the first whose body is a
673
+ * table with a recognized Quick Tasks schema — a legacy/unparseable table
674
+ * first in document order no longer shadows a usable one further down. When
675
+ * NO section is usable, return the FIRST match so the caller's downstream
676
+ * error describes the real problem (unparseable/legacy table) instead of a
677
+ * false QUICK_TASKS_SECTION_ABSENT.
678
+ *
679
+ * Bounding: `collectSections` ends a candidate's body only at the NEXT
680
+ * matching heading — far too wide for splicing (it would swallow an
681
+ * intervening `## Deferred Items` table into the Quick Tasks body, and
682
+ * `appendQuickTaskRow`'s last-table-line scan would then splice the new row
683
+ * into that WRONG table). Each candidate is therefore re-collected through
684
+ * `collectSection` with an offset-precise predicate, whose default
685
+ * level-bounded stop (next heading of the same or higher level) is exactly
686
+ * the semantics the pre-#3860 single-section lookup had.
687
+ *
688
+ * Convention: "first" is document order — the newest-on-top layout the issue
689
+ * itself demonstrates (`(v1.1+)` above an archived `(v1.0)`). When several
690
+ * suffixed sections all carry recognized schemas, this layer has no signal
691
+ * for which milestone is active, so document order is the pinned tie-break.
692
+ */
693
+ function selectQuickTasksSection(stateContent) {
694
+ const candidates = (0, markdown_sectionizer_cjs_1.collectSections)(stateContent, isQuickTasksHeading);
695
+ if (candidates.length === 0)
696
+ return null;
697
+ const boundedOf = (cand) => (0, markdown_sectionizer_cjs_1.collectSection)(stateContent, (h) => h.offset === cand.heading.offset);
698
+ const firstBounded = boundedOf(candidates[0]);
699
+ for (const cand of candidates) {
700
+ const bounded = boundedOf(cand);
701
+ if (!bounded)
702
+ continue;
703
+ const parsed = parseMarkdownTable(bounded.body);
704
+ if (parsed.ok && matchTableSchema(parsed.value.columns)?.id === 'QuickTasks')
705
+ return bounded;
706
+ }
707
+ return firstBounded;
708
+ }
660
709
  /**
661
710
  * Append one row to STATE.md's "Quick Tasks Completed" table.
662
711
  *
@@ -677,7 +726,7 @@ exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
677
726
  * rows), preserving any surrounding blank lines/trailing content in the section.
678
727
  */
679
728
  function appendQuickTaskRow(stateContent, fields) {
680
- const section = (0, markdown_sectionizer_cjs_1.collectSection)(stateContent, (h) => /^quick tasks completed$/i.test(h.text.trim()));
729
+ const section = selectQuickTasksSection(stateContent);
681
730
  if (!section) {
682
731
  return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
683
732
  }
@@ -697,7 +746,7 @@ function appendQuickTaskRow(stateContent, fields) {
697
746
  const rowNumber = parsed.value.rows.length + 1;
698
747
  const cellFor = (col) => {
699
748
  switch (col) {
700
- case '#': return escapeCell(String(rowNumber));
749
+ case '#': return escapeCell(fields.quickId ?? String(rowNumber));
701
750
  case 'Description': return escapeCell(fields.description);
702
751
  case 'Date': return escapeCell(fields.date);
703
752
  case 'Commit': return escapeCell(fields.commit);
@@ -728,6 +777,128 @@ function appendQuickTaskRow(stateContent, fields) {
728
777
  const content = (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody);
729
778
  return { ok: true, value: { content, row, variant: match.label } };
730
779
  }
780
+ // ─── migrateQuickTasksTable (#3730 option b) ───────────────────────────────
781
+ /**
782
+ * Column-name → canonical-cell mapping for migrating a legacy Quick Tasks table
783
+ * (#3730). Name-based, case-insensitive; every column NOT in this map lands in
784
+ * the Description bucket (joined with ' · ' in original column order, unknown
785
+ * names kept as `name: value`) so no historical datum is dropped.
786
+ */
787
+ const QUICK_TASKS_COLUMN_ALIASES = {
788
+ '#': '#', id: '#',
789
+ date: 'Date', when: 'Date',
790
+ commit: 'Commit', sha: 'Commit',
791
+ status: 'Status',
792
+ directory: 'Directory', artifacts: 'Directory', path: 'Directory', dir: 'Directory',
793
+ description: 'Description', task: 'Description', summary: 'Description',
794
+ slug: 'Description', scope: 'Description', title: 'Description',
795
+ };
796
+ /**
797
+ * Migrate STATE.md's "Quick Tasks Completed" table onto the canonical
798
+ * `with-status` schema (#3730, maintainer decision 2026-09-02: option b).
799
+ *
800
+ * The pre-registry prose template licensed arbitrary column shapes GSD itself
801
+ * emitted, and `appendQuickTaskRow` fails loud on every one of them — leaving a
802
+ * project permanently unappendable. This is the supported repair path: the
803
+ * `gsd-tools quick-tasks-migrate` subcommand, plus an automatic check on the
804
+ * first `quick`/`fast` run (the workflow calls this before appending), which is
805
+ * a silent no-op when the section is absent, the table is already canonical, or
806
+ * the user never runs quick at all.
807
+ *
808
+ * Same section-selection, parse, and fail-loud posture as `appendQuickTaskRow`
809
+ * (ADR-2143 §7 upheld — append still rejects; this REPAIRS). Returns
810
+ * `{ok:true, value:{content, migrated:false}}` for the no-op cases so callers
811
+ * can stay silent, and `{migrated:true, from, rows}` when a rewrite happened so
812
+ * the CLI can report exactly what moved.
813
+ */
814
+ function migrateQuickTasksTable(stateContent) {
815
+ const section = selectQuickTasksSection(stateContent);
816
+ if (!section) {
817
+ return { ok: true, value: { content: stateContent, migrated: false } };
818
+ }
819
+ const parsed = parseMarkdownTable(section.body);
820
+ if (!parsed.ok) {
821
+ return { ok: false, reason: `quick-tasks table: ${parsed.reason}` };
822
+ }
823
+ const match = matchTableSchema(parsed.value.columns);
824
+ if (match && match.id === 'QuickTasks') {
825
+ return { ok: true, value: { content: stateContent, migrated: false } };
826
+ }
827
+ const canonical = exports.TABLE_SCHEMAS.QuickTasks.find((v) => v.label === 'with-status').columns;
828
+ // Map each legacy column to its canonical target; unmapped names keep their
829
+ // identity so the Description bucket can render them losslessly.
830
+ const targets = parsed.value.columns.map((c) => QUICK_TASKS_COLUMN_ALIASES[c.trim().toLowerCase()] ?? null);
831
+ const rows = parsed.value.rows.map((row, rowIdx) => {
832
+ const bucket = {
833
+ '#': '', Description: '', Date: '', Commit: '', Status: '', Directory: '',
834
+ };
835
+ const descriptionParts = [];
836
+ // parseMarkdownTable hands back name-keyed records (rows[i][columnName]).
837
+ parsed.value.columns.forEach((col, i) => {
838
+ const raw = (row[col] ?? '').trim();
839
+ const target = targets[i];
840
+ if (target === 'Description') {
841
+ if (raw)
842
+ descriptionParts.push(raw);
843
+ }
844
+ else if (target) {
845
+ // Collision-safe (#3730 review): two legacy columns aliasing the same
846
+ // canonical target must not silently overwrite — the displaced value
847
+ // joins the Description bucket under its own name, same convention as
848
+ // an unknown column, so no historical datum is dropped.
849
+ if (raw && bucket[target]) {
850
+ descriptionParts.push(`${col.trim()}: ${raw}`);
851
+ }
852
+ else if (raw) {
853
+ bucket[target] = raw;
854
+ }
855
+ }
856
+ else if (raw) {
857
+ descriptionParts.push(`${col.trim()}: ${raw}`);
858
+ }
859
+ });
860
+ bucket['#'] = bucket['#'] || String(rowIdx + 1);
861
+ bucket.Description = descriptionParts.join(' · ');
862
+ bucket.Date = bucket.Date || '—';
863
+ bucket.Commit = bucket.Commit || '—';
864
+ bucket.Status = bucket.Status || '—';
865
+ bucket.Directory = bucket.Directory || '—';
866
+ return `| ${canonical.map((c) => escapeCell(bucket[c])).join(' | ')} |`;
867
+ });
868
+ const eol = /\r\n/.test(section.body) ? '\r\n' : '\n';
869
+ const lines = section.body.split(/\r?\n/);
870
+ // CONTIGUOUS-run bound (#3730 review), mirroring resetQuickTaskRows: the
871
+ // section body may carry a SECOND table (a `#### Notes` subsection or a
872
+ // trailing table after a blank line) that parseMarkdownTable never read —
873
+ // scanning for the last pipe line anywhere in the body would splice the
874
+ // rewrite across it and silently destroy it. Only the first CONTIGUOUS run
875
+ // of pipe lines — the table that was parsed — is replaced.
876
+ const firstTableLineIdx = lines.findIndex((l) => l.trim().startsWith('|'));
877
+ let lastTableLineIdx = firstTableLineIdx;
878
+ while (lastTableLineIdx + 1 < lines.length && lines[lastTableLineIdx + 1].trim().startsWith('|')) {
879
+ lastTableLineIdx++;
880
+ }
881
+ const header = `| ${canonical.join(' | ')} |`;
882
+ // Widths match workflows/quick.md's canonical template byte-for-byte
883
+ // (#3730 review): its delimiter is max(3, len+2) per column.
884
+ const delimiter = `| ${canonical.map((c) => '-'.repeat(Math.max(3, c.length + 2))).join(' | ')} |`;
885
+ const newBody = [
886
+ ...lines.slice(0, firstTableLineIdx),
887
+ header,
888
+ delimiter,
889
+ ...rows,
890
+ ...lines.slice(lastTableLineIdx + 1),
891
+ ].join(eol);
892
+ return {
893
+ ok: true,
894
+ value: {
895
+ content: (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody),
896
+ migrated: true,
897
+ from: parsed.value.columns,
898
+ rows: rows.length,
899
+ },
900
+ };
901
+ }
731
902
  // ─── resetQuickTaskRows (#2142) ────────────────────────────────────────────
732
903
  /**
733
904
  * Clear every DATA row from STATE.md's "Quick Tasks Completed" table, leaving
@@ -736,7 +907,7 @@ function appendQuickTaskRow(stateContent, fields) {
736
907
  * directories out from under the table (see `src/milestone.cts`'s
737
908
  * `archiveQuickTaskDirectories` / `cmdMilestoneComplete` wiring).
738
909
  *
739
- * Mirrors `appendQuickTaskRow`'s exact contract (same `collectSection` ->
910
+ * Mirrors `appendQuickTaskRow`'s exact contract (same `selectQuickTasksSection` ->
740
911
  * `parseMarkdownTable` -> `matchTableSchema` pipeline, same fail-loud posture,
741
912
  * same EOL-detect-before-split handling) rather than inventing a second one:
742
913
  * - no "Quick Tasks Completed" heading -> `{ok:false, reason:
@@ -758,7 +929,7 @@ function resetQuickTaskRows(stateContent) {
758
929
  if (typeof stateContent !== 'string' || stateContent.trim() === '') {
759
930
  return { ok: false, reason: 'empty or non-string input' };
760
931
  }
761
- const section = (0, markdown_sectionizer_cjs_1.collectSection)(stateContent, (h) => /^quick tasks completed$/i.test(h.text.trim()));
932
+ const section = selectQuickTasksSection(stateContent);
762
933
  if (!section) {
763
934
  return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
764
935
  }
@@ -31,8 +31,14 @@ const { resolveQuickTaskSummaryFile } = auditMod;
31
31
  const ioMod = require("./io.cjs");
32
32
  const { output, error } = ioMod;
33
33
  // eslint-disable-next-line @typescript-eslint/no-require-imports
34
+ const cliExitMod = require("./cli-exit.cjs");
35
+ const { ExitError } = cliExitMod;
36
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
37
+ const stateContract = require("./state-contract.cjs");
38
+ const { publishStateContract } = stateContract;
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
34
40
  const phaseIdMod = require("./phase-id.cjs");
35
- const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId } = phaseIdMod;
41
+ const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId, isSentinelPhaseDir } = phaseIdMod;
36
42
  const pattern_cjs_1 = require("./pattern.cjs");
37
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports
38
44
  const roadmapParserMod = require("./roadmap-parser.cjs");
@@ -49,7 +55,7 @@ const { scanPhasePlans } = planScanMod;
49
55
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
50
56
  const phaseLocatorMod = require("./phase-locator.cjs");
51
57
  const { listMilestonePhaseDirs } = phaseLocatorMod;
52
- const { planningPaths } = planningWorkspace;
58
+ const { planningPaths, resolvePhaseIdConvention } = planningWorkspace;
53
59
  const { extractFrontmatter } = frontmatterMod;
54
60
  // ADR-3408 §8.3 / #3469: `writeStateMd` gets sync and NO preservation — the
55
61
  // same #3374-shaped exposure the milestone-complete write used to carry (a
@@ -477,7 +483,7 @@ function applyQuickTasksReset(content) {
477
483
  }
478
484
  function cmdMilestoneComplete(cwd, version, options, raw) {
479
485
  if (!version) {
480
- error('version required for milestone complete (e.g., v1.0)');
486
+ error('version required for milestone complete (e.g., v1.0) — and --confirm to mutate');
481
487
  }
482
488
  // #2288 security: `version` is a CLI positional that is interpolated into
483
489
  // multiple filesystem sinks below — `path.join(archiveDir, `${version}-ROADMAP.md`)`,
@@ -488,6 +494,23 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
488
494
  if (!ARCHIVE_VERSION_LABEL_RE.test(version)) {
489
495
  error(`milestone complete: version "${version}" is invalid — a milestone version label may contain only letters, digits, '.', '-' and '_', and must not contain path separators or "..".`);
490
496
  }
497
+ // #3726: confirmation gate — refuse before ANY read of the tree beyond the
498
+ // arg checks above, so an unconfirmed invocation is a guaranteed no-op on
499
+ // disk. The threat model is the NEVER_VALID_FLAGS one (gsd-tools.cjs): a
500
+ // caller supplies a token it believes is inert and a destructive operation
501
+ // proceeds unchecked — here the caller-side belief was that the `query`
502
+ // meta-prefix implies a read, and `query milestone.complete <v>` archived
503
+ // the milestone with no confirmation. The prefix is an intentional
504
+ // invocation-compatibility mechanism, not a permission boundary, so the
505
+ // gate lives on the destructive command itself and covers every invocation
506
+ // path. --dry-run needs no confirmation (it mutates nothing and is the
507
+ // recommended first step); --force does NOT imply it (see
508
+ // MilestoneCompleteOptions.confirm).
509
+ if (!options.dryRun && !options.confirm) {
510
+ error(`milestone complete is irreversible: it archives ROADMAP.md and REQUIREMENTS.md, MOVES every phase ` +
511
+ `directory for ${version} into .planning/milestones/, and rewrites STATE.md. ` +
512
+ `Nothing has been changed. Re-run with --confirm to proceed, or --dry-run to preview exactly what would move.`);
513
+ }
491
514
  const roadmapPath = planningPaths(cwd).roadmap;
492
515
  const reqPath = planningPaths(cwd).requirements;
493
516
  const statePath = planningPaths(cwd).state;
@@ -660,6 +683,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
660
683
  }
661
684
  }
662
685
  catch (e) {
686
+ // ADR-3889: error() now throws ExitError (carries no message) instead
687
+ // of calling process.exit() directly, so it can no longer be detected
688
+ // by sniffing e.message — an ExitError from our own guard above must be
689
+ // re-thrown UNCONDITIONALLY, before any message inspection, or the
690
+ // guard silently stops blocking milestone completion.
691
+ if (e instanceof ExitError)
692
+ throw e;
663
693
  // If the error came from our guard, re-throw it; otherwise skip silently.
664
694
  const message = e instanceof Error ? e.message : String(e);
665
695
  if (message && message.startsWith('Cannot mark milestone complete:'))
@@ -672,6 +702,22 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
672
702
  let totalPlans = 0;
673
703
  let totalTasks = 0;
674
704
  const accomplishments = [];
705
+ // #2761 (round-11 BLOCKER): resolved ONCE, ambiently (no explicit `ws` —
706
+ // this function has none of its own and already resolves everything else
707
+ // off `cwd` via planningPaths(cwd), matching the ambient-workstream
708
+ // contract `resolvePhaseIdConvention`/`planningDir` share), and threaded
709
+ // into the single `listMilestonePhaseDirs` call directly below. Left
710
+ // unthreaded, `phaseIdConvention` stays `undefined` and
711
+ // `getMilestonePhaseFilter` silently resolves it FROM CONFIG itself —
712
+ // which is exactly the behavior the changeset originally (and wrongly)
713
+ // claimed this PR does not reach: on a bracket-convention project that
714
+ // lazily-resolved convention changes `milestonePhaseDirs`/
715
+ // `milestonePhaseScope` below, and this function goes on to ARCHIVE
716
+ // (rename/move) whatever that set names. Resolving explicitly here removes
717
+ // the silent-inherit path without changing behavior for `null` /
718
+ // `milestone-prefixed` projects, whose resolved convention is the same
719
+ // value `undefined` would have lazily produced anyway.
720
+ const phaseConvention = resolvePhaseIdConvention(cwd);
675
721
  // #3597 (ADR-3180 Decision 2): SINGLE resolution of "which phase
676
722
  // directories belong to the current milestone" AND the SCOPE discriminator
677
723
  // that resolution came from — shared verbatim by the read-only stats loop
@@ -685,7 +731,11 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
685
731
  // disk, same as before #3597. Only the destructive archive pass (and its
686
732
  // --dry-run preview) refuses to act on a non-COMPLETE (non-answer) scope;
687
733
  // see the guard built from `milestonePhaseScope` further down.
688
- const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, { cwd, versionOverride: version });
734
+ const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
735
+ cwd,
736
+ versionOverride: version,
737
+ phaseIdConvention: phaseConvention,
738
+ });
689
739
  try {
690
740
  for (const dir of milestonePhaseDirs) {
691
741
  phaseCount++;
@@ -838,6 +888,12 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
838
888
  // Create/append MILESTONES.md entry
839
889
  const accomplishmentsList = accomplishments.map((a) => `- ${a}`).join('\n');
840
890
  const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
891
+ // #3685: mirror requirementsUpdated's diff-tracking contract — the result
892
+ // below used to report `milestones_updated: true` hardcoded, never
893
+ // consulting whether the MILESTONES.md write actually changed anything.
894
+ // Captured before the write branches below so the after-comparison reports
895
+ // a real content diff instead of an assumed one.
896
+ const milestonesBefore = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
841
897
  if (node_fs_1.default.existsSync(milestonesPath)) {
842
898
  const existing = node_fs_1.default.readFileSync(milestonesPath, 'utf-8');
843
899
  if (!existing.trim()) {
@@ -867,6 +923,10 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
867
923
  else {
868
924
  (0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, `# Milestones\n\n${milestoneEntry}`);
869
925
  }
926
+ // #3685: real content diff, not the hardcoded `true` this used to report —
927
+ // see the `milestonesBefore` capture above.
928
+ const milestonesAfter = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
929
+ const milestonesUpdated = milestonesAfter !== milestonesBefore;
870
930
  // #2142 BLOCKER 2 (review): opt-in quick-task archival. This call MUST sit
871
931
  // immediately adjacent to the STATE.md write block directly below it, with
872
932
  // NO unguarded IO in between (unlike the ROADMAP/REQUIREMENTS/audit/
@@ -906,6 +966,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
906
966
  // taken). `resync: true` mirrors `cmdPhaseComplete`'s posture (progress
907
967
  // recomputed from disk; only the preserve-when-unchanged deltas apply) —
908
968
  // milestone completion is the same kind of lifecycle transition.
969
+ // #3685: mirror requirementsUpdated's diff-tracking contract — this used to
970
+ // report `state_updated: fs.existsSync(statePath)`, true even on a no-op
971
+ // transaction. Declared here beside `stateUpdated`'s sibling flags and
972
+ // defaulted to `false` so the "STATE.md absent" case keeps today's answer
973
+ // (existsSync also returns false there) reached via a real content
974
+ // comparison instead.
975
+ let stateUpdated = false;
909
976
  if (node_fs_1.default.existsSync(statePath)) {
910
977
  withStateLock(statePath, () => {
911
978
  const originalStateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '';
@@ -976,6 +1043,24 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
976
1043
  divergedFields,
977
1044
  });
978
1045
  (0, shell_command_projection_cjs_1.platformWriteSync)(statePath, finalContent);
1046
+ // #3685 / #3691: compare NORMALIZED bytes, not the pre-normalize
1047
+ // `finalContent` string, against the pre-normalize `originalStateContent`
1048
+ // read above. `platformWriteSync` runs Markdown normalization (blank-line
1049
+ // insertion around headings/fences/lists) before persisting — the
1050
+ // transition core (`transitionCore`'s `## Current Position` section
1051
+ // reset) regenerates that section fresh on every call, including on a
1052
+ // genuine no-op re-run, and its raw un-normalized output differs from
1053
+ // the already-normalized on-disk original even though the write
1054
+ // converges to byte-identical content. Comparing pre-normalize strings
1055
+ // (mirroring cmdPhaseComplete's shape verbatim) was verified live to
1056
+ // report `true` on three consecutive byte-identical writes.
1057
+ // `contentChangedAfterNormalize` runs BOTH sides through the exact same
1058
+ // normalizer `platformWriteSync` used to persist (no extra disk I/O,
1059
+ // and immune by construction to this ordering artifact) — this used to
1060
+ // re-read the file to get the same answer; #3691 hoisted that seam so
1061
+ // this site, `updateRoadmapAfterPhaseRemoval`, and `cmdPhaseComplete`'s
1062
+ // roadmap/state/requirements flags all agree by construction.
1063
+ stateUpdated = (0, shell_command_projection_cjs_1.contentChangedAfterNormalize)(statePath, originalStateContent, finalContent);
979
1064
  for (const field of divergedFields) {
980
1065
  preservationWarnings.push({ field, reason: 'preserved-over-disagreeing-derived' });
981
1066
  }
@@ -1053,11 +1138,27 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
1053
1138
  phases_archive_skip_reason: phasesArchiveSkipReason,
1054
1139
  quick: !!quickArchiveResult && quickArchiveResult.archived > 0,
1055
1140
  },
1056
- milestones_updated: true,
1057
- state_updated: node_fs_1.default.existsSync(statePath),
1141
+ // #3685: mirror requirementsUpdated's diff-tracking contract — both flags
1142
+ // now report a real before/after content diff instead of the previous
1143
+ // hardcoded `true` (milestones_updated) / bare fs.existsSync (state_updated).
1144
+ milestones_updated: milestonesUpdated,
1145
+ state_updated: stateUpdated,
1058
1146
  preservation_warnings: preservationWarnings,
1059
1147
  };
1060
1148
  output(result, raw);
1149
+ // #3227 (design doc §40 row 26 / "Not-corruption" rule): a refreshed
1150
+ // state.json `updated_at` must always mean something on disk actually
1151
+ // moved. This site is unconditional because every reachable path either
1152
+ // exits via `error()` (process.exit — refusals like a truncated milestone
1153
+ // window, an unstarted phase, or an invalid version never reach here) or
1154
+ // returns early on `--dry-run` (before any mutation, see the `dry_run:
1155
+ // true` branch above) — the only way execution reaches this line is after
1156
+ // the unconditional MILESTONES.md `platformWriteSync` a few lines above,
1157
+ // which always runs (new file, empty file, or append) once the run is
1158
+ // committed to mutating. Best-effort — cannot throw, cannot change this
1159
+ // command's exit code or output. publishStateContract resolves the
1160
+ // workstream planning root itself via planningPaths.
1161
+ publishStateContract(cwd);
1061
1162
  }
1062
1163
  function cmdPhasesClear(cwd, raw, args) {
1063
1164
  const phasesDir = planningPaths(cwd).phases;
@@ -1100,7 +1201,11 @@ function cmdPhasesClear(cwd, raw, args) {
1100
1201
  // divergence meant a `0-*` directory `roadmap analyze` preserves as a
1101
1202
  // sentinel was DELETED here. Routed through the canonical predicate so
1102
1203
  // every reader of "is this a sentinel phase" agrees by construction.
1103
- const dirs = entries.filter((e) => e.isDirectory() && !isSentinelPhaseId(e.name));
1204
+ // #3639: the DIR-AWARE recognizer — the convention-less id predicate
1205
+ // never saw bracket sentinel dirs (GSD.999-07-icebox), so they were
1206
+ // counted for deletion here while the disk guards (post-#3639) preserve
1207
+ // them; the destructive path must not be the one blind reader left.
1208
+ const dirs = entries.filter((e) => e.isDirectory() && !isSentinelPhaseDir(e.name));
1104
1209
  if (dirs.length > 0 && !confirm) {
1105
1210
  error(`phases clear would delete ${dirs.length} phase director${dirs.length === 1 ? 'y' : 'ies'}. ` +
1106
1211
  `Pass --confirm to proceed.`);