@opengsd/gsd-core 1.13.0 → 1.15.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 (441) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ja-JP.md +3 -3
  4. package/README.ko-KR.md +3 -3
  5. package/README.pt-BR.md +3 -3
  6. package/README.zh-CN.md +3 -3
  7. package/agents/gsd-advisor-researcher.compact.md +85 -0
  8. package/agents/gsd-ai-researcher.compact.md +96 -0
  9. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  10. package/agents/gsd-code-fixer.compact.md +459 -0
  11. package/agents/gsd-code-fixer.md +9 -8
  12. package/agents/gsd-code-reviewer.compact.md +269 -0
  13. package/agents/gsd-code-reviewer.md +15 -3
  14. package/agents/gsd-codebase-mapper.compact.md +760 -0
  15. package/agents/gsd-debug-session-manager.compact.md +360 -0
  16. package/agents/gsd-debug-session-manager.md +17 -2
  17. package/agents/gsd-debugger.md +2 -2
  18. package/agents/gsd-doc-classifier.compact.md +192 -0
  19. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  20. package/agents/gsd-doc-verifier.compact.md +143 -0
  21. package/agents/gsd-doc-writer.compact.md +440 -0
  22. package/agents/gsd-dom-verifier.compact.md +138 -0
  23. package/agents/gsd-domain-researcher.compact.md +141 -0
  24. package/agents/gsd-eval-auditor.compact.md +160 -0
  25. package/agents/gsd-eval-auditor.md +1 -1
  26. package/agents/gsd-eval-planner.compact.md +137 -0
  27. package/agents/gsd-executor.md +13 -8
  28. package/agents/gsd-framework-selector.compact.md +82 -0
  29. package/agents/gsd-integration-checker.compact.md +245 -0
  30. package/agents/gsd-intel-updater.compact.md +226 -0
  31. package/agents/gsd-intel-updater.md +1 -1
  32. package/agents/gsd-mempalace-curator.compact.md +45 -0
  33. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  34. package/agents/gsd-pattern-mapper.compact.md +275 -0
  35. package/agents/gsd-phase-researcher.md +19 -11
  36. package/agents/gsd-plan-checker.md +8 -7
  37. package/agents/gsd-planner.md +12 -8
  38. package/agents/gsd-project-researcher.compact.md +587 -0
  39. package/agents/gsd-project-researcher.md +1 -1
  40. package/agents/gsd-research-synthesizer.compact.md +212 -0
  41. package/agents/gsd-research-synthesizer.md +1 -1
  42. package/agents/gsd-roadmapper.compact.md +454 -0
  43. package/agents/gsd-roadmapper.md +13 -0
  44. package/agents/gsd-security-auditor.compact.md +162 -0
  45. package/agents/gsd-ui-auditor.compact.md +404 -0
  46. package/agents/gsd-ui-auditor.md +155 -17
  47. package/agents/gsd-ui-checker.compact.md +277 -0
  48. package/agents/gsd-ui-researcher.compact.md +282 -0
  49. package/agents/gsd-ui-researcher.md +1 -1
  50. package/agents/gsd-user-profiler.compact.md +108 -0
  51. package/agents/gsd-verifier.md +10 -9
  52. package/bin/install.js +848 -163
  53. package/commands/gsd/autonomous.md +2 -2
  54. package/commands/gsd/capture.md +1 -1
  55. package/commands/gsd/cleanup.md +1 -0
  56. package/commands/gsd/code-review.md +2 -1
  57. package/commands/gsd/complete-milestone.md +1 -0
  58. package/commands/gsd/config.md +1 -0
  59. package/commands/gsd/debug.md +1 -0
  60. package/commands/gsd/graphify.md +1 -0
  61. package/commands/gsd/health.md +1 -0
  62. package/commands/gsd/mempalace-capture.md +8 -3
  63. package/commands/gsd/mempalace-recall.md +1 -0
  64. package/commands/gsd/new-milestone.md +1 -0
  65. package/commands/gsd/new-project.md +1 -0
  66. package/commands/gsd/next.md +1 -0
  67. package/commands/gsd/pause-work.md +1 -0
  68. package/commands/gsd/phase.md +1 -0
  69. package/commands/gsd/plan-review-convergence.md +6 -6
  70. package/commands/gsd/pr-branch.md +1 -0
  71. package/commands/gsd/progress.md +1 -1
  72. package/commands/gsd/quick-batch.md +1 -1
  73. package/commands/gsd/resume-work.md +1 -0
  74. package/commands/gsd/review-backlog.md +1 -0
  75. package/commands/gsd/review.md +2 -3
  76. package/commands/gsd/settings.md +2 -1
  77. package/commands/gsd/stats.md +1 -0
  78. package/commands/gsd/thread.md +1 -0
  79. package/commands/gsd/workspace.md +1 -0
  80. package/commands/gsd/workstreams.md +1 -0
  81. package/gsd-core/bin/check-latest-version.cjs +8 -3
  82. package/gsd-core/bin/gsd-tools.cjs +672 -146
  83. package/gsd-core/bin/lib/adr-parser.cjs +4 -2
  84. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  85. package/gsd-core/bin/lib/audit.cjs +119 -34
  86. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  87. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  88. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  89. package/gsd-core/bin/lib/capability-registry.cjs +96 -189
  90. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  91. package/gsd-core/bin/lib/capability-validator.cjs +14 -2
  92. package/gsd-core/bin/lib/check-command-router.cjs +213 -49
  93. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  94. package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
  95. package/gsd-core/bin/lib/commands.cjs +823 -112
  96. package/gsd-core/bin/lib/config-loader.cjs +66 -4
  97. package/gsd-core/bin/lib/config.cjs +186 -45
  98. package/gsd-core/bin/lib/coverage.cjs +1 -1
  99. package/gsd-core/bin/lib/decisions.cjs +164 -45
  100. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  101. package/gsd-core/bin/lib/frontmatter.cjs +13 -0
  102. package/gsd-core/bin/lib/graphify.cjs +10 -2
  103. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  104. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
  105. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  106. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  107. package/gsd-core/bin/lib/init.cjs +614 -86
  108. package/gsd-core/bin/lib/install-engine.cjs +29 -3
  109. package/gsd-core/bin/lib/install-profiles.cjs +14 -0
  110. package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
  111. package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
  112. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  113. package/gsd-core/bin/lib/milestone.cjs +37 -13
  114. package/gsd-core/bin/lib/model-resolver.cjs +253 -53
  115. package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
  116. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  117. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  118. package/gsd-core/bin/lib/phase-id.cjs +268 -27
  119. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  120. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  121. package/gsd-core/bin/lib/phase.cjs +393 -88
  122. package/gsd-core/bin/lib/plan-document.cjs +49 -1
  123. package/gsd-core/bin/lib/planning-document.cjs +459 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
  126. package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
  127. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  128. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  129. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  130. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  131. package/gsd-core/bin/lib/project-root.cjs +41 -2
  132. package/gsd-core/bin/lib/quick-batch.cjs +1 -1
  133. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  134. package/gsd-core/bin/lib/research-store.cjs +11 -12
  135. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  136. package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
  137. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  138. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  139. package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
  140. package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
  141. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
  142. package/gsd-core/bin/lib/roadmap.cjs +356 -42
  143. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
  144. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
  145. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
  146. package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
  147. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
  148. package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
  149. package/gsd-core/bin/lib/security.cjs +126 -7
  150. package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
  151. package/gsd-core/bin/lib/state-document.cjs +130 -28
  152. package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
  153. package/gsd-core/bin/lib/state-transition.cjs +181 -30
  154. package/gsd-core/bin/lib/state.cjs +265 -27
  155. package/gsd-core/bin/lib/surface.cjs +77 -3
  156. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  157. package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
  158. package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
  159. package/gsd-core/bin/lib/uat.cjs +9 -1
  160. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  161. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
  162. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  163. package/gsd-core/bin/lib/update-context.cjs +30 -24
  164. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  165. package/gsd-core/bin/lib/verification.cjs +315 -30
  166. package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
  167. package/gsd-core/bin/lib/verify.cjs +320 -48
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
  169. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  170. package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
  171. package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
  172. package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
  173. package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
  174. package/gsd-core/references/checkpoints.md +5 -3
  175. package/gsd-core/references/compact-content-gate.md +66 -0
  176. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  177. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  178. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  179. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  180. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  181. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  182. package/gsd-core/references/edge-probe.md +195 -21
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
  184. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  185. package/gsd-core/references/gsd-run-resolver.md +1 -1
  186. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  187. package/gsd-core/references/model-profiles.md +13 -4
  188. package/gsd-core/references/phase-argument-parsing.md +9 -7
  189. package/gsd-core/references/phase-id-convention.md +28 -0
  190. package/gsd-core/references/planner-gap-closure.md +2 -0
  191. package/gsd-core/references/planner-load-graph-context.md +24 -13
  192. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  193. package/gsd-core/references/planning-config.md +14 -2
  194. package/gsd-core/references/tdd.md +30 -4
  195. package/gsd-core/references/thinking-models-planning.md +18 -2
  196. package/gsd-core/references/ui-consideration-probe.md +10 -5
  197. package/gsd-core/references/verification-patterns.md +17 -4
  198. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  199. package/gsd-core/references/worktree-path-safety.md +433 -2
  200. package/gsd-core/templates/README.md +7 -1
  201. package/gsd-core/templates/state.md +6 -3
  202. package/gsd-core/templates/summary.compact.md +212 -0
  203. package/gsd-core/templates/user-setup.compact.md +199 -0
  204. package/gsd-core/templates/user-setup.md +0 -9
  205. package/gsd-core/templates/verification-report.md +1 -1
  206. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  207. package/gsd-core/workflows/add-backlog.md +1 -1
  208. package/gsd-core/workflows/add-phase.md +1 -1
  209. package/gsd-core/workflows/add-tests.md +2 -2
  210. package/gsd-core/workflows/add-todo.md +6 -5
  211. package/gsd-core/workflows/ai-integration-phase.md +11 -3
  212. package/gsd-core/workflows/audit-fix.md +1 -1
  213. package/gsd-core/workflows/audit-milestone.md +1 -1
  214. package/gsd-core/workflows/audit-uat.md +1 -1
  215. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  216. package/gsd-core/workflows/autonomous.md +29 -16
  217. package/gsd-core/workflows/check-todos.md +6 -4
  218. package/gsd-core/workflows/cleanup.md +5 -3
  219. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  220. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
  221. package/gsd-core/workflows/code-review-fix.md +108 -22
  222. package/gsd-core/workflows/code-review.md +216 -73
  223. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  224. package/gsd-core/workflows/complete-milestone.md +41 -264
  225. package/gsd-core/workflows/debug.md +3 -3
  226. package/gsd-core/workflows/diagnose-issues.md +1 -1
  227. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  228. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  229. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  230. package/gsd-core/workflows/discuss-phase.md +1 -1
  231. package/gsd-core/workflows/do.md +2 -2
  232. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  233. package/gsd-core/workflows/docs-update.md +17 -158
  234. package/gsd-core/workflows/edit-phase.md +1 -1
  235. package/gsd-core/workflows/eval-review.md +10 -3
  236. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  237. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
  238. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
  239. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  240. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
  241. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  242. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  243. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  244. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  245. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  246. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  247. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  248. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  249. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  250. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  251. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
  252. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  253. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  254. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
  255. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  256. package/gsd-core/workflows/execute-phase.md +83 -172
  257. package/gsd-core/workflows/execute-plan.md +24 -10
  258. package/gsd-core/workflows/explore.md +3 -3
  259. package/gsd-core/workflows/extract-learnings.md +2 -1
  260. package/gsd-core/workflows/fast.md +1 -1
  261. package/gsd-core/workflows/forensics.md +1 -1
  262. package/gsd-core/workflows/graduation.md +1 -1
  263. package/gsd-core/workflows/health.md +2 -2
  264. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  265. package/gsd-core/workflows/help/modes/full.md +5 -5
  266. package/gsd-core/workflows/help/modes/topic.md +15 -5
  267. package/gsd-core/workflows/help.md +1 -1
  268. package/gsd-core/workflows/import.md +2 -2
  269. package/gsd-core/workflows/inbox.md +2 -2
  270. package/gsd-core/workflows/ingest-docs.md +3 -3
  271. package/gsd-core/workflows/insert-phase.md +1 -1
  272. package/gsd-core/workflows/list-seeds.md +1 -1
  273. package/gsd-core/workflows/list-workspaces.md +1 -1
  274. package/gsd-core/workflows/manager.md +2 -2
  275. package/gsd-core/workflows/map-codebase.md +52 -5
  276. package/gsd-core/workflows/milestone-summary.md +1 -1
  277. package/gsd-core/workflows/mvp-phase.md +1 -1
  278. package/gsd-core/workflows/new-milestone.md +56 -14
  279. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  280. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  281. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  282. package/gsd-core/workflows/new-project.md +39 -209
  283. package/gsd-core/workflows/new-workspace.md +2 -2
  284. package/gsd-core/workflows/next.md +1 -1
  285. package/gsd-core/workflows/note.md +1 -1
  286. package/gsd-core/workflows/onboard.md +1 -1
  287. package/gsd-core/workflows/pause-work.md +1 -1
  288. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  289. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  290. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  291. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  292. package/gsd-core/workflows/plan-phase.md +45 -187
  293. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  294. package/gsd-core/workflows/plant-seed.md +62 -20
  295. package/gsd-core/workflows/pr-branch.md +132 -20
  296. package/gsd-core/workflows/profile-user.md +2 -2
  297. package/gsd-core/workflows/progress.md +1 -1
  298. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
  299. package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
  300. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
  301. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  302. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  303. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  304. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  305. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  306. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  307. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
  308. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  309. package/gsd-core/workflows/quick-batch.md +1 -1
  310. package/gsd-core/workflows/quick.md +29 -10
  311. package/gsd-core/workflows/reapply-patches.md +86 -6
  312. package/gsd-core/workflows/remove-phase.md +1 -1
  313. package/gsd-core/workflows/remove-workspace.md +2 -2
  314. package/gsd-core/workflows/resume-project.md +1 -1
  315. package/gsd-core/workflows/review.md +31 -16
  316. package/gsd-core/workflows/scan.md +1 -1
  317. package/gsd-core/workflows/secure-phase.md +3 -2
  318. package/gsd-core/workflows/settings-advanced.md +30 -10
  319. package/gsd-core/workflows/settings-integrations.md +2 -3
  320. package/gsd-core/workflows/settings.md +22 -9
  321. package/gsd-core/workflows/ship.md +3 -2
  322. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  323. package/gsd-core/workflows/sketch.md +1 -1
  324. package/gsd-core/workflows/smart-entry.md +2 -2
  325. package/gsd-core/workflows/spec-phase.md +15 -5
  326. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  327. package/gsd-core/workflows/spike.md +1 -1
  328. package/gsd-core/workflows/stats.md +1 -1
  329. package/gsd-core/workflows/sync-skills.md +5 -5
  330. package/gsd-core/workflows/thread.md +1 -1
  331. package/gsd-core/workflows/transition.md +1 -1
  332. package/gsd-core/workflows/ui-phase.md +44 -8
  333. package/gsd-core/workflows/ui-review.md +18 -4
  334. package/gsd-core/workflows/ultraplan-phase.md +1 -1
  335. package/gsd-core/workflows/undo.md +339 -20
  336. package/gsd-core/workflows/update.md +14 -12
  337. package/gsd-core/workflows/validate-phase.md +3 -2
  338. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  339. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  340. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  341. package/gsd-core/workflows/verify-work.md +101 -196
  342. package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
  343. package/hooks/dist/gsd-context-monitor.js +88 -15
  344. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  345. package/hooks/dist/gsd-secret-read-guard.js +71 -19
  346. package/hooks/dist/gsd-statusline.js +81 -20
  347. package/hooks/dist/gsd-validate-commit.sh +97 -8
  348. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  349. package/hooks/dist/gsd-write-guard.js +46 -1
  350. package/hooks/dist/lib/dispatch-identity.js +187 -0
  351. package/hooks/dist/lib/filename-classification.js +64 -0
  352. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  353. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  354. package/hooks/gsd-agent-isolation-guard.js +66 -16
  355. package/hooks/gsd-context-monitor.js +88 -15
  356. package/hooks/gsd-cursor-subagent-start.js +34 -14
  357. package/hooks/gsd-secret-read-guard.js +71 -19
  358. package/hooks/gsd-statusline.js +81 -20
  359. package/hooks/gsd-validate-commit.sh +97 -8
  360. package/hooks/gsd-worktree-path-guard.js +25 -14
  361. package/hooks/gsd-write-guard.js +46 -1
  362. package/hooks/lib/dispatch-identity.js +187 -0
  363. package/hooks/lib/filename-classification.js +64 -0
  364. package/hooks/lib/isolation-deny-reason.js +53 -1
  365. package/hooks/lib/isolation-sentinel.js +58 -19
  366. package/package.json +11 -6
  367. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  368. package/scripts/benchmark-compact-content.cjs +368 -0
  369. package/scripts/build-hooks.js +15 -6
  370. package/scripts/check-contract-drift.cjs +131 -12
  371. package/scripts/check-env.cjs +36 -8
  372. package/scripts/check-glossary-refs.cjs +25 -21
  373. package/scripts/ci-next-health.cjs +271 -0
  374. package/scripts/ci-prepare-test-scope.cjs +7 -7
  375. package/scripts/ci-test-scope.cjs +126 -20
  376. package/scripts/ci-timeout-report.cjs +1 -1
  377. package/scripts/command-contract-helpers.cjs +3 -0
  378. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  379. package/scripts/docs-guard-registry.cjs +35 -2
  380. package/scripts/gen-adr-index.cjs +8 -2
  381. package/scripts/gen-inventory-manifest.cjs +12 -0
  382. package/scripts/gen-loop-host-contract.cjs +69 -0
  383. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  384. package/scripts/lib/drift-scan.cjs +1 -1
  385. package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
  386. package/scripts/lib/ndjson-reporter.cjs +3 -2
  387. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  388. package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
  389. package/scripts/lib/suite-detection.cjs +32 -0
  390. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  391. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
  392. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  393. package/scripts/lint-phase-id-drift.cjs +623 -13
  394. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  395. package/scripts/lint-response-language-coverage.cjs +9 -3
  396. package/scripts/lint-retired-runtime-name.cjs +619 -0
  397. package/scripts/lint-source-test-name-collision.cjs +1 -1
  398. package/scripts/lint-state-write-path-drift.cjs +93 -0
  399. package/scripts/lint-test-file-count.allowlist.json +29 -9
  400. package/scripts/lint-vendored-deps.cjs +128 -17
  401. package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
  402. package/scripts/prompt-injection-scan.sh +18 -0
  403. package/scripts/release-tarball-smoke.cjs +194 -1
  404. package/scripts/workflow-size.cjs +139 -0
  405. package/skills/gsd-autonomous/SKILL.md +2 -2
  406. package/skills/gsd-capture/SKILL.md +1 -1
  407. package/skills/gsd-cleanup/SKILL.md +1 -0
  408. package/skills/gsd-code-review/SKILL.md +2 -1
  409. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  410. package/skills/gsd-config/SKILL.md +1 -0
  411. package/skills/gsd-debug/SKILL.md +1 -0
  412. package/skills/gsd-graphify/SKILL.md +1 -0
  413. package/skills/gsd-health/SKILL.md +1 -0
  414. package/skills/gsd-mempalace-capture/SKILL.md +8 -3
  415. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  416. package/skills/gsd-new-milestone/SKILL.md +1 -0
  417. package/skills/gsd-new-project/SKILL.md +1 -0
  418. package/skills/gsd-next/SKILL.md +1 -0
  419. package/skills/gsd-pause-work/SKILL.md +1 -0
  420. package/skills/gsd-phase/SKILL.md +1 -0
  421. package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
  422. package/skills/gsd-pr-branch/SKILL.md +1 -0
  423. package/skills/gsd-progress/SKILL.md +1 -1
  424. package/skills/gsd-quick-batch/SKILL.md +1 -1
  425. package/skills/gsd-resume-work/SKILL.md +1 -0
  426. package/skills/gsd-review/SKILL.md +2 -3
  427. package/skills/gsd-review-backlog/SKILL.md +1 -0
  428. package/skills/gsd-settings/SKILL.md +2 -1
  429. package/skills/gsd-stats/SKILL.md +1 -0
  430. package/skills/gsd-thread/SKILL.md +1 -0
  431. package/skills/gsd-workspace/SKILL.md +1 -0
  432. package/skills/gsd-workstreams/SKILL.md +1 -0
  433. package/vscode/package.json +1 -1
  434. package/gsd-core/templates/claude-md.md +0 -145
  435. package/gsd-core/templates/codebase/concerns.md +0 -310
  436. package/gsd-core/templates/codebase/conventions.md +0 -307
  437. package/gsd-core/templates/codebase/integrations.md +0 -280
  438. package/gsd-core/templates/codebase/structure.md +0 -285
  439. package/gsd-core/templates/codebase/testing.md +0 -480
  440. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  441. package/gsd-core/templates/discovery.md +0 -146
@@ -571,6 +571,55 @@ function sliceMilestoneWindow(content, version) {
571
571
  return null;
572
572
  return content.slice(selected.index, computeMilestoneSectionEnd(content, selected[0], selected.index));
573
573
  }
574
+ /**
575
+ * Enumerate the milestone section ranges recognized by the ROADMAP readers.
576
+ * Markdown boundaries reuse the reader's heading tokenizer, milestone-signal
577
+ * vocabulary, name/version extractor, and section-end owner. Collapsed
578
+ * archives reuse the same `<details><summary>` shape handled by
579
+ * `extractCurrentMilestoneScoped`.
580
+ */
581
+ function milestoneSections(content) {
582
+ const sections = [];
583
+ const headings = (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(content);
584
+ for (const heading of headings) {
585
+ if (heading.level < 1 || heading.level > 3)
586
+ continue;
587
+ if (/^Phase\s+\S/i.test(heading.text))
588
+ continue;
589
+ if (!MILESTONE_HEADING_SIGNAL_PATTERN.test(heading.text))
590
+ continue;
591
+ const extracted = extractMilestoneHeadingName(heading.text);
592
+ const version = extracted?.version ?? null;
593
+ const versionMajor = version?.match(/^v(\d+)/i);
594
+ const milestoneInt = versionMajor ? parseInt(versionMajor[1], 10) : null;
595
+ const lineEnd = content.indexOf('\n', heading.offset);
596
+ const fullLine = content.slice(heading.offset, lineEnd === -1 ? content.length : lineEnd);
597
+ sections.push({
598
+ start: heading.offset,
599
+ end: computeMilestoneSectionEnd(content, fullLine, heading.offset, undefined, headings),
600
+ version,
601
+ milestoneInt: milestoneInt !== null && Number.isSafeInteger(milestoneInt) ? milestoneInt : null,
602
+ });
603
+ }
604
+ const detailsPattern = /<details\b[^>]*>[\s\S]*?<\/details>/gi;
605
+ let detailsMatch;
606
+ while ((detailsMatch = detailsPattern.exec(content)) !== null) {
607
+ const summary = detailsMatch[0].match(/<summary[^>]*>([^<]*)<\/summary>/i)?.[1]?.trim() ?? '';
608
+ if (!MILESTONE_HEADING_SIGNAL_PATTERN.test(summary))
609
+ continue;
610
+ const extracted = extractMilestoneHeadingName(summary);
611
+ const version = extracted?.version ?? null;
612
+ const versionMajor = version?.match(/^v(\d+)/i);
613
+ const milestoneInt = versionMajor ? parseInt(versionMajor[1], 10) : null;
614
+ sections.push({
615
+ start: detailsMatch.index,
616
+ end: detailsMatch.index + detailsMatch[0].length,
617
+ version,
618
+ milestoneInt: milestoneInt !== null && Number.isSafeInteger(milestoneInt) ? milestoneInt : null,
619
+ });
620
+ }
621
+ return sections.sort((a, b) => a.start - b.start || a.end - b.end);
622
+ }
574
623
  /**
575
624
  * #3184: counts RAW phase references — a `#{2,4} Phase <id>:` heading
576
625
  * (fence-aware via `tokenizeHeadings`) or a `#2199` bullet entry — BEFORE any
@@ -600,17 +649,37 @@ function sliceMilestoneWindow(content, version) {
600
649
  // decoy `### [GSD.04] Notes:` outside the window manufactured a false V005
601
650
  // while suppressing the correct V004. The digit anchor forecloses both.
602
651
  const BRACKET_PHASE_ENTRY_HEADING_RE = new RegExp(`^${PHASE_HEADING_PREFIX_SRC}${PHASE_NUMBER_TOKEN_SOURCE}${OPTIONAL_PHASE_TAG_SOURCE}\\s*:`, 'i');
652
+ // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal
653
+ // mirror of OPTIONAL_PHASE_TAG_SOURCE). Hoisted out of hasPhaseEntries (#4144
654
+ // round 5 Blocker 1) so a consumer OUTSIDE this file's own phase-entry count —
655
+ // the bracket roadmap migrator's "phase-like but unparsed" refusal
656
+ // (src/roadmap-upgrade.cts) — can ask "is this heading text a phase heading
657
+ // by the readers' own rules" without re-deriving the pattern. A heading with
658
+ // no phase-number token and no trailing colon (`## Phase Details`, `##
659
+ // Phase Lifecycle`, `### Phase Notes`) is NOT a phase heading by this
660
+ // grammar — it is an ordinary section heading that happens to start with the
661
+ // word "Phase".
662
+ const PHASE_HEADING_TEXT_RE = /^(?:\[[^\]]{1,200}\]\s*)?Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:/i;
663
+ /**
664
+ * #4144 round 5 Blocker 1: whether `headingText` — a heading's text with its
665
+ * leading `#{1,6}` markers and surrounding whitespace already stripped
666
+ * (`tokenizeHeadings`'s own `h.text`, or the equivalent for a line a caller
667
+ * already knows is a `#{2,4}` heading) — is a phase heading by the readers'
668
+ * own grammar. The single owner of this test; `hasPhaseEntries` below is
669
+ * itself just the first caller.
670
+ */
671
+ function isPhaseHeadingText(headingText) {
672
+ return PHASE_HEADING_TEXT_RE.test(headingText);
673
+ }
603
674
  function hasPhaseEntries(markdown, phaseIdConvention) {
604
- // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
605
675
  // #3641: the widened grammar engages ONLY when the resolved convention is
606
676
  // 'bracket' — a project that has not opted in runs the legacy pattern
607
677
  // alone, byte-identically.
608
- const phaseHeadingPattern = /^(?:\[[^\]]{1,200}\]\s*)?Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:/i;
609
678
  const bracketMode = phaseIdConvention === 'bracket';
610
679
  for (const h of (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(markdown)) {
611
680
  if (h.level < 2 || h.level > 4)
612
681
  continue;
613
- if (phaseHeadingPattern.test(h.text))
682
+ if (isPhaseHeadingText(h.text))
614
683
  return true;
615
684
  if (bracketMode && BRACKET_PHASE_ENTRY_HEADING_RE.test(h.text))
616
685
  return true;
@@ -626,16 +695,17 @@ function hasPhaseEntries(markdown, phaseIdConvention) {
626
695
  return collectTablePhaseRows(markdown).length > 0;
627
696
  }
628
697
  // ─── #3577: markdown-table phase listings ─────────────────────────────────────
629
- // #3577: a GFM table declares phases when its header's FIRST cell is the literal
630
- // `Phase` (optionally `Phase #` / `Phase No.` / `Phase number`) and the header does
631
- // NOT match a known non-listing schema — the canonical RoadmapProgress table
632
- // (`| Phase | Plans Complete | Status | Completed |`) leads with `Phase` too, and
633
- // its rows are progress markers, not declarations. Data rows carry the phase id in
634
- // their first cell (digit-bearing canonical shape — `Phase`-word header cells and
635
- // `---` delimiter rows are digit-free and excluded by construction). Fence-aware
636
- // via stripFencedCode, matching the #3184 lesson: a fenced EXAMPLE of the table
637
- // form is not a declared phase.
698
+ // #3577/#4480: a GFM table declares phases only when its header's FIRST cell is
699
+ // the literal `Phase` (optionally `Phase #` / `Phase No.` / `Phase number`) AND
700
+ // it positively identifies a `Name` or `Phase Name` column. This fails closed:
701
+ // ordinary progress/summary tables such as `| Phase | Status |` cannot mint a
702
+ // phase whose name is whichever value happens to occupy column two. Data rows
703
+ // carry the phase id in their first cell (digit-bearing canonical shape —
704
+ // `Phase`-word header cells and `---` delimiter rows are digit-free and excluded
705
+ // by construction). Fence-aware via stripFencedCode, matching the #3184 lesson:
706
+ // a fenced EXAMPLE of the table form is not a declared phase.
638
707
  const PHASE_LISTING_HEADER_RE = /^\|?\s*phase(?:\s*(?:#|no\.?|number))?\s*\|/i;
708
+ const PHASE_NAME_HEADER_RE = /^(?:phase\s+)?name$/i;
639
709
  const TABLE_PHASE_ID_RE = /^[A-Za-z]?\d[\w.-]*$/;
640
710
  function collectTablePhaseRows(window) {
641
711
  const unfenced = (0, markdown_sectionizer_cjs_1.stripFencedCode)(window).text;
@@ -645,8 +715,9 @@ function collectTablePhaseRows(window) {
645
715
  if (!PHASE_LISTING_HEADER_RE.test(lines[i]))
646
716
  continue;
647
717
  const headerCells = (0, markdown_table_cjs_1.splitTableRow)(lines[i]);
648
- if ((0, markdown_table_cjs_1.matchTableSchema)(headerCells) !== null)
649
- continue; // canonical non-listing schema
718
+ const nameColumn = headerCells.findIndex((cell) => PHASE_NAME_HEADER_RE.test(cell));
719
+ if (nameColumn === -1)
720
+ continue;
650
721
  if (!(0, markdown_table_cjs_1.isDelimiterRow)((0, markdown_table_cjs_1.splitTableRow)(lines[i + 1])))
651
722
  continue;
652
723
  for (let j = i + 2; j < lines.length; j++) {
@@ -662,12 +733,30 @@ function collectTablePhaseRows(window) {
662
733
  if (!TABLE_PHASE_ID_RE.test(first))
663
734
  continue;
664
735
  if (!/^999\b/.test(first)) {
665
- rows.push({ id: first, name: cells[1] && cells[1] !== '' ? cells[1] : null, row: lines[j] });
736
+ const name = cells[nameColumn];
737
+ rows.push({ id: first, name: name && name !== '' ? name : null, row: lines[j] });
666
738
  }
667
739
  }
668
740
  }
669
741
  return rows;
670
742
  }
743
+ /**
744
+ * True when `content` contains a GFM table whose header row is phase-listing
745
+ * SHAPED (`| Phase | ... |`), regardless of whether it also declares a `Name`
746
+ * column. `collectTablePhaseRows` correctly refuses to mint a phase from a
747
+ * `| Phase | Status |`-shaped table (no name column, so no declaration) —
748
+ * but that table's mere presence still signals this roadmap has moved to
749
+ * table-based tracking, not checklist-based tracking. Callers that decide
750
+ * whether to treat bare checklist entries as phase declarations (#4899's
751
+ * roadmap.cts synthesis fallback) must see this signal even when
752
+ * `collectTablePhaseRows` itself returns nothing for the same content —
753
+ * otherwise a thin, name-less Progress table cannot suppress a checklist
754
+ * synthesis it was never meant to coexist with.
755
+ */
756
+ function hasPhaseListingTableHeader(content) {
757
+ const unfenced = (0, markdown_sectionizer_cjs_1.stripFencedCode)(content).text;
758
+ return unfenced.split(/\r?\n/).some((line) => PHASE_LISTING_HEADER_RE.test(line));
759
+ }
671
760
  /**
672
761
  * #3262: the sole owner of "which phase ids does THIS milestone window
673
762
  * declare". Extracted verbatim from `getMilestonePhaseFilter`'s former inline
@@ -962,6 +1051,7 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
962
1051
  const preambleCutoff = firstMilestoneMatch ? firstMilestoneMatch.index : detailsOpenIdx;
963
1052
  const preamble = (0, markdown_sectionizer_cjs_1.stripTaggedBlocks)(content.slice(0, preambleCutoff), 'details')
964
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)
965
1055
  .replace(/^#{2,4}\s*Phase\s+[\w][\w.-]*(?:\s*\([^)\n]{0,200}\))?\s*:[^\n]*(?:\n(?!#{1,6}\s)[^\n]*)*\n?/gim, '')
966
1056
  .replace(/^#{1,4}\s*Phase Details\b[^\n]*\n?/gim, '');
967
1057
  const value = preamble + content.slice(detailsOpenIdx, detailsEnd);
@@ -1197,6 +1287,7 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
1197
1287
  // every phase (phase_count: 0, exit 0). Only strip preamble phase details when
1198
1288
  // the selected milestone section actually contains its own — otherwise the
1199
1289
  // 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)
1200
1291
  const currentSectionHasPhaseDetails = /^#{2,4}\s*Phase\s+\S/im.test(currentSection);
1201
1292
  const preambleBase = (0, markdown_sectionizer_cjs_1.stripTaggedBlocks)(beforeMilestones, 'details');
1202
1293
  // #3235: the conditional wraps the REPLACE, not the pattern. This used to select between the
@@ -1205,6 +1296,7 @@ function extractCurrentMilestoneScoped(content, cwd, ws, phaseIdConvention) {
1205
1296
  // replacement argument, so changing `''` would silently give the no-op branch a real effect.
1206
1297
  // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
1207
1298
  const preambleWithoutPhaseDetails = currentSectionHasPhaseDetails
1299
+ // phase-id-owner: pre-existing hand-rolled Phase-heading pattern — grandfathered pending Phase 6 migration (ADR-4910 §8, epic #4906)
1208
1300
  ? preambleBase.replace(/^#{2,4}\s*Phase\s+[\w][\w.-]*(?:\s*\([^)\n]{0,200}\))?\s*:[^\n]*(?:\n(?!#{1,6}\s)[^\n]*)*\n?/gim, '')
1209
1301
  : preambleBase;
1210
1302
  // Unconditional in BOTH branches -- the #730 `Phase Details` heading strip is independent of
@@ -1481,6 +1573,23 @@ function findRoadmapProgressTable(roadmapContent) {
1481
1573
  function stripLeadingDelimiter(s) {
1482
1574
  return s.replace(/^[\s—–:-]+/, '').trim();
1483
1575
  }
1576
+ /**
1577
+ * #4134/#4433 (§7.2 rule 6 floor, applied symmetrically): a captured "name"
1578
+ * with no letter or digit anywhere is heading/bullet STRUCTURE, not a curated
1579
+ * name — e.g. the trailing `)` a name-then-version heading leaves after its
1580
+ * version token, or a malformed 🚧-bullet whose only content past the version
1581
+ * is punctuation (`---`, `***`, a lone `:`). #4134 fixed this for
1582
+ * `extractMilestoneHeadingName`'s heading path only; #4433 found the sibling
1583
+ * 🚧-bullet capture (`getMilestoneInfo`'s `listMatch`) and the no-STATE.md
1584
+ * fallback (`inProgressMatch`) both skipped straight to a bare truthiness
1585
+ * check, so a punctuation-only bullet name passed through as a real one. This
1586
+ * is now the SOLE name-validity predicate — every capture site in this file
1587
+ * calls it instead of re-deriving the character class. A name that merely
1588
+ * CONTAINS punctuation is unaffected; digits alone qualify.
1589
+ */
1590
+ function hasNameableContent(s) {
1591
+ return /[\p{L}\p{N}]/u.test(s);
1592
+ }
1484
1593
  /**
1485
1594
  * #3216 (ADR-3180 §7.2's "Name extraction — pinned rule"): the sole "milestone
1486
1595
  * heading text → version + curated name" rule. Strips everything through the
@@ -1493,6 +1602,13 @@ function stripLeadingDelimiter(s) {
1493
1602
  * one implementation. Returns `null` when `headingText` carries no version
1494
1603
  * token at all (e.g. a non-milestone heading reached this by mistake).
1495
1604
  *
1605
+ * #4134 (§7.2 rule 6 floor): the rule's direction assumes version-then-name.
1606
+ * A name-then-version heading (`# Roadmap: Project — Name (v1.13)`) leaves a
1607
+ * punctuation fragment (`)`) after the token; a remainder with no letter or
1608
+ * digit anywhere is heading structure, not a curated name, and is refused as
1609
+ * `name: null` so callers report the honest rule-6 answer instead of
1610
+ * fabricating garbage. Names that merely CONTAIN punctuation are unaffected.
1611
+ *
1496
1612
  * @param expectedVersion - When the caller already knows the exact version it
1497
1613
  * is looking for (the STATE-anchored `getMilestoneInfo` path, which located
1498
1614
  * this heading via `selectMilestoneHeading(roadmap, stateVersion)`), pass it
@@ -1529,7 +1645,18 @@ function extractMilestoneHeadingName(headingText, expectedVersion) {
1529
1645
  // whitespace — the marker is already carried structurally by `closed`, so
1530
1646
  // duplicating it inside `name` (e.g. "Old ✅") is redundant and wrong. Only
1531
1647
  // these three markers, only at the end; a marker inside a name is untouched.
1532
- const name = stripLeadingDelimiter(afterVersion).replace(/\s*(?:[✅📋🚧]\s*)+$/, '') || null;
1648
+ const candidate = stripLeadingDelimiter(afterVersion).replace(/\s*(?:[✅📋🚧]\s*)+$/, '') || null;
1649
+ // #4134 (§7.2 rule 6 floor): a "name" with no letter or digit anywhere is
1650
+ // heading structure, not a curated name. The pinned rule takes everything
1651
+ // AFTER the version token, so a name-then-version heading (`# Roadmap:
1652
+ // Project — Name (v1.13)` — the shape a first-ever ROADMAP.md drifts into)
1653
+ // leaves exactly `)` there, which used to be returned as a COMPLETE-scope
1654
+ // name and propagated into init.* output and STATE.md. Refuse it: callers
1655
+ // already report the honest rule-6 answer (version kept, `name: null`,
1656
+ // scope TRUNCATED) for an unresolvable name. A name that merely CONTAINS
1657
+ // punctuation is untouched — `(` is an ordinary name character (#3171) —
1658
+ // and digits alone qualify (`## v4.0 — 42` is the name `42`).
1659
+ const name = candidate !== null && hasNameableContent(candidate) ? candidate : null;
1533
1660
  return { version, name };
1534
1661
  }
1535
1662
  /**
@@ -1614,7 +1741,7 @@ function getMilestoneInfo(cwd) {
1614
1741
  const listMatch = roadmap.match(new RegExp(`🚧\\s*\\*?\\*?${escapedVer}\\*?\\*?\\s+([^*\\n]+)`, 'i'));
1615
1742
  if (listMatch) {
1616
1743
  const name = stripLeadingDelimiter(listMatch[1]);
1617
- if (name)
1744
+ if (name && hasNameableContent(name))
1618
1745
  return scoped({ version: stateVersion, name }, SCOPE.COMPLETE);
1619
1746
  }
1620
1747
  // #3216: heading selection routes through the shared owner
@@ -1648,7 +1775,10 @@ function getMilestoneInfo(cwd) {
1648
1775
  // (unchanged from the pre-#3216 fallback).
1649
1776
  const inProgressMatch = roadmap.match(/🚧\s*\*\*v(\d+(?:\.\d+)+)\s+([^*]+)\*\*/);
1650
1777
  if (inProgressMatch) {
1651
- return scoped({ version: 'v' + inProgressMatch[1], name: inProgressMatch[2].trim() }, SCOPE.COMPLETE);
1778
+ const inProgressName = inProgressMatch[2].trim();
1779
+ if (hasNameableContent(inProgressName)) {
1780
+ return scoped({ version: 'v' + inProgressMatch[1], name: inProgressName }, SCOPE.COMPLETE);
1781
+ }
1652
1782
  }
1653
1783
  // #3216: enumerate every OPEN (non-shipped) milestone heading via the
1654
1784
  // shared owner and take the first in document order — deletes the
@@ -2015,6 +2145,69 @@ function currentMilestoneRawRanges(content, cwd) {
2015
2145
  }
2016
2146
  return { primary: { start: sectionStart, end: sectionEnd }, details };
2017
2147
  }
2148
+ /**
2149
+ * #4731 — extract a bold-labeled phase field (Goal, Requirements, ...) reading
2150
+ * past hard-wrapped continuation lines. The roadmapper soft-wraps long fields
2151
+ * at ~85 chars, so a single-line capture silently truncated every wrapped
2152
+ * Goal/Requirements. Stops at: another `**Field**` label line, a blank line,
2153
+ * or any heading. Wrapped lines are trimmed and joined with single spaces; a
2154
+ * single-line field is returned unchanged (trimmed). Returns null when the
2155
+ * label is absent.
2156
+ */
2157
+ function extractPhaseFieldMultiline(section, label) {
2158
+ // #2769 label shapes: `**X:**`, `**X**:`, and the spaced `**X** :`.
2159
+ // #4837: anchored to line start (`^[ \t]*`, `m` flag) so an inline
2160
+ // `**Label**` mention embedded in an EARLIER field's own body text cannot
2161
+ // shadow the real field's own declaration line. `[ \t]*` (not `\s*`) keeps
2162
+ // the anchor scoped to same-line leading whitespace under the `m` flag.
2163
+ const labelRe = new RegExp('^[ \\t]*\\*\\*' + label + '(?::\\*\\*|\\*\\*\\s*:?)\\s*([^\\n]+)', 'im');
2164
+ const match = section.match(labelRe);
2165
+ if (!match)
2166
+ return null;
2167
+ const startIdx = match.index ?? 0;
2168
+ const after = section.slice(startIdx + match[0].length);
2169
+ const firstLine = match[1].trim();
2170
+ // #4837 (isolated-review finding): the continuation loop's fence-stop check
2171
+ // only ever sees lines AFTER the label's own line — a fence opener on the
2172
+ // SAME line as the label (`**Requirements:** ```js`) was invisible to it,
2173
+ // leaking one line of fence content before the closing fence line
2174
+ // coincidentally matched the same stop check. Checked here too, so a fence
2175
+ // opening on the label's own line is caught just as conservatively.
2176
+ if (/^(?:`{3,}|~{3,})/.test(firstLine))
2177
+ return null;
2178
+ const contLines = [];
2179
+ const lines = after.split('\n');
2180
+ for (let li = 0; li < lines.length; li++) {
2181
+ const raw = lines[li];
2182
+ // The split's first element is the remainder of the captured first line's
2183
+ // own newline — an empty leading element is the line break, not a blank
2184
+ // continuation line.
2185
+ if (li === 0 && !raw.trim())
2186
+ continue;
2187
+ if (!raw.trim())
2188
+ break;
2189
+ // #4837: a list item is a structural boundary — the real corruption
2190
+ // vector (a `- Deferred to Phase N: OTHER-ID` bullet folding a foreign
2191
+ // REQ-ID into this field's citation-scan input).
2192
+ if (/^\s*[-*+]\s/.test(raw))
2193
+ break;
2194
+ // #4837: case-insensitive label-start character class — a
2195
+ // lowercase-first-letter label (`**requirements:**`) is as much a field
2196
+ // boundary as a capitalized one.
2197
+ if (/^\s*\*\*[A-Za-z][A-Za-z ]*:?(\*\*)?:?\s/.test(raw))
2198
+ break;
2199
+ if (/^\s*#{1,4}\s/.test(raw))
2200
+ break;
2201
+ if (/^\s*\|/.test(raw))
2202
+ break;
2203
+ // #4837: a fenced code block opener stops the scan — folding fence
2204
+ // content is never safe, and stopping early is conservative.
2205
+ if (/^\s*(?:`{3,}|~{3,})/.test(raw))
2206
+ break;
2207
+ contLines.push(raw.trim());
2208
+ }
2209
+ return [firstLine, ...contLines].join(' ').trim() || null;
2210
+ }
2018
2211
  module.exports = {
2019
2212
  stripShippedMilestones,
2020
2213
  extractCurrentMilestone,
@@ -2036,6 +2229,7 @@ module.exports = {
2036
2229
  // own doc comment. milestone.cts's destructive-consumer guard consumes
2037
2230
  // this instead of composing locate+select+section-end itself.
2038
2231
  sliceMilestoneWindow,
2232
+ milestoneSections,
2039
2233
  hasVersionedMilestones,
2040
2234
  hasMilestoneSectioning,
2041
2235
  // #3642: the >=1 sibling buildStateFrontmatter's unbounded branch consumes.
@@ -2049,9 +2243,16 @@ module.exports = {
2049
2243
  // guards and the edit-phase workflow's pre/post capture are built on.
2050
2244
  scanMilestonePhaseIds,
2051
2245
  collectTablePhaseRows,
2246
+ hasPhaseListingTableHeader,
2052
2247
  findMilestoneScopeHeadingLines,
2053
2248
  // #3641: the scope axis's phase-ENTRY predicate, exported so roadmap
2054
2249
  // validate's V004 document-level check routes through the same single
2055
2250
  // owner (and its convention gate) instead of a private inline copy.
2251
+ extractPhaseFieldMultiline,
2056
2252
  hasPhaseEntries,
2253
+ // #4144 round 5 Blocker 1: the readers' own phase-heading grammar, so the
2254
+ // bracket roadmap migrator's "phase-like but unparsed" refusal
2255
+ // (src/roadmap-upgrade.cts) can gate on it instead of a private, looser
2256
+ // "starts with the word Phase" regex.
2257
+ isPhaseHeadingText,
2057
2258
  };