@opengsd/gsd-core 1.12.0 → 1.14.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 (455) 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 +12 -0
  4. package/agents/gsd-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -80,6 +80,10 @@ function inferPreferredRuntime({ fs, env, preferredConfigDir }) {
80
80
  return 'opencode';
81
81
  if (fs.exists(node_path_1.default.join(preferredConfigDir, exports.CODEX_CONFIG_MARKER)))
82
82
  return 'codex';
83
+ const resolved = node_path_1.default.resolve(preferredConfigDir);
84
+ const known = exports.RUNTIME_DIRS.find(([, reldir]) => resolved.endsWith(node_path_1.default.sep + reldir.split('/').join(node_path_1.default.sep)));
85
+ if (known)
86
+ return known[0];
83
87
  }
84
88
  if (env['CODEX_HOME'])
85
89
  return 'codex';
@@ -91,7 +95,7 @@ function inferPreferredRuntime({ fs, env, preferredConfigDir }) {
91
95
  return 'opencode';
92
96
  if (env['CLAUDE_CONFIG_DIR'])
93
97
  return 'claude';
94
- return 'claude';
98
+ return '';
95
99
  }
96
100
  // Absolute env-override candidates, mirroring the bash ENV_RUNTIME_DIRS block.
97
101
  function envRuntimeDirs({ env, home }) {
@@ -123,6 +127,23 @@ function preferFirst(entries, preferred) {
123
127
  const rest = entries.filter(([rt]) => rt !== preferred);
124
128
  return [...pref, ...rest];
125
129
  }
130
+ // GLOBAL probe: absolute env candidates first (in preferFirst order, first
131
+ // hasInstall hit wins), then $HOME-relative. Single resolver shared by the
132
+ // preferredConfigDir fast path's same-path dedup and the full cascade (#4197),
133
+ // so both compare against the global dir the resolution would actually select —
134
+ // an env-directed candidate, not necessarily the $HOME-relative pathname.
135
+ function resolveGlobalCandidate(fs, env, home, preferred) {
136
+ for (const [rt, absdir] of preferFirst(envRuntimeDirs({ env, home }), preferred)) {
137
+ if (hasInstall(fs, absdir))
138
+ return { runtime: rt, dir: node_path_1.default.resolve(absdir) };
139
+ }
140
+ for (const [rt, reldir] of preferFirst(exports.RUNTIME_DIRS, preferred)) {
141
+ const cand = node_path_1.default.resolve(home, reldir);
142
+ if (hasInstall(fs, cand))
143
+ return { runtime: rt, dir: cand };
144
+ }
145
+ return { runtime: '', dir: '' };
146
+ }
126
147
  /**
127
148
  * Pure resolver. Returns { installedVersion, scope, runtime, gsdDir }.
128
149
  */
@@ -133,11 +154,17 @@ function resolveUpdateContext({ home, cwd, env = {}, fs, preferredConfigDir = ''
133
154
  // Fast path: a validated preferredConfigDir (custom --config-dir install).
134
155
  if (preferredConfigDir && hasInstall(fs, preferredConfigDir)) {
135
156
  const resolvedPref = node_path_1.default.resolve(preferredConfigDir);
157
+ // Same-path dedup the cascade applies (#4197): a preferred dir that IS the
158
+ // selected global install (an env candidate or the $HOME-relative dir) is
159
+ // GLOBAL even when cwd === $HOME also makes it the cwd-relative match.
160
+ const { dir: globalDir } = resolveGlobalCandidate(fs, env, home, preferred);
136
161
  let scope = 'GLOBAL';
137
- for (const [, reldir] of exports.RUNTIME_DIRS) {
138
- if (node_path_1.default.resolve(cwd, reldir) === resolvedPref) {
139
- scope = 'LOCAL';
140
- break;
162
+ if (resolvedPref !== globalDir) {
163
+ for (const [, reldir] of exports.RUNTIME_DIRS) {
164
+ if (node_path_1.default.resolve(cwd, reldir) === resolvedPref) {
165
+ scope = 'LOCAL';
166
+ break;
167
+ }
141
168
  }
142
169
  }
143
170
  return {
@@ -147,7 +174,6 @@ function resolveUpdateContext({ home, cwd, env = {}, fs, preferredConfigDir = ''
147
174
  gsdDir: preferredConfigDir,
148
175
  };
149
176
  }
150
- const orderedEnv = preferFirst(envRuntimeDirs({ env, home }), preferred);
151
177
  const orderedRuntime = preferFirst(exports.RUNTIME_DIRS, preferred);
152
178
  // LOCAL probe (relative to cwd).
153
179
  let localRuntime = '', localDir = '';
@@ -159,25 +185,9 @@ function resolveUpdateContext({ home, cwd, env = {}, fs, preferredConfigDir = ''
159
185
  break;
160
186
  }
161
187
  }
162
- // GLOBAL probe: absolute env candidates first, then $HOME-relative.
163
- let globalRuntime = '', globalDir = '';
164
- for (const [rt, absdir] of orderedEnv) {
165
- if (hasInstall(fs, absdir)) {
166
- globalRuntime = rt;
167
- globalDir = node_path_1.default.resolve(absdir);
168
- break;
169
- }
170
- }
171
- if (!globalRuntime) {
172
- for (const [rt, reldir] of orderedRuntime) {
173
- const cand = node_path_1.default.resolve(home, reldir);
174
- if (hasInstall(fs, cand)) {
175
- globalRuntime = rt;
176
- globalDir = cand;
177
- break;
178
- }
179
- }
180
- }
188
+ // GLOBAL probe: absolute env candidates first, then $HOME-relative — the
189
+ // same resolver the fast path dedups against.
190
+ const { runtime: globalRuntime, dir: globalDir } = resolveGlobalCandidate(fs, env, home, preferred);
181
191
  const localValid = trustedVersionAt(fs, localDir);
182
192
  const isLocal = !!localValid && (!globalDir || localDir !== globalDir);
183
193
  if (isLocal) {
@@ -196,7 +206,7 @@ function resolveUpdateContext({ home, cwd, env = {}, fs, preferredConfigDir = ''
196
206
  if (globalRuntime) {
197
207
  return { installedVersion: '0.0.0', scope: 'GLOBAL', runtime: globalRuntime, gsdDir: globalDir };
198
208
  }
199
- return { installedVersion: '0.0.0', scope: 'UNKNOWN', runtime: 'claude', gsdDir: '' };
209
+ return { installedVersion: '0.0.0', scope: 'UNKNOWN', runtime: '', gsdDir: '' };
200
210
  }
201
211
  /**
202
212
  * CLI wiring: resolve against the real filesystem.
@@ -32,11 +32,14 @@
32
32
  * - PR #156 (issue #6) — validate.ts generator that #26 extends
33
33
  */
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.MILESTONE_ARCHIVE_DIR_RE = exports.PHASE_TOKEN_FROM_DIR_RE = exports.phaseDirNameRe = void 0;
35
+ exports.BRACKET_PHASE_DIR_RE = exports.MILESTONE_ARCHIVE_DIR_RE = exports.PHASE_TOKEN_FROM_DIR_RE = exports.phaseDirNameRe = void 0;
36
+ exports.isPhaseDirName = isPhaseDirName;
37
+ exports.phaseTokenFromDir = phaseTokenFromDir;
36
38
  exports.canonicalPlanStem = canonicalPlanStem;
37
39
  exports.phaseVariants = phaseVariants;
38
40
  exports.buildRoadmapPhaseVariants = buildRoadmapPhaseVariants;
39
41
  exports.buildNotStartedPhaseVariants = buildNotStartedPhaseVariants;
42
+ exports.checkBracketCoherence = checkBracketCoherence;
40
43
  exports.textEncodingError = textEncodingError;
41
44
  // eslint-disable-next-line @typescript-eslint/no-require-imports
42
45
  const phaseIdMod = require("./phase-id.cjs");
@@ -45,7 +48,17 @@ const { OPTIONAL_PROJECT_CODE_PREFIX_SOURCE,
45
48
  // `replaceAll('A-Z', 'A-Za-z')` — that derivation silently no-ops (and narrows
46
49
  // this module to uppercase-only) the day phase-id.cts renders the class any
47
50
  // other way. See the constants' doc comment in phase-id.cts.
48
- CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE, CASE_FLEXIBLE_PHASE_NUMBER_TOKEN_SOURCE, PHASE_CONTINUATION_SEGMENT_SOURCE, } = phaseIdMod;
51
+ CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE, CASE_FLEXIBLE_PHASE_NUMBER_TOKEN_SOURCE, PHASE_CONTINUATION_SEGMENT_SOURCE, BRACKET_DIR_PREFIX_SRC, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, extractPhaseToken, isSentinelPhaseId,
52
+ // #612 / #3309 re-homing: `checkBracketCoherence` (below) moved into this
53
+ // module when #3309 migrated `cmdValidateHealth` onto the rule table and
54
+ // deleted every local helper it used to sit beside in `verify.cts`. These
55
+ // four are the grammar owners it consumes, imported unchanged — nothing about
56
+ // the check is widened by the relocation.
57
+ BRACKET_ID_SRC, PHASE_NUMBER_TOKEN_SOURCE,
58
+ // #2761 M3: canonical bracket milestone intro, re-homed here with
59
+ // checkBracketCoherence when health diagnostics moved to the rule table.
60
+ BRACKET_MILESTONE_INTRO_CAPTURING_SRC, foldBracketId, } = phaseIdMod;
61
+ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
49
62
  // ── Issue #26: regex constants (W005, W006-archived) ────────────────────────
50
63
  // Matches legacy numeric dirs (01-setup), milestone-prefixed dirs (02-01-setup),
51
64
  // deep dirs (02-04-01-deep), and project-code-prefixed variants (GSD-02-01-setup).
@@ -72,6 +85,64 @@ exports.phaseDirNameRe = new RegExp(`^${OPTIONAL_PROJECT_CODE_PREFIX_SOURCE}\\d{
72
85
  exports.PHASE_TOKEN_FROM_DIR_RE = new RegExp(`^(${CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE}` +
73
86
  `\\d+[A-Za-z]?(?:-${PHASE_CONTINUATION_SEGMENT_SOURCE}[A-Z]?)*(?:\\.\\d+)*)(?:-|$)`);
74
87
  exports.MILESTONE_ARCHIVE_DIR_RE = /^v\d+.*-phases$/i;
88
+ // ── #612: bracket phase-directory recognition (convention-gated) ────────────
89
+ // `{CODE}.{MM}-{PP}[.{SS}][-slug]`, built from the one bracket identity grammar.
90
+ //
91
+ // This lives BESIDE phaseDirNameRe / PHASE_TOKEN_FROM_DIR_RE rather than being
92
+ // folded into them. The `{CODE}.{MM}-` prefix is string-indistinguishable from
93
+ // the legacy letter-prefixed-decimal family this repo documents as "ambiguous
94
+ // with a padded bracket dir", and folding a bracket branch in changes those
95
+ // constants' answers on exactly that family: `P0.34-56-name` goes null -> "56",
96
+ // and phaseDirNameRe goes false -> true, silencing a W005 that fires today. A
97
+ // RegExp constant has nowhere to attach a convention gate, so the gate goes on
98
+ // the functions and the constants stay byte-identical for every consumer.
99
+ //
100
+ // The numeric run mirrors the EMIT grammar rather than accepting any digit run:
101
+ // CANONICAL_NUMERIC_RE (what toDir enforces) is digits-only with at most one
102
+ // sub-phase, so `GSD.02-12A-hotfix` and `GSD.02-05.03.07-x` are not bracket
103
+ // directories. Admitting them would make this recognizer disagree with
104
+ // extractPhaseToken, which the milestone-complete check resolves through — and
105
+ // then W006/W007 would resolve a directory that W021 simultaneously reported
106
+ // unstarted, inside one `validate health` run.
107
+ exports.BRACKET_PHASE_DIR_RE = new RegExp(`^(?:${BRACKET_DIR_PREFIX_SRC})\\d+(?:\\.\\d+)?(?:-[\\w-]+)?$`, 'i');
108
+ // The constants these functions wrap are consumed as `e.name.match(RE)`, which
109
+ // throws on a non-string. Coercing instead would invent a phase token out of a
110
+ // number (`42` -> `"42"`), so the contract is preserved rather than softened.
111
+ function assertDirName(value, fn) {
112
+ if (typeof value !== 'string') {
113
+ throw new TypeError(`${fn}: directory name must be a string, received ${typeof value}`);
114
+ }
115
+ return value;
116
+ }
117
+ /**
118
+ * True when `dirName` is a recognizable phase directory under `convention`.
119
+ * Under 'bracket' the `{CODE}.{MM}-{PP}` form is additionally accepted, so W005
120
+ * stops reporting every bracket phase directory as malformed. Every other
121
+ * convention value delegates to the unchanged `phaseDirNameRe`.
122
+ */
123
+ function isPhaseDirName(dirName, convention) {
124
+ const name = assertDirName(dirName, 'isPhaseDirName');
125
+ if (convention === 'bracket' && exports.BRACKET_PHASE_DIR_RE.test(name))
126
+ return true;
127
+ return exports.phaseDirNameRe.test(name);
128
+ }
129
+ /**
130
+ * Extract a phase token from a directory name under `convention`, or null when
131
+ * the name is not a phase directory — the same contract as
132
+ * `PHASE_TOKEN_FROM_DIR_RE.exec()[1]`.
133
+ *
134
+ * Under 'bracket' the SHAPE is recognized here and the TOKEN is delegated to the
135
+ * canonical owner, so this and every other bracket directory reader resolve
136
+ * identically by construction rather than by two regexes agreeing today.
137
+ */
138
+ function phaseTokenFromDir(dirName, convention) {
139
+ const name = assertDirName(dirName, 'phaseTokenFromDir');
140
+ if (convention === 'bracket' && exports.BRACKET_PHASE_DIR_RE.test(name)) {
141
+ return extractPhaseToken(name, 'bracket');
142
+ }
143
+ const legacy = name.match(exports.PHASE_TOKEN_FROM_DIR_RE);
144
+ return legacy ? legacy[1] : null;
145
+ }
75
146
  // ── Issue #26: I001 canonicalization ────────────────────────────────────────
76
147
  function canonicalPlanStem(stem) {
77
148
  // #2043: the plan component (after the phase number) must be zero-padded,
@@ -116,35 +187,72 @@ function phaseVariants(phase) {
116
187
  variants.add(`${numericHead.padStart(2, '0')}${letterSuffix}${tail}`);
117
188
  return variants;
118
189
  }
119
- function buildRoadmapPhaseVariants(roadmapContent) {
190
+ function buildRoadmapPhaseVariants(roadmapContent, convention) {
120
191
  const roadmapPhases = new Set();
121
192
  const roadmapPhaseVariants = new Set();
193
+ const sentinelOnly = new Set();
194
+ const realTokens = new Set();
122
195
  // Matches both legacy numeric (Phase 1:), decimal (Phase 2.1:), milestone-prefixed (Phase 2-01:),
123
196
  // and bracket-prefixed (### [GSD] Phase 2-01:) headings.
124
197
  // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
125
- const phasePattern = /#{2,4}\s*(?:\[[^\]]{1,200}\]\s*)?Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:/gi;
198
+ // #612: SELECTED by the resolved convention. This capture class is
199
+ // letter-tolerant, which makes it the site where an ungated widening does the
200
+ // most damage — `### [RFC.2119] 5:` enters roadmapPhases as a phantom and
201
+ // becomes a W007 "in ROADMAP.md but no directory on disk" on a repo that never
202
+ // opted in. A non-bracket repo compiles the base source unchanged.
203
+ const capturing = convention === 'bracket';
204
+ const g = capturing ? 1 : 0;
205
+ const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.ANY_BRACKET, convention, capturing)}([\\w][\\w.-]*)(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
126
206
  let m;
127
207
  while ((m = phasePattern.exec(roadmapContent)) !== null) {
128
- roadmapPhases.add(m[1]);
129
- for (const variant of phaseVariants(m[1]))
208
+ const token = m[1 + g];
209
+ const bracketId = g ? m[1] : undefined;
210
+ if (bracketId && isSentinelPhaseId(`${bracketId}-${token}`, 'bracket'))
211
+ sentinelOnly.add(token);
212
+ else
213
+ realTokens.add(token);
214
+ roadmapPhases.add(token);
215
+ for (const variant of phaseVariants(token))
130
216
  roadmapPhaseVariants.add(variant);
131
217
  }
132
218
  // Also matches checklist-style entries (checked or unchecked):
133
219
  // - [x] **Phase 01: name** - [X] **Phase 2-01: name** - [ ] **Phase 3: name**
134
220
  // This is a supported ROADMAP format (parallel to buildNotStartedPhaseVariants).
135
- const checklistPattern = /-\s*\[[ xX]\]\s*\*{0,2}Phase\s+([\w][\w.-]*)\s*:/gi;
221
+ // #612: CAPTURING, exactly as the sibling checklist scan in roadmap.cts does
222
+ // and for the same stated reason — "the bracket id rides along so the sentinel
223
+ // filter below is not blind to `- [ ] **[GSD.999] 01: Icebox**`". Left
224
+ // un-capturing here, this scan called every checklist token REAL, and the
225
+ // occurrence-aware un-suppression loop below then deleted the icebox token that
226
+ // the HEADING scan had correctly marked sentinel — so `validate consistency`
227
+ // warned that a bracket ICEBOX phase had no directory, in the house ROADMAP
228
+ // shape (bold bullet index + detail headings) where the icebox appears as both.
229
+ // `validate health` stayed silent on the same repo, so the two verbs disagreed
230
+ // — the disagreement `sentinelPhases` exists to close.
231
+ const checklistPattern = new RegExp(`-\\s*\\[[ xX]\\]\\s*\\*{0,2}${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention, capturing)}([\\w][\\w.-]*)\\s*:`, 'gi');
136
232
  let cm;
137
233
  while ((cm = checklistPattern.exec(roadmapContent)) !== null) {
138
- roadmapPhases.add(cm[1]);
139
- for (const variant of phaseVariants(cm[1]))
234
+ const cBracketId = g ? cm[1] : undefined;
235
+ const cToken = cm[1 + g];
236
+ if (cBracketId && isSentinelPhaseId(`${cBracketId}-${cToken}`, 'bracket'))
237
+ sentinelOnly.add(cToken);
238
+ else
239
+ realTokens.add(cToken);
240
+ roadmapPhases.add(cToken);
241
+ for (const variant of phaseVariants(cToken))
140
242
  roadmapPhaseVariants.add(variant);
141
243
  }
142
- return { roadmapPhases, roadmapPhaseVariants };
244
+ // A token borne by BOTH a sentinel and a real heading is not suppressed.
245
+ for (const t of realTokens)
246
+ sentinelOnly.delete(t);
247
+ return { roadmapPhases, roadmapPhaseVariants, sentinelPhases: sentinelOnly };
143
248
  }
144
- function buildNotStartedPhaseVariants(roadmapContent) {
249
+ function buildNotStartedPhaseVariants(roadmapContent, convention) {
145
250
  const notStartedPhases = new Set();
146
251
  // Also matches milestone-prefixed and bracket-prefixed checklist items.
147
- const uncheckedPattern = /-\s*\[\s\]\s*\*{0,2}Phase\s+([\w][\w.-]*)[:\s*]/gi;
252
+ // Trailing class is `[:\s*]` — a SPACE terminates the token here, not only a
253
+ // colon — so this site is the loosest of the three and the one where a
254
+ // retro-granted bracket tolerance would suppress a live W006.
255
+ const uncheckedPattern = new RegExp(`-\\s*\\[\\s\\]\\s*\\*{0,2}${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention)}([\\w][\\w.-]*)[:\\s*]`, 'gi');
148
256
  let um;
149
257
  while ((um = uncheckedPattern.exec(roadmapContent)) !== null) {
150
258
  for (const variant of phaseVariants(um[1]))
@@ -152,6 +260,116 @@ function buildNotStartedPhaseVariants(roadmapContent) {
152
260
  }
153
261
  return notStartedPhases;
154
262
  }
263
+ /**
264
+ * Bracket-coherence check (#612). ADVISORY, and consumed ONLY under
265
+ * `phase_id_convention === 'bracket'`. Two sub-checks, both surfaced as W021:
266
+ * (1) a phase whose in-bracket milestone differs from its enclosing section's;
267
+ * (2) a phase heading not in bracket form under a repo that opted into bracket.
268
+ * Sentinel milestones are exempt — they have no real milestone to cohere with.
269
+ *
270
+ * Anchored to tokenizeHeadings(): the tokenizer strips fenced code blocks, so a
271
+ * bracket heading inside a ```markdown example cannot raise a warning, and it
272
+ * yields the heading LEVEL so section-vs-phase is structural rather than a
273
+ * hash-counting regex.
274
+ *
275
+ * SCOPE RULES, all three load-bearing:
276
+ * - Only a genuine MILESTONE heading opens or closes a section. A `### Notes`
277
+ * is not a section boundary; treating any non-phase heading as one meant a
278
+ * single prose heading silently disabled both sub-checks for every phase
279
+ * after it.
280
+ * - A legacy `## v3.0` milestone heading CLOSES the bracket section, so a
281
+ * phase under it is out of scope rather than compared against — and reported
282
+ * against — a section it is not in.
283
+ * - An M-NN or letter-suffixed phase heading (`### Phase 2-01:`, `### Phase 12A:`)
284
+ * raises missing-bracket AND CONTINUES. Treating it as a section reset let a
285
+ * single M-NN heading — the exact mid-migration content this epic targets —
286
+ * silently disable the whole check.
287
+ *
288
+ * A bare `### 2026:` with no `Phase` label and no bracket is NOT a phase heading
289
+ * and raises nothing; the previous rule flagged year and version headings as
290
+ * phases needing migration.
291
+ *
292
+ * #3309 RE-HOMING NOTE: this function lived in `verify.cts` beside
293
+ * `checkMilestonePrefixMismatches`, its legacy-convention sibling, and was
294
+ * called inline from `cmdValidateHealth`'s W021 block. #3309 migrated
295
+ * `cmdValidateHealth` onto the rule table and relocated
296
+ * `checkMilestonePrefixMismatches`'s section walk into
297
+ * `planning-snapshot.cts`'s `roadmapDeclaredPhases` builder. This check follows
298
+ * its sibling: the body is byte-identical to the reviewed original, it moves
299
+ * into this pure no-I/O module (the owner of the other three ROADMAP-content
300
+ * scanners W006/W007 consume), `planning-snapshot.cts` calls it to produce the
301
+ * `roadmapBracketIncoherences` field, and `RULE_W021`
302
+ * (`health-diagnostic-rules/state-consistency.cts`) renders the messages. A
303
+ * `Rule.check(snapshot)` may not read raw `.planning/` text (ADR-3180 §8.1
304
+ * rule 2), so the read must happen in the snapshot layer either way.
305
+ */
306
+ function checkBracketCoherence(roadmapContent) {
307
+ const incoherences = [];
308
+ // #3185: routed through the owner's predicate rather than reaching into its
309
+ // exported `SENTINEL_RANGES` array. `n` is an already-parsed, safe,
310
+ // non-negative milestone integer, so `String(n)` is a bare integer token and
311
+ // `isSentinelPhaseId`'s convention-less leading-int rule reduces to exactly
312
+ // `SENTINEL_RANGES.includes(n)` — same answer, one owner.
313
+ const isSentinel = (n) => isSentinelPhaseId(String(n));
314
+ const pad2 = (n) => (Number.isSafeInteger(n) ? String(n).padStart(2, '0') : 'unknown');
315
+ // A bracket PHASE heading: `### [CODE.MM] 05:` or `### [CODE.MM] Phase 5:`.
316
+ const bracketPhaseRe = new RegExp(`^\\[(${BRACKET_ID_SRC})\\][ \\t]*(?:Phase\\s+)?(${PHASE_NUMBER_TOKEN_SOURCE})\\s*:`, 'i');
317
+ // A NON-bracket phase heading — requires the literal `Phase` label, so a bare
318
+ // `2026:` year heading is not a phase. Token is the full phase-number grammar
319
+ // so M-NN and letter-suffixed ids are RECOGNIZED (and flagged), not skipped.
320
+ const legacyPhaseRe = new RegExp(`^Phase\\s+(${PHASE_NUMBER_TOKEN_SOURCE}|\\d+(?:-\\d+)+[A-Z]?(?:\\.\\d+)*)\\s*:`, 'i');
321
+ // A bracket MILESTONE section heading.
322
+ const bracketSectionRe = new RegExp(`^${BRACKET_MILESTONE_INTRO_CAPTURING_SRC}`, 'i');
323
+ // A legacy milestone section heading (`## v2.0 — Name`).
324
+ const legacyMilestoneRe = /^v\d+(?:\.\d+)*\b/i;
325
+ let sectionMilestone = null;
326
+ for (const heading of (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(roadmapContent)) {
327
+ const text = heading.text.trim();
328
+ const bracketPhase = text.match(bracketPhaseRe);
329
+ const legacyPhase = bracketPhase ? null : text.match(legacyPhaseRe);
330
+ const isPhaseHeading = Boolean(bracketPhase || legacyPhase);
331
+ if (!isPhaseHeading && heading.level <= 3) {
332
+ const bracketSection = text.match(bracketSectionRe);
333
+ if (bracketSection) {
334
+ const mm = parseInt(bracketSection[1], 10);
335
+ sectionMilestone = Number.isSafeInteger(mm) ? mm : null;
336
+ continue;
337
+ }
338
+ // Only a MILESTONE heading closes the section. Any other heading (`### Notes`,
339
+ // `## Backlog`) leaves the scope exactly as it was.
340
+ if (legacyMilestoneRe.test(text.replace(/^\[[^\]]{0,200}\][ \t]*/, '')))
341
+ sectionMilestone = null;
342
+ continue;
343
+ }
344
+ if (!isPhaseHeading)
345
+ continue;
346
+ if (sectionMilestone === null || isSentinel(sectionMilestone))
347
+ continue;
348
+ if (bracketPhase) {
349
+ const folded = foldBracketId(bracketPhase[1]);
350
+ const dot = folded.lastIndexOf('.');
351
+ const phaseMilestone = parseInt(folded.slice(dot + 1), 10);
352
+ if (!Number.isSafeInteger(phaseMilestone) || isSentinel(phaseMilestone))
353
+ continue;
354
+ if (phaseMilestone !== sectionMilestone) {
355
+ incoherences.push({
356
+ kind: 'mismatch',
357
+ phaseId: bracketPhase[2],
358
+ sectionMilestone: pad2(sectionMilestone),
359
+ phaseMilestone: pad2(phaseMilestone),
360
+ });
361
+ }
362
+ continue;
363
+ }
364
+ incoherences.push({
365
+ kind: 'missing-bracket',
366
+ phaseId: legacyPhase[1],
367
+ sectionMilestone: pad2(sectionMilestone),
368
+ phaseMilestone: pad2(sectionMilestone),
369
+ });
370
+ }
371
+ return incoherences;
372
+ }
155
373
  /**
156
374
  * Detect binary corruption (embedded NUL bytes) in a text artifact's bytes.
157
375
  *
@@ -1262,16 +1262,21 @@
1262
1262
  state.result += _result;
1263
1263
  }
1264
1264
  }
1265
+ function chargeMergeWork(state) {
1266
+ state.totalMergeKeys++;
1267
+ if (state.maxTotalMergeKeys !== -1 && state.totalMergeKeys > state.maxTotalMergeKeys) {
1268
+ throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
1269
+ }
1270
+ }
1265
1271
  function mergeMappings(state, destination, source, overridableKeys) {
1266
1272
  if (!common2.isObject(source)) {
1267
1273
  throwError(state, "cannot merge mappings; the provided source object is unacceptable");
1268
1274
  }
1275
+ chargeMergeWork(state);
1269
1276
  var sourceKeys = Object.keys(source);
1270
1277
  for (var index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
1271
1278
  var key = sourceKeys[index];
1272
- if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) {
1273
- throwError(state, "merge keys exceeded maxTotalMergeKeys (" + state.maxTotalMergeKeys + ")");
1274
- }
1279
+ chargeMergeWork(state);
1275
1280
  if (!_hasOwnProperty.call(destination, key)) {
1276
1281
  setProperty(destination, key, source[key]);
1277
1282
  overridableKeys[key] = true;
@@ -1299,6 +1304,9 @@
1299
1304
  }
1300
1305
  if (keyTag === "tag:yaml.org,2002:merge") {
1301
1306
  if (Array.isArray(valueNode)) {
1307
+ if (valueNode.length > 100) {
1308
+ throwError(state, "abnormal merge sequence size");
1309
+ }
1302
1310
  for (var _index = 0, _quantity = valueNode.length; _index < _quantity; _index += 1) {
1303
1311
  mergeMappings(state, _result, valueNode[_index], overridableKeys);
1304
1312
  }
@@ -13,7 +13,7 @@
13
13
  const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
14
14
  const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
15
15
  // ─── Implementation ───────────────────────────────────────────────────────────
16
- const VERIFICATION_SUBCOMMANDS = ['status', 'resolve-file'];
16
+ const VERIFICATION_SUBCOMMANDS = ['status', 'resolve-file', 'fingerprint'];
17
17
  function routeVerificationCommand({ verification, args, cwd, raw, error, }) {
18
18
  routeCjsCommandFamily({
19
19
  args,
@@ -24,6 +24,7 @@ function routeVerificationCommand({ verification, args, cwd, raw, error, }) {
24
24
  handlers: {
25
25
  status: () => verification.cmdVerificationStatus(cwd, args[2], raw),
26
26
  'resolve-file': () => verification.cmdVerificationResolveFile(cwd, args[2], raw),
27
+ fingerprint: () => verification.cmdVerificationFingerprint(cwd, args[2], args.slice(3), raw),
27
28
  },
28
29
  });
29
30
  }