@opengsd/gsd-core 1.12.0 → 1.14.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 (455) 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-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /**
3
+ * Quick-Batch command router — CLI subcommand dispatcher for
4
+ * `gsd-tools quick-batch` (#3676, Phase 4 of epic #3344 / ADR-1239).
5
+ *
6
+ * Quick-batch is a first-party, always-on command family (like `/gsd:quick`,
7
+ * which has no capability-registry entry), so this router is wired directly
8
+ * into `HOST_COMMAND_ROUTERS` (`gsd-core/bin/gsd-tools.cjs`) — NOT the opt-in
9
+ * capability-registry/`activationKey` path `graphify` uses. Shape follows
10
+ * `graphify-command-router.cts` (thin `routeHubCommandFamily` wrapper), which
11
+ * gives the Hub's `makeUnknownCommand` handling for free on an unknown
12
+ * subcommand (test-matrix row 47).
13
+ *
14
+ * Verbs wrap `src/quick-batch.cts` (durable manifest read/write — reused
15
+ * as-is) and `src/quick-batch-dispatch.cts` (pure decision logic, #3676's
16
+ * own new module). This router performs NO decision logic of its own beyond
17
+ * argument shaping — every behavioral rule lives in one of those two
18
+ * modules, per the design doc's "Do the simplest thing" law.
19
+ *
20
+ * Test seam: pass `_quickBatch`/`_quickBatchDispatch` in the options object
21
+ * to inject recording mocks instead of the real modules — same `_`-prefix
22
+ * convention `graphify-command-router.cts` uses.
23
+ *
24
+ * ADR-457 build-at-publish: compiled by tsc to
25
+ * gsd-core/bin/lib/quick-batch-command-router.cjs.
26
+ */
27
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
28
+ const quickBatch = require("./quick-batch.cjs");
29
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
30
+ const quickBatchDispatch = require("./quick-batch-dispatch.cjs");
31
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
32
+ const io = require("./io.cjs");
33
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
34
+ const commandRoutingHub = require("./command-routing-hub.cjs");
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
36
+ const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
37
+ const security_cjs_1 = require("./security.cjs");
38
+ const { output, ERROR_REASON } = io;
39
+ const { makeInvalidArgs } = commandRoutingHub;
40
+ const { routeHubCommandFamily } = cjsCommandRouterAdapter;
41
+ // ─── Small arg-parsing helpers (local — no new shared convention needed) ────
42
+ /** `--flag value` lookup; undefined when the flag is absent. */
43
+ function argValue(args, flag) {
44
+ const idx = args.indexOf(flag);
45
+ if (idx === -1)
46
+ return undefined;
47
+ return args[idx + 1];
48
+ }
49
+ function parseJsonArg(raw, label) {
50
+ if (raw === undefined) {
51
+ return { ok: false, reason: `${label} requires a JSON value` };
52
+ }
53
+ const parsed = (0, security_cjs_1.safeJsonParse)(raw, { maxLength: 1048576, label });
54
+ if (!parsed.ok) {
55
+ return { ok: false, reason: `${label} is not valid JSON: ${parsed.error ?? 'unknown parse error'}` };
56
+ }
57
+ return { ok: true, value: parsed.value };
58
+ }
59
+ // ─── Implementation ───────────────────────────────────────────────────────────
60
+ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatchDispatch }) {
61
+ const qb = _quickBatch ?? quickBatch;
62
+ const dispatch = _quickBatchDispatch ?? quickBatchDispatch;
63
+ /** Forward a `Result<T>` from either module straight to output()/error(). */
64
+ function emit(result) {
65
+ if (result && typeof result === 'object' && 'ok' in result) {
66
+ const r = result;
67
+ if (!r.ok) {
68
+ error(r.reason ?? 'quick-batch command failed', ERROR_REASON.USAGE);
69
+ return;
70
+ }
71
+ output(r.value, raw);
72
+ return;
73
+ }
74
+ output(result, raw);
75
+ }
76
+ routeHubCommandFamily({
77
+ family: 'quick-batch',
78
+ args,
79
+ subcommands: [
80
+ 'create',
81
+ 'update',
82
+ 'resume',
83
+ 'complete',
84
+ 'effective-concurrency',
85
+ 'merge-eligible',
86
+ 'spawn-plan',
87
+ 'filter-executed',
88
+ 'verification-routing',
89
+ 'merge-routing',
90
+ 'cleanup-entry',
91
+ 'parse-args',
92
+ ],
93
+ handlers: {
94
+ // `quick-batch create --file <path> [--base-revision <sha>] [--options <json>]`
95
+ create: () => {
96
+ const filePath = argValue(args, '--file');
97
+ if (!filePath) {
98
+ return makeInvalidArgs('--file', 'Usage: gsd-tools quick-batch create --file <path> [--base-revision <sha>] [--options <json>]', ERROR_REASON.USAGE);
99
+ }
100
+ const parsed = qb.parseTaskListFromFile(cwd, filePath);
101
+ if (!parsed.ok) {
102
+ return makeInvalidArgs('--file', parsed.reason ?? 'unable to parse task list', ERROR_REASON.USAGE);
103
+ }
104
+ const baseRevision = argValue(args, '--base-revision');
105
+ const optionsRaw = argValue(args, '--options');
106
+ let batchOptions;
107
+ if (optionsRaw !== undefined) {
108
+ const optResult = parseJsonArg(optionsRaw, '--options');
109
+ if (!optResult.ok)
110
+ return makeInvalidArgs('--options', optResult.reason, ERROR_REASON.USAGE);
111
+ batchOptions = optResult.value;
112
+ }
113
+ const items = (parsed.value ?? []).map((it) => ({ description: it.description }));
114
+ emit(qb.createBatch(cwd, items, { baseRevision, batchOptions }));
115
+ },
116
+ // `quick-batch update --batch <id> --updates <json>`
117
+ update: () => {
118
+ const batchId = argValue(args, '--batch');
119
+ if (!batchId) {
120
+ return makeInvalidArgs('--batch', 'Usage: gsd-tools quick-batch update --batch <id> --updates <json>', ERROR_REASON.USAGE);
121
+ }
122
+ const updatesResult = parseJsonArg(argValue(args, '--updates'), '--updates');
123
+ if (!updatesResult.ok)
124
+ return makeInvalidArgs('--updates', updatesResult.reason, ERROR_REASON.USAGE);
125
+ emit(qb.updateBatchItems(cwd, batchId, updatesResult.value));
126
+ },
127
+ // `quick-batch resume --batch <id> [--current-base-revision <sha>]`
128
+ resume: () => {
129
+ const batchId = argValue(args, '--batch');
130
+ if (!batchId) {
131
+ return makeInvalidArgs('--batch', 'Usage: gsd-tools quick-batch resume --batch <id> [--current-base-revision <sha>]', ERROR_REASON.USAGE);
132
+ }
133
+ const currentBaseRevision = argValue(args, '--current-base-revision');
134
+ emit(qb.resumeBatch(cwd, batchId, currentBaseRevision !== undefined ? { currentBaseRevision } : {}));
135
+ },
136
+ // `quick-batch complete --batch <id> --quick-id <id> --description <t> --date <d> --commit <sha> [--directory <dir>]`
137
+ complete: () => {
138
+ const batchId = argValue(args, '--batch');
139
+ const quickId = argValue(args, '--quick-id');
140
+ const description = argValue(args, '--description');
141
+ const date = argValue(args, '--date');
142
+ const commit = argValue(args, '--commit');
143
+ if (!batchId || !quickId || !description || !date || !commit) {
144
+ return makeInvalidArgs('--batch/--quick-id/--description/--date/--commit', 'Usage: gsd-tools quick-batch complete --batch <id> --quick-id <id> --description <t> --date <d> --commit <sha> [--directory <dir>]', ERROR_REASON.USAGE);
145
+ }
146
+ const directory = argValue(args, '--directory');
147
+ emit(qb.completeQuickItem(cwd, batchId, quickId, { description, date, commit, directory }));
148
+ },
149
+ // `quick-batch effective-concurrency --jobs <auto|N> --task-count <N> --capacity <N> --isolation <str> [--mutating]`
150
+ 'effective-concurrency': () => {
151
+ const jobsRaw = argValue(args, '--jobs');
152
+ const taskCount = Number(argValue(args, '--task-count'));
153
+ const capacity = Number(argValue(args, '--capacity'));
154
+ const isolation = argValue(args, '--isolation') ?? '';
155
+ if (jobsRaw === undefined || !Number.isFinite(taskCount) || !Number.isFinite(capacity)) {
156
+ return makeInvalidArgs('--jobs/--task-count/--capacity', 'Usage: gsd-tools quick-batch effective-concurrency --jobs <auto|N> --task-count <N> --capacity <N> --isolation <str> [--mutating]', ERROR_REASON.USAGE);
157
+ }
158
+ const jobs = jobsRaw === 'auto' ? 'auto' : Number(jobsRaw);
159
+ const mutating = args.includes('--mutating');
160
+ output({
161
+ concurrency: dispatch.computeEffectiveConcurrency({ jobs, taskCount, capacity, isolation, mutating }),
162
+ }, raw);
163
+ },
164
+ // `quick-batch merge-eligible --wave-order <json-array> --ready <json-array>`
165
+ 'merge-eligible': () => {
166
+ const waveOrderResult = parseJsonArg(argValue(args, '--wave-order'), '--wave-order');
167
+ if (!waveOrderResult.ok)
168
+ return makeInvalidArgs('--wave-order', waveOrderResult.reason, ERROR_REASON.USAGE);
169
+ const readyResult = parseJsonArg(argValue(args, '--ready'), '--ready');
170
+ if (!readyResult.ok)
171
+ return makeInvalidArgs('--ready', readyResult.reason, ERROR_REASON.USAGE);
172
+ output({
173
+ mergeable: dispatch.computeMergeOrder(waveOrderResult.value, new Set(readyResult.value)),
174
+ }, raw);
175
+ },
176
+ // `quick-batch spawn-plan --eligible <json-array> --capacity <N> --in-flight <N> [--refused <json-array>]`
177
+ 'spawn-plan': () => {
178
+ const eligibleResult = parseJsonArg(argValue(args, '--eligible'), '--eligible');
179
+ if (!eligibleResult.ok)
180
+ return makeInvalidArgs('--eligible', eligibleResult.reason, ERROR_REASON.USAGE);
181
+ const capacity = Number(argValue(args, '--capacity'));
182
+ const currentInFlight = Number(argValue(args, '--in-flight'));
183
+ if (!Number.isFinite(capacity) || !Number.isFinite(currentInFlight)) {
184
+ return makeInvalidArgs('--capacity/--in-flight', 'Usage: gsd-tools quick-batch spawn-plan --eligible <json-array> --capacity <N> --in-flight <N> [--refused <json-array>]', ERROR_REASON.USAGE);
185
+ }
186
+ let refused = [];
187
+ const refusedRaw = argValue(args, '--refused');
188
+ if (refusedRaw !== undefined) {
189
+ const refusedResult = parseJsonArg(refusedRaw, '--refused');
190
+ if (!refusedResult.ok)
191
+ return makeInvalidArgs('--refused', refusedResult.reason, ERROR_REASON.USAGE);
192
+ refused = refusedResult.value;
193
+ }
194
+ output(dispatch.computeSpawnPlan({ eligibleIds: eligibleResult.value, capacity, currentInFlight, refused }), raw);
195
+ },
196
+ // `quick-batch filter-executed --eligible <json-array> --executed <json-array>`
197
+ // Crash-window duplicate-dispatch guard (#3677): splits this round's
198
+ // eligible ids into `spawnEligible` (safe to dispatch) and
199
+ // `alreadyExecuted` (SUMMARY.md already exists on disk — caller
200
+ // determines this via its own filesystem check; NEVER re-dispatch
201
+ // these — merge-wave.md's own on-disk criterion picks them up).
202
+ 'filter-executed': () => {
203
+ const eligibleResult = parseJsonArg(argValue(args, '--eligible'), '--eligible');
204
+ if (!eligibleResult.ok)
205
+ return makeInvalidArgs('--eligible', eligibleResult.reason, ERROR_REASON.USAGE);
206
+ const executedResult = parseJsonArg(argValue(args, '--executed'), '--executed');
207
+ if (!executedResult.ok)
208
+ return makeInvalidArgs('--executed', executedResult.reason, ERROR_REASON.USAGE);
209
+ output(dispatch.filterAlreadyExecuted(eligibleResult.value, executedResult.value), raw);
210
+ },
211
+ // `quick-batch verification-routing --status <passed|gaps_found|human_needed>`
212
+ 'verification-routing': () => {
213
+ const status = argValue(args, '--status');
214
+ if (status !== 'passed' && status !== 'gaps_found' && status !== 'human_needed') {
215
+ return makeInvalidArgs('--status', 'Usage: gsd-tools quick-batch verification-routing --status <passed|gaps_found|human_needed>', ERROR_REASON.USAGE);
216
+ }
217
+ output(dispatch.routeVerificationOutcome(status), raw);
218
+ },
219
+ // `quick-batch merge-routing --kind <merged|merge_failed|scope_violation> [--detail <text>]`
220
+ 'merge-routing': () => {
221
+ const kind = argValue(args, '--kind');
222
+ if (kind !== 'merged' && kind !== 'merge_failed' && kind !== 'scope_violation') {
223
+ return makeInvalidArgs('--kind', 'Usage: gsd-tools quick-batch merge-routing --kind <merged|merge_failed|scope_violation> [--detail <text>]', ERROR_REASON.USAGE);
224
+ }
225
+ const detail = argValue(args, '--detail');
226
+ output(dispatch.routeMergeOutcome(detail !== undefined ? { kind, detail } : { kind }), raw);
227
+ },
228
+ // `quick-batch cleanup-entry --agent-id <id|null> --worktree-path <p> --branch <b> --expected-base <b> [--allowed-bases <json-array>] --plan-content <text>`
229
+ 'cleanup-entry': () => {
230
+ const agentIdRaw = argValue(args, '--agent-id');
231
+ const worktreePath = argValue(args, '--worktree-path');
232
+ const branch = argValue(args, '--branch');
233
+ const expectedBase = argValue(args, '--expected-base');
234
+ const planContent = argValue(args, '--plan-content');
235
+ if (!worktreePath || !branch || !expectedBase || planContent === undefined) {
236
+ return makeInvalidArgs('--worktree-path/--branch/--expected-base/--plan-content', 'Usage: gsd-tools quick-batch cleanup-entry --worktree-path <p> --branch <b> --expected-base <b> --plan-content <text> [--agent-id <id>] [--allowed-bases <json-array>]', ERROR_REASON.USAGE);
237
+ }
238
+ let allowedBases;
239
+ const allowedBasesRaw = argValue(args, '--allowed-bases');
240
+ if (allowedBasesRaw !== undefined) {
241
+ const allowedResult = parseJsonArg(allowedBasesRaw, '--allowed-bases');
242
+ if (!allowedResult.ok)
243
+ return makeInvalidArgs('--allowed-bases', allowedResult.reason, ERROR_REASON.USAGE);
244
+ allowedBases = allowedResult.value;
245
+ }
246
+ output(dispatch.buildCleanupManifestEntry({
247
+ agentId: agentIdRaw ?? null,
248
+ worktreePath,
249
+ branch,
250
+ expectedBase,
251
+ allowedBases,
252
+ planContent,
253
+ }), raw);
254
+ },
255
+ // `quick-batch parse-args --text "<raw $ARGUMENTS string>"` (preferred —
256
+ // callers pass the ENTIRE, still-quoted $ARGUMENTS as ONE argv element;
257
+ // this handler does the whitespace split itself, in Node, so shell
258
+ // pathname expansion (globbing) on attacker-influenced task text never
259
+ // happens before this parser sees it — quoting `"$ARGUMENTS"` at the
260
+ // call site is what closes that off; splitting it here is what keeps
261
+ // the caller from having to word-split it unsafely beforehand).
262
+ // `quick-batch parse-args -- <already-tokenized args>` (legacy/direct
263
+ // form — still supported for a caller that already has a real argv
264
+ // array with no shell splitting involved, e.g. a test harness).
265
+ 'parse-args': () => {
266
+ const textArg = argValue(args, '--text');
267
+ if (textArg !== undefined) {
268
+ const rawArgs = textArg.trim().length === 0 ? [] : textArg.trim().split(/\s+/);
269
+ emit(dispatch.parseQuickBatchArgs(rawArgs));
270
+ return;
271
+ }
272
+ const sepIdx = args.indexOf('--');
273
+ const rawArgs = sepIdx === -1 ? [] : args.slice(sepIdx + 1);
274
+ emit(dispatch.parseQuickBatchArgs(rawArgs));
275
+ },
276
+ },
277
+ unknownMessage: (subcommand, available) => `Unknown quick-batch subcommand. Available: ${available.join(', ')}`,
278
+ error,
279
+ cwd,
280
+ raw,
281
+ });
282
+ }
283
+ module.exports = {
284
+ routeQuickBatchCommand,
285
+ };
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /**
3
+ * Quick-Batch Dispatch Core (#3676, Phase 4 of epic #3344 / ADR-1239
4
+ * "Quick-batch binding").
5
+ *
6
+ * PURE decision logic for `/gsd:quick-batch`: argument validation, effective
7
+ * concurrency, deterministic merge ordering, spawn backpressure, and
8
+ * failure/verification routing. This module answers "what should happen
9
+ * next" — it NEVER performs `Agent()` dispatch, `git worktree` I/O, or writes
10
+ * `BATCH.json`/STATE.md itself. Those live in the workflow markdown (a
11
+ * separate follow-up pass) and in `src/quick-batch.cts` (durable manifest
12
+ * mutation, reused as-is).
13
+ *
14
+ * Capacity and isolation are CALLER-SUPPLIED inputs here — the CLI layer
15
+ * resolves those via the existing `dispatch-capacity`/`dispatch-isolation`
16
+ * queries (`gsd-core/bin/gsd-tools.cjs`'s `routeDispatchCapacity`/
17
+ * `routeDispatchIsolation`); this module never re-derives that negotiation.
18
+ *
19
+ * The one exception to "never performs I/O" is `buildCleanupManifestEntry`,
20
+ * which accepts a plan document's raw text (already read by the caller) and
21
+ * parses it via the existing `parsePlanDocument` (`src/plan-document.cts`) —
22
+ * no filesystem access happens inside this module.
23
+ *
24
+ * Design lock: `.gsd/phase/feat-3676-quick-batch-command-workflow/40-design.md`.
25
+ * Test matrix: `.gsd/phase/feat-3676-quick-batch-command-workflow/50-test-matrix.md`.
26
+ *
27
+ * ADR-457 build-at-publish: compiled by tsc to gsd-core/bin/lib/quick-batch-dispatch.cjs.
28
+ */
29
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
30
+ const planDocumentMod = require("./plan-document.cjs");
31
+ const { parsePlanDocument } = planDocumentMod;
32
+ /**
33
+ * Validate `/gsd:quick-batch` CLI args BEFORE any dispatch. Rejects
34
+ * `--discuss`/`--full` outright (v1 exclusion, design rows 7-9,13-15 — a
35
+ * mixed valid+rejected-flag invocation is still rejected: presence alone is
36
+ * sufficient, regardless of other flags), and rejects a malformed `--jobs`
37
+ * value (non-numeric or <= 0, row 5/9) before any dispatch. `--jobs` omitted
38
+ * defaults to `'auto'` (row 10).
39
+ */
40
+ function parseQuickBatchArgs(args) {
41
+ if (!Array.isArray(args)) {
42
+ return { ok: false, reason: 'quick-batch args must be an array' };
43
+ }
44
+ if (args.includes('--discuss')) {
45
+ return { ok: false, reason: 'quick-batch does not support --discuss in v1 — use /gsd:quick --discuss per item instead' };
46
+ }
47
+ if (args.includes('--full')) {
48
+ return { ok: false, reason: 'quick-batch does not support --full in v1' };
49
+ }
50
+ let jobs = 'auto';
51
+ const jobsIdx = args.indexOf('--jobs');
52
+ if (jobsIdx !== -1) {
53
+ const raw = args[jobsIdx + 1];
54
+ if (raw === undefined || raw.startsWith('--')) {
55
+ return { ok: false, reason: 'quick-batch --jobs requires a value ("auto" or a positive integer)' };
56
+ }
57
+ if (raw === 'auto') {
58
+ jobs = 'auto';
59
+ }
60
+ else {
61
+ const n = Number(raw);
62
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
63
+ return { ok: false, reason: `quick-batch --jobs must be "auto" or a positive integer, got ${JSON.stringify(raw)}` };
64
+ }
65
+ jobs = n;
66
+ }
67
+ }
68
+ const validate = args.includes('--validate');
69
+ const research = args.includes('--research');
70
+ let resume = null;
71
+ const resumeIdx = args.indexOf('--resume');
72
+ if (resumeIdx !== -1) {
73
+ const raw = args[resumeIdx + 1];
74
+ if (raw === undefined || raw.startsWith('--')) {
75
+ return { ok: false, reason: 'quick-batch --resume requires a batch id' };
76
+ }
77
+ resume = raw;
78
+ }
79
+ return { ok: true, value: { jobs, validate, research, resume } };
80
+ }
81
+ /**
82
+ * `--jobs auto` (or omitted) → capacity alone (rows 3,10). `--jobs N` →
83
+ * `min(taskCount, N, capacity)` (row 4). When `isolation === 'none'` and the
84
+ * wave is `mutating`, concurrency is forced to 1 regardless of `--jobs`/
85
+ * capacity (row 6) — a non-mutating wave is unaffected (row 12).
86
+ */
87
+ function computeEffectiveConcurrency(input) {
88
+ let concurrency = input.jobs === 'auto'
89
+ ? input.capacity
90
+ : Math.min(input.taskCount, input.jobs, input.capacity);
91
+ if (input.isolation === 'none' && input.mutating) {
92
+ concurrency = Math.min(concurrency, 1);
93
+ }
94
+ return Math.max(concurrency, 0);
95
+ }
96
+ // ─── Deterministic merge ordering (design row 24; test-matrix rows 32-33) ───
97
+ /**
98
+ * Given a wave's ORIGINAL dispatch order (`waveOrder`, as `computeWaves`
99
+ * assigned it) and the set of ids whose leaf has finished ("ready"),
100
+ * returns the prefix of `waveOrder` that is currently mergeable — merges
101
+ * happen strictly in wave order, so an item is only mergeable once every
102
+ * item before it in `waveOrder` is ALSO ready. An out-of-order completion
103
+ * (e.g. item 2 finishes before item 1) waits: `computeMergeOrder` returns
104
+ * only `[]` until item 1 is also ready.
105
+ */
106
+ function computeMergeOrder(waveOrder, readyIds) {
107
+ const mergeable = [];
108
+ for (const id of waveOrder) {
109
+ if (!readyIds.has(id))
110
+ break;
111
+ mergeable.push(id);
112
+ }
113
+ return mergeable;
114
+ }
115
+ /**
116
+ * Pure backpressure model: given eligible items, a capacity ceiling, and a
117
+ * set of host-refused ids, decide which ids spawn now and which stay
118
+ * `pending`. Total in-flight (`currentInFlight + spawn.length`) never
119
+ * exceeds `capacity` at any point (row 27, property row 53). A refused id
120
+ * is never counted against capacity and never marked `failed` — it simply
121
+ * returns to `pending` for a later round.
122
+ */
123
+ function computeSpawnPlan(input) {
124
+ const refusedSet = input.refused instanceof Set ? input.refused : new Set(input.refused ?? []);
125
+ let available = Math.max(input.capacity - input.currentInFlight, 0);
126
+ const spawn = [];
127
+ const pending = [];
128
+ for (const id of input.eligibleIds) {
129
+ if (refusedSet.has(id)) {
130
+ pending.push(id);
131
+ continue;
132
+ }
133
+ if (available > 0) {
134
+ spawn.push(id);
135
+ available -= 1;
136
+ }
137
+ else {
138
+ pending.push(id);
139
+ }
140
+ }
141
+ return { spawn, pending };
142
+ }
143
+ /**
144
+ * Route a verifier's status to an item action. `human_needed` is terminal
145
+ * for the item — the caller must NOT call `completeQuickItem` (row 30, no
146
+ * STATE row appended). `gaps_found` fails the item WITHOUT rollback and
147
+ * WITHOUT an automatic gap-fix retry (row 31,34). `passed` completes it.
148
+ */
149
+ function routeVerificationOutcome(status) {
150
+ switch (status) {
151
+ case 'passed':
152
+ return { action: 'complete' };
153
+ case 'human_needed':
154
+ return { action: 'human_needed' };
155
+ case 'gaps_found':
156
+ return { action: 'fail', failureReason: 'verification reported gaps_found (no automatic gap-fix retry in v1)' };
157
+ }
158
+ }
159
+ /**
160
+ * Route a merge attempt's outcome to an item action. Both `merge_failed`
161
+ * (real conflict) and `scope_violation` (undeclared deletion / advisory
162
+ * scope drift escalated to a gate — see `executeWorktreeWaveCleanupPlan`)
163
+ * mark the item `failed` with a `failure_reason` and PRESERVE the worktree
164
+ * for diagnosis — this function never signals worktree removal (row 28,
165
+ * 34-35). `merged` completes the item.
166
+ */
167
+ function routeMergeOutcome(outcome) {
168
+ switch (outcome.kind) {
169
+ case 'merged':
170
+ return { action: 'complete' };
171
+ case 'merge_failed':
172
+ return {
173
+ action: 'fail',
174
+ failureReason: outcome.detail ? `merge_failed: ${outcome.detail}` : 'merge_failed',
175
+ preserveWorktree: true,
176
+ };
177
+ case 'scope_violation':
178
+ return {
179
+ action: 'fail',
180
+ failureReason: outcome.detail ? `scope_violation: ${outcome.detail}` : 'scope_violation',
181
+ preserveWorktree: true,
182
+ };
183
+ }
184
+ }
185
+ /**
186
+ * Crash-window duplicate-dispatch guard
187
+ * (`.gsd/phase/feat-3677-quick-batch-hardening-acceptance/40-design.md` §1).
188
+ * `quick-batch resume`'s `eligible` is purely status/dependency-derived —
189
+ * it has no awareness that an item already finished executing (a real
190
+ * commit, `SUMMARY.md` written) before a coordinator crash left
191
+ * `BATCH.json` at `pending` (the STATE.md-row crash-window detection
192
+ * inside `resumeBatch` only fires once Step 9 has run). This mirrors the
193
+ * file-existence exclusion `planner-wave.md` already applies one layer
194
+ * earlier for `PLAN.md`, extracted as its own pure decision (rather than
195
+ * left as workflow prose only) so it is independently testable: given the
196
+ * eligible ids for this round and the subset the CALLER has already
197
+ * determined finished executing, splits them into ids safe to spawn now
198
+ * and ids that must never be re-dispatched.
199
+ */
200
+ function filterAlreadyExecuted(eligibleIds, executedIds) {
201
+ const executedSet = executedIds instanceof Set ? executedIds : new Set(executedIds);
202
+ const spawnEligible = [];
203
+ const alreadyExecuted = [];
204
+ for (const id of eligibleIds) {
205
+ if (executedSet.has(id)) {
206
+ alreadyExecuted.push(id);
207
+ }
208
+ else {
209
+ spawnEligible.push(id);
210
+ }
211
+ }
212
+ return { spawnEligible, alreadyExecuted };
213
+ }
214
+ /**
215
+ * Build one `worktree.cleanup-wave` manifest entry for an item, deriving
216
+ * `files_modified`/`declared_deletions` FRESH from the item's own PLAN.md via
217
+ * the existing `parsePlanDocument` (never from `BATCH.json`'s `planned_files`
218
+ * alone — Open Question 2's accepted resolution). An empty `declared_deletions`
219
+ * for a plan that genuinely deletes nothing is indistinguishable from a plan
220
+ * that forgot to declare one — this is `partitionDeclaredDeletions`'s own
221
+ * pre-existing "absent/empty = declares nothing" convention, inherited here,
222
+ * not resolved.
223
+ */
224
+ function buildCleanupManifestEntry(input) {
225
+ const parsed = parsePlanDocument(input.planContent);
226
+ const entry = {
227
+ agent_id: input.agentId,
228
+ worktree_path: input.worktreePath,
229
+ branch: input.branch,
230
+ expected_base: input.expectedBase,
231
+ files_modified: parsed.filesModified,
232
+ declared_deletions: parsed.filesDeleted,
233
+ };
234
+ if (input.allowedBases !== undefined) {
235
+ entry.allowed_bases = input.allowedBases;
236
+ }
237
+ return entry;
238
+ }
239
+ // ─── Exports ────────────────────────────────────────────────────────────────
240
+ const quickBatchDispatch = {
241
+ parseQuickBatchArgs,
242
+ computeEffectiveConcurrency,
243
+ computeMergeOrder,
244
+ computeSpawnPlan,
245
+ routeVerificationOutcome,
246
+ routeMergeOutcome,
247
+ buildCleanupManifestEntry,
248
+ filterAlreadyExecuted,
249
+ };
250
+ module.exports = quickBatchDispatch;