@opengsd/gsd-core 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +85 -5
  4. package/agents/gsd-code-fixer.compact.md +1 -1
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-code-reviewer.compact.md +5 -3
  7. package/agents/gsd-code-reviewer.md +8 -6
  8. package/agents/gsd-debug-session-manager.compact.md +1 -1
  9. package/agents/gsd-debug-session-manager.md +1 -1
  10. package/agents/gsd-debugger.md +2 -2
  11. package/agents/gsd-eval-auditor.compact.md +1 -1
  12. package/agents/gsd-eval-auditor.md +1 -1
  13. package/agents/gsd-executor.md +4 -4
  14. package/agents/gsd-intel-updater.compact.md +1 -1
  15. package/agents/gsd-intel-updater.md +1 -1
  16. package/agents/gsd-mempalace-curator.md +2 -2
  17. package/agents/gsd-phase-researcher.md +1 -1
  18. package/agents/gsd-plan-checker.md +7 -2
  19. package/agents/gsd-planner.md +3 -3
  20. package/agents/gsd-project-researcher.compact.md +1 -1
  21. package/agents/gsd-project-researcher.md +1 -1
  22. package/agents/gsd-research-synthesizer.compact.md +1 -1
  23. package/agents/gsd-research-synthesizer.md +1 -1
  24. package/agents/gsd-ui-auditor.compact.md +21 -30
  25. package/agents/gsd-ui-auditor.md +42 -51
  26. package/agents/gsd-ui-researcher.compact.md +1 -1
  27. package/agents/gsd-ui-researcher.md +1 -1
  28. package/agents/gsd-verifier.md +31 -9
  29. package/bin/install.js +126 -196
  30. package/commands/gsd/add-tests.md +6 -1
  31. package/commands/gsd/ai-integration-phase.md +6 -1
  32. package/commands/gsd/audit-fix.md +5 -0
  33. package/commands/gsd/audit-milestone.md +6 -1
  34. package/commands/gsd/autonomous.md +5 -0
  35. package/commands/gsd/capture.md +8 -4
  36. package/commands/gsd/code-review.md +7 -2
  37. package/commands/gsd/complete-milestone.md +4 -0
  38. package/commands/gsd/config.md +7 -3
  39. package/commands/gsd/debug.md +11 -7
  40. package/commands/gsd/discuss-phase.md +7 -3
  41. package/commands/gsd/docs-update.md +12 -7
  42. package/commands/gsd/eval-review.md +6 -1
  43. package/commands/gsd/execute-phase.md +12 -7
  44. package/commands/gsd/extract-learnings.md +5 -0
  45. package/commands/gsd/fast.md +4 -0
  46. package/commands/gsd/forensics.md +5 -1
  47. package/commands/gsd/graphify.md +10 -6
  48. package/commands/gsd/health.md +5 -0
  49. package/commands/gsd/help.md +7 -2
  50. package/commands/gsd/import.md +7 -3
  51. package/commands/gsd/inbox.md +5 -0
  52. package/commands/gsd/ingest-docs.md +5 -1
  53. package/commands/gsd/manager.md +6 -1
  54. package/commands/gsd/map-codebase.md +7 -3
  55. package/commands/gsd/mempalace-capture.md +5 -1
  56. package/commands/gsd/mempalace-recall.md +5 -1
  57. package/commands/gsd/milestone-summary.md +5 -1
  58. package/commands/gsd/mvp-phase.md +8 -3
  59. package/commands/gsd/new-milestone.md +6 -1
  60. package/commands/gsd/new-project.md +5 -0
  61. package/commands/gsd/next.md +6 -1
  62. package/commands/gsd/ns-context.md +4 -0
  63. package/commands/gsd/ns-ideate.md +4 -0
  64. package/commands/gsd/ns-manage.md +4 -0
  65. package/commands/gsd/ns-project.md +4 -0
  66. package/commands/gsd/ns-review.md +4 -0
  67. package/commands/gsd/ns-workflow.md +4 -0
  68. package/commands/gsd/onboard.md +6 -1
  69. package/commands/gsd/pause-work.md +5 -1
  70. package/commands/gsd/phase.md +8 -4
  71. package/commands/gsd/plan-phase.md +6 -1
  72. package/commands/gsd/plan-review-convergence.md +5 -1
  73. package/commands/gsd/pr-branch.md +4 -0
  74. package/commands/gsd/profile-user.md +5 -1
  75. package/commands/gsd/progress.md +6 -1
  76. package/commands/gsd/quick-batch.md +20 -8
  77. package/commands/gsd/quick.md +12 -7
  78. package/commands/gsd/review.md +5 -1
  79. package/commands/gsd/secure-phase.md +6 -1
  80. package/commands/gsd/ship.md +5 -0
  81. package/commands/gsd/sketch.md +7 -2
  82. package/commands/gsd/spec-phase.md +5 -1
  83. package/commands/gsd/spike.md +8 -3
  84. package/commands/gsd/surface.md +5 -1
  85. package/commands/gsd/thread.md +4 -0
  86. package/commands/gsd/ui-phase.md +6 -1
  87. package/commands/gsd/ui-review.md +6 -1
  88. package/commands/gsd/ultraplan-phase.md +5 -1
  89. package/commands/gsd/undo.md +5 -1
  90. package/commands/gsd/update.md +6 -2
  91. package/commands/gsd/validate-phase.md +6 -1
  92. package/commands/gsd/verify-work.md +6 -1
  93. package/commands/gsd/workspace.md +7 -3
  94. package/gsd-core/bin/gsd-tools.cjs +142 -56
  95. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  96. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  97. package/gsd-core/bin/lib/audit.cjs +78 -44
  98. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  99. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  100. package/gsd-core/bin/lib/capability-registry.cjs +183 -103
  101. package/gsd-core/bin/lib/capability-validator.cjs +16 -0
  102. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  103. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  104. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  105. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  106. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  107. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  108. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  109. package/gsd-core/bin/lib/commands.cjs +173 -170
  110. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  111. package/gsd-core/bin/lib/config.cjs +43 -12
  112. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  113. package/gsd-core/bin/lib/coverage.cjs +4 -8
  114. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  115. package/gsd-core/bin/lib/drift.cjs +177 -42
  116. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  117. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  118. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  119. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  120. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  121. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  122. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  123. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  124. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  125. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  126. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  127. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  128. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  129. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  130. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  131. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  132. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  133. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  134. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  135. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  136. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  137. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  138. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  139. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  140. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  141. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  142. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  143. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  144. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  145. package/gsd-core/bin/lib/init.cjs +162 -96
  146. package/gsd-core/bin/lib/install-engine.cjs +19 -35
  147. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  148. package/gsd-core/bin/lib/io.cjs +122 -3
  149. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  150. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  151. package/gsd-core/bin/lib/milestone.cjs +19 -1
  152. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  153. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  154. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  155. package/gsd-core/bin/lib/pattern.cjs +10 -0
  156. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  157. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  158. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  159. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  160. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  161. package/gsd-core/bin/lib/phase.cjs +280 -80
  162. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  163. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  164. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  165. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  166. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  167. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  168. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  169. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  170. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  171. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  172. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  173. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  174. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  175. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  176. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  177. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  178. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  179. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
  180. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  181. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  182. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  183. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
  184. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  185. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  186. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  187. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  188. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  189. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  190. package/gsd-core/bin/lib/state-document.cjs +100 -22
  191. package/gsd-core/bin/lib/state.cjs +214 -105
  192. package/gsd-core/bin/lib/surface.cjs +2 -1
  193. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  194. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  195. package/gsd-core/bin/lib/uat.cjs +424 -7
  196. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  197. package/gsd-core/bin/lib/vendor/README.md +31 -9
  198. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  199. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  200. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  201. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  202. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  203. package/gsd-core/bin/lib/verification.cjs +1165 -314
  204. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  205. package/gsd-core/bin/lib/verify.cjs +286 -567
  206. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  207. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  208. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  209. package/gsd-core/bin/lib/workstream.cjs +11 -2
  210. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  211. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  212. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  213. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  214. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  215. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  216. package/gsd-core/references/execute-phase-response-language.md +1 -1
  217. package/gsd-core/references/gsd-run-resolver.md +1 -1
  218. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  219. package/gsd-core/references/offer-next.md +1 -1
  220. package/gsd-core/references/planning-config.md +1 -1
  221. package/gsd-core/references/spidr-splitting.md +1 -1
  222. package/gsd-core/references/tdd.md +10 -4
  223. package/gsd-core/references/verifier-phase-gates.md +5 -2
  224. package/gsd-core/references/verify-mvp-mode.md +2 -2
  225. package/gsd-core/references/workstream-flag.md +33 -3
  226. package/gsd-core/templates/README.md +1 -1
  227. package/gsd-core/templates/UAT.md +17 -1
  228. package/gsd-core/templates/config.json +2 -11
  229. package/gsd-core/templates/verification-report.md +1 -1
  230. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  231. package/gsd-core/workflows/add-backlog.md +1 -1
  232. package/gsd-core/workflows/add-phase.md +8 -7
  233. package/gsd-core/workflows/add-tests.md +3 -2
  234. package/gsd-core/workflows/add-todo.md +4 -3
  235. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  236. package/gsd-core/workflows/audit-fix.md +1 -1
  237. package/gsd-core/workflows/audit-milestone.md +4 -3
  238. package/gsd-core/workflows/audit-uat.md +1 -1
  239. package/gsd-core/workflows/autonomous.md +28 -18
  240. package/gsd-core/workflows/check-todos.md +6 -5
  241. package/gsd-core/workflows/cleanup.md +1 -1
  242. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  243. package/gsd-core/workflows/code-review-fix.md +5 -4
  244. package/gsd-core/workflows/code-review.md +84 -90
  245. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  246. package/gsd-core/workflows/complete-milestone.md +12 -7
  247. package/gsd-core/workflows/debug.md +30 -5
  248. package/gsd-core/workflows/diagnose-issues.md +3 -2
  249. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  250. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  252. package/gsd-core/workflows/discuss-phase.md +4 -3
  253. package/gsd-core/workflows/do.md +1 -1
  254. package/gsd-core/workflows/docs-update.md +4 -3
  255. package/gsd-core/workflows/edit-phase.md +4 -3
  256. package/gsd-core/workflows/eval-review.md +5 -3
  257. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  258. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  259. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  260. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  261. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  263. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  264. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  265. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  266. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  267. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  268. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  269. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  270. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  271. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  272. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  273. package/gsd-core/workflows/execute-phase.md +78 -170
  274. package/gsd-core/workflows/execute-plan.md +13 -19
  275. package/gsd-core/workflows/explore.md +2 -2
  276. package/gsd-core/workflows/extract-learnings.md +3 -2
  277. package/gsd-core/workflows/fast.md +1 -1
  278. package/gsd-core/workflows/forensics.md +1 -1
  279. package/gsd-core/workflows/graduation.md +1 -1
  280. package/gsd-core/workflows/health.md +2 -1
  281. package/gsd-core/workflows/import.md +3 -2
  282. package/gsd-core/workflows/inbox.md +1 -1
  283. package/gsd-core/workflows/ingest-docs.md +1 -1
  284. package/gsd-core/workflows/insert-phase.md +4 -3
  285. package/gsd-core/workflows/list-seeds.md +1 -1
  286. package/gsd-core/workflows/list-workspaces.md +1 -1
  287. package/gsd-core/workflows/manager.md +5 -3
  288. package/gsd-core/workflows/map-codebase.md +4 -3
  289. package/gsd-core/workflows/milestone-summary.md +3 -2
  290. package/gsd-core/workflows/mvp-phase.md +14 -14
  291. package/gsd-core/workflows/new-milestone.md +10 -10
  292. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  293. package/gsd-core/workflows/new-project.md +1 -1
  294. package/gsd-core/workflows/new-workspace.md +1 -1
  295. package/gsd-core/workflows/next.md +1 -1
  296. package/gsd-core/workflows/pause-work.md +2 -2
  297. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  298. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  299. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  300. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  301. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  302. package/gsd-core/workflows/plan-phase.md +27 -18
  303. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  304. package/gsd-core/workflows/plant-seed.md +1 -1
  305. package/gsd-core/workflows/pr-branch.md +1 -1
  306. package/gsd-core/workflows/profile-user.md +1 -1
  307. package/gsd-core/workflows/progress.md +19 -49
  308. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  309. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  310. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  311. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  312. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  313. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  314. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  315. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  316. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  317. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  318. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  319. package/gsd-core/workflows/quick-batch.md +15 -10
  320. package/gsd-core/workflows/quick.md +42 -31
  321. package/gsd-core/workflows/remove-phase.md +3 -2
  322. package/gsd-core/workflows/remove-workspace.md +1 -1
  323. package/gsd-core/workflows/resume-project.md +3 -2
  324. package/gsd-core/workflows/review.md +3 -2
  325. package/gsd-core/workflows/scan.md +3 -2
  326. package/gsd-core/workflows/secure-phase.md +11 -12
  327. package/gsd-core/workflows/settings-advanced.md +1 -1
  328. package/gsd-core/workflows/settings-integrations.md +1 -1
  329. package/gsd-core/workflows/settings.md +1 -1
  330. package/gsd-core/workflows/ship.md +12 -12
  331. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  332. package/gsd-core/workflows/sketch.md +1 -1
  333. package/gsd-core/workflows/smart-entry.md +1 -1
  334. package/gsd-core/workflows/spec-phase.md +1 -1
  335. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  336. package/gsd-core/workflows/spike.md +1 -1
  337. package/gsd-core/workflows/stats.md +1 -1
  338. package/gsd-core/workflows/sync-skills.md +1 -1
  339. package/gsd-core/workflows/thread.md +2 -2
  340. package/gsd-core/workflows/transition.md +13 -23
  341. package/gsd-core/workflows/ui-phase.md +5 -4
  342. package/gsd-core/workflows/ui-review.md +4 -3
  343. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  344. package/gsd-core/workflows/undo.md +1 -1
  345. package/gsd-core/workflows/validate-phase.md +10 -12
  346. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  347. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  348. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  349. package/gsd-core/workflows/verify-work.md +68 -59
  350. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  351. package/hooks/dist/gsd-check-update-worker.js +8 -0
  352. package/hooks/dist/gsd-check-update.js +8 -0
  353. package/hooks/dist/gsd-context-monitor.js +31 -8
  354. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  355. package/hooks/dist/gsd-secret-read-guard.js +147 -16
  356. package/hooks/dist/gsd-statusline.js +15 -7
  357. package/hooks/dist/gsd-update-banner.js +8 -0
  358. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  359. package/hooks/dist/gsd-workflow-guard.js +5 -4
  360. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  361. package/hooks/dist/lib/cli-exit.js +12 -0
  362. package/hooks/dist/lib/git-probe.js +17 -1
  363. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  364. package/hooks/gsd-agent-isolation-guard.js +8 -0
  365. package/hooks/gsd-check-update-worker.js +8 -0
  366. package/hooks/gsd-check-update.js +8 -0
  367. package/hooks/gsd-context-monitor.js +31 -8
  368. package/hooks/gsd-cursor-subagent-start.js +8 -0
  369. package/hooks/gsd-secret-read-guard.js +147 -16
  370. package/hooks/gsd-statusline.js +15 -7
  371. package/hooks/gsd-update-banner.js +8 -0
  372. package/hooks/gsd-windsurf-pre-write.js +11 -2
  373. package/hooks/gsd-workflow-guard.js +5 -4
  374. package/hooks/gsd-worktree-path-guard.js +6 -2
  375. package/hooks/hooks.json +5 -5
  376. package/hooks/lib/cli-exit.js +12 -0
  377. package/hooks/lib/git-probe.js +17 -1
  378. package/hooks/lib/isolation-sentinel.js +2 -2
  379. package/package.json +21 -4
  380. package/scripts/changeset/parse.cjs +52 -4
  381. package/scripts/ci-timeout-report.cjs +770 -4
  382. package/scripts/command-contract-helpers.cjs +12 -8
  383. package/scripts/docs-guard-registry.cjs +6 -0
  384. package/scripts/gen-features.cjs +13 -8
  385. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  386. package/scripts/gen-loop-host-contract.cjs +10 -1
  387. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  388. package/scripts/gen-plugin-skills.cjs +87 -1
  389. package/scripts/gen-research-agents.cjs +24 -31
  390. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  391. package/scripts/gen-test-timings.cjs +32 -7
  392. package/scripts/lib/cli-exit.cjs +12 -0
  393. package/scripts/lib/macos-conformance-tier.generated.cjs +20 -2
  394. package/scripts/lib/ndjson-reporter.cjs +28 -3
  395. package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
  396. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  397. package/scripts/lib/vendor-bundle.cjs +59 -0
  398. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  399. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  400. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  401. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  402. package/scripts/lint-descriptions.cjs +7 -3
  403. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  404. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  405. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  406. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  407. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  408. package/scripts/lint-phase-id-drift.cjs +29 -0
  409. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  410. package/scripts/lint-response-language-coverage.cjs +3 -0
  411. package/scripts/lint-skill-deps.cjs +7 -3
  412. package/scripts/lint-test-file-count.allowlist.json +11 -1
  413. package/scripts/lint-test-file-count.cjs +34 -1
  414. package/scripts/lint-vendored-deps.cjs +41 -5
  415. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  416. package/scripts/mutation-matrix.cjs +50 -5
  417. package/scripts/require-issue-link-policy.cjs +6 -2
  418. package/scripts/sync-runtime-launcher.cjs +184 -2
  419. package/scripts/verify-npm-publish.cjs +76 -20
  420. package/skills/gsd-add-tests/SKILL.md +6 -1
  421. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  422. package/skills/gsd-audit-fix/SKILL.md +5 -0
  423. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  424. package/skills/gsd-autonomous/SKILL.md +5 -0
  425. package/skills/gsd-capture/SKILL.md +8 -4
  426. package/skills/gsd-code-review/SKILL.md +7 -2
  427. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  428. package/skills/gsd-config/SKILL.md +8 -4
  429. package/skills/gsd-debug/SKILL.md +11 -7
  430. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  431. package/skills/gsd-docs-update/SKILL.md +12 -7
  432. package/skills/gsd-eval-review/SKILL.md +6 -1
  433. package/skills/gsd-execute-phase/SKILL.md +12 -7
  434. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  435. package/skills/gsd-fast/SKILL.md +4 -0
  436. package/skills/gsd-forensics/SKILL.md +5 -1
  437. package/skills/gsd-graphify/SKILL.md +10 -6
  438. package/skills/gsd-health/SKILL.md +5 -0
  439. package/skills/gsd-help/SKILL.md +7 -2
  440. package/skills/gsd-import/SKILL.md +7 -3
  441. package/skills/gsd-inbox/SKILL.md +5 -0
  442. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  443. package/skills/gsd-manager/SKILL.md +6 -1
  444. package/skills/gsd-map-codebase/SKILL.md +7 -3
  445. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  446. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  447. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  448. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  449. package/skills/gsd-new-milestone/SKILL.md +6 -1
  450. package/skills/gsd-new-project/SKILL.md +5 -0
  451. package/skills/gsd-next/SKILL.md +6 -1
  452. package/skills/gsd-ns-context/SKILL.md +4 -0
  453. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  454. package/skills/gsd-ns-manage/SKILL.md +4 -0
  455. package/skills/gsd-ns-project/SKILL.md +4 -0
  456. package/skills/gsd-ns-review/SKILL.md +4 -0
  457. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  458. package/skills/gsd-onboard/SKILL.md +6 -1
  459. package/skills/gsd-pause-work/SKILL.md +5 -1
  460. package/skills/gsd-phase/SKILL.md +8 -4
  461. package/skills/gsd-plan-phase/SKILL.md +6 -1
  462. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  463. package/skills/gsd-pr-branch/SKILL.md +4 -0
  464. package/skills/gsd-profile-user/SKILL.md +5 -1
  465. package/skills/gsd-progress/SKILL.md +6 -1
  466. package/skills/gsd-quick/SKILL.md +16 -10
  467. package/skills/gsd-quick-batch/SKILL.md +20 -8
  468. package/skills/gsd-review/SKILL.md +5 -1
  469. package/skills/gsd-review-backlog/SKILL.md +3 -2
  470. package/skills/gsd-secure-phase/SKILL.md +6 -1
  471. package/skills/gsd-ship/SKILL.md +5 -0
  472. package/skills/gsd-sketch/SKILL.md +7 -2
  473. package/skills/gsd-spec-phase/SKILL.md +5 -1
  474. package/skills/gsd-spike/SKILL.md +8 -3
  475. package/skills/gsd-surface/SKILL.md +5 -1
  476. package/skills/gsd-thread/SKILL.md +4 -0
  477. package/skills/gsd-ui-phase/SKILL.md +6 -1
  478. package/skills/gsd-ui-review/SKILL.md +6 -1
  479. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  480. package/skills/gsd-undo/SKILL.md +5 -1
  481. package/skills/gsd-update/SKILL.md +6 -2
  482. package/skills/gsd-validate-phase/SKILL.md +6 -1
  483. package/skills/gsd-verify-work/SKILL.md +6 -1
  484. package/skills/gsd-workspace/SKILL.md +7 -3
  485. package/skills/gsd-workstreams/SKILL.md +6 -6
  486. package/vscode/package.json +1 -1
  487. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
@@ -17,10 +17,22 @@
17
17
  * `passed+gaps_found+human_needed` instead of `passed` and blocking a
18
18
  * passed phase).
19
19
  *
20
- * The fix-forward is to scope the grep to the leading frontmatter block and
21
- * take only the first match:
20
+ * The fix-forward for a scalar with no owning query is to scope the grep to
21
+ * the leading frontmatter block and take only the first match:
22
22
  * sed -n '/^---$/,/^---$/p' "$f" | grep -m1 "^<key>:" | cut -d: -f2 | tr -d ' '
23
23
  *
24
+ * ## Rule `verification-status-raw-read` (#5118, ADR-5057 Phase 4)
25
+ *
26
+ * A VERIFICATION report's `status` HAS an owning query — its closed enum,
27
+ * staleness check and routing live in src/verification.cts. Reading that
28
+ * scalar with awk / sed / grep `^status:` (scoped to the frontmatter or not)
29
+ * bypasses all three (transition.md's awk read was W15 in the #5118 census),
30
+ * so it is red even when frontmatter-scoped. The fix-forward is the owner:
31
+ * gsd_run query verification.status "$PHASE_DIR" --pick status
32
+ * `findRawVerificationStatusReadsInBlock` flags a logical line (backslash
33
+ * continuations joined) that names a `*VERIFICATION*` path or variable and
34
+ * reads `^status` with awk, sed or grep.
35
+ *
24
36
  * ## What this scans
25
37
  *
26
38
  * Every fenced ```bash / ```sh code block in `gsd-core/workflows/*.md`,
@@ -68,6 +80,17 @@ const FRONTMATTER_SCOPE_RE = /\^---[\s\S]{0,300}?---/;
68
80
 
69
81
  const ALLOW_RE = /#\s*lint-allow:\s*frontmatter-scalar-broad-grep/;
70
82
 
83
+ const RULE_BROAD_GREP = 'frontmatter-scalar-broad-grep';
84
+ const RULE_VERIFICATION_RAW_READ = 'verification-status-raw-read';
85
+
86
+ // #5118: a VERIFICATION report path or variable (`*-VERIFICATION.md`,
87
+ // `$VERIFICATION_FILE`, `${VERIFICATION_PATH}`), a raw `^status` pattern, and
88
+ // the text tool doing the read. Case-INsensitive (#5118 review): a lower-case
89
+ // `$verification_file` / `*-verification.md` read is the same bypass.
90
+ const VERIFICATION_PATH_RE = /verification/i;
91
+ const RAW_STATUS_PATTERN_RE = /\^status\b/;
92
+ const RAW_READ_TOOL_RE = /(^|[\s|;&(`$])(awk|gawk|sed|grep|egrep)\s/;
93
+
71
94
  // `| head -1` / `| head -n 1` immediately after the grep is functionally
72
95
  // equivalent to `-m1` for this check: frontmatter always precedes the body
73
96
  // in these generated reports, so the first grep match is always the
@@ -163,12 +186,63 @@ function findBroadGrepsInBlock(lines) {
163
186
  }
164
187
 
165
188
  if (pipedToTokenTool || comparedLater) {
166
- findings.push({ lineIndex: i, key, snippet: line.trim() });
189
+ findings.push({ lineIndex: i, key, snippet: line.trim(), rule: RULE_BROAD_GREP });
167
190
  }
168
191
  }
169
192
  return findings;
170
193
  }
171
194
 
195
+ /**
196
+ * Join backslash-continued physical lines into logical lines, remembering the
197
+ * index of each logical line's FIRST physical line.
198
+ * @param {string[]} lines
199
+ * @returns {{ lineIndex: number, text: string }[]}
200
+ */
201
+ function logicalLines(lines) {
202
+ const out = [];
203
+ let current = null;
204
+ for (let i = 0; i < lines.length; i += 1) {
205
+ const line = lines[i];
206
+ const continued = /\\\s*$/.test(line);
207
+ const piece = continued ? line.replace(/\\\s*$/, ' ') : line;
208
+ if (current === null) current = { lineIndex: i, text: piece };
209
+ else current.text += piece;
210
+ if (!continued) {
211
+ out.push(current);
212
+ current = null;
213
+ }
214
+ }
215
+ if (current !== null) out.push(current);
216
+ return out;
217
+ }
218
+
219
+ /**
220
+ * Pure (#5118): find every raw read of a VERIFICATION report's `status` in a
221
+ * single fenced bash/sh block — awk / sed / grep over a `*VERIFICATION*`
222
+ * path with a `^status` pattern, whether or not it is frontmatter-scoped.
223
+ * The owner query (`gsd_run query verification.status …`) carries no
224
+ * `^status` pattern and is never flagged. Returns
225
+ * `{ lineIndex, key, snippet, rule }[]` (lineIndex = the logical line's first
226
+ * physical line).
227
+ * @param {string[]} lines
228
+ * @returns {{ lineIndex: number, key: string, snippet: string, rule: string }[]}
229
+ */
230
+ function findRawVerificationStatusReadsInBlock(lines) {
231
+ const findings = [];
232
+ for (const { lineIndex, text } of logicalLines(lines)) {
233
+ if (!VERIFICATION_PATH_RE.test(text)) continue;
234
+ if (!RAW_STATUS_PATTERN_RE.test(text)) continue;
235
+ if (!RAW_READ_TOOL_RE.test(text)) continue;
236
+ findings.push({
237
+ lineIndex,
238
+ key: 'status',
239
+ snippet: text.replace(/\s+/g, ' ').trim(),
240
+ rule: RULE_VERIFICATION_RAW_READ,
241
+ });
242
+ }
243
+ return findings;
244
+ }
245
+
172
246
  function walkMarkdown(dir) {
173
247
  const out = [];
174
248
  let entries;
@@ -198,12 +272,18 @@ function scan(roots = DEFAULT_ROOTS) {
198
272
  for (const file of walkMarkdown(abs)) {
199
273
  const blocks = extractBashBlocks(fs.readFileSync(file, 'utf8'));
200
274
  for (const block of blocks) {
201
- for (const finding of findBroadGrepsInBlock(block.lines)) {
275
+ const rawReads = findRawVerificationStatusReadsInBlock(block.lines);
276
+ const rawReadLines = new Set(rawReads.map((f) => f.lineIndex));
277
+ // A raw VERIFICATION `status` read is reported once, under its own
278
+ // rule, not a second time as a broad grep.
279
+ const broad = findBroadGrepsInBlock(block.lines).filter((f) => !rawReadLines.has(f.lineIndex));
280
+ for (const finding of [...rawReads, ...broad]) {
202
281
  offenders.push({
203
282
  file: path.relative(ROOT, file),
204
283
  line: block.startLine + finding.lineIndex,
205
284
  key: finding.key,
206
285
  snippet: finding.snippet,
286
+ rule: finding.rule,
207
287
  });
208
288
  }
209
289
  }
@@ -217,21 +297,47 @@ function main() {
217
297
  const roots = rootsEnv ? rootsEnv.split(path.delimiter).filter(Boolean) : DEFAULT_ROOTS;
218
298
  const offenders = scan(roots);
219
299
  if (offenders.length > 0) {
220
- const detail = offenders.map((o) => ` ${o.file}:${o.line} ${o.snippet}`).join('\n');
221
- throw new ExitError(
222
- 1,
223
- 'lint-frontmatter-scalar-broad-grep: `grep "^key:"` over the whole file, compared to an\n'
224
- + 'exact token, with no frontmatter scoping and no -m1 (DEFECT.FRONTMATTER-SCALAR-BROAD-GREP).\n'
225
- + 'A body line beginning `key:` is enough to break this. Scope to the frontmatter block:\n'
226
- + ' sed -n \'/^---$/,/^---$/p\' "$f" | grep -m1 "^<key>:" | cut -d: -f2 | tr -d \' \'\n'
227
- + 'or add `# lint-allow: frontmatter-scalar-broad-grep — <reason>` if this is a genuine\n'
228
- + 'whole-body scan:\n'
229
- + detail,
230
- );
300
+ const detailFor = (rule) => offenders
301
+ .filter((o) => o.rule === rule)
302
+ .map((o) => ` ${o.file}:${o.line} ${o.snippet}`)
303
+ .join('\n');
304
+ const sections = [];
305
+ const rawReads = detailFor(RULE_VERIFICATION_RAW_READ);
306
+ if (rawReads) {
307
+ sections.push(
308
+ `[${RULE_VERIFICATION_RAW_READ}] a VERIFICATION report's \`status\` read with awk/sed/grep\n`
309
+ + '(DEFECT.FRONTMATTER-SCALAR-BROAD-GREP, #5118). It bypasses the closed VerificationStatus\n'
310
+ + 'enum, the staleness check and the routing table. Read it through its owner:\n'
311
+ + ' gsd_run query verification.status "$PHASE_DIR" --pick status\n'
312
+ + rawReads,
313
+ );
314
+ }
315
+ const broad = detailFor(RULE_BROAD_GREP);
316
+ if (broad) {
317
+ sections.push(
318
+ `[${RULE_BROAD_GREP}] \`grep "^key:"\` over the whole file, compared to an exact token,\n`
319
+ + 'with no frontmatter scoping and no -m1 (DEFECT.FRONTMATTER-SCALAR-BROAD-GREP).\n'
320
+ + 'A body line beginning `key:` is enough to break this. Scope a scalar that has no owning\n'
321
+ + 'query to the frontmatter block (a VERIFICATION `status` always goes through its owner):\n'
322
+ + ' sed -n \'/^---$/,/^---$/p\' "$f" | grep -m1 "^<key>:" | cut -d: -f2 | tr -d \' \'\n'
323
+ + 'or add `# lint-allow: frontmatter-scalar-broad-grep — <reason>` if this is a genuine\n'
324
+ + 'whole-body scan:\n'
325
+ + broad,
326
+ );
327
+ }
328
+ throw new ExitError(1, `lint-frontmatter-scalar-broad-grep:\n${sections.join('\n')}`);
231
329
  }
232
330
  console.log(`ok lint-frontmatter-scalar-broad-grep: no un-scoped frontmatter-scalar greps in ${roots.length} root(s)`);
233
331
  }
234
332
 
235
- module.exports = { findBroadGrepsInBlock, extractBashBlocks, scan, DEFAULT_ROOTS };
333
+ module.exports = {
334
+ findBroadGrepsInBlock,
335
+ findRawVerificationStatusReadsInBlock,
336
+ extractBashBlocks,
337
+ scan,
338
+ DEFAULT_ROOTS,
339
+ RULE_BROAD_GREP,
340
+ RULE_VERIFICATION_RAW_READ,
341
+ };
236
342
 
237
343
  if (require.main === module) runMain(main);
@@ -84,13 +84,16 @@
84
84
  * shape as `collectDiskPhases` and the `audit.cts` scanners; it must see
85
85
  * every phase directory regardless of milestone window to catch a
86
86
  * naming/duplicate defect wherever it lives.
87
- * - `src/verify.cts` `resolvePhaseDirByToken`: resolves ONE caller-supplied
88
- * `phase` argument to its directory (falling back to an exact-name
89
- * match) — a single-phase LOOKUP, not a current-milestone enumeration.
90
- * Originally `cmdVerifySchemaDrift`'s own inline block; #3348 lifted it
91
- * into this shared helper (also used by the new `cmdVerifyContextDrift`)
92
- * without changing what question it asks, so the exemption moved with
93
- * the call site rather than multiplying.
87
+ * - `src/gate-phase-context.cts` `resolvePhaseDirByToken`: resolves ONE
88
+ * caller-supplied `phase` argument to its directory (falling back to an
89
+ * exact-name match) — a single-phase LOOKUP, not a current-milestone
90
+ * enumeration. Originally `cmdVerifySchemaDrift`'s own inline block;
91
+ * #3348 lifted it into this shared helper (also used by the context-drift
92
+ * check) without changing what question it asks, so the exemption moved
93
+ * with the call site rather than multiplying. #5219 (ADR-5057 §4) moved
94
+ * both checks into gate modules and the helper beside `resolvePhaseDir`
95
+ * in the gate phase-context module (it is not `resolvePhaseDir`: it
96
+ * lists ONE given phases directory and nothing else).
94
97
  * - `src/init.cts` `detectHasPriorPhases`: answers "has this project EVER
95
98
  * completed a phase", explicitly excluding the current one. A history
96
99
  * probe across all milestones, not a current-milestone enumeration.
@@ -309,7 +312,8 @@ const OWNER_FILES = new Set([
309
312
  // `lint-milestone-window-drift.cjs`'s FUNCTION_SCOPED_EXEMPTIONS mechanism.
310
313
  // See the header comment for the full written reason behind each entry.
311
314
  const FUNCTION_SCOPED_EXEMPTIONS = new Map([
312
- [path.join('src', 'verify.cts'), new Set(['cmdValidateHealth', 'resolvePhaseDirByToken'])],
315
+ [path.join('src', 'verify.cts'), new Set(['cmdValidateHealth'])],
316
+ [path.join('src', 'gate-phase-context.cts'), new Set(['resolvePhaseDirByToken'])],
313
317
  [path.join('src', 'init.cts'), new Set(['detectHasPriorPhases', 'detectUiPhaseActive'])],
314
318
  [path.join('src', 'milestone.cts'), new Set(['archivePhaseDirectories', 'cmdMilestoneComplete', 'cmdPhasesClear'])],
315
319
  // #3849: collectSiblingWorktreePhaseNums reads a SIBLING worktree's phases dir —
@@ -283,6 +283,34 @@ function findPhaseHeadingScanLiteralDrift(text) {
283
283
  return out;
284
284
  }
285
285
 
286
+ /**
287
+ * Scan the authored source tree (`src/**\/*.cts`, the same `SCAN_DIRS` root
288
+ * and `phase-id.cts` owner exemption that `scanRepo` applies to this
289
+ * detector — the heading-scan literal is a `src/**\/*.cts` defect, not a
290
+ * markdown one, unlike its sibling wrappers below) for unsanctioned
291
+ * hand-rolled heading-scan literals. Returns [{ file, line, found }] with
292
+ * repo-relative paths.
293
+ */
294
+ function scanMarkdownPhaseHeadingScanLiteral(root) {
295
+ const violations = [];
296
+ for (const dir of SCAN_DIRS) {
297
+ for (const file of walk(path.join(root, dir), [])) {
298
+ const rel = path.relative(root, file);
299
+ if (EXEMPT.has(rel)) continue;
300
+ let text;
301
+ try {
302
+ text = fs.readFileSync(file, 'utf8');
303
+ } catch {
304
+ continue;
305
+ }
306
+ for (const d of findPhaseHeadingScanLiteralDrift(text)) {
307
+ violations.push({ file: rel, kind: 'phase-heading-scan-literal', ...d });
308
+ }
309
+ }
310
+ }
311
+ return violations;
312
+ }
313
+
286
314
  // #4634: ban base-10-forced shell arithmetic (`$((10#...))`) on a variable
287
315
  // that still carries a possibly-decimal/multi-segment phase id — this
288
316
  // construct is exactly the pattern that breaks on a value like `08.5`. The
@@ -857,6 +885,7 @@ module.exports = {
857
885
  findNameValidityDrift,
858
886
  findBranchSlugFallbackDrift,
859
887
  findPhaseHeadingScanLiteralDrift,
888
+ scanMarkdownPhaseHeadingScanLiteral,
860
889
  findShellPhaseArithDrift,
861
890
  findSingleSegmentPhaseRegexDrift,
862
891
  findLetterlessPhaseMirrorDrift,
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * PlanningDoc positive-control lint (ADR-4910 §7, "Every parser ships a
6
+ * positive control per accepted grammar", epic #4906 Phase 6 / #5007).
7
+ *
8
+ * `NodeKind` (`src/planning-document.cts`, compiled to
9
+ * `gsd-core/bin/lib/planning-document.cjs`) is the single-source union naming
10
+ * every grammar `parsePlanningDoc` accepts: `frontmatter` / `section` /
11
+ * `boldField` / `table` / `checklist`. This lint asserts one positive-control
12
+ * fixture per declared grammar, under `tests/fixtures/planning-document/`,
13
+ * each of which must round-trip through the real `parsePlanningDoc` and
14
+ * actually produce a node of that kind — not merely "the parser didn't
15
+ * throw". Per CONTRIBUTING.md's fixture-provenance rule (#2371), every
16
+ * fixture here is authored independently, never copied from
17
+ * `src/planning-document.cts`'s own docstring examples or from
18
+ * `tests/planning-document.test.cjs`'s existing fixtures.
19
+ *
20
+ * The `accepted` set (`NodeKind`) is a TypeScript-only type, erased by
21
+ * compilation — it carries no runtime value in the built `.cjs` seam. So this
22
+ * lint reads it directly out of `src/planning-document.cts`'s own source
23
+ * text (the `export type NodeKind = ...;` declaration), the same way
24
+ * `scripts/lint-phase-id-drift.cjs` reads source text for its own literal
25
+ * checks — NOT by importing a type, which is impossible from a `.cjs`
26
+ * script. This is what makes the guarantee live: a NEW grammar kind added to
27
+ * `NodeKind` with no registry entry fails this lint immediately, before any
28
+ * fixture is even written, closing the "parser without one fails the build"
29
+ * requirement in ADR-4910 §7.
30
+ *
31
+ * Modeled on `scripts/lint-table-schema-drift.cjs`'s registry/exit-code/
32
+ * error-message structure — a hardcoded `{grammar -> fixture file}` map,
33
+ * exit 0 clean / exit 1 with a per-entry reason on drift, wired into
34
+ * `lint:ci` — but the ASSERTION is stricter than that script's verbatim-
35
+ * substring check: proof the grammar is genuinely *accepted* (the fixture
36
+ * parses without error and yields a node of that kind), per ADR-4910 §7's
37
+ * own text, not just "a string appears somewhere".
38
+ */
39
+
40
+ const fs = require('node:fs');
41
+ const path = require('node:path');
42
+
43
+ const PLANNING_DOCUMENT_SRC = path.join('src', 'planning-document.cts');
44
+ const PLANNING_DOCUMENT_LIB = path.join('gsd-core', 'bin', 'lib', 'planning-document.cjs');
45
+ const FIXTURE_DIR = path.join('tests', 'fixtures', 'planning-document');
46
+
47
+ // grammar kind (must match a `NodeKind` union member verbatim) -> the ONE
48
+ // canonical positive-control fixture file (relative to FIXTURE_DIR) that
49
+ // proves that grammar is accepted (ADR-4910 §7).
50
+ const POSITIVE_CONTROL_REGISTRY = {
51
+ frontmatter: 'frontmatter.md',
52
+ section: 'section.md',
53
+ boldField: 'boldfield.md',
54
+ table: 'table.md',
55
+ checklist: 'checklist.md',
56
+ };
57
+
58
+ // Any real entry from `PLANNING_ARTIFACTS` satisfies `parsePlanningDoc`'s
59
+ // artifact-kind gate equally — the specific name does not affect which
60
+ // grammars a fixture's BODY is recognised as containing.
61
+ const FIXTURE_ARTIFACT_NAME = 'ROADMAP.md';
62
+
63
+ // Test-only env var name. `resolveRegistry` below JSON-parses this (when set)
64
+ // and shallow-merges it over `POSITIVE_CONTROL_REGISTRY`, so a test can spawn
65
+ // THIS SAME, otherwise-unmodified, script file as a real child process
66
+ // against a deliberately broken registry entry (a missing entry, or one
67
+ // pointed at a nonexistent fixture) — proving the lint genuinely fails the
68
+ // build, per ADR-4910 §7's "a parser without one fails the build" guarantee
69
+ // — without ever touching the real registry or fixture files.
70
+ //
71
+ // This env var alone is NEVER sufficient to activate the override — a bare
72
+ // `process.env` lookup would mean a stray, accidentally-inherited copy of
73
+ // this variable (a leftover shell `export`, a misconfigured CI environment,
74
+ // a compromised dependency's postinstall script) could silently swap the
75
+ // registry in a REAL `npm run lint:ci` / `npm run lint:planning-document-
76
+ // positive-control` invocation, with nothing in the code enforcing otherwise
77
+ // (flagged by both the security review and the code-review Standards axis
78
+ // on #5007 Phase 6: the old comment asserted "no real invocation ever sets
79
+ // this" as a convention, not a fact the code made true).
80
+ //
81
+ // What now makes it true, by construction: `scanRepo` only reads this env
82
+ // var via `resolveRegistry` when its caller ALSO passes `allowOverride ===
83
+ // true`, which `main()` only does when the `--test-registry-override` CLI
84
+ // flag is present in argv (see `parseArgs`). The test file spawns this
85
+ // script as a real child process (`tests/helpers/process-seam.cjs`'s
86
+ // `runNode`, over `spawnSync`) — a process boundary that can only carry data
87
+ // in via `env`/`argv`, so the override cannot be passed as an in-process
88
+ // function parameter here the way it can be for the pure functions below.
89
+ // Requiring BOTH the env var and the CLI flag closes the gap: neither
90
+ // `lint:ci` nor `lint:planning-document-positive-control` (see package.json)
91
+ // ever passes `--test-registry-override` in their fixed argv, and unlike an
92
+ // env var, a CLI flag is never accidentally "leaked" into a fixed npm-script
93
+ // invocation. See tests/lint-planning-document-positive-control.test.cjs.
94
+ const TEST_REGISTRY_OVERRIDE_ENV_VAR = 'LINT_PLANNING_DOCUMENT_POSITIVE_CONTROL_TEST_REGISTRY_OVERRIDE';
95
+
96
+ /** CLI flag gate for the test-only override (see `TEST_REGISTRY_OVERRIDE_ENV_VAR`
97
+ * above). Must be passed explicitly in argv — never inferred from env — for
98
+ * `resolveRegistry`'s env-var override to be honored at all. */
99
+ const TEST_REGISTRY_OVERRIDE_CLI_FLAG = '--test-registry-override';
100
+
101
+ /** Resolve the registry to scan against: the real hardcoded registry, unless
102
+ * BOTH `allowOverride` is `true` (only ever set by the
103
+ * `--test-registry-override` CLI flag, see `TEST_REGISTRY_OVERRIDE_CLI_FLAG`)
104
+ * AND the test-only override env var is set and parses as JSON, in which
105
+ * case its keys are shallow-merged on top (see
106
+ * `TEST_REGISTRY_OVERRIDE_ENV_VAR`). Without `allowOverride === true`, the
107
+ * env var is never even read. */
108
+ function resolveRegistry(env, allowOverride) {
109
+ if (!allowOverride) return POSITIVE_CONTROL_REGISTRY;
110
+ const override = env[TEST_REGISTRY_OVERRIDE_ENV_VAR];
111
+ if (!override) return POSITIVE_CONTROL_REGISTRY;
112
+ try {
113
+ const parsed = JSON.parse(override);
114
+ return { ...POSITIVE_CONTROL_REGISTRY, ...parsed };
115
+ } catch {
116
+ return POSITIVE_CONTROL_REGISTRY;
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Extract the live `NodeKind` union members from `planning-document.cts`'s
122
+ * own source text — the "accepted grammars" set (ADR-4910 §4a) — read
123
+ * directly from source (never from the compiled seam, which erases the TS
124
+ * type) so a newly added grammar kind is detected even before any registry
125
+ * entry or fixture exists for it.
126
+ *
127
+ * Pure: takes a `readFile(relPath) -> string|null` accessor so tests can
128
+ * inject a scratch source file without touching the real repo tree.
129
+ */
130
+ function readLiveGrammarKinds(readFile) {
131
+ const text = readFile(PLANNING_DOCUMENT_SRC);
132
+ if (text == null) {
133
+ return { ok: false, reason: `cannot read ${PLANNING_DOCUMENT_SRC}` };
134
+ }
135
+ const m = /export type NodeKind\s*=\s*([^;]+);/.exec(text);
136
+ if (!m) {
137
+ return {
138
+ ok: false,
139
+ reason: `could not find 'export type NodeKind = ...;' in ${PLANNING_DOCUMENT_SRC} — has the type been renamed or removed?`,
140
+ };
141
+ }
142
+ const kinds = [...m[1].matchAll(/'([^']+)'/g)].map((mm) => mm[1]);
143
+ if (kinds.length === 0) {
144
+ return {
145
+ ok: false,
146
+ reason: `the 'NodeKind' union in ${PLANNING_DOCUMENT_SRC} matched but contained no string-literal members`,
147
+ };
148
+ }
149
+ return { ok: true, value: kinds };
150
+ }
151
+
152
+ /**
153
+ * Pure: given the live grammar-kind list, the hardcoded registry, a
154
+ * `readFixture(fileName) -> string|null` accessor, the real `parsePlanningDoc`
155
+ * function, and the artifact name to parse fixtures as, find every gap.
156
+ * Returns `[{ kind, fixture?, reason }]`; empty when clean.
157
+ *
158
+ * Checks, per live grammar kind:
159
+ * 1. a registry entry exists at all (the §7 "parser without one fails the
160
+ * build" guarantee — this fires even with an EMPTY registry);
161
+ * 2. the registered fixture file exists and is readable;
162
+ * 3. the fixture parses successfully via the real parser;
163
+ * 4. the parsed document actually contains a node of that kind (proof the
164
+ * grammar was recognised, not just "didn't throw").
165
+ * Also flags a registry entry naming a grammar the parser no longer
166
+ * declares (a stale entry), so the registry cannot silently drift wider
167
+ * than the parser either.
168
+ */
169
+ function findPositiveControlGaps(liveKinds, registry, readFixture, parsePlanningDocFn, artifactName) {
170
+ const violations = [];
171
+
172
+ for (const kind of liveKinds) {
173
+ const fixtureName = registry[kind];
174
+ if (!fixtureName) {
175
+ violations.push({
176
+ kind,
177
+ reason:
178
+ `parser declares grammar '${kind}' (NodeKind) but POSITIVE_CONTROL_REGISTRY has no entry for it — ` +
179
+ 'add one and a matching fixture under tests/fixtures/planning-document/',
180
+ });
181
+ continue;
182
+ }
183
+
184
+ const content = readFixture(fixtureName);
185
+ if (content == null) {
186
+ violations.push({
187
+ kind,
188
+ fixture: fixtureName,
189
+ reason: `fixture file not found or unreadable: ${path.join(FIXTURE_DIR, fixtureName)}`,
190
+ });
191
+ continue;
192
+ }
193
+
194
+ const result = parsePlanningDocFn(content, artifactName);
195
+ if (!result.ok) {
196
+ violations.push({
197
+ kind,
198
+ fixture: fixtureName,
199
+ reason: `fixture failed to parse via parsePlanningDoc: ${result.reason}`,
200
+ });
201
+ continue;
202
+ }
203
+
204
+ const hasKind = result.value.nodes.some((n) => n.kind === kind);
205
+ if (!hasKind) {
206
+ violations.push({
207
+ kind,
208
+ fixture: fixtureName,
209
+ reason: `fixture parsed successfully but produced no '${kind}' node — it does not actually exercise this grammar`,
210
+ });
211
+ }
212
+ }
213
+
214
+ for (const kind of Object.keys(registry)) {
215
+ if (!liveKinds.includes(kind)) {
216
+ violations.push({
217
+ kind,
218
+ fixture: registry[kind],
219
+ reason: `registry entry names grammar '${kind}', which is not in the parser's live NodeKind union — stale entry, remove it`,
220
+ });
221
+ }
222
+ }
223
+
224
+ return violations;
225
+ }
226
+
227
+ /**
228
+ * Load the built seam and the real source/fixture tree rooted at `root`, and
229
+ * run `findPositiveControlGaps` against them. Returns the same shape as
230
+ * `findPositiveControlGaps`. If the seam hasn't been built yet, or the
231
+ * `NodeKind` union can't be located, reports a single actionable violation
232
+ * rather than throwing.
233
+ *
234
+ * `allowOverride` (default `false`) must be explicitly `true` for the
235
+ * test-only env-var registry override to be honored at all — see
236
+ * `resolveRegistry` and `TEST_REGISTRY_OVERRIDE_ENV_VAR`'s header comment.
237
+ */
238
+ function scanRepo(root, env, allowOverride = false) {
239
+ const readFile = (relPath) => {
240
+ try {
241
+ return fs.readFileSync(path.join(root, relPath), 'utf8');
242
+ } catch {
243
+ return null;
244
+ }
245
+ };
246
+ const readFixture = (fileName) => readFile(path.join(FIXTURE_DIR, fileName));
247
+
248
+ const live = readLiveGrammarKinds(readFile);
249
+ if (!live.ok) {
250
+ return [{ kind: null, reason: live.reason }];
251
+ }
252
+
253
+ const libPath = path.join(root, PLANNING_DOCUMENT_LIB);
254
+ let seam;
255
+ try {
256
+ seam = require(libPath);
257
+ } catch (e) {
258
+ return [
259
+ {
260
+ kind: null,
261
+ reason: `cannot load the planning-document seam at ${path.relative(root, libPath)} — run 'npm run build:lib' first (${e.message})`,
262
+ },
263
+ ];
264
+ }
265
+
266
+ const registry = resolveRegistry(env || process.env, allowOverride);
267
+ return findPositiveControlGaps(live.value, registry, readFixture, seam.parsePlanningDoc, FIXTURE_ARTIFACT_NAME);
268
+ }
269
+
270
+ /** Parse a bare `--root <path>` CLI override (defaults to the real repo root)
271
+ * and the `--test-registry-override` flag that gates `resolveRegistry`'s
272
+ * env-var override (see `TEST_REGISTRY_OVERRIDE_CLI_FLAG`'s header comment).
273
+ * No real invocation (`npm run lint:ci`, `npm run lint:planning-document-
274
+ * positive-control`) ever passes this flag. */
275
+ function parseArgs(argv) {
276
+ let root = path.join(__dirname, '..');
277
+ let allowTestRegistryOverride = false;
278
+ for (let i = 0; i < argv.length; i++) {
279
+ if (argv[i] === '--root' && argv[i + 1]) {
280
+ root = path.resolve(argv[i + 1]);
281
+ i += 1;
282
+ } else if (argv[i] === TEST_REGISTRY_OVERRIDE_CLI_FLAG) {
283
+ allowTestRegistryOverride = true;
284
+ }
285
+ }
286
+ return { root, allowTestRegistryOverride };
287
+ }
288
+
289
+ function main() {
290
+ const { root, allowTestRegistryOverride } = parseArgs(process.argv.slice(2));
291
+ const violations = scanRepo(root, process.env, allowTestRegistryOverride);
292
+ if (violations.length === 0) {
293
+ process.stdout.write(
294
+ 'ok planning-document-positive-control: every declared PlanningDoc grammar has a passing positive-control fixture\n',
295
+ );
296
+ return;
297
+ }
298
+ process.stderr.write(
299
+ 'planning-document-positive-control: PlanningDoc grammar(s) missing or failing a positive-control fixture (ADR-4910 §7).\n',
300
+ );
301
+ process.stderr.write(
302
+ 'Add/fix a fixture under tests/fixtures/planning-document/ and register it in POSITIVE_CONTROL_REGISTRY in\n' +
303
+ 'scripts/lint-planning-document-positive-control.cjs — each fixture must round-trip through parsePlanningDoc\n' +
304
+ 'and yield a node of the declared kind.\n',
305
+ );
306
+ for (const v of violations) {
307
+ const id = v.kind ?? '?';
308
+ const loc = v.fixture ? ` (${path.join(FIXTURE_DIR, v.fixture)})` : '';
309
+ process.stderr.write(` ${id}${loc}: ${v.reason}\n`);
310
+ }
311
+ process.exitCode = 1;
312
+ }
313
+
314
+ if (require.main === module) main();
315
+
316
+ module.exports = {
317
+ findPositiveControlGaps,
318
+ readLiveGrammarKinds,
319
+ resolveRegistry,
320
+ scanRepo,
321
+ parseArgs,
322
+ POSITIVE_CONTROL_REGISTRY,
323
+ FIXTURE_ARTIFACT_NAME,
324
+ PLANNING_DOCUMENT_SRC,
325
+ PLANNING_DOCUMENT_LIB,
326
+ FIXTURE_DIR,
327
+ TEST_REGISTRY_OVERRIDE_ENV_VAR,
328
+ TEST_REGISTRY_OVERRIDE_CLI_FLAG,
329
+ };
@@ -78,7 +78,10 @@ const EXACT_INLINE_DIRECTIVE_WORKFLOWS = new Set([
78
78
  'discuss-phase/modes/text.md',
79
79
  'discuss-phase/templates/context.md',
80
80
  'discuss-phase/templates/discussion-log.md',
81
+ 'execute-phase/steps/code-review-disposition.md',
81
82
  'execute-phase/steps/codebase-drift-gate.md',
83
+ 'execute-phase/steps/gap-closure-artifacts.md',
84
+ 'execute-phase/steps/regression-gate.md',
82
85
  'execute-phase/steps/regression-gate-run.md',
83
86
  'execute-phase/steps/worktree-recovery-policy.md',
84
87
  'help/modes/brief.md',
@@ -29,6 +29,8 @@ const { runMain } = require('./lib/cli-exit.cjs');
29
29
 
30
30
  const PROFILES_MODULE = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'install-profiles.cjs');
31
31
  const { PROFILES, loadSkillsManifest, resolveProfile } = require(PROFILES_MODULE);
32
+ // The one frontmatter fence owner, from the same build:lib output as install-profiles.
33
+ const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
32
34
 
33
35
  // ---------------------------------------------------------------------------
34
36
  // Argument parsing
@@ -58,9 +60,11 @@ function extractBodyReferences(body) {
58
60
  }
59
61
 
60
62
  function extractBody(content) {
61
- const fmEnd = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/m);
62
- if (!fmEnd) return content;
63
- return content.slice(fmEnd[0].length);
63
+ // The block is the one the one fence owner finds (found while implementing #5105);
64
+ // the body starts past the closing fence line and its line ending.
65
+ const fence = locateFrontmatterFence(content);
66
+ if (!fence || !fence.closed) return content;
67
+ return content.slice(fence.closingFenceEnd).replace(/^\r?\n/, '');
64
68
  }
65
69
 
66
70
  // ---------------------------------------------------------------------------
@@ -142,6 +142,7 @@
142
142
  "files": [
143
143
  "verify-archive-dirs-live-path.test.cjs",
144
144
  "verify-health.test.cjs",
145
+ "verify-lifecycle-writes-e2e.test.cjs",
145
146
  "verify-mvp-uat.test.cjs",
146
147
  "verify-npm-publish.test.cjs",
147
148
  "verify-test-quality.test.cjs",
@@ -150,7 +151,7 @@
150
151
  "verify.test.cjs"
151
152
  ],
152
153
  "issue": "#3813 — the live-path #1883 suite drives buildPlanningSnapshot in-process with an fs fault; the verify module suites are CLI-driving and cannot host an fs monkeypatch",
153
- "justification": "verify-command-grounding added by #2401/#3678; allowlist follow-up landed with #3606 (base was red on this lane). #4546 adds verify-work-deferred-promotion (deferred-follow-up promotion writer contract + parity)."
154
+ "justification": "verify-command-grounding added by #2401/#3678; allowlist follow-up landed with #3606 (base was red on this lane). #4546 adds verify-work-deferred-promotion (deferred-follow-up promotion writer contract + parity). #5105 adds verify-lifecycle-writes-e2e.test.cjs (Phase 3 of #5056: end-to-end fixture proving the fixed post-fingerprint verbs leave an unchanged re-run fresh, plus its own pre-fix CONTROL — a distinct end-to-end concern from the existing per-verb unit suites)."
154
155
  },
155
156
  "install": {
156
157
  "files": [
@@ -293,6 +294,15 @@
293
294
  ],
294
295
  "issue": "4657",
295
296
  "justification": "The ui-consideration-probe cluster gains its workflow-prose contract file (tests/ui-consideration-probe-ui-phase-contract.test.cjs, #4657), mirroring the edge probe's split (edge-probe.test.cjs / edge-probe-docs-fixtures.test.cjs / edge-probe-spec-phase-contract.test.cjs). The contract file reads shipped gsd-core/workflows/ui-phase.md under the source-text-is-the-product exception, which the unit file deliberately does not carry (it asserts typed returns only)."
297
+ },
298
+ "verification": {
299
+ "files": [
300
+ "verification-append-audit.test.cjs",
301
+ "verification-overrides.test.cjs",
302
+ "verification-status.test.cjs"
303
+ ],
304
+ "issue": "5105",
305
+ "justification": "#5105 (Phase 3 of #5056) adds verification-append-audit.test.cjs for the new `verification.append-audit` verb (deny-by-default audit-block append/no-op, #4887) — a distinct concern from verification-status.test.cjs's status/fingerprint suite and verification-overrides.test.cjs's override handling."
296
306
  }
297
307
  }
298
308
  }