@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
@@ -71,7 +71,7 @@ const planningScopeMod = require("./planning-scope.cjs");
71
71
  const { SCOPE } = planningScopeMod;
72
72
  // eslint-disable-next-line @typescript-eslint/no-require-imports
73
73
  const verificationMod = require("./verification.cjs");
74
- const { readVerificationStatus } = verificationMod;
74
+ const { readVerificationStatus, VerificationStatusError, failOnVerificationStatusError } = verificationMod;
75
75
  // eslint-disable-next-line @typescript-eslint/no-require-imports
76
76
  const phaseIdMod = require("./phase-id.cjs");
77
77
  const { phaseKeyFromDir, phaseKeyFromToken, phaseMarkdownRegexSource } = phaseIdMod;
@@ -304,6 +304,8 @@ function containmentEnforcingVerificationFs(planningRoot) {
304
304
  return node_fs_1.default.readFileSync(filePath, encoding);
305
305
  },
306
306
  statSync(filePath) {
307
+ // #5118: a code-less containment throw here is NOT "phase directory
308
+ // not found" — readVerificationStatus falls through to `missing`.
307
309
  assertContained(filePath);
308
310
  return node_fs_1.default.statSync(filePath);
309
311
  },
@@ -917,7 +919,24 @@ function extractDependencyTokens(sectionBody) {
917
919
  const m = DEPENDS_ON_LINE_RE.exec(sectionBody);
918
920
  if (!m)
919
921
  return [];
920
- return sortedUnique([...m[1].matchAll(/\d+(?:\.\d+)*/g)].map((t) => t[0]));
922
+ // #4764: phase REFERENCES, not digit runs — the same prose-anchored grammar
923
+ // init.manager's dep_phases extraction uses (owner: phase-id.cts's
924
+ // PHASE_DEP_REF_SOURCE). The whole-field token scrape this replaces pulled
925
+ // calendar dates, git shas and ledger ids in as dependencies. The grammar's
926
+ // capture group 1 already excludes the "Phase(s)" anchor word, so no
927
+ // prefix-strip literal is needed here. Self-exclusion (init.manager drops
928
+ // the row's own number) is deliberately NOT applied: this reader has no row
929
+ // context at the extraction site and reports informationally, it does not
930
+ // gate.
931
+ const refRe = new RegExp(phaseIdMod.PHASE_DEP_REF_SOURCE, 'gi');
932
+ const tokenRe = new RegExp(phaseIdMod.PHASE_NUMBER_TOKEN_SOURCE, 'g');
933
+ const tokens = [];
934
+ let refMatch;
935
+ while ((refMatch = refRe.exec(m[1])) !== null) {
936
+ for (const t of refMatch[1].matchAll(tokenRe))
937
+ tokens.push(t[0]);
938
+ }
939
+ return sortedUnique(tokens);
921
940
  }
922
941
  /**
923
942
  * This phase's own ROADMAP.md section body — milestone-scoped via the SAME
@@ -999,6 +1018,17 @@ function buildPhaseGoalAndDependencies(cwd, roadmapDoc, phaseId, phaseDirLabel,
999
1018
  }
1000
1019
  // ─── Entry points ─────────────────────────────────────────────────────────────
1001
1020
  function buildPlanningInspect(cwd) {
1021
+ return buildPlanningInspectResult(cwd).payload;
1022
+ }
1023
+ /**
1024
+ * #5118: the inspect payload plus the first verification report whose
1025
+ * `status` is outside the closed set (`statusError`, or `null`) — the
1026
+ * aggregate CARRIES the owner's error in its own result, and `planning
1027
+ * inspect` fails with it instead of printing an answer computed over a
1028
+ * report the owner refused.
1029
+ */
1030
+ function buildPlanningInspectResult(cwd) {
1031
+ let statusError = null;
1002
1032
  const diagnostics = [];
1003
1033
  const paths = planningPaths(cwd);
1004
1034
  const planningExists = node_fs_1.default.existsSync(paths.planning);
@@ -1061,8 +1091,8 @@ function buildPlanningInspect(cwd) {
1061
1091
  // GAP 2 (#2790 follow-up security review): `readVerificationStatus`
1062
1092
  // (`src/verification.cts`) is a shared owner with its own unguarded
1063
1093
  // `readFileSync` — a `*-VERIFICATION.md` symlinked outside the planning
1064
- // root would leak an unrecognized `status:` value verbatim via its
1065
- // "Unexpected verification status '<value>'" `next_action` string. Fixed
1094
+ // root would leak its unrecognized `status:` value verbatim (today via
1095
+ // the VerificationStatusError message, #5118). Fixed
1066
1096
  // from THIS consumer's side via the injectable `opts.fs` seam that
1067
1097
  // function already exposes, never by touching its signature — see
1068
1098
  // `containmentEnforcingVerificationFs`'s doc comment. This same seam's
@@ -1077,9 +1107,22 @@ function buildPlanningInspect(cwd) {
1077
1107
  // (`superseded`) rather than document text, and GAP 1's directory
1078
1108
  // containment check already covers the escaped-DIRECTORY case for it —
1079
1109
  // so it needs no fix of its own.
1080
- const verification = readVerificationStatus(phaseDir, {
1081
- fs: containmentEnforcingVerificationFs(paths.planning),
1082
- });
1110
+ // #5118: an out-of-set report status is carried, not thrown past the
1111
+ // other phases — the row reads `status: null` and the command fails with
1112
+ // the first such error once the payload is built.
1113
+ let verification;
1114
+ try {
1115
+ verification = readVerificationStatus(phaseDir, {
1116
+ fs: containmentEnforcingVerificationFs(paths.planning),
1117
+ });
1118
+ }
1119
+ catch (err) {
1120
+ if (!(err instanceof VerificationStatusError))
1121
+ throw err;
1122
+ if (statusError === null)
1123
+ statusError = err;
1124
+ verification = { status: null, next_action: err.message, route: '' };
1125
+ }
1083
1126
  const token = /^(\d+(?:\.\d+)*)/.exec(phase.dir);
1084
1127
  const phaseId = token ? token[1] : null;
1085
1128
  const { goal, dependencies } = buildPhaseGoalAndDependencies(cwd, roadmapDoc, phaseId, phase.dir, diagnostics);
@@ -1112,6 +1155,8 @@ function buildPlanningInspect(cwd) {
1112
1155
  verification: {
1113
1156
  status: verification.status,
1114
1157
  next_action: verification.next_action ?? null,
1158
+ // #5118: additive — the bare command the owner routes this status to.
1159
+ route: verification.route,
1115
1160
  },
1116
1161
  roadmap_acceptance: {
1117
1162
  checkbox: checkboxByPhaseKey.has(phaseKeyFromDir(phase.dir))
@@ -1133,7 +1178,7 @@ function buildPlanningInspect(cwd) {
1133
1178
  const phaseScope = worstScope(snapshot.phaseDirs.scope, snapshot.phases.scope, ...phaseRows.map((p) => p.scope));
1134
1179
  const acceptedPhases = makeFraction(phaseRows.filter((p) => p.complete).length, phaseRows.length, phaseScope, 'progress.accepted_phases', diagnostics);
1135
1180
  const completedPlans = makeFraction(phaseRows.reduce((sum, p) => sum + p.summary_count, 0), phaseRows.reduce((sum, p) => sum + p.plan_count, 0), phaseScope, 'progress.completed_plans', diagnostics);
1136
- return {
1181
+ const payload = {
1137
1182
  schema_version: PLANNING_INSPECT_SCHEMA_VERSION,
1138
1183
  generated_from: {
1139
1184
  cwd: toPosix(cwd),
@@ -1161,6 +1206,7 @@ function buildPlanningInspect(cwd) {
1161
1206
  },
1162
1207
  diagnostics,
1163
1208
  };
1209
+ return { payload, statusError };
1164
1210
  }
1165
1211
  /**
1166
1212
  * `planning inspect` — emit the schema-v1 snapshot.
@@ -1170,7 +1216,12 @@ function buildPlanningInspect(cwd) {
1170
1216
  * transparently on stdout. Bypassing `output()` would lose that for free.
1171
1217
  */
1172
1218
  function cmdPlanningInspect(cwd, raw) {
1173
- output(buildPlanningInspect(cwd), raw);
1219
+ const { payload, statusError } = buildPlanningInspectResult(cwd);
1220
+ // #5118: a read-only aggregate over a refused report prints nothing and
1221
+ // fails with the error's own reason (`verification_status_invalid`).
1222
+ if (statusError)
1223
+ failOnVerificationStatusError(statusError);
1224
+ output(payload, raw);
1174
1225
  }
1175
1226
  const planningInspect = {
1176
1227
  PLANNING_INSPECT_SCHEMA_VERSION,
@@ -124,10 +124,12 @@ function buildPhaseSnapshot(phasesDir, dir, convention) {
124
124
  // report exactly like its legacy twin.
125
125
  const completionResult = isPhaseComplete(fullPhaseDir, { convention });
126
126
  const scanResult = scanPhasePlans(fullPhaseDir);
127
+ const statusError = completionResult.value.statusError;
127
128
  return {
128
129
  dir,
129
130
  complete: completionResult.value.complete,
130
131
  verificationStatus: completionResult.value.verification.status,
132
+ verificationStatusError: statusError ? { file: statusError.file, message: statusError.message } : null,
131
133
  planCount: scanResult.planCount,
132
134
  summaryCount: scanResult.summaryCount,
133
135
  scope: worstScope(completionResult.scope, scanResult.scope),
@@ -882,6 +884,22 @@ function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath, convention)
882
884
  // and `bracketGroup` is 0. Inferring the convention from a matched bracket's
883
885
  // shape would run a repo-failing check against a repo that never opted in.
884
886
  const bracketGroup = convention === 'bracket' ? 1 : 0;
887
+ // #5007 (Phase 6 / ADR-4910 §8): already composed from `phaseHeadingPrefixSrcFor`
888
+ // directly, not a hand-rolled literal — the site's only marked defect was
889
+ // this doc comment's own prose quoting the pattern verbatim (fixed above),
890
+ // which is exactly what the drift detector matched. Audited against the
891
+ // design doc's suggested further swap onto `buildPhaseHeadingScanRegex`
892
+ // (the same owner `bracketGroup`/`phaseNumGroup` above hand-derives) and
893
+ // deliberately did NOT make that swap: the owner's regex requires a title
894
+ // (`${OPTIONAL_PHASE_TAG_SOURCE}\s*:\s*([^\n]+)` — at least one non-newline
895
+ // character after the colon), while this site's own tail is bare `:` with
896
+ // no title requirement. A live probe against this function (a `### Phase 3:`
897
+ // heading with no title text) confirms the current, pre-migration behavior
898
+ // DOES count that phase — swapping to the owner would silently drop it from
899
+ // W026's scan, and no existing test (including the ADR-612 census test
900
+ // pinned to this site by name) would catch that narrowing, since the census
901
+ // only pins `phaseHeadingPrefixSrcFor`'s own output, not this regex's tail.
902
+ // Kept as its own composition to preserve this exact byte-for-behavior.
885
903
  const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention, Boolean(bracketGroup))}(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
886
904
  const value = [];
887
905
  for (const m of scoped.matchAll(phasePattern)) {
@@ -114,13 +114,18 @@ const PLANNING_LOCK_RETRY_ERRNOS = new Set([
114
114
  * two-readers-two-bases lesson).
115
115
  */
116
116
  function resolveEnvWorkstream() {
117
- return process.env['GSD_WORKSTREAM'] ?? null;
117
+ const value = process.env['GSD_WORKSTREAM']?.trim();
118
+ return value || null;
118
119
  }
119
120
  function planningDir(cwd, ws, project) {
120
121
  if (project === undefined)
121
- project = process.env['GSD_PROJECT'] ?? null;
122
+ project = process.env['GSD_PROJECT']?.trim() || null;
123
+ else if (typeof project === 'string')
124
+ project = project.trim() || null;
122
125
  if (ws === undefined)
123
126
  ws = resolveEnvWorkstream();
127
+ else if (typeof ws === 'string')
128
+ ws = ws.trim() || null;
124
129
  // Reject path separators and traversal components in project/workstream names
125
130
  const BAD_SEGMENT = /[/\\]|\.\./;
126
131
  if (project && BAD_SEGMENT.test(project)) {
@@ -139,60 +144,73 @@ function planningDir(cwd, ws, project) {
139
144
  function planningRoot(cwd) {
140
145
  return node_path_1.default.join(cwd, '.planning');
141
146
  }
147
+ const CONFIG_VALUE_ABSENT = Object.freeze({ present: false, value: undefined });
142
148
  /**
143
- * #3972: the ONE owner of "is this planning scope opted out of worktrees?" —
144
- * the effective `workflow.use_worktrees === false` read every
145
- * isolation-deciding surface must share (config-get's merged view is the
146
- * contract). Ladder: the scoped config's OWN key wins (planningDir is
147
- * project- and workstream-aware); otherwise the flat root's key, but only
148
- * under the GSD_WORKSTREAM env gate — config-get deliberately does NOT
149
- * inherit root under GSD_PROJECT alone, and this read must not diverge
150
- * (#3963). Strict `=== false` (never coerced); any read failure degrades to
151
- * "not opted out" (worktrees on — the fail-safe direction: the guard keeps
152
- * enforcing). Direct file reads only — never loadConfig, which normalizes
153
- * and rewrites config on paths that back sentinel writes.
149
+ * The value one config file sets at `keyPath`, if it sets one at all. Every
150
+ * segment must be an OWN property of a plain (non-null, non-array) object, so
151
+ * an absent, unreadable, or unparseable file — or a non-object anywhere on the
152
+ * path — does not set the key.
154
153
  */
155
- function worktreesOptedOut(cwd) {
156
- // #3972 review: the WHOLE body is guarded — planningDir/planningRoot
157
- // themselves throw on a GSD_PROJECT/GSD_WORKSTREAM value containing path
158
- // separators or `..`, and this contract ("any failure degrades to not
159
- // opted out — worktrees on, keep enforcing") must hold for that shape too.
154
+ function ownConfigValue(configPath, keyPath) {
155
+ let node;
160
156
  try {
161
- return worktreesOptedOutUnguarded(cwd);
157
+ node = JSON.parse(String(node_fs_1.default.readFileSync(configPath, 'utf8')));
162
158
  }
163
159
  catch {
164
- return false;
160
+ return CONFIG_VALUE_ABSENT;
161
+ }
162
+ for (const key of keyPath) {
163
+ if (node === null || typeof node !== 'object' || Array.isArray(node))
164
+ return CONFIG_VALUE_ABSENT;
165
+ if (!Object.prototype.hasOwnProperty.call(node, key))
166
+ return CONFIG_VALUE_ABSENT;
167
+ node = node[key];
165
168
  }
169
+ return { present: true, value: node };
166
170
  }
167
- function worktreesOptedOutUnguarded(cwd) {
168
- const readCfg = (p) => {
169
- try {
170
- return JSON.parse(String(node_fs_1.default.readFileSync(p, 'utf8')));
171
- }
172
- catch {
173
- return null;
171
+ /**
172
+ * #3972/#4975: the ONE scope-aware config read for surfaces that must answer
173
+ * with the value `config-get <keyPath>` reports but may not call loadConfig
174
+ * (it normalizes and rewrites config.json, spawns git, and prints warnings).
175
+ * Ladder: the scoped config's OWN key wins (planningDir is project- and
176
+ * workstream-aware); otherwise the flat root's key, but only under the
177
+ * GSD_WORKSTREAM env gate — config-get deliberately does NOT inherit root
178
+ * under GSD_PROJECT alone, and no reader of this ladder may diverge (#3963).
179
+ * An unreadable or unparseable file sets nothing, so the ladder moves on:
180
+ * under GSD_WORKSTREAM a broken workstream config inherits the root's key.
181
+ * That is the one place this read and config-get part ways — for a scoped
182
+ * config.json that exists but cannot be read or parsed, config-get fails
183
+ * with CONFIG_PARSE_FAILED instead of reporting a value.
184
+ * The value is returned uncoerced; each caller applies its own strict
185
+ * comparison. Never throws: planningDir/planningRoot reject a
186
+ * GSD_PROJECT/GSD_WORKSTREAM value containing path separators or `..`, and
187
+ * that shape resolves to "not present".
188
+ */
189
+ function readScopedConfigValue(cwd, keyPath) {
190
+ try {
191
+ const scoped = ownConfigValue(node_path_1.default.join(planningDir(cwd), 'config.json'), keyPath);
192
+ if (scoped.present)
193
+ return scoped;
194
+ if (resolveEnvWorkstream() !== null) {
195
+ return ownConfigValue(node_path_1.default.join(planningRoot(cwd), 'config.json'), keyPath);
174
196
  }
175
- };
176
- const ownKey = (cfg) => {
177
- if (cfg === null || typeof cfg !== 'object')
178
- return { present: false, value: undefined };
179
- const wf = cfg.workflow;
180
- if (wf === null || typeof wf !== 'object' || Array.isArray(wf))
181
- return { present: false, value: undefined };
182
- const wfRec = wf;
183
- return Object.prototype.hasOwnProperty.call(wfRec, 'use_worktrees')
184
- ? { present: true, value: wfRec['use_worktrees'] }
185
- : { present: false, value: undefined };
186
- };
187
- const scoped = ownKey(readCfg(node_path_1.default.join(planningDir(cwd), 'config.json')));
188
- if (scoped.present)
189
- return scoped.value === false;
190
- if (process.env['GSD_WORKSTREAM']) {
191
- const root = ownKey(readCfg(node_path_1.default.join(planningRoot(cwd), 'config.json')));
192
- if (root.present)
193
- return root.value === false;
194
197
  }
195
- return false;
198
+ catch {
199
+ // Degrade to "not present" — see the contract above.
200
+ }
201
+ return CONFIG_VALUE_ABSENT;
202
+ }
203
+ /**
204
+ * #3972: the ONE owner of "is this planning scope opted out of worktrees?" —
205
+ * the effective `workflow.use_worktrees === false` read every
206
+ * isolation-deciding surface must share (config-get's merged view is the
207
+ * contract), resolved on the readScopedConfigValue ladder. Strict `=== false`
208
+ * (never coerced); a config that cannot be read never opts out by itself
209
+ * (worktrees on — the fail-safe direction: the guard keeps enforcing).
210
+ */
211
+ function worktreesOptedOut(cwd) {
212
+ const { present, value } = readScopedConfigValue(cwd, ['workflow', 'use_worktrees']);
213
+ return present && value === false;
196
214
  }
197
215
  /**
198
216
  * #612: resolve `phase_id_convention` with the SAME workstream->root federation
@@ -300,8 +318,8 @@ function listAvailableWorkstreams(cwd) {
300
318
  function quickDirFrom(planningBase) {
301
319
  return node_path_1.default.join(planningBase, 'quick');
302
320
  }
303
- // #4256: the todos directory — deliberately ROOT-SCOPED, unlike every other
304
- // planningPaths key. Todos are shared project state by construction: the
321
+ // #4256: the todos directory — deliberately ROOT-SCOPED, like debug (#5042).
322
+ // Todos are shared project state by construction: the
305
323
  // migrateToWorkstreams contract keeps them among the shared files that "stay
306
324
  // in place" at .planning/todos/ (workstream.cts), and every workflow writer
307
325
  // writes that literal cwd-relative root path. The six todos readers
@@ -325,6 +343,14 @@ function todosDirFrom(planningBase) {
325
343
  function todosDir(cwd) {
326
344
  return todosDirFrom(planningRoot(cwd));
327
345
  }
346
+ // Debug sessions are shared project state: the workflow and debugger agent
347
+ // write them under the root .planning/debug directory in every workstream.
348
+ function debugDirFrom(planningBase) {
349
+ return node_path_1.default.join(planningBase, 'debug');
350
+ }
351
+ function debugDir(cwd) {
352
+ return debugDirFrom(planningRoot(cwd));
353
+ }
328
354
  function planningPaths(cwd, ws) {
329
355
  const base = planningDir(cwd, ws);
330
356
  return {
@@ -335,14 +361,13 @@ function planningPaths(cwd, ws) {
335
361
  config: node_path_1.default.join(base, 'config.json'),
336
362
  phases: node_path_1.default.join(base, 'phases'),
337
363
  requirements: node_path_1.default.join(base, 'REQUIREMENTS.md'),
338
- // #3149: the debug-session directory. Single source for both `state.load`'s
339
- // `debug_dir` field and `init.debug`'s — previously each composed its own
340
- // `path.join(planning, 'debug')` (DEFECT.GENERATIVE-FIX).
341
- debug: node_path_1.default.join(base, 'debug'),
364
+ // #3149/#5042: the debug-session directory shared by state.load,
365
+ // init.debug, and the root-scoped writers.
366
+ debug: debugDir(cwd),
342
367
  // #2142: quick-task directory, composed via the shared quickDirFrom helper.
343
368
  quick: quickDirFrom(base),
344
- // #4256: todos directory — deliberately ROOT-scoped while the rest of
345
- // this record follows the active workstream/project (todos are shared
369
+ // #4256: todos directory — deliberately ROOT-scoped like debug, while
370
+ // most other keys follow the active workstream/project (todos are shared
346
371
  // project state per the migrateToWorkstreams contract), composed via the
347
372
  // shared todosDir helper so this key and every direct caller agree.
348
373
  todos: todosDir(cwd),
@@ -607,6 +632,7 @@ function findContextMdIn(absDirOrFiles) {
607
632
  }
608
633
  module.exports = {
609
634
  worktreesOptedOut,
635
+ readScopedConfigValue,
610
636
  createPlanningWorkspace,
611
637
  createSharedPointerAdapter,
612
638
  createSessionScopedPointerAdapter,
@@ -618,6 +644,8 @@ module.exports = {
618
644
  listAvailableWorkstreams,
619
645
  planningPaths,
620
646
  quickDirFrom,
647
+ debugDirFrom,
648
+ debugDir,
621
649
  todosDirFrom,
622
650
  todosDir,
623
651
  withPlanningLock,
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * PR-branch `.planning/` path classification patterns — single source of
4
+ * truth (ADR-4910 §8, epic #4906 Phase 5; issues #4605, #4606).
5
+ *
6
+ * `gsd-core/workflows/pr-branch.md` cannot `require()` this module at
7
+ * runtime — a workflow markdown file has no module system, and its bash
8
+ * steps are copy-pasted text executed by whatever shell interprets the
9
+ * workflow. So the three exported constants below are mirrored VERBATIM
10
+ * into that file's `TRANSIENT_DIRS=`, `STRUCTURAL_RE=`, and
11
+ * `MILESTONE_PHASES_RE=` bash variable assignments, and
12
+ * `scripts/lint-pr-branch-pattern-drift.cjs` asserts the mirror never
13
+ * drifts from this file (compiled to `gsd-core/bin/lib/pr-branch-patterns.cjs`)
14
+ * as part of `npm run lint:ci`. Change a value here, then update
15
+ * `gsd-core/workflows/pr-branch.md` to match, or the drift guard fails.
16
+ *
17
+ * This module does not read the filesystem and has no dependencies (a leaf
18
+ * module, mirroring `src/secrets.cts` / `src/planning-scope.cts`).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.MILESTONE_PHASES_RE_SRC = exports.STRUCTURAL_RE_SRC = exports.TRANSIENT_DIRS_SRC = void 0;
22
+ /**
23
+ * Space-separated list of `.planning/` subdirectory names that are reviewer
24
+ * noise (PLAN.md, SUMMARY.md, CONTEXT.md, RESEARCH.md, and friends) —
25
+ * filtered out of a PR branch in BOTH default and strict mode.
26
+ *
27
+ * Mirrored verbatim into `gsd-core/workflows/pr-branch.md`'s
28
+ * `TRANSIENT_DIRS="..."` assignment.
29
+ */
30
+ exports.TRANSIENT_DIRS_SRC = 'phases quick research threads todos debug seeds codebase ui-reviews';
31
+ /**
32
+ * Structural planning files — repository planning state, preserved in
33
+ * default mode and filtered out in strict mode. Anchored on both
34
+ * alternatives so `.planning/STATEX.md` and `.planning/STATE.md.bak` are
35
+ * NOT treated as structural. The `milestones` alternative matches only
36
+ * FILES directly under `.planning/milestones/` (e.g. `v1.0-ROADMAP.md`) —
37
+ * not a `<milestone>-phases/` subdirectory nested there. That subdirectory
38
+ * is reviewer noise, not structural state (#4605); it falls through to
39
+ * `MILESTONE_PHASES_RE_SRC` below instead.
40
+ *
41
+ * Mirrored verbatim into `gsd-core/workflows/pr-branch.md`'s
42
+ * `STRUCTURAL_RE="..."` assignment.
43
+ */
44
+ exports.STRUCTURAL_RE_SRC = '^\\.planning/(STATE|ROADMAP|MILESTONES|PROJECT|REQUIREMENTS)\\.md$|^\\.planning/milestones/[^/]+\\.md$';
45
+ /**
46
+ * Milestone-scoped phase-plan directories — the same reviewer noise as
47
+ * `TRANSIENT_DIRS_SRC`'s `phases` entry, but nested per-milestone once a
48
+ * project has passed at least one milestone:
49
+ * `.planning/milestones/<milestone>-phases/`. The milestone slug (`v1.0`,
50
+ * `m2`, ...) varies per project, so this is declared as a shape, not a
51
+ * literal path — a single path segment standing in for the slug, anchored
52
+ * the same way `STRUCTURAL_RE_SRC`'s alternatives are (#4605).
53
+ *
54
+ * Mirrored verbatim into `gsd-core/workflows/pr-branch.md`'s
55
+ * `MILESTONE_PHASES_RE="..."` assignment.
56
+ */
57
+ exports.MILESTONE_PHASES_RE_SRC = '^\\.planning/milestones/[^/]+-phases/';
@@ -27,6 +27,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
28
  exports.sha256File = sha256File;
29
29
  exports.findPristineByHash = findPristineByHash;
30
+ exports.isCleanRelativePosixPath = isCleanRelativePosixPath;
31
+ exports.gitExec = gitExec;
30
32
  exports.findPristineInGit = findPristineInGit;
31
33
  const node_fs_1 = __importDefault(require("node:fs"));
32
34
  const node_path_1 = __importDefault(require("node:path"));
@@ -125,6 +127,14 @@ const GIT_MAX_COMMITS_PER_FILE = 100;
125
127
  const GIT_SUBPROCESS_TIMEOUT_MS = 10_000;
126
128
  /** git log --format=%H output cap; 100 full shas are ~4 KB, this is headroom. */
127
129
  const GIT_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
130
+ /**
131
+ * #5105 review finding 1/4: shared containment guard AND the shared
132
+ * non-trimming `git` subprocess runner, exported so `uat.cts`'s
133
+ * `readBaselineAtHead` (byte-exact `git show HEAD:<path>` read) can route
134
+ * through the same bounded, `windowsHide`d, stderr-discarding call this
135
+ * module already uses, rather than maintaining a second private
136
+ * `execFileSync` wrapper that can drift from this one's timeout/maxBuffer.
137
+ */
128
138
  function isCleanRelativePosixPath(relPath) {
129
139
  if (!relPath || relPath.startsWith('/') || relPath.includes('\\') || relPath.includes('\0')) {
130
140
  return false;
@@ -234,6 +234,12 @@ function analyzeCoverage(items, resolutions = [], validators) {
234
234
  const unresolved = merged.filter((i) => i.status === 'unresolved').length;
235
235
  const applicable = merged.length;
236
236
  const resolved = applicable - unresolved; // closed set: resolved-status + dismissed
237
+ // #4656: the unclassified soft-signal rows count toward `applicable` (the
238
+ // rollup is count-preserving and `resolved = applicable - unresolved` is a
239
+ // documented identity), so the count is exposed as a SIBLING field — the
240
+ // zero-applicable guards can then also fire when EVERY requirement is
241
+ // unclassified, the case the spec-phase/ui-phase docs promise to catch.
242
+ const unclassified = merged.filter((i) => i.category === 'unclassified').length;
237
243
  const byVerification = {};
238
244
  for (const tier of validators.verification)
239
245
  byVerification[tier] = 0;
@@ -242,7 +248,7 @@ function analyzeCoverage(items, resolutions = [], validators) {
242
248
  byVerification[i.verification] = (byVerification[i.verification] ?? 0) + 1;
243
249
  }
244
250
  }
245
- return { items: merged, coverage: { applicable, resolved, unresolved, byVerification } };
251
+ return { items: merged, coverage: { applicable, resolved, unresolved, unclassified, byVerification } };
246
252
  }
247
253
  /**
248
254
  * The prohibition adapter's injected runtime validators (ADR-550 #5). There is no closed
@@ -29,6 +29,7 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
29
29
  const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
30
30
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
31
31
  const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
32
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
32
33
  // ─── Constants ────────────────────────────────────────────────────────────────
33
34
  const DIMENSION_KEYS = [
34
35
  'communication_style', 'decision_speed', 'explanation_depth',
@@ -203,6 +204,7 @@ function buildClaudeMdWorkflowEnforcement(runtime) {
203
204
  'Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.',
204
205
  '',
205
206
  'Use these entry points:',
207
+ `- \`${String((0, runtime_slash_cjs_1.formatGsdSlash)('fast', runtime))}\` for a trivial task inline, with no subagents and no PLAN.md`,
206
208
  `- \`${String((0, runtime_slash_cjs_1.formatGsdSlash)('quick', runtime))}\` for small fixes, doc updates, and ad-hoc tasks`,
207
209
  `- \`${String((0, runtime_slash_cjs_1.formatGsdSlash)('debug', runtime))}\` for investigation and bug fixing`,
208
210
  `- \`${String((0, runtime_slash_cjs_1.formatGsdSlash)('execute-phase', runtime))}\` for planned phase work`,
@@ -477,10 +479,11 @@ function generateSkillsSection(cwd) {
477
479
  */
478
480
  function extractSkillFrontmatter(content) {
479
481
  const result = { name: '', description: '' };
480
- const fmMatch = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
481
- if (!fmMatch)
482
+ // The block is the one the one fence owner (`locateFrontmatterFence`) finds.
483
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
484
+ if (!fence?.closed)
482
485
  return result;
483
- const fmBlock = fmMatch[1];
486
+ const fmBlock = content.slice(fence.openEnd, fence.bodyEnd);
484
487
  const lines = fmBlock.split('\n');
485
488
  let currentKey = '';
486
489
  for (const line of lines) {
@@ -58,14 +58,9 @@ exports.eslintHasFatalError = eslintHasFatalError;
58
58
  exports.eslintJsonHasRule = eslintJsonHasRule;
59
59
  exports.defaultProveFailFirst = defaultProveFailFirst;
60
60
  exports.runProhibitionEnforcement = runProhibitionEnforcement;
61
- exports.routeProhibitionEnforcement = routeProhibitionEnforcement;
62
61
  const node_fs_1 = __importDefault(require("node:fs"));
63
62
  const node_path_1 = __importDefault(require("node:path"));
64
63
  const node_child_process_1 = require("node:child_process");
65
- // Import the leaf I/O module directly (core.cjs re-export spine retired in epic #1267).
66
- // eslint-disable-next-line @typescript-eslint/no-require-imports
67
- const io = require("./io.cjs");
68
- const { output, error, ERROR_REASON } = io;
69
64
  const probe_core_cjs_1 = require("./probe-core.cjs");
70
65
  /**
71
66
  * READ-BACK ADAPTER (#1278, plan 01-03): reconstruct a `CheckDescriptor` from the flat scalar keys
@@ -730,53 +725,3 @@ function runProhibitionEnforcement(prohibition, check, options = {}) {
730
725
  ...(mode ? { mode } : {}),
731
726
  };
732
727
  }
733
- /**
734
- * Parse a `{ prohibition, check, mode }` request from a JSON file path or inline `--json` string.
735
- * Returns null on any parse failure (the caller surfaces a structured error, never a throw).
736
- */
737
- function parseRequest(args) {
738
- // args[0] = 'check', args[1] = 'prohibition-enforcement', args[2] = <json-file-path | --json>
739
- const jsonFlagIdx = args.indexOf('--json');
740
- let payload = '';
741
- if (jsonFlagIdx !== -1 && typeof args[jsonFlagIdx + 1] === 'string') {
742
- payload = args[jsonFlagIdx + 1];
743
- }
744
- else if (typeof args[2] === 'string' && args[2]) {
745
- try {
746
- payload = node_fs_1.default.readFileSync(args[2], 'utf-8');
747
- }
748
- catch {
749
- return null;
750
- }
751
- }
752
- else {
753
- return null;
754
- }
755
- try {
756
- const parsed = JSON.parse(payload);
757
- const checkRaw = parsed['check'];
758
- const check = (checkRaw && typeof checkRaw === 'object')
759
- ? checkRaw
760
- : null;
761
- const modeRaw = parsed['mode'];
762
- const mode = typeof modeRaw === 'string' ? modeRaw : undefined;
763
- return { prohibition: parsed['prohibition'] ?? null, check, ...(mode ? { mode } : {}) };
764
- }
765
- catch {
766
- return null;
767
- }
768
- }
769
- /**
770
- * CLI surface: `gsd_run check prohibition-enforcement <request.json>` (or `--json '<inline>'`).
771
- * Parses the request, runs the producer, and emits the result as JSON. Honors the no-throw
772
- * contract: malformed input -> structured `error(...)`, never an uncaught throw.
773
- */
774
- function routeProhibitionEnforcement(args, raw) {
775
- const req = parseRequest(args);
776
- if (!req) {
777
- error('prohibition-enforcement requires a JSON request: check prohibition-enforcement <request.json> | --json \'{"prohibition":{...},"check":{...}}\'', ERROR_REASON.SDK_MISSING_ARG);
778
- return;
779
- }
780
- const result = runProhibitionEnforcement(req.prohibition, req.check, req.mode ? { mode: req.mode } : {});
781
- output(result, raw, undefined);
782
- }
@@ -17,12 +17,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.setExplicitProjectRoot = setExplicitProjectRoot;
21
+ exports.resolveProjectRoot = resolveProjectRoot;
20
22
  exports.findProjectRoot = findProjectRoot;
21
23
  exports.consentProjectRoot = consentProjectRoot;
22
24
  const node_fs_1 = __importDefault(require("node:fs"));
23
25
  const node_path_1 = __importDefault(require("node:path"));
24
26
  const node_os_1 = __importDefault(require("node:os"));
25
27
  const FIND_PROJECT_ROOT_MAX_DEPTH = 10;
28
+ // #4894: an operator-supplied `--project-dir` IS the project root — the
29
+ // dispatcher validates it and skips the ancestor walk-up for `cwd`, but code
30
+ // that derives a root from a PHASE DIRECTORY (verification) never sees `cwd`.
31
+ // The dispatcher records the validated root here, once per process, and those
32
+ // callers resolve through `resolveProjectRoot`. `null` (the default, and what
33
+ // the dispatcher sets when the flag is absent) leaves `findProjectRoot`'s
34
+ // walk-up as the only source of truth, so no-flag behavior is unchanged.
35
+ let explicitProjectRoot = null;
36
+ function setExplicitProjectRoot(root) {
37
+ explicitProjectRoot = root;
38
+ }
39
+ function resolveProjectRoot(startDir) {
40
+ return explicitProjectRoot ?? findProjectRoot(startDir);
41
+ }
26
42
  function findProjectRoot(startDir) {
27
43
  let resolvedStart;
28
44
  try {
@@ -43,12 +59,35 @@ function findProjectRoot(startDir) {
43
59
  catch {
44
60
  // fall through
45
61
  }
62
+ // A `.planning` that is itself a symlink is a deliberate hop into a separate
63
+ // tree (the documented "keep planning content out of the tracked repo"
64
+ // convention: `.planning -> ~/.gsd-external-planning/<project>/`, its own git
65
+ // repo). `fs.existsSync(d + '/.git')` DEREFERENCES `d`, so at `d ===
66
+ // <the .planning symlink path>` it finds the EXTERNAL store's `.git`, and
67
+ // both isInsideGitRepo/nearestGitRoot misread it as a crossed nested-child-
68
+ // repo boundary (#2843's guard). `fs.lstatSync` does not dereference its
69
+ // final path component, so it answers "is `d` ITSELF a symlink" (#4815).
70
+ //
71
+ // Deliberately scoped to a symlink named `.planning`, NOT any symlinked
72
+ // directory: a nested child repo reached through some other symlinked
73
+ // ancestor is still a real repo boundary, and skipping it would reopen
74
+ // #2843 through a different trigger.
75
+ function isSymlinkedPlanningDir(d) {
76
+ if (node_path_1.default.basename(d) !== '.planning')
77
+ return false;
78
+ try {
79
+ return node_fs_1.default.lstatSync(d).isSymbolicLink();
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
46
85
  // Walk upward, mirroring isInsideGitRepo from the CJS reference.
47
86
  function isInsideGitRepo(candidateParent) {
48
87
  let d = resolvedStart;
49
88
  while (d !== fsRoot) {
50
89
  try {
51
- if (node_fs_1.default.existsSync(d + node_path_1.default.sep + '.git'))
90
+ if (!isSymlinkedPlanningDir(d) && node_fs_1.default.existsSync(d + node_path_1.default.sep + '.git'))
52
91
  return true;
53
92
  }
54
93
  catch {
@@ -77,7 +116,7 @@ function findProjectRoot(startDir) {
77
116
  if (d === upTo)
78
117
  break;
79
118
  try {
80
- if (node_fs_1.default.existsSync(d + node_path_1.default.sep + '.git'))
119
+ if (!isSymlinkedPlanningDir(d) && node_fs_1.default.existsSync(d + node_path_1.default.sep + '.git'))
81
120
  return d;
82
121
  }
83
122
  catch {