@opengsd/gsd-core 1.10.0 → 1.12.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 (544) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -270,8 +270,9 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
270
270
  // access to getCommitAttribution (which lives in bin/install.js). Until that
271
271
  // is refactored into a shared module, surface-path agents for descriptor-
272
272
  // driven runtimes will lack the Co-Authored-By trailer that install adds.
273
- // Parity is proven when resolveAttribution IS provided (see
274
- // tests/issue-1575-agent-descriptor-parity.test.cjs).
273
+ // Parity is proven when resolveAttribution IS provided (see the
274
+ // folded:issue-1575-agent-descriptor-parity describe block in
275
+ // tests/golden-parity-single-source.test.cjs).
275
276
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
276
277
  applySurface(resolvedConfigDir, layout, manifest, undefined, registry, opts?.materialize?.resolveAttribution
277
278
  ? { resolveAttribution: opts.materialize.resolveAttribution }
@@ -28,6 +28,7 @@ const { extractFrontmatter } = frontmatterMod;
28
28
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
29
29
  const security_cjs_1 = require("./security.cjs");
30
30
  const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
31
+ const ui_frontend_evidence_cjs_1 = require("./ui-frontend-evidence.cjs");
31
32
  // eslint-disable-next-line @typescript-eslint/no-require-imports
32
33
  const verifyModule = require("./verify.cjs");
33
34
  const { cmdVerifySchemaDrift, cmdVerifyCodebaseDrift } = verifyModule;
@@ -45,6 +46,15 @@ const { evaluatePredicate } = gatePredicateEval;
45
46
  const apiCoverageMod = require("./api-coverage.cjs");
46
47
  const { detectApiIntegration, validateCoverageMatrix } = apiCoverageMod;
47
48
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
49
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
50
+ const planScanMod = require("./plan-scan.cjs");
51
+ const { scanPhasePlans } = planScanMod;
52
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
53
+ const planningScopeMod = require("./planning-scope.cjs");
54
+ const { SCOPE } = planningScopeMod;
55
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
56
+ const verifyCommandGroundingMod = require("./verify-command-grounding.cjs");
57
+ const { probePhaseVerifyCommands, probePhaseFailingDirections } = verifyCommandGroundingMod;
48
58
  // ─── Helpers ──────────────────────────────────────────────────────────────────
49
59
  function normalizePhrase(text) {
50
60
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
@@ -128,14 +138,13 @@ function gateEnabled(projectDir) {
128
138
  function loadPlanContents(phaseDir) {
129
139
  if (!node_fs_1.default.existsSync(phaseDir))
130
140
  return [];
131
- try {
132
- return node_fs_1.default.readdirSync(phaseDir)
133
- .filter((entry) => /-PLAN\.md$/.test(entry))
134
- .map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)));
135
- }
136
- catch {
137
- return [];
138
- }
141
+ // #3183 (lint-plan-count-drift): source live plan files from the single
142
+ // owner (scanPhasePlans) instead of a local `-PLAN.md` readdirSync filter
143
+ // — picks up bare PLAN.md and nested plans/, and excludes plans marked
144
+ // `status: superseded`, which the prior root-only exact-suffix filter did
145
+ // neither for.
146
+ return scanPhasePlans(phaseDir).planFiles
147
+ .map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)));
139
148
  }
140
149
  const DESIGNATED_HEADINGS_RE = /^#{1,6}\s+(?:must[_ ]haves?|truths?|tasks?|objective)\b/i;
141
150
  // #2372: scanned-tag set must match the planner-canonical surfaces where a D-NN citation
@@ -337,6 +346,7 @@ function recentCommitMessages(projectDir) {
337
346
  encoding: 'utf-8',
338
347
  maxBuffer: 4 * 1024 * 1024,
339
348
  windowsHide: true,
349
+ timeout: 15_000,
340
350
  });
341
351
  }
342
352
  catch {
@@ -411,8 +421,11 @@ function cmdDecisionCoverageVerify(projectDir, args, raw) {
411
421
  return;
412
422
  }
413
423
  const planContents = loadPlanContents(phaseDir);
424
+ // #3183 (lint-plan-count-drift): same single-owner sourcing as
425
+ // loadPlanContents above — scanPhasePlans's summaryFiles instead of a
426
+ // local `-SUMMARY.md` readdirSync filter.
414
427
  const summaryParts = node_fs_1.default.existsSync(phaseDir)
415
- ? node_fs_1.default.readdirSync(phaseDir).filter((entry) => /-SUMMARY\.md$/.test(entry)).map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)))
428
+ ? scanPhasePlans(phaseDir).summaryFiles.map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)))
416
429
  : [];
417
430
  const haystack = [
418
431
  planContents.join('\n\n'),
@@ -442,8 +455,9 @@ function cmdDecisionCoverageVerify(projectDir, args, raw) {
442
455
  * ui-plan-gate: given a phase number, checks whether the phase has frontend
443
456
  * indicators and whether a *-UI-SPEC.md already exists in the phase directory.
444
457
  *
445
- * Returns JSON: { frontend: boolean, hasUiSpec: boolean, block: boolean }
446
- * block = frontend && !hasUiSpec (gate fires when UI work is detected but no spec exists)
458
+ * Returns JSON: { frontend, hasFrontendEvidence, hasUiSpec, block, uiSpecPath, matchedToken, matchedLine }
459
+ * block = frontend && hasFrontendEvidence && !hasUiSpec (#3312: gate fires when
460
+ * UI work is detected AND the repo has static frontend evidence but no spec exists)
447
461
  *
448
462
  * Invocable as: gsd_run check ui-plan-gate <phase>
449
463
  *
@@ -477,10 +491,21 @@ function findUiSpecInDir(phaseDir) {
477
491
  * (b) Runs checkUiPresence (frontend detection) — no reimplementation.
478
492
  * (c) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
479
493
  *
480
- * Returns: { frontend, hasUiSpec, block, uiSpecPath, phaseLookupFailed }
481
- * block = frontend && !hasUiSpec
494
+ * Returns: { frontend, hasFrontendEvidence, hasUiSpec, block, uiSpecPath, matchedToken, matchedLine, phaseLookupFailed }
495
+ * block = frontend && hasFrontendEvidence && !hasUiSpec (#3312)
482
496
  * phaseLookupFailed = ROADMAP.md present but phase header not found (surfaced for
483
497
  * onError:halt gates so a missing phase doesn't silently bypass)
498
+ *
499
+ * #3312 — structural corroboration: `frontend` is a vocabulary signal only. A
500
+ * hyphen is a word boundary, so a phase naming the repo `dashboard-financeiro`
501
+ * matches the token `dashboard` exactly like the real compound `micro-frontend`
502
+ * (the boundary rule of #3718 is intentional and untouched). The gate therefore
503
+ * blocks only when the token match is corroborated by static frontend evidence
504
+ * in the repo tree (hasStaticFrontendEvidence: package.json UI-framework dep or
505
+ * a component-framework file). This mirrors the sibling post-wave gate
506
+ * computeUiSafetyGate, which requires `hasUiFiles` (git diff) before blocking.
507
+ * matchedToken/matchedLine surface what tripped the sniffer so an operator can
508
+ * judge the flag in one second instead of reaching for --skip-ui.
484
509
  */
485
510
  function computeUiPlanGate(projectDir, phase) {
486
511
  // (a) Read the phase section text using the same two-pass lookup as roadmap.get-phase.
@@ -509,6 +534,9 @@ function computeUiPlanGate(projectDir, phase) {
509
534
  // (b) Run checkUiPresence (frontend detection) — reuse existing helper; no reimplementation
510
535
  const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
511
536
  const frontend = presenceResult.hasUI;
537
+ // (b') #3312 — static structural corroboration. Only probed when the sniffer
538
+ // matched (evidence is irrelevant otherwise); failures degrade to false.
539
+ const hasFrontendEvidence = frontend ? (0, ui_frontend_evidence_cjs_1.hasStaticFrontendEvidence)(projectDir) : false;
512
540
  // (c) Resolve phase directory via findPhaseInternal and check for *-UI-SPEC.md
513
541
  let phaseDir = '';
514
542
  try {
@@ -528,10 +556,13 @@ function computeUiPlanGate(projectDir, phase) {
528
556
  catch { /* phase dir lookup failure → hasUiSpec=false */ }
529
557
  const uiSpecPath = findUiSpecInDir(phaseDir);
530
558
  const hasUiSpec = uiSpecPath !== '';
531
- // block = frontend phase with no UI-SPEC
532
- const block = frontend && !hasUiSpec;
559
+ // block = frontend phase with structural frontend evidence and no UI-SPEC (#3312)
560
+ const block = frontend && hasFrontendEvidence && !hasUiSpec;
533
561
  const result = {
534
- frontend, hasUiSpec, block, uiSpecPath: hasUiSpec ? uiSpecPath : null,
562
+ frontend, hasFrontendEvidence, hasUiSpec, block,
563
+ uiSpecPath: hasUiSpec ? uiSpecPath : null,
564
+ matchedToken: presenceResult.matchedToken,
565
+ matchedLine: presenceResult.matchedLine,
535
566
  };
536
567
  if (phaseLookupFailed)
537
568
  result.phaseLookupFailed = true;
@@ -613,6 +644,7 @@ function computeUiSafetyGate(projectDir, phase) {
613
644
  encoding: 'utf-8',
614
645
  maxBuffer: 2 * 1024 * 1024,
615
646
  windowsHide: true,
647
+ timeout: 10_000,
616
648
  });
617
649
  hasUiFiles = changed.split('\n').some((f) => f.trim() && (UI_FILE_EXTENSIONS_RE.test(f) || UI_PATH_PATTERNS_RE.test(f)));
618
650
  }
@@ -679,7 +711,9 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
679
711
  const tddPlanFiles = [];
680
712
  if (phaseDir) {
681
713
  try {
682
- const files = node_fs_1.default.readdirSync(phaseDir).filter(f => f.endsWith('-PLAN.md'));
714
+ // #3183: canonical plan set (root+nested, superseded-excluded) from the
715
+ // single owner, rather than a root-only hand-rolled readdirSync filter.
716
+ const files = scanPhasePlans(phaseDir).planFiles;
683
717
  for (const file of files) {
684
718
  const planPath = node_path_1.default.join(phaseDir, file);
685
719
  const content = readIfExists(planPath);
@@ -728,17 +762,17 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
728
762
  let green = false;
729
763
  let refactor = false;
730
764
  try {
731
- const redCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^test(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
765
+ const redCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^test(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
732
766
  red = redCommit.trim().length > 0;
733
767
  }
734
768
  catch { /* git unavailable or no match */ }
735
769
  try {
736
- const greenCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^feat(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
770
+ const greenCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^feat(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
737
771
  green = greenCommit.trim().length > 0;
738
772
  }
739
773
  catch { /* git unavailable or no match */ }
740
774
  try {
741
- const refactorCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^refactor(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
775
+ const refactorCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^refactor(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
742
776
  refactor = refactorCommit.trim().length > 0;
743
777
  }
744
778
  catch { /* git unavailable or no match */ }
@@ -752,14 +786,11 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
752
786
  }
753
787
  const violations = rows.filter(r => r.status === 'FAIL').length;
754
788
  // Build review table
755
- const sep = '━'.repeat(53);
756
789
  const tableHeader = '| Plan | RED | GREEN | REFACTOR | Status |';
757
790
  const tableDivider = '|------|-----|-------|----------|--------|';
758
791
  const tableRows = rows.map(r => `| ${r.planId.padEnd(4)} | ${r.red ? ' ✓ ' : ' ✗ '} | ${r.green ? ' ✓ ' : ' ✗ '} | ${r.refactor ? ' ✓ ' : ' — '} | ${r.status.padEnd(6)} |`);
759
792
  let table = [
760
- sep,
761
- ` TDD REVIEW — Phase ${phase}`,
762
- sep,
793
+ `### TDD REVIEW — Phase ${phase}`,
763
794
  '',
764
795
  `TDD Plans: ${tddPlanFiles.length} | Gate violations: ${violations}`,
765
796
  '',
@@ -794,6 +825,111 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
794
825
  // the host-loop dispatch's advisory branch can surface it.
795
826
  output(result, raw, undefined);
796
827
  }
828
+ /**
829
+ * Resolve a phase argument to an absolute phase directory, or '' when it
830
+ * cannot be resolved. Shared by every `check` arm that probes a phase's
831
+ * PLAN.md files, so the two never drift (DEFECT.GENERATIVE-FIX-DIVERGENCE).
832
+ * Never throws — the callers emit a degraded JSON payload instead, because
833
+ * a consumer must be able to tell "nothing to report" from "could not look".
834
+ */
835
+ function resolvePhaseDirOrEmpty(projectDir, phase) {
836
+ try {
837
+ const result = findPhaseInternal(projectDir, phase);
838
+ if (result && typeof result === 'object') {
839
+ // findPhaseInternal returns { directory: '<relative-posix-path>', ... }
840
+ // directory is relative to cwd — resolve it to absolute.
841
+ const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
842
+ if (relDir) {
843
+ return node_path_1.default.resolve(projectDir, relDir);
844
+ }
845
+ }
846
+ else if (typeof result === 'string') {
847
+ return result;
848
+ }
849
+ }
850
+ catch { /* phase dir lookup failure → caller emits degraded payload */ }
851
+ return '';
852
+ }
853
+ // ─── verify-command-paths (#2401) ──────────────────────────────────────────────
854
+ /**
855
+ * verify-command-paths: probes every `<automated>` verify command declared in a
856
+ * phase's `-PLAN.md` files against the filesystem WITHOUT executing anything —
857
+ * see verify-command-grounding.cjs for the recognizer contract.
858
+ *
859
+ * Args: check verify-command-paths <phase>
860
+ * Invocable as: gsd_run check verify-command-paths <phase>
861
+ *
862
+ * When the phase cannot be resolved to a directory, this emits a non-throwing
863
+ * degraded JSON payload (status/commands/counts all zeroed, `readError`
864
+ * populated) rather than calling `error()` — the plan-checker parses this
865
+ * result and must be able to distinguish "nothing to report" from "could not
866
+ * look", which a non-zero exit / thrown error would collapse.
867
+ */
868
+ function cmdVerifyCommandPaths(projectDir, args, raw) {
869
+ // args[0] = 'check', args[1] = 'verify-command-paths', args[2] = phase
870
+ const phase = args[2] || '';
871
+ if (!phase) {
872
+ output({
873
+ status: 'unresolvable',
874
+ commands: [],
875
+ counts: { blocker: 0, warning: 0, total: 0 },
876
+ readError: 'verify-command-paths requires a phase argument: check verify-command-paths <phase>',
877
+ }, raw, undefined);
878
+ return;
879
+ }
880
+ const phaseDir = resolvePhaseDirOrEmpty(projectDir, phase);
881
+ if (!phaseDir) {
882
+ output({
883
+ status: 'unresolvable',
884
+ commands: [],
885
+ counts: { blocker: 0, warning: 0, total: 0 },
886
+ readError: `could not resolve phase directory for phase ${phase}`,
887
+ }, raw, undefined);
888
+ return;
889
+ }
890
+ const probed = probePhaseVerifyCommands({ phaseDir, projectRoot: projectDir });
891
+ output(probed, raw, undefined);
892
+ }
893
+ // ─── verify-failure-directions (#3172) ─────────────────────────────────────────
894
+ /**
895
+ * verify-failure-directions: probes every `<automated>` verify command
896
+ * declared in a phase's `-PLAN.md` files for a stated `<fails_when>` failing
897
+ * direction — see verify-command-grounding.cjs for the recognizer contract.
898
+ *
899
+ * Args: check verify-failure-directions <phase>
900
+ * Invocable as: gsd_run check verify-failure-directions <phase>
901
+ *
902
+ * When the phase cannot be resolved to a directory, this emits a non-throwing
903
+ * degraded JSON payload (status/commands/counts all zeroed, `readError`
904
+ * populated) rather than calling `error()` — the plan-checker parses this
905
+ * result and must be able to distinguish "nothing to report" from "could not
906
+ * look", which a non-zero exit / thrown error would collapse.
907
+ */
908
+ function cmdVerifyFailureDirections(projectDir, args, raw) {
909
+ // args[0] = 'check', args[1] = 'verify-failure-directions', args[2] = phase
910
+ const phase = args[2] || '';
911
+ if (!phase) {
912
+ output({
913
+ status: 'unresolvable',
914
+ commands: [],
915
+ counts: { blocker: 0, warning: 0, total: 0 },
916
+ readError: 'verify-failure-directions requires a phase argument: check verify-failure-directions <phase>',
917
+ }, raw, undefined);
918
+ return;
919
+ }
920
+ const phaseDir = resolvePhaseDirOrEmpty(projectDir, phase);
921
+ if (!phaseDir) {
922
+ output({
923
+ status: 'unresolvable',
924
+ commands: [],
925
+ counts: { blocker: 0, warning: 0, total: 0 },
926
+ readError: `could not resolve phase directory for phase ${phase}`,
927
+ }, raw, undefined);
928
+ return;
929
+ }
930
+ const result = probePhaseFailingDirections({ phaseDir });
931
+ output(result, raw, undefined);
932
+ }
797
933
  // ─── gap-analysis-plan-post ───────────────────────────────────────────────────
798
934
  /**
799
935
  * gap-analysis-plan-post: non-blocking advisory check that runs the post-planning
@@ -1172,6 +1308,27 @@ function cmdApiCoverageVerifyPre(projectDir, args, raw) {
1172
1308
  }, raw, undefined);
1173
1309
  return;
1174
1310
  }
1311
+ // An EMPTY scope is not a negative verdict. This gate's neighbouring arms
1312
+ // already fail closed (unresolvable phase → block; unreadable plan → block),
1313
+ // but a phase with no plan body AND no roadmap section fell through to
1314
+ // detection over zero bytes and CERTIFIED "no external-API integration" —
1315
+ // clearing a blocking seal gate on a probe that examined nothing
1316
+ // (ADR-3889 failure class (c), #3909). The discriminator is BYTES EXAMINED,
1317
+ // never SIGNALS FOUND: a phase with real plans and no API vocabulary still
1318
+ // reaches the pass below unchanged.
1319
+ if (scope.text.trim() === '') {
1320
+ output({
1321
+ block: true,
1322
+ passed: false,
1323
+ coverage_present: false,
1324
+ detected: false,
1325
+ scope_unavailable: true,
1326
+ message: 'api-coverage: the phase scope is empty — no plan body and no roadmap section were ' +
1327
+ 'found, so nothing was examined. Refusing to certify no external-API integration ' +
1328
+ 'from an unestablished scope. Add the phase plan, or add a COVERAGE.md declaration.',
1329
+ }, raw, undefined);
1330
+ return;
1331
+ }
1175
1332
  const detection = detectApiIntegration(scope.text);
1176
1333
  if (detection.detected) {
1177
1334
  // Surface only verb/noun (typed, bounded) — NOT raw prose snippets — so the
@@ -1207,12 +1364,26 @@ function isRealReadFailure(err) {
1207
1364
  function readPhaseScope(projectDir, phaseDir, phaseNumber) {
1208
1365
  const chunks = [];
1209
1366
  let readError = null;
1210
- try {
1211
- const entries = node_fs_1.default.readdirSync(phaseDir, { withFileTypes: true });
1212
- const plans = entries
1213
- .filter((e) => e.isFile() && /-PLAN\.md$/i.test(e.name))
1214
- .map((e) => e.name)
1215
- .sort();
1367
+ // A MISSING phase directory is fine (no plans yet → fall through to the
1368
+ // roadmap). Checked up front (rather than via a readdirSync catch) because
1369
+ // #3183 (lint-plan-count-drift) now sources the plan-file list from the
1370
+ // single owner (scanPhasePlans) instead of a local `-PLAN\.md$` readdirSync
1371
+ // filter — picks up bare PLAN.md and nested plans/, and excludes
1372
+ // superseded plans, none of which the prior root-only exact-suffix filter
1373
+ // did.
1374
+ if (node_fs_1.default.existsSync(phaseDir)) {
1375
+ const scan = scanPhasePlans(phaseDir);
1376
+ if (scan.scope === SCOPE.UNREADABLE) {
1377
+ // Directory exists but scanPhasePlans's own readdirSync(phaseDir) call
1378
+ // failed (EACCES/EIO race) — a real read failure the gate must not
1379
+ // silently pass (#2365 review), mirroring the prior isRealReadFailure
1380
+ // branch below for the readdirSync-throws case.
1381
+ return {
1382
+ text: '',
1383
+ readError: 'could not read the phase directory: scanPhasePlans reported scope UNREADABLE',
1384
+ };
1385
+ }
1386
+ const plans = [...scan.planFiles].sort();
1216
1387
  for (const p of plans) {
1217
1388
  try {
1218
1389
  chunks.push(node_fs_1.default.readFileSync(node_path_1.default.join(phaseDir, p), 'utf8'));
@@ -1226,17 +1397,6 @@ function readPhaseScope(projectDir, phaseDir, phaseNumber) {
1226
1397
  }
1227
1398
  }
1228
1399
  }
1229
- catch (err) {
1230
- // A MISSING phase directory is fine (no plans yet → fall through to the
1231
- // roadmap). A directory that exists but cannot be enumerated (EACCES/EIO)
1232
- // is a real read failure the gate must not silently pass (#2365 review).
1233
- if (isRealReadFailure(err)) {
1234
- return {
1235
- text: '',
1236
- readError: `could not read the phase directory: ${err instanceof Error ? err.message : String(err)}`,
1237
- };
1238
- }
1239
- }
1240
1400
  if (readError)
1241
1401
  return { text: chunks.join('\n\n'), readError };
1242
1402
  if (chunks.join('').trim().length > 0)
@@ -1290,6 +1450,18 @@ function routeCheckCommand({ args, cwd, raw }) {
1290
1450
  cmdGapAnalysisPlanPost(cwd, args, raw);
1291
1451
  return;
1292
1452
  }
1453
+ if (subcommand === 'verify-command-paths') {
1454
+ // Deterministic filesystem probe for <automated> verify commands (#2401) —
1455
+ // never executes anything; see verify-command-grounding.cjs.
1456
+ cmdVerifyCommandPaths(cwd, args, raw);
1457
+ return;
1458
+ }
1459
+ if (subcommand === 'verify-failure-directions') {
1460
+ // Presence probe for a stated <fails_when> per <automated> command
1461
+ // (#3172) — never executes anything; see verify-command-grounding.cjs.
1462
+ cmdVerifyFailureDirections(cwd, args, raw);
1463
+ return;
1464
+ }
1293
1465
  if (subcommand === 'api-coverage-verify-pre') {
1294
1466
  // ai-integration capability blocking gate at verify:pre (#1562). Dot-to-
1295
1467
  // hyphen normalization means query "api-coverage.verify-pre" routes here.
@@ -1336,7 +1508,7 @@ function routeCheckCommand({ args, cwd, raw }) {
1336
1508
  (0, prohibition_enforcement_cjs_1.routeProhibitionEnforcement)(args, raw);
1337
1509
  return;
1338
1510
  }
1339
- error('Unknown check subcommand. Available: api-coverage-verify-pre, auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, predicate, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
1511
+ error('Unknown check subcommand. Available: api-coverage-verify-pre, auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, predicate, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-command-paths, verify-failure-directions, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
1340
1512
  }
1341
1513
  module.exports = {
1342
1514
  routeCheckCommand,
@@ -1345,6 +1517,8 @@ module.exports = {
1345
1517
  computeUiPlanGate,
1346
1518
  computeUiSafetyGate,
1347
1519
  cmdGapAnalysisPlanPost,
1520
+ cmdVerifyCommandPaths,
1521
+ cmdVerifyFailureDirections,
1348
1522
  cmdTddReviewCheckpoint,
1349
1523
  cmdCheckPredicate,
1350
1524
  buildPredicateDeps,
@@ -470,13 +470,43 @@ function emitWorkflowScript(input) {
470
470
  lines.push('// read-only in a Workflow script — set it via the caller\'s token directive.');
471
471
  }
472
472
  lines.push('');
473
+ // #3302: the generated script must hand the per-agent executor results back
474
+ // to the orchestrator so it can feed the wave merge chain. Emitted right
475
+ // after the header comments (meta stays the first statement): a helper that
476
+ // extracts the executor's <worktree_metadata> JSON (agents/gsd-executor.md
477
+ // <worktree_metadata_capture>) from one agent() result, plus the outcomes
478
+ // accumulator the stage barriers below push into. `metadata` is null when
479
+ // the result carried no parseable block — the LOUD-failure input the
480
+ // orchestrator halts on for expects_worktree plans (never a silent skip).
481
+ lines.push('// #3302: extract the executor-returned <worktree_metadata> JSON so the');
482
+ lines.push('// orchestrator can record it into WAVE_WORKTREE_MANIFEST after the run');
483
+ lines.push('// (worktree.record-agent -> worktree.cleanup-wave, the same manifest-scoped');
484
+ lines.push('// merge chain inline dispatch feeds). null = absent/unparseable/interrupted.');
485
+ lines.push('function gsdWorktreeMetadata(agentResult) {');
486
+ lines.push(' if (typeof agentResult !== \'string\') return null;');
487
+ lines.push(' const m = agentResult.match(/<worktree_metadata>([\\s\\S]*?)<\\/worktree_metadata>/);');
488
+ lines.push(' if (m === null) return null;');
489
+ lines.push(' try {');
490
+ lines.push(' const parsed = JSON.parse(m[1]);');
491
+ lines.push(' return (parsed !== null && typeof parsed === \'object\') ? parsed : null;');
492
+ lines.push(' } catch (e) {');
493
+ lines.push(' return null;');
494
+ lines.push(' }');
495
+ lines.push('}');
496
+ lines.push('const gsdAgentOutcomes = [];');
497
+ lines.push('');
473
498
  const stagesByWave = [];
474
499
  let totalPlans = 0;
500
+ let worktreePlans = 0;
475
501
  for (let wi = 0; wi < waves.length; wi++) {
476
502
  const wave = waves[wi];
477
503
  const stages = partitionStages(wave.plans);
478
504
  stagesByWave.push(stages);
479
505
  totalPlans += wave.plans.length;
506
+ for (const p of wave.plans) {
507
+ if (p.use_worktree !== false)
508
+ worktreePlans += 1;
509
+ }
480
510
  lines.push('// Wave ' + wave.id);
481
511
  // Title must match this wave's meta.phases entry EXACTLY.
482
512
  lines.push('phase(' + quoteString('Wave ' + wave.id) + ')');
@@ -491,17 +521,37 @@ function emitWorkflowScript(input) {
491
521
  // threw "parallel() expects an array of functions" (#2590). Passing
492
522
  // agent() results directly would also start every agent eagerly, before
493
523
  // parallel() could bound concurrency.
494
- lines.push('await parallel([');
524
+ // #3302: capture the barrier's resolved results (one per thunk, in thunk
525
+ // order — the documented parallel() contract) so each plan's outcome can
526
+ // be tagged and returned below. Discarding them stranded every
527
+ // worktree-wf_* branch: the merge chain had no input (#3302).
528
+ lines.push('const gsdStage_' + wi + '_' + si + ' = await parallel([');
495
529
  for (const p of stagePlans) {
496
530
  lines.push(' () => agent(' + quoteString(p.brief) + ', ' + agentOptions(p, executorModel) + '),');
497
531
  }
498
532
  lines.push('])');
533
+ // Positional tagging is decided at EMIT time from the validated manifest,
534
+ // so attribution survives out-of-order completion and needs no runtime
535
+ // introspection. expects_worktree mirrors agentOptions' own per-plan
536
+ // decision (use_worktree !== false).
537
+ lines.push('gsdAgentOutcomes.push(');
538
+ for (let pi = 0; pi < stagePlans.length; pi++) {
539
+ const p = stagePlans[pi];
540
+ const tail = pi < stagePlans.length - 1 ? ',' : '';
541
+ lines.push(' { plan: ' + quoteString(p.id) + ', expects_worktree: ' + (p.use_worktree !== false) + ', metadata: gsdWorktreeMetadata(gsdStage_' + wi + '_' + si + '[' + pi + ']) }' + tail);
542
+ }
543
+ lines.push(')');
499
544
  }
500
545
  if (wi < waves.length - 1)
501
546
  lines.push('');
502
547
  }
503
- lines.push('// Each agent writes SUMMARY.md on its worktree branch; commits land there');
504
- lines.push('// and are merged by the orchestrator exactly as in inline wave dispatch.');
548
+ // #3302: the script's top-level return value is what the Workflow tool hands
549
+ // back to the orchestrator. One { plan, expects_worktree, metadata } entry
550
+ // per dispatched plan; the orchestrator records every worktree entry via
551
+ // `gsd_run query worktree.record-agent` and HALTS on a null metadata entry
552
+ // for an expects_worktree plan (see the execute:wave:pre fragment) — a
553
+ // silently-empty manifest is the exact #3302 failure mode.
554
+ lines.push('return gsdAgentOutcomes');
505
555
  const script = lines.join('\n');
506
556
  return {
507
557
  ok: true,
@@ -509,6 +559,9 @@ function emitWorkflowScript(input) {
509
559
  summary: {
510
560
  waves: waves.length,
511
561
  plans: totalPlans,
562
+ // #3302: the number of record-agent entries the orchestrator must end up
563
+ // with in WAVE_WORKTREE_MANIFEST after the run — the loud count check.
564
+ worktreePlans,
512
565
  stagesByWave,
513
566
  resumeRunId: runId,
514
567
  budgetTokens,