@opengsd/gsd-core 1.10.0 → 1.11.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 (328) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-debug-session-manager.md +11 -0
  4. package/agents/gsd-doc-synthesizer.md +2 -4
  5. package/agents/gsd-executor.md +5 -5
  6. package/agents/gsd-mempalace-curator.md +5 -2
  7. package/agents/gsd-phase-researcher.md +20 -1
  8. package/agents/gsd-plan-checker.md +37 -0
  9. package/agents/gsd-planner.md +44 -46
  10. package/agents/gsd-user-profiler.md +3 -0
  11. package/agents/gsd-verifier.md +12 -3
  12. package/bin/install.js +841 -971
  13. package/bin/lib/ui-safety-gate.cjs +2 -0
  14. package/commands/gsd/code-review.md +1 -1
  15. package/commands/gsd/execute-phase.md +1 -1
  16. package/commands/gsd/map-codebase.md +1 -1
  17. package/commands/gsd/mempalace-capture.md +1 -1
  18. package/commands/gsd/mempalace-recall.md +1 -1
  19. package/commands/gsd/new-milestone.md +1 -1
  20. package/commands/gsd/quick.md +1 -1
  21. package/commands/gsd/review-backlog.md +2 -1
  22. package/commands/gsd/verify-work.md +1 -1
  23. package/gsd-core/bin/gsd-tools.cjs +469 -88
  24. package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
  25. package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
  26. package/gsd-core/bin/lib/api-coverage.cjs +3 -5
  27. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  28. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  29. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  30. package/gsd-core/bin/lib/audit.cjs +876 -240
  31. package/gsd-core/bin/lib/broken-windows.cjs +1 -1
  32. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  33. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  34. package/gsd-core/bin/lib/capability-registry.cjs +575 -101
  35. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  36. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  37. package/gsd-core/bin/lib/capability-validator.cjs +495 -22
  38. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  39. package/gsd-core/bin/lib/check-command-router.cjs +71 -37
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  41. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  42. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  43. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  44. package/gsd-core/bin/lib/commands.cjs +651 -86
  45. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  47. package/gsd-core/bin/lib/config-loader.cjs +75 -0
  48. package/gsd-core/bin/lib/config.cjs +10 -1
  49. package/gsd-core/bin/lib/core-utils.cjs +127 -29
  50. package/gsd-core/bin/lib/decisions.cjs +23 -0
  51. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  52. package/gsd-core/bin/lib/frontmatter.cjs +155 -20
  53. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  54. package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
  55. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  56. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  57. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  58. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  59. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  60. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  61. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  62. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  63. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  64. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  65. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  66. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  67. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  68. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  69. package/gsd-core/bin/lib/init.cjs +321 -129
  70. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  71. package/gsd-core/bin/lib/install-engine.cjs +745 -258
  72. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  73. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  74. package/gsd-core/bin/lib/install-profiles.cjs +134 -57
  75. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  76. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  77. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  78. package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
  79. package/gsd-core/bin/lib/io.cjs +10 -0
  80. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  81. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  82. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  83. package/gsd-core/bin/lib/milestone.cjs +754 -70
  84. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  85. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  86. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  87. package/gsd-core/bin/lib/pattern.cjs +122 -0
  88. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  89. package/gsd-core/bin/lib/phase-id.cjs +444 -36
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  91. package/gsd-core/bin/lib/phase-locator.cjs +125 -18
  92. package/gsd-core/bin/lib/phase.cjs +646 -143
  93. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  94. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  95. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  96. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  97. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  98. package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
  99. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  100. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  101. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  102. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  103. package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
  104. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  105. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  106. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  107. package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
  108. package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
  109. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  110. package/gsd-core/bin/lib/roadmap.cjs +385 -94
  111. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
  112. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  113. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
  114. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  115. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  116. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
  117. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  118. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  119. package/gsd-core/bin/lib/security.cjs +104 -5
  120. package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
  121. package/gsd-core/bin/lib/smart-entry.cjs +142 -22
  122. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  123. package/gsd-core/bin/lib/state-document.cjs +152 -8
  124. package/gsd-core/bin/lib/state-transition.cjs +371 -117
  125. package/gsd-core/bin/lib/state.cjs +1794 -357
  126. package/gsd-core/bin/lib/surface.cjs +23 -9
  127. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  128. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  129. package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
  130. package/gsd-core/bin/lib/uat.cjs +399 -56
  131. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  132. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  133. package/gsd-core/bin/lib/unusable-input.cjs +24 -0
  134. package/gsd-core/bin/lib/update-context.cjs +8 -2
  135. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  136. package/gsd-core/bin/lib/validate.cjs +20 -6
  137. package/gsd-core/bin/lib/vendor/README.md +37 -0
  138. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  139. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  140. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  141. package/gsd-core/bin/lib/verification.cjs +258 -8
  142. package/gsd-core/bin/lib/verify.cjs +368 -888
  143. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  144. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  145. package/gsd-core/bin/lib/workstream.cjs +2 -2
  146. package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
  147. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  148. package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
  149. package/gsd-core/references/agent-contracts.md +43 -26
  150. package/gsd-core/references/checkpoints.md +2 -2
  151. package/gsd-core/references/context-budget.md +1 -1
  152. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  153. package/gsd-core/references/doc-conflict-engine.md +1 -1
  154. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  155. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  156. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  157. package/gsd-core/references/execute-phase-response-language.md +1 -1
  158. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  159. package/gsd-core/references/gate-prompts.md +1 -1
  160. package/gsd-core/references/git-planning-commit.md +2 -1
  161. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  162. package/gsd-core/references/model-profiles.md +12 -4
  163. package/gsd-core/references/mvp-concepts.md +9 -9
  164. package/gsd-core/references/planner-guidance.md +3 -9
  165. package/gsd-core/references/planner-preconditions.md +1 -1
  166. package/gsd-core/references/planner-reviews.md +1 -1
  167. package/gsd-core/references/planning-config.md +8 -6
  168. package/gsd-core/references/revision-loop.md +1 -1
  169. package/gsd-core/references/specless-probe-fallback.md +1 -1
  170. package/gsd-core/references/universal-anti-patterns.md +3 -3
  171. package/gsd-core/references/verifier-phase-gates.md +192 -0
  172. package/gsd-core/references/verify-mvp-mode.md +1 -1
  173. package/gsd-core/references/workstream-flag.md +22 -6
  174. package/gsd-core/templates/discussion-log.md +1 -1
  175. package/gsd-core/templates/phase-prompt.md +2 -4
  176. package/gsd-core/templates/state.md +4 -4
  177. package/gsd-core/templates/verification-report.md +9 -1
  178. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  179. package/gsd-core/workflows/autonomous.md +1 -1
  180. package/gsd-core/workflows/cleanup.md +62 -3
  181. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
  182. package/gsd-core/workflows/code-review-fix.md +37 -10
  183. package/gsd-core/workflows/code-review.md +38 -12
  184. package/gsd-core/workflows/complete-milestone.md +141 -18
  185. package/gsd-core/workflows/debug.md +7 -5
  186. package/gsd-core/workflows/diagnose-issues.md +35 -9
  187. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  188. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  189. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
  190. package/gsd-core/workflows/edit-phase.md +26 -1
  191. package/gsd-core/workflows/eval-review.md +3 -5
  192. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
  193. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  194. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
  195. package/gsd-core/workflows/execute-phase.md +38 -50
  196. package/gsd-core/workflows/execute-plan.md +36 -4
  197. package/gsd-core/workflows/explore.md +131 -4
  198. package/gsd-core/workflows/fast.md +10 -2
  199. package/gsd-core/workflows/health.md +73 -4
  200. package/gsd-core/workflows/import.md +4 -4
  201. package/gsd-core/workflows/ingest-docs.md +5 -5
  202. package/gsd-core/workflows/mvp-phase.md +6 -3
  203. package/gsd-core/workflows/new-milestone.md +14 -9
  204. package/gsd-core/workflows/new-project.md +14 -14
  205. package/gsd-core/workflows/next.md +12 -0
  206. package/gsd-core/workflows/plan-phase.md +41 -17
  207. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  208. package/gsd-core/workflows/progress.md +34 -6
  209. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
  210. package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
  211. package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
  212. package/gsd-core/workflows/quick.md +35 -15
  213. package/gsd-core/workflows/review.md +26 -5
  214. package/gsd-core/workflows/secure-phase.md +1 -1
  215. package/gsd-core/workflows/session-report.md +2 -1
  216. package/gsd-core/workflows/settings.md +66 -2
  217. package/gsd-core/workflows/ship.md +104 -44
  218. package/gsd-core/workflows/spec-phase.md +30 -12
  219. package/gsd-core/workflows/sync-skills.md +63 -8
  220. package/gsd-core/workflows/transition.md +46 -11
  221. package/gsd-core/workflows/ui-phase.md +5 -5
  222. package/gsd-core/workflows/ui-review.md +2 -2
  223. package/gsd-core/workflows/update.md +1 -1
  224. package/gsd-core/workflows/validate-phase.md +1 -1
  225. package/gsd-core/workflows/verify-work.md +9 -7
  226. package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
  227. package/hooks/dist/gsd-check-update-worker.js +56 -13
  228. package/hooks/dist/gsd-check-update.js +19 -1
  229. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  230. package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
  231. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  232. package/hooks/dist/gsd-prompt-guard.js +21 -20
  233. package/hooks/dist/gsd-read-injection-scanner.js +38 -24
  234. package/hooks/dist/gsd-statusline.js +18 -0
  235. package/hooks/dist/gsd-update-banner.js +22 -1
  236. package/hooks/dist/gsd-workflow-guard.js +134 -36
  237. package/hooks/dist/lib/git-cmd.js +92 -59
  238. package/hooks/dist/lib/injection-patterns.js +45 -0
  239. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  240. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  241. package/hooks/gsd-agent-isolation-guard.js +103 -14
  242. package/hooks/gsd-check-update-worker.js +56 -13
  243. package/hooks/gsd-check-update.js +19 -1
  244. package/hooks/gsd-cursor-pre-tool.js +0 -3
  245. package/hooks/gsd-cursor-subagent-start.js +77 -2
  246. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  247. package/hooks/gsd-prompt-guard.js +21 -20
  248. package/hooks/gsd-read-injection-scanner.js +38 -24
  249. package/hooks/gsd-statusline.js +18 -0
  250. package/hooks/gsd-update-banner.js +22 -1
  251. package/hooks/gsd-workflow-guard.js +134 -36
  252. package/hooks/lib/git-cmd.js +92 -59
  253. package/hooks/lib/injection-patterns.js +45 -0
  254. package/hooks/lib/isolation-deny-reason.js +39 -0
  255. package/hooks/lib/isolation-sentinel.js +9 -0
  256. package/package.json +21 -9
  257. package/pi/gsd.cjs +19 -5
  258. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  259. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  260. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  261. package/scripts/changeset/lint.cjs +60 -5
  262. package/scripts/check-alias-drift.cjs +7 -43
  263. package/scripts/check-contract-drift.cjs +297 -0
  264. package/scripts/ci-test-scope.cjs +19 -2
  265. package/scripts/command-contract-helpers.cjs +903 -1
  266. package/scripts/gen-adr-index.cjs +728 -38
  267. package/scripts/gen-capability-registry.cjs +3 -15
  268. package/scripts/gen-context-index.cjs +2 -11
  269. package/scripts/gen-health-docs.cjs +390 -0
  270. package/scripts/gen-inventory-manifest.cjs +50 -4
  271. package/scripts/gen-loop-host-contract.cjs +4 -24
  272. package/scripts/gen-registry.cjs +3 -14
  273. package/scripts/lib/alias-drift-families.cjs +46 -0
  274. package/scripts/lib/drift-scan.cjs +278 -0
  275. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  276. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  277. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  278. package/scripts/lint-canary-version-leak.cjs +73 -0
  279. package/scripts/lint-command-contract.cjs +96 -13
  280. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  281. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  282. package/scripts/lint-default-flip-documentation.cjs +193 -0
  283. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  284. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  285. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  286. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  287. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  288. package/scripts/lint-milestone-window-drift.cjs +468 -0
  289. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  290. package/scripts/lint-plan-count-drift.cjs +318 -0
  291. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  292. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  293. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  294. package/scripts/lint-regression-test-names.cjs +15 -13
  295. package/scripts/lint-removed-but-needed.cjs +320 -0
  296. package/scripts/lint-state-field-drift.cjs +805 -0
  297. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  298. package/scripts/lint-test-file-count.allowlist.json +21 -10
  299. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  300. package/scripts/lint-vendored-deps.cjs +124 -0
  301. package/scripts/pr-changed-files.cjs +63 -0
  302. package/scripts/pr-template-policy.cjs +14 -4
  303. package/scripts/prompt-injection-scan.sh +25 -0
  304. package/scripts/require-issue-link-policy.cjs +192 -0
  305. package/scripts/state-write-path-drift-baseline.json +19 -0
  306. package/scripts/sync-runtime-launcher.cjs +2 -4
  307. package/skills/gsd-autonomous/SKILL.md +0 -1
  308. package/skills/gsd-code-review/SKILL.md +1 -1
  309. package/skills/gsd-execute-phase/SKILL.md +1 -2
  310. package/skills/gsd-map-codebase/SKILL.md +1 -1
  311. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  312. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  313. package/skills/gsd-new-milestone/SKILL.md +1 -1
  314. package/skills/gsd-next/SKILL.md +0 -1
  315. package/skills/gsd-plan-phase/SKILL.md +0 -1
  316. package/skills/gsd-progress/SKILL.md +0 -1
  317. package/skills/gsd-quick/SKILL.md +1 -1
  318. package/skills/gsd-review-backlog/SKILL.md +2 -1
  319. package/skills/gsd-stats/SKILL.md +0 -1
  320. package/skills/gsd-verify-work/SKILL.md +1 -1
  321. package/vscode/package.json +1 -1
  322. package/gsd-core/workflows/discovery-phase.md +0 -298
  323. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  324. package/gsd-core/workflows/verify-phase.md +0 -574
  325. package/scripts/affected-tests-lib.cjs +0 -554
  326. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  327. package/scripts/run-affected-tests.cjs +0 -7
  328. package/scripts/run-tests.cjs +0 -1051
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Dependencies (leaf modules only — no loadConfig):
14
14
  * - node:fs / node:path (stdlib)
15
- * - ./phase-id.cjs (normalizePhaseName, phaseTokenMatches, extractPhaseToken)
15
+ * - ./phase-id.cjs (normalizePhaseName, matchPhaseDirs, phaseNumberForMatch)
16
16
  * - ./core-utils.cjs (readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath)
17
17
  * - ./planning-workspace.cjs (planningDir)
18
18
  */
@@ -23,10 +23,10 @@ const node_fs_1 = __importDefault(require("node:fs"));
23
23
  const node_path_1 = __importDefault(require("node:path"));
24
24
  // eslint-disable-next-line @typescript-eslint/no-require-imports
25
25
  const phaseIdModule = require("./phase-id.cjs");
26
- const { normalizePhaseName, phaseTokenMatches, extractPhaseToken } = phaseIdModule;
26
+ const { normalizePhaseName, matchPhaseDirs, phaseNumberForMatch, isSentinelPhaseId, comparePhaseNum } = phaseIdModule;
27
27
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
28
  const coreUtilsModule = require("./core-utils.cjs");
29
- const { readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath } = coreUtilsModule;
29
+ const { readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath, findUnsummarizedPlans } = coreUtilsModule;
30
30
  // eslint-disable-next-line @typescript-eslint/no-require-imports
31
31
  const planningWorkspace = require("./planning-workspace.cjs");
32
32
  const { planningDir } = planningWorkspace;
@@ -36,6 +36,12 @@ const { extractFrontmatter } = frontmatterModule;
36
36
  // eslint-disable-next-line @typescript-eslint/no-require-imports
37
37
  const planDependencyGraphModule = require("./plan-dependency-graph.cjs");
38
38
  const { computeHaltPropagation, buildSummaryFileIndex, isSummaryFileHalted } = planDependencyGraphModule;
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
40
+ const roadmapParserModule = require("./roadmap-parser.cjs");
41
+ const { getMilestonePhaseFilter } = roadmapParserModule;
42
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
43
+ const planningScopeMod = require("./planning-scope.cjs");
44
+ const { SCOPE } = planningScopeMod;
39
45
  /**
40
46
  * #2830: parse a plan file's `depends_on` frontmatter. Returns [] — never
41
47
  * throws — on a missing/unreadable/malformed plan or absent field, matching
@@ -68,9 +74,27 @@ function parsePlanDependsOn(phaseDir, planFile) {
68
74
  * exactly the shape that let the original #2855 bug (hardcoded root path)
69
75
  * exist in one copy and not the other. Sharing this seam means a future
70
76
  * change to how the archive tree is located only needs to happen once.
71
- * Most-recent-milestone-first order (reverse-sorted directory names).
77
+ * Most-recent-milestone-first order, compared numerically segment-by-segment
78
+ * on the version (e.g. `v1.10` before `v1.9`) — NOT lexicographically. A
79
+ * lexicographic `.sort().reverse()` (the prior implementation) ranks `v1.9`
80
+ * ahead of `v1.10` because the string `"1.9"` sorts after `"1.10"`; that is
81
+ * deterministic but wrong for every double-digit-or-higher minor/patch
82
+ * version, and #3458 is what first surfaces archived phases in audit output
83
+ * where the misordering becomes user-visible.
72
84
  * Never throws: an absent/unreadable milestones/ dir yields [].
73
85
  */
86
+ function compareArchiveVersionDesc(aName, bName) {
87
+ const aParts = (aName.match(/^v([\d.]+)-phases$/)?.[1] ?? '').split('.').map(Number);
88
+ const bParts = (bName.match(/^v([\d.]+)-phases$/)?.[1] ?? '').split('.').map(Number);
89
+ const len = Math.max(aParts.length, bParts.length);
90
+ for (let i = 0; i < len; i++) {
91
+ const a = aParts[i] ?? 0;
92
+ const b = bParts[i] ?? 0;
93
+ if (a !== b)
94
+ return b - a; // descending: newest (numerically largest) first
95
+ }
96
+ return 0;
97
+ }
74
98
  function listArchiveVersionDirs(cwd) {
75
99
  const milestonesDir = node_path_1.default.join(planningDir(cwd), 'milestones');
76
100
  if (!node_fs_1.default.existsSync(milestonesDir))
@@ -80,8 +104,7 @@ function listArchiveVersionDirs(cwd) {
80
104
  return milestoneEntries
81
105
  .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
82
106
  .map(e => e.name)
83
- .sort()
84
- .reverse()
107
+ .sort(compareArchiveVersionDesc)
85
108
  .map(archiveName => ({
86
109
  version: archiveName.match(/^(v[\d.]+)-phases$/)[1],
87
110
  archivePath: node_path_1.default.join(milestonesDir, archiveName),
@@ -94,7 +117,10 @@ function listArchiveVersionDirs(cwd) {
94
117
  function searchPhaseInDir(baseDir, relBase, normalized) {
95
118
  try {
96
119
  const dirs = readSubdirectories(baseDir, true);
97
- const matches = dirs.filter(d => phaseTokenMatches(d, normalized));
120
+ // #2528: canonical two-pass selection (exact token match, then the
121
+ // bare-integer leading-digit-run fallback) shared with the find-phase and
122
+ // phase-plan-index scans — see phase-id.cts::matchPhaseDirs.
123
+ const { matches, usedBareFallback } = matchPhaseDirs(dirs, normalized);
98
124
  if (matches.length === 0)
99
125
  return null;
100
126
  // #2237: fail loud when multiple directories match the same bare phase
@@ -121,7 +147,7 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
121
147
  };
122
148
  }
123
149
  const match = matches[0];
124
- const phaseToken = extractPhaseToken(match);
150
+ const phaseToken = phaseNumberForMatch(match, usedBareFallback);
125
151
  const phaseNumber = phaseToken || normalized;
126
152
  const afterToken = match.slice(phaseToken ? phaseToken.length : 0).replace(/^-/, '');
127
153
  const phaseName = afterToken || null;
@@ -129,16 +155,13 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
129
155
  const { plans: unsortedPlans, summaries: unsortedSummaries, hasResearch, hasContext, hasVerification, hasReviews } = getPhaseFileStats(phaseDir);
130
156
  const plans = unsortedPlans.sort();
131
157
  const summaries = unsortedSummaries.sort();
132
- const completedPlanIds = new Set(summaries.flatMap(s => {
133
- const exact = s.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
134
- const canonical = extractCanonicalPlanId(s);
135
- return canonical === exact ? [exact] : [exact, canonical];
136
- }));
137
- const incompletePlans = plans.filter(p => {
138
- const planId = p.replace('-PLAN.md', '').replace('PLAN.md', '');
139
- const canonical = extractCanonicalPlanId(p);
140
- return !completedPlanIds.has(planId) && !completedPlanIds.has(canonical);
141
- });
158
+ // #3183 (ADR-3180 Decision 2): the summary→plan pairing used to be a
159
+ // bespoke rule local to this function (a third pairing rule alongside
160
+ // scanPhasePlans's completion check and countMatchedSummaries). Routed
161
+ // through the canonical core-utils.findUnsummarizedPlans instead, which
162
+ // shares its `summaryCandidates` matching rule with countMatchedSummaries
163
+ // so the count and this named list can never disagree.
164
+ const incompletePlans = findUnsummarizedPlans(plans, summaries);
142
165
  // #2830: reverse lookup from a completed plan's id (exact or canonical) to
143
166
  // its actual summary filename. Shared builder (also used by phase.cts's
144
167
  // cmdPhasePlanIndex) so the two can never disagree about which summary
@@ -233,6 +256,89 @@ function findPhaseInternal(cwd, phase) {
233
256
  }
234
257
  return null;
235
258
  }
259
+ /**
260
+ * #3185 (epic #3180 Phase 3, ADR-3180 Decision 1 row "Phase enumeration"):
261
+ * the SINGLE canonical owner of "which phase directories belong to the current
262
+ * milestone". Applies the milestone window AND the sentinel filter, in that
263
+ * order, and returns the surviving directory names.
264
+ *
265
+ * Before this existed the derivation had four independent implementations and
266
+ * only `cmdRoadmapAnalyze` carried both halves; `cmdProgressRender`,
267
+ * `cmdStats` and `cmdPhasesList` each carried neither or one.
268
+ *
269
+ * TWO THINGS THIS GETS RIGHT THAT A HEADING-SIDE FILTER CANNOT:
270
+ *
271
+ * 1. The sentinel test runs against DIRECTORY NAMES and is UNCONDITIONAL.
272
+ * `getMilestonePhaseFilter` excludes sentinels from its ROADMAP HEADING
273
+ * set, but when that set is empty it degrades to a literal `() => true`
274
+ * pass-all predicate and never consults the heading set at all — so its
275
+ * own sentinel exclusion becomes unreachable exactly when it is needed,
276
+ * and every directory on disk (backlog included) is reported as a
277
+ * current-milestone phase. That degrade is the #3167 symptom path.
278
+ *
279
+ * 2. The sentinel predicate is the canonical `isSentinelPhaseId`
280
+ * (`src/phase-id.cts`, SENTINEL_RANGES [0, 999]), not a local literal.
281
+ * The rule had five copies and three different regexes before this phase,
282
+ * and they disagreed about Phase 0.
283
+ *
284
+ * The pass-all degrade is narrowed MINIMALLY: it stays over-inclusive for
285
+ * non-sentinel directories, so a project whose window declares no phases
286
+ * still sees its real phase directories. Only sentinels are refused.
287
+ *
288
+ * `scope` distinguishes a REAL empty from a NON-answer (ADR-3180 Decision 2):
289
+ * an absent `phasesDir` is a real empty (a new project genuinely has no
290
+ * phases) and inherits the window's scope, whereas a `phasesDir` that exists
291
+ * but cannot be read is UNREADABLE.
292
+ *
293
+ * `opts.ws` is tri-state, matching `planningDir`'s own contract: `undefined`
294
+ * (the default — do not pass `ws` at all) resolves the AMBIENT workstream
295
+ * from `GSD_WORKSTREAM`; `null` FORCES the project root regardless of any
296
+ * ambient workstream; a string forces that specific workstream.
297
+ */
298
+ function listMilestonePhaseDirs(phasesDir, opts = {}) {
299
+ // #3597: `ws` must default to `undefined`, NOT `null`. `undefined` means
300
+ // "resolve the ambient workstream" (mirrors planningDir's own contract,
301
+ // src/planning-workspace.cts:124); `null` means "force the project root".
302
+ // Every cwd-bearing caller derives `phasesDir` ambiently (planningPaths(cwd)
303
+ // / planningDir(cwd) with no explicit ws), so defaulting `ws` to `null` here
304
+ // forced the milestone WINDOW to the root ROADMAP while the caller's
305
+ // `phasesDir` stayed workstream-scoped — numerator and denominator drawn
306
+ // from different scoped sets (ADR-3180 §7.6 rule 3 violation). That is what
307
+ // made `--ws <name> progress` read `phase_scope: "unreadable"` and withhold
308
+ // `percent` once `workstream create` migrated the root ROADMAP away.
309
+ const { cwd, ws, versionOverride = null, phaseIdConvention = null } = opts;
310
+ // Without a cwd there is nothing to scope AGAINST — the caller asked for an
311
+ // unscoped read, which is a real answer (mirrors extractCurrentMilestoneScoped's
312
+ // row 1). Sentinels are still refused: they are never milestone phases.
313
+ let inWindow = () => true;
314
+ let scope = SCOPE.COMPLETE;
315
+ if (cwd) {
316
+ const filter = getMilestonePhaseFilter(cwd, versionOverride, phaseIdConvention, ws);
317
+ inWindow = filter;
318
+ scope = filter.scope;
319
+ }
320
+ // An ABSENT phases dir is a real empty, not a failure: a freshly-created
321
+ // project genuinely has no phase directories yet. Distinguishing this from
322
+ // the unreadable case below is the whole point of the scope discriminator.
323
+ if (!node_fs_1.default.existsSync(phasesDir))
324
+ return { value: [], scope };
325
+ let names;
326
+ try {
327
+ names = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true })
328
+ .filter((e) => e.isDirectory())
329
+ .map((e) => e.name);
330
+ }
331
+ catch {
332
+ // The directory EXISTS but could not be read (EACCES/EIO). An empty list
333
+ // here is a NON-answer and must not be reported as "this milestone has no
334
+ // phases" — that collapse is the defect class this epic removes.
335
+ return { value: [], scope: SCOPE.UNREADABLE };
336
+ }
337
+ const value = names
338
+ .filter((name) => inWindow(name) && !isSentinelPhaseId(name, phaseIdConvention ?? undefined))
339
+ .sort((a, b) => comparePhaseNum(a, b));
340
+ return { value, scope };
341
+ }
236
342
  function getArchivedPhaseDirs(cwd) {
237
343
  // #2855: same workstream-scoped resolution as findPhaseInternal above, via
238
344
  // the shared listArchiveVersionDirs helper. `phase.list --include-archived`
@@ -256,4 +362,5 @@ module.exports = {
256
362
  searchPhaseInDir,
257
363
  findPhaseInternal,
258
364
  getArchivedPhaseDirs,
365
+ listMilestonePhaseDirs,
259
366
  };