@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
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gen-hooks-cli-exit.cjs — generates hooks/lib/cli-exit.js from a fresh
4
+ * compile of src/cli-exit.cts.
5
+ *
6
+ * ADR-3889 Phase 7 (#3911): shipped hooks must be able to terminate through
7
+ * `terminateNow` WITHOUT depending on any build artifact. hooks/ runs
8
+ * straight from a raw, unbuilt clone (a hook is required by name via
9
+ * `require('./lib/cli-exit.js')` relative to the hook's own __dirname), so
10
+ * the generated file is compiled to a THROWAWAY outDir rather than read from
11
+ * gsd-core/bin/lib/ — reading the tracked build output would let a stale
12
+ * build produce a false green. This mirrors scripts/gen-scripts-cli-exit.cjs
13
+ * exactly (same compile-to-temp strategy, same banner/check/write shape);
14
+ * kept as a SIBLING script rather than folded into that one because
15
+ * gen-scripts-cli-exit.cjs is hard-coded to a single output path/extension
16
+ * (`scripts/lib/cli-exit.cjs`) throughout its BANNER prose and REASON
17
+ * messaging — parameterizing it for a second, differently-extensioned
18
+ * target (`.js`, not `.cjs`, to match the hooks/lib/*.js convention) would
19
+ * tangle a script that is otherwise simple and single-purpose.
20
+ *
21
+ * Usage:
22
+ * node scripts/gen-hooks-cli-exit.cjs # same as --write
23
+ * node scripts/gen-hooks-cli-exit.cjs --write # write hooks/lib/cli-exit.js
24
+ * node scripts/gen-hooks-cli-exit.cjs --check # exit 1 if committed file is stale
25
+ * node scripts/gen-hooks-cli-exit.cjs --out <path> # override the output path (default: hooks/lib/cli-exit.js) — honoured by BOTH --write and --check
26
+ *
27
+ * `--out` restores parity with the sibling scripts/gen-exit-code-registry.cjs,
28
+ * which already supports `--out`/`--scripts-out`/`--hooks-out`/`--dts-out`/
29
+ * `--sh-out` overrides honoured by its own `--check`. This script hardcoding
30
+ * `OUTPUT_PATH` with no override was an inconsistency between two sibling
31
+ * generators, not an intentionally narrower surface — tests need to redirect
32
+ * `--check` at a disposable tmpdir copy instead of corrupting the real
33
+ * committed artifact in place.
34
+ */
35
+
36
+ 'use strict';
37
+
38
+ const { execFileSync } = require('node:child_process');
39
+ const fs = require('node:fs');
40
+ const os = require('node:os');
41
+ const path = require('node:path');
42
+
43
+ const REPO_ROOT = path.resolve(__dirname, '..');
44
+ const OUTPUT_PATH = path.join(REPO_ROOT, 'hooks', 'lib', 'cli-exit.js');
45
+ const COMPILE_TIMEOUT_MS = 60_000;
46
+
47
+ /** Frozen reason codes so tests assert on structure, not prose. */
48
+ const REASON = Object.freeze({
49
+ OK: 'ok_generated_sync',
50
+ DRIFTED: 'fail_generated_drifted',
51
+ BUILD_FAILED: 'fail_build_failed',
52
+ MISSING_EMIT: 'fail_missing_emit',
53
+ USAGE: 'fail_usage',
54
+ });
55
+
56
+ const USAGE_MESSAGE = [
57
+ 'Usage: node scripts/gen-hooks-cli-exit.cjs [--write|--check] [--out <path>]',
58
+ ' (no flag) same as --write',
59
+ ' --write write hooks/lib/cli-exit.js',
60
+ ' --check exit 1 if the committed file is stale',
61
+ ' --out override the output artifact path (default: hooks/lib/cli-exit.js)',
62
+ ].join('\n');
63
+
64
+ const BANNER = [
65
+ '// GENERATED FILE — DO NOT EDIT BY HAND.',
66
+ '// Source of truth: src/cli-exit.cts. Regenerate with:',
67
+ '// node scripts/gen-hooks-cli-exit.cjs --write',
68
+ '// Byte-compared by `npm run lint:generated-sync` (#3911, ADR-3889 Phase 7).',
69
+ '//',
70
+ '// Why this copy exists: hooks/ runs straight from a raw, unbuilt clone — a',
71
+ '// shipped hook must be able to `require(\'./lib/cli-exit.js\')` relative to',
72
+ '// its own __dirname and terminate through `terminateNow` without depending',
73
+ '// on any build artifact. gsd-core/bin/lib/cli-exit.cjs is gitignored tsc',
74
+ '// output and doubles as the build sentinel, so it cannot be required from',
75
+ '// here. `.js`, not `.cjs`, to match the hooks/lib/*.js convention. Hence one',
76
+ '// source, three emitted locations (gsd-core/bin/lib, scripts/lib, hooks/lib).',
77
+ '',
78
+ '',
79
+ ].join('\n');
80
+
81
+ /** Compile the whole project to a throwaway outDir so the work tree is untouched. */
82
+ function compileToTemp() {
83
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-hooks-cli-exit-'));
84
+ try {
85
+ execFileSync(
86
+ process.execPath,
87
+ [
88
+ path.join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'),
89
+ '-p', path.join(REPO_ROOT, 'tsconfig.build.json'),
90
+ '--outDir', tmp,
91
+ // A throwaway outDir must not reuse the in-tree incremental state, or
92
+ // tsc skips emit for files it believes are already current.
93
+ '--incremental', 'false',
94
+ '--tsBuildInfoFile', 'null',
95
+ ],
96
+ { cwd: REPO_ROOT, encoding: 'utf8', stdio: 'pipe', timeout: COMPILE_TIMEOUT_MS },
97
+ );
98
+ return { ok: true, dir: tmp };
99
+ } catch (err) {
100
+ fs.rmSync(tmp, { recursive: true, force: true });
101
+ const detail = [err.stdout, err.stderr].filter(Boolean).join('\n').trim();
102
+ return { ok: false, detail };
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Compile src/cli-exit.cts to a throwaway outDir and return the expected
108
+ * generated content (banner + compiled bytes, with the sibling registry
109
+ * require rewritten to the `.js` extension hooks/lib/ uses), or a failure
110
+ * descriptor.
111
+ *
112
+ * @returns {{ ok: true, content: string } | { ok: false, reason: string, detail?: string }}
113
+ */
114
+ function buildExpectedContent() {
115
+ const build = compileToTemp();
116
+ if (!build.ok) {
117
+ return { ok: false, reason: REASON.BUILD_FAILED, detail: build.detail };
118
+ }
119
+ try {
120
+ const emitted = path.join(build.dir, 'cli-exit.cjs');
121
+ if (!fs.existsSync(emitted)) {
122
+ return { ok: false, reason: REASON.MISSING_EMIT, detail: `no emit produced at ${emitted} from src/cli-exit.cts` };
123
+ }
124
+ const compiled = fs.readFileSync(emitted, 'utf8');
125
+ // hooks/lib/ ships `.js`, not `.cjs` (matching the hooks/lib/*.js
126
+ // convention — see the module header), so the sibling registry require
127
+ // tsc emits for src/cli-exit.cts's `require('./exit-code-registry.cjs')`
128
+ // must be rewritten to resolve the `.js` copy scripts/gen-exit-code-
129
+ // registry.cjs emits alongside this file, not the `.cjs` one. A literal,
130
+ // anchored replace (never a regex) so this can only ever touch the exact
131
+ // string tsc is known to emit for that one import.
132
+ const REGISTRY_REQUIRE_CJS = 'require("./exit-code-registry.cjs")';
133
+ const REGISTRY_REQUIRE_JS = 'require("./exit-code-registry.js")';
134
+ if (!compiled.includes(REGISTRY_REQUIRE_CJS)) {
135
+ return {
136
+ ok: false,
137
+ reason: REASON.MISSING_EMIT,
138
+ detail: `expected compiled output to contain ${REGISTRY_REQUIRE_CJS} (the sibling registry require) — tsc's emit shape may have changed`,
139
+ };
140
+ }
141
+ const rewritten = compiled.split(REGISTRY_REQUIRE_CJS).join(REGISTRY_REQUIRE_JS);
142
+ return { ok: true, content: BANNER + rewritten };
143
+ } finally {
144
+ fs.rmSync(build.dir, { recursive: true, force: true });
145
+ }
146
+ }
147
+
148
+ function doWrite(outPath) {
149
+ const result = buildExpectedContent();
150
+ if (!result.ok) {
151
+ console.error(`FAIL gen-hooks-cli-exit: ${result.reason}`);
152
+ if (result.detail) console.error(result.detail);
153
+ return 1;
154
+ }
155
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
156
+ fs.writeFileSync(outPath, result.content, 'utf8');
157
+ console.log(`ok gen-hooks-cli-exit: wrote ${path.relative(REPO_ROOT, outPath)}`);
158
+ return 0;
159
+ }
160
+
161
+ function doCheck(outPath) {
162
+ const result = buildExpectedContent();
163
+ if (!result.ok) {
164
+ console.error(`FAIL gen-hooks-cli-exit: ${result.reason}`);
165
+ if (result.detail) console.error(result.detail);
166
+ return 1;
167
+ }
168
+
169
+ if (!fs.existsSync(outPath)) {
170
+ console.error(`FAIL gen-hooks-cli-exit: ${REASON.MISSING_EMIT}`);
171
+ console.error(` ${path.relative(REPO_ROOT, outPath)} does not exist. Run:`);
172
+ console.error(' node scripts/gen-hooks-cli-exit.cjs --write');
173
+ return 1;
174
+ }
175
+
176
+ const committed = fs.readFileSync(outPath, 'utf8');
177
+ if (committed !== result.content) {
178
+ console.error(`FAIL gen-hooks-cli-exit: ${REASON.DRIFTED}`);
179
+ console.error(
180
+ ` ${path.relative(REPO_ROOT, outPath)} (${committed.length} bytes) != ` +
181
+ `compile of src/cli-exit.cts (${result.content.length} bytes)`,
182
+ );
183
+ console.error('');
184
+ console.error('Regenerate with:');
185
+ console.error(' node scripts/gen-hooks-cli-exit.cjs --write');
186
+ return 1;
187
+ }
188
+
189
+ console.log(`ok gen-hooks-cli-exit: ${path.relative(REPO_ROOT, outPath)} matches src/cli-exit.cts`);
190
+ return 0;
191
+ }
192
+
193
+ /**
194
+ * @returns {{mode:'write'|'check', outPath:?string}}
195
+ */
196
+ function parseArgs(argv) {
197
+ let mode = null;
198
+ let outPath = null;
199
+
200
+ for (let i = 0; i < argv.length; i++) {
201
+ const arg = argv[i];
202
+ if (arg === '--write' || arg === '--check') {
203
+ if (mode !== null) {
204
+ throw new Error(`conflicting mode flags: --${mode} and ${arg}`);
205
+ }
206
+ mode = arg === '--write' ? 'write' : 'check';
207
+ } else if (arg === '--out') {
208
+ const value = argv[++i];
209
+ if (value === undefined) throw new Error('--out requires a value');
210
+ outPath = value;
211
+ } else if (arg.startsWith('--out=')) {
212
+ outPath = arg.slice('--out='.length);
213
+ } else {
214
+ throw new Error(`unrecognized argument: ${arg}`);
215
+ }
216
+ }
217
+
218
+ return { mode: mode || 'write', outPath };
219
+ }
220
+
221
+ function main() {
222
+ let args;
223
+ try {
224
+ args = parseArgs(process.argv.slice(2));
225
+ } catch (err) {
226
+ console.error(`FAIL gen-hooks-cli-exit: ${REASON.USAGE}`);
227
+ console.error(` ${err.message}`);
228
+ console.error(USAGE_MESSAGE);
229
+ return 1;
230
+ }
231
+
232
+ const outPath = args.outPath || OUTPUT_PATH;
233
+
234
+ return args.mode === 'check' ? doCheck(outPath) : doWrite(outPath);
235
+ }
236
+
237
+ if (require.main === module) process.exitCode = main();
238
+
239
+ module.exports = { REASON, buildExpectedContent, OUTPUT_PATH, BANNER };
@@ -21,7 +21,7 @@ const ROOT = path.resolve(__dirname, '..');
21
21
  // buildParityManifest) is the canonical single source of truth in
22
22
  // tests/helpers/install-shared.cjs (issue #2266/#2267) — this generator does
23
23
  // not keep its own inline copy of the walk/exclusion logic.
24
- const { runMinimalInstall, RUNTIME_META, buildInstallTree, BUILD_SCRIPT } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
24
+ const { runMinimalInstall, RUNTIME_META, buildInstallTree, extraEmitRootsFor, BUILD_SCRIPT } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
25
25
  const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'install-tree');
26
26
 
27
27
  function cleanup(root) {
@@ -51,7 +51,7 @@ for (const runtime of targets) {
51
51
  const { configDir, root } = runMinimalInstall({ runtime, scope: 'global' });
52
52
  let actual;
53
53
  try {
54
- actual = buildInstallTree(configDir, root);
54
+ actual = buildInstallTree(configDir, root, extraEmitRootsFor(runtime, 'global', root));
55
55
  } finally {
56
56
  cleanup(root);
57
57
  }
@@ -30,7 +30,13 @@ const CONTRACT_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'loop-host-contr
30
30
  // The five step workflows in pipeline order
31
31
  const STEP_WORKFLOWS = [
32
32
  { file: 'discuss-phase.md', step: 'discuss' },
33
- { file: 'plan-phase.md', step: 'plan' },
33
+ {
34
+ file: 'plan-phase.md',
35
+ step: 'plan',
36
+ auxiliaryHosts: [
37
+ { file: 'quick.md', point: 'plan:pre', kinds: ['contribution'], into: 'planner' },
38
+ ],
39
+ },
34
40
  { file: 'execute-phase.md', step: 'execute' },
35
41
  { file: 'verify-work.md', step: 'verify' },
36
42
  { file: 'ship.md', step: 'ship' },
@@ -295,7 +301,7 @@ function buildContract(workflowsDir) {
295
301
  const contract = [];
296
302
  const allErrors = [];
297
303
 
298
- for (const { file, step } of STEP_WORKFLOWS) {
304
+ for (const { file, step, auxiliaryHosts = [] } of STEP_WORKFLOWS) {
299
305
  const filePath = path.join(resolvedDir, file);
300
306
  let content;
301
307
  try {
@@ -325,6 +331,34 @@ function buildContract(workflowsDir) {
325
331
  const roleErrors = crossCheckRoles(content, entry.agentRoles, file);
326
332
  allErrors.push(...roleErrors);
327
333
 
334
+ for (const auxiliary of auxiliaryHosts) {
335
+ let auxiliaryContent;
336
+ try {
337
+ auxiliaryContent = fs.readFileSync(path.join(resolvedDir, auxiliary.file), 'utf8');
338
+ } catch (err) {
339
+ allErrors.push('Could not read auxiliary host ' + auxiliary.file + ': ' + String(err.message));
340
+ continue;
341
+ }
342
+
343
+ const wiredPoints = scanWiredPoints(auxiliaryContent);
344
+ if (!wiredPoints.has(auxiliary.point)) {
345
+ allErrors.push(
346
+ auxiliary.file + ': auxiliary host missing expected point "' + auxiliary.point + '"',
347
+ );
348
+ continue;
349
+ }
350
+
351
+ const wiredKinds = scanWiredKinds(auxiliaryContent, auxiliary.into).get(auxiliary.point) || new Set();
352
+ for (const kind of auxiliary.kinds) {
353
+ if (!wiredKinds.has(kind)) {
354
+ allErrors.push(
355
+ auxiliary.file + ': auxiliary host point "' + auxiliary.point +
356
+ '" missing expected kind "' + kind + '"',
357
+ );
358
+ }
359
+ }
360
+ }
361
+
328
362
  contract.push(entry);
329
363
  }
330
364
 
@@ -437,7 +471,10 @@ function main() {
437
471
  * registry generator, conformance gate) must derive from this rather than maintaining
438
472
  * a separate hardcoded list.
439
473
  */
440
- const HOST_LOOP_FILES = STEP_WORKFLOWS.map((w) => 'gsd-core/workflows/' + w.file);
474
+ const HOST_LOOP_FILES = STEP_WORKFLOWS.flatMap(({ file, auxiliaryHosts = [] }) => [
475
+ 'gsd-core/workflows/' + file,
476
+ ...auxiliaryHosts.map((host) => 'gsd-core/workflows/' + host.file),
477
+ ]);
441
478
 
442
479
  /**
443
480
  * Pure function: scan a text string for `loop render-hooks <point>` call sites.
@@ -446,8 +483,11 @@ const HOST_LOOP_FILES = STEP_WORKFLOWS.map((w) => 'gsd-core/workflows/' + w.file
446
483
  * @param {string} text Content of a workflow file (or any text).
447
484
  * @returns {Set<string>}
448
485
  */
486
+ /** The call-site shape both scanners key on — one regex, two consumers (#3606). */
487
+ const CALL_SITE_RE = /loop render-hooks\s+([a-z:]+)/g;
488
+
449
489
  function scanWiredPoints(text) {
450
- const re = /loop render-hooks\s+([a-z:]+)/g;
490
+ const re = CALL_SITE_RE;
451
491
  const result = new Set();
452
492
  let m;
453
493
  while ((m = re.exec(text)) !== null) {
@@ -456,6 +496,147 @@ function scanWiredPoints(text) {
456
496
  return result;
457
497
  }
458
498
 
499
+ // ─── Hook-kind coverage (#3606) ──────────────────────────────────────────────
500
+
501
+ const HOOK_KINDS = ['contribution', 'step', 'gate'];
502
+
503
+ /**
504
+ * The kinds one call site's dispatch text actually covers (#3606).
505
+ *
506
+ * A point having a `loop render-hooks <point>` call site proves the hooks are
507
+ * RENDERED, not that they are DISPATCHED — a consumer that iterates only
508
+ * `kind == "gate"` (or narrows `kind == "step"` to one `ref.skill`) silently
509
+ * drops every other registered kind. Coverage rules for the text following a
510
+ * call site, up to the next call site or the region cap, judged LINE by line:
511
+ *
512
+ * - A deferral line (one carrying an `@`-included path to the generic
513
+ * contract, e.g. `@gsd-core/references/loop-hook-dispatch.md`) that names a
514
+ * kind (`kind == "step"`) covers that kind; a deferral line with no kind
515
+ * discriminator ("apply each entry") covers every kind only when no role
516
+ * target is required. With `expectedInto`, the same segment must explicitly
517
+ * name both the kind and that exact `into` target. A bare §-citation of the
518
+ * reference (validation guidance only, no `@`) covers nothing — plan-phase
519
+ * cites the gate-validation section while dispatching only gates.
520
+ * - Otherwise a kind is covered when some LINE dispatches it unconditionally:
521
+ * a `kind == "<kind>"` discriminator with NO same-line narrowing to one
522
+ * hook (`ref.skill ==`, `ref.agent ==`, `ref.command ==`). A narrowed line
523
+ * special-cases ONE hook and proves nothing about the kind generally — the
524
+ * exact hand-rolled-consumer shape the reference warns about.
525
+ *
526
+ * Quote style and spacing vary across the corpus (`kind == "step"`,
527
+ * `kind === 'gate'`), so the matcher is tolerant of both quote characters and
528
+ * of `==`/`===`.
529
+ *
530
+ * Pure: same input, same output; CRLF-safe (line splitting tolerates \r).
531
+ *
532
+ * @param {string} region Dispatch text following one call site.
533
+ * @param {string} [expectedInto] Optional role target required in the same segment.
534
+ * @returns {Set<string>}
535
+ */
536
+ function coveredKindsInRegion(region, expectedInto) {
537
+ const covered = new Set();
538
+ // Same-SEGMENT narrowing to ONE hook voids credit: `ref.skill ==`, `capId ==`,
539
+ // and `into ==` each special-case a subset, not the kind generally. An
540
+ // auxiliary host may name the one role it actually hosts via `expectedInto`;
541
+ // any other role target still voids credit.
542
+ // (plan-phase's `kind == "contribution" and capId == "security"` is the
543
+ // hand-rolled shape; `into == "planner"` covers only planner-targeted
544
+ // contributions). Segments, not lines: execute-phase legitimately writes
545
+ // "dispatch `kind == "step"` hooks per … . `ref.skill == "code-review"`:" —
546
+ // the deferral is one sentence, the specialization the next; narrowing in a
547
+ // DIFFERENT segment must not void the deferral's credit.
548
+ const identityNarrowingRe = /(?:ref\.(?:skill|agent|command)|capId)\s*={2,3}/;
549
+ const intoNarrowingRe = /into\s*={2,3}/;
550
+ const expectedIntoRe = expectedInto
551
+ ? new RegExp(`into\\s*={2,3}\\s*["']${escapeRegExp(expectedInto)}["']`)
552
+ : null;
553
+ // Negated mentions describe an absence, not a dispatch ("Branch 1 — no active
554
+ // step hooks (`activeHooks` has no entry with `kind == "step"`)" — ship.md).
555
+ const negationRe = /\b(?:no|without|absent|lacks?|missing)\b[^.|]*kind\s*={2,3}/;
556
+ const deferralRe = /@\S*loop-hook-dispatch\.md/;
557
+ for (const line of region.split(/\r?\n/)) {
558
+ // Sentence segments: a `.`/`;` followed by whitespace ends a segment. A
559
+ // period NOT followed by whitespace (the `.md` inside a deferral path,
560
+ // `ref.skill`) is not a boundary.
561
+ for (const segment of line.split(/(?<=[.;])\s+/)) {
562
+ const kindDiscriminators = [];
563
+ for (const kind of HOOK_KINDS) {
564
+ if (new RegExp(`kind\\s*={2,3}\\s*["']${kind}["']`).test(segment)) kindDiscriminators.push(kind);
565
+ }
566
+ if (kindDiscriminators.length === 0) {
567
+ // A deferral with no kind discriminator ("apply each entry per …")
568
+ // still covers every kind for generic hosts. An auxiliary host with a
569
+ // required role target must state both its kind and target explicitly.
570
+ if (!expectedInto && deferralRe.test(segment)) for (const kind of HOOK_KINDS) covered.add(kind);
571
+ continue;
572
+ }
573
+ if (negationRe.test(segment)) continue;
574
+ const narrowed = identityNarrowingRe.test(segment) ||
575
+ (expectedInto
576
+ ? !expectedIntoRe.test(segment)
577
+ : intoNarrowingRe.test(segment));
578
+ if (deferralRe.test(segment)) {
579
+ // Deferral naming kinds ("dispatch `kind == "step"` hooks per …").
580
+ if (!narrowed) for (const kind of kindDiscriminators) covered.add(kind);
581
+ continue;
582
+ }
583
+ if (!narrowed) for (const kind of kindDiscriminators) covered.add(kind);
584
+ }
585
+ }
586
+ return covered;
587
+ }
588
+
589
+ /**
590
+ * Scan every `loop render-hooks <point>` call site in `text` and accumulate,
591
+ * per point, the union of hook kinds its dispatch regions cover (#3606).
592
+ *
593
+ * @param {string} text Content of a workflow file (or any text).
594
+ * @param {string} [expectedInto] Optional role target an auxiliary host must dispatch.
595
+ * @returns {Map<string, Set<string>>} point → covered kinds.
596
+ */
597
+ function scanWiredKinds(text, expectedInto) {
598
+ const result = new Map();
599
+ const siteRe = CALL_SITE_RE;
600
+ const sites = [];
601
+ let m;
602
+ while ((m = siteRe.exec(text)) !== null) sites.push({ point: m[1], start: m.index });
603
+ const REGION_CAP = 6000;
604
+ for (let i = 0; i < sites.length; i++) {
605
+ const regionEnd = i + 1 < sites.length ? sites[i + 1].start : Math.min(text.length, sites[i].start + REGION_CAP);
606
+ const region = text.slice(sites[i].start, regionEnd);
607
+ const covered = coveredKindsInRegion(region, expectedInto);
608
+ if (!result.has(sites[i].point)) result.set(sites[i].point, new Set());
609
+ for (const kind of covered) result.get(sites[i].point).add(kind);
610
+ }
611
+ return result;
612
+ }
613
+
614
+ /**
615
+ * Read every host-loop workflow file and return, per point, the union of hook
616
+ * kinds its call sites' dispatch text covers (#3606).
617
+ *
618
+ * @param {string} [repoRoot] Path to the repository root. Defaults to ROOT.
619
+ * @returns {Map<string, Set<string>>}
620
+ */
621
+ function getWiredKinds(repoRoot) {
622
+ const resolvedRoot = repoRoot !== undefined ? repoRoot : ROOT;
623
+ const result = new Map();
624
+ for (const relPath of HOST_LOOP_FILES) {
625
+ const absPath = path.join(resolvedRoot, relPath);
626
+ let content;
627
+ try {
628
+ content = fs.readFileSync(absPath, 'utf8');
629
+ } catch (err) {
630
+ throw new Error('getWiredKinds: cannot read host-loop file ' + absPath + ': ' + err.message);
631
+ }
632
+ for (const [point, kinds] of scanWiredKinds(content)) {
633
+ if (!result.has(point)) result.set(point, new Set());
634
+ for (const kind of kinds) result.get(point).add(kind);
635
+ }
636
+ }
637
+ return result;
638
+ }
639
+
459
640
  /**
460
641
  * Read every host-loop workflow file and return the union of all wired loop points
461
642
  * (i.e. points that have a `loop render-hooks <point>` call site).
@@ -497,6 +678,10 @@ module.exports = {
497
678
  ROLE_TO_AGENT,
498
679
  scanWiredPoints,
499
680
  getWiredLoopPoints,
681
+ coveredKindsInRegion,
682
+ scanWiredKinds,
683
+ getWiredKinds,
684
+ HOOK_KINDS,
500
685
  };
501
686
 
502
687
  // ─── CLI entry point ──────────────────────────────────────────────────────────
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gen-scripts-cli-exit.cjs — generates scripts/lib/cli-exit.cjs from a fresh
4
+ * compile of src/cli-exit.cts.
5
+ *
6
+ * ADR-3889 Phase 0 (#3904): scripts/lib/cli-exit.cjs used to be a hand-written
7
+ * fork of gsd-core/bin/lib/cli-exit.cjs (the compiled artifact of
8
+ * src/cli-exit.cts). The two drifted — only the .cts copy routed a
9
+ * non-ExitError throw through getJsonErrorMode() to emit a structured
10
+ * { ok:false, reason, message } envelope. This script makes scripts/lib/cli-exit.cjs
11
+ * a generated artifact of the SAME source, so the two surfaces cannot diverge
12
+ * again.
13
+ *
14
+ * scripts/ runs straight from the repo checkout and must work on an unbuilt
15
+ * clone (scripts/check-env.cjs requires this file before any build runs), so
16
+ * the generated file is compiled to a THROWAWAY outDir rather than read from
17
+ * gsd-core/bin/lib/ — reading the tracked build output would let a stale build
18
+ * produce a false green.
19
+ *
20
+ * Usage:
21
+ * node scripts/gen-scripts-cli-exit.cjs # same as --write
22
+ * node scripts/gen-scripts-cli-exit.cjs --write # write scripts/lib/cli-exit.cjs
23
+ * node scripts/gen-scripts-cli-exit.cjs --check # exit 1 if committed file is stale
24
+ */
25
+
26
+ 'use strict';
27
+
28
+ const { execFileSync } = require('node:child_process');
29
+ const fs = require('node:fs');
30
+ const os = require('node:os');
31
+ const path = require('node:path');
32
+
33
+ const REPO_ROOT = path.resolve(__dirname, '..');
34
+ const OUTPUT_PATH = path.join(REPO_ROOT, 'scripts', 'lib', 'cli-exit.cjs');
35
+ const COMPILE_TIMEOUT_MS = 60_000;
36
+
37
+ /** Frozen reason codes so tests assert on structure, not prose. */
38
+ const REASON = Object.freeze({
39
+ OK: 'ok_generated_sync',
40
+ DRIFTED: 'fail_generated_drifted',
41
+ BUILD_FAILED: 'fail_build_failed',
42
+ MISSING_EMIT: 'fail_missing_emit',
43
+ USAGE: 'fail_usage',
44
+ });
45
+
46
+ const USAGE_MESSAGE = [
47
+ 'Usage: node scripts/gen-scripts-cli-exit.cjs [--write|--check]',
48
+ ' (no flag) same as --write',
49
+ ' --write write scripts/lib/cli-exit.cjs',
50
+ ' --check exit 1 if the committed file is stale',
51
+ ].join('\n');
52
+
53
+ const BANNER = [
54
+ '// GENERATED FILE — DO NOT EDIT BY HAND.',
55
+ '// Source of truth: src/cli-exit.cts. Regenerate with:',
56
+ '// node scripts/gen-scripts-cli-exit.cjs --write',
57
+ '// Byte-compared by `npm run lint:generated-sync` (#3904, ADR-3889 Phase 0).',
58
+ '//',
59
+ '// Why this copy exists: scripts/ runs straight from the repo checkout and must',
60
+ '// work on an unbuilt clone — 64+ scripts require this file, including',
61
+ '// check-env.cjs, which runs before any build. gsd-core/bin/lib/cli-exit.cjs is',
62
+ '// gitignored tsc output and doubles as the build sentinel, so it cannot be',
63
+ '// required from here. Hence one source, two emitted locations.',
64
+ '',
65
+ '',
66
+ ].join('\n');
67
+
68
+ /** Compile the whole project to a throwaway outDir so the work tree is untouched. */
69
+ function compileToTemp() {
70
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-scripts-cli-exit-'));
71
+ try {
72
+ execFileSync(
73
+ process.execPath,
74
+ [
75
+ path.join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'),
76
+ '-p', path.join(REPO_ROOT, 'tsconfig.build.json'),
77
+ '--outDir', tmp,
78
+ // A throwaway outDir must not reuse the in-tree incremental state, or
79
+ // tsc skips emit for files it believes are already current.
80
+ '--incremental', 'false',
81
+ '--tsBuildInfoFile', 'null',
82
+ ],
83
+ { cwd: REPO_ROOT, encoding: 'utf8', stdio: 'pipe', timeout: COMPILE_TIMEOUT_MS },
84
+ );
85
+ return { ok: true, dir: tmp };
86
+ } catch (err) {
87
+ fs.rmSync(tmp, { recursive: true, force: true });
88
+ const detail = [err.stdout, err.stderr].filter(Boolean).join('\n').trim();
89
+ return { ok: false, detail };
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Compile src/cli-exit.cts to a throwaway outDir and return the expected
95
+ * generated content (banner + compiled bytes), or a failure descriptor.
96
+ *
97
+ * @returns {{ ok: true, content: string } | { ok: false, reason: string, detail?: string }}
98
+ */
99
+ function buildExpectedContent() {
100
+ const build = compileToTemp();
101
+ if (!build.ok) {
102
+ return { ok: false, reason: REASON.BUILD_FAILED, detail: build.detail };
103
+ }
104
+ try {
105
+ const emitted = path.join(build.dir, 'cli-exit.cjs');
106
+ if (!fs.existsSync(emitted)) {
107
+ return { ok: false, reason: REASON.MISSING_EMIT, detail: `no emit produced at ${emitted} from src/cli-exit.cts` };
108
+ }
109
+ const compiled = fs.readFileSync(emitted, 'utf8');
110
+ return { ok: true, content: BANNER + compiled };
111
+ } finally {
112
+ fs.rmSync(build.dir, { recursive: true, force: true });
113
+ }
114
+ }
115
+
116
+ function doWrite() {
117
+ const result = buildExpectedContent();
118
+ if (!result.ok) {
119
+ console.error(`FAIL gen-scripts-cli-exit: ${result.reason}`);
120
+ if (result.detail) console.error(result.detail);
121
+ return 1;
122
+ }
123
+ fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
124
+ fs.writeFileSync(OUTPUT_PATH, result.content, 'utf8');
125
+ console.log(`ok gen-scripts-cli-exit: wrote ${path.relative(REPO_ROOT, OUTPUT_PATH)}`);
126
+ return 0;
127
+ }
128
+
129
+ function doCheck() {
130
+ const result = buildExpectedContent();
131
+ if (!result.ok) {
132
+ console.error(`FAIL gen-scripts-cli-exit: ${result.reason}`);
133
+ if (result.detail) console.error(result.detail);
134
+ return 1;
135
+ }
136
+
137
+ if (!fs.existsSync(OUTPUT_PATH)) {
138
+ console.error(`FAIL gen-scripts-cli-exit: ${REASON.MISSING_EMIT}`);
139
+ console.error(` ${path.relative(REPO_ROOT, OUTPUT_PATH)} does not exist. Run:`);
140
+ console.error(' node scripts/gen-scripts-cli-exit.cjs --write');
141
+ return 1;
142
+ }
143
+
144
+ const committed = fs.readFileSync(OUTPUT_PATH, 'utf8');
145
+ if (committed !== result.content) {
146
+ console.error(`FAIL gen-scripts-cli-exit: ${REASON.DRIFTED}`);
147
+ console.error(
148
+ ` ${path.relative(REPO_ROOT, OUTPUT_PATH)} (${committed.length} bytes) != ` +
149
+ `compile of src/cli-exit.cts (${result.content.length} bytes)`,
150
+ );
151
+ console.error('');
152
+ console.error('Regenerate with:');
153
+ console.error(' node scripts/gen-scripts-cli-exit.cjs --write');
154
+ return 1;
155
+ }
156
+
157
+ console.log(`ok gen-scripts-cli-exit: ${path.relative(REPO_ROOT, OUTPUT_PATH)} matches src/cli-exit.cts`);
158
+ return 0;
159
+ }
160
+
161
+ function main() {
162
+ const flag = process.argv[2];
163
+ const extra = process.argv[3];
164
+
165
+ if (flag !== undefined && flag !== '--write' && flag !== '--check') {
166
+ console.error(`FAIL gen-scripts-cli-exit: ${REASON.USAGE}`);
167
+ console.error(` unrecognized argument: ${flag}`);
168
+ console.error(USAGE_MESSAGE);
169
+ return 1;
170
+ }
171
+
172
+ if (extra !== undefined) {
173
+ console.error(`FAIL gen-scripts-cli-exit: ${REASON.USAGE}`);
174
+ console.error(` unexpected extra argument: ${extra}`);
175
+ console.error(USAGE_MESSAGE);
176
+ return 1;
177
+ }
178
+
179
+ if (flag === '--check') return doCheck();
180
+ return doWrite();
181
+ }
182
+
183
+ if (require.main === module) process.exitCode = main();
184
+
185
+ module.exports = { REASON, buildExpectedContent, OUTPUT_PATH, BANNER };