@opengsd/gsd-core 1.11.0 → 1.12.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 (395) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +1 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-dom-verifier.md +169 -0
  7. package/agents/gsd-eval-auditor.md +1 -1
  8. package/agents/gsd-executor.md +17 -9
  9. package/agents/gsd-framework-selector.md +1 -3
  10. package/agents/gsd-intel-updater.md +1 -1
  11. package/agents/gsd-mempalace-curator.md +0 -1
  12. package/agents/gsd-pattern-mapper.md +11 -0
  13. package/agents/gsd-phase-researcher.md +3 -1
  14. package/agents/gsd-plan-checker.md +15 -55
  15. package/agents/gsd-planner.md +6 -4
  16. package/agents/gsd-project-researcher.md +1 -1
  17. package/agents/gsd-research-synthesizer.md +2 -2
  18. package/agents/gsd-roadmapper.md +15 -11
  19. package/agents/gsd-ui-checker.md +63 -4
  20. package/agents/gsd-ui-researcher.md +41 -3
  21. package/agents/gsd-verifier.md +1 -1
  22. package/bin/install.js +609 -134
  23. package/commands/gsd/discuss-phase.md +1 -1
  24. package/commands/gsd/import.md +1 -1
  25. package/commands/gsd/quick.md +8 -4
  26. package/gsd-core/bin/gsd-tools.cjs +567 -51
  27. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  28. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  29. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  30. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  31. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  32. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  33. package/gsd-core/bin/lib/audit.cjs +163 -41
  34. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  35. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  36. package/gsd-core/bin/lib/capability-registry.cjs +336 -95
  37. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  38. package/gsd-core/bin/lib/capability-validator.cjs +205 -18
  39. package/gsd-core/bin/lib/check-command-router.cjs +145 -5
  40. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  41. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  42. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  43. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  44. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  45. package/gsd-core/bin/lib/commands.cjs +543 -44
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  47. package/gsd-core/bin/lib/config-loader.cjs +118 -29
  48. package/gsd-core/bin/lib/config.cjs +92 -2
  49. package/gsd-core/bin/lib/configuration.cjs +129 -37
  50. package/gsd-core/bin/lib/core-utils.cjs +84 -7
  51. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  52. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  53. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  54. package/gsd-core/bin/lib/frontmatter.cjs +840 -305
  55. package/gsd-core/bin/lib/gap-checker.cjs +27 -3
  56. package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
  57. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  58. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
  59. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  60. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  61. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  62. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  63. package/gsd-core/bin/lib/init.cjs +120 -41
  64. package/gsd-core/bin/lib/install-engine.cjs +68 -3
  65. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
  66. package/gsd-core/bin/lib/install-profiles.cjs +78 -4
  67. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  68. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  69. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  70. package/gsd-core/bin/lib/intel.cjs +101 -26
  71. package/gsd-core/bin/lib/io.cjs +160 -15
  72. package/gsd-core/bin/lib/learnings.cjs +85 -14
  73. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  74. package/gsd-core/bin/lib/markdown-table.cjs +52 -4
  75. package/gsd-core/bin/lib/milestone.cjs +90 -5
  76. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  77. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  78. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  79. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  80. package/gsd-core/bin/lib/phase-id.cjs +70 -4
  81. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  82. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  83. package/gsd-core/bin/lib/phase.cjs +405 -84
  84. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  85. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  86. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  87. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  88. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
  89. package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
  90. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  91. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  92. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  93. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  94. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  95. package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
  96. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  97. package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
  98. package/gsd-core/bin/lib/roadmap.cjs +74 -19
  99. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
  100. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
  101. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
  102. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  103. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  104. package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
  105. package/gsd-core/bin/lib/smart-entry.cjs +12 -22
  106. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  107. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  108. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  109. package/gsd-core/bin/lib/state-document.cjs +186 -0
  110. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  111. package/gsd-core/bin/lib/state-transition.cjs +517 -101
  112. package/gsd-core/bin/lib/state.cjs +946 -163
  113. package/gsd-core/bin/lib/surface.cjs +10 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  115. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  116. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  117. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  118. package/gsd-core/bin/lib/uat.cjs +1376 -125
  119. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  120. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  121. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  122. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  123. package/gsd-core/bin/lib/vendor/README.md +43 -5
  124. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  125. package/gsd-core/bin/lib/verification.cjs +14 -1
  126. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  127. package/gsd-core/bin/lib/verify.cjs +95 -40
  128. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  129. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  130. package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
  131. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  132. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  133. package/gsd-core/bin/shared/exit-codes.json +8 -0
  134. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  135. package/gsd-core/bin/shared/model-catalog.json +8 -1
  136. package/gsd-core/references/agent-contracts.md +3 -2
  137. package/gsd-core/references/api-coverage.md +24 -2
  138. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  139. package/gsd-core/references/checkpoints.md +37 -19
  140. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  141. package/gsd-core/references/edge-probe.md +8 -0
  142. package/gsd-core/references/execute-mvp-tdd.md +1 -3
  143. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  144. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  145. package/gsd-core/references/failing-direction.md +78 -0
  146. package/gsd-core/references/gate-prompts.md +1 -1
  147. package/gsd-core/references/git-integration.md +5 -5
  148. package/gsd-core/references/git-planning-commit.md +3 -3
  149. package/gsd-core/references/gsd-run-resolver.md +1 -1
  150. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  151. package/gsd-core/references/model-profiles.md +1 -1
  152. package/gsd-core/references/nyquist-compliance.md +74 -0
  153. package/gsd-core/references/offer-next.md +3 -5
  154. package/gsd-core/references/phase-argument-parsing.md +3 -3
  155. package/gsd-core/references/planner-failing-direction.md +53 -0
  156. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  157. package/gsd-core/references/planner-revision.md +1 -1
  158. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  159. package/gsd-core/references/planning-config.md +37 -8
  160. package/gsd-core/references/reviewer-instances.md +31 -0
  161. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  162. package/gsd-core/references/tdd.md +1 -3
  163. package/gsd-core/references/ui-brand.md +65 -21
  164. package/gsd-core/references/ui-consideration-probe.md +1 -1
  165. package/gsd-core/references/universal-anti-patterns.md +2 -2
  166. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  167. package/gsd-core/references/verify-mvp-mode.md +1 -1
  168. package/gsd-core/references/workstream-flag.md +11 -11
  169. package/gsd-core/templates/README.md +1 -1
  170. package/gsd-core/templates/SECURITY.md +3 -3
  171. package/gsd-core/templates/UI-SPEC.md +25 -3
  172. package/gsd-core/templates/VALIDATION.md +3 -3
  173. package/gsd-core/templates/phase-prompt.md +3 -0
  174. package/gsd-core/templates/state.md +7 -0
  175. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  176. package/gsd-core/workflows/add-backlog.md +1 -1
  177. package/gsd-core/workflows/add-phase.md +3 -3
  178. package/gsd-core/workflows/add-tests.md +3 -8
  179. package/gsd-core/workflows/add-todo.md +1 -1
  180. package/gsd-core/workflows/ai-integration-phase.md +4 -9
  181. package/gsd-core/workflows/audit-fix.md +12 -3
  182. package/gsd-core/workflows/audit-milestone.md +9 -9
  183. package/gsd-core/workflows/audit-uat.md +17 -2
  184. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  185. package/gsd-core/workflows/autonomous.md +10 -26
  186. package/gsd-core/workflows/check-todos.md +1 -1
  187. package/gsd-core/workflows/cleanup.md +2 -2
  188. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
  189. package/gsd-core/workflows/code-review-fix.md +1 -1
  190. package/gsd-core/workflows/code-review.md +121 -40
  191. package/gsd-core/workflows/complete-milestone.md +15 -10
  192. package/gsd-core/workflows/debug.md +5 -3
  193. package/gsd-core/workflows/diagnose-issues.md +12 -6
  194. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  195. package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
  196. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  197. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  198. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
  199. package/gsd-core/workflows/discuss-phase.md +1 -1
  200. package/gsd-core/workflows/do.md +3 -6
  201. package/gsd-core/workflows/docs-update.md +5 -4
  202. package/gsd-core/workflows/edit-phase.md +1 -1
  203. package/gsd-core/workflows/eval-review.md +4 -9
  204. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  205. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
  206. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  207. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  208. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  209. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
  210. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  211. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  212. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  213. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  214. package/gsd-core/workflows/execute-phase.md +38 -54
  215. package/gsd-core/workflows/execute-plan.md +17 -12
  216. package/gsd-core/workflows/explore.md +1 -1
  217. package/gsd-core/workflows/extract-learnings.md +1 -1
  218. package/gsd-core/workflows/fast.md +2 -2
  219. package/gsd-core/workflows/forensics.md +1 -1
  220. package/gsd-core/workflows/graduation.md +5 -5
  221. package/gsd-core/workflows/health.md +3 -6
  222. package/gsd-core/workflows/import.md +14 -11
  223. package/gsd-core/workflows/inbox.md +4 -5
  224. package/gsd-core/workflows/ingest-docs.md +44 -11
  225. package/gsd-core/workflows/insert-phase.md +5 -5
  226. package/gsd-core/workflows/list-seeds.md +5 -3
  227. package/gsd-core/workflows/list-workspaces.md +1 -1
  228. package/gsd-core/workflows/manager.md +12 -23
  229. package/gsd-core/workflows/map-codebase.md +1 -1
  230. package/gsd-core/workflows/milestone-summary.md +1 -1
  231. package/gsd-core/workflows/mvp-phase.md +2 -2
  232. package/gsd-core/workflows/new-milestone.md +9 -21
  233. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  234. package/gsd-core/workflows/new-project.md +12 -26
  235. package/gsd-core/workflows/new-workspace.md +1 -1
  236. package/gsd-core/workflows/next.md +2 -2
  237. package/gsd-core/workflows/pause-work.md +1 -1
  238. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  239. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  240. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  241. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  242. package/gsd-core/workflows/plan-phase.md +121 -42
  243. package/gsd-core/workflows/plan-review-convergence.md +46 -9
  244. package/gsd-core/workflows/plant-seed.md +2 -2
  245. package/gsd-core/workflows/pr-branch.md +187 -51
  246. package/gsd-core/workflows/profile-user.md +16 -14
  247. package/gsd-core/workflows/progress.md +27 -12
  248. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  249. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
  250. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  251. package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
  252. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  253. package/gsd-core/workflows/quick.md +20 -29
  254. package/gsd-core/workflows/remove-phase.md +4 -4
  255. package/gsd-core/workflows/remove-workspace.md +2 -2
  256. package/gsd-core/workflows/resume-project.md +8 -12
  257. package/gsd-core/workflows/review.md +193 -15
  258. package/gsd-core/workflows/scan.md +1 -1
  259. package/gsd-core/workflows/secure-phase.md +2 -2
  260. package/gsd-core/workflows/settings-advanced.md +7 -9
  261. package/gsd-core/workflows/settings-integrations.md +64 -31
  262. package/gsd-core/workflows/settings.md +3 -5
  263. package/gsd-core/workflows/ship.md +12 -6
  264. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  265. package/gsd-core/workflows/sketch.md +12 -18
  266. package/gsd-core/workflows/smart-entry.md +3 -5
  267. package/gsd-core/workflows/spec-phase.md +23 -1
  268. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  269. package/gsd-core/workflows/spike.md +20 -31
  270. package/gsd-core/workflows/stats.md +2 -2
  271. package/gsd-core/workflows/sync-skills.md +1 -1
  272. package/gsd-core/workflows/thread.md +11 -7
  273. package/gsd-core/workflows/transition.md +5 -5
  274. package/gsd-core/workflows/ui-phase.md +10 -16
  275. package/gsd-core/workflows/ui-review.md +6 -10
  276. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  277. package/gsd-core/workflows/undo.md +8 -16
  278. package/gsd-core/workflows/update.md +6 -10
  279. package/gsd-core/workflows/validate-phase.md +2 -2
  280. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  281. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  282. package/gsd-core/workflows/verify-work.md +57 -18
  283. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  284. package/hooks/dist/gsd-config-reload.js +18 -12
  285. package/hooks/dist/gsd-context-monitor.js +19 -10
  286. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  287. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  288. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  289. package/hooks/dist/gsd-cursor-stop.js +2 -1
  290. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  291. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  292. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  293. package/hooks/dist/gsd-graphify-update.sh +22 -18
  294. package/hooks/dist/gsd-node-runner.sh +76 -0
  295. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  296. package/hooks/dist/gsd-prompt-guard.js +16 -7
  297. package/hooks/dist/gsd-read-guard.js +16 -7
  298. package/hooks/dist/gsd-read-injection-scanner.js +17 -8
  299. package/hooks/dist/gsd-session-state.sh +1 -0
  300. package/hooks/dist/gsd-statusline.js +215 -26
  301. package/hooks/dist/gsd-validate-commit.sh +80 -6
  302. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  303. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  304. package/hooks/dist/gsd-workflow-guard.js +34 -16
  305. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  306. package/hooks/dist/gsd-write-guard.js +35 -25
  307. package/hooks/dist/lib/cli-exit.js +560 -0
  308. package/hooks/dist/lib/exit-code-registry.js +98 -0
  309. package/hooks/dist/lib/git-probe.js +84 -0
  310. package/hooks/dist/lib/hook-exit.js +81 -0
  311. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  312. package/hooks/gsd-agent-isolation-guard.js +77 -38
  313. package/hooks/gsd-config-reload.js +18 -12
  314. package/hooks/gsd-context-monitor.js +19 -10
  315. package/hooks/gsd-cursor-post-tool.js +3 -1
  316. package/hooks/gsd-cursor-pre-tool.js +3 -1
  317. package/hooks/gsd-cursor-session-start.js +2 -1
  318. package/hooks/gsd-cursor-stop.js +2 -1
  319. package/hooks/gsd-cursor-subagent-start.js +28 -23
  320. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  321. package/hooks/gsd-ensure-canonical-path.js +2 -1
  322. package/hooks/gsd-graphify-update.sh +22 -18
  323. package/hooks/gsd-node-runner.sh +76 -0
  324. package/hooks/gsd-phase-boundary.sh +1 -0
  325. package/hooks/gsd-prompt-guard.js +16 -7
  326. package/hooks/gsd-read-guard.js +16 -7
  327. package/hooks/gsd-read-injection-scanner.js +17 -8
  328. package/hooks/gsd-session-state.sh +1 -0
  329. package/hooks/gsd-statusline.js +215 -26
  330. package/hooks/gsd-validate-commit.sh +80 -6
  331. package/hooks/gsd-windsurf-pre-command.js +16 -11
  332. package/hooks/gsd-windsurf-pre-write.js +22 -13
  333. package/hooks/gsd-workflow-guard.js +34 -16
  334. package/hooks/gsd-worktree-path-guard.js +36 -21
  335. package/hooks/gsd-write-guard.js +35 -25
  336. package/hooks/lib/cli-exit.js +560 -0
  337. package/hooks/lib/exit-code-registry.js +98 -0
  338. package/hooks/lib/git-probe.js +84 -0
  339. package/hooks/lib/hook-exit.js +81 -0
  340. package/hooks/managed-hooks-registry.cjs +3 -0
  341. package/package.json +12 -7
  342. package/scripts/base64-scan.sh +74 -12
  343. package/scripts/build-hooks.js +5 -0
  344. package/scripts/check-glossary-refs.cjs +77 -15
  345. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  346. package/scripts/ci-check-job-near-cap.cjs +49 -0
  347. package/scripts/ci-pr-mergeability.cjs +262 -0
  348. package/scripts/ci-test-scope.cjs +45 -12
  349. package/scripts/ci-timeout-report.cjs +230 -0
  350. package/scripts/docs-guard-registry.cjs +396 -0
  351. package/scripts/gen-capability-registry.cjs +8 -6
  352. package/scripts/gen-exit-code-docs.cjs +318 -0
  353. package/scripts/gen-exit-code-registry.cjs +891 -0
  354. package/scripts/gen-features.cjs +836 -0
  355. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  356. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  357. package/scripts/gen-loop-host-contract.cjs +134 -1
  358. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  359. package/scripts/gen-state-md-docs.cjs +727 -0
  360. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  361. package/scripts/lib/ci-job-timing.cjs +72 -0
  362. package/scripts/lib/cli-exit.cjs +546 -44
  363. package/scripts/lib/drift-scan.cjs +32 -2
  364. package/scripts/lib/exit-code-registry.cjs +98 -0
  365. package/scripts/lib/ndjson-reporter.cjs +119 -0
  366. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  367. package/scripts/lint-docs-guard-registration.cjs +495 -0
  368. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  369. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  370. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  371. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  372. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  373. package/scripts/lint-phase-enumeration-drift.cjs +21 -8
  374. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  375. package/scripts/lint-removed-but-needed.cjs +184 -16
  376. package/scripts/lint-seam-enforcement.cjs +182 -0
  377. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  378. package/scripts/lint-source-test-name-collision.cjs +241 -0
  379. package/scripts/lint-state-write-path-drift.cjs +337 -432
  380. package/scripts/lint-test-file-count.allowlist.json +122 -4
  381. package/scripts/lint-test-file-count.cjs +25 -3
  382. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  383. package/scripts/lint-vendored-deps.cjs +208 -35
  384. package/scripts/mutation-matrix.cjs +599 -50
  385. package/scripts/prompt-injection-scan.sh +75 -14
  386. package/scripts/secret-scan.sh +75 -13
  387. package/scripts/select-docs-guards.cjs +56 -0
  388. package/scripts/sync-runtime-launcher.cjs +22 -3
  389. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  390. package/skills/gsd-import/SKILL.md +1 -1
  391. package/skills/gsd-quick/SKILL.md +8 -4
  392. package/vscode/package.json +1 -1
  393. package/bin/lib/ui-safety-gate.cjs +0 -109
  394. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  395. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -11,6 +11,17 @@
11
11
  * Dependencies:
12
12
  * - ./pattern.cjs (escapeRegex — #3212 Phase 1 seam; this module is no
13
13
  * longer the owner of pattern-escaping, only a consumer)
14
+ * - ./core-utils.cjs (generateSlugInternal — #3883/ADR-3473 §8.3: the
15
+ * canonical slug formula). core-utils.cjs also requires THIS module
16
+ * (comparePhaseNum, scopeToPhase), so a top-level require here would be
17
+ * circular and — per this codebase's compiled-.cjs convention of a
18
+ * single `module.exports = {...}` reassignment at the bottom of each
19
+ * file — a top-level circular require captures a stale, still-empty
20
+ * exports object forever (verified live: it throws
21
+ * "generateSlugInternal is not a function" when core-utils.cjs happens
22
+ * to load first). The require is deferred (lazy, inside each function
23
+ * body) instead, mirroring the same cycle-break already used by
24
+ * core-utils.cts's own getPhaseFileStats/plan-scan.cjs seam.
14
25
  */
15
26
  const pattern_cjs_1 = require("./pattern.cjs");
16
27
  // ─── Phase-id helpers ─────────────────────────────────────────────────────────
@@ -214,9 +225,15 @@ function getPhaseDirFromPhaseId(phaseId, phaseName, projectCode) {
214
225
  const milestone = String(parseInt(m[1], 10)).padStart(2, '0');
215
226
  const subParts = m[2].split('-').map(p => String(parseInt(p, 10)).padStart(2, '0'));
216
227
  const sub = subParts.join('-');
217
- const slug = phaseName
218
- ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
219
- : '';
228
+ // #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
229
+ // (generateSlugInternal, core-utils.cts) rather than re-implementing it.
230
+ // `maxLen: null` preserves this site's pre-migration untruncated contract —
231
+ // the 60-char default would silently shadow one on-disk phase dir's
232
+ // reported phase_slug behind another distinct >60-char phase name's.
233
+ // Lazy require to break the core-utils.cjs <-> phase-id.cjs cycle (see the
234
+ // module dependency doc comment above).
235
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
236
+ const slug = phaseName ? (require('./core-utils.cjs').generateSlugInternal(phaseName, null) ?? '') : '';
220
237
  const parts = [milestone, sub, slug].filter(Boolean);
221
238
  const base = parts.join('-');
222
239
  return projectCode ? `${projectCode}-${base}` : base;
@@ -324,7 +341,22 @@ function toDir(id, slug) {
324
341
  const sub = id.subphase ? `.${id.subphase}` : '';
325
342
  // Slug guard: the slug becomes an on-disk path segment, so collapse it to a
326
343
  // safe lowercase token — never a path separator or `..` traversal.
327
- const safeSlug = slug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
344
+ // #3883 (ADR-3473 §8.3): delegate the sanitize formula itself to the
345
+ // canonical (generateSlugInternal, core-utils.cts) — this fixes the
346
+ // Cyrillic-collapses-to-empty defect (#2848-class) that toDir carried
347
+ // before (it never transliterated). The empty-sanitize and all-digit
348
+ // throw guards below stay: they are a DECLARED DIFFERENCE from every
349
+ // other slug call site, not a bug — a slug here becomes a real directory
350
+ // name, and toDir protects the parsePhaseId dir↔identity bijection
351
+ // (see the toDir docstring above) by refusing to emit an unusable name,
352
+ // where every other site silently accepts "" or a re-truncated value.
353
+ // `maxLen: null` preserves toDir's pre-migration untruncated contract — the
354
+ // 60-char default let two distinct >60-char phase names collapse onto the
355
+ // identical directory name, one silently shadowing the other on disk.
356
+ // Lazy require to break the core-utils.cjs <-> phase-id.cjs cycle (see the
357
+ // module dependency doc comment above).
358
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
359
+ const safeSlug = require('./core-utils.cjs').generateSlugInternal(slug, null) ?? '';
328
360
  // A slug that sanitizes to nothing (e.g. '!!!') would otherwise emit a
329
361
  // dangling trailing hyphen.
330
362
  if (!safeSlug) {
@@ -360,6 +392,39 @@ function isSentinelPhaseId(phaseId, convention) {
360
392
  return false;
361
393
  return SENTINEL_RANGES.includes(parseInt(legacy[1], 10));
362
394
  }
395
+ /**
396
+ * Disk-side sentinel recognizer (#3639): is this on-disk PHASE DIRECTORY a
397
+ * sentinel (never-on-roadmap by convention)?
398
+ *
399
+ * The disk-side guards (C001 gap numbering, W007 orphan dirs) see raw
400
+ * directory names and do not know the repo's naming convention — and neither
401
+ * convention-blind route could recognize a bracket sentinel: `isSentinelPhaseId`
402
+ * without the convention argument reads only the legacy leading int, while
403
+ * `extractPhaseToken(dirName)` (convention-aware or not) strips the MILESTONE
404
+ * and returns the bare phase token — bracket sentinel-ness lives in the
405
+ * milestone portion (`GSD.999-07-icebox` is icebox because of the 999, not
406
+ * the 07). This helper reads the milestone directly off the dir name.
407
+ *
408
+ * The bracket branch requires the FULL bracket dir shape — code prefix, dot,
409
+ * milestone digits, hyphen, PHASE DIGITS — so a #1324 letter-prefixed real
410
+ * dir with a LETTER slug (`P0.0-foundation`) never matches it (ADR-2121
411
+ * indistinguishability, same gate as extractPhaseToken below). DISCLOSED
412
+ * RESIDUAL (#3639 review): the #1324 family also has digit continuations
413
+ * (`P0.0-1-foundation`, `P0.3-2` are real shapes per derivePhaseTokenSegments),
414
+ * and `{code}.{0|999}-{digit}...` is string-indistinguishable from a bracket
415
+ * sentinel dir — no convention-free discriminator exists (ADR-2121). Such a
416
+ * dir reads as sentinel here, which at the disk-guard call sites suppresses
417
+ * a warning (conservative for a linter) rather than deleting data. The
418
+ * digit-continuation family with NON-sentinel first decimals (`P0.3-2`)
419
+ * reads milestone 3 — ordinary — exactly as the convention-gated id
420
+ * predicate does. Everything else falls to the legacy leading-int rule.
421
+ */
422
+ function isSentinelPhaseDir(dirName) {
423
+ const bracketDir = dirName.match(/^[A-Z][A-Z0-9_]*\.(\d+)-\d/); // milestone digits + hyphen + phase DIGITS
424
+ if (bracketDir)
425
+ return SENTINEL_RANGES.includes(parseInt(bracketDir[1], 10));
426
+ return isSentinelPhaseId(dirName);
427
+ }
363
428
  /**
364
429
  * Render a regex source fragment matching a phase number against ROADMAP/STATE
365
430
  * prose regardless of zero-padding on either side.
@@ -1120,6 +1185,7 @@ module.exports = {
1120
1185
  toDir,
1121
1186
  SENTINEL_RANGES,
1122
1187
  isSentinelPhaseId,
1188
+ isSentinelPhaseDir,
1123
1189
  phaseMarkdownRegexSource,
1124
1190
  phaseMarkdownRegexSourceExact,
1125
1191
  comparePhaseNum,
@@ -21,6 +21,7 @@
21
21
  * - Issue #4 (open-gsd/gsd-core)
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.locateProgressTable = locateProgressTable;
24
25
  exports.deriveProgressFromRoadmap = deriveProgressFromRoadmap;
25
26
  exports.clampPercentFromFraction = clampPercentFromFraction;
26
27
  exports.clampPercent = clampPercent;
@@ -29,8 +30,10 @@ const markdown_table_cjs_1 = require("./markdown-table.cjs");
29
30
  const phaseIdMod = require("./phase-id.cjs");
30
31
  const { isSentinelPhaseId } = phaseIdMod;
31
32
  /**
32
- * Derive completed_phases, total_phases, and total_plans from ROADMAP content.
33
- * Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
33
+ * #3227: the single owner of "where is this ROADMAP's Progress table".
34
+ * Lifted verbatim out of deriveProgressFromRoadmap so `state-contract.cts`
35
+ * enumerates phases from THE SAME table this module derives its counts from.
36
+ * A second copy of this locator is the DEFECT.GENERATIVE-FIX shape.
34
37
  *
35
38
  * ADR-2143 §3 ("addressed by NAME, never ordinal"): the Progress table is
36
39
  * located via the markdown-table seam's `findTableWithColumns`, which is
@@ -49,6 +52,24 @@ const { isSentinelPhaseId } = phaseIdMod;
49
52
  * to scanning the whole input, preserving the "Progress table not under a
50
53
  * `## Progress` heading, or not the first table in the document, still
51
54
  * resolves" behaviour.
55
+ */
56
+ function locateProgressTable(roadmapContent) {
57
+ const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
58
+ let scoped = roadmapContent;
59
+ if (progressMatch && progressMatch.index !== undefined) {
60
+ const afterHeading = roadmapContent.slice(progressMatch.index);
61
+ const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
62
+ scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
63
+ }
64
+ return (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
65
+ }
66
+ /**
67
+ * Derive completed_phases, total_phases, and total_plans from ROADMAP content.
68
+ * Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
69
+ *
70
+ * The Progress table itself is located by `locateProgressTable` (ADR-2143 §3,
71
+ * lifted out as #3227's single-owner extraction) — this function consumes
72
+ * that table.
52
73
  *
53
74
  * Cells are read by column NAME (`r['Status']`, `r['Plans Complete']`,
54
75
  * `r['Phase']`), fixing #2137 (the old position-based regex assumed "Status"
@@ -67,20 +88,7 @@ function deriveProgressFromRoadmap(roadmapContent) {
67
88
  // `{ ok: false, reason }`, not an exception — so the catch was masking
68
89
  // nothing but dead code paths. Removed per ADR-2143 §5; the public
69
90
  // `RoadmapProgress` contract (nulls = absent) is unchanged.
70
- //
71
- // ADR-2143 §3: read the Progress table by column NAME (order/injection-invariant),
72
- // via the markdown-table seam. Scope to the `## Progress` section when present
73
- // (#2012 decoy avoidance); a headingless milestone slice (#1445) falls back to the
74
- // whole input. Requires the canonical Phase/Plans Complete/Status/Completed columns
75
- // in any order (extra columns ignored) — supersedes findTableBySchema's exact-schema lookup.
76
- const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
77
- let scoped = roadmapContent;
78
- if (progressMatch && progressMatch.index !== undefined) {
79
- const afterHeading = roadmapContent.slice(progressMatch.index);
80
- const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
81
- scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
82
- }
83
- const table = (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
91
+ const table = locateProgressTable(roadmapContent);
84
92
  if (table) {
85
93
  const allRows = table.rows;
86
94
  const completed = allRows.filter((r) => /^complete$/i.test((r['Status'] ?? '').trim())).length;
@@ -29,7 +29,7 @@ const coreUtilsModule = require("./core-utils.cjs");
29
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
- const { planningDir } = planningWorkspace;
32
+ const { planningDir, planningRoot } = planningWorkspace;
33
33
  // eslint-disable-next-line @typescript-eslint/no-require-imports
34
34
  const frontmatterModule = require("./frontmatter.cjs");
35
35
  const { extractFrontmatter } = frontmatterModule;
@@ -95,24 +95,56 @@ function compareArchiveVersionDesc(aName, bName) {
95
95
  }
96
96
  return 0;
97
97
  }
98
- function listArchiveVersionDirs(cwd) {
99
- const milestonesDir = node_path_1.default.join(planningDir(cwd), 'milestones');
100
- if (!node_fs_1.default.existsSync(milestonesDir))
101
- return [];
98
+ function listArchiveVersionDirs(cwd, wsOverride) {
99
+ // #3804: enumerate BOTH archive shapes under the CURRENT SCOPE's milestones
100
+ // tree (planningDir — GSD_WORKSTREAM/GSD_PROJECT-aware, exactly the
101
+ // #2855 scoping findPhaseInternal and getArchivedPhaseDirs rely on):
102
+ // flat: <scope>/milestones/vX.Y-phases/<phase-dir>/
103
+ // workstream archive: <scope>/milestones/ws-<slug>-<date>/phases/<phase-dir>/
104
+ // Pre-#3804 only the flat shape matched, so workstream-archived milestones
105
+ // were invisible (the reporter's repo: 20 hidden phase artifacts). The
106
+ // ws-* shape's phase dirs sit one level deeper (under phases/) and its dir
107
+ // name fails ^v[\d.]+-phases$ — both the name AND the level are modeled.
108
+ // Version labels: flat keeps the bare vX.Y; ws-* shapes carry the dir name
109
+ // (no numeric version). Flat-newest-first (compareArchiveVersionDesc), then
110
+ // ws dirs by name descending — deterministic. The AUDIT's cross-workstream
111
+ // enumeration (audit.cts listAuditPhaseTargets) calls this helper once per
112
+ // tree (root + each workstream) rather than widening this scope — the
113
+ // #2855 no-leak contract for findPhaseInternal/getArchivedPhaseDirs is
114
+ // preserved untouched.
115
+ const milestonesDir = node_path_1.default.join(planningDir(cwd, wsOverride ?? undefined), 'milestones');
116
+ const out = [];
117
+ const seen = new Set();
118
+ const pushVersion = (version, archivePath) => {
119
+ const rel = toPosixPath(node_path_1.default.relative(cwd, archivePath));
120
+ if (seen.has(rel))
121
+ return;
122
+ seen.add(rel);
123
+ out.push({ version, archivePath });
124
+ };
125
+ let entries;
102
126
  try {
103
- const milestoneEntries = node_fs_1.default.readdirSync(milestonesDir, { withFileTypes: true });
104
- return milestoneEntries
105
- .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
106
- .map(e => e.name)
107
- .sort(compareArchiveVersionDesc)
108
- .map(archiveName => ({
109
- version: archiveName.match(/^(v[\d.]+)-phases$/)[1],
110
- archivePath: node_path_1.default.join(milestonesDir, archiveName),
111
- }));
127
+ entries = node_fs_1.default.readdirSync(milestonesDir, { withFileTypes: true });
112
128
  }
113
129
  catch {
114
130
  return [];
115
131
  }
132
+ const flat = entries
133
+ .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
134
+ .map(e => e.name)
135
+ .sort(compareArchiveVersionDesc);
136
+ for (const archiveName of flat) {
137
+ pushVersion(archiveName.match(/^(v[\d.]+)-phases$/)[1], node_path_1.default.join(milestonesDir, archiveName));
138
+ }
139
+ const wsDirs = entries
140
+ .filter(e => e.isDirectory() && /^ws-/.test(e.name))
141
+ .map(e => e.name)
142
+ .sort()
143
+ .reverse();
144
+ for (const wsName of wsDirs) {
145
+ pushVersion(wsName, node_path_1.default.join(milestonesDir, wsName, 'phases'));
146
+ }
147
+ return out;
116
148
  }
117
149
  function searchPhaseInDir(baseDir, relBase, normalized) {
118
150
  try {
@@ -211,7 +243,12 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
211
243
  directory: toPosixPath(node_path_1.default.join(relBase, match)),
212
244
  phase_number: phaseNumber,
213
245
  phase_name: phaseName,
214
- phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null,
246
+ // #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
247
+ // (generateSlugInternal, core-utils.cts) rather than re-implementing
248
+ // it. `maxLen: null` preserves this site's pre-migration untruncated
249
+ // contract — the 60-char default would drop an on-disk phase slug's
250
+ // reported value out of sync with the real directory name.
251
+ phase_slug: phaseName ? coreUtilsModule.generateSlugInternal(phaseName, null) : null,
215
252
  plans,
216
253
  summaries,
217
254
  incomplete_plans: incompletePlans,
@@ -339,13 +376,56 @@ function listMilestonePhaseDirs(phasesDir, opts = {}) {
339
376
  .sort((a, b) => comparePhaseNum(a, b));
340
377
  return { value, scope };
341
378
  }
342
- function getArchivedPhaseDirs(cwd) {
379
+ /**
380
+ * #3882 (ADR-3473 §8.2, issue #3882): the single owner of "the PHYSICAL set
381
+ * of phase directories on disk, entirely un-windowed" — the OTHER axis
382
+ * `listMilestonePhaseDirs` above deliberately does not offer. That owner
383
+ * refuses sentinels UNCONDITIONALLY (see its own doc comment); it has no way
384
+ * to say "physical set, sentinels included". That is exactly what an
385
+ * archival, lookup-index, or health-sweep caller needs — e.g. a heading ->
386
+ * directory lookup index that must resolve a directory regardless of
387
+ * milestone window (`cmdRoadmapAnalyze`'s `_phaseDirNames`,
388
+ * `cmdInitMilestoneOp`'s `diskPhaseDirs`) — which is why those callers used
389
+ * to hand-roll a `readdirSync` instead of calling either owner.
390
+ *
391
+ * `includeSentinels` is REQUIRED, with no default value. #3882/ADR-3473 §8.2:
392
+ * "a caller that wants sentinels asks for them explicitly" — obtaining
393
+ * sentinel-inclusion by silent omission is exactly the defect class this
394
+ * axis exists to close, so the call site is refused at COMPILE TIME without
395
+ * it, not merely documented against it here.
396
+ *
397
+ * Mirrors `listMilestonePhaseDirs`'s own absent/unreadable handling
398
+ * (ADR-3180 Decision 2): an ABSENT `phasesDir` is a real empty (a project
399
+ * with no phase directories yet), `scope: SCOPE.COMPLETE`; a `phasesDir`
400
+ * that EXISTS but cannot be read is a NON-answer, `scope: SCOPE.UNREADABLE`
401
+ * — a caller must not treat that empty list as "this project has no
402
+ * phases."
403
+ */
404
+ function listAllPhaseDirs(phasesDir, opts) {
405
+ const { includeSentinels, phaseIdConvention = null } = opts;
406
+ if (!node_fs_1.default.existsSync(phasesDir))
407
+ return { value: [], scope: SCOPE.COMPLETE };
408
+ let names;
409
+ try {
410
+ names = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true })
411
+ .filter((e) => e.isDirectory())
412
+ .map((e) => e.name);
413
+ }
414
+ catch {
415
+ return { value: [], scope: SCOPE.UNREADABLE };
416
+ }
417
+ const value = names
418
+ .filter((name) => includeSentinels || !isSentinelPhaseId(name, phaseIdConvention ?? undefined))
419
+ .sort((a, b) => comparePhaseNum(a, b));
420
+ return { value, scope: SCOPE.COMPLETE };
421
+ }
422
+ function getArchivedPhaseDirs(cwd, wsOverride) {
343
423
  // #2855: same workstream-scoped resolution as findPhaseInternal above, via
344
424
  // the shared listArchiveVersionDirs helper. `phase.list --include-archived`
345
425
  // (the primary non-init consumer) must not leak a different workstream's
346
426
  // archive either.
347
427
  const results = [];
348
- for (const { version, archivePath } of listArchiveVersionDirs(cwd)) {
428
+ for (const { version, archivePath } of listArchiveVersionDirs(cwd, wsOverride)) {
349
429
  const dirs = readSubdirectories(archivePath, true);
350
430
  for (const dir of dirs) {
351
431
  results.push({
@@ -358,9 +438,50 @@ function getArchivedPhaseDirs(cwd) {
358
438
  }
359
439
  return results;
360
440
  }
441
+ /**
442
+ * #3804 — the CROSS-WORKSTREAM archive enumeration the audit surfaces need.
443
+ * getArchivedPhaseDirs above is deliberately #2855-SCOPED (an ambient
444
+ * workstream must not leak other trees' phases to findPhaseInternal), but
445
+ * audit-uat's charter (#2766: outstanding items do not stop mattering) is
446
+ * cross-workstream: enumerate the project root plus every workstream's own
447
+ * milestones tree, labeling workstream entries '<ws>/<version>' so
448
+ * acknowledge-by-label stays unambiguous. Deduped by full path (the same
449
+ * tree cannot be reached twice, but the guard keeps the invariant explicit).
450
+ */
451
+ function getAllArchivedPhaseDirs(cwd) {
452
+ const out = [];
453
+ const seen = new Set();
454
+ const collect = (labelPrefix, wsOverride) => {
455
+ for (const archived of getArchivedPhaseDirs(cwd, wsOverride)) {
456
+ const rel = toPosixPath(node_path_1.default.relative(cwd, archived.fullPath));
457
+ if (seen.has(rel))
458
+ continue;
459
+ seen.add(rel);
460
+ out.push({ ...archived, milestone: `${labelPrefix}${archived.milestone}` });
461
+ }
462
+ };
463
+ collect('', null);
464
+ const workstreamsDir = node_path_1.default.join(planningRoot(cwd), 'workstreams');
465
+ try {
466
+ const wsEntries = node_fs_1.default.readdirSync(workstreamsDir, { withFileTypes: true })
467
+ .filter((e) => e.isDirectory())
468
+ .map((e) => e.name)
469
+ .sort()
470
+ .reverse();
471
+ for (const ws of wsEntries) {
472
+ collect(`${ws}/`, ws);
473
+ }
474
+ }
475
+ catch {
476
+ /* no workstreams dir — the root pass above already ran */
477
+ }
478
+ return out;
479
+ }
361
480
  module.exports = {
362
481
  searchPhaseInDir,
363
482
  findPhaseInternal,
364
483
  getArchivedPhaseDirs,
484
+ getAllArchivedPhaseDirs,
365
485
  listMilestonePhaseDirs,
486
+ listAllPhaseDirs,
366
487
  };