@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
@@ -18,19 +18,32 @@ const node_fs_1 = __importDefault(require("node:fs"));
18
18
  const node_path_1 = __importDefault(require("node:path"));
19
19
  // eslint-disable-next-line @typescript-eslint/no-require-imports
20
20
  const frontmatter = require("./frontmatter.cjs");
21
- const { extractFrontmatter } = frontmatter;
21
+ const { extractFrontmatter, frontmatterBlock } = frontmatter;
22
22
  // eslint-disable-next-line @typescript-eslint/no-require-imports
23
23
  const markdownSectionizer = require("./markdown-sectionizer.cjs");
24
24
  const { stripFencedCode } = markdownSectionizer;
25
25
  // eslint-disable-next-line @typescript-eslint/no-require-imports
26
26
  const verification = require("./verification.cjs");
27
- const { readVerificationStatus } = verification;
27
+ const { readVerificationStatus, reportStatusOf, isReportContained, VERIFICATION_STATUS } = verification;
28
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
29
29
  const phaseIdMod = require("./phase-id.cjs");
30
30
  const { scopeToPhase } = phaseIdMod;
31
31
  // eslint-disable-next-line @typescript-eslint/no-require-imports
32
32
  const coreUtils = require("./core-utils.cjs");
33
- const { normalizeLineEndings } = coreUtils;
33
+ const { normalizeLineEndings, countMatchedSummaries } = coreUtils;
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const planScanMod = require("./plan-scan.cjs");
36
+ const { isRootPlanFile } = planScanMod;
37
+ // Two CommonMark-legal line terminators (LINE SEPARATOR, PARAGRAPH SEPARATOR)
38
+ // that a naive `split('\n')`-only scan would not treat as line breaks.
39
+ // Built via String.fromCharCode rather than written as a regex-literal escape
40
+ // sequence, deliberately: this source file is round-tripped through tooling
41
+ // that decodes a literal backslash-u escape into the real character, which
42
+ // would leave an ACTUAL U+2028/U+2029 sitting inside a regex literal — and
43
+ // both are themselves JS/TS source line terminators, so the regex literal
44
+ // containing one would be truncated at that point and fail to parse at all.
45
+ const EXOTIC_LINE_SEPARATORS = String.fromCharCode(0x2028) + String.fromCharCode(0x2029);
46
+ const LINE_SPLIT_RE = new RegExp('[\\n' + EXOTIC_LINE_SEPARATORS + ']');
34
47
  // ─── Blocking state sets (documented for maintainability) ─────────────────────
35
48
  // UAT file frontmatter `status` values that indicate the file is not fully done
36
49
  const BLOCKING_UAT_FM_STATUSES = new Set([
@@ -38,15 +51,41 @@ const BLOCKING_UAT_FM_STATUSES = new Set([
38
51
  ]);
39
52
  // UAT file frontmatter `result` values that indicate failure
40
53
  const BLOCKING_UAT_FM_RESULTS = new Set(['pending', 'blocked', 'failed']);
41
- // Canonical VERIFICATION frontmatter `status` value that indicates passing.
42
- const PASSING_VERIFICATION_STATUSES = new Set(['passed']);
43
- // VERIFICATION file frontmatter `status` values that explicitly block
44
- const BLOCKING_VERIFICATION_FM_STATUSES = new Set([
45
- 'human_needed', 'gaps_found', 'pending', 'blocked', 'partial',
46
- 'failed', 'in_progress',
47
- ]);
54
+ // #5118: the VERIFICATION report's `status` vocabulary is the owner's closed
55
+ // enum (src/verification.cts) — this module no longer keeps its own passing /
56
+ // blocking sets. `pending|blocked|partial|failed|in_progress` were never
57
+ // written by the verifier; a report carrying one (or any other out-of-set
58
+ // value) is now a VerificationStatusError from the owner, not a blocker.
48
59
  // UAT test-item `result` values that count as passing
49
60
  const PASSING_RESULTS = new Set(['passed', 'pass']);
61
+ // #4546 — a `skipped` test-item whose reason carries the verify-work writer's
62
+ // deferral template prefix ("Deferred follow-up: …", #1921) is a deliberately
63
+ // deferred follow-up: non-blocking. Quote-tolerant (the writer wraps the value
64
+ // in double quotes) and case-insensitive (human-edited files vary). Anything
65
+ // else — a reasonless skip, a non-deferral reason — still blocks. PARITY: this
66
+ // matcher and the writer template in gsd-core/workflows/verify-work.md
67
+ // (process_response) are two halves of one contract, pinned together by
68
+ // tests/verify-work-deferred-promotion.test.cjs.
69
+ const DEFERRED_REASON_RE = /^["']?deferred follow-up\b/i;
70
+ // Trust note (#4546 review): the reason line is user-authored state — an
71
+ // author could equally write `result: passed` — so this prefix is an
72
+ // AUTHORING contract with the verify-work writer, not a security boundary.
73
+ // A hand-written deferral that skips the UAT file's ## Deferred Follow-Ups
74
+ // section also bypasses the complete_session promotion offer; the section is
75
+ // the durable project-level record. Variant spellings that do not match
76
+ // ("Deferred follow-ups:", "followup") block — fail-closed by design.
77
+ // #4983 — the sibling case #4546 left unfixed: a `result: issue` test whose
78
+ // `## Gaps` entry (templates/UAT.md) has been reconciled to `status: resolved`
79
+ // by an executed gap-closure plan (verify-work.md's reconcile_gaps step,
80
+ // #1921) is a genuinely fixed-and-verified issue: non-blocking. Unlike the
81
+ // deferred-skip case above, this signal is NOT trusted on the entry's
82
+ // `status:`/`resolved_by:` text alone — `isTestGapResolved` below also
83
+ // requires `resolved_by` to name a `*-PLAN.md` file that actually exists in
84
+ // this phase directory with a matching `*-SUMMARY.md` (mirroring
85
+ // reconcile_gaps' own criterion: a plan whose `gap_ids` names the gap AND has
86
+ // a SUMMARY was actually executed) — so a `resolved_by` naming no executed
87
+ // plan still blocks, closing the false-green risk a text-only trust model
88
+ // would leave open for a status this predicate gate is not free to bypass.
50
89
  // ─── stripFalsePositiveContexts ───────────────────────────────────────────────
51
90
  /**
52
91
  * Remove contexts that can contain `result: ...` lines that are NOT real test results:
@@ -59,8 +98,10 @@ const PASSING_RESULTS = new Set(['passed', 'pass']);
59
98
  * Returns surviving lines joined by '\n'. Robust to CRLF input.
60
99
  */
61
100
  function stripFalsePositiveContexts(content) {
62
- // Step (a): strip leading frontmatter block only at byte 0
63
- let stripped = content.replace(/^---\r?\n[\s\S]*?\r?\n---[ \t]*(\r?\n|$)/, '');
101
+ // Step (a): strip the leading frontmatter block — the one `frontmatterBlock` (the one fence
102
+ // owner) finds — with its closing fence line's line ending.
103
+ const block = frontmatterBlock(content);
104
+ let stripped = block ? block.rest.replace(/^\r?\n/, '') : content;
64
105
  // Step (b): remove HTML comments anywhere; unterminated comment swallows to EOF
65
106
  stripped = stripped.replace(/<!--[\s\S]*?(?:-->|$)/g, '');
66
107
  // Step (c): remove fenced code blocks via the canonical seam (ADR-1372 T5)
@@ -119,19 +160,19 @@ function parseUatResultItems(cleanContent) {
119
160
  // #3078-CR MEDIUM (security review follow-up): STRUCTURE and ATTRIBUTION
120
161
  // need different split frames. This is a STRUCTURE scan — finding where a
121
162
  // heading block begins — and there is no attribution distinction to
122
- // preserve, so split on any of `\n`, U+2028, U+2029: a heading delimited by
123
- // an exotic line separator (origin/next's `/m`-anchored scan found these;
124
- // a naive `split('\n')`-only port silently stopped finding them, making the
125
- // gate MORE permissive than origin/next) is found exactly like a
126
- // `\n`-delimited one. Contrast the `result:` scan below, which is an
127
- // ATTRIBUTION scan and must NOT do this.
163
+ // preserve, so split on any of the two exotic line separators alongside
164
+ // ordinary `\n` (LINE_SPLIT_RE, above): a heading delimited by one of them
165
+ // (origin/next's `/m`-anchored scan found these; a naive `split('\n')`-only
166
+ // port silently stopped finding them, making the gate MORE permissive than
167
+ // origin/next) is found exactly like a `\n`-delimited one. Contrast the
168
+ // `result:` scan below, which is an ATTRIBUTION scan and must NOT do this.
128
169
  const HEADING_LINE_RE = /^###\s*(\d+)\.\s*(.+)$/;
129
170
  const headings = [];
130
171
  {
131
172
  // All three separators are exactly one UTF-16 code unit, so the
132
173
  // `line.length + 1` offset arithmetic below stays valid regardless of
133
174
  // which separator terminated a given line.
134
- const lines = cleanContent.split(/[\n\u2028\u2029]/);
175
+ const lines = cleanContent.split(LINE_SPLIT_RE);
135
176
  let offset = 0;
136
177
  for (const line of lines) {
137
178
  const hMatch = line.match(HEADING_LINE_RE);
@@ -152,8 +193,8 @@ function parseUatResultItems(cleanContent) {
152
193
  // A block spans until the START of the next heading's line (tracked
153
194
  // directly from the same split-frame scan above), not a re-search for a
154
195
  // literal '\n###' over unsplit text -- the latter would silently miss a
155
- // next heading delimited by U+2028/U+2029 instead of '\n' and swallow
156
- // every subsequent block into this one.
196
+ // next heading delimited by an exotic separator instead of '\n' and
197
+ // swallow every subsequent block into this one.
157
198
  const blockContent = i + 1 < headings.length
158
199
  ? cleanContent.slice(blockStart, headings[i + 1].lineStart)
159
200
  : cleanContent.slice(blockStart);
@@ -161,21 +202,33 @@ function parseUatResultItems(cleanContent) {
161
202
  // the heading scan above): test each already-split line individually
162
203
  // against a single-line (no `/m` anchor) pattern instead of anchoring
163
204
  // over unsplit `blockContent`, so a `result:`-shaped line reachable only
164
- // via a U+2028/U+2029 separator inside an `expected: |` scalar body can
165
- // never register as a fake column-0 match. FIRST MATCH WINS — no
166
- // ambiguity counting, matching src/uat.cts's contract.
205
+ // via an exotic separator inside an `expected: |` scalar body can never
206
+ // register as a fake column-0 match. FIRST MATCH WINS — no ambiguity
207
+ // counting, matching src/uat.cts's contract.
167
208
  // Uses [ \t]* (not \s*) so the captured value must sit on the SAME line as result:.
168
209
  // A result: key with the value on a subsequent line yields no match → 'missing' (blocker).
210
+ // #4546: the `reason:` line is captured with the same frame and FIRST-match
211
+ // rule — it is the deferral signal the evaluator needs (a `skipped` item
212
+ // whose reason is the verify-work writer's "Deferred follow-up:" template
213
+ // is non-blocking). Quoted values are captured with their quotes so the
214
+ // evaluator's matcher can tolerate them exactly as written.
169
215
  const RESULT_LINE_RE = /^result:[ \t]*\[?([\w-]+)\]?/i;
170
216
  const resultMatch = blockContent
171
217
  .split('\n')
172
218
  .map((line) => line.match(RESULT_LINE_RE))
173
219
  .find((m) => m !== null) ?? null;
220
+ const REASON_LINE_RE = /^reason:[ \t]*(.*)$/i;
221
+ const reasonMatch = blockContent
222
+ .split('\n')
223
+ .map((line) => line.match(REASON_LINE_RE))
224
+ .find((m) => m !== null) ?? null;
225
+ const reason = reasonMatch ? reasonMatch[1].trim() : '';
174
226
  if (resultMatch) {
175
227
  items.push({
176
228
  test: h.test,
177
229
  name: h.name,
178
230
  result: resultMatch[1].toLowerCase(),
231
+ reason,
179
232
  });
180
233
  }
181
234
  else {
@@ -184,11 +237,240 @@ function parseUatResultItems(cleanContent) {
184
237
  test: h.test,
185
238
  name: h.name,
186
239
  result: 'missing',
240
+ reason,
187
241
  });
188
242
  }
189
243
  }
190
244
  return items;
191
245
  }
246
+ /**
247
+ * #4983: parse the UAT file's `## Gaps` bullet list (templates/UAT.md) for
248
+ * the four scalar fields the predicate needs to decide whether a resolved
249
+ * `result: issue` test still blocks — `test`, `status`, `resolved_by`,
250
+ * `gap_id`.
251
+ *
252
+ * Deliberately a separate, minimal parser from src/uat.cts's `parseGapsItems`
253
+ * (the read-side/audit parser): this module hardens independently, on the
254
+ * ALREADY-CLEANED body (stripFalsePositiveContexts has already removed
255
+ * frontmatter/HTML comments/fenced code/blockquotes), and only needs these
256
+ * four fields — never the nested `artifacts:`/`missing:` sub-lists the
257
+ * read-side parser also handles.
258
+ *
259
+ * Entries are split on column-0 `- ` bullet openers (the template's shape);
260
+ * a nested sub-list item (e.g. a ` artifacts:` key followed by an indented
261
+ * ` - path: "..."` line) is indented and never matches the column-0
262
+ * opener, so it can never be mistaken for a new top-level entry.
263
+ *
264
+ * Fields are matched per line, first-match-wins, bounded to the entry's OWN
265
+ * top-level indentation — either the bullet-opener line itself (0 leading
266
+ * spaces once `BULLET_OPENER_RE` has stripped the `"- "` marker) or a
267
+ * continuation line indented by exactly the template's 2 spaces (each field
268
+ * regex below is anchored `^[ ]{0,2}key:`). A candidate line indented deeper
269
+ * than that (4+ spaces) is a nested sub-list's body or a multi-line scalar
270
+ * VALUE, never a sibling
271
+ * field declaration, and is deliberately NOT trimmed-and-matched the way an
272
+ * earlier revision of this function did (#4983 review round 1, MEDIUM): a
273
+ * block-scalar value line that happens to read "status: resolved" after
274
+ * trimming (e.g. embedded free text) could otherwise be mistaken for a real
275
+ * field at the wrong indentation, the same false-positive class
276
+ * `parseUatResultItems` above already guards against via column-0 anchoring
277
+ * for `result:`/`reason:`.
278
+ */
279
+ function parseGapsEntries(cleanContent) {
280
+ const GAPS_HEADING_RE = /^##\s*Gaps\s*$/i;
281
+ const OTHER_HEADING_RE = /^##\s+\S/;
282
+ const lines = cleanContent.split('\n');
283
+ let sectionStart = -1;
284
+ let sectionEnd = lines.length;
285
+ for (let i = 0; i < lines.length; i++) {
286
+ if (sectionStart === -1) {
287
+ if (GAPS_HEADING_RE.test(lines[i]))
288
+ sectionStart = i + 1;
289
+ }
290
+ else if (OTHER_HEADING_RE.test(lines[i])) {
291
+ sectionEnd = i;
292
+ break;
293
+ }
294
+ }
295
+ if (sectionStart === -1)
296
+ return [];
297
+ // Split the section body into entries at column-0 `- ` bullet openers.
298
+ // The opener line's own field (if any) is stored with a synthetic 0-space
299
+ // prefix removed — see FIELD_INDENT_RE below, which accepts both 0 and 2
300
+ // leading spaces as "this entry's own top level".
301
+ const BULLET_OPENER_RE = /^-\s?(.*)$/;
302
+ const entryBlocks = [];
303
+ for (const line of lines.slice(sectionStart, sectionEnd)) {
304
+ const m = line.match(BULLET_OPENER_RE);
305
+ if (m) {
306
+ entryBlocks.push([m[1]]);
307
+ }
308
+ else if (entryBlocks.length > 0) {
309
+ entryBlocks[entryBlocks.length - 1].push(line);
310
+ }
311
+ // Lines before the first bullet (blank lines, the template's own
312
+ // `<!-- YAML format ... -->` comment — already stripped by the caller)
313
+ // carry no entry and are simply ignored.
314
+ }
315
+ const stripQuotes = (v) => v.trim().replace(/^["']+|["']+$/g, '').trim();
316
+ // Bounded to 0-2 leading spaces: the bullet-opener line's stored text has
317
+ // 0 (its `- ` marker already stripped), and a real continuation line in
318
+ // the template's own two-space-indented shape has exactly 2. Anything
319
+ // indented deeper (a scalar body, a nested `artifacts:`/`missing:` list
320
+ // item) is excluded by construction, not merely by accident of key name.
321
+ const TEST_FIELD_RE = /^[ ]{0,2}test:[ \t]*(.*)$/i;
322
+ const STATUS_FIELD_RE = /^[ ]{0,2}status:[ \t]*(.*)$/i;
323
+ const RESOLVED_BY_FIELD_RE = /^[ ]{0,2}resolved_by:[ \t]*(.*)$/i;
324
+ const GAP_ID_FIELD_RE = /^[ ]{0,2}gap_id:[ \t]*(.*)$/i;
325
+ const entries = [];
326
+ for (const block of entryBlocks) {
327
+ let test = null;
328
+ let status = '';
329
+ let resolvedBy = '';
330
+ let gapId = '';
331
+ for (const rawLine of block) {
332
+ if (test === null) {
333
+ const tm = rawLine.match(TEST_FIELD_RE);
334
+ if (tm) {
335
+ const n = parseInt(stripQuotes(tm[1]).replace(/[[\]]/g, ''), 10);
336
+ if (!Number.isNaN(n))
337
+ test = n;
338
+ continue;
339
+ }
340
+ }
341
+ if (!status) {
342
+ const sm = rawLine.match(STATUS_FIELD_RE);
343
+ if (sm) {
344
+ status = stripQuotes(sm[1]).toLowerCase();
345
+ continue;
346
+ }
347
+ }
348
+ if (!resolvedBy) {
349
+ const rm = rawLine.match(RESOLVED_BY_FIELD_RE);
350
+ if (rm) {
351
+ resolvedBy = stripQuotes(rm[1]);
352
+ continue;
353
+ }
354
+ }
355
+ if (!gapId) {
356
+ const gm = rawLine.match(GAP_ID_FIELD_RE);
357
+ if (gm) {
358
+ gapId = stripQuotes(gm[1]);
359
+ continue;
360
+ }
361
+ }
362
+ }
363
+ entries.push({ test, status, resolvedBy, gapId });
364
+ }
365
+ return entries;
366
+ }
367
+ // ─── isTestGapResolved ────────────────────────────────────────────────────────
368
+ /**
369
+ * #4983: true only when EVERY `## Gaps` entry recorded against `testNum` is a
370
+ * verified resolution — never on the presence of just one resolved entry,
371
+ * since a later regression re-opens the SAME test number with a fresh
372
+ * `gap_id` (per verify-work.md's reconcile_gaps contract) while a prior entry
373
+ * for it stays `resolved`; an unresolved regression must still block.
374
+ *
375
+ * A single entry only counts as a verified resolution when ALL of:
376
+ * - `status` is `resolved` (an entry with no gap recorded, or `status:
377
+ * failed`/blank, is not a resolution — fails closed);
378
+ * - `resolved_by` is non-empty (a partial resolution missing the writer's
379
+ * attribution fails closed, per #4983's acceptance criteria);
380
+ * - `resolved_by`, taken as a bare basename (rejecting any path separator —
381
+ * no directory traversal), names a `*-PLAN.md` file that actually exists
382
+ * in this phase directory;
383
+ * - that plan has a matching `*-SUMMARY.md` sibling in the same directory —
384
+ * the same "plan executed" evidence verify-work.md's own reconcile_gaps
385
+ * step requires (elaboration.md §1) before it ever writes `resolved_by`;
386
+ * - the entry's own `gap_id` is present, AND the named plan's frontmatter
387
+ * `gap_ids` array includes it — proof the plan resolved THIS gap, not
388
+ * some other one.
389
+ *
390
+ * The `gap_id` cross-check (#4983 review round 1, CRITICAL) closes a false-
391
+ * green a plan-existence-only check leaves wide open: `resolved_by` naming a
392
+ * real, executed plan is not proof that plan addressed THIS gap. Concretely,
393
+ * a phase with a legitimately-resolved gap on test 3 (`resolved_by:
394
+ * 05-02-PLAN.md`, `05-02-PLAN.md`'s own `gap_ids: [G-05-3]`) could otherwise
395
+ * have an UNRELATED, still-broken test 7 marked `status: resolved,
396
+ * resolved_by: 05-02-PLAN.md` (copy-pasted or mis-attributed) and the prior
397
+ * checks alone would accept it — `05-02-PLAN.md` and its SUMMARY genuinely
398
+ * exist, they just never touched gap 7. Reading the plan's own `gap_ids` and
399
+ * requiring it to name this entry's `gap_id` closes that: a resolution claim
400
+ * this gate cannot independently verify against the phase directory's own
401
+ * files, cross-referenced back to the SPECIFIC gap, is not trusted. An entry
402
+ * with no `gap_id` at all (an older, hand-written entry predating the
403
+ * `gap_id` convention) fails closed — there is nothing to cross-reference,
404
+ * so it cannot be verified.
405
+ *
406
+ * This is deliberately MORE than a text-only trust check (contrast the
407
+ * `DEFERRED_REASON_RE` deferred-skip case above, an accepted authoring
408
+ * contract, not a security boundary): #4983's acceptance criteria explicitly
409
+ * requires that "a `resolved_by` that names no executed plan still blocks" —
410
+ * reading this as "no plan that executed AND resolved THIS gap" is the
411
+ * reading that actually closes the false-green risk, not merely "some plan
412
+ * ran somewhere in this phase."
413
+ */
414
+ function isTestGapResolved(entries, testNum, dirEntries, phaseFullDir) {
415
+ const forTest = entries.filter((e) => e.test === testNum);
416
+ if (forTest.length === 0)
417
+ return false;
418
+ return forTest.every((e) => {
419
+ if (e.status !== 'resolved')
420
+ return false;
421
+ if (!e.resolvedBy)
422
+ return false;
423
+ if (node_path_1.default.basename(e.resolvedBy) !== e.resolvedBy)
424
+ return false;
425
+ // Canonical plan-filename predicate (src/plan-scan.cts) rather than a
426
+ // hand-rolled `-PLAN.md$` regex — one owner for "what is a plan file"
427
+ // across the codebase (lint-plan-count-drift.cjs enforces this).
428
+ if (!isRootPlanFile(e.resolvedBy))
429
+ return false;
430
+ if (!dirEntries.includes(e.resolvedBy))
431
+ return false;
432
+ // Canonical plan→summary pairing (src/core-utils.cts), same reason: a
433
+ // single-plan/single-candidate-set query reuses the exact matching rules
434
+ // scanPhasePlans's own summaryCount is built from, rather than a
435
+ // hand-rolled `-PLAN.md` → `-SUMMARY.md` suffix swap.
436
+ if (countMatchedSummaries([e.resolvedBy], dirEntries) !== 1)
437
+ return false;
438
+ // Fail closed on an entry with no gap_id to cross-reference — there is
439
+ // nothing to verify the named plan actually resolved THIS gap against.
440
+ if (!e.gapId)
441
+ return false;
442
+ return planClaimsGapId(phaseFullDir, e.resolvedBy, e.gapId);
443
+ });
444
+ }
445
+ /**
446
+ * #4983 (review round 1, CRITICAL follow-up): read the named plan's own
447
+ * frontmatter `gap_ids` array and confirm it includes `gapId` — the
448
+ * cross-reference `isTestGapResolved` needs to confirm a plan that exists
449
+ * and has a SUMMARY actually claims to resolve THIS gap, not merely that it
450
+ * ran. Any failure to read or parse the plan (missing file, unreadable,
451
+ * malformed frontmatter, `gap_ids` absent or not an array) returns `false` —
452
+ * fail-closed, matching this module's convention throughout.
453
+ */
454
+ function planClaimsGapId(phaseFullDir, planBasename, gapId) {
455
+ let raw;
456
+ try {
457
+ raw = node_fs_1.default.readFileSync(node_path_1.default.join(phaseFullDir, planBasename), 'utf-8');
458
+ }
459
+ catch {
460
+ return false;
461
+ }
462
+ let fm;
463
+ try {
464
+ fm = extractFrontmatter(raw, planBasename);
465
+ }
466
+ catch {
467
+ return false;
468
+ }
469
+ const gapIds = fm['gap_ids'];
470
+ if (!Array.isArray(gapIds))
471
+ return false;
472
+ return gapIds.some((id) => typeof id === 'string' && id.trim() === gapId.trim());
473
+ }
192
474
  // ─── evaluateUatPassed ────────────────────────────────────────────────────────
193
475
  /**
194
476
  * Evaluate all UAT/VERIFICATION files in a phase directory.
@@ -198,7 +480,13 @@ function parseUatResultItems(cleanContent) {
198
480
  * Pass requires at least one real passing check AND no blockers.
199
481
  */
200
482
  function evaluateUatPassed(phaseFullDir, opts) {
201
- const requireVerification = opts?.policy?.requireVerification === true;
483
+ // uatOnly (#4663) takes precedence: it evaluates the UAT rows ONLY, skipping
484
+ // the VERIFICATION-file blockers entirely. The verify-work canonicalize
485
+ // pre-check needs exactly that — it runs while the report still reads
486
+ // `human_needed`, which is itself a blocking verification status, so the
487
+ // full predicate could never pass there and the flip would deadlock.
488
+ const uatOnly = opts?.policy?.uatOnly === true;
489
+ const requireVerification = !uatOnly && opts?.policy?.requireVerification === true;
202
490
  const blockers = [];
203
491
  const checks = [];
204
492
  const uatFiles = [];
@@ -221,7 +509,7 @@ function evaluateUatPassed(phaseFullDir, opts) {
221
509
  checks: [],
222
510
  blockers,
223
511
  no_uat_artifacts,
224
- policy: { require_verification: requireVerification },
512
+ policy: { require_verification: requireVerification, uat_only: uatOnly },
225
513
  // readVerificationStatus was never reached on this early-return path.
226
514
  verification_stale_check_indeterminate: false,
227
515
  };
@@ -234,8 +522,15 @@ function evaluateUatPassed(phaseFullDir, opts) {
234
522
  const verFileNames = scopeToPhase(dirEntries.filter(f => f.includes('-VERIFICATION') && f.endsWith('.md')), phaseDirBaseName);
235
523
  // ── Process UAT files ──────────────────────────────────────────────────────
236
524
  for (const file of uatFileNames) {
237
- uatFiles.push(file);
238
525
  const uatFilePath = node_path_1.default.join(phaseFullDir, file);
526
+ // #5118 security review (SEC-1): containment BEFORE the read, exactly as
527
+ // for the VERIFICATION loop below — a `*-UAT.md` / `*-HUMAN-UAT.md` whose
528
+ // real path escapes the phase directory is treated as absent (not listed,
529
+ // no blocker naming it), so the `### N. <name>` text it points at never
530
+ // reaches a blocker or a check row.
531
+ if (!isReportContained(phaseFullDir, uatFilePath))
532
+ continue;
533
+ uatFiles.push(file);
239
534
  let raw = '';
240
535
  try {
241
536
  // #3078-CR MEDIUM: normalize line endings at the read boundary — the
@@ -268,14 +563,29 @@ function evaluateUatPassed(phaseFullDir, opts) {
268
563
  // Parse test items from the cleaned body (hardened against false positives)
269
564
  const cleanContent = stripFalsePositiveContexts(raw);
270
565
  const items = parseUatResultItems(cleanContent);
566
+ // #4983: parsed once per file — `evaluateUatPassed` already has `dirEntries`
567
+ // from the readdir above, so `isTestGapResolved` can verify `resolved_by`
568
+ // against this phase directory's own files.
569
+ const gapEntries = parseGapsEntries(cleanContent);
271
570
  for (const item of items) {
272
- const passing = PASSING_RESULTS.has(item.result);
571
+ // #4546: a `skipped` item whose reason matches the verify-work writer's
572
+ // "Deferred follow-up:" template is a deliberately deferred follow-up
573
+ // (#1921) — non-blocking. Quote-tolerant because the writer emits the
574
+ // reason WITH its wrapping quotes. Everything else — pending, blocked,
575
+ // issue, missing, and a plain or non-deferral skipped — still blocks.
576
+ const deferred = item.result === 'skipped' && DEFERRED_REASON_RE.test(item.reason);
577
+ // #4983: an `issue` item whose `## Gaps` entry has been verified-resolved
578
+ // (see isTestGapResolved) is a genuinely fixed issue — non-blocking.
579
+ const resolved = item.result === 'issue' && isTestGapResolved(gapEntries, item.test, dirEntries, phaseFullDir);
580
+ const passing = PASSING_RESULTS.has(item.result) || deferred || resolved;
273
581
  checks.push({
274
582
  file,
275
583
  test: item.test,
276
584
  name: item.name,
277
585
  result: item.result,
278
586
  passing,
587
+ deferred,
588
+ resolved,
279
589
  });
280
590
  if (!passing) {
281
591
  blockers.push(`${file}: test ${item.test} (${item.result})`);
@@ -284,9 +594,14 @@ function evaluateUatPassed(phaseFullDir, opts) {
284
594
  }
285
595
  // ── Process VERIFICATION files ─────────────────────────────────────────────
286
596
  let hasPassingVerification = false;
287
- for (const file of verFileNames) {
597
+ for (const file of uatOnly ? [] : verFileNames) {
288
598
  verificationFiles.push(file);
289
599
  const verificationFilePath = node_path_1.default.join(phaseFullDir, file);
600
+ // #5118 security review (S1): containment BEFORE the read — a report whose
601
+ // real path escapes the planning root reads `missing` (no status counts,
602
+ // no blocker) and not a byte of it reaches any message.
603
+ if (!isReportContained(phaseFullDir, verificationFilePath))
604
+ continue;
290
605
  let raw = '';
291
606
  try {
292
607
  // #3078-CR MEDIUM: same read-boundary normalization as the UAT loop above.
@@ -296,16 +611,17 @@ function evaluateUatPassed(phaseFullDir, opts) {
296
611
  blockers.push(`${file}: could not read verification file`);
297
612
  continue;
298
613
  }
299
- const vfm = extractFrontmatter(raw, verificationFilePath);
300
- const vStatus = vfm['status'];
301
- if (vStatus && BLOCKING_VERIFICATION_FM_STATUSES.has(vStatus)) {
614
+ // #5118: judged by the owner's report reader — a status outside the
615
+ // closed writer set throws VerificationStatusError (no silent pass-through).
616
+ const vStatus = reportStatusOf(extractFrontmatter(raw, verificationFilePath), verificationFilePath);
617
+ if (vStatus === VERIFICATION_STATUS.HUMAN_NEEDED || vStatus === VERIFICATION_STATUS.GAPS_FOUND) {
302
618
  blockers.push(`${file}: verification status=${vStatus}`);
303
619
  }
304
- else if (vStatus && PASSING_VERIFICATION_STATUSES.has(vStatus)) {
305
- // Allowlist: only explicitly-passing statuses count
620
+ else if (vStatus === VERIFICATION_STATUS.PASSED) {
621
+ // Allowlist: only an explicitly-passing status counts
306
622
  hasPassingVerification = true;
307
623
  }
308
- // Missing or unknown status: does NOT count as passing, does NOT push a blocker
624
+ // No status: does NOT count as passing, does NOT push a blocker
309
625
  // (handled by the requireVerification policy check below if needed)
310
626
  }
311
627
  // ── Policy: requireVerification ───────────────────────────────────────────
@@ -318,10 +634,10 @@ function evaluateUatPassed(phaseFullDir, opts) {
318
634
  const verificationResult = readVerificationStatus(phaseFullDir);
319
635
  const verificationStatus = verificationResult.status;
320
636
  verificationStaleCheckIndeterminate = verificationResult.staleCheckIndeterminate === true;
321
- if (verificationStatus === 'stale') {
322
- blockers.push('policy: verification status=stale');
637
+ if (verificationStatus === VERIFICATION_STATUS.STALE) {
638
+ blockers.push(`policy: verification status=${VERIFICATION_STATUS.STALE}`);
323
639
  }
324
- else if (verificationStatus !== 'passed' || !hasPassingVerification) {
640
+ else if (verificationStatus !== VERIFICATION_STATUS.PASSED || !hasPassingVerification) {
325
641
  blockers.push('policy: verification required but no passing *-VERIFICATION.md found');
326
642
  }
327
643
  }
@@ -340,6 +656,7 @@ function evaluateUatPassed(phaseFullDir, opts) {
340
656
  no_uat_artifacts,
341
657
  policy: {
342
658
  require_verification: requireVerification,
659
+ uat_only: uatOnly,
343
660
  },
344
661
  verification_stale_check_indeterminate: verificationStaleCheckIndeterminate,
345
662
  };
@@ -349,4 +666,8 @@ module.exports = {
349
666
  parseUatResultItems,
350
667
  analyzeMarkdown,
351
668
  evaluateUatPassed,
669
+ // #4983: exported for direct unit-testability of the Gaps-resolution
670
+ // parser/validator, independent of a full evaluateUatPassed round-trip.
671
+ parseGapsEntries,
672
+ isTestGapResolved,
352
673
  };