@opengsd/gsd-core 1.15.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 (487) 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/agents/gsd-code-fixer.compact.md +1 -1
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-code-reviewer.compact.md +5 -3
  7. package/agents/gsd-code-reviewer.md +8 -6
  8. package/agents/gsd-debug-session-manager.compact.md +1 -1
  9. package/agents/gsd-debug-session-manager.md +1 -1
  10. package/agents/gsd-debugger.md +2 -2
  11. package/agents/gsd-eval-auditor.compact.md +1 -1
  12. package/agents/gsd-eval-auditor.md +1 -1
  13. package/agents/gsd-executor.md +4 -4
  14. package/agents/gsd-intel-updater.compact.md +1 -1
  15. package/agents/gsd-intel-updater.md +1 -1
  16. package/agents/gsd-mempalace-curator.md +2 -2
  17. package/agents/gsd-phase-researcher.md +1 -1
  18. package/agents/gsd-plan-checker.md +7 -2
  19. package/agents/gsd-planner.md +3 -3
  20. package/agents/gsd-project-researcher.compact.md +1 -1
  21. package/agents/gsd-project-researcher.md +1 -1
  22. package/agents/gsd-research-synthesizer.compact.md +1 -1
  23. package/agents/gsd-research-synthesizer.md +1 -1
  24. package/agents/gsd-ui-auditor.compact.md +21 -30
  25. package/agents/gsd-ui-auditor.md +42 -51
  26. package/agents/gsd-ui-researcher.compact.md +1 -1
  27. package/agents/gsd-ui-researcher.md +1 -1
  28. package/agents/gsd-verifier.md +31 -9
  29. package/bin/install.js +126 -196
  30. package/commands/gsd/add-tests.md +6 -1
  31. package/commands/gsd/ai-integration-phase.md +6 -1
  32. package/commands/gsd/audit-fix.md +5 -0
  33. package/commands/gsd/audit-milestone.md +6 -1
  34. package/commands/gsd/autonomous.md +5 -0
  35. package/commands/gsd/capture.md +8 -4
  36. package/commands/gsd/code-review.md +7 -2
  37. package/commands/gsd/complete-milestone.md +4 -0
  38. package/commands/gsd/config.md +7 -3
  39. package/commands/gsd/debug.md +11 -7
  40. package/commands/gsd/discuss-phase.md +7 -3
  41. package/commands/gsd/docs-update.md +12 -7
  42. package/commands/gsd/eval-review.md +6 -1
  43. package/commands/gsd/execute-phase.md +12 -7
  44. package/commands/gsd/extract-learnings.md +5 -0
  45. package/commands/gsd/fast.md +4 -0
  46. package/commands/gsd/forensics.md +5 -1
  47. package/commands/gsd/graphify.md +10 -6
  48. package/commands/gsd/health.md +5 -0
  49. package/commands/gsd/help.md +7 -2
  50. package/commands/gsd/import.md +7 -3
  51. package/commands/gsd/inbox.md +5 -0
  52. package/commands/gsd/ingest-docs.md +5 -1
  53. package/commands/gsd/manager.md +6 -1
  54. package/commands/gsd/map-codebase.md +7 -3
  55. package/commands/gsd/mempalace-capture.md +5 -1
  56. package/commands/gsd/mempalace-recall.md +5 -1
  57. package/commands/gsd/milestone-summary.md +5 -1
  58. package/commands/gsd/mvp-phase.md +8 -3
  59. package/commands/gsd/new-milestone.md +6 -1
  60. package/commands/gsd/new-project.md +5 -0
  61. package/commands/gsd/next.md +6 -1
  62. package/commands/gsd/ns-context.md +4 -0
  63. package/commands/gsd/ns-ideate.md +4 -0
  64. package/commands/gsd/ns-manage.md +4 -0
  65. package/commands/gsd/ns-project.md +4 -0
  66. package/commands/gsd/ns-review.md +4 -0
  67. package/commands/gsd/ns-workflow.md +4 -0
  68. package/commands/gsd/onboard.md +6 -1
  69. package/commands/gsd/pause-work.md +5 -1
  70. package/commands/gsd/phase.md +8 -4
  71. package/commands/gsd/plan-phase.md +6 -1
  72. package/commands/gsd/plan-review-convergence.md +5 -1
  73. package/commands/gsd/pr-branch.md +4 -0
  74. package/commands/gsd/profile-user.md +5 -1
  75. package/commands/gsd/progress.md +6 -1
  76. package/commands/gsd/quick-batch.md +20 -8
  77. package/commands/gsd/quick.md +12 -7
  78. package/commands/gsd/review.md +5 -1
  79. package/commands/gsd/secure-phase.md +6 -1
  80. package/commands/gsd/ship.md +5 -0
  81. package/commands/gsd/sketch.md +7 -2
  82. package/commands/gsd/spec-phase.md +5 -1
  83. package/commands/gsd/spike.md +8 -3
  84. package/commands/gsd/surface.md +5 -1
  85. package/commands/gsd/thread.md +4 -0
  86. package/commands/gsd/ui-phase.md +6 -1
  87. package/commands/gsd/ui-review.md +6 -1
  88. package/commands/gsd/ultraplan-phase.md +5 -1
  89. package/commands/gsd/undo.md +5 -1
  90. package/commands/gsd/update.md +6 -2
  91. package/commands/gsd/validate-phase.md +6 -1
  92. package/commands/gsd/verify-work.md +6 -1
  93. package/commands/gsd/workspace.md +7 -3
  94. package/gsd-core/bin/gsd-tools.cjs +142 -56
  95. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  96. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  97. package/gsd-core/bin/lib/audit.cjs +78 -44
  98. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  99. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  100. package/gsd-core/bin/lib/capability-registry.cjs +183 -103
  101. package/gsd-core/bin/lib/capability-validator.cjs +16 -0
  102. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  103. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  104. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  105. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  106. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  107. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  108. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  109. package/gsd-core/bin/lib/commands.cjs +173 -170
  110. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  111. package/gsd-core/bin/lib/config.cjs +43 -12
  112. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  113. package/gsd-core/bin/lib/coverage.cjs +4 -8
  114. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  115. package/gsd-core/bin/lib/drift.cjs +177 -42
  116. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  117. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  118. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  119. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  120. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  121. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  122. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  123. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  124. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  125. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  126. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  127. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  128. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  129. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  130. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  131. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  132. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  133. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  134. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  135. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  136. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  137. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  138. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  139. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  140. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  141. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  142. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  143. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  144. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  145. package/gsd-core/bin/lib/init.cjs +162 -96
  146. package/gsd-core/bin/lib/install-engine.cjs +19 -35
  147. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  148. package/gsd-core/bin/lib/io.cjs +122 -3
  149. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  150. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  151. package/gsd-core/bin/lib/milestone.cjs +19 -1
  152. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  153. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  154. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  155. package/gsd-core/bin/lib/pattern.cjs +10 -0
  156. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  157. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  158. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  159. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  160. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  161. package/gsd-core/bin/lib/phase.cjs +280 -80
  162. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  163. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  164. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  165. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  166. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  167. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  168. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  169. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  170. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  171. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  172. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  173. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  174. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  175. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  176. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  177. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  178. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  179. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
  180. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  181. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  182. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  183. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
  184. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  185. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  186. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  187. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  188. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  189. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  190. package/gsd-core/bin/lib/state-document.cjs +100 -22
  191. package/gsd-core/bin/lib/state.cjs +214 -105
  192. package/gsd-core/bin/lib/surface.cjs +2 -1
  193. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  194. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  195. package/gsd-core/bin/lib/uat.cjs +424 -7
  196. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  197. package/gsd-core/bin/lib/vendor/README.md +31 -9
  198. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  199. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  200. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  201. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  202. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  203. package/gsd-core/bin/lib/verification.cjs +1165 -314
  204. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  205. package/gsd-core/bin/lib/verify.cjs +286 -567
  206. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  207. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  208. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  209. package/gsd-core/bin/lib/workstream.cjs +11 -2
  210. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  211. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  212. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  213. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  214. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  215. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  216. package/gsd-core/references/execute-phase-response-language.md +1 -1
  217. package/gsd-core/references/gsd-run-resolver.md +1 -1
  218. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  219. package/gsd-core/references/offer-next.md +1 -1
  220. package/gsd-core/references/planning-config.md +1 -1
  221. package/gsd-core/references/spidr-splitting.md +1 -1
  222. package/gsd-core/references/tdd.md +10 -4
  223. package/gsd-core/references/verifier-phase-gates.md +5 -2
  224. package/gsd-core/references/verify-mvp-mode.md +2 -2
  225. package/gsd-core/references/workstream-flag.md +33 -3
  226. package/gsd-core/templates/README.md +1 -1
  227. package/gsd-core/templates/UAT.md +17 -1
  228. package/gsd-core/templates/config.json +2 -11
  229. package/gsd-core/templates/verification-report.md +1 -1
  230. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  231. package/gsd-core/workflows/add-backlog.md +1 -1
  232. package/gsd-core/workflows/add-phase.md +8 -7
  233. package/gsd-core/workflows/add-tests.md +3 -2
  234. package/gsd-core/workflows/add-todo.md +4 -3
  235. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  236. package/gsd-core/workflows/audit-fix.md +1 -1
  237. package/gsd-core/workflows/audit-milestone.md +4 -3
  238. package/gsd-core/workflows/audit-uat.md +1 -1
  239. package/gsd-core/workflows/autonomous.md +28 -18
  240. package/gsd-core/workflows/check-todos.md +6 -5
  241. package/gsd-core/workflows/cleanup.md +1 -1
  242. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  243. package/gsd-core/workflows/code-review-fix.md +5 -4
  244. package/gsd-core/workflows/code-review.md +84 -90
  245. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  246. package/gsd-core/workflows/complete-milestone.md +12 -7
  247. package/gsd-core/workflows/debug.md +30 -5
  248. package/gsd-core/workflows/diagnose-issues.md +3 -2
  249. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  250. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  252. package/gsd-core/workflows/discuss-phase.md +4 -3
  253. package/gsd-core/workflows/do.md +1 -1
  254. package/gsd-core/workflows/docs-update.md +4 -3
  255. package/gsd-core/workflows/edit-phase.md +4 -3
  256. package/gsd-core/workflows/eval-review.md +5 -3
  257. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  258. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  259. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  260. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  261. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  263. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  264. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  265. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  266. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  267. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  268. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  269. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  270. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  271. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  272. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  273. package/gsd-core/workflows/execute-phase.md +78 -170
  274. package/gsd-core/workflows/execute-plan.md +13 -19
  275. package/gsd-core/workflows/explore.md +2 -2
  276. package/gsd-core/workflows/extract-learnings.md +3 -2
  277. package/gsd-core/workflows/fast.md +1 -1
  278. package/gsd-core/workflows/forensics.md +1 -1
  279. package/gsd-core/workflows/graduation.md +1 -1
  280. package/gsd-core/workflows/health.md +2 -1
  281. package/gsd-core/workflows/import.md +3 -2
  282. package/gsd-core/workflows/inbox.md +1 -1
  283. package/gsd-core/workflows/ingest-docs.md +1 -1
  284. package/gsd-core/workflows/insert-phase.md +4 -3
  285. package/gsd-core/workflows/list-seeds.md +1 -1
  286. package/gsd-core/workflows/list-workspaces.md +1 -1
  287. package/gsd-core/workflows/manager.md +5 -3
  288. package/gsd-core/workflows/map-codebase.md +4 -3
  289. package/gsd-core/workflows/milestone-summary.md +3 -2
  290. package/gsd-core/workflows/mvp-phase.md +14 -14
  291. package/gsd-core/workflows/new-milestone.md +10 -10
  292. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  293. package/gsd-core/workflows/new-project.md +1 -1
  294. package/gsd-core/workflows/new-workspace.md +1 -1
  295. package/gsd-core/workflows/next.md +1 -1
  296. package/gsd-core/workflows/pause-work.md +2 -2
  297. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  298. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  299. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  300. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  301. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  302. package/gsd-core/workflows/plan-phase.md +27 -18
  303. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  304. package/gsd-core/workflows/plant-seed.md +1 -1
  305. package/gsd-core/workflows/pr-branch.md +1 -1
  306. package/gsd-core/workflows/profile-user.md +1 -1
  307. package/gsd-core/workflows/progress.md +19 -49
  308. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  309. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  310. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  311. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  312. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  313. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  314. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  315. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  316. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  317. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  318. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  319. package/gsd-core/workflows/quick-batch.md +15 -10
  320. package/gsd-core/workflows/quick.md +42 -31
  321. package/gsd-core/workflows/remove-phase.md +3 -2
  322. package/gsd-core/workflows/remove-workspace.md +1 -1
  323. package/gsd-core/workflows/resume-project.md +3 -2
  324. package/gsd-core/workflows/review.md +3 -2
  325. package/gsd-core/workflows/scan.md +3 -2
  326. package/gsd-core/workflows/secure-phase.md +11 -12
  327. package/gsd-core/workflows/settings-advanced.md +1 -1
  328. package/gsd-core/workflows/settings-integrations.md +1 -1
  329. package/gsd-core/workflows/settings.md +1 -1
  330. package/gsd-core/workflows/ship.md +12 -12
  331. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  332. package/gsd-core/workflows/sketch.md +1 -1
  333. package/gsd-core/workflows/smart-entry.md +1 -1
  334. package/gsd-core/workflows/spec-phase.md +1 -1
  335. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  336. package/gsd-core/workflows/spike.md +1 -1
  337. package/gsd-core/workflows/stats.md +1 -1
  338. package/gsd-core/workflows/sync-skills.md +1 -1
  339. package/gsd-core/workflows/thread.md +2 -2
  340. package/gsd-core/workflows/transition.md +13 -23
  341. package/gsd-core/workflows/ui-phase.md +5 -4
  342. package/gsd-core/workflows/ui-review.md +4 -3
  343. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  344. package/gsd-core/workflows/undo.md +1 -1
  345. package/gsd-core/workflows/validate-phase.md +10 -12
  346. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  347. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  348. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  349. package/gsd-core/workflows/verify-work.md +68 -59
  350. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  351. package/hooks/dist/gsd-check-update-worker.js +8 -0
  352. package/hooks/dist/gsd-check-update.js +8 -0
  353. package/hooks/dist/gsd-context-monitor.js +31 -8
  354. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  355. package/hooks/dist/gsd-secret-read-guard.js +147 -16
  356. package/hooks/dist/gsd-statusline.js +15 -7
  357. package/hooks/dist/gsd-update-banner.js +8 -0
  358. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  359. package/hooks/dist/gsd-workflow-guard.js +5 -4
  360. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  361. package/hooks/dist/lib/cli-exit.js +12 -0
  362. package/hooks/dist/lib/git-probe.js +17 -1
  363. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  364. package/hooks/gsd-agent-isolation-guard.js +8 -0
  365. package/hooks/gsd-check-update-worker.js +8 -0
  366. package/hooks/gsd-check-update.js +8 -0
  367. package/hooks/gsd-context-monitor.js +31 -8
  368. package/hooks/gsd-cursor-subagent-start.js +8 -0
  369. package/hooks/gsd-secret-read-guard.js +147 -16
  370. package/hooks/gsd-statusline.js +15 -7
  371. package/hooks/gsd-update-banner.js +8 -0
  372. package/hooks/gsd-windsurf-pre-write.js +11 -2
  373. package/hooks/gsd-workflow-guard.js +5 -4
  374. package/hooks/gsd-worktree-path-guard.js +6 -2
  375. package/hooks/hooks.json +5 -5
  376. package/hooks/lib/cli-exit.js +12 -0
  377. package/hooks/lib/git-probe.js +17 -1
  378. package/hooks/lib/isolation-sentinel.js +2 -2
  379. package/package.json +21 -4
  380. package/scripts/changeset/parse.cjs +52 -4
  381. package/scripts/ci-timeout-report.cjs +770 -4
  382. package/scripts/command-contract-helpers.cjs +12 -8
  383. package/scripts/docs-guard-registry.cjs +6 -0
  384. package/scripts/gen-features.cjs +13 -8
  385. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  386. package/scripts/gen-loop-host-contract.cjs +10 -1
  387. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  388. package/scripts/gen-plugin-skills.cjs +87 -1
  389. package/scripts/gen-research-agents.cjs +24 -31
  390. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  391. package/scripts/gen-test-timings.cjs +32 -7
  392. package/scripts/lib/cli-exit.cjs +12 -0
  393. package/scripts/lib/macos-conformance-tier.generated.cjs +20 -2
  394. package/scripts/lib/ndjson-reporter.cjs +28 -3
  395. package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
  396. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  397. package/scripts/lib/vendor-bundle.cjs +59 -0
  398. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  399. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  400. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  401. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  402. package/scripts/lint-descriptions.cjs +7 -3
  403. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  404. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  405. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  406. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  407. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  408. package/scripts/lint-phase-id-drift.cjs +29 -0
  409. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  410. package/scripts/lint-response-language-coverage.cjs +3 -0
  411. package/scripts/lint-skill-deps.cjs +7 -3
  412. package/scripts/lint-test-file-count.allowlist.json +11 -1
  413. package/scripts/lint-test-file-count.cjs +34 -1
  414. package/scripts/lint-vendored-deps.cjs +41 -5
  415. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  416. package/scripts/mutation-matrix.cjs +50 -5
  417. package/scripts/require-issue-link-policy.cjs +6 -2
  418. package/scripts/sync-runtime-launcher.cjs +184 -2
  419. package/scripts/verify-npm-publish.cjs +76 -20
  420. package/skills/gsd-add-tests/SKILL.md +6 -1
  421. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  422. package/skills/gsd-audit-fix/SKILL.md +5 -0
  423. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  424. package/skills/gsd-autonomous/SKILL.md +5 -0
  425. package/skills/gsd-capture/SKILL.md +8 -4
  426. package/skills/gsd-code-review/SKILL.md +7 -2
  427. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  428. package/skills/gsd-config/SKILL.md +8 -4
  429. package/skills/gsd-debug/SKILL.md +11 -7
  430. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  431. package/skills/gsd-docs-update/SKILL.md +12 -7
  432. package/skills/gsd-eval-review/SKILL.md +6 -1
  433. package/skills/gsd-execute-phase/SKILL.md +12 -7
  434. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  435. package/skills/gsd-fast/SKILL.md +4 -0
  436. package/skills/gsd-forensics/SKILL.md +5 -1
  437. package/skills/gsd-graphify/SKILL.md +10 -6
  438. package/skills/gsd-health/SKILL.md +5 -0
  439. package/skills/gsd-help/SKILL.md +7 -2
  440. package/skills/gsd-import/SKILL.md +7 -3
  441. package/skills/gsd-inbox/SKILL.md +5 -0
  442. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  443. package/skills/gsd-manager/SKILL.md +6 -1
  444. package/skills/gsd-map-codebase/SKILL.md +7 -3
  445. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  446. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  447. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  448. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  449. package/skills/gsd-new-milestone/SKILL.md +6 -1
  450. package/skills/gsd-new-project/SKILL.md +5 -0
  451. package/skills/gsd-next/SKILL.md +6 -1
  452. package/skills/gsd-ns-context/SKILL.md +4 -0
  453. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  454. package/skills/gsd-ns-manage/SKILL.md +4 -0
  455. package/skills/gsd-ns-project/SKILL.md +4 -0
  456. package/skills/gsd-ns-review/SKILL.md +4 -0
  457. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  458. package/skills/gsd-onboard/SKILL.md +6 -1
  459. package/skills/gsd-pause-work/SKILL.md +5 -1
  460. package/skills/gsd-phase/SKILL.md +8 -4
  461. package/skills/gsd-plan-phase/SKILL.md +6 -1
  462. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  463. package/skills/gsd-pr-branch/SKILL.md +4 -0
  464. package/skills/gsd-profile-user/SKILL.md +5 -1
  465. package/skills/gsd-progress/SKILL.md +6 -1
  466. package/skills/gsd-quick/SKILL.md +16 -10
  467. package/skills/gsd-quick-batch/SKILL.md +20 -8
  468. package/skills/gsd-review/SKILL.md +5 -1
  469. package/skills/gsd-review-backlog/SKILL.md +3 -2
  470. package/skills/gsd-secure-phase/SKILL.md +6 -1
  471. package/skills/gsd-ship/SKILL.md +5 -0
  472. package/skills/gsd-sketch/SKILL.md +7 -2
  473. package/skills/gsd-spec-phase/SKILL.md +5 -1
  474. package/skills/gsd-spike/SKILL.md +8 -3
  475. package/skills/gsd-surface/SKILL.md +5 -1
  476. package/skills/gsd-thread/SKILL.md +4 -0
  477. package/skills/gsd-ui-phase/SKILL.md +6 -1
  478. package/skills/gsd-ui-review/SKILL.md +6 -1
  479. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  480. package/skills/gsd-undo/SKILL.md +5 -1
  481. package/skills/gsd-update/SKILL.md +6 -2
  482. package/skills/gsd-validate-phase/SKILL.md +6 -1
  483. package/skills/gsd-verify-work/SKILL.md +6 -1
  484. package/skills/gsd-workspace/SKILL.md +7 -3
  485. package/skills/gsd-workstreams/SKILL.md +6 -6
  486. package/vscode/package.json +1 -1
  487. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestStatus = exports.ReportFormat = void 0;
4
+ exports.parseTestReport = parseTestReport;
5
+ /** Format adapters for RED evidence. Parsing never depends on the runner command. */
6
+ const tap_parser_cjs_1 = require("./vendor/tap-parser.cjs");
7
+ const saxes_cjs_1 = require("./vendor/saxes.cjs");
8
+ exports.ReportFormat = {
9
+ Tap: 'tap', Junit: 'junit', SwiftTesting: 'swift-testing', Unittest: 'unittest', Unknown: 'unknown',
10
+ };
11
+ exports.TestStatus = { Passed: 'passed', Failed: 'failed', Skipped: 'skipped', Todo: 'todo' };
12
+ function parseTap(output) {
13
+ const report = { format: exports.ReportFormat.Tap, valid: true, tests: [], issues: [] };
14
+ const parser = new tap_parser_cjs_1.Parser({ strict: true });
15
+ const assertions = [];
16
+ const openBuffered = new Map();
17
+ let completed = false;
18
+ const watch = (p) => {
19
+ p.on('child', (child) => {
20
+ if (child.buffered)
21
+ openBuffered.set(p, (openBuffered.get(p) ?? 0) + 1);
22
+ watch(child);
23
+ });
24
+ // tap-parser finalizes buffered children at EOF even without their closing
25
+ // brace. Track its child/line events so that truncation cannot prove RED.
26
+ p.on('line', (line) => {
27
+ if (line === '}\n')
28
+ openBuffered.set(p, (openBuffered.get(p) ?? 0) - 1);
29
+ });
30
+ p.on('assert', (result) => {
31
+ if (!result.closingTestPoint)
32
+ assertions.push({ result, owner: p });
33
+ if (result.buffered && !result.closingTestPoint)
34
+ report.issues.push('Missing buffered TAP subtest');
35
+ const diag = result.diag;
36
+ if (diag && ['cancelledByParent', 'hookFailed', 'testTimeoutFailure'].includes(String(diag['failureType']))) {
37
+ report.issues.push('Test run interrupted by cancellation, hook failure, or timeout');
38
+ }
39
+ });
40
+ p.on('extra', (extra) => { if (extra.trim())
41
+ report.issues.push('Non-TAP data in report'); });
42
+ p.on('complete', (summary) => {
43
+ if (p === parser)
44
+ completed = true;
45
+ const { start, end } = summary.plan;
46
+ if (start !== 1 || end === null || summary.count !== end)
47
+ report.issues.push('Missing or incomplete TAP plan');
48
+ if (summary.bailout)
49
+ report.issues.push('TAP bailout');
50
+ if (summary.failures.some((failure) => failure.tapError))
51
+ report.issues.push('Malformed TAP');
52
+ });
53
+ };
54
+ watch(parser);
55
+ parser.end(output);
56
+ if ([...openBuffered.values()].some((count) => count !== 0))
57
+ report.issues.push('Unclosed buffered TAP subtest');
58
+ for (const { result, owner } of assertions) {
59
+ let skipped = Boolean(result.skip);
60
+ let todo = Boolean(result.todo);
61
+ for (let parent = owner; parent; parent = parent.parent) {
62
+ skipped ||= Boolean(parent.closingTestPoint?.skip);
63
+ todo ||= Boolean(parent.closingTestPoint?.todo);
64
+ }
65
+ report.tests.push({
66
+ name: result.name,
67
+ identities: [...new Set([result.name, result.fullname])],
68
+ group: null,
69
+ groupIdentities: [],
70
+ status: skipped ? exports.TestStatus.Skipped : todo ? exports.TestStatus.Todo : result.ok ? exports.TestStatus.Passed : exports.TestStatus.Failed,
71
+ });
72
+ }
73
+ if (!completed)
74
+ report.issues.push('Incomplete TAP stream');
75
+ report.valid = report.issues.length === 0;
76
+ return report;
77
+ }
78
+ /** Surefire and Failsafe share the JUnit XML report shape. */
79
+ function parseJunit(output) {
80
+ const report = { format: exports.ReportFormat.Junit, valid: true, tests: [], issues: [] };
81
+ const parser = new saxes_cjs_1.SaxesParser({ xmlns: false });
82
+ const elements = [];
83
+ const suites = [];
84
+ let current = null;
85
+ parser.on('error', () => { report.issues.push('Malformed XML'); });
86
+ parser.on('doctype', () => { report.issues.push('JUnit reports must not declare a DTD'); });
87
+ parser.on('opentag', (tag) => {
88
+ const parent = elements.at(-1);
89
+ if (!parent && tag.name !== 'testsuite' && tag.name !== 'testsuites')
90
+ report.issues.push('Not a JUnit report');
91
+ if (tag.name === 'testsuite' || tag.name === 'testsuites') {
92
+ if (parent && parent !== 'testsuite' && parent !== 'testsuites')
93
+ report.issues.push('Misplaced test suite');
94
+ const count = tag.attributes['tests'];
95
+ const planned = count === undefined ? null : Number(count);
96
+ if (count !== undefined && (!/^\d+$/.test(count) || !Number.isSafeInteger(planned)))
97
+ report.issues.push('Invalid JUnit test count');
98
+ suites.push({ firstTest: report.tests.length, planned });
99
+ }
100
+ else if (tag.name === 'testcase') {
101
+ if (parent !== 'testsuite' || current)
102
+ report.issues.push('Misplaced test case');
103
+ const name = tag.attributes['name'] ?? '';
104
+ const className = tag.attributes['classname'] ?? '';
105
+ const qualified = className ? `${className}#${name}` : name;
106
+ if (!name)
107
+ report.issues.push('Unnamed test case');
108
+ current = {
109
+ name: qualified,
110
+ identities: [...new Set([name, qualified])],
111
+ group: className || null,
112
+ groupIdentities: className ? [className, className.split('.').at(-1)] : [],
113
+ status: exports.TestStatus.Passed,
114
+ };
115
+ }
116
+ else if (current && parent === 'testcase') {
117
+ if (tag.name === 'failure' || tag.name === 'error') {
118
+ if (current.status === exports.TestStatus.Skipped)
119
+ report.issues.push('Contradictory JUnit status');
120
+ current.status = exports.TestStatus.Failed;
121
+ }
122
+ if (tag.name === 'skipped') {
123
+ if (current.status === exports.TestStatus.Failed)
124
+ report.issues.push('Contradictory JUnit status');
125
+ current.status = exports.TestStatus.Skipped;
126
+ }
127
+ }
128
+ elements.push(tag.name);
129
+ });
130
+ parser.on('closetag', (tag) => {
131
+ if (tag.name === 'testcase' && current) {
132
+ report.tests.push(current);
133
+ current = null;
134
+ }
135
+ else if (tag.name === 'testsuite' || tag.name === 'testsuites') {
136
+ const suite = suites.pop();
137
+ if (!suite || (suite.planned !== null && suite.planned !== report.tests.length - suite.firstTest)) {
138
+ report.issues.push('Incomplete JUnit test suite');
139
+ }
140
+ }
141
+ elements.pop();
142
+ });
143
+ parser.write(output).close();
144
+ report.valid = report.issues.length === 0;
145
+ return report;
146
+ }
147
+ /**
148
+ * swift-testing console output (#4957). Only the anchored aggregate line marks
149
+ * the format; every declared test must have its own result line. The aggregate
150
+ * counts skipped tests too, and a parameterized test names its case count.
151
+ * A test is named by its quoted display name (with `(aka 'function()')` under
152
+ * --verbose) or, without one, by its bare function name. Windows consoles
153
+ * print โˆš ร— - in place of โœ” โœ˜ โ”. swift-testing 6.1 omits the suite count from
154
+ * the aggregate and marks skipped tests with โœ˜ (later sources add the count
155
+ * and use โžœ).
156
+ */
157
+ const SWIFT_AGGREGATE = /^[ \t]*[โœ˜โœ”โ”ร—โˆš-][ \t]*Test run with (\d+) tests?(?: in \d+ suites?)? (passed|failed)\b/gm;
158
+ const SWIFT_RESULT = /^[ \t]*[โœ˜โœ”โ”โžœร—โˆš-][ \t]*Test (?:"([^"]+)"(?: \(aka '([^']+)'\))?|([^\s"]\S*\)))(?: with \d+ test cases?)? (?:(failed|passed|was cancelled) after [\d.]+ seconds?(?: with \d+ [^\n]*?)?(?:\.|: "[^\n]*")|(skipped)(?:\.|: "[^\n]*"))$/gm;
159
+ function parseSwiftTesting(output) {
160
+ const report = { format: exports.ReportFormat.SwiftTesting, valid: true, tests: [], issues: [] };
161
+ let declared = 0;
162
+ let runPassed = true;
163
+ for (const match of output.matchAll(SWIFT_AGGREGATE)) {
164
+ declared += Number(match[1]);
165
+ runPassed &&= match[2] === 'passed';
166
+ }
167
+ for (const match of output.matchAll(SWIFT_RESULT)) {
168
+ const fn = match[2] ?? match[3];
169
+ report.tests.push({
170
+ name: match[1] ?? match[3],
171
+ identities: [...new Set([match[1], fn, fn?.replace(/\(.*\)$/, '')].filter((id) => Boolean(id)))],
172
+ group: null,
173
+ groupIdentities: [],
174
+ status: match[4] === 'failed' ? exports.TestStatus.Failed : match[4] === 'passed' ? exports.TestStatus.Passed : exports.TestStatus.Skipped,
175
+ });
176
+ }
177
+ if (report.tests.length !== declared)
178
+ report.issues.push('Incomplete swift-testing report');
179
+ if (runPassed && report.tests.some((test) => test.status === exports.TestStatus.Failed)) {
180
+ report.issues.push('Contradictory swift-testing status');
181
+ }
182
+ report.valid = report.issues.length === 0;
183
+ return report;
184
+ }
185
+ /**
186
+ * Python stdlib unittest text output (#4970). Passing tests are unnamed unless
187
+ * verbose, so they are counted from the summary; failures are named by their
188
+ * FAIL:/ERROR: headers, which must account for every counted failure. Each
189
+ * failing subTest repeats its method's header and counts as a failure, while
190
+ * Ran counts methods, so identical headers collapse into one failed test. An
191
+ * unexpected success fails the run but is a test that passed.
192
+ */
193
+ const UNITTEST_RAN = /^Ran (\d+) tests? in [\d.]+s$/gm;
194
+ const UNITTEST_HEADER = /^(?:FAIL|ERROR): (\S+)(?: \(([^)]*)\))?/gm;
195
+ function parseUnittest(output) {
196
+ const report = { format: exports.ReportFormat.Unittest, valid: true, tests: [], issues: [] };
197
+ const runs = [...output.matchAll(UNITTEST_RAN)];
198
+ if (runs.length !== 1) {
199
+ report.issues.push('Expected exactly one unittest summary');
200
+ report.valid = false;
201
+ return report;
202
+ }
203
+ const ran = Number(runs[0][1]);
204
+ const outcome = /^(OK|FAILED)(?: \(([^)]*)\))?$/m.exec(output.slice(runs[0].index + runs[0][0].length));
205
+ const counts = {};
206
+ for (const part of outcome?.[2]?.split(', ') ?? []) {
207
+ const [key, value] = part.split('=');
208
+ counts[key] = Number(value);
209
+ }
210
+ if (!outcome || Object.keys(counts).some((key) => !['failures', 'errors', 'skipped', 'expected failures', 'unexpected successes'].includes(key))) {
211
+ report.issues.push('Unsupported unittest outcome');
212
+ }
213
+ let headers = 0;
214
+ const failedIds = new Set();
215
+ for (const match of output.matchAll(UNITTEST_HEADER)) {
216
+ headers++;
217
+ const id = match[2] ?? '';
218
+ const key = `${match[1]} ${id}`;
219
+ if (failedIds.has(key))
220
+ continue;
221
+ failedIds.add(key);
222
+ // unittest synthesizes _FailedTest for an import/collection crash; its
223
+ // method name can equal the target, so it is a load failure, never RED.
224
+ if (id.includes('_FailedTest')) {
225
+ report.issues.push('unittest module failed to load');
226
+ continue;
227
+ }
228
+ report.tests.push({
229
+ name: match[1],
230
+ identities: [...new Set([match[1], id].filter(Boolean))],
231
+ group: null,
232
+ groupIdentities: [],
233
+ status: exports.TestStatus.Failed,
234
+ });
235
+ }
236
+ const failed = (counts['failures'] ?? 0) + (counts['errors'] ?? 0);
237
+ const skipped = counts['skipped'] ?? 0;
238
+ const runFailed = failed > 0 || (counts['unexpected successes'] ?? 0) > 0;
239
+ if (headers !== failed || (outcome?.[1] === 'FAILED') !== runFailed || failedIds.size + skipped > ran) {
240
+ report.issues.push('Incomplete unittest report');
241
+ }
242
+ const unnamed = (status) => ({ name: '', identities: [], group: null, groupIdentities: [], status });
243
+ for (let i = 0; i < skipped; i++)
244
+ report.tests.push(unnamed(exports.TestStatus.Skipped));
245
+ for (let i = Math.max(ran - failedIds.size - skipped, 0); i > 0; i--)
246
+ report.tests.push(unnamed(exports.TestStatus.Passed));
247
+ report.valid = report.issues.length === 0;
248
+ return report;
249
+ }
250
+ const adapters = [
251
+ { matches: (output) => /^(?:TAP version \d+|(?:not )?ok\b|1\.\.\d+|#)/.test(output.trimStart()), parse: parseTap },
252
+ { matches: (output) => output.trimStart().startsWith('<'), parse: parseJunit },
253
+ { matches: (output) => new RegExp(SWIFT_AGGREGATE.source, 'm').test(output), parse: parseSwiftTesting },
254
+ { matches: (output) => new RegExp(UNITTEST_RAN.source, 'm').test(output), parse: parseUnittest },
255
+ ];
256
+ /** Parse supported reports into one result contract; unknown/malformed input fails closed. */
257
+ function parseTestReport(output) {
258
+ const adapter = adapters.find((candidate) => candidate.matches(output));
259
+ if (!adapter)
260
+ return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Unsupported report format'] };
261
+ try {
262
+ const report = adapter.parse(output);
263
+ // One entry per problem: a long run of bad lines must not amplify the payload.
264
+ return { ...report, issues: [...new Set(report.issues)] };
265
+ }
266
+ catch {
267
+ return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Malformed report'] };
268
+ }
269
+ }
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const command_aliases_cjs_1 = require("./command-aliases.cjs");
16
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
16
17
  // eslint-disable-next-line @typescript-eslint/no-require-imports
17
18
  const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
18
19
  const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
@@ -145,14 +146,13 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
145
146
  if (roadmapContent.trim() === '') {
146
147
  warnings.push({ code: 'V002', message: 'ROADMAP.md is empty' });
147
148
  }
148
- // Malformed frontmatter โ€” a `---` opener with no matching closer.
149
- // Tolerate a leading BOM (#3057) before the fence.
150
- const contentAfterBom = roadmapContent.replace(/^\uFEFF/, '');
151
- if (contentAfterBom.startsWith('---')) {
152
- const closeMatch = contentAfterBom.slice(3).match(/\r?\n---\s*(\r?\n|$)/);
153
- if (!closeMatch) {
154
- warnings.push({ code: 'V003', message: 'ROADMAP.md frontmatter is malformed (unterminated --- fence)' });
155
- }
149
+ // Malformed frontmatter โ€” a `---` opener with no matching closer, as the one fence
150
+ // owner reads it (a leading BOM, #3057, is tolerated there).
151
+ const roadmapFence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(roadmapContent);
152
+ // The milestone-window read below sees the document without its leading BOM (#3057).
153
+ const contentAfterBom = roadmapContent.charCodeAt(0) === 0xFEFF ? roadmapContent.slice(1) : roadmapContent;
154
+ if (roadmapFence && !roadmapFence.closed) {
155
+ warnings.push({ code: 'V003', message: 'ROADMAP.md frontmatter is malformed (unterminated --- fence)' });
156
156
  }
157
157
  // #3641: resolve phase_id_convention ONCE, ahead of every consumer in
158
158
  // this validate pass โ€” V004's entry check and V005's scope classifier
@@ -169,13 +169,11 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
169
169
  convention = undefined;
170
170
  }
171
171
  if (convention === undefined || convention === null) {
172
- // Fallback: read from ROADMAP.md frontmatter. Bounded to match
173
- // cmdRoadmapMilestoneScope's copy exactly (#3641 review NEW-1: an
174
- // unbounded capture here read past 4KB frontmatters the probe's
175
- // bounded copy could not, diverging validate from the probe).
176
- const fmMatch = roadmapContent.match(/^---\r?\n([\s\S]{0,4000}?)\r?\n---/);
177
- if (fmMatch) {
178
- const kvMatch = fmMatch[1].match(/^phase_id_convention:\s*(.*)$/m);
172
+ // Fallback: read from ROADMAP.md frontmatter โ€” the block the one fence owner finds,
173
+ // exactly as cmdRoadmapMilestoneScope reads it (#3641 review NEW-1: two copies of
174
+ // this read disagreed on long frontmatter, diverging validate from the probe).
175
+ if (roadmapFence?.closed) {
176
+ const kvMatch = roadmapContent.slice(roadmapFence.openEnd, roadmapFence.bodyEnd).match(/^phase_id_convention:\s*(.*)$/m);
179
177
  if (kvMatch) {
180
178
  const val = kvMatch[1].trim();
181
179
  if (val !== 'null' && val !== '') {
@@ -926,6 +926,44 @@ function classifyMilestoneWindow(input) {
926
926
  SCOPE.COMPLETE // rows 6, 7
927
927
  );
928
928
  }
929
+ // #5007 (Phase 6 / ADR-4910 ยง8): the bulk phase-heading-block strip (heading
930
+ // line + body through to the next heading of any level) used by BOTH the
931
+ // <details>-fallback branch and preambleWithoutPhaseDetails inside
932
+ // extractCurrentMilestoneScoped below โ€” literally the same regex, previously
933
+ // duplicated verbatim at both call sites. Composed from
934
+ // phaseHeadingPrefixSrcFor (LABEL_ONLY, no convention argument โ€” reproduces
935
+ // the prior bare `Phase\s+` literal byte-for-byte, same non-widening
936
+ // discipline as the phase.cts counter sites) rather than routed through
937
+ // buildPhaseHeadingScanRegex, since this site needs the whole matched block
938
+ // (heading + body), not a phase-number/title capture. The one real behavior
939
+ // change here is the phase-number token itself: the prior literal admitted
940
+ // any `[\w][\w.-]*` (including non-numeric garbage); PHASE_NUMBER_TOKEN_SOURCE
941
+ // tightens it to the real phase-number grammar (digits, optional trailing
942
+ // letter, dotted subphases) โ€” verified against real phase-number fixtures
943
+ // (plain, decimal, and bracket-tag-bearing) in tests/roadmap-parser.test.cjs.
944
+ const PHASE_HEADING_BLOCK_STRIP_RE = new RegExp(
945
+ // #1729: `(?:\s*\([^)\n]{0,200}\))?` (OPTIONAL_PHASE_TAG_SOURCE) tolerates a pre-colon ( ) tag.
946
+ `^#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY)}${PHASE_NUMBER_TOKEN_SOURCE}${OPTIONAL_PHASE_TAG_SOURCE}\\s*:[^\\n]*(?:\\n(?!#{1,6}\\s)[^\\n]*)*\\n?`, 'gim');
947
+ /**
948
+ * #5038: single owner for classifying a `milestone:` scalar as STATE.md spells
949
+ * it. The frontmatter parser (FAILSAFE_SCHEMA) and the raw-regex readers both
950
+ * see YAML null as text, so every reader classifies it here. Recognizes the
951
+ * YAML 1.2 core-schema null spellings (`null`, `Null`, `NULL`, `~`) with an
952
+ * optional trailing ` # comment` and optional surrounding quotes (the
953
+ * frontmatter parser has already stripped them, so the raw readers must agree);
954
+ * a blank value is absent, not null.
955
+ */
956
+ function classifyMilestoneScalar(raw) {
957
+ if (typeof raw !== 'string')
958
+ return { explicitNull: false, version: null };
959
+ const trimmed = raw.trim();
960
+ if (trimmed === '')
961
+ return { explicitNull: false, version: null };
962
+ const bare = trimmed.replace(/\s+#.*$/, '').trim().replace(/^(['"])(.*)\1$/, '$2');
963
+ if (/^(?:null|Null|NULL|~)$/.test(bare))
964
+ return { explicitNull: true, version: null };
965
+ return { explicitNull: false, version: trimmed };
966
+ }
929
967
  /**
930
968
  * Extract the current milestone section from ROADMAP.md by positive lookup,
931
969
  * carrying a `scope` discriminator (ADR-3180 Decision 2) alongside the value.
@@ -960,18 +998,23 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
960
998
  return { value: stripShippedMilestones(content), scope: SCOPE.COMPLETE };
961
999
  }
962
1000
  let version = null;
1001
+ let stateAssertsNoMilestone = false;
963
1002
  try {
964
1003
  const statePath = node_path_1.default.join(planningDir(cwd, ws), 'STATE.md');
965
1004
  const stateRaw = (0, shell_command_projection_cjs_1.platformReadSync)(statePath);
966
1005
  if (stateRaw !== null) {
967
1006
  const milestoneMatch = stateRaw.match(/^milestone:\s*(.+)/m);
968
1007
  if (milestoneMatch) {
969
- version = milestoneMatch[1].trim();
1008
+ const classified = classifyMilestoneScalar(milestoneMatch[1]);
1009
+ version = classified.version;
1010
+ stateAssertsNoMilestone = classified.explicitNull;
970
1011
  }
971
1012
  }
972
1013
  }
973
1014
  catch { /* ignore */ }
974
- if (!version) {
1015
+ // #5038: an explicit `milestone: null` is a deliberate "no milestone" โ€” do
1016
+ // not let the in-progress bullet guess one (same stance as getMilestoneInfo).
1017
+ if (!version && !stateAssertsNoMilestone) {
975
1018
  const inProgressMatch = content.match(/(?:๐Ÿšง|๐Ÿ”„)\s*\*\*v(\d+\.\d+)\s/);
976
1019
  if (inProgressMatch) {
977
1020
  version = 'v' + inProgressMatch[1];
@@ -1050,9 +1093,9 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
1050
1093
  const firstMilestoneMatch = content.match(anyMilestoneOrDetails);
1051
1094
  const preambleCutoff = firstMilestoneMatch ? firstMilestoneMatch.index : detailsOpenIdx;
1052
1095
  const preamble = (0, markdown_sectionizer_cjs_1.stripTaggedBlocks)(content.slice(0, preambleCutoff), 'details')
1053
- // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
1054
- // phase-id-owner: pre-existing hand-rolled Phase-heading pattern โ€” grandfathered pending Phase 6 migration (ADR-4910 ยง8, epic #4906)
1055
- .replace(/^#{2,4}\s*Phase\s+[\w][\w.-]*(?:\s*\([^)\n]{0,200}\))?\s*:[^\n]*(?:\n(?!#{1,6}\s)[^\n]*)*\n?/gim, '')
1096
+ // #5007 (Phase 6 / ADR-4910 ยง8): shared owner, see
1097
+ // PHASE_HEADING_BLOCK_STRIP_RE above.
1098
+ .replace(PHASE_HEADING_BLOCK_STRIP_RE, '')
1056
1099
  .replace(/^#{1,4}\s*Phase Details\b[^\n]*\n?/gim, '');
1057
1100
  const value = preamble + content.slice(detailsOpenIdx, detailsEnd);
1058
1101
  return {
@@ -1287,17 +1330,23 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
1287
1330
  // every phase (phase_count: 0, exit 0). Only strip preamble phase details when
1288
1331
  // the selected milestone section actually contains its own โ€” otherwise the
1289
1332
  // preamble phases ARE this milestone's phases and must be preserved.
1290
- // phase-id-owner: pre-existing hand-rolled Phase-heading pattern โ€” grandfathered pending Phase 6 migration (ADR-4910 ยง8, epic #4906)
1291
- const currentSectionHasPhaseDetails = /^#{2,4}\s*Phase\s+\S/im.test(currentSection);
1333
+ // #5007 (Phase 6 / ADR-4910 ยง8): a pure "does this section have ANY phase
1334
+ // heading" boolean โ€” same shape as phase.cts:1691's anyHeadingPattern
1335
+ // (composed directly from phaseHeadingPrefixSrcFor, no shared owner exposes
1336
+ // a captureless existence check), but this site's terminal token is `\S`
1337
+ // (any non-whitespace), not phase.cts:1691's `\d` โ€” kept as `\S` to
1338
+ // reproduce this site's own prior behavior byte-for-byte rather than
1339
+ // forcing the two sites onto an identical terminal token that would be a
1340
+ // behavior change here.
1341
+ const currentSectionHasPhaseDetails = new RegExp(`^#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY)}\\S`, 'im').test(currentSection);
1292
1342
  const preambleBase = (0, markdown_sectionizer_cjs_1.stripTaggedBlocks)(beforeMilestones, 'details');
1293
1343
  // #3235: the conditional wraps the REPLACE, not the pattern. This used to select between the
1294
1344
  // strip regex and a `/$/` sentinel, which made the do-not-strip branch an identity replacement
1295
1345
  // (CodeQL js/identity-replacement, alert 53) -- correct, but it left both branches sharing one
1296
1346
  // replacement argument, so changing `''` would silently give the no-op branch a real effect.
1297
- // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
1347
+ // #5007 (Phase 6 / ADR-4910 ยง8): shared owner, see PHASE_HEADING_BLOCK_STRIP_RE above.
1298
1348
  const preambleWithoutPhaseDetails = currentSectionHasPhaseDetails
1299
- // phase-id-owner: pre-existing hand-rolled Phase-heading pattern โ€” grandfathered pending Phase 6 migration (ADR-4910 ยง8, epic #4906)
1300
- ? preambleBase.replace(/^#{2,4}\s*Phase\s+[\w][\w.-]*(?:\s*\([^)\n]{0,200}\))?\s*:[^\n]*(?:\n(?!#{1,6}\s)[^\n]*)*\n?/gim, '')
1349
+ ? preambleBase.replace(PHASE_HEADING_BLOCK_STRIP_RE, '')
1301
1350
  : preambleBase;
1302
1351
  // Unconditional in BOTH branches -- the #730 `Phase Details` heading strip is independent of
1303
1352
  // whether the selected milestone section carries phase details of its own.
@@ -1707,14 +1756,22 @@ function getMilestoneInfo(cwd) {
1707
1756
  if (roadmap === null)
1708
1757
  throw new Error('missing');
1709
1758
  let stateVersion = null;
1759
+ // #5038: true only when the `milestone:` key is PRESENT and classifies as
1760
+ // an explicit null (any spelling classifyMilestoneScalar accepts), as
1761
+ // opposed to ABSENT. Gates the ROADMAP auto-derivation fallback, which
1762
+ // only bootstraps a milestone for a project that never mentioned one.
1763
+ let explicitlyNoMilestone = false;
1710
1764
  if (cwd) {
1711
1765
  try {
1712
1766
  const statePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
1713
1767
  const stateRaw = (0, shell_command_projection_cjs_1.platformReadSync)(statePath);
1714
1768
  if (stateRaw !== null) {
1715
1769
  const m = stateRaw.match(/^milestone:\s*(.+)/m);
1716
- if (m)
1717
- stateVersion = m[1].trim();
1770
+ if (m) {
1771
+ const classified = classifyMilestoneScalar(m[1]);
1772
+ explicitlyNoMilestone = classified.explicitNull;
1773
+ stateVersion = classified.version;
1774
+ }
1718
1775
  }
1719
1776
  }
1720
1777
  catch {
@@ -1771,6 +1828,11 @@ function getMilestoneInfo(cwd) {
1771
1828
  // or heading-but-nameless). ยง7.2 rule 4 โ€” never fabricate a name.
1772
1829
  return scoped({ version: stateVersion, name: null }, SCOPE.TRUNCATED);
1773
1830
  }
1831
+ // #5038: an explicit null is a deliberate "no milestone"; do not let the
1832
+ // auto-derivation fallback below replace it with a guessed version.
1833
+ if (explicitlyNoMilestone) {
1834
+ return scoped(null, SCOPE.UNSCOPED);
1835
+ }
1774
1836
  // No STATE.md version. The ๐Ÿšง in-progress bullet is still consulted first
1775
1837
  // (unchanged from the pre-#3216 fallback).
1776
1838
  const inProgressMatch = roadmap.match(/๐Ÿšง\s*\*\*v(\d+(?:\.\d+)+)\s+([^*]+)\*\*/);
@@ -2104,17 +2166,21 @@ function currentMilestoneRawRanges(content, cwd) {
2104
2166
  if (!cwd)
2105
2167
  return null;
2106
2168
  let version = null;
2169
+ let stateAssertsNoMilestone = false;
2107
2170
  try {
2108
2171
  const statePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
2109
2172
  const stateRaw = (0, shell_command_projection_cjs_1.platformReadSync)(statePath);
2110
2173
  if (stateRaw !== null) {
2111
2174
  const milestoneMatch = stateRaw.match(/^milestone:\s*(.+)/m);
2112
- if (milestoneMatch)
2113
- version = milestoneMatch[1].trim();
2175
+ if (milestoneMatch) {
2176
+ const classified = classifyMilestoneScalar(milestoneMatch[1]);
2177
+ version = classified.version;
2178
+ stateAssertsNoMilestone = classified.explicitNull;
2179
+ }
2114
2180
  }
2115
2181
  }
2116
2182
  catch { /* ignore */ }
2117
- if (!version) {
2183
+ if (!version && !stateAssertsNoMilestone) {
2118
2184
  const inProgressMatch = content.match(/(?:๐Ÿšง|๐Ÿ”„)\s*\*\*v(\d+\.\d+)\s/);
2119
2185
  if (inProgressMatch)
2120
2186
  version = 'v' + inProgressMatch[1];
@@ -2217,6 +2283,7 @@ module.exports = {
2217
2283
  replaceInCurrentMilestone,
2218
2284
  getRoadmapPhaseInternal,
2219
2285
  getMilestoneInfo,
2286
+ classifyMilestoneScalar,
2220
2287
  getMilestonePhaseFilter,
2221
2288
  currentMilestoneRawRanges,
2222
2289
  withPhaseSection,