@opengsd/gsd-core 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (551) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +85 -5
  4. package/README.ja-JP.md +3 -3
  5. package/README.ko-KR.md +3 -3
  6. package/README.pt-BR.md +3 -3
  7. package/README.zh-CN.md +3 -3
  8. package/agents/gsd-code-fixer.compact.md +7 -6
  9. package/agents/gsd-code-fixer.md +9 -8
  10. package/agents/gsd-code-reviewer.compact.md +5 -3
  11. package/agents/gsd-code-reviewer.md +8 -6
  12. package/agents/gsd-debug-session-manager.compact.md +17 -2
  13. package/agents/gsd-debug-session-manager.md +17 -2
  14. package/agents/gsd-debugger.md +3 -3
  15. package/agents/gsd-eval-auditor.compact.md +1 -1
  16. package/agents/gsd-eval-auditor.md +1 -1
  17. package/agents/gsd-executor.md +17 -12
  18. package/agents/gsd-intel-updater.compact.md +1 -1
  19. package/agents/gsd-intel-updater.md +1 -1
  20. package/agents/gsd-mempalace-curator.md +2 -2
  21. package/agents/gsd-phase-researcher.md +19 -11
  22. package/agents/gsd-plan-checker.md +15 -9
  23. package/agents/gsd-planner.md +15 -11
  24. package/agents/gsd-project-researcher.compact.md +1 -1
  25. package/agents/gsd-project-researcher.md +1 -1
  26. package/agents/gsd-research-synthesizer.compact.md +1 -1
  27. package/agents/gsd-research-synthesizer.md +1 -1
  28. package/agents/gsd-ui-auditor.compact.md +21 -30
  29. package/agents/gsd-ui-auditor.md +166 -37
  30. package/agents/gsd-ui-researcher.compact.md +1 -1
  31. package/agents/gsd-ui-researcher.md +1 -1
  32. package/agents/gsd-verifier.md +37 -14
  33. package/bin/install.js +764 -287
  34. package/commands/gsd/add-tests.md +6 -1
  35. package/commands/gsd/ai-integration-phase.md +6 -1
  36. package/commands/gsd/audit-fix.md +5 -0
  37. package/commands/gsd/audit-milestone.md +6 -1
  38. package/commands/gsd/autonomous.md +7 -2
  39. package/commands/gsd/capture.md +9 -5
  40. package/commands/gsd/code-review.md +7 -2
  41. package/commands/gsd/complete-milestone.md +4 -0
  42. package/commands/gsd/config.md +7 -3
  43. package/commands/gsd/debug.md +11 -7
  44. package/commands/gsd/discuss-phase.md +7 -3
  45. package/commands/gsd/docs-update.md +12 -7
  46. package/commands/gsd/eval-review.md +6 -1
  47. package/commands/gsd/execute-phase.md +12 -7
  48. package/commands/gsd/extract-learnings.md +5 -0
  49. package/commands/gsd/fast.md +4 -0
  50. package/commands/gsd/forensics.md +5 -1
  51. package/commands/gsd/graphify.md +10 -6
  52. package/commands/gsd/health.md +5 -0
  53. package/commands/gsd/help.md +7 -2
  54. package/commands/gsd/import.md +7 -3
  55. package/commands/gsd/inbox.md +5 -0
  56. package/commands/gsd/ingest-docs.md +5 -1
  57. package/commands/gsd/manager.md +6 -1
  58. package/commands/gsd/map-codebase.md +7 -3
  59. package/commands/gsd/mempalace-capture.md +12 -4
  60. package/commands/gsd/mempalace-recall.md +5 -1
  61. package/commands/gsd/milestone-summary.md +5 -1
  62. package/commands/gsd/mvp-phase.md +8 -3
  63. package/commands/gsd/new-milestone.md +6 -1
  64. package/commands/gsd/new-project.md +5 -0
  65. package/commands/gsd/next.md +6 -1
  66. package/commands/gsd/ns-context.md +4 -0
  67. package/commands/gsd/ns-ideate.md +4 -0
  68. package/commands/gsd/ns-manage.md +4 -0
  69. package/commands/gsd/ns-project.md +4 -0
  70. package/commands/gsd/ns-review.md +4 -0
  71. package/commands/gsd/ns-workflow.md +4 -0
  72. package/commands/gsd/onboard.md +6 -1
  73. package/commands/gsd/pause-work.md +5 -1
  74. package/commands/gsd/phase.md +8 -4
  75. package/commands/gsd/plan-phase.md +6 -1
  76. package/commands/gsd/plan-review-convergence.md +11 -7
  77. package/commands/gsd/pr-branch.md +4 -0
  78. package/commands/gsd/profile-user.md +5 -1
  79. package/commands/gsd/progress.md +7 -2
  80. package/commands/gsd/quick-batch.md +21 -9
  81. package/commands/gsd/quick.md +12 -7
  82. package/commands/gsd/review.md +7 -4
  83. package/commands/gsd/secure-phase.md +6 -1
  84. package/commands/gsd/ship.md +5 -0
  85. package/commands/gsd/sketch.md +7 -2
  86. package/commands/gsd/spec-phase.md +5 -1
  87. package/commands/gsd/spike.md +8 -3
  88. package/commands/gsd/surface.md +5 -1
  89. package/commands/gsd/thread.md +4 -0
  90. package/commands/gsd/ui-phase.md +6 -1
  91. package/commands/gsd/ui-review.md +6 -1
  92. package/commands/gsd/ultraplan-phase.md +5 -1
  93. package/commands/gsd/undo.md +5 -1
  94. package/commands/gsd/update.md +6 -2
  95. package/commands/gsd/validate-phase.md +6 -1
  96. package/commands/gsd/verify-work.md +6 -1
  97. package/commands/gsd/workspace.md +7 -3
  98. package/gsd-core/bin/gsd-tools.cjs +477 -78
  99. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  100. package/gsd-core/bin/lib/adr-parser.cjs +3 -1
  101. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  102. package/gsd-core/bin/lib/audit.cjs +144 -42
  103. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  104. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  105. package/gsd-core/bin/lib/capability-registry.cjs +197 -222
  106. package/gsd-core/bin/lib/capability-validator.cjs +16 -1
  107. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  108. package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
  109. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  110. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  111. package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
  112. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  113. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  114. package/gsd-core/bin/lib/commands.cjs +343 -207
  115. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  116. package/gsd-core/bin/lib/config-loader.cjs +65 -4
  117. package/gsd-core/bin/lib/config.cjs +76 -19
  118. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  119. package/gsd-core/bin/lib/coverage.cjs +4 -8
  120. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  121. package/gsd-core/bin/lib/decisions.cjs +30 -14
  122. package/gsd-core/bin/lib/drift.cjs +177 -42
  123. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  124. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  125. package/gsd-core/bin/lib/frontmatter.cjs +426 -234
  126. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  127. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  128. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  129. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  130. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  131. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  132. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  133. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  134. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  135. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  136. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  137. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  138. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  139. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  140. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  141. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  142. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  143. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  144. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  145. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  146. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  147. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  148. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  149. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  150. package/gsd-core/bin/lib/graphify.cjs +10 -2
  151. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  152. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  153. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  154. package/gsd-core/bin/lib/init.cjs +364 -132
  155. package/gsd-core/bin/lib/install-engine.cjs +30 -33
  156. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  157. package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
  158. package/gsd-core/bin/lib/io.cjs +122 -3
  159. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  160. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  161. package/gsd-core/bin/lib/milestone.cjs +37 -6
  162. package/gsd-core/bin/lib/model-resolver.cjs +171 -62
  163. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  164. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  165. package/gsd-core/bin/lib/pattern.cjs +10 -0
  166. package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
  167. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  168. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  169. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  170. package/gsd-core/bin/lib/phase-id.cjs +110 -8
  171. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  172. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  173. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  174. package/gsd-core/bin/lib/phase.cjs +489 -88
  175. package/gsd-core/bin/lib/plan-document.cjs +142 -20
  176. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  177. package/gsd-core/bin/lib/planning-document.cjs +692 -0
  178. package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
  179. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  180. package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
  181. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  182. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  183. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  184. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  185. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  186. package/gsd-core/bin/lib/project-root.cjs +41 -2
  187. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  188. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  189. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  190. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  191. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  192. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  193. package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
  194. package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
  195. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
  196. package/gsd-core/bin/lib/roadmap.cjs +405 -88
  197. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
  198. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
  199. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  200. package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
  201. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
  202. package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
  203. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  204. package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
  205. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  206. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  207. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  208. package/gsd-core/bin/lib/state-document.cjs +100 -22
  209. package/gsd-core/bin/lib/state-transition.cjs +39 -2
  210. package/gsd-core/bin/lib/state.cjs +256 -105
  211. package/gsd-core/bin/lib/surface.cjs +19 -2
  212. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
  213. package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
  214. package/gsd-core/bin/lib/uat.cjs +432 -7
  215. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  216. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
  217. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  218. package/gsd-core/bin/lib/vendor/README.md +31 -9
  219. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  220. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  221. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  222. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  223. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  224. package/gsd-core/bin/lib/verification.cjs +1378 -274
  225. package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
  226. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  227. package/gsd-core/bin/lib/verify.cjs +357 -531
  228. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  229. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  230. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  231. package/gsd-core/bin/lib/workstream.cjs +11 -2
  232. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  233. package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
  234. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
  235. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  236. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  237. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  238. package/gsd-core/references/checkpoints.md +5 -3
  239. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  240. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  241. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  242. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  243. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  244. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  245. package/gsd-core/references/edge-probe.md +195 -21
  246. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  247. package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
  248. package/gsd-core/references/execute-phase-response-language.md +1 -1
  249. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  250. package/gsd-core/references/gsd-run-resolver.md +1 -1
  251. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  252. package/gsd-core/references/model-profiles.md +1 -1
  253. package/gsd-core/references/offer-next.md +1 -1
  254. package/gsd-core/references/phase-argument-parsing.md +9 -7
  255. package/gsd-core/references/phase-id-convention.md +28 -0
  256. package/gsd-core/references/planner-gap-closure.md +2 -0
  257. package/gsd-core/references/planner-load-graph-context.md +24 -13
  258. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  259. package/gsd-core/references/planning-config.md +12 -3
  260. package/gsd-core/references/spidr-splitting.md +1 -1
  261. package/gsd-core/references/tdd.md +37 -8
  262. package/gsd-core/references/ui-consideration-probe.md +10 -5
  263. package/gsd-core/references/verifier-phase-gates.md +5 -2
  264. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  265. package/gsd-core/references/verify-mvp-mode.md +2 -2
  266. package/gsd-core/references/workstream-flag.md +33 -3
  267. package/gsd-core/references/worktree-path-safety.md +321 -0
  268. package/gsd-core/templates/README.md +1 -1
  269. package/gsd-core/templates/UAT.md +17 -1
  270. package/gsd-core/templates/config.json +2 -11
  271. package/gsd-core/templates/verification-report.md +1 -1
  272. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  273. package/gsd-core/workflows/add-backlog.md +1 -1
  274. package/gsd-core/workflows/add-phase.md +8 -7
  275. package/gsd-core/workflows/add-tests.md +4 -3
  276. package/gsd-core/workflows/add-todo.md +6 -5
  277. package/gsd-core/workflows/ai-integration-phase.md +13 -4
  278. package/gsd-core/workflows/audit-fix.md +1 -1
  279. package/gsd-core/workflows/audit-milestone.md +4 -3
  280. package/gsd-core/workflows/audit-uat.md +1 -1
  281. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  282. package/gsd-core/workflows/autonomous.md +43 -23
  283. package/gsd-core/workflows/check-todos.md +7 -6
  284. package/gsd-core/workflows/cleanup.md +2 -2
  285. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  286. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  287. package/gsd-core/workflows/code-review-fix.md +112 -25
  288. package/gsd-core/workflows/code-review.md +146 -135
  289. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  290. package/gsd-core/workflows/complete-milestone.md +13 -8
  291. package/gsd-core/workflows/debug.md +32 -7
  292. package/gsd-core/workflows/diagnose-issues.md +3 -2
  293. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  294. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  295. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  296. package/gsd-core/workflows/discuss-phase.md +4 -3
  297. package/gsd-core/workflows/do.md +2 -2
  298. package/gsd-core/workflows/docs-update.md +6 -5
  299. package/gsd-core/workflows/edit-phase.md +4 -3
  300. package/gsd-core/workflows/eval-review.md +14 -5
  301. package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
  302. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
  303. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  304. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  305. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
  306. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  307. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  308. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  309. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  310. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  311. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  312. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  313. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  314. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  315. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  316. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  317. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  318. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  319. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  320. package/gsd-core/workflows/execute-phase.md +96 -178
  321. package/gsd-core/workflows/execute-plan.md +18 -23
  322. package/gsd-core/workflows/explore.md +4 -4
  323. package/gsd-core/workflows/extract-learnings.md +4 -2
  324. package/gsd-core/workflows/fast.md +1 -1
  325. package/gsd-core/workflows/forensics.md +1 -1
  326. package/gsd-core/workflows/graduation.md +1 -1
  327. package/gsd-core/workflows/health.md +3 -2
  328. package/gsd-core/workflows/help/modes/full.compact.md +3 -3
  329. package/gsd-core/workflows/help/modes/full.md +5 -5
  330. package/gsd-core/workflows/help/modes/topic.md +15 -5
  331. package/gsd-core/workflows/import.md +4 -3
  332. package/gsd-core/workflows/inbox.md +2 -2
  333. package/gsd-core/workflows/ingest-docs.md +3 -3
  334. package/gsd-core/workflows/insert-phase.md +4 -3
  335. package/gsd-core/workflows/list-seeds.md +1 -1
  336. package/gsd-core/workflows/list-workspaces.md +1 -1
  337. package/gsd-core/workflows/manager.md +6 -4
  338. package/gsd-core/workflows/map-codebase.md +5 -4
  339. package/gsd-core/workflows/milestone-summary.md +3 -2
  340. package/gsd-core/workflows/mvp-phase.md +14 -14
  341. package/gsd-core/workflows/new-milestone.md +11 -11
  342. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  343. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  344. package/gsd-core/workflows/new-project.md +7 -7
  345. package/gsd-core/workflows/new-workspace.md +2 -2
  346. package/gsd-core/workflows/next.md +1 -1
  347. package/gsd-core/workflows/note.md +1 -1
  348. package/gsd-core/workflows/onboard.md +1 -1
  349. package/gsd-core/workflows/pause-work.md +2 -2
  350. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  351. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  352. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  353. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  354. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  355. package/gsd-core/workflows/plan-phase.md +50 -24
  356. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  357. package/gsd-core/workflows/plant-seed.md +62 -20
  358. package/gsd-core/workflows/pr-branch.md +113 -13
  359. package/gsd-core/workflows/profile-user.md +2 -2
  360. package/gsd-core/workflows/progress.md +19 -49
  361. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
  362. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  363. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  364. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  365. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  366. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  367. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  368. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  369. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  370. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  371. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  372. package/gsd-core/workflows/quick-batch.md +15 -10
  373. package/gsd-core/workflows/quick.md +62 -39
  374. package/gsd-core/workflows/reapply-patches.md +9 -3
  375. package/gsd-core/workflows/remove-phase.md +3 -2
  376. package/gsd-core/workflows/remove-workspace.md +2 -2
  377. package/gsd-core/workflows/resume-project.md +3 -2
  378. package/gsd-core/workflows/review.md +33 -17
  379. package/gsd-core/workflows/scan.md +3 -2
  380. package/gsd-core/workflows/secure-phase.md +13 -13
  381. package/gsd-core/workflows/settings-advanced.md +30 -10
  382. package/gsd-core/workflows/settings-integrations.md +2 -3
  383. package/gsd-core/workflows/settings.md +4 -4
  384. package/gsd-core/workflows/ship.md +14 -13
  385. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  386. package/gsd-core/workflows/sketch.md +1 -1
  387. package/gsd-core/workflows/smart-entry.md +2 -2
  388. package/gsd-core/workflows/spec-phase.md +15 -5
  389. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  390. package/gsd-core/workflows/spike.md +1 -1
  391. package/gsd-core/workflows/stats.md +1 -1
  392. package/gsd-core/workflows/sync-skills.md +5 -5
  393. package/gsd-core/workflows/thread.md +2 -2
  394. package/gsd-core/workflows/transition.md +13 -23
  395. package/gsd-core/workflows/ui-phase.md +48 -11
  396. package/gsd-core/workflows/ui-review.md +21 -6
  397. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  398. package/gsd-core/workflows/undo.md +339 -20
  399. package/gsd-core/workflows/update.md +7 -7
  400. package/gsd-core/workflows/validate-phase.md +12 -13
  401. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  402. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  403. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  404. package/gsd-core/workflows/verify-work.md +129 -55
  405. package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
  406. package/hooks/dist/gsd-check-update-worker.js +8 -0
  407. package/hooks/dist/gsd-check-update.js +8 -0
  408. package/hooks/dist/gsd-context-monitor.js +31 -8
  409. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  410. package/hooks/dist/gsd-secret-read-guard.js +161 -4
  411. package/hooks/dist/gsd-statusline.js +85 -20
  412. package/hooks/dist/gsd-update-banner.js +8 -0
  413. package/hooks/dist/gsd-validate-commit.sh +63 -4
  414. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  415. package/hooks/dist/gsd-workflow-guard.js +5 -4
  416. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  417. package/hooks/dist/lib/cli-exit.js +12 -0
  418. package/hooks/dist/lib/git-probe.js +17 -1
  419. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  420. package/hooks/gsd-agent-isolation-guard.js +32 -0
  421. package/hooks/gsd-check-update-worker.js +8 -0
  422. package/hooks/gsd-check-update.js +8 -0
  423. package/hooks/gsd-context-monitor.js +31 -8
  424. package/hooks/gsd-cursor-subagent-start.js +8 -0
  425. package/hooks/gsd-secret-read-guard.js +161 -4
  426. package/hooks/gsd-statusline.js +85 -20
  427. package/hooks/gsd-update-banner.js +8 -0
  428. package/hooks/gsd-validate-commit.sh +63 -4
  429. package/hooks/gsd-windsurf-pre-write.js +11 -2
  430. package/hooks/gsd-workflow-guard.js +5 -4
  431. package/hooks/gsd-worktree-path-guard.js +6 -2
  432. package/hooks/hooks.json +5 -5
  433. package/hooks/lib/cli-exit.js +12 -0
  434. package/hooks/lib/git-probe.js +17 -1
  435. package/hooks/lib/isolation-sentinel.js +2 -2
  436. package/package.json +22 -4
  437. package/scripts/build-hooks.js +15 -6
  438. package/scripts/changeset/parse.cjs +52 -4
  439. package/scripts/check-contract-drift.cjs +127 -11
  440. package/scripts/ci-timeout-report.cjs +770 -4
  441. package/scripts/command-contract-helpers.cjs +15 -8
  442. package/scripts/docs-guard-registry.cjs +34 -0
  443. package/scripts/gen-features.cjs +13 -8
  444. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  445. package/scripts/gen-loop-host-contract.cjs +79 -1
  446. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  447. package/scripts/gen-plugin-skills.cjs +87 -1
  448. package/scripts/gen-research-agents.cjs +24 -31
  449. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  450. package/scripts/gen-test-timings.cjs +32 -7
  451. package/scripts/lib/cli-exit.cjs +12 -0
  452. package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
  453. package/scripts/lib/ndjson-reporter.cjs +31 -5
  454. package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
  455. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  456. package/scripts/lib/vendor-bundle.cjs +59 -0
  457. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  458. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  459. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  460. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  461. package/scripts/lint-descriptions.cjs +7 -3
  462. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
  463. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  464. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  465. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  466. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  467. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  468. package/scripts/lint-phase-id-drift.cjs +319 -5
  469. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  470. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  471. package/scripts/lint-response-language-coverage.cjs +3 -0
  472. package/scripts/lint-retired-runtime-name.cjs +619 -0
  473. package/scripts/lint-skill-deps.cjs +7 -3
  474. package/scripts/lint-state-write-path-drift.cjs +93 -0
  475. package/scripts/lint-test-file-count.allowlist.json +38 -9
  476. package/scripts/lint-test-file-count.cjs +34 -1
  477. package/scripts/lint-vendored-deps.cjs +41 -5
  478. package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
  479. package/scripts/mutation-matrix.cjs +50 -5
  480. package/scripts/prompt-injection-scan.sh +4 -0
  481. package/scripts/release-tarball-smoke.cjs +194 -1
  482. package/scripts/require-issue-link-policy.cjs +6 -2
  483. package/scripts/sync-runtime-launcher.cjs +184 -2
  484. package/scripts/verify-npm-publish.cjs +76 -20
  485. package/skills/gsd-add-tests/SKILL.md +6 -1
  486. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  487. package/skills/gsd-audit-fix/SKILL.md +5 -0
  488. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  489. package/skills/gsd-autonomous/SKILL.md +7 -2
  490. package/skills/gsd-capture/SKILL.md +9 -5
  491. package/skills/gsd-code-review/SKILL.md +7 -2
  492. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  493. package/skills/gsd-config/SKILL.md +8 -4
  494. package/skills/gsd-debug/SKILL.md +11 -7
  495. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  496. package/skills/gsd-docs-update/SKILL.md +12 -7
  497. package/skills/gsd-eval-review/SKILL.md +6 -1
  498. package/skills/gsd-execute-phase/SKILL.md +12 -7
  499. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  500. package/skills/gsd-fast/SKILL.md +4 -0
  501. package/skills/gsd-forensics/SKILL.md +5 -1
  502. package/skills/gsd-graphify/SKILL.md +10 -6
  503. package/skills/gsd-health/SKILL.md +5 -0
  504. package/skills/gsd-help/SKILL.md +7 -2
  505. package/skills/gsd-import/SKILL.md +7 -3
  506. package/skills/gsd-inbox/SKILL.md +5 -0
  507. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  508. package/skills/gsd-manager/SKILL.md +6 -1
  509. package/skills/gsd-map-codebase/SKILL.md +7 -3
  510. package/skills/gsd-mempalace-capture/SKILL.md +12 -4
  511. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  512. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  513. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  514. package/skills/gsd-new-milestone/SKILL.md +6 -1
  515. package/skills/gsd-new-project/SKILL.md +5 -0
  516. package/skills/gsd-next/SKILL.md +6 -1
  517. package/skills/gsd-ns-context/SKILL.md +4 -0
  518. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  519. package/skills/gsd-ns-manage/SKILL.md +4 -0
  520. package/skills/gsd-ns-project/SKILL.md +4 -0
  521. package/skills/gsd-ns-review/SKILL.md +4 -0
  522. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  523. package/skills/gsd-onboard/SKILL.md +6 -1
  524. package/skills/gsd-pause-work/SKILL.md +5 -1
  525. package/skills/gsd-phase/SKILL.md +8 -4
  526. package/skills/gsd-plan-phase/SKILL.md +6 -1
  527. package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
  528. package/skills/gsd-pr-branch/SKILL.md +4 -0
  529. package/skills/gsd-profile-user/SKILL.md +5 -1
  530. package/skills/gsd-progress/SKILL.md +7 -2
  531. package/skills/gsd-quick/SKILL.md +16 -10
  532. package/skills/gsd-quick-batch/SKILL.md +21 -9
  533. package/skills/gsd-review/SKILL.md +7 -4
  534. package/skills/gsd-review-backlog/SKILL.md +3 -2
  535. package/skills/gsd-secure-phase/SKILL.md +6 -1
  536. package/skills/gsd-ship/SKILL.md +5 -0
  537. package/skills/gsd-sketch/SKILL.md +7 -2
  538. package/skills/gsd-spec-phase/SKILL.md +5 -1
  539. package/skills/gsd-spike/SKILL.md +8 -3
  540. package/skills/gsd-surface/SKILL.md +5 -1
  541. package/skills/gsd-thread/SKILL.md +4 -0
  542. package/skills/gsd-ui-phase/SKILL.md +6 -1
  543. package/skills/gsd-ui-review/SKILL.md +6 -1
  544. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  545. package/skills/gsd-undo/SKILL.md +5 -1
  546. package/skills/gsd-update/SKILL.md +6 -2
  547. package/skills/gsd-validate-phase/SKILL.md +6 -1
  548. package/skills/gsd-verify-work/SKILL.md +6 -1
  549. package/skills/gsd-workspace/SKILL.md +7 -3
  550. package/skills/gsd-workstreams/SKILL.md +6 -6
  551. package/vscode/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * `check decision-coverage-verify` — advisory verify-phase decision-coverage gate, as a gate
4
+ * module (#5139, epic #5056, ADR-5057 §4 first bullet): it returns a `GateResult`; the command
5
+ * router formats it. Always `blocking:false` — a soft warning, never a verification failure.
6
+ * Imports no io module and performs no direct console/stdout/stderr write (ESLint-enforced).
7
+ *
8
+ * Argv after the verb: `<phase-dir> <context-path>` (positional; no `--context` flag here).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.evaluateDecisionCoverageVerify = evaluateDecisionCoverageVerify;
12
+ const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
13
+ const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
14
+ const gate_config_cjs_1 = require("./gate-config.cjs");
15
+ const decision_coverage_support_cjs_1 = require("./decision-coverage-support.cjs");
16
+ /**
17
+ * The verdict when CONTEXT.md or a shipped artifact could not be read. The gate stays advisory
18
+ * (`block: false`, `blocking: false`); the outcome is `unreadable`, so the exit status is
19
+ * UNAVAILABLE and no decision is reported honored or not honored from content never seen (#5170).
20
+ */
21
+ function unreadableVerify(readError) {
22
+ return (0, gate_verdict_cjs_1.gateUnreadable)(false, {
23
+ skipped: false,
24
+ blocking: false,
25
+ reason: 'unreadable evidence',
26
+ total: null,
27
+ honored: null,
28
+ not_honored: [],
29
+ readError,
30
+ message: `Decision coverage verify (warning): could not read its evidence (${readError}); no decision was checked.`,
31
+ });
32
+ }
33
+ /** The verdict when there is no CONTEXT.md: the legitimate green skip (authoritatively `none`, never `unreadable`). */
34
+ function contextMissingSkip() {
35
+ return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { skipped: true, blocking: false, reason: 'CONTEXT.md missing', total: 0, honored: 0, not_honored: [], message: 'No CONTEXT.md - nothing to check.' });
36
+ }
37
+ function evaluateDecisionCoverageVerify(input) {
38
+ const { projectDir, args } = input;
39
+ let phaseDir = '';
40
+ if (args[0]) {
41
+ const resolved = (0, gate_phase_context_cjs_1.resolveContainedPath)(args[0], projectDir);
42
+ if ((0, gate_verdict_cjs_1.isGateUsageFailure)(resolved))
43
+ return resolved;
44
+ phaseDir = resolved;
45
+ }
46
+ let contextPath = '';
47
+ if (args[1]) {
48
+ const resolved = (0, gate_phase_context_cjs_1.resolveContainedPath)(args[1], projectDir);
49
+ if ((0, gate_verdict_cjs_1.isGateUsageFailure)(resolved))
50
+ return resolved;
51
+ contextPath = resolved;
52
+ }
53
+ if (!(0, gate_config_cjs_1.isDecisionCoverageGateEnabled)(projectDir)) {
54
+ return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { skipped: true, blocking: false, reason: 'workflow.context_coverage_gate is false', total: 0, honored: 0, not_honored: [], message: 'Decision coverage gate disabled by config.' });
55
+ }
56
+ // No context argument is the only up-front skip: an absent file is `none` and an unreadable one is
57
+ // `unreadable` below, from the one read that loads the decisions (`fs.existsSync` answered `false`
58
+ // for an EACCES on a parent, skipping a CONTEXT.md the gate never saw — #5170).
59
+ if (!contextPath) {
60
+ return contextMissingSkip();
61
+ }
62
+ const extracted = (0, decision_coverage_support_cjs_1.loadDecisionExtraction)(contextPath);
63
+ if (extracted.kind === 'unreadable')
64
+ return unreadableVerify(`${extracted.span ?? contextPath}: ${extracted.reason}`);
65
+ if (extracted.kind === 'none') {
66
+ return contextMissingSkip();
67
+ }
68
+ const { trackable: decisions, outcome: decisionOutcome } = extracted.value;
69
+ // Mirror could-not-parse surface for verify (non-blocking advisory WARN).
70
+ // Fire independent of decisions.length — a parse-miss on any bullet must surface,
71
+ // even when some decisions were partially extracted (#1365 fix-parity with plan gate).
72
+ if (decisionOutcome === 'could-not-parse') {
73
+ const partialParse = decisions.length > 0;
74
+ return (0, gate_verdict_cjs_1.gateVerdict)('advisory', false, {
75
+ skipped: false,
76
+ blocking: false,
77
+ reason: 'could-not-parse',
78
+ total: decisions.length,
79
+ honored: 0,
80
+ not_honored: [],
81
+ message: partialParse
82
+ ? 'Decision coverage verify (warning): decisions could not be fully parsed — one or more ' +
83
+ '`- **D-NN ...**` bullets appear malformed (missing `:` or ` — ` separator, or a phase ' +
84
+ 'prefix that is not a digit run). Fix the bullet format in the CONTEXT.md decisions block.'
85
+ : 'Decision coverage verify (warning): could not parse decisions — possible format mismatch. ' +
86
+ 'Check the formatting of the CONTEXT.md decisions block (accepted forms: `- **D-NN:** text`, ' +
87
+ '`- **D4-NN:** text` (phase-prefixed), `- **D-NN — title** body`).',
88
+ });
89
+ }
90
+ if (decisions.length === 0) {
91
+ return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { skipped: true, blocking: false, reason: 'no trackable decisions', total: 0, honored: 0, not_honored: [], message: 'No trackable decisions in CONTEXT.md.' });
92
+ }
93
+ // Every file the haystack is built from is typed evidence (#5170): one that exists but cannot be
94
+ // read is `unreadable`, never an empty slice of the haystack that makes a decision look unhonored.
95
+ const planContents = (0, decision_coverage_support_cjs_1.loadPlanContents)(phaseDir);
96
+ if (planContents.kind === 'unreadable')
97
+ return unreadableVerify(`${planContents.span ?? phaseDir}: ${planContents.reason}`);
98
+ const summaryParts = (0, decision_coverage_support_cjs_1.loadSummaryContents)(phaseDir);
99
+ if (summaryParts.kind === 'unreadable')
100
+ return unreadableVerify(`${summaryParts.span ?? phaseDir}: ${summaryParts.reason}`);
101
+ const modifiedFiles = (0, decision_coverage_support_cjs_1.readModifiedFilesContent)(projectDir, summaryParts.value);
102
+ if (modifiedFiles.kind === 'unreadable')
103
+ return unreadableVerify(`${modifiedFiles.span ?? 'files_modified'}: ${modifiedFiles.reason}`);
104
+ const haystack = [
105
+ planContents.value.join('\n\n'),
106
+ summaryParts.value.join('\n\n'),
107
+ modifiedFiles.value,
108
+ (0, decision_coverage_support_cjs_1.phaseCommitMessages)(projectDir, phaseDir),
109
+ ].join('\n\n');
110
+ const notHonored = [];
111
+ let honored = 0;
112
+ for (const decision of decisions) {
113
+ if ((0, decision_coverage_support_cjs_1.decisionMentioned)(haystack, decision))
114
+ honored++;
115
+ else
116
+ notHonored.push({ id: decision.id, text: decision.text, category: decision.category });
117
+ }
118
+ return (0, gate_verdict_cjs_1.gateVerdict)(notHonored.length === 0 ? 'pass' : 'advisory', false, {
119
+ skipped: false,
120
+ blocking: false,
121
+ total: decisions.length,
122
+ honored,
123
+ not_honored: notHonored,
124
+ message: (0, decision_coverage_support_cjs_1.buildVerifyMessage)(notHonored),
125
+ });
126
+ }
@@ -0,0 +1,555 @@
1
+ "use strict";
2
+ /**
3
+ * Evaluation-scope resolver (#5164, epic #5056, ADR-5057 §4 second bullet): the ONE answer to
4
+ * "which commits and which file set does this gate or workflow step evaluate for this plan,
5
+ * phase or quick task".
6
+ *
7
+ * Before this module every consumer derived its own answer — `HEAD~1..HEAD`, `DIFF_BASE..HEAD`,
8
+ * `git log --all --grep` — and all of them disagreed with each other and with the work they
9
+ * meant to scope. Three constraints from the design lock hold here:
10
+ *
11
+ * 1. The scope is the UNION of each commit's own file set, never a range. A range keeps every
12
+ * interleaved non-phase commit in its window (measured on a 9,676-commit repo: the range
13
+ * held 23, 194 and 602 files where the union of the phase's commits held 9, 42 and 69).
14
+ * 2. Only commits reachable from the evaluated ref count (`git log --all` is gone). A commit
15
+ * that lives only on another branch is NAMED in `unreachable` and contributes nothing.
16
+ * 3. An empty union never becomes an empty scope that reviews nothing and reports success. It
17
+ * degrades to wider evidence (the phase-directory range) and SAYS so (`status: 'degraded'`,
18
+ * `reason`). What the union drops is named by path, not counted: `outsideUnion` (range
19
+ * minus union) and `missingOnDisk` (scoped paths that no longer exist).
20
+ *
21
+ * A phase's commits are the `## Task Commits` rows of its SUMMARY files (path-anchored: no commit
22
+ * message grep, the class re-fixed in #2989/#3191/#3503/#3995). A plan's commits are the
23
+ * reachable commits whose SUBJECT is `<type>(<phase>-<plan>):`, anchored and zero-padding
24
+ * tolerant (#4003). The row parse is a port of the byte-parity model from the closed PR #4127.
25
+ *
26
+ * Every git call goes through the `execGit` seam, which bounds each subprocess (its default
27
+ * budget); a timeout or a missing git is `unresolvable`, never a throw and never an empty scope.
28
+ *
29
+ * A gate module: imports no io module and writes nothing; `evaluateEvaluationScope` returns a
30
+ * `GateResult` the command router formats (`check evaluation-scope`).
31
+ */
32
+ var __importDefault = (this && this.__importDefault) || function (mod) {
33
+ return (mod && mod.__esModule) ? mod : { "default": mod };
34
+ };
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MAX_COMMITS_CEILING = exports.SCOPE_EXCLUSION_PATHSPECS = void 0;
37
+ exports.extractTaskCommitRefs = extractTaskCommitRefs;
38
+ exports.planSubjectPattern = planSubjectPattern;
39
+ exports.isSafeRefArgument = isSafeRefArgument;
40
+ exports.isSafeDateArgument = isSafeDateArgument;
41
+ exports.resolveEvaluationScope = resolveEvaluationScope;
42
+ exports.evaluateEvaluationScope = evaluateEvaluationScope;
43
+ const node_fs_1 = __importDefault(require("node:fs"));
44
+ const node_path_1 = __importDefault(require("node:path"));
45
+ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
46
+ const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
47
+ const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
48
+ const pattern_cjs_1 = require("./pattern.cjs");
49
+ const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
50
+ // ─── Constants ────────────────────────────────────────────────────────────────
51
+ /** Paths that are never part of a code-review or UI scope (planning artifacts, lockfiles). */
52
+ exports.SCOPE_EXCLUSION_PATHSPECS = Object.freeze([
53
+ ':!.planning/', ':!ROADMAP.md', ':!STATE.md',
54
+ ':!*-SUMMARY.md', ':!*-VERIFICATION.md', ':!*-PLAN.md',
55
+ ':!package-lock.json', ':!yarn.lock', ':!Gemfile.lock', ':!poetry.lock',
56
+ ]);
57
+ /** 41 chars per sha; 400 of them stay well under the 28K Windows-safe argv chunk. */
58
+ const SHA_CHUNK = 400;
59
+ const DEFAULT_MAX_COMMITS = 200;
60
+ const MAX_PATHSPECS = 20;
61
+ // ─── SUMMARY `## Task Commits` extraction (port of the #4127 model) ───────────
62
+ const TASK_COMMITS_HEADING = /^## Task Commits[ \t\r]*$/;
63
+ const NEXT_HEADING = /^## /;
64
+ const TASK_ROW_PREFIX = /^[ \t]*(?:[0-9]+\.|[-*])?[ \t]*\*\*Task[ \t]+[0-9]+:/;
65
+ const HEX_TOKEN = /`[0-9a-f]{7,40}`/g;
66
+ /**
67
+ * The commit ids a SUMMARY names, in document order, duplicates included. Section-scoped (every
68
+ * `## Task Commits` section), row-scoped (an optional list marker, the `**Task N:` label closed by
69
+ * its FIRST `**`) and backtick-anchored: only backticked lowercase hex AFTER that closing bold
70
+ * counts, so a sha quoted in prose or on the `**Plan metadata:**` line is not a task commit.
71
+ */
72
+ function extractTaskCommitRefs(text) {
73
+ const refs = [];
74
+ let inside = false;
75
+ for (const line of text.split('\n')) {
76
+ if (TASK_COMMITS_HEADING.test(line)) {
77
+ inside = true;
78
+ continue;
79
+ }
80
+ if (NEXT_HEADING.test(line)) {
81
+ inside = false;
82
+ continue;
83
+ }
84
+ if (!inside)
85
+ continue;
86
+ const row = TASK_ROW_PREFIX.exec(line);
87
+ if (!row)
88
+ continue;
89
+ const rest = line.slice(row[0].length);
90
+ const close = rest.indexOf('**');
91
+ if (close === -1)
92
+ continue;
93
+ for (const token of rest.slice(close + 2).match(HEX_TOKEN) ?? [])
94
+ refs.push(token.slice(1, -1));
95
+ }
96
+ return refs;
97
+ }
98
+ // ─── Plan-id → anchored subject pattern ───────────────────────────────────────
99
+ /**
100
+ * Tolerate zero padding in the leading integer of a phase or plan segment (#4003, #4619, #4748);
101
+ * everything after the integer (`A`, `.1.2`) is matched literally.
102
+ */
103
+ function paddedPattern(value) {
104
+ const m = /^(\d+)(.*)$/.exec(value);
105
+ if (!m)
106
+ return (0, pattern_cjs_1.escapeEre)(value);
107
+ const intPart = (m[1] ?? '').replace(/^0+(?=\d)/, '');
108
+ return `0*${(0, pattern_cjs_1.escapeEre)(intPart)}${(0, pattern_cjs_1.escapeEre)(m[2] ?? '')}`;
109
+ }
110
+ /**
111
+ * The anchored subject pattern for a plan id, or null when `planId` is empty, over-long or carries a
112
+ * control / whitespace character.
113
+ *
114
+ * A `<phase>-<plan>` id (`03-01`) is zero-padding tolerant: `feat(03-01):`, `test(3-1):` and
115
+ * `fix(03-01)!:` match, `feat(03-010):` does not. Any other id (a plan FILE NAME that does not follow
116
+ * the numbering) is matched LITERALLY — every ERE metacharacter escaped — so `x.*` or `a[b]` can only
117
+ * match a commit that names that exact id, and never widens the pattern.
118
+ */
119
+ function planSubjectPattern(planId) {
120
+ if (planId.length === 0 || planId.length > 200 || /[\s\x00-\x1f\x7f]/.test(planId))
121
+ return null;
122
+ const dash = planId.indexOf('-');
123
+ const phasePart = dash > 0 ? planId.slice(0, dash) : '';
124
+ const planPart = dash > 0 ? planId.slice(dash + 1) : '';
125
+ if (/^[0-9A-Za-z.]+$/.test(phasePart) && /^[0-9A-Za-z.]+$/.test(planPart)) {
126
+ return `^[a-z]+\\(${paddedPattern(phasePart)}-${paddedPattern(planPart)}\\)!?:`;
127
+ }
128
+ return `^[a-z]+\\(${(0, pattern_cjs_1.escapeEre)(planId)}\\)!?:`;
129
+ }
130
+ // ─── Git plumbing ─────────────────────────────────────────────────────────────
131
+ class ScopeUnreadable extends Error {
132
+ reason;
133
+ /** git's exit status when the failure was a plain non-zero exit (not a timeout or a missing git). */
134
+ exitCode;
135
+ constructor(reason, exitCode = null) {
136
+ super(reason);
137
+ this.reason = reason;
138
+ this.exitCode = exitCode;
139
+ }
140
+ }
141
+ function makeGit(projectDir, runner) {
142
+ return (args, cwd = projectDir) => {
143
+ const result = runner(args, { cwd });
144
+ if (result.timedOut)
145
+ throw new ScopeUnreadable('git-timeout');
146
+ if (result.exitCode === 127)
147
+ throw new ScopeUnreadable('git-unavailable');
148
+ if (result.exitCode !== 0)
149
+ throw new ScopeUnreadable(`git-failed:${args[0] ?? ''}`, result.exitCode);
150
+ return result.stdout;
151
+ };
152
+ }
153
+ /**
154
+ * Run a git question whose exit status 1 is a legitimate "no" (`merge-base --is-ancestor`,
155
+ * `rev-parse --verify --quiet`). Exit 1 returns `null`; every other failure — a timeout, a missing
156
+ * git, a bad object (exit 128) — still rethrows, so a hung or broken git is `unresolvable`, never a
157
+ * silent "no".
158
+ */
159
+ function gitOrNo(git, args, cwd) {
160
+ try {
161
+ return git(args, cwd);
162
+ }
163
+ catch (error) {
164
+ if (error instanceof ScopeUnreadable && error.exitCode === 1)
165
+ return null;
166
+ throw error;
167
+ }
168
+ }
169
+ function splitNul(output) {
170
+ return output.split('\0').filter((entry) => entry.length > 0);
171
+ }
172
+ function chunk(items, size) {
173
+ const out = [];
174
+ for (let i = 0; i < items.length; i += size)
175
+ out.push(items.slice(i, i + size));
176
+ return out;
177
+ }
178
+ function uniqueSorted(items) {
179
+ return [...new Set(items)].sort();
180
+ }
181
+ /** True when `candidate` cannot be read as a git option and carries no whitespace or control byte. */
182
+ function isSafeRefArgument(candidate) {
183
+ return candidate.length > 0 && candidate.length <= 256 && !candidate.startsWith('-') && !/[\s\x00-\x1f\x7f]/.test(candidate);
184
+ }
185
+ /** A git approxidate: words, digits and `- : . ,` only, so it can never read as an option or a pathspec. */
186
+ function isSafeDateArgument(candidate) {
187
+ return /^[A-Za-z0-9][A-Za-z0-9 .,:+-]{0,63}$/.test(candidate);
188
+ }
189
+ function isAncestor(git, sha, of) {
190
+ return gitOrNo(git, ['merge-base', '--is-ancestor', sha, of]) !== null;
191
+ }
192
+ // ─── The resolver ─────────────────────────────────────────────────────────────
193
+ function emptyScope(unit) {
194
+ return {
195
+ unit, status: 'resolved', source: 'none', reason: null, commits: [],
196
+ changedFiles: [], files: [], missingOnDisk: [], outsideUnion: [], unreachable: [], rangeBase: null,
197
+ };
198
+ }
199
+ function resolveEvaluationScope(projectDir, unit, options = {}) {
200
+ const scope = emptyScope(unit);
201
+ const git = makeGit(projectDir, options.execGit ?? shell_command_projection_cjs_1.execGit);
202
+ const ref = options.ref ?? 'HEAD';
203
+ try {
204
+ if (!isSafeRefArgument(ref))
205
+ throw new ScopeUnreadable('unsafe-ref');
206
+ if (options.since !== undefined && !isSafeRefArgument(options.since))
207
+ throw new ScopeUnreadable('unsafe-since');
208
+ git(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]);
209
+ const repoRoot = git(['rev-parse', '--show-toplevel']);
210
+ if (unit.kind === 'phase')
211
+ resolvePhase(projectDir, unit, scope, git, ref, options, repoRoot);
212
+ else
213
+ resolveBySubject(unit, scope, git, ref, options, repoRoot);
214
+ return scope;
215
+ }
216
+ catch (error) {
217
+ const reason = error instanceof ScopeUnreadable ? error.reason : 'resolver-error';
218
+ return { ...emptyScope(unit), status: 'unresolvable', reason };
219
+ }
220
+ }
221
+ /** A commit's own file set. `--first-parent`: a merge commit contributes its diff against parent 1. */
222
+ function commitFiles(git, sha) {
223
+ const out = git(['-c', 'core.quotepath=off', 'show', '--first-parent', '--pretty=format:', '--name-only', '-z', sha, '--', '.', ...exports.SCOPE_EXCLUSION_PATHSPECS]);
224
+ return splitNul(out);
225
+ }
226
+ function rangeFiles(git, base, tip) {
227
+ const out = git(['-c', 'core.quotepath=off', 'diff', '--name-only', '-z', `${base}..${tip}`, '--', '.', ...exports.SCOPE_EXCLUSION_PATHSPECS]);
228
+ return splitNul(out);
229
+ }
230
+ function finalizeFiles(scope, repoRoot, changed) {
231
+ scope.changedFiles = uniqueSorted(changed);
232
+ const existing = [];
233
+ const missing = [];
234
+ for (const file of scope.changedFiles) {
235
+ (node_fs_1.default.existsSync(node_path_1.default.join(repoRoot, file)) ? existing : missing).push(file);
236
+ }
237
+ scope.files = existing;
238
+ scope.missingOnDisk = missing;
239
+ }
240
+ function logFormat(withBody) {
241
+ return withBody ? '--format=%H%x1f%s%x1f%b%x1e' : '--format=%H%x1f%s%x1e';
242
+ }
243
+ function parseLog(out, withBody) {
244
+ const commits = [];
245
+ for (const record of out.split('\x1e')) {
246
+ const trimmed = record.replace(/^\n+/, '');
247
+ if (!trimmed)
248
+ continue;
249
+ const [sha, subject, body] = trimmed.split('\x1f');
250
+ if (!sha || !/^[0-9a-f]{40}$/.test(sha))
251
+ continue;
252
+ const commit = { sha, subject: subject ?? '' };
253
+ if (withBody)
254
+ commit.body = (body ?? '').trim();
255
+ commits.push(commit);
256
+ }
257
+ return commits;
258
+ }
259
+ function subjectsFor(git, shas, withBody, chunkSize) {
260
+ const out = [];
261
+ for (const group of chunk(shas, chunkSize)) {
262
+ out.push(...parseLog(git(['log', '--no-walk=unsorted', logFormat(withBody), ...group]), withBody));
263
+ }
264
+ return out;
265
+ }
266
+ /**
267
+ * The tip of `shas`: topological order lists a descendant before its ancestors, so the first
268
+ * commit of a walk started at all of them is one no other listed commit descends from. (Commit
269
+ * DATES are not used — commits made within one second tie, and a tie picks an arbitrary tip.)
270
+ */
271
+ function newestOf(git, shas, chunkSize) {
272
+ // More shas than fit one argv chunk are reduced chunk by chunk: the chunks' tips are themselves a
273
+ // (shorter) list, until one chunk holds them all. Nothing is silently dropped.
274
+ if (shas.length > chunkSize) {
275
+ const tips = chunk(shas, chunkSize).map((group) => newestOf(git, group, chunkSize));
276
+ return newestOf(git, tips, chunkSize);
277
+ }
278
+ const tip = git(['rev-list', '--topo-order', '-n', '1', ...shas]).split('\n').filter(Boolean)[0];
279
+ return tip ?? shas[0] ?? 'HEAD';
280
+ }
281
+ /** One clamp for every commit cap, so the CLI limit and the resolver's ceiling cannot diverge. */
282
+ exports.MAX_COMMITS_CEILING = 1000;
283
+ function clampMaxCommits(requested) {
284
+ return Math.max(1, Math.min(requested ?? DEFAULT_MAX_COMMITS, exports.MAX_COMMITS_CEILING));
285
+ }
286
+ /** git's empty tree (SHA-1 repositories): the base a range needs to include a root commit's own files. */
287
+ const EMPTY_TREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
288
+ /**
289
+ * The phase-start anchor on `ref`: the parent of the commit that first added anything under the
290
+ * phase dir. A phase directory that arrived with the repository's root commit has no parent, so the
291
+ * anchor is the empty tree — `<empty>..<ref>` then includes the root commit's own files.
292
+ */
293
+ function phaseRangeBase(git, phaseDir, ref) {
294
+ const first = git(['log', ref, '--format=%H', '--diff-filter=A', '--', '.'], phaseDir)
295
+ .split('\n').filter(Boolean).pop();
296
+ if (!first)
297
+ return null;
298
+ return gitOrNo(git, ['rev-parse', '--verify', '--quiet', `${first}^`]) ?? EMPTY_TREE;
299
+ }
300
+ function resolvePhase(projectDir, unit, scope, git, ref, options, repoRoot) {
301
+ let phaseDir = unit.phaseDir ?? '';
302
+ if (unit.phaseDir === undefined) {
303
+ const located = (0, gate_phase_context_cjs_1.resolvePhaseDir)(projectDir, unit.phase);
304
+ if (located.kind === 'unreadable')
305
+ throw new ScopeUnreadable(`phase-dir-lookup-failed:${located.reason}`);
306
+ if (located.kind === 'found')
307
+ phaseDir = located.value;
308
+ }
309
+ if (!phaseDir)
310
+ throw new ScopeUnreadable('phase-dir-not-found');
311
+ const refs = [];
312
+ let summaryCount = 0;
313
+ // The canonical LIVE summary set (root + nested, superseded excluded) from its single owner. A scan
314
+ // that did not see every summary (an existing nested plans/ that could not be read) is "could not
315
+ // look": a short summary set would narrow the phase's commits and report success (#5170).
316
+ const planScan = (0, gate_evidence_cjs_1.readPlanScanEvidence)(phaseDir);
317
+ if (planScan.kind === 'unreadable')
318
+ throw new ScopeUnreadable('phase-dir-unreadable');
319
+ const entries = [...planScan.value.summaryFiles].sort();
320
+ for (const name of entries) {
321
+ try {
322
+ refs.push(...extractTaskCommitRefs(node_fs_1.default.readFileSync(node_path_1.default.join(phaseDir, name), 'utf-8')));
323
+ summaryCount += 1;
324
+ }
325
+ catch {
326
+ throw new ScopeUnreadable(`summary-unreadable:${name}`);
327
+ }
328
+ }
329
+ // Resolve each listed id to a full sha reachable from the ref (and, for wave scoping, newer than `since`).
330
+ const shas = [];
331
+ const unreachable = [];
332
+ let sinceDropped = 0;
333
+ for (const id of [...new Set(refs)]) {
334
+ // Exit 1 = no object answers to the id (rebased away); exit 128 = an ambiguous short id. Both
335
+ // are named below, per id, and neither poisons the phase. A timeout still rethrows.
336
+ let full = '';
337
+ try {
338
+ full = gitOrNo(git, ['rev-parse', '--verify', '--quiet', `${id}^{commit}`]) ?? '';
339
+ }
340
+ catch (error) {
341
+ if (!(error instanceof ScopeUnreadable && error.exitCode === 128))
342
+ throw error;
343
+ }
344
+ if (!full || !isAncestor(git, full, ref)) {
345
+ unreachable.push(id);
346
+ continue;
347
+ }
348
+ if (options.since !== undefined && isAncestor(git, full, options.since)) {
349
+ sinceDropped += 1;
350
+ continue;
351
+ }
352
+ shas.push(full);
353
+ }
354
+ scope.unreachable = unreachable.sort();
355
+ scope.rangeBase = options.since ?? phaseRangeBase(git, phaseDir, ref);
356
+ const union = new Set();
357
+ const commits = [];
358
+ const withFiles = options.includeFiles ?? true;
359
+ // At least 2: a chunk of one never shrinks the list `newestOf` reduces.
360
+ const chunkSize = Math.max(2, options.shaChunk ?? SHA_CHUNK);
361
+ for (const meta of subjectsFor(git, [...new Set(shas)], options.includeBody === true, chunkSize)) {
362
+ const files = commitFiles(git, meta.sha);
363
+ for (const file of files)
364
+ union.add(file);
365
+ commits.push(withFiles ? { ...meta, files: [...files].sort() } : meta);
366
+ }
367
+ if (union.size > 0) {
368
+ scope.source = 'task-commits';
369
+ scope.commits = commits;
370
+ finalizeFiles(scope, repoRoot, union);
371
+ if (scope.rangeBase) {
372
+ const window = rangeFiles(git, scope.rangeBase, newestOf(git, commits.map((c) => c.sha), chunkSize));
373
+ scope.outsideUnion = uniqueSorted(window.filter((f) => !union.has(f)));
374
+ }
375
+ return;
376
+ }
377
+ // Empty union: the planning-only phase, a phase with no SUMMARY, or one whose ids are all gone.
378
+ const reason = summaryCount === 0 ? 'no-summary'
379
+ : refs.length === 0 ? 'no-task-commit-rows'
380
+ : shas.length === 0 ? (sinceDropped > 0 ? 'no-task-commits-since' : 'no-reachable-task-commits')
381
+ : 'empty-after-exclusions';
382
+ if (!scope.rangeBase)
383
+ throw new ScopeUnreadable(`${reason}:no-phase-start-anchor`);
384
+ scope.status = 'degraded';
385
+ scope.reason = reason;
386
+ scope.source = 'phase-range';
387
+ const max = clampMaxCommits(options.maxCommits);
388
+ scope.commits = parseLog(git(['log', `${scope.rangeBase}..${ref}`, logFormat(options.includeBody === true), '-n', String(max)]), options.includeBody === true);
389
+ finalizeFiles(scope, repoRoot, rangeFiles(git, scope.rangeBase, ref));
390
+ }
391
+ function resolveBySubject(unit, scope, git, ref, options, repoRoot) {
392
+ const withBody = options.includeBody === true;
393
+ // Refuse rather than truncate: a silently dropped positive pathspec narrows the match.
394
+ const pathspecs = options.pathspecs ?? [];
395
+ if (pathspecs.length > MAX_PATHSPECS)
396
+ throw new ScopeUnreadable('too-many-pathspecs');
397
+ const max = clampMaxCommits(options.maxCommits);
398
+ let range = ref;
399
+ if (options.milestoneBound === true) {
400
+ let tag = '';
401
+ try {
402
+ tag = git(['describe', '--tags', '--abbrev=0', ref]);
403
+ }
404
+ catch (error) {
405
+ // Exit 128 = no reachable tag → unbounded; a timeout or a missing git still rethrows.
406
+ if (!(error instanceof ScopeUnreadable && error.exitCode === 128))
407
+ throw error;
408
+ }
409
+ if (tag)
410
+ range = `${tag}..${ref}`;
411
+ }
412
+ const grepArgs = [];
413
+ let anchored = null;
414
+ if (unit.kind === 'plan') {
415
+ const pattern = planSubjectPattern(unit.planId);
416
+ if (pattern === null)
417
+ throw new ScopeUnreadable('invalid-plan-id');
418
+ grepArgs.push('--extended-regexp', `--grep=${pattern}`);
419
+ anchored = new RegExp(pattern);
420
+ scope.source = 'plan-subjects';
421
+ }
422
+ else {
423
+ if (!isSafeRefArgument(unit.id))
424
+ throw new ScopeUnreadable('invalid-quick-id');
425
+ grepArgs.push('--fixed-strings', `--grep=${unit.id}`);
426
+ scope.source = 'quick-subjects';
427
+ }
428
+ const sinceArgs = [];
429
+ if (options.committedSince !== undefined) {
430
+ if (!isSafeDateArgument(options.committedSince))
431
+ throw new ScopeUnreadable('unsafe-committed-since');
432
+ sinceArgs.push(`--since=${options.committedSince}`);
433
+ }
434
+ const out = git(['log', range, logFormat(withBody), '-n', String(max * 2), ...sinceArgs, ...grepArgs, ...(pathspecs.length > 0 ? ['--', ...pathspecs] : [])]);
435
+ // `--grep` matches ANY message line; a plan lookup is subject-anchored, so filter on the subject.
436
+ let commits = parseLog(out, withBody).filter((c) => anchored === null || anchored.test(c.subject)).slice(0, max);
437
+ const since = options.since;
438
+ if (since !== undefined)
439
+ commits = commits.filter((c) => !isAncestor(git, c.sha, since));
440
+ if (options.commitsOnly === true) {
441
+ scope.commits = commits;
442
+ if (commits.length === 0)
443
+ scope.reason = 'no-matching-commits';
444
+ return;
445
+ }
446
+ const withFiles = options.includeFiles ?? false;
447
+ const union = new Set();
448
+ const result = [];
449
+ for (const meta of commits) {
450
+ const files = commitFiles(git, meta.sha);
451
+ for (const file of files)
452
+ union.add(file);
453
+ result.push(withFiles ? { ...meta, files: [...files].sort() } : meta);
454
+ }
455
+ scope.commits = result;
456
+ finalizeFiles(scope, repoRoot, union);
457
+ // Found nothing: a truthful "none" (status stays `resolved` — git was read), said out loud so a
458
+ // caller can tell "no such commits" from "an empty scope that was never computed". Commits whose
459
+ // every path is excluded (planning-only) are an empty scope too, and say so.
460
+ if (result.length === 0)
461
+ scope.reason = 'no-matching-commits';
462
+ else if (scope.changedFiles.length === 0)
463
+ scope.reason = 'empty-after-exclusions';
464
+ }
465
+ // ─── The `check evaluation-scope` verb ────────────────────────────────────────
466
+ const VALUE_FLAGS = new Set(['--phase', '--phase-dir', '--plan', '--quick', '--since', '--ref', '--pathspec', '--committed-since', '--max-commits']);
467
+ const BOOLEAN_FLAGS = new Set(['--milestone-bound', '--include-body', '--include-files', '--commits-only']);
468
+ function parseScopeArgs(args) {
469
+ const values = new Map();
470
+ const flags = new Set();
471
+ for (let i = 0; i < args.length; i += 1) {
472
+ const arg = args[i] ?? '';
473
+ if (BOOLEAN_FLAGS.has(arg)) {
474
+ flags.add(arg);
475
+ continue;
476
+ }
477
+ if (!VALUE_FLAGS.has(arg))
478
+ return `unknown argument: ${arg}`;
479
+ const value = args[i + 1];
480
+ if (value === undefined)
481
+ return `${arg} requires a value`;
482
+ i += 1;
483
+ values.set(arg, [...(values.get(arg) ?? []), value]);
484
+ }
485
+ const one = (flag) => values.get(flag)?.[0];
486
+ const phase = one('--phase');
487
+ const phaseDir = one('--phase-dir');
488
+ const plan = one('--plan');
489
+ const quick = one('--quick');
490
+ const given = [phase, phaseDir, plan, quick].filter((v) => v !== undefined).length;
491
+ if (given !== 1)
492
+ return 'exactly one of --phase <phase>, --phase-dir <dir>, --plan <phase>-<plan> or --quick <id> is required';
493
+ let unit;
494
+ if (quick !== undefined)
495
+ unit = { kind: 'quick', id: quick };
496
+ else if (plan !== undefined)
497
+ unit = { kind: 'plan', planId: plan };
498
+ else if (phaseDir !== undefined)
499
+ unit = { kind: 'phase', phase: '', phaseDir };
500
+ else
501
+ unit = { kind: 'phase', phase: phase ?? '' };
502
+ const options = {};
503
+ const ref = one('--ref');
504
+ const since = one('--since');
505
+ const pathspecs = values.get('--pathspec');
506
+ if (ref !== undefined)
507
+ options.ref = ref;
508
+ if (since !== undefined)
509
+ options.since = since;
510
+ if (pathspecs !== undefined)
511
+ options.pathspecs = pathspecs;
512
+ const committedSince = one('--committed-since');
513
+ if (committedSince !== undefined)
514
+ options.committedSince = committedSince;
515
+ const maxCommits = one('--max-commits');
516
+ if (maxCommits !== undefined) {
517
+ if (!/^[1-9][0-9]{0,3}$/.test(maxCommits) || Number(maxCommits) > exports.MAX_COMMITS_CEILING) {
518
+ return `--max-commits must be an integer from 1 to ${exports.MAX_COMMITS_CEILING}, got: ${maxCommits}`;
519
+ }
520
+ options.maxCommits = Number(maxCommits);
521
+ }
522
+ if (flags.has('--milestone-bound'))
523
+ options.milestoneBound = true;
524
+ if (flags.has('--include-body'))
525
+ options.includeBody = true;
526
+ if (flags.has('--include-files'))
527
+ options.includeFiles = true;
528
+ if (flags.has('--commits-only'))
529
+ options.commitsOnly = true;
530
+ return { unit, options };
531
+ }
532
+ /** `check evaluation-scope` — argv after the verb. A resolver, not a policy: it never blocks. */
533
+ function evaluateEvaluationScope(input) {
534
+ const parsed = parseScopeArgs(input.args);
535
+ if (typeof parsed === 'string') {
536
+ return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.USAGE, `check evaluation-scope: ${parsed}`);
537
+ }
538
+ const options = input.execGit ? { ...parsed.options, execGit: input.execGit } : parsed.options;
539
+ let unit = parsed.unit;
540
+ if (unit.kind === 'phase' && unit.phaseDir !== undefined) {
541
+ // `--phase-dir` is caller-supplied: it must stay inside the project (realpath containment).
542
+ const contained = (0, gate_phase_context_cjs_1.resolveContainedPath)(unit.phaseDir, input.projectDir);
543
+ if ((0, gate_verdict_cjs_1.isGateUsageFailure)(contained))
544
+ return contained;
545
+ unit = { kind: 'phase', phase: '', phaseDir: contained };
546
+ }
547
+ const scope = resolveEvaluationScope(input.projectDir, unit, options);
548
+ // An unresolvable scope is "could not look" (a missing git, a timeout): outcome `unreadable`, exit
549
+ // UNAVAILABLE, never a pass-shaped exit 0 (#5170). The payload is unchanged.
550
+ if (scope.status === 'unresolvable')
551
+ return (0, gate_verdict_cjs_1.gateUnreadable)(false, { ...scope });
552
+ // `pass` only for a scope with nothing to explain; an empty-but-resolved scope (it carries a reason) is advisory.
553
+ const outcome = scope.status === 'degraded' || scope.reason !== null ? 'advisory' : 'pass';
554
+ return (0, gate_verdict_cjs_1.gateVerdict)(outcome, false, { ...scope });
555
+ }