@opengsd/gsd-core 1.15.0 → 1.17.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 (505) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -2
  3. package/.opencode/plugins/gsd-core.js +336 -6
  4. package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
  5. package/GEMINI.md +1 -1
  6. package/agents/gsd-code-fixer.compact.md +1 -1
  7. package/agents/gsd-code-fixer.md +1 -1
  8. package/agents/gsd-code-reviewer.compact.md +5 -3
  9. package/agents/gsd-code-reviewer.md +8 -6
  10. package/agents/gsd-debug-session-manager.compact.md +7 -1
  11. package/agents/gsd-debug-session-manager.md +7 -1
  12. package/agents/gsd-debugger.md +2 -2
  13. package/agents/gsd-eval-auditor.compact.md +1 -1
  14. package/agents/gsd-eval-auditor.md +1 -1
  15. package/agents/gsd-executor.md +4 -4
  16. package/agents/gsd-intel-updater.compact.md +1 -1
  17. package/agents/gsd-intel-updater.md +1 -1
  18. package/agents/gsd-mempalace-curator.md +2 -2
  19. package/agents/gsd-phase-researcher.md +1 -1
  20. package/agents/gsd-plan-checker.md +7 -2
  21. package/agents/gsd-planner.md +3 -3
  22. package/agents/gsd-project-researcher.compact.md +1 -1
  23. package/agents/gsd-project-researcher.md +1 -1
  24. package/agents/gsd-research-synthesizer.compact.md +1 -1
  25. package/agents/gsd-research-synthesizer.md +1 -1
  26. package/agents/gsd-ui-auditor.compact.md +21 -30
  27. package/agents/gsd-ui-auditor.md +42 -51
  28. package/agents/gsd-ui-researcher.compact.md +1 -1
  29. package/agents/gsd-ui-researcher.md +1 -1
  30. package/agents/gsd-verifier.md +31 -9
  31. package/bin/gsd-mcp-server.js +3 -1
  32. package/bin/install.js +487 -290
  33. package/commands/gsd/add-tests.md +6 -1
  34. package/commands/gsd/ai-integration-phase.md +6 -1
  35. package/commands/gsd/audit-fix.md +5 -0
  36. package/commands/gsd/audit-milestone.md +6 -1
  37. package/commands/gsd/autonomous.md +5 -0
  38. package/commands/gsd/capture.md +8 -4
  39. package/commands/gsd/code-review.md +7 -2
  40. package/commands/gsd/complete-milestone.md +4 -0
  41. package/commands/gsd/config.md +7 -3
  42. package/commands/gsd/debug.md +11 -7
  43. package/commands/gsd/discuss-phase.md +7 -3
  44. package/commands/gsd/docs-update.md +12 -7
  45. package/commands/gsd/eval-review.md +6 -1
  46. package/commands/gsd/execute-phase.md +12 -7
  47. package/commands/gsd/extract-learnings.md +5 -0
  48. package/commands/gsd/fast.md +4 -0
  49. package/commands/gsd/forensics.md +5 -1
  50. package/commands/gsd/graphify.md +10 -6
  51. package/commands/gsd/health.md +5 -0
  52. package/commands/gsd/help.md +7 -2
  53. package/commands/gsd/import.md +7 -3
  54. package/commands/gsd/inbox.md +5 -0
  55. package/commands/gsd/ingest-docs.md +5 -1
  56. package/commands/gsd/manager.md +6 -1
  57. package/commands/gsd/map-codebase.md +7 -3
  58. package/commands/gsd/mempalace-capture.md +5 -1
  59. package/commands/gsd/mempalace-recall.md +5 -1
  60. package/commands/gsd/milestone-summary.md +5 -1
  61. package/commands/gsd/mvp-phase.md +8 -3
  62. package/commands/gsd/new-milestone.md +6 -1
  63. package/commands/gsd/new-project.md +5 -0
  64. package/commands/gsd/next.md +6 -1
  65. package/commands/gsd/ns-context.md +4 -0
  66. package/commands/gsd/ns-ideate.md +4 -0
  67. package/commands/gsd/ns-manage.md +4 -0
  68. package/commands/gsd/ns-project.md +4 -0
  69. package/commands/gsd/ns-review.md +4 -0
  70. package/commands/gsd/ns-workflow.md +4 -0
  71. package/commands/gsd/onboard.md +6 -1
  72. package/commands/gsd/pause-work.md +5 -1
  73. package/commands/gsd/phase.md +8 -4
  74. package/commands/gsd/plan-phase.md +6 -1
  75. package/commands/gsd/plan-review-convergence.md +5 -1
  76. package/commands/gsd/pr-branch.md +4 -0
  77. package/commands/gsd/profile-user.md +5 -1
  78. package/commands/gsd/progress.md +6 -1
  79. package/commands/gsd/quick-batch.md +20 -8
  80. package/commands/gsd/quick.md +12 -7
  81. package/commands/gsd/review.md +5 -1
  82. package/commands/gsd/secure-phase.md +6 -1
  83. package/commands/gsd/ship.md +5 -0
  84. package/commands/gsd/sketch.md +7 -2
  85. package/commands/gsd/spec-phase.md +5 -1
  86. package/commands/gsd/spike.md +8 -3
  87. package/commands/gsd/surface.md +5 -1
  88. package/commands/gsd/thread.md +4 -0
  89. package/commands/gsd/ui-phase.md +6 -1
  90. package/commands/gsd/ui-review.md +6 -1
  91. package/commands/gsd/ultraplan-phase.md +5 -1
  92. package/commands/gsd/undo.md +5 -1
  93. package/commands/gsd/update.md +6 -2
  94. package/commands/gsd/validate-phase.md +6 -1
  95. package/commands/gsd/verify-work.md +6 -1
  96. package/commands/gsd/workspace.md +7 -3
  97. package/gsd-core/bin/gsd-tools.cjs +159 -58
  98. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  99. package/gsd-core/bin/lib/adr-parser.cjs +37 -5
  100. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  101. package/gsd-core/bin/lib/audit.cjs +78 -44
  102. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  103. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  104. package/gsd-core/bin/lib/capability-registry.cjs +249 -117
  105. package/gsd-core/bin/lib/capability-validator.cjs +18 -1
  106. package/gsd-core/bin/lib/capability-writer.cjs +2 -1
  107. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  108. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  109. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  110. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  111. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  112. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  113. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  114. package/gsd-core/bin/lib/commands.cjs +201 -178
  115. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  116. package/gsd-core/bin/lib/config.cjs +43 -12
  117. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  118. package/gsd-core/bin/lib/coverage.cjs +4 -8
  119. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  120. package/gsd-core/bin/lib/drift.cjs +177 -42
  121. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  122. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  123. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  124. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  125. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  126. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  127. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  128. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  129. package/gsd-core/bin/lib/gate-context-drift.cjs +142 -0
  130. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  131. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  132. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  133. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  134. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  135. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  136. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  137. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  138. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  139. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  140. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  141. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  142. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  143. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  144. package/gsd-core/bin/lib/gate-ui-safety.cjs +81 -0
  145. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  146. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  147. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  148. package/gsd-core/bin/lib/graphify.cjs +3 -2
  149. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  150. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  151. package/gsd-core/bin/lib/init.cjs +194 -108
  152. package/gsd-core/bin/lib/install-engine.cjs +30 -35
  153. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  154. package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
  155. package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
  156. package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
  157. package/gsd-core/bin/lib/io.cjs +122 -3
  158. package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
  159. package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
  160. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  161. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  162. package/gsd-core/bin/lib/mcp-server.cjs +5 -1
  163. package/gsd-core/bin/lib/milestone.cjs +19 -1
  164. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  165. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  166. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  167. package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
  168. package/gsd-core/bin/lib/opencode-version.cjs +486 -0
  169. package/gsd-core/bin/lib/pattern.cjs +10 -0
  170. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  171. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  172. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  173. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  174. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  175. package/gsd-core/bin/lib/phase.cjs +301 -80
  176. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  177. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  178. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  179. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  180. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  181. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  182. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  183. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  184. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  185. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  186. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +13 -9
  187. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  188. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  189. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  190. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  191. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  192. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  193. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +186 -161
  194. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  195. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  196. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  197. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +365 -594
  198. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  199. package/gsd-core/bin/lib/runtime-slash.cjs +60 -30
  200. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  201. package/gsd-core/bin/lib/smart-entry.cjs +111 -56
  202. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  203. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  204. package/gsd-core/bin/lib/state-document.cjs +100 -22
  205. package/gsd-core/bin/lib/state-transition.cjs +67 -9
  206. package/gsd-core/bin/lib/state.cjs +668 -171
  207. package/gsd-core/bin/lib/surface.cjs +2 -1
  208. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  209. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  210. package/gsd-core/bin/lib/uat.cjs +424 -7
  211. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  212. package/gsd-core/bin/lib/update-context.cjs +18 -1
  213. package/gsd-core/bin/lib/vendor/README.md +31 -9
  214. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  215. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  216. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  217. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  218. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  219. package/gsd-core/bin/lib/verification.cjs +1265 -324
  220. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  221. package/gsd-core/bin/lib/verify.cjs +286 -567
  222. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  223. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  224. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  225. package/gsd-core/bin/lib/workstream.cjs +11 -2
  226. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  227. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  228. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  229. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  230. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  231. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  232. package/gsd-core/references/execute-phase-response-language.md +1 -1
  233. package/gsd-core/references/gsd-run-resolver.md +1 -1
  234. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  235. package/gsd-core/references/offer-next.md +1 -1
  236. package/gsd-core/references/planning-config.md +1 -1
  237. package/gsd-core/references/spidr-splitting.md +1 -1
  238. package/gsd-core/references/tdd.md +10 -4
  239. package/gsd-core/references/verifier-phase-gates.md +5 -2
  240. package/gsd-core/references/verify-mvp-mode.md +2 -2
  241. package/gsd-core/references/workstream-flag.md +33 -3
  242. package/gsd-core/templates/README.md +1 -1
  243. package/gsd-core/templates/UAT.md +17 -1
  244. package/gsd-core/templates/config.json +2 -11
  245. package/gsd-core/templates/verification-report.md +1 -1
  246. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  247. package/gsd-core/workflows/add-backlog.md +1 -1
  248. package/gsd-core/workflows/add-phase.md +8 -7
  249. package/gsd-core/workflows/add-tests.md +3 -2
  250. package/gsd-core/workflows/add-todo.md +4 -3
  251. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  252. package/gsd-core/workflows/audit-fix.md +1 -1
  253. package/gsd-core/workflows/audit-milestone.md +4 -3
  254. package/gsd-core/workflows/audit-uat.md +1 -1
  255. package/gsd-core/workflows/autonomous.md +28 -18
  256. package/gsd-core/workflows/check-todos.md +6 -5
  257. package/gsd-core/workflows/cleanup.md +1 -1
  258. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  259. package/gsd-core/workflows/code-review-fix.md +5 -4
  260. package/gsd-core/workflows/code-review.md +100 -91
  261. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  262. package/gsd-core/workflows/complete-milestone.md +12 -7
  263. package/gsd-core/workflows/debug.md +30 -5
  264. package/gsd-core/workflows/diagnose-issues.md +3 -2
  265. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  266. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  267. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  268. package/gsd-core/workflows/discuss-phase.md +4 -3
  269. package/gsd-core/workflows/do.md +1 -1
  270. package/gsd-core/workflows/docs-update.md +4 -3
  271. package/gsd-core/workflows/edit-phase.md +4 -3
  272. package/gsd-core/workflows/eval-review.md +5 -3
  273. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  274. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  275. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  276. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  277. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +2 -2
  278. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  279. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  280. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  281. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  282. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  283. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  284. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  285. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  286. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  288. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  289. package/gsd-core/workflows/execute-phase.md +81 -171
  290. package/gsd-core/workflows/execute-plan.md +13 -19
  291. package/gsd-core/workflows/explore.md +2 -2
  292. package/gsd-core/workflows/extract-learnings.md +3 -2
  293. package/gsd-core/workflows/fast.md +1 -1
  294. package/gsd-core/workflows/forensics.md +1 -1
  295. package/gsd-core/workflows/graduation.md +1 -1
  296. package/gsd-core/workflows/health.md +2 -1
  297. package/gsd-core/workflows/import.md +3 -2
  298. package/gsd-core/workflows/inbox.md +1 -1
  299. package/gsd-core/workflows/ingest-docs.md +1 -1
  300. package/gsd-core/workflows/insert-phase.md +4 -3
  301. package/gsd-core/workflows/list-seeds.md +1 -1
  302. package/gsd-core/workflows/list-workspaces.md +1 -1
  303. package/gsd-core/workflows/manager.md +5 -3
  304. package/gsd-core/workflows/map-codebase.md +4 -3
  305. package/gsd-core/workflows/milestone-summary.md +3 -2
  306. package/gsd-core/workflows/mvp-phase.md +14 -14
  307. package/gsd-core/workflows/new-milestone.md +10 -10
  308. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  309. package/gsd-core/workflows/new-project.md +1 -1
  310. package/gsd-core/workflows/new-workspace.md +1 -1
  311. package/gsd-core/workflows/next.md +1 -1
  312. package/gsd-core/workflows/pause-work.md +2 -2
  313. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  314. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  315. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  316. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  317. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  318. package/gsd-core/workflows/plan-phase.md +27 -18
  319. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  320. package/gsd-core/workflows/plant-seed.md +1 -1
  321. package/gsd-core/workflows/pr-branch.md +1 -1
  322. package/gsd-core/workflows/profile-user.md +1 -1
  323. package/gsd-core/workflows/progress.md +19 -49
  324. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  325. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  326. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  327. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  328. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  329. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  330. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  331. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  332. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  333. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  334. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  335. package/gsd-core/workflows/quick-batch.md +15 -10
  336. package/gsd-core/workflows/quick.md +42 -31
  337. package/gsd-core/workflows/remove-phase.md +3 -2
  338. package/gsd-core/workflows/remove-workspace.md +1 -1
  339. package/gsd-core/workflows/resume-project.md +17 -12
  340. package/gsd-core/workflows/review.md +3 -2
  341. package/gsd-core/workflows/scan.md +3 -2
  342. package/gsd-core/workflows/secure-phase.md +11 -12
  343. package/gsd-core/workflows/settings-advanced.md +1 -1
  344. package/gsd-core/workflows/settings-integrations.md +1 -1
  345. package/gsd-core/workflows/settings.md +1 -1
  346. package/gsd-core/workflows/ship.md +12 -12
  347. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  348. package/gsd-core/workflows/sketch.md +1 -1
  349. package/gsd-core/workflows/smart-entry.md +1 -1
  350. package/gsd-core/workflows/spec-phase.md +1 -1
  351. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  352. package/gsd-core/workflows/spike.md +1 -1
  353. package/gsd-core/workflows/stats.md +1 -1
  354. package/gsd-core/workflows/sync-skills.md +1 -1
  355. package/gsd-core/workflows/thread.md +2 -2
  356. package/gsd-core/workflows/transition.md +13 -23
  357. package/gsd-core/workflows/ui-phase.md +5 -4
  358. package/gsd-core/workflows/ui-review.md +4 -3
  359. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  360. package/gsd-core/workflows/undo.md +1 -1
  361. package/gsd-core/workflows/validate-phase.md +10 -12
  362. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  365. package/gsd-core/workflows/verify-work.md +68 -59
  366. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  367. package/hooks/dist/gsd-check-update-worker.js +8 -0
  368. package/hooks/dist/gsd-check-update.js +34 -20
  369. package/hooks/dist/gsd-context-monitor.js +45 -13
  370. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  371. package/hooks/dist/gsd-secret-read-guard.js +263 -16
  372. package/hooks/dist/gsd-statusline.js +336 -29
  373. package/hooks/dist/gsd-update-banner.js +8 -0
  374. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  375. package/hooks/dist/gsd-workflow-guard.js +5 -4
  376. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  377. package/hooks/dist/lib/cli-exit.js +12 -0
  378. package/hooks/dist/lib/git-probe.js +17 -1
  379. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  380. package/hooks/gsd-agent-isolation-guard.js +8 -0
  381. package/hooks/gsd-check-update-worker.js +8 -0
  382. package/hooks/gsd-check-update.js +34 -20
  383. package/hooks/gsd-context-monitor.js +45 -13
  384. package/hooks/gsd-cursor-subagent-start.js +8 -0
  385. package/hooks/gsd-secret-read-guard.js +263 -16
  386. package/hooks/gsd-statusline.js +336 -29
  387. package/hooks/gsd-update-banner.js +8 -0
  388. package/hooks/gsd-windsurf-pre-write.js +11 -2
  389. package/hooks/gsd-workflow-guard.js +5 -4
  390. package/hooks/gsd-worktree-path-guard.js +6 -2
  391. package/hooks/hooks.json +5 -5
  392. package/hooks/lib/cli-exit.js +12 -0
  393. package/hooks/lib/git-probe.js +17 -1
  394. package/hooks/lib/isolation-sentinel.js +2 -2
  395. package/package.json +21 -4
  396. package/scripts/changeset/parse.cjs +52 -4
  397. package/scripts/ci-timeout-report.cjs +770 -4
  398. package/scripts/command-contract-helpers.cjs +12 -8
  399. package/scripts/docs-guard-registry.cjs +6 -0
  400. package/scripts/gen-capability-registry.cjs +7 -1
  401. package/scripts/gen-features.cjs +13 -8
  402. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  403. package/scripts/gen-loop-host-contract.cjs +14 -2
  404. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  405. package/scripts/gen-plugin-skills.cjs +87 -1
  406. package/scripts/gen-research-agents.cjs +24 -31
  407. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  408. package/scripts/gen-test-timings.cjs +32 -7
  409. package/scripts/lib/cli-exit.cjs +12 -0
  410. package/scripts/lib/macos-conformance-tier.generated.cjs +24 -2
  411. package/scripts/lib/ndjson-reporter.cjs +28 -3
  412. package/scripts/lib/platform-conformance-tier.generated.cjs +44 -5
  413. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  414. package/scripts/lib/vendor-bundle.cjs +59 -0
  415. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  416. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  417. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  418. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  419. package/scripts/lint-descriptions.cjs +7 -3
  420. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  421. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  422. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  423. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  424. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  425. package/scripts/lint-phase-id-drift.cjs +29 -0
  426. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  427. package/scripts/lint-response-language-coverage.cjs +3 -0
  428. package/scripts/lint-skill-deps.cjs +7 -3
  429. package/scripts/lint-test-file-count.allowlist.json +11 -1
  430. package/scripts/lint-test-file-count.cjs +34 -1
  431. package/scripts/lint-vendored-deps.cjs +41 -5
  432. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  433. package/scripts/mutation-matrix.cjs +53 -6
  434. package/scripts/release-tarball-smoke.cjs +14 -7
  435. package/scripts/require-issue-link-policy.cjs +6 -2
  436. package/scripts/sync-runtime-launcher.cjs +184 -2
  437. package/scripts/verify-npm-publish.cjs +76 -20
  438. package/skills/gsd-add-tests/SKILL.md +6 -1
  439. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  440. package/skills/gsd-audit-fix/SKILL.md +5 -0
  441. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  442. package/skills/gsd-autonomous/SKILL.md +5 -0
  443. package/skills/gsd-capture/SKILL.md +8 -4
  444. package/skills/gsd-code-review/SKILL.md +7 -2
  445. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  446. package/skills/gsd-config/SKILL.md +8 -4
  447. package/skills/gsd-debug/SKILL.md +11 -7
  448. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  449. package/skills/gsd-docs-update/SKILL.md +12 -7
  450. package/skills/gsd-eval-review/SKILL.md +6 -1
  451. package/skills/gsd-execute-phase/SKILL.md +12 -7
  452. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  453. package/skills/gsd-fast/SKILL.md +4 -0
  454. package/skills/gsd-forensics/SKILL.md +5 -1
  455. package/skills/gsd-graphify/SKILL.md +10 -6
  456. package/skills/gsd-health/SKILL.md +5 -0
  457. package/skills/gsd-help/SKILL.md +7 -2
  458. package/skills/gsd-import/SKILL.md +7 -3
  459. package/skills/gsd-inbox/SKILL.md +5 -0
  460. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  461. package/skills/gsd-manager/SKILL.md +6 -1
  462. package/skills/gsd-map-codebase/SKILL.md +7 -3
  463. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  464. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  465. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  466. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  467. package/skills/gsd-new-milestone/SKILL.md +6 -1
  468. package/skills/gsd-new-project/SKILL.md +5 -0
  469. package/skills/gsd-next/SKILL.md +6 -1
  470. package/skills/gsd-ns-context/SKILL.md +4 -0
  471. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  472. package/skills/gsd-ns-manage/SKILL.md +4 -0
  473. package/skills/gsd-ns-project/SKILL.md +4 -0
  474. package/skills/gsd-ns-review/SKILL.md +4 -0
  475. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  476. package/skills/gsd-onboard/SKILL.md +6 -1
  477. package/skills/gsd-pause-work/SKILL.md +5 -1
  478. package/skills/gsd-phase/SKILL.md +8 -4
  479. package/skills/gsd-plan-phase/SKILL.md +6 -1
  480. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  481. package/skills/gsd-pr-branch/SKILL.md +4 -0
  482. package/skills/gsd-profile-user/SKILL.md +5 -1
  483. package/skills/gsd-progress/SKILL.md +6 -1
  484. package/skills/gsd-quick/SKILL.md +16 -10
  485. package/skills/gsd-quick-batch/SKILL.md +20 -8
  486. package/skills/gsd-review/SKILL.md +5 -1
  487. package/skills/gsd-review-backlog/SKILL.md +3 -2
  488. package/skills/gsd-secure-phase/SKILL.md +6 -1
  489. package/skills/gsd-ship/SKILL.md +5 -0
  490. package/skills/gsd-sketch/SKILL.md +7 -2
  491. package/skills/gsd-spec-phase/SKILL.md +5 -1
  492. package/skills/gsd-spike/SKILL.md +8 -3
  493. package/skills/gsd-surface/SKILL.md +5 -1
  494. package/skills/gsd-thread/SKILL.md +4 -0
  495. package/skills/gsd-ui-phase/SKILL.md +6 -1
  496. package/skills/gsd-ui-review/SKILL.md +6 -1
  497. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  498. package/skills/gsd-undo/SKILL.md +5 -1
  499. package/skills/gsd-update/SKILL.md +6 -2
  500. package/skills/gsd-validate-phase/SKILL.md +6 -1
  501. package/skills/gsd-verify-work/SKILL.md +6 -1
  502. package/skills/gsd-workspace/SKILL.md +7 -3
  503. package/skills/gsd-workstreams/SKILL.md +6 -6
  504. package/vscode/package.json +1 -1
  505. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
@@ -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
  }
@@ -141,6 +141,38 @@ function buildTestMap(prodPrefixes, allTestFiles) {
141
141
  return map;
142
142
  }
143
143
 
144
+ // #5074: a `<stem>.platform.test.cjs` alongside `<stem>.test.cjs` (or
145
+ // `.test.ts`) in the SAME directory is the other half of that primary file —
146
+ // the platform-sensitive slice extracted so the Windows CI selector runs
147
+ // only the small sibling — not an additional test file. Exclude it from the
148
+ // per-module count and from the allowlist identity comparison. A
149
+ // `.platform.test.cjs` with no base file in the same directory counts
150
+ // normally, so the rule cannot be used to smuggle in new standalone files.
151
+ // An empty stem (bare `platform.test.cjs`) is never treated as a sibling.
152
+ const PLATFORM_SIBLING_RE = /^(.+)\.platform\.test\.(cjs|ts)$/;
153
+
154
+ function isSplitSibling(fileBasename, siblingBasenamesInDir) {
155
+ const m = fileBasename.match(PLATFORM_SIBLING_RE);
156
+ if (!m) return false;
157
+ const stem = m[1];
158
+ if (!stem) return false;
159
+ return siblingBasenamesInDir.includes(`${stem}.test.cjs`) ||
160
+ siblingBasenamesInDir.includes(`${stem}.test.ts`);
161
+ }
162
+
163
+ function excludeSplitSiblings(testFiles) {
164
+ const basenamesByDir = new Map();
165
+ for (const f of testFiles) {
166
+ const dir = path.dirname(f);
167
+ if (!basenamesByDir.has(dir)) basenamesByDir.set(dir, []);
168
+ basenamesByDir.get(dir).push(path.basename(f));
169
+ }
170
+ return testFiles.filter(f => {
171
+ const dir = path.dirname(f);
172
+ return !isSplitSibling(path.basename(f), basenamesByDir.get(dir));
173
+ });
174
+ }
175
+
144
176
  function loadAllowlist() {
145
177
  try { return JSON.parse(fs.readFileSync(ALLOWLIST_PATH, 'utf-8')).modules || {}; }
146
178
  catch (_) { return {}; }
@@ -160,6 +192,7 @@ function loadAllowlist() {
160
192
  * Returns { verdict, prefix, count, knownFiles, novel, stale, files }
161
193
  */
162
194
  function evaluateLint({ prefix, testFiles, allowlist }) {
195
+ testFiles = excludeSplitSiblings(testFiles);
163
196
  const count = testFiles.length;
164
197
  const entry = allowlist[prefix];
165
198
  const currentNames = testFiles.map(f => path.basename(f));
@@ -258,7 +291,7 @@ function run() {
258
291
  }
259
292
 
260
293
  module.exports = {
261
- Verdict, evaluateLint, testEffectivePrefix, prodPrefix,
294
+ Verdict, evaluateLint, testEffectivePrefix, prodPrefix, isSplitSibling,
262
295
  _collectProdPrefixes: collectProdPrefixes,
263
296
  _collectAllTestFiles: collectAllTestFiles,
264
297
  _buildTestMap: buildTestMap,
@@ -57,6 +57,7 @@
57
57
  const fs = require('node:fs');
58
58
  const path = require('node:path');
59
59
  const { ExitError, runMain } = require('./lib/cli-exit.cjs');
60
+ const { buildVendorBundle } = require('./lib/vendor-bundle.cjs');
60
61
 
61
62
  const ROOT = path.join(__dirname, '..');
62
63
 
@@ -89,16 +90,29 @@ function resolvePath(p) {
89
90
  * or null when there is no bin-side type twin
90
91
  * @property {string|null} srcTwin path under src/vendor/ to the source-side type twin tsc
91
92
  * resolves for a relative import from src/**, or null
92
- * @property {'upstream-verbatim'|'hand-authored'} twinKind
93
+ * @property {boolean} [bundle] Reproducibly bundle upstream and its locked dependencies.
94
+ * @property {'upstream-verbatim'|'upstream-reference'|'hand-authored'} twinKind
93
95
  * 'upstream-verbatim': srcTwin/vendoredDts are byte-compared
94
96
  * against upstream and each other.
95
97
  * 'hand-authored': no upstream counterpart exists, so the
96
98
  * twin is excluded from the byte-compare (checks 2 and 3
97
99
  * above are skipped for this row).
100
+ * 'upstream-reference': srcTwin must be exactly
101
+ * `export * from '<name>';` (bundled rows).
98
102
  */
99
103
 
100
104
  /** @type {VendoredPackage[]} */
101
105
  const VENDORED = [
106
+ ...['tap-parser', 'saxes'].map((name) => ({
107
+ name,
108
+ upstreamCjs: name === 'tap-parser' ? 'node_modules/tap-parser/dist/commonjs/index.js' : 'node_modules/saxes/saxes.js',
109
+ vendoredCjs: `gsd-core/bin/lib/vendor/${name}.cjs`,
110
+ upstreamDts: null,
111
+ vendoredDts: null,
112
+ srcTwin: `src/vendor/${name}.d.cts`,
113
+ twinKind: 'upstream-reference',
114
+ bundle: true,
115
+ })),
102
116
  {
103
117
  name: 're2js',
104
118
  upstreamCjs: 'node_modules/re2js/build/index.cjs',
@@ -127,6 +141,7 @@ const VENDORED = [
127
141
  * @returns {string}
128
142
  */
129
143
  function buildRefreshCommand(row) {
144
+ if (row.bundle) return 'npm run lint:vendored-deps -- --fix';
130
145
  const parts = [`cp ${row.upstreamCjs} ${row.vendoredCjs}`];
131
146
  if (row.twinKind === 'upstream-verbatim' && row.upstreamDts) {
132
147
  if (row.vendoredDts) parts.push(`cp ${row.upstreamDts} ${row.vendoredDts}`);
@@ -135,7 +150,8 @@ function buildRefreshCommand(row) {
135
150
  return parts.join(' && ');
136
151
  }
137
152
 
138
- const REFRESH_COMMAND = VENDORED.map(buildRefreshCommand).join(' && ');
153
+ // Bundled rows share one refresh command; list it once.
154
+ const REFRESH_COMMAND = [...new Set(VENDORED.map(buildRefreshCommand))].join(' && ');
139
155
 
140
156
  /**
141
157
  * Compare two files byte-for-byte. Returns null when equal, or a short
@@ -263,8 +279,17 @@ function readPinState(row, pkgRoot = ROOT) {
263
279
  function checkRow(row, pkgRoot = ROOT) {
264
280
  const findings = [];
265
281
 
266
- const cjsDrift = compareFiles(row.vendoredCjs, row.upstreamCjs);
267
- if (cjsDrift) findings.push(cjsDrift);
282
+ if (row.bundle) {
283
+ const built = buildVendorBundle(pkgRoot, row.upstreamCjs);
284
+ for (const [file, expected] of [[row.vendoredCjs, built.code], [`${row.vendoredCjs}.LICENSE.txt`, built.notices]]) {
285
+ if (!fs.existsSync(resolvePath(file)) || !fs.readFileSync(resolvePath(file)).equals(expected)) {
286
+ findings.push(`${file} != reproducible upstream bundle (including license notices)`);
287
+ }
288
+ }
289
+ } else {
290
+ const cjsDrift = compareFiles(row.vendoredCjs, row.upstreamCjs);
291
+ if (cjsDrift) findings.push(cjsDrift);
292
+ }
268
293
 
269
294
  if (row.twinKind === 'upstream-verbatim') {
270
295
  if (row.upstreamDts && row.vendoredDts) {
@@ -277,6 +302,11 @@ function checkRow(row, pkgRoot = ROOT) {
277
302
  }
278
303
  } else if (row.twinKind === 'hand-authored') {
279
304
  findings.push(...checkHandAuthoredTwin(row));
305
+ } else if (row.twinKind === 'upstream-reference') {
306
+ const expected = `export * from '${row.name}';\n`;
307
+ if (!row.srcTwin || !fs.existsSync(resolvePath(row.srcTwin)) || fs.readFileSync(resolvePath(row.srcTwin), 'utf8') !== expected) {
308
+ findings.push(`${row.name}: type twin must forward the upstream declarations verbatim`);
309
+ }
280
310
  }
281
311
 
282
312
  const { pinnedSpec, installedVersion } = readPinState(row, pkgRoot);
@@ -318,7 +348,13 @@ function checkRow(row, pkgRoot = ROOT) {
318
348
  * @returns {string[]} findings remaining after the fix (empty when fully resolved)
319
349
  */
320
350
  function fixRow(row, pkgRoot = ROOT) {
321
- fs.copyFileSync(resolvePath(row.upstreamCjs), resolvePath(row.vendoredCjs));
351
+ if (row.bundle) {
352
+ const built = buildVendorBundle(pkgRoot, row.upstreamCjs);
353
+ fs.writeFileSync(resolvePath(row.vendoredCjs), built.code);
354
+ fs.writeFileSync(resolvePath(`${row.vendoredCjs}.LICENSE.txt`), built.notices);
355
+ } else {
356
+ fs.copyFileSync(resolvePath(row.upstreamCjs), resolvePath(row.vendoredCjs));
357
+ }
322
358
 
323
359
  if (row.twinKind === 'upstream-verbatim' && row.upstreamDts) {
324
360
  if (row.vendoredDts) fs.copyFileSync(resolvePath(row.upstreamDts), resolvePath(row.vendoredDts));
@@ -354,11 +354,6 @@
354
354
  "code": "2012",
355
355
  "message": "Use find instead of ls to better handle non-alphanumeric filenames."
356
356
  },
357
- {
358
- "file": "gsd-core/workflows/execute-phase.md",
359
- "code": "2012",
360
- "message": "Use find instead of ls to better handle non-alphanumeric filenames."
361
- },
362
357
  {
363
358
  "file": "gsd-core/workflows/execute-phase.md",
364
359
  "code": "2016",
@@ -384,11 +379,6 @@
384
379
  "code": "2086",
385
380
  "message": "Double quote to prevent globbing and word splitting."
386
381
  },
387
- {
388
- "file": "gsd-core/workflows/execute-phase.md",
389
- "code": "2086",
390
- "message": "Double quote to prevent globbing and word splitting."
391
- },
392
382
  {
393
383
  "file": "gsd-core/workflows/execute-phase/detail/elaboration.md",
394
384
  "code": "2086",
@@ -414,6 +404,16 @@
414
404
  "code": "2086",
415
405
  "message": "Double quote to prevent globbing and word splitting."
416
406
  },
407
+ {
408
+ "file": "gsd-core/workflows/execute-phase/steps/verify-phase-goal.md",
409
+ "code": "2012",
410
+ "message": "Use find instead of ls to better handle non-alphanumeric filenames."
411
+ },
412
+ {
413
+ "file": "gsd-core/workflows/execute-phase/steps/verify-phase-goal.md",
414
+ "code": "2086",
415
+ "message": "Double quote to prevent globbing and word splitting."
416
+ },
417
417
  {
418
418
  "file": "gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md",
419
419
  "code": "2086",