@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
@@ -143,6 +143,7 @@ function parseXmlTasks(content) {
143
143
  acceptanceCriteria: [],
144
144
  done: null,
145
145
  trackerId: null,
146
+ tdd: null,
146
147
  };
147
148
  }
148
149
  return {
@@ -154,6 +155,7 @@ function parseXmlTasks(content) {
154
155
  acceptanceCriteria: splitCriteria(elementBody(block, 'acceptance_criteria')),
155
156
  done: collapseWhitespace(elementBody(block, 'done')),
156
157
  trackerId: tagAttribute(openTag, 'tracker-id'),
158
+ tdd: tagAttribute(openTag, 'tdd'),
157
159
  };
158
160
  });
159
161
  }
@@ -172,6 +174,7 @@ function parseMarkdownTasks(content) {
172
174
  acceptanceCriteria: [],
173
175
  done: null,
174
176
  trackerId: null,
177
+ tdd: null,
175
178
  }));
176
179
  }
177
180
  // ─── Objective ────────────────────────────────────────────────────────────────
@@ -247,8 +250,15 @@ function parsePlanDocument(content, planPath = '') {
247
250
  const hintStr = String(fmAgentHint).trim();
248
251
  agentHint = hintStr !== '' ? hintStr : null;
249
252
  }
253
+ let planType = null;
254
+ const fmType = fm['type'];
255
+ if (fmType !== undefined) {
256
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string -- FrontmatterValue scalar-to-string
257
+ planType = String(fmType);
258
+ }
250
259
  return {
251
260
  objective: extractObjective(content) || fm['objective'] || null,
261
+ type: planType,
252
262
  declaredWave,
253
263
  dependsOn,
254
264
  autonomous,
@@ -94,6 +94,9 @@ const { parseMarkdownTable, matchTableSchema } = markdownTable;
94
94
  // eslint-disable-next-line @typescript-eslint/no-require-imports
95
95
  const coreUtilsMod = require("./core-utils.cjs");
96
96
  const { normalizeLineEndings } = coreUtilsMod;
97
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
98
+ const securityMod = require("./security.cjs");
99
+ const { tryWithinRoot, PathAcceptance, isContainedIn } = securityMod;
97
100
  /**
98
101
  * The wire schema version. A consumer MUST reject any value other than this
99
102
  * one rather than best-effort-parsing an unknown shape.
@@ -184,32 +187,45 @@ function toPosix(value) {
184
187
  * directory like `.planning-evil/` that merely shares a string prefix. Pure
185
188
  * string comparison, no I/O — callers own their own `fs.realpathSync` call
186
189
  * (and its own not-found/broken-symlink handling).
190
+ *
191
+ * NOT an independent containment implementation — it is the comparison step
192
+ * of one, and that comparison now comes from `security.cts`'s exported
193
+ * `isContainedIn` rather than being redeclared here. `readDocument` below
194
+ * realpaths target and root itself (to keep its own exists-vs-escaped
195
+ * tri-state) and calls `isContainedIn` directly; `isPathContained` gets its
196
+ * containment DECISION from the canonical `tryWithinRoot` predicate instead
197
+ * (ADR-4650 decision 6) and never called this comparison directly. Every
198
+ * caller owns its own resolution.
187
199
  */
188
- function isWithinRoot(resolvedTarget, resolvedRoot) {
189
- return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + node_path_1.default.sep);
190
- }
191
200
  /**
192
- * Containment check for a path (file OR directory) that resolves its own
193
- * `fs.realpathSync`, then delegates the actual boundary comparison to
194
- * `isWithinRoot`. Used where the caller does not need to distinguish "target
195
- * vanished / broken symlink" from "target resolved but escapes root" — both
196
- * degrade the same way at every call site that uses this (an escaped or
197
- * unresolvable phase directory is treated identically to an unreadable one).
198
- * `readDocument` below needs that distinction for its own exists/readable
199
- * tri-state, so it keeps its own inline `realpathSync` calls and calls
200
- * `isWithinRoot` directly instead of this wrapper.
201
+ * Containment check for a path (file OR directory), used where the caller
202
+ * does not need to distinguish "target vanished / broken symlink" from
203
+ * "target resolved but escapes root" — both degrade the same way at every
204
+ * call site that uses this (an escaped or unresolvable phase directory is
205
+ * treated identically to an unreadable one). `readDocument` below needs
206
+ * that distinction for its own exists/readable tri-state, so it keeps its
207
+ * own inline `realpathSync` calls and calls `isContainedIn` directly instead
208
+ * of this wrapper.
209
+ *
210
+ * The containment DECISION comes from the canonical `tryWithinRoot`
211
+ * predicate (ADR-4650 decision 6: a wrapper may decide HOW to degrade,
212
+ * never WHETHER a path is contained). Must-exist stays this module's OWN
213
+ * degradation condition, applied after: `tryWithinRoot` deliberately accepts
214
+ * a not-yet-created path under the root (ancestor-walk realpath), but every
215
+ * caller of `isPathContained` guards an `fs` read that is about to happen
216
+ * against an already-existing directory, so a vanished/unresolvable path
217
+ * must still degrade the same as an escaped one.
201
218
  */
202
219
  function isPathContained(target, root) {
203
- let realTarget;
204
- let realRoot;
220
+ if (tryWithinRoot(target, root, PathAcceptance.AbsoluteInsideRoot) === null)
221
+ return false;
205
222
  try {
206
- realTarget = node_fs_1.default.realpathSync(target);
207
- realRoot = node_fs_1.default.realpathSync(root);
223
+ node_fs_1.default.realpathSync(target);
208
224
  }
209
225
  catch {
210
226
  return false;
211
227
  }
212
- return isWithinRoot(realTarget, realRoot);
228
+ return true;
213
229
  }
214
230
  function readDocument(filePath, root) {
215
231
  let stat;
@@ -234,7 +250,7 @@ function readDocument(filePath, root) {
234
250
  // here — the same non-answer `readDocument` already gives "not exists".
235
251
  return { text: null, exists: false, readable: false };
236
252
  }
237
- if (!isWithinRoot(realTarget, realRoot)) {
253
+ if (!isContainedIn(realTarget, realRoot)) {
238
254
  return { text: null, exists: true, readable: false };
239
255
  }
240
256
  try {
@@ -37,7 +37,19 @@ const { isPhaseComplete } = verificationMod;
37
37
  const scanPhasePlans = require("./plan-scan.cjs");
38
38
  // eslint-disable-next-line @typescript-eslint/no-require-imports
39
39
  const planningWorkspace = require("./planning-workspace.cjs");
40
- const { planningPaths, planningRoot } = planningWorkspace;
40
+ // #612: `resolvePhaseIdConvention` is the federated (workstream -> root)
41
+ // `phase_id_convention` reader, from the same §7 owner module `planningPaths`
42
+ // comes from. Resolved once in `buildPlanningSnapshot` — see the
43
+ // `phaseIdConvention` field's comment for why one resolution point matters.
44
+ // #4257: `resolveEnvWorkstream` is that same module's ONE owner of the env
45
+ // workstream discriminator `planningDir` applies — the name W002's scope
46
+ // clause prints comes from the same resolution point that scoped the reads.
47
+ const { planningPaths, planningRoot, resolvePhaseIdConvention, resolveEnvWorkstream } = planningWorkspace;
48
+ // #4257: canonical CommonMark code strippers (markdown-sectionizer is the
49
+ // repo's T0 structural seam, adopted per the #2365 composition order — fenced
50
+ // blocks first, then inline spans) so the `statePhaseTokens` harvest sees
51
+ // PROSE, not quoted literals.
52
+ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
41
53
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
42
54
  // eslint-disable-next-line @typescript-eslint/no-require-imports
43
55
  const frontmatterMod = require("./frontmatter.cjs");
@@ -64,7 +76,15 @@ const configLoaderMod = require("./config-loader.cjs");
64
76
  const { isGitIgnored } = configLoaderMod;
65
77
  // eslint-disable-next-line @typescript-eslint/no-require-imports
66
78
  const phaseIdMod = require("./phase-id.cjs");
67
- const { PHASE_NUMBER_TOKEN_SOURCE, OPTIONAL_PHASE_TAG_SOURCE, stripProjectCodePrefix, scopeToPhase } = phaseIdMod;
79
+ // #612: `phaseHeadingPrefixSrcFor`/`PHASE_HEADING_BASELINE` SELECT a heading
80
+ // intro by convention (a convention-less call compiles the byte-identical base
81
+ // source the literal it replaced spelled); `isSentinelPhaseId` gets its bracket
82
+ // reading only when handed the convention explicitly.
83
+ const { PHASE_NUMBER_TOKEN_SOURCE, OPTIONAL_PHASE_TAG_SOURCE, stripProjectCodePrefix, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, isSentinelPhaseId, scopeToPhase, } = phaseIdMod;
84
+ // #612: `phaseTokenFromDir` is the convention-SELECTED counterpart of
85
+ // `PHASE_TOKEN_FROM_DIR_RE` — handed no convention it delegates to that very
86
+ // regex, so a legacy repo's tokenization is unchanged. `checkBracketCoherence`
87
+ // re-homed into `validate.cts` when #3309 deleted its `verify.cts` neighbours.
68
88
  const validate_cjs_1 = require("./validate.cjs");
69
89
  // ─── worstScope — the one new piece of coordination logic ───────────────────
70
90
  /**
@@ -97,9 +117,12 @@ function worstScope(...scopes) {
97
117
  * uncorrelated (isPhaseComplete's readability check never re-derives or
98
118
  * requires scanPhasePlans, and vice versa).
99
119
  */
100
- function buildPhaseSnapshot(phasesDir, dir) {
120
+ function buildPhaseSnapshot(phasesDir, dir, convention) {
101
121
  const fullPhaseDir = node_path_1.default.join(phasesDir, dir);
102
- const completionResult = isPhaseComplete(fullPhaseDir);
122
+ // #612: the snapshot's single federated convention resolution rides into
123
+ // completion, so a bracket phase dir resolves and scopes its verification
124
+ // report exactly like its legacy twin.
125
+ const completionResult = isPhaseComplete(fullPhaseDir, { convention });
103
126
  const scanResult = scanPhasePlans(fullPhaseDir);
104
127
  return {
105
128
  dir,
@@ -148,9 +171,11 @@ function buildPhaseSnapshot(phasesDir, dir) {
148
171
  * a whole-body fallback, together.
149
172
  * - `statePhaseTokens` scans the WHOLE document (`verify.cts`'s exact
150
173
  * `PHASE_NUMBER_TOKEN_SOURCE` regex, relocated verbatim from
151
- * `verify.cts:1731-1735`), not just the Current Position section, so it is
152
- * NOT degraded to `TRUNCATED` by a missing section header — it stays
153
- * `COMPLETE` whenever the file itself was read successfully.
174
+ * `verify.cts:1731-1735`; #4257 adds the left word boundary and the
175
+ * fenced-block/inline-span strip — see the harvest site's comment), not
176
+ * just the Current Position section, so it is NOT degraded to `TRUNCATED`
177
+ * by a missing section header — it stays `COMPLETE` whenever the file
178
+ * itself was read successfully.
154
179
  */
155
180
  function buildStateFields(statePath) {
156
181
  let content;
@@ -210,7 +235,30 @@ function buildStateFields(statePath) {
210
235
  scope: currentPositionScope,
211
236
  });
212
237
  const statePhaseTokens = {
213
- value: [...content.matchAll(new RegExp(`[Pp]hase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})`, 'g'))].map((m) => m[1]),
238
+ // #4257: harvest PROSE phase references, not every literal token match.
239
+ // Two precisions over the pre-#4257 verbatim relocation of verify.cts's
240
+ // scan (which was `[Pp]hase\s+(TOKEN)`, unanchored, over the raw file):
241
+ //
242
+ // 1. Strip fenced code blocks, then inline code spans (the #2365
243
+ // composition order, via the canonical markdown-sectionizer seam) —
244
+ // a token inside backticks is a QUOTED LITERAL (a ledger row quoting
245
+ // `` `/gsd-execute-phase 5` `` or `` `- [ ] **Phase 40:` `` from a
246
+ // sibling roadmap), not a reference. Pinned tradeoff: a GENUINE
247
+ // reference written in backticks stops counting too — a quoted
248
+ // literal and a reference are indistinguishable inside a code span.
249
+ // 2. Left word boundary `(?<![-\w])` — the `-phase 5` tail of GSD's own
250
+ // command names (`/gsd-execute-phase 5`, bare or in prose) is a
251
+ // command mention, not a reference, and word-suffixed carriers
252
+ // (`myphase 5`) never were references. `Phase 5` at line start,
253
+ // `**Phase 5:**`, `(Phase 5)`, `[Phase 5]`, and `### Phase 5:` all
254
+ // still harvest — the char before `Phase` is not in `[-\w]`.
255
+ //
256
+ // Still scans the WHOLE document (frontmatter included — the `Phase: 3`
257
+ // field syntax never matched, `phase` is followed by a colon, not `\s`),
258
+ // still `COMPLETE` whenever the file itself was read successfully.
259
+ value: [
260
+ ...(0, markdown_sectionizer_cjs_1.stripInlineCode)((0, markdown_sectionizer_cjs_1.stripFencedCode)(content).text).matchAll(new RegExp(`(?<![-\\w])[Pp]hase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})`, 'g')),
261
+ ].map((m) => m[1]),
214
262
  scope: SCOPE.COMPLETE,
215
263
  };
216
264
  return { currentPhaseLabel, statePhaseTokens, stateStatus };
@@ -462,18 +510,31 @@ function buildProjectSectionsField(cwd) {
462
510
  * only the mismatches" to "record every attribution" — this field exposes
463
511
  * the parsed fact; the future W021/W026 rules make the mismatch judgment.
464
512
  */
465
- function buildRoadmapDeclaredPhasesField(roadmapPath) {
513
+ function buildRoadmapDeclaredPhasesField(roadmapPath, convention) {
466
514
  if (!node_fs_1.default.existsSync(roadmapPath)) {
467
- return { value: [], scope: SCOPE.UNREADABLE };
515
+ return {
516
+ declared: { value: [], scope: SCOPE.UNREADABLE },
517
+ sentinelTokens: { value: [], scope: SCOPE.UNREADABLE },
518
+ };
468
519
  }
469
520
  let content;
470
521
  try {
471
522
  content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
472
523
  }
473
524
  catch {
474
- return { value: [], scope: SCOPE.UNREADABLE };
525
+ return {
526
+ declared: { value: [], scope: SCOPE.UNREADABLE },
527
+ sentinelTokens: { value: [], scope: SCOPE.UNREADABLE },
528
+ };
475
529
  }
476
- const { roadmapPhases } = (0, validate_cjs_1.buildRoadmapPhaseVariants)(content);
530
+ // #612: the declared-phase scan is SELECTED by the resolved convention — a
531
+ // non-bracket repo compiles the byte-identical pattern sources this call
532
+ // compiled before, so its declared set is unchanged. `sentinelPhases` is the
533
+ // same call's third output (empty off the bracket convention) and is surfaced
534
+ // rather than filtered in place: `roadmapPhases` feeds both a membership check
535
+ // (W002's valid-phase set) and a missing-directory warning (W006), and only
536
+ // the latter should ignore an icebox item.
537
+ const { roadmapPhases, sentinelPhases } = (0, validate_cjs_1.buildRoadmapPhaseVariants)(content, convention);
477
538
  const milestoneByPhase = new Map();
478
539
  const sectionRx = /^#{1,3}\s+(?:\[[^\]]{1,200}\]\s*)?.*v(\d+\.\d+)/gim;
479
540
  const sections = [];
@@ -497,7 +558,10 @@ function buildRoadmapDeclaredPhasesField(roadmapPath) {
497
558
  phaseId,
498
559
  milestone: milestoneByPhase.get(phaseId) ?? null,
499
560
  }));
500
- return { value, scope: SCOPE.COMPLETE };
561
+ return {
562
+ declared: { value, scope: SCOPE.COMPLETE },
563
+ sentinelTokens: { value: [...sentinelPhases], scope: SCOPE.COMPLETE },
564
+ };
501
565
  }
502
566
  /**
503
567
  * Resolve `roadmapPhaseCheckboxes` — parsed `[x]`/`[ ]` checkbox state per
@@ -518,7 +582,7 @@ function buildRoadmapDeclaredPhasesField(roadmapPath) {
518
582
  * diagnostic (W011) whose entire purpose is flagging when the two DISAGREE —
519
583
  * reading the data is not re-litigating who is authoritative.
520
584
  */
521
- function buildRoadmapPhaseCheckboxesField(roadmapPath) {
585
+ function buildRoadmapPhaseCheckboxesField(roadmapPath, convention) {
522
586
  if (!node_fs_1.default.existsSync(roadmapPath)) {
523
587
  return { value: {}, scope: SCOPE.UNREADABLE };
524
588
  }
@@ -529,7 +593,15 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
529
593
  catch {
530
594
  return { value: {}, scope: SCOPE.UNREADABLE };
531
595
  }
532
- const checkboxRe = new RegExp(`-\\s*\\[([xX ])\\].*?Phase\\s+0*(${PHASE_NUMBER_TOKEN_SOURCE})${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'gi');
596
+ // #612: the `Phase\s+` label intro is SELECTED, exactly as
597
+ // `buildNotStartedPhaseVariants` (`validate.cts`) selects it for the same
598
+ // ROADMAP checklist shape — this field is what W006's not-started exclusion
599
+ // now reads instead of that helper, so the two must recognize the same
600
+ // checklist lines or a bracket repo's `- [ ] **[GSD.02] 05: Name**` entries
601
+ // vanish from the exclusion set and every unstarted bracket phase gains a
602
+ // W006. NON-capturing (`capturing` defaults false), so the phase token stays
603
+ // group 2 and the legacy repo compiles a byte-identical source.
604
+ const checkboxRe = new RegExp(`-\\s*\\[([xX ])\\].*?${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention)}0*(${PHASE_NUMBER_TOKEN_SOURCE})${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'gi');
533
605
  const value = {};
534
606
  let m;
535
607
  while ((m = checkboxRe.exec(content)) !== null) {
@@ -537,6 +609,42 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
537
609
  }
538
610
  return { value, scope: SCOPE.COMPLETE };
539
611
  }
612
+ /**
613
+ * Resolve `roadmapBracketIncoherences` — W021's bracket half (#612). Delegates
614
+ * wholly to `checkBracketCoherence` (`validate.cjs`), which is pure and owns
615
+ * both sub-checks; this builder only supplies the ROADMAP text and the
616
+ * convention gate.
617
+ *
618
+ * GATED, not merely filtered downstream: off the bracket convention the ROADMAP
619
+ * is never parsed for this at all and the field is a `COMPLETE`-scoped empty
620
+ * list. Inferring 'bracket' from the SHAPE of a matched heading would run a
621
+ * repo-failing check against a repo that never opted in — a legacy ROADMAP
622
+ * containing `### [RFC.2119] 5:` is legal legacy content, and that is the exact
623
+ * regression PR-2's round 1 killed the original ungated design over.
624
+ */
625
+ function buildRoadmapBracketIncoherencesField(roadmapPath, convention) {
626
+ // File-readability is decided FIRST, so `scope` means the same thing on every
627
+ // repo: UNREADABLE iff ROADMAP.md could not be read, never "this convention
628
+ // was skipped." Ordering the convention gate first would have made an absent
629
+ // ROADMAP.md report COMPLETE on a legacy repo and UNREADABLE on a bracket one
630
+ // — the same "empty, nothing to say" state wearing two different scopes, which
631
+ // is precisely the non-answer/answer distinction ADR-3180 §8.1 gives `scope`
632
+ // to carry.
633
+ if (!node_fs_1.default.existsSync(roadmapPath))
634
+ return { value: [], scope: SCOPE.UNREADABLE };
635
+ // A non-bracket repo has no bracket incoherences BY DEFINITION — a real,
636
+ // COMPLETE answer, not a skipped read.
637
+ if (convention !== 'bracket')
638
+ return { value: [], scope: SCOPE.COMPLETE };
639
+ let content;
640
+ try {
641
+ content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
642
+ }
643
+ catch {
644
+ return { value: [], scope: SCOPE.UNREADABLE };
645
+ }
646
+ return { value: (0, validate_cjs_1.checkBracketCoherence)(content), scope: SCOPE.COMPLETE };
647
+ }
540
648
  /**
541
649
  * Resolve `researchValidationStatus` — per phase directory, whether its
542
650
  * `*-RESEARCH.md` contains the literal heading `## Validation Architecture`,
@@ -560,7 +668,7 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
560
668
  * deliberate fail-open match to the pre-migration behavior, not a scope
561
669
  * degradation, since the original never surfaced these failures either.
562
670
  */
563
- function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumerationScope) {
671
+ function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumerationScope, convention) {
564
672
  const value = phaseDirNames.map((dir) => {
565
673
  const fullPhaseDir = node_path_1.default.join(phasesDir, dir);
566
674
  let files;
@@ -574,7 +682,9 @@ function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumeratio
574
682
  // phase-numbered-artifact predicates, so a stray cross-phase
575
683
  // -RESEARCH.md/-VALIDATION.md sitting in the wrong directory cannot flip
576
684
  // this phase's flags — mirrors core-utils.cts's getPhaseFileStats.
577
- const scopedFiles = scopeToPhase(files, dir);
685
+ // #612: the snapshot's federated convention threaded, so a bracket dir
686
+ // scopes by its real token instead of the include-everything fail-safe.
687
+ const scopedFiles = scopeToPhase(files, dir, convention);
578
688
  const researchFile = scopedFiles.find((f) => f.endsWith('-RESEARCH.md'));
579
689
  const hasValidationMd = scopedFiles.some((f) => f.endsWith('-VALIDATION.md'));
580
690
  let hasValidationArchitecture = false;
@@ -699,7 +809,7 @@ function buildAllPhaseDirNamesField(phasesDir) {
699
809
  * present-but-unreadable per-archive-dir entry is silently skipped, mirroring
700
810
  * `forEachArchivedPhaseToken`'s own per-directory `catch { /* absent/unreadable *\/ }`.
701
811
  */
702
- function buildArchivedPhaseTokensField(planBase) {
812
+ function buildArchivedPhaseTokensField(planBase, convention) {
703
813
  const milestonesDir = node_path_1.default.join(planBase, 'milestones');
704
814
  let archiveDirs;
705
815
  try {
@@ -720,9 +830,19 @@ function buildArchivedPhaseTokensField(planBase) {
720
830
  for (const e of entries) {
721
831
  if (!e.isDirectory())
722
832
  continue;
723
- const m = e.name.match(validate_cjs_1.PHASE_TOKEN_FROM_DIR_RE);
724
- if (m)
725
- value.push(stripProjectCodePrefix(m[1]));
833
+ // #612: composed, not chosen. The convention-aware extractor decides
834
+ // WHICH directory shapes are recognized (so an archived
835
+ // `{CODE}.{MM}-{PP}-slug` is seen at all — `PHASE_TOKEN_FROM_DIR_RE`
836
+ // rejects it outright, which is why every archived bracket phase used
837
+ // to still draw a W006/W002); `stripProjectCodePrefix` then normalizes
838
+ // the token it returns. The strip is a no-op on every bracket token
839
+ // (`01`, `01.02` — the `{CODE}.{MM}` prefix is not part of the token)
840
+ // and does the #2528 work on legacy ones (`MEM-05` -> `05`), so neither
841
+ // side loses its case. Handed no convention, `phaseTokenFromDir`
842
+ // delegates to `PHASE_TOKEN_FROM_DIR_RE` itself — legacy is unchanged.
843
+ const token = (0, validate_cjs_1.phaseTokenFromDir)(e.name, convention);
844
+ if (token)
845
+ value.push(stripProjectCodePrefix(token));
726
846
  }
727
847
  }
728
848
  catch {
@@ -740,7 +860,7 @@ function buildArchivedPhaseTokensField(planBase) {
740
860
  * ROADMAP.md degrades to an empty list, mirroring every other
741
861
  * ROADMAP-sourced field's absent-file handling.
742
862
  */
743
- function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath) {
863
+ function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath, convention) {
744
864
  if (!node_fs_1.default.existsSync(roadmapPath))
745
865
  return { value: [], scope: SCOPE.UNREADABLE };
746
866
  let content;
@@ -753,8 +873,32 @@ function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath) {
753
873
  const scoped = extractCurrentMilestone(content, cwd);
754
874
  // #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal
755
875
  // mirror of OPTIONAL_PHASE_TAG_SOURCE) — verbatim from `verify.cts:2366`.
756
- const phasePattern = new RegExp(`#{2,4}\\s*Phase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
757
- const value = [...scoped.matchAll(phasePattern)].map((m) => m[1]);
876
+ //
877
+ // #612: this scan is convention-AGNOSTIC in POSTURE — W026 is ungated, and
878
+ // bug-557 pins it with an empty config so it fires on every repo — but its
879
+ // heading grammar is still SELECTED, never widened. Under bracket the intro
880
+ // CAPTURES, so the phase token moves to group 2 and `bracketGroup` carries
881
+ // that offset; off bracket the source is byte-identical to the literal above
882
+ // and `bracketGroup` is 0. Inferring the convention from a matched bracket's
883
+ // shape would run a repo-failing check against a repo that never opted in.
884
+ const bracketGroup = convention === 'bracket' ? 1 : 0;
885
+ const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention, Boolean(bracketGroup))}(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
886
+ const value = [];
887
+ for (const m of scoped.matchAll(phasePattern)) {
888
+ const bracketId = bracketGroup ? m[1] : undefined;
889
+ const phaseNum = m[1 + bracketGroup];
890
+ // A bracket sentinel is an ICEBOX item, not an unstarted phase — it
891
+ // legitimately has no directory, so leaving it in this list makes W026
892
+ // ("STATE says milestone complete but ROADMAP lists an unstarted phase")
893
+ // fire on every bracket repo that keeps an icebox. Filtered here rather
894
+ // than in `RULE_W026` because the bracket id is only visible at the match:
895
+ // the emitted token is `07`, and sentinel-ness lives in the `[GSD.999]`
896
+ // milestone this scan just discarded. This field's only consumer is W026
897
+ // (see its own doc comment above).
898
+ if (bracketId && isSentinelPhaseId(`${bracketId}-${phaseNum}`, 'bracket'))
899
+ continue;
900
+ value.push(phaseNum);
901
+ }
758
902
  return { value, scope: SCOPE.COMPLETE };
759
903
  }
760
904
  /**
@@ -853,11 +997,39 @@ function buildPerPhasePlanScanFields(phasesDir, phaseDirNames, enumerationScope)
853
997
  */
854
998
  function buildPlanningSnapshot(cwd) {
855
999
  const paths = planningPaths(cwd);
1000
+ // #612: ONE federated (workstream -> root) resolution for the whole snapshot.
1001
+ // See the `phaseIdConvention` field's comment for why one resolution point is
1002
+ // load-bearing rather than a micro-optimisation.
1003
+ const phaseIdConvention = resolvePhaseIdConvention(cwd) ?? null;
1004
+ // #4257: the workstream `planningPaths(cwd)` just scoped every read to
1005
+ // (its `planningDir` call applies this exact discriminator when handed no
1006
+ // `ws`), resolved through the same owner so W002's scope clause names the
1007
+ // scope the valid set was ACTUALLY built from.
1008
+ const workstream = resolveEnvWorkstream();
856
1009
  const milestone = getMilestoneInfo(cwd);
1010
+ // #612: deliberately LEFT to `listMilestonePhaseDirs`'s own lazy resolve —
1011
+ // this call is byte-identical to upstream's.
1012
+ //
1013
+ // Passing `phaseIdConvention` here would NOT be a no-op, which is exactly why
1014
+ // it is not passed. The lazy path resolves `resolvePhaseIdConvention(cwd, ws)`
1015
+ // with this call's `ws`, which defaults to `null` — the PROJECT-only reading,
1016
+ // with no root fallback. The field above is resolved with `ws` undefined,
1017
+ // i.e. the FEDERATED workstream -> root reading. On a workstream repo whose
1018
+ // root opts into bracket while the workstream config does not, the two answers
1019
+ // genuinely differ, and substituting one for the other would silently re-scope
1020
+ // `phaseDirs` — a change this PR does not need and no test covers. The
1021
+ // federation guarantee PR-2 exists to deliver is delivered where it is
1022
+ // observable: in the rules that read `snapshot.phaseIdConvention`. Per-phase
1023
+ // completion and the research/validation scoping below deliberately use the
1024
+ // FEDERATED `phaseIdConvention` (the same value `snapshot.phaseIdConvention`
1025
+ // publishes), while `phaseDirs` keeps `listMilestonePhaseDirs`'s lazy
1026
+ // PROJECT-only resolve — the divergence documented above is unchanged by
1027
+ // this thread. No behavior change.
857
1028
  const phaseDirs = listMilestonePhaseDirs(paths.phases, { cwd });
858
- const phasesValue = phaseDirs.value.map((dir) => buildPhaseSnapshot(paths.phases, dir));
1029
+ const phasesValue = phaseDirs.value.map((dir) => buildPhaseSnapshot(paths.phases, dir, phaseIdConvention));
859
1030
  const stateFields = buildStateFields(paths.state);
860
1031
  const allPhaseDirNames = buildAllPhaseDirNamesField(paths.phases);
1032
+ const roadmapDeclared = buildRoadmapDeclaredPhasesField(paths.roadmap, phaseIdConvention);
861
1033
  const perPhasePlanScanFields = buildPerPhasePlanScanFields(paths.phases, allPhaseDirNames.value, allPhaseDirNames.scope);
862
1034
  return {
863
1035
  cwd: node_path_1.default.resolve(cwd),
@@ -875,17 +1047,21 @@ function buildPlanningSnapshot(cwd) {
875
1047
  projectSections: buildProjectSectionsField(cwd),
876
1048
  statePhaseTokens: stateFields.statePhaseTokens,
877
1049
  stateStatus: stateFields.stateStatus,
878
- roadmapDeclaredPhases: buildRoadmapDeclaredPhasesField(paths.roadmap),
879
- roadmapPhaseCheckboxes: buildRoadmapPhaseCheckboxesField(paths.roadmap),
880
- researchValidationStatus: buildResearchValidationStatusField(paths.phases, phaseDirs.value, phaseDirs.scope),
1050
+ roadmapDeclaredPhases: roadmapDeclared.declared,
1051
+ roadmapPhaseCheckboxes: buildRoadmapPhaseCheckboxesField(paths.roadmap, phaseIdConvention),
1052
+ researchValidationStatus: buildResearchValidationStatusField(paths.phases, phaseDirs.value, phaseDirs.scope, phaseIdConvention),
881
1053
  milestoneArchiveStatus: buildMilestoneArchiveStatusField(cwd),
882
1054
  planningRootFiles: buildPlanningRootFilesField(cwd),
883
1055
  allPhaseDirNames,
884
- archivedPhaseTokens: buildArchivedPhaseTokensField(paths.planning),
885
- currentMilestoneRoadmapPhaseIds: buildCurrentMilestoneRoadmapPhaseIdsField(cwd, paths.roadmap),
1056
+ archivedPhaseTokens: buildArchivedPhaseTokensField(paths.planning, phaseIdConvention),
1057
+ currentMilestoneRoadmapPhaseIds: buildCurrentMilestoneRoadmapPhaseIdsField(cwd, paths.roadmap, phaseIdConvention),
886
1058
  perPhasePlanNumbering: perPhasePlanScanFields.perPhasePlanNumbering,
887
1059
  perPhaseOrphanSummaries: perPhasePlanScanFields.perPhaseOrphanSummaries,
888
1060
  perPhaseWaveMissingPlans: perPhasePlanScanFields.perPhaseWaveMissingPlans,
1061
+ phaseIdConvention,
1062
+ roadmapSentinelPhaseTokens: roadmapDeclared.sentinelTokens,
1063
+ roadmapBracketIncoherences: buildRoadmapBracketIncoherencesField(paths.roadmap, phaseIdConvention),
1064
+ workstream,
889
1065
  };
890
1066
  }
891
1067
  module.exports = {