@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
@@ -55,24 +55,24 @@ const { planningDir } = planningWorkspaceMod;
55
55
  // <install>/gsd-core/bin/lib). Precedence for the gate: project config.runtime →
56
56
  // GSD_RUNTIME env (manual/CI override + test seam) → install marker → 'claude'.
57
57
  //
58
- // `claude` is currently the ONLY runtime with nativeModelAliases:true; a
59
- // registry-parity test guards this set so a future alias-capable runtime fails
60
- // loudly here instead of silently omitting.
61
- const RUNTIMES_WITH_NATIVE_ALIASES = new Set(['claude']);
58
+ // Whether a runtime resolves tier aliases natively is a runtime-descriptor fact
59
+ // (`hostBehaviors.nativeModelAliases`, #5169) — `claude` is currently the ONLY
60
+ // runtime that declares it — not a hand-kept set in this module. A label that is
61
+ // not a registered runtime (a future runtime named by env/config) has none.
62
+ function hasNativeModelAliases(runtime) {
63
+ return (0, runtime_name_policy_cjs_1.hostBehaviorsFor)(runtime).nativeModelAliases === true;
64
+ }
62
65
  // #3897 rung 2: the marker reader + its cache and test seams were promoted to
63
66
  // the canonical owner, `runtime-slash.cts` (imported above) — this module now
64
67
  // consumes that single implementation instead of holding its own copy. N5:
65
68
  // behaviour and the seam contract are unchanged by the move; the re-exports
66
69
  // below (`export =` at the bottom of this file) preserve every existing
67
70
  // caller's `require('./model-resolver.cjs')` surface byte-for-behaviour.
68
- // The runtime whose install is actually resolving, canonicalized so an alias or
69
- // case variant (e.g. "claude-code"/"Claude") cannot defeat the native-alias
70
- // check below (#2297 review). Precedence mirrors resolveRuntime()
71
- // (runtime-slash.cts): GSD_RUNTIME env → project config.runtime → per-install
72
- // .gsd-runtime marker → 'claude'.
73
- function resolveActiveRuntime(config) {
74
- return (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], config['runtime'], (0, runtime_slash_cjs_1.readInstallRuntimeMarker)()) || 'claude';
75
- }
71
+ // The runtime whose install is actually resolving — one chain, owned by
72
+ // runtime-slash.cts's `resolveActiveRuntime` (#5169): GSD_RUNTIME env → project
73
+ // config.runtime → per-install .gsd-runtime marker → 'claude', canonicalized so
74
+ // an alias or case variant (e.g. "claude-code"/"Claude") cannot defeat the
75
+ // native-alias check below (#2297 review).
76
76
  // Did the PROJECT's own config (root `.planning/config.json` or the active
77
77
  // workstream/project override) explicitly set resolve_model_ids to "omit"?
78
78
  // Project config takes precedence over the shared ~/.gsd/defaults.json (#2297
@@ -129,7 +129,7 @@ function _resolveRuntimeTier(config, tier) {
129
129
  // the per-install marker), and reading it raw made
130
130
  // `model_profile_overrides.<runtime>.<tier>` silently inert for every
131
131
  // install that did not also write the key by hand.
132
- runtime: resolveActiveRuntime(config),
132
+ runtime: (0, runtime_slash_cjs_1.resolveActiveRuntime)(config),
133
133
  tier,
134
134
  overrides: config['model_profile_overrides'],
135
135
  });
@@ -502,7 +502,7 @@ function resolveModelInternal(cwd, agentType) {
502
502
  ? modelOverrides[agentType]
503
503
  : undefined;
504
504
  if (override) {
505
- const mapped = mapClaudeOverrideForRuntime(override, resolveActiveRuntime(config), agentType);
505
+ const mapped = mapClaudeOverrideForRuntime(override, (0, runtime_slash_cjs_1.resolveActiveRuntime)(config), agentType);
506
506
  if (mapped !== null)
507
507
  return mapped;
508
508
  // Unmappable Claude ID — fall through to tier resolution (matches model_policy).
@@ -528,7 +528,7 @@ function resolveModelInternal(cwd, agentType) {
528
528
  // Never undefined, so the "no runtime declared anywhere" case still resolves
529
529
  // 'claude' and step 3's deliberate claude skip (#1156/#2297/#4192) is
530
530
  // unchanged for every existing install.
531
- const activeRuntime = resolveActiveRuntime(config);
531
+ const activeRuntime = (0, runtime_slash_cjs_1.resolveActiveRuntime)(config);
532
532
  if (tier && tier !== 'inherit') {
533
533
  const onClaude = activeRuntime === 'claude';
534
534
  const effectiveRuntime = activeRuntime;
@@ -582,7 +582,7 @@ function resolveModelInternal(cwd, agentType) {
582
582
  // map, but only outranks the omit gate when that key canonicalizes to a
583
583
  // recognised non-Claude runtime.
584
584
  const omitApplies = config['resolve_model_ids'] === 'omit'
585
- && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(activeRuntime));
585
+ && (projectExplicitlySetsOmit(cwd) || !hasNativeModelAliases(activeRuntime));
586
586
  // CANONICALIZED, not the raw field. Comparing the raw value against the literal
587
587
  // 'claude' made every spelling that is not exactly that string count as a
588
588
  // non-Claude opt-in and outrank the omit gate: `runtime:"Claude"`,
@@ -764,13 +764,13 @@ function resolveModelForTier(cwd, agentType, attempt) {
764
764
  ? modelOverrides[agentType]
765
765
  : undefined;
766
766
  if (override) {
767
- const mapped = mapClaudeOverrideForRuntime(override, resolveActiveRuntime(config), agentType);
767
+ const mapped = mapClaudeOverrideForRuntime(override, (0, runtime_slash_cjs_1.resolveActiveRuntime)(config), agentType);
768
768
  if (mapped !== null)
769
769
  return mapped;
770
770
  // Unmappable Claude ID — fall through to dynamic_routing / model_policy resolution.
771
771
  }
772
772
  // #4505: same active-runtime rule as resolveModelInternal step 3.
773
- if (config['model_policy'] && resolveActiveRuntime(config) !== 'claude') {
773
+ if (config['model_policy'] && (0, runtime_slash_cjs_1.resolveActiveRuntime)(config) !== 'claude') {
774
774
  return resolveModelInternal(cwd, agentType);
775
775
  }
776
776
  const alias = dynamicRoutingModel(config, agentType, attemptN);
@@ -16,7 +16,7 @@
16
16
  * (RFC 4122); invalid values are silently coerced to undefined.
17
17
  * command: string — the dispatched verb
18
18
  * args?: unknown — only present when includeArgs === true
19
- * result: { kind: 'ok' | 'UnknownCommand' | 'InvalidArgs' | 'HandlerRefusal' | 'HandlerFailure', ...payload }
19
+ * result: { kind: 'ok' | 'UnknownCommand' | 'InvalidArgs' | 'HandlerRefusal' | 'HandlerFailure' | 'VerificationStatusInvalid', ...payload }
20
20
  * timestamp: string — ISO 8601
21
21
  */
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -16,6 +16,11 @@
16
16
  * No-op logger (createNoOpLogger):
17
17
  * Silent on all events. Used as the Hub default when no logger is injected.
18
18
  *
19
+ * Live-seam gate (resolveDispatchLogger, #4975):
20
+ * The one opt-in decision both live createHub() seams share. Resolves
21
+ * `audit.enabled` for the seam's cwd and returns the default logger, or
22
+ * undefined (inject nothing) when observability is off.
23
+ *
19
24
  * ADR-457 build-at-publish: the hand-written bin/lib/observability/logger.cjs
20
25
  * collapsed to a TypeScript source of truth. Behaviour is preserved
21
26
  * byte-for-behaviour from the prior hand-written .cjs; only types are added.
@@ -26,6 +31,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
31
  const node_fs_1 = __importDefault(require("node:fs"));
27
32
  const node_path_1 = __importDefault(require("node:path"));
28
33
  const redaction_cjs_1 = require("./redaction.cjs");
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const planningWorkspace = require("../planning-workspace.cjs");
36
+ const { readScopedConfigValue } = planningWorkspace;
29
37
  const AUDIT_FILE_NAME = '.gsd-trace.jsonl';
30
38
  const PLANNING_DIR = '.planning';
31
39
  // ─── helpers ─────────────────────────────────────────────────────────────────
@@ -47,6 +55,8 @@ function _safeStringify(value) {
47
55
  * inject nothing (the Hub stays byte-for-byte silent, preserving the default
48
56
  * dispatch output contract, incl. --json-errors); when on, the caller injects
49
57
  * createDefaultLogger and gets the stderr-on-error line + opt-in file audit.
58
+ * The live seams reach it through resolveDispatchLogger, which supplies the
59
+ * project config — called bare, only the env var is seen (#4975).
50
60
  */
51
61
  function _isAuditEnabled(config) {
52
62
  if (process.env['GSD_AUDIT'] === '1')
@@ -148,4 +158,39 @@ function createDefaultLogger({ cwd = process.cwd(), config } = {}) {
148
158
  },
149
159
  };
150
160
  }
151
- module.exports = { createDefaultLogger, createNoOpLogger, isAuditEnabled: _isAuditEnabled };
161
+ /**
162
+ * Resolve `audit.enabled` for `cwd` as the value `config-get audit.enabled`
163
+ * reports, through planning-workspace's readScopedConfigValue — the one
164
+ * scope-aware ladder worktreesOptedOut (#3972) reads too, so the two gates
165
+ * can never resolve the same config differently. Strict `=== true`, never
166
+ * coerced.
167
+ *
168
+ * Direct file reads, deliberately NOT loadConfig: this runs on every live
169
+ * dispatch, and loadConfig can rewrite config.json, spawn git, scan the
170
+ * capability registry, and print warnings — none of which a dispatch that
171
+ * never opted in may do (the default dispatch output is a stable contract,
172
+ * ADR-2619). Never throws. An unreadable or unparseable config file sets
173
+ * nothing, so the ladder moves on: under GSD_WORKSTREAM a broken workstream
174
+ * config inherits the root's `audit.enabled` (where config-get fails with
175
+ * CONFIG_PARSE_FAILED); otherwise the GSD_AUDIT env var alone decides.
176
+ */
177
+ function _readAuditConfig(cwd) {
178
+ const { present, value } = readScopedConfigValue(cwd, ['audit', 'enabled']);
179
+ return { audit: { enabled: present && value === true } };
180
+ }
181
+ /**
182
+ * The DispatchLogger a live createHub() seam injects (#4975). Either
183
+ * GSD_AUDIT=1 or `audit.enabled: true` in the project config turns the opt-in
184
+ * audit trail (and the structured stderr line on error) on; neither turns the
185
+ * other off. When observability is off this returns undefined so the Hub keeps
186
+ * its no-op fallback and the default dispatch output — including the
187
+ * --json-errors envelope — stays byte-for-byte unchanged (#2620).
188
+ *
189
+ * `cwd` defaults exactly as createDefaultLogger's does, so the config that
190
+ * decides and the directory that receives the trail are always the same.
191
+ */
192
+ function resolveDispatchLogger(cwd = process.cwd()) {
193
+ const config = _readAuditConfig(cwd);
194
+ return _isAuditEnabled(config) ? createDefaultLogger({ cwd, config }) : undefined;
195
+ }
196
+ module.exports = { createDefaultLogger, createNoOpLogger, isAuditEnabled: _isAuditEnabled, resolveDispatchLogger };
@@ -44,6 +44,7 @@
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.MAX_USER_PATTERN_LEN = void 0;
46
46
  exports.escapeRegex = escapeRegex;
47
+ exports.escapeEre = escapeEre;
47
48
  exports.literalPattern = literalPattern;
48
49
  exports.compileUserPattern = compileUserPattern;
49
50
  // re2js is vendored, not an npm dependency at runtime: gsd-core/bin/** is
@@ -66,6 +67,15 @@ const escapeMetachars = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
66
67
  function escapeRegex(value) {
67
68
  return (escapeBuiltin ?? escapeMetachars)(value);
68
69
  }
70
+ /**
71
+ * Escape `value` for a POSIX extended regular expression (e.g. `git log --extended-regexp
72
+ * --grep=…`). Always the plain backslash-escape of every ERE metacharacter, never
73
+ * `RegExp.escape`: that built-in writes a leading digit as `\x31`, which ERE does not read as a
74
+ * digit. #5139 (a plan id interpolated into a git `--grep` pattern).
75
+ */
76
+ function escapeEre(value) {
77
+ return escapeMetachars(value);
78
+ }
69
79
  function literalPattern(value, flags) {
70
80
  return new RegExp(escapeRegex(value), flags);
71
81
  }
@@ -22,10 +22,12 @@ const commandRoutingHub = require("./command-routing-hub.cjs");
22
22
  const { createHub, ERROR_KINDS, makeInvalidArgs } = commandRoutingHub;
23
23
  // #2620 (ADR-0174 §6): inject the reference DispatchLogger on the live phase
24
24
  // dispatch path, but only when observability is opt-in enabled; otherwise the
25
- // Hub stays byte-for-byte silent via its no-op fallback.
25
+ // Hub stays byte-for-byte silent via its no-op fallback. #4975: the opt-in is
26
+ // GSD_AUDIT=1 or `audit.enabled` in the project config — resolveDispatchLogger
27
+ // owns that whole decision for every live seam.
26
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
27
29
  const observabilityLogger = require("./observability/logger.cjs");
28
- const { createDefaultLogger, isAuditEnabled } = observabilityLogger;
30
+ const { resolveDispatchLogger } = observabilityLogger;
29
31
  // ─── Implementation ───────────────────────────────────────────────────────────
30
32
  function routePhaseCommand({ phase, args, cwd, raw, error }) {
31
33
  // ── Unsupported subcommands ─────────────────────────────────────────────────
@@ -258,8 +260,8 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
258
260
  // #175: Hub is CJS-only — no mode param, no sdkLoader.
259
261
  // #2620: wire the reference logger (ADR-0174 §6) only when observability is
260
262
  // opt-in enabled; otherwise leave it unset so the Hub stays byte-for-byte
261
- // silent via its no-op fallback.
262
- const hub = createHub({ cjsRegistry, manifest, logger: isAuditEnabled() ? createDefaultLogger({ cwd }) : undefined });
263
+ // silent via its no-op fallback. #4975: GSD_AUDIT=1 or `audit.enabled`.
264
+ const hub = createHub({ cjsRegistry, manifest, logger: resolveDispatchLogger(cwd) });
263
265
  // ── Dispatch ────────────────────────────────────────────────────────────────
264
266
  const result = hub.dispatch({
265
267
  family: 'phase',
@@ -282,6 +284,11 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
282
284
  error(result.reason);
283
285
  return;
284
286
  }
287
+ if (result.kind === ERROR_KINDS.VerificationStatusInvalid) {
288
+ // #5118: typed reason survives to the CLI surface
289
+ error(result.message, result.reason);
290
+ return;
291
+ }
285
292
  // HandlerFailure: message field
286
293
  error(result.message);
287
294
  return;
@@ -47,6 +47,7 @@ exports.parseCalibrationDocument = parseCalibrationDocument;
47
47
  exports.renderCalibrationDocument = renderCalibrationDocument;
48
48
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- prompt-budget.cjs is an export= CommonJS module
49
49
  const promptBudget = require("./prompt-budget.cjs");
50
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
50
51
  const { estimateTokens } = promptBudget;
51
52
  /**
52
53
  * Assert that a bare number is an UNCORRECTED projection.
@@ -239,11 +240,11 @@ function applyCalibration(rawTokens, factor) {
239
240
  function extractFrontmatterBlock(text, key) {
240
241
  if (typeof text !== 'string')
241
242
  return null;
242
- // Anchor at byte 0 — CRLF-tolerant.
243
- const fm = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|\r?$)/.exec(text);
244
- if (fm === null)
243
+ // The block is the one the one fence owner finds (`locateFrontmatterFence`).
244
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(text);
245
+ if (!fence?.closed)
245
246
  return null;
246
- const lines = fm[1].split(/\r?\n/);
247
+ const lines = text.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/);
247
248
  const startIdx = lines.findIndex((l) => l === `${key}:` || l.startsWith(`${key}:`));
248
249
  if (startIdx === -1)
249
250
  return null;
@@ -1342,7 +1342,7 @@ const unpad = (digits) => digits.replace(/^0+(?=\d)/, '');
1342
1342
  * candidates (#2237).
1343
1343
  *
1344
1344
  * TAKE `matches[0]` — `cmdPhasesList`, `cmdInitManager`, `cmdRoadmapAnalyze`,
1345
- * `cmdVerifySchemaDrift`, `detectVerifyFailed`. Each read a directory to
1345
+ * the schema-drift gate (`resolvePhaseDirByToken`), `detectVerifyFailed`. Each read a directory to
1346
1346
  * DECORATE a row they are already emitting; each used `.find()` before this
1347
1347
  * PR, so first-match is their prior behavior preserved verbatim, and each is
1348
1348
  * order-stable because the directory list is sorted and this function filters
@@ -31,6 +31,9 @@ const markdown_table_cjs_1 = require("./markdown-table.cjs");
31
31
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
32
32
  const phaseIdMod = require("./phase-id.cjs");
33
33
  const { isSentinelPhaseId } = phaseIdMod;
34
+ // #5060: `phase-status.cjs` is a load-time leaf (no top-level requires), safe
35
+ // to import here for the ROADMAP Status-cell reader.
36
+ const phase_status_cjs_1 = require("./phase-status.cjs");
34
37
  /**
35
38
  * #3227: the single owner of "where is this ROADMAP's Progress table".
36
39
  * Lifted verbatim out of deriveProgressFromRoadmap so `state-contract.cts`
@@ -93,8 +96,6 @@ function deriveProgressFromRoadmap(roadmapContent) {
93
96
  const table = locateProgressTable(roadmapContent);
94
97
  if (table) {
95
98
  const allRows = table.rows;
96
- const completed = allRows.filter((r) => /^complete$/i.test((r['Status'] ?? '').trim())).length;
97
- completedPhases = completed > 0 ? completed : null;
98
99
  // Data rows only (exclude sentinel phases 0 and 999.x).
99
100
  // #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
100
101
  const dataRows = allRows.filter((r) => {
@@ -102,6 +103,12 @@ function deriveProgressFromRoadmap(roadmapContent) {
102
103
  return /^\d/.test(phase) && !isSentinelPhaseId(phase);
103
104
  });
104
105
  totalPhases = dataRows.length > 0 ? dataRows.length : null;
106
+ // #5060: routed through the Phase Status Module's `parseRoadmapStatusCell`
107
+ // owner (rather than a local `/^complete$/i` regex) and counted over
108
+ // `dataRows` (not `allRows`) — a sentinel row's Status cell must never
109
+ // contribute to `completedPhases`.
110
+ const completed = dataRows.filter((r) => (0, phase_status_cjs_1.parseRoadmapStatusCell)(r['Status']) === phase_status_cjs_1.WIRE_STATUS.COMPLETE).length;
111
+ completedPhases = completed > 0 ? completed : null;
105
112
  let totalPlansSum = 0;
106
113
  for (const r of allRows) {
107
114
  const cell = (r['Plans Complete'] ?? '').trim();
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+ /**
3
+ * Phase Status Module — the single owner of "what state is phase P in?"
4
+ * (ADR-5057 §1/§2, epic #5056 Phase 1, #5060).
5
+ *
6
+ * ADR-3180 §7.4 made `isPhaseComplete` the owner of "is phase P complete?".
7
+ * The richer question the UI, the health rules and the ROADMAP writers ask —
8
+ * which rung of the lifecycle is this phase on — had no owner: it was derived
9
+ * at eleven sites in seven vocabularies, and two of them disagreed on a stale
10
+ * `passed` report. This module is that owner.
11
+ *
12
+ * - `PHASE_STATUS` is a closed, frozen ladder. Its values are the upper-case
13
+ * rung names, spelled unlike every projection word, so a display label or
14
+ * a wire value can never pass for a status.
15
+ * - `phaseStatusFromFacts` is the ladder itself — pure. Callers that already
16
+ * hold the §7 owners' answers (the health rules over `PlanningSnapshot`,
17
+ * the pure Workstream Inventory builder, `update-plan-progress`'s stricter
18
+ * write gate) hand it those facts.
19
+ * - `phaseStatus(phaseDir)` gathers the facts from the §7 owners only —
20
+ * `scanPhasePlans` (§7.5) for counts and `isPhaseComplete` (§7.4) for
21
+ * completion and the verification verdict. It never reads VERIFICATION.md
22
+ * frontmatter itself.
23
+ * - Every other vocabulary is a projection exported here: display labels,
24
+ * the `state.json` / Workstream Inventory wire values, the ROADMAP
25
+ * `## Progress` Status cell (both directions), `roadmap analyze` /
26
+ * `init manager`'s `disk_status`, `init`'s `completion_status`, and
27
+ * `init progress`'s `status`. A consumer never maps raw inputs to a word.
28
+ * - Every function that takes a `PhaseStatus` throws `TypeError` on a value
29
+ * outside the ladder, so an out-of-vocabulary value fails where it is
30
+ * produced (ADR-5057 §1.2).
31
+ *
32
+ * LOAD-TIME LEAF. `verification.cjs` and `plan-scan.cjs` are required lazily
33
+ * inside `phaseStatus()`. `state-contract.cjs` imports this module at top
34
+ * level for its wire vocabulary, and `state-contract` sits on the documented
35
+ * `state -> state-contract -> smart-entry -> state` require cycle; keeping this
36
+ * module import-free at load keeps it off that cycle.
37
+ *
38
+ * ADR-457 build-at-publish: source in src/phase-status.cts, compiled to
39
+ * gsd-core/bin/lib/phase-status.cjs (gitignored).
40
+ */
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.COMPLETION_STATUS = exports.PROGRESS_STATUS = exports.DISK_STATUS = exports.ROADMAP_STATUS_TOKEN = exports.WIRE_STATUS = exports.PHASE_STATUS = void 0;
43
+ exports.phaseStatusFromFacts = phaseStatusFromFacts;
44
+ exports.phaseStatus = phaseStatus;
45
+ exports.foldPhaseStatuses = foldPhaseStatuses;
46
+ exports.toDisplayLabel = toDisplayLabel;
47
+ exports.toWireStatus = toWireStatus;
48
+ exports.toRoadmapStatusCell = toRoadmapStatusCell;
49
+ exports.toDiskStatus = toDiskStatus;
50
+ exports.toCompletionStatus = toCompletionStatus;
51
+ exports.toProgressStatus = toProgressStatus;
52
+ exports.matchRoadmapStatusCell = matchRoadmapStatusCell;
53
+ exports.parseRoadmapStatusCell = parseRoadmapStatusCell;
54
+ // ─── Closed vocabularies ───────────────────────────────────────────────────
55
+ /** The lifecycle ladder, lowest rung first. */
56
+ exports.PHASE_STATUS = Object.freeze({
57
+ NOT_STARTED: 'NOT_STARTED',
58
+ PLANNED: 'PLANNED',
59
+ IN_PROGRESS: 'IN_PROGRESS',
60
+ EXECUTED: 'EXECUTED',
61
+ NEEDS_REVIEW: 'NEEDS_REVIEW',
62
+ COMPLETE: 'COMPLETE',
63
+ });
64
+ /**
65
+ * The three-value wire vocabulary published by `.planning/state.json`
66
+ * (`phases[].status`) and the Workstream Inventory (`PhaseStatus.status`).
67
+ */
68
+ exports.WIRE_STATUS = Object.freeze({
69
+ COMPLETE: 'complete',
70
+ IN_PROGRESS: 'in_progress',
71
+ PENDING: 'pending',
72
+ });
73
+ /**
74
+ * The ROADMAP `## Progress` table Status-cell vocabulary: the template's
75
+ * `Not started | In progress | Complete | Deferred` plus the `Planned` word
76
+ * `roadmap update-plan-progress` writes. `In Progress` is spelled the way the
77
+ * writers have always written it.
78
+ */
79
+ exports.ROADMAP_STATUS_TOKEN = Object.freeze({
80
+ NOT_STARTED: 'Not started',
81
+ PLANNED: 'Planned',
82
+ IN_PROGRESS: 'In Progress',
83
+ COMPLETE: 'Complete',
84
+ DEFERRED: 'Deferred',
85
+ });
86
+ /** `roadmap analyze` / `init manager`'s `disk_status` vocabulary (ADR-5057 §1.2). */
87
+ exports.DISK_STATUS = Object.freeze({
88
+ COMPLETE: 'complete',
89
+ EXECUTED: 'executed',
90
+ PARTIAL: 'partial',
91
+ PLANNED: 'planned',
92
+ RESEARCHED: 'researched',
93
+ DISCUSSED: 'discussed',
94
+ EMPTY: 'empty',
95
+ NO_DIRECTORY: 'no_directory',
96
+ });
97
+ /** `init progress`'s per-phase `status` vocabulary (ADR-5057 §1.2). */
98
+ exports.PROGRESS_STATUS = Object.freeze({
99
+ COMPLETE: 'complete',
100
+ EXECUTED: 'executed',
101
+ IN_PROGRESS: 'in_progress',
102
+ RESEARCHED: 'researched',
103
+ PENDING: 'pending',
104
+ NOT_STARTED: 'not_started',
105
+ });
106
+ /** `init`'s `completion_status` vocabulary (ADR-5057 §1.2). */
107
+ exports.COMPLETION_STATUS = Object.freeze({
108
+ COMPLETE: 'complete',
109
+ EXECUTED: 'executed',
110
+ INCOMPLETE: 'incomplete',
111
+ });
112
+ const LADDER = Object.freeze([
113
+ exports.PHASE_STATUS.NOT_STARTED,
114
+ exports.PHASE_STATUS.PLANNED,
115
+ exports.PHASE_STATUS.IN_PROGRESS,
116
+ exports.PHASE_STATUS.EXECUTED,
117
+ exports.PHASE_STATUS.NEEDS_REVIEW,
118
+ exports.PHASE_STATUS.COMPLETE,
119
+ ]);
120
+ const LADDER_RANK = new Map(LADDER.map((s, i) => [s, i]));
121
+ function assertPhaseStatus(value, where) {
122
+ if (typeof value !== 'string' || !LADDER_RANK.has(value)) {
123
+ throw new TypeError(`${where}: ${JSON.stringify(value)} is not a PHASE_STATUS value`);
124
+ }
125
+ }
126
+ function assertCount(value, name) {
127
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
128
+ throw new TypeError(`phaseStatusFromFacts: ${name} must be a non-negative integer, got ${JSON.stringify(value)}`);
129
+ }
130
+ }
131
+ /**
132
+ * The ladder. First matching rung wins:
133
+ * complete → COMPLETE (disk-strict: no plan precondition, #3168)
134
+ * no plans → NOT_STARTED
135
+ * no summaries → PLANNED
136
+ * fewer summaries than plans → IN_PROGRESS
137
+ * verification `human_needed` → NEEDS_REVIEW
138
+ * otherwise → EXECUTED (gaps_found, stale, missing, unparseable, phase_dir_not_found)
139
+ *
140
+ * `verificationStatus`, when non-null, must be a VerificationStatus member
141
+ * (#5118) — the same fail-where-produced rule as `assertPhaseStatus`.
142
+ *
143
+ * Completion comes ONLY from `complete`. The ladder never restates a
144
+ * summary-versus-plan completion comparison (scripts/lint-completion-predicate-drift.cjs shape (c)).
145
+ */
146
+ function phaseStatusFromFacts(facts) {
147
+ if (facts === null || typeof facts !== 'object') {
148
+ throw new TypeError('phaseStatusFromFacts: facts object required');
149
+ }
150
+ const { planCount, summaryCount, complete, verificationStatus } = facts;
151
+ assertCount(planCount, 'planCount');
152
+ assertCount(summaryCount, 'summaryCount');
153
+ if (typeof complete !== 'boolean') {
154
+ throw new TypeError(`phaseStatusFromFacts: complete must be a boolean, got ${JSON.stringify(complete)}`);
155
+ }
156
+ const { verification } = owners();
157
+ if (verificationStatus !== null && verificationStatus !== undefined) {
158
+ // Called through a plain function type: a lazily-required owner's
159
+ // assertion signature cannot narrow here (TS2775), and needs not to.
160
+ const assertMember = verification.assertVerificationStatus;
161
+ assertMember(verificationStatus, 'phaseStatusFromFacts: verificationStatus');
162
+ }
163
+ if (complete)
164
+ return exports.PHASE_STATUS.COMPLETE;
165
+ if (planCount === 0)
166
+ return exports.PHASE_STATUS.NOT_STARTED;
167
+ if (summaryCount === 0)
168
+ return exports.PHASE_STATUS.PLANNED;
169
+ if (summaryCount < planCount)
170
+ return exports.PHASE_STATUS.IN_PROGRESS;
171
+ if (verificationStatus === verification.VERIFICATION_STATUS.HUMAN_NEEDED)
172
+ return exports.PHASE_STATUS.NEEDS_REVIEW;
173
+ return exports.PHASE_STATUS.EXECUTED;
174
+ }
175
+ const SCOPE_SEVERITY = Object.freeze({
176
+ complete: 0,
177
+ truncated: 1,
178
+ unscoped: 2,
179
+ unreadable: 3,
180
+ });
181
+ let _verification = null;
182
+ let _planScan = null;
183
+ function owners() {
184
+ /* eslint-disable @typescript-eslint/no-require-imports */
185
+ if (!_verification)
186
+ _verification = require('./verification.cjs');
187
+ if (!_planScan)
188
+ _planScan = require('./plan-scan.cjs');
189
+ /* eslint-enable @typescript-eslint/no-require-imports */
190
+ return { verification: _verification, planScan: _planScan };
191
+ }
192
+ /**
193
+ * What state is the phase in `phaseDir` in? Composes only ADR-3180's §7
194
+ * owners. `scope` is the worse of the plan scan's and the completion read's
195
+ * scopes, so a caller can tell "not started" from "could not look".
196
+ */
197
+ function phaseStatus(phaseDir, deps = {}) {
198
+ const { verification, planScan } = owners();
199
+ const scan = planScan.scanPhasePlans(phaseDir);
200
+ const completion = verification.isPhaseComplete(phaseDir, {
201
+ convention: deps.convention,
202
+ fs: deps.fs,
203
+ phaseCleanCommitTimesMs: deps.phaseCleanCommitTimesMs,
204
+ });
205
+ const status = phaseStatusFromFacts({
206
+ planCount: scan.planCount,
207
+ summaryCount: scan.summaryCount,
208
+ complete: completion.value.complete,
209
+ verificationStatus: completion.value.verification.status,
210
+ });
211
+ const scope = (SCOPE_SEVERITY[completion.scope] ?? 0) > (SCOPE_SEVERITY[scan.scope] ?? 0)
212
+ ? completion.scope
213
+ : scan.scope;
214
+ return {
215
+ value: {
216
+ status,
217
+ planCount: scan.planCount,
218
+ summaryCount: scan.summaryCount,
219
+ verification: completion.value.verification,
220
+ ...(completion.value.statusError ? { statusError: completion.value.statusError } : {}),
221
+ },
222
+ scope,
223
+ };
224
+ }
225
+ // ─── Fold (#2408) ──────────────────────────────────────────────────────────
226
+ /**
227
+ * Fold two statuses for directories that collide on one phase key (#2408):
228
+ * the further-along rung wins. Commutative, idempotent and associative, so
229
+ * `readdirSync` order cannot change the answer.
230
+ */
231
+ function foldPhaseStatuses(a, b) {
232
+ assertPhaseStatus(a, 'foldPhaseStatuses');
233
+ assertPhaseStatus(b, 'foldPhaseStatuses');
234
+ return LADDER_RANK.get(a) >= LADDER_RANK.get(b) ? a : b;
235
+ }
236
+ const DISPLAY_LABEL = Object.freeze({
237
+ [exports.PHASE_STATUS.NOT_STARTED]: 'Not Started',
238
+ [exports.PHASE_STATUS.PLANNED]: 'Planned',
239
+ [exports.PHASE_STATUS.IN_PROGRESS]: 'In Progress',
240
+ [exports.PHASE_STATUS.EXECUTED]: 'Executed',
241
+ [exports.PHASE_STATUS.NEEDS_REVIEW]: 'Needs Review',
242
+ [exports.PHASE_STATUS.COMPLETE]: 'Complete',
243
+ });
244
+ /**
245
+ * The label `progress`, `stats`, `init plan-phase` and `gsd-health` show. The
246
+ * NOT_STARTED word is the caller's display choice (`progress` and `init` say
247
+ * `Pending`, `stats` and the health rule say `Not Started`).
248
+ */
249
+ function toDisplayLabel(status, opts = {}) {
250
+ assertPhaseStatus(status, 'toDisplayLabel');
251
+ const pendingWord = opts.pendingWord ?? 'Not Started';
252
+ if (pendingWord !== 'Pending' && pendingWord !== 'Not Started') {
253
+ throw new TypeError(`toDisplayLabel: pendingWord must be 'Pending' or 'Not Started', got ${JSON.stringify(pendingWord)}`);
254
+ }
255
+ return status === exports.PHASE_STATUS.NOT_STARTED ? pendingWord : DISPLAY_LABEL[status];
256
+ }
257
+ /** `pending` means nothing is planned yet; every rung between that and COMPLETE is `in_progress`. */
258
+ function toWireStatus(status) {
259
+ assertPhaseStatus(status, 'toWireStatus');
260
+ if (status === exports.PHASE_STATUS.COMPLETE)
261
+ return exports.WIRE_STATUS.COMPLETE;
262
+ if (status === exports.PHASE_STATUS.NOT_STARTED)
263
+ return exports.WIRE_STATUS.PENDING;
264
+ return exports.WIRE_STATUS.IN_PROGRESS;
265
+ }
266
+ /** The word a ROADMAP writer puts in the Status cell for this rung. */
267
+ function toRoadmapStatusCell(status) {
268
+ assertPhaseStatus(status, 'toRoadmapStatusCell');
269
+ switch (status) {
270
+ case exports.PHASE_STATUS.COMPLETE: return exports.ROADMAP_STATUS_TOKEN.COMPLETE;
271
+ case exports.PHASE_STATUS.PLANNED: return exports.ROADMAP_STATUS_TOKEN.PLANNED;
272
+ case exports.PHASE_STATUS.NOT_STARTED: return exports.ROADMAP_STATUS_TOKEN.NOT_STARTED;
273
+ default: return exports.ROADMAP_STATUS_TOKEN.IN_PROGRESS;
274
+ }
275
+ }
276
+ /**
277
+ * `roadmap analyze` / `init manager`'s `disk_status`. Below NOT_STARTED the
278
+ * word records which pre-planning artifacts exist. (`no_directory` is the
279
+ * callers' own word for "no directory matched" — there is no phase to ask.)
280
+ */
281
+ function toDiskStatus(status, artifacts) {
282
+ assertPhaseStatus(status, 'toDiskStatus');
283
+ switch (status) {
284
+ case exports.PHASE_STATUS.COMPLETE: return exports.DISK_STATUS.COMPLETE;
285
+ case exports.PHASE_STATUS.NEEDS_REVIEW:
286
+ case exports.PHASE_STATUS.EXECUTED: return exports.DISK_STATUS.EXECUTED;
287
+ case exports.PHASE_STATUS.IN_PROGRESS: return exports.DISK_STATUS.PARTIAL;
288
+ case exports.PHASE_STATUS.PLANNED: return exports.DISK_STATUS.PLANNED;
289
+ default:
290
+ if (artifacts.hasResearch)
291
+ return exports.DISK_STATUS.RESEARCHED;
292
+ if (artifacts.hasContext)
293
+ return exports.DISK_STATUS.DISCUSSED;
294
+ return exports.DISK_STATUS.EMPTY;
295
+ }
296
+ }
297
+ /** `init`'s `completion_status`: complete / executed / incomplete. */
298
+ function toCompletionStatus(status) {
299
+ assertPhaseStatus(status, 'toCompletionStatus');
300
+ if (status === exports.PHASE_STATUS.COMPLETE)
301
+ return exports.COMPLETION_STATUS.COMPLETE;
302
+ if (status === exports.PHASE_STATUS.EXECUTED || status === exports.PHASE_STATUS.NEEDS_REVIEW)
303
+ return exports.COMPLETION_STATUS.EXECUTED;
304
+ return exports.COMPLETION_STATUS.INCOMPLETE;
305
+ }
306
+ /** `init progress`'s per-phase `status`. */
307
+ function toProgressStatus(status, artifacts) {
308
+ assertPhaseStatus(status, 'toProgressStatus');
309
+ switch (status) {
310
+ case exports.PHASE_STATUS.COMPLETE: return exports.PROGRESS_STATUS.COMPLETE;
311
+ case exports.PHASE_STATUS.NEEDS_REVIEW:
312
+ case exports.PHASE_STATUS.EXECUTED: return exports.PROGRESS_STATUS.EXECUTED;
313
+ case exports.PHASE_STATUS.IN_PROGRESS:
314
+ case exports.PHASE_STATUS.PLANNED: return exports.PROGRESS_STATUS.IN_PROGRESS;
315
+ default: return artifacts.hasResearch ? exports.PROGRESS_STATUS.RESEARCHED : exports.PROGRESS_STATUS.PENDING;
316
+ }
317
+ }
318
+ // ─── ROADMAP Status cell reader ────────────────────────────────────────────
319
+ // Leading token of a trimmed cell, case-insensitive, a whitespace run allowed
320
+ // inside the two-word tokens, and `(?!\w)` so `Completed` / `Planning` are not
321
+ // tokens. #4925 lets operator prose follow the token; that prose is ignored.
322
+ const CELL_TOKEN_RE = /^(not\s+started|planned|in\s+progress|complete|deferred)(?!\w)/i;
323
+ const TOKEN_BY_KEY = Object.freeze({
324
+ 'not started': exports.ROADMAP_STATUS_TOKEN.NOT_STARTED,
325
+ planned: exports.ROADMAP_STATUS_TOKEN.PLANNED,
326
+ 'in progress': exports.ROADMAP_STATUS_TOKEN.IN_PROGRESS,
327
+ complete: exports.ROADMAP_STATUS_TOKEN.COMPLETE,
328
+ deferred: exports.ROADMAP_STATUS_TOKEN.DEFERRED,
329
+ });
330
+ /**
331
+ * Read a Status cell's leading token. Returns the canonical token and the raw
332
+ * text it matched (writers compare `text` to decide whether a rewrite is a
333
+ * no-op), or null when the cell has no leading token. Never throws.
334
+ */
335
+ function matchRoadmapStatusCell(cell) {
336
+ if (typeof cell !== 'string')
337
+ return null;
338
+ const m = CELL_TOKEN_RE.exec(cell.trim());
339
+ if (!m)
340
+ return null;
341
+ const key = m[1].toLowerCase().replace(/\s+/g, ' ');
342
+ return { token: TOKEN_BY_KEY[key], text: m[1] };
343
+ }
344
+ /**
345
+ * A Status cell as a wire value. `Deferred`, `Not started`, a cell with no
346
+ * leading token, and a non-string all read `pending` — an unrecognized cell
347
+ * never becomes a fourth wire value. Inverse of `toRoadmapStatusCell` under
348
+ * `toWireStatus` (ADR-5057 Phase 1 property).
349
+ */
350
+ function parseRoadmapStatusCell(cell) {
351
+ const m = matchRoadmapStatusCell(cell);
352
+ if (m === null)
353
+ return exports.WIRE_STATUS.PENDING;
354
+ switch (m.token) {
355
+ case exports.ROADMAP_STATUS_TOKEN.COMPLETE: return exports.WIRE_STATUS.COMPLETE;
356
+ case exports.ROADMAP_STATUS_TOKEN.IN_PROGRESS:
357
+ case exports.ROADMAP_STATUS_TOKEN.PLANNED: return exports.WIRE_STATUS.IN_PROGRESS;
358
+ default: return exports.WIRE_STATUS.PENDING;
359
+ }
360
+ }