@opengsd/gsd-core 1.14.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 (551) 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/README.ja-JP.md +3 -3
  5. package/README.ko-KR.md +3 -3
  6. package/README.pt-BR.md +3 -3
  7. package/README.zh-CN.md +3 -3
  8. package/agents/gsd-code-fixer.compact.md +7 -6
  9. package/agents/gsd-code-fixer.md +9 -8
  10. package/agents/gsd-code-reviewer.compact.md +5 -3
  11. package/agents/gsd-code-reviewer.md +8 -6
  12. package/agents/gsd-debug-session-manager.compact.md +17 -2
  13. package/agents/gsd-debug-session-manager.md +17 -2
  14. package/agents/gsd-debugger.md +3 -3
  15. package/agents/gsd-eval-auditor.compact.md +1 -1
  16. package/agents/gsd-eval-auditor.md +1 -1
  17. package/agents/gsd-executor.md +17 -12
  18. package/agents/gsd-intel-updater.compact.md +1 -1
  19. package/agents/gsd-intel-updater.md +1 -1
  20. package/agents/gsd-mempalace-curator.md +2 -2
  21. package/agents/gsd-phase-researcher.md +19 -11
  22. package/agents/gsd-plan-checker.md +15 -9
  23. package/agents/gsd-planner.md +15 -11
  24. package/agents/gsd-project-researcher.compact.md +1 -1
  25. package/agents/gsd-project-researcher.md +1 -1
  26. package/agents/gsd-research-synthesizer.compact.md +1 -1
  27. package/agents/gsd-research-synthesizer.md +1 -1
  28. package/agents/gsd-ui-auditor.compact.md +21 -30
  29. package/agents/gsd-ui-auditor.md +166 -37
  30. package/agents/gsd-ui-researcher.compact.md +1 -1
  31. package/agents/gsd-ui-researcher.md +1 -1
  32. package/agents/gsd-verifier.md +37 -14
  33. package/bin/install.js +764 -287
  34. package/commands/gsd/add-tests.md +6 -1
  35. package/commands/gsd/ai-integration-phase.md +6 -1
  36. package/commands/gsd/audit-fix.md +5 -0
  37. package/commands/gsd/audit-milestone.md +6 -1
  38. package/commands/gsd/autonomous.md +7 -2
  39. package/commands/gsd/capture.md +9 -5
  40. package/commands/gsd/code-review.md +7 -2
  41. package/commands/gsd/complete-milestone.md +4 -0
  42. package/commands/gsd/config.md +7 -3
  43. package/commands/gsd/debug.md +11 -7
  44. package/commands/gsd/discuss-phase.md +7 -3
  45. package/commands/gsd/docs-update.md +12 -7
  46. package/commands/gsd/eval-review.md +6 -1
  47. package/commands/gsd/execute-phase.md +12 -7
  48. package/commands/gsd/extract-learnings.md +5 -0
  49. package/commands/gsd/fast.md +4 -0
  50. package/commands/gsd/forensics.md +5 -1
  51. package/commands/gsd/graphify.md +10 -6
  52. package/commands/gsd/health.md +5 -0
  53. package/commands/gsd/help.md +7 -2
  54. package/commands/gsd/import.md +7 -3
  55. package/commands/gsd/inbox.md +5 -0
  56. package/commands/gsd/ingest-docs.md +5 -1
  57. package/commands/gsd/manager.md +6 -1
  58. package/commands/gsd/map-codebase.md +7 -3
  59. package/commands/gsd/mempalace-capture.md +12 -4
  60. package/commands/gsd/mempalace-recall.md +5 -1
  61. package/commands/gsd/milestone-summary.md +5 -1
  62. package/commands/gsd/mvp-phase.md +8 -3
  63. package/commands/gsd/new-milestone.md +6 -1
  64. package/commands/gsd/new-project.md +5 -0
  65. package/commands/gsd/next.md +6 -1
  66. package/commands/gsd/ns-context.md +4 -0
  67. package/commands/gsd/ns-ideate.md +4 -0
  68. package/commands/gsd/ns-manage.md +4 -0
  69. package/commands/gsd/ns-project.md +4 -0
  70. package/commands/gsd/ns-review.md +4 -0
  71. package/commands/gsd/ns-workflow.md +4 -0
  72. package/commands/gsd/onboard.md +6 -1
  73. package/commands/gsd/pause-work.md +5 -1
  74. package/commands/gsd/phase.md +8 -4
  75. package/commands/gsd/plan-phase.md +6 -1
  76. package/commands/gsd/plan-review-convergence.md +11 -7
  77. package/commands/gsd/pr-branch.md +4 -0
  78. package/commands/gsd/profile-user.md +5 -1
  79. package/commands/gsd/progress.md +7 -2
  80. package/commands/gsd/quick-batch.md +21 -9
  81. package/commands/gsd/quick.md +12 -7
  82. package/commands/gsd/review.md +7 -4
  83. package/commands/gsd/secure-phase.md +6 -1
  84. package/commands/gsd/ship.md +5 -0
  85. package/commands/gsd/sketch.md +7 -2
  86. package/commands/gsd/spec-phase.md +5 -1
  87. package/commands/gsd/spike.md +8 -3
  88. package/commands/gsd/surface.md +5 -1
  89. package/commands/gsd/thread.md +4 -0
  90. package/commands/gsd/ui-phase.md +6 -1
  91. package/commands/gsd/ui-review.md +6 -1
  92. package/commands/gsd/ultraplan-phase.md +5 -1
  93. package/commands/gsd/undo.md +5 -1
  94. package/commands/gsd/update.md +6 -2
  95. package/commands/gsd/validate-phase.md +6 -1
  96. package/commands/gsd/verify-work.md +6 -1
  97. package/commands/gsd/workspace.md +7 -3
  98. package/gsd-core/bin/gsd-tools.cjs +477 -78
  99. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  100. package/gsd-core/bin/lib/adr-parser.cjs +3 -1
  101. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  102. package/gsd-core/bin/lib/audit.cjs +144 -42
  103. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  104. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  105. package/gsd-core/bin/lib/capability-registry.cjs +197 -222
  106. package/gsd-core/bin/lib/capability-validator.cjs +16 -1
  107. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  108. package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
  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 +32 -33
  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 +343 -207
  115. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  116. package/gsd-core/bin/lib/config-loader.cjs +65 -4
  117. package/gsd-core/bin/lib/config.cjs +76 -19
  118. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  119. package/gsd-core/bin/lib/coverage.cjs +4 -8
  120. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  121. package/gsd-core/bin/lib/decisions.cjs +30 -14
  122. package/gsd-core/bin/lib/drift.cjs +177 -42
  123. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  124. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  125. package/gsd-core/bin/lib/frontmatter.cjs +426 -234
  126. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  127. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  128. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  129. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  130. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  131. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  132. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  133. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  134. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  135. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  136. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  137. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  138. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  139. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  140. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  141. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  142. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  143. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  144. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  145. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  146. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  147. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  148. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  149. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  150. package/gsd-core/bin/lib/graphify.cjs +10 -2
  151. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  152. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  153. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  154. package/gsd-core/bin/lib/init.cjs +364 -132
  155. package/gsd-core/bin/lib/install-engine.cjs +30 -33
  156. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  157. package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
  158. package/gsd-core/bin/lib/io.cjs +122 -3
  159. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  160. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  161. package/gsd-core/bin/lib/milestone.cjs +37 -6
  162. package/gsd-core/bin/lib/model-resolver.cjs +171 -62
  163. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  164. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  165. package/gsd-core/bin/lib/pattern.cjs +10 -0
  166. package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
  167. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  168. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  169. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  170. package/gsd-core/bin/lib/phase-id.cjs +110 -8
  171. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  172. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  173. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  174. package/gsd-core/bin/lib/phase.cjs +489 -88
  175. package/gsd-core/bin/lib/plan-document.cjs +142 -20
  176. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  177. package/gsd-core/bin/lib/planning-document.cjs +692 -0
  178. package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
  179. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  180. package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
  181. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  182. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  183. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  184. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  185. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  186. package/gsd-core/bin/lib/project-root.cjs +41 -2
  187. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  188. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  189. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  190. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  191. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  192. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  193. package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
  194. package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
  195. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
  196. package/gsd-core/bin/lib/roadmap.cjs +405 -88
  197. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
  198. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
  199. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  200. package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
  201. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
  202. package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
  203. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  204. package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
  205. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  206. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  207. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  208. package/gsd-core/bin/lib/state-document.cjs +100 -22
  209. package/gsd-core/bin/lib/state-transition.cjs +39 -2
  210. package/gsd-core/bin/lib/state.cjs +256 -105
  211. package/gsd-core/bin/lib/surface.cjs +19 -2
  212. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
  213. package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
  214. package/gsd-core/bin/lib/uat.cjs +432 -7
  215. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  216. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
  217. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  218. package/gsd-core/bin/lib/vendor/README.md +31 -9
  219. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  220. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  221. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  222. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  223. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  224. package/gsd-core/bin/lib/verification.cjs +1378 -274
  225. package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
  226. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  227. package/gsd-core/bin/lib/verify.cjs +357 -531
  228. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  229. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  230. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  231. package/gsd-core/bin/lib/workstream.cjs +11 -2
  232. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  233. package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
  234. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
  235. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  236. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  237. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  238. package/gsd-core/references/checkpoints.md +5 -3
  239. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  240. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  241. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  242. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  243. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  244. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  245. package/gsd-core/references/edge-probe.md +195 -21
  246. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  247. package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
  248. package/gsd-core/references/execute-phase-response-language.md +1 -1
  249. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  250. package/gsd-core/references/gsd-run-resolver.md +1 -1
  251. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  252. package/gsd-core/references/model-profiles.md +1 -1
  253. package/gsd-core/references/offer-next.md +1 -1
  254. package/gsd-core/references/phase-argument-parsing.md +9 -7
  255. package/gsd-core/references/phase-id-convention.md +28 -0
  256. package/gsd-core/references/planner-gap-closure.md +2 -0
  257. package/gsd-core/references/planner-load-graph-context.md +24 -13
  258. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  259. package/gsd-core/references/planning-config.md +12 -3
  260. package/gsd-core/references/spidr-splitting.md +1 -1
  261. package/gsd-core/references/tdd.md +37 -8
  262. package/gsd-core/references/ui-consideration-probe.md +10 -5
  263. package/gsd-core/references/verifier-phase-gates.md +5 -2
  264. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  265. package/gsd-core/references/verify-mvp-mode.md +2 -2
  266. package/gsd-core/references/workstream-flag.md +33 -3
  267. package/gsd-core/references/worktree-path-safety.md +321 -0
  268. package/gsd-core/templates/README.md +1 -1
  269. package/gsd-core/templates/UAT.md +17 -1
  270. package/gsd-core/templates/config.json +2 -11
  271. package/gsd-core/templates/verification-report.md +1 -1
  272. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  273. package/gsd-core/workflows/add-backlog.md +1 -1
  274. package/gsd-core/workflows/add-phase.md +8 -7
  275. package/gsd-core/workflows/add-tests.md +4 -3
  276. package/gsd-core/workflows/add-todo.md +6 -5
  277. package/gsd-core/workflows/ai-integration-phase.md +13 -4
  278. package/gsd-core/workflows/audit-fix.md +1 -1
  279. package/gsd-core/workflows/audit-milestone.md +4 -3
  280. package/gsd-core/workflows/audit-uat.md +1 -1
  281. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  282. package/gsd-core/workflows/autonomous.md +43 -23
  283. package/gsd-core/workflows/check-todos.md +7 -6
  284. package/gsd-core/workflows/cleanup.md +2 -2
  285. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  286. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  287. package/gsd-core/workflows/code-review-fix.md +112 -25
  288. package/gsd-core/workflows/code-review.md +146 -135
  289. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  290. package/gsd-core/workflows/complete-milestone.md +13 -8
  291. package/gsd-core/workflows/debug.md +32 -7
  292. package/gsd-core/workflows/diagnose-issues.md +3 -2
  293. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  294. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  295. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  296. package/gsd-core/workflows/discuss-phase.md +4 -3
  297. package/gsd-core/workflows/do.md +2 -2
  298. package/gsd-core/workflows/docs-update.md +6 -5
  299. package/gsd-core/workflows/edit-phase.md +4 -3
  300. package/gsd-core/workflows/eval-review.md +14 -5
  301. package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
  302. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
  303. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  304. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  305. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
  306. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  307. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  308. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  309. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  310. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  311. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  312. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  313. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  314. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  315. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  316. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  317. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  318. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  319. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  320. package/gsd-core/workflows/execute-phase.md +96 -178
  321. package/gsd-core/workflows/execute-plan.md +18 -23
  322. package/gsd-core/workflows/explore.md +4 -4
  323. package/gsd-core/workflows/extract-learnings.md +4 -2
  324. package/gsd-core/workflows/fast.md +1 -1
  325. package/gsd-core/workflows/forensics.md +1 -1
  326. package/gsd-core/workflows/graduation.md +1 -1
  327. package/gsd-core/workflows/health.md +3 -2
  328. package/gsd-core/workflows/help/modes/full.compact.md +3 -3
  329. package/gsd-core/workflows/help/modes/full.md +5 -5
  330. package/gsd-core/workflows/help/modes/topic.md +15 -5
  331. package/gsd-core/workflows/import.md +4 -3
  332. package/gsd-core/workflows/inbox.md +2 -2
  333. package/gsd-core/workflows/ingest-docs.md +3 -3
  334. package/gsd-core/workflows/insert-phase.md +4 -3
  335. package/gsd-core/workflows/list-seeds.md +1 -1
  336. package/gsd-core/workflows/list-workspaces.md +1 -1
  337. package/gsd-core/workflows/manager.md +6 -4
  338. package/gsd-core/workflows/map-codebase.md +5 -4
  339. package/gsd-core/workflows/milestone-summary.md +3 -2
  340. package/gsd-core/workflows/mvp-phase.md +14 -14
  341. package/gsd-core/workflows/new-milestone.md +11 -11
  342. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  343. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  344. package/gsd-core/workflows/new-project.md +7 -7
  345. package/gsd-core/workflows/new-workspace.md +2 -2
  346. package/gsd-core/workflows/next.md +1 -1
  347. package/gsd-core/workflows/note.md +1 -1
  348. package/gsd-core/workflows/onboard.md +1 -1
  349. package/gsd-core/workflows/pause-work.md +2 -2
  350. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  351. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  352. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  353. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  354. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  355. package/gsd-core/workflows/plan-phase.md +50 -24
  356. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  357. package/gsd-core/workflows/plant-seed.md +62 -20
  358. package/gsd-core/workflows/pr-branch.md +113 -13
  359. package/gsd-core/workflows/profile-user.md +2 -2
  360. package/gsd-core/workflows/progress.md +19 -49
  361. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
  362. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  363. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  364. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  365. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  366. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  367. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  368. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  369. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  370. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  371. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  372. package/gsd-core/workflows/quick-batch.md +15 -10
  373. package/gsd-core/workflows/quick.md +62 -39
  374. package/gsd-core/workflows/reapply-patches.md +9 -3
  375. package/gsd-core/workflows/remove-phase.md +3 -2
  376. package/gsd-core/workflows/remove-workspace.md +2 -2
  377. package/gsd-core/workflows/resume-project.md +3 -2
  378. package/gsd-core/workflows/review.md +33 -17
  379. package/gsd-core/workflows/scan.md +3 -2
  380. package/gsd-core/workflows/secure-phase.md +13 -13
  381. package/gsd-core/workflows/settings-advanced.md +30 -10
  382. package/gsd-core/workflows/settings-integrations.md +2 -3
  383. package/gsd-core/workflows/settings.md +4 -4
  384. package/gsd-core/workflows/ship.md +14 -13
  385. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  386. package/gsd-core/workflows/sketch.md +1 -1
  387. package/gsd-core/workflows/smart-entry.md +2 -2
  388. package/gsd-core/workflows/spec-phase.md +15 -5
  389. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  390. package/gsd-core/workflows/spike.md +1 -1
  391. package/gsd-core/workflows/stats.md +1 -1
  392. package/gsd-core/workflows/sync-skills.md +5 -5
  393. package/gsd-core/workflows/thread.md +2 -2
  394. package/gsd-core/workflows/transition.md +13 -23
  395. package/gsd-core/workflows/ui-phase.md +48 -11
  396. package/gsd-core/workflows/ui-review.md +21 -6
  397. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  398. package/gsd-core/workflows/undo.md +339 -20
  399. package/gsd-core/workflows/update.md +7 -7
  400. package/gsd-core/workflows/validate-phase.md +12 -13
  401. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  402. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  403. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  404. package/gsd-core/workflows/verify-work.md +129 -55
  405. package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
  406. package/hooks/dist/gsd-check-update-worker.js +8 -0
  407. package/hooks/dist/gsd-check-update.js +8 -0
  408. package/hooks/dist/gsd-context-monitor.js +31 -8
  409. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  410. package/hooks/dist/gsd-secret-read-guard.js +161 -4
  411. package/hooks/dist/gsd-statusline.js +85 -20
  412. package/hooks/dist/gsd-update-banner.js +8 -0
  413. package/hooks/dist/gsd-validate-commit.sh +63 -4
  414. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  415. package/hooks/dist/gsd-workflow-guard.js +5 -4
  416. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  417. package/hooks/dist/lib/cli-exit.js +12 -0
  418. package/hooks/dist/lib/git-probe.js +17 -1
  419. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  420. package/hooks/gsd-agent-isolation-guard.js +32 -0
  421. package/hooks/gsd-check-update-worker.js +8 -0
  422. package/hooks/gsd-check-update.js +8 -0
  423. package/hooks/gsd-context-monitor.js +31 -8
  424. package/hooks/gsd-cursor-subagent-start.js +8 -0
  425. package/hooks/gsd-secret-read-guard.js +161 -4
  426. package/hooks/gsd-statusline.js +85 -20
  427. package/hooks/gsd-update-banner.js +8 -0
  428. package/hooks/gsd-validate-commit.sh +63 -4
  429. package/hooks/gsd-windsurf-pre-write.js +11 -2
  430. package/hooks/gsd-workflow-guard.js +5 -4
  431. package/hooks/gsd-worktree-path-guard.js +6 -2
  432. package/hooks/hooks.json +5 -5
  433. package/hooks/lib/cli-exit.js +12 -0
  434. package/hooks/lib/git-probe.js +17 -1
  435. package/hooks/lib/isolation-sentinel.js +2 -2
  436. package/package.json +22 -4
  437. package/scripts/build-hooks.js +15 -6
  438. package/scripts/changeset/parse.cjs +52 -4
  439. package/scripts/check-contract-drift.cjs +127 -11
  440. package/scripts/ci-timeout-report.cjs +770 -4
  441. package/scripts/command-contract-helpers.cjs +15 -8
  442. package/scripts/docs-guard-registry.cjs +34 -0
  443. package/scripts/gen-features.cjs +13 -8
  444. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  445. package/scripts/gen-loop-host-contract.cjs +79 -1
  446. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  447. package/scripts/gen-plugin-skills.cjs +87 -1
  448. package/scripts/gen-research-agents.cjs +24 -31
  449. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  450. package/scripts/gen-test-timings.cjs +32 -7
  451. package/scripts/lib/cli-exit.cjs +12 -0
  452. package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
  453. package/scripts/lib/ndjson-reporter.cjs +31 -5
  454. package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
  455. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  456. package/scripts/lib/vendor-bundle.cjs +59 -0
  457. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  458. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  459. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  460. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  461. package/scripts/lint-descriptions.cjs +7 -3
  462. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
  463. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  464. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  465. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  466. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  467. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  468. package/scripts/lint-phase-id-drift.cjs +319 -5
  469. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  470. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  471. package/scripts/lint-response-language-coverage.cjs +3 -0
  472. package/scripts/lint-retired-runtime-name.cjs +619 -0
  473. package/scripts/lint-skill-deps.cjs +7 -3
  474. package/scripts/lint-state-write-path-drift.cjs +93 -0
  475. package/scripts/lint-test-file-count.allowlist.json +38 -9
  476. package/scripts/lint-test-file-count.cjs +34 -1
  477. package/scripts/lint-vendored-deps.cjs +41 -5
  478. package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
  479. package/scripts/mutation-matrix.cjs +50 -5
  480. package/scripts/prompt-injection-scan.sh +4 -0
  481. package/scripts/release-tarball-smoke.cjs +194 -1
  482. package/scripts/require-issue-link-policy.cjs +6 -2
  483. package/scripts/sync-runtime-launcher.cjs +184 -2
  484. package/scripts/verify-npm-publish.cjs +76 -20
  485. package/skills/gsd-add-tests/SKILL.md +6 -1
  486. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  487. package/skills/gsd-audit-fix/SKILL.md +5 -0
  488. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  489. package/skills/gsd-autonomous/SKILL.md +7 -2
  490. package/skills/gsd-capture/SKILL.md +9 -5
  491. package/skills/gsd-code-review/SKILL.md +7 -2
  492. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  493. package/skills/gsd-config/SKILL.md +8 -4
  494. package/skills/gsd-debug/SKILL.md +11 -7
  495. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  496. package/skills/gsd-docs-update/SKILL.md +12 -7
  497. package/skills/gsd-eval-review/SKILL.md +6 -1
  498. package/skills/gsd-execute-phase/SKILL.md +12 -7
  499. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  500. package/skills/gsd-fast/SKILL.md +4 -0
  501. package/skills/gsd-forensics/SKILL.md +5 -1
  502. package/skills/gsd-graphify/SKILL.md +10 -6
  503. package/skills/gsd-health/SKILL.md +5 -0
  504. package/skills/gsd-help/SKILL.md +7 -2
  505. package/skills/gsd-import/SKILL.md +7 -3
  506. package/skills/gsd-inbox/SKILL.md +5 -0
  507. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  508. package/skills/gsd-manager/SKILL.md +6 -1
  509. package/skills/gsd-map-codebase/SKILL.md +7 -3
  510. package/skills/gsd-mempalace-capture/SKILL.md +12 -4
  511. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  512. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  513. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  514. package/skills/gsd-new-milestone/SKILL.md +6 -1
  515. package/skills/gsd-new-project/SKILL.md +5 -0
  516. package/skills/gsd-next/SKILL.md +6 -1
  517. package/skills/gsd-ns-context/SKILL.md +4 -0
  518. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  519. package/skills/gsd-ns-manage/SKILL.md +4 -0
  520. package/skills/gsd-ns-project/SKILL.md +4 -0
  521. package/skills/gsd-ns-review/SKILL.md +4 -0
  522. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  523. package/skills/gsd-onboard/SKILL.md +6 -1
  524. package/skills/gsd-pause-work/SKILL.md +5 -1
  525. package/skills/gsd-phase/SKILL.md +8 -4
  526. package/skills/gsd-plan-phase/SKILL.md +6 -1
  527. package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
  528. package/skills/gsd-pr-branch/SKILL.md +4 -0
  529. package/skills/gsd-profile-user/SKILL.md +5 -1
  530. package/skills/gsd-progress/SKILL.md +7 -2
  531. package/skills/gsd-quick/SKILL.md +16 -10
  532. package/skills/gsd-quick-batch/SKILL.md +21 -9
  533. package/skills/gsd-review/SKILL.md +7 -4
  534. package/skills/gsd-review-backlog/SKILL.md +3 -2
  535. package/skills/gsd-secure-phase/SKILL.md +6 -1
  536. package/skills/gsd-ship/SKILL.md +5 -0
  537. package/skills/gsd-sketch/SKILL.md +7 -2
  538. package/skills/gsd-spec-phase/SKILL.md +5 -1
  539. package/skills/gsd-spike/SKILL.md +8 -3
  540. package/skills/gsd-surface/SKILL.md +5 -1
  541. package/skills/gsd-thread/SKILL.md +4 -0
  542. package/skills/gsd-ui-phase/SKILL.md +6 -1
  543. package/skills/gsd-ui-review/SKILL.md +6 -1
  544. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  545. package/skills/gsd-undo/SKILL.md +5 -1
  546. package/skills/gsd-update/SKILL.md +6 -2
  547. package/skills/gsd-validate-phase/SKILL.md +6 -1
  548. package/skills/gsd-verify-work/SKILL.md +6 -1
  549. package/skills/gsd-workspace/SKILL.md +7 -3
  550. package/skills/gsd-workstreams/SKILL.md +6 -6
  551. package/vscode/package.json +1 -1
@@ -12,6 +12,11 @@
12
12
  * enforced by both consumers.
13
13
  */
14
14
 
15
+ // The one frontmatter fence owner (src/frontmatter-fence.cts, built to bin/lib by
16
+ // build:lib). Every consumer of this module runs after the build: lint:ci (CI builds
17
+ // bin/lib first) and the test suite (pretest).
18
+ const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
19
+
15
20
  const CANONICAL_TOOLS = new Set([
16
21
  'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep',
17
22
  'Task', 'Agent', 'Skill', 'SlashCommand',
@@ -22,16 +27,15 @@ const CANONICAL_TOOLS = new Set([
22
27
  ]);
23
28
 
24
29
  function parseFrontmatter(content) {
25
- // CRLF-tolerant split: Windows checkouts (autocrlf=true) leave a trailing
26
- // \r on every line, making lines.indexOf('---', 1) return -1 (the value
27
- // would be '---\r', not '---') → returns {} → every field appears missing.
28
- const lines = content.split(/\r?\n/);
29
- if (lines[0].trim() !== '---') return {};
30
- const end = lines.indexOf('---', 1);
31
- if (end === -1) return {};
30
+ // The block is the one the one fence owner finds (found while implementing
31
+ // #5105) — BOM, CRLF (a Windows autocrlf checkout), an adjacent empty block
32
+ // and a `----` look-alike all read exactly as every runtime reader sees them.
33
+ // Its lines are split CRLF-tolerantly so a trailing \r never leaks into a value.
34
+ const fence = locateFrontmatterFence(content);
35
+ if (!fence || !fence.closed) return {};
32
36
  const fm = {};
33
37
  let key = null;
34
- for (const line of lines.slice(1, end)) {
38
+ for (const line of content.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/)) {
35
39
  const kv = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)/);
36
40
  if (kv) { key = kv[1]; fm[key] = kv[2].trim(); }
37
41
  else if (key && line.match(/^\s+-\s+/)) {
@@ -890,6 +894,7 @@ const VIOLATION_KINDS = Object.freeze({
890
894
  LEGACY_READ_TAG: 'legacy_read_tag',
891
895
  READ_TAG_GATE_MISSING: 'read_tag_gate_missing',
892
896
  UNMATCHED_CONSUMER_TOKEN: 'unmatched_consumer_token',
897
+ UNRESOLVED_REFERENCE_INCLUDE: 'unresolved_reference_include',
893
898
  });
894
899
 
895
900
  /**
@@ -925,6 +930,8 @@ const REMEDIES = Object.freeze({
925
930
  "add the <required_reading> MUST-Read gate clause to this agent's instructions (see docs/AGENTS.md)",
926
931
  unmatched_consumer_token:
927
932
  'declare the producing agent and marker in the registry, or remove the dispatch — nothing emits this token',
933
+ unresolved_reference_include:
934
+ 'fix the gsd-core/references/ pointer so it names an existing reference file whole and ends at the name (whitespace, end of line, or closing punctuation)',
928
935
  });
929
936
 
930
937
  /**
@@ -174,6 +174,7 @@ const DOCS_GUARD_TESTS = {
174
174
  // entry).
175
175
  'tests/learnings.test.cjs': ['docs/FEATURES.md'],
176
176
  'tests/analyze-dependencies.test.cjs': ['docs/COMMANDS.md'],
177
+ 'tests/auto-select-attribute.test.cjs': ['docs/reference/plan-md.md'],
177
178
  'tests/autonomous-converge.test.cjs': [
178
179
  'docs/COMMANDS.md',
179
180
  'docs/how-to/run-phases-autonomously.md',
@@ -200,6 +201,9 @@ const DOCS_GUARD_TESTS = {
200
201
  'tests/compact-content-4139.test.cjs': ['docs/CONFIGURATION.md'],
201
202
  'tests/config-field-docs.test.cjs': ['docs/CONFIGURATION.md'],
202
203
  'tests/config.test.cjs': ['docs/CONFIGURATION.md'],
204
+ // #4974: doc/workflow content-parity checks read docs/CONFIGURATION.md's
205
+ // Gate Settings section directly (see tests/gates-confirmation-toggle-config.test.cjs).
206
+ 'tests/gates-confirmation-toggle-config.test.cjs': ['docs/CONFIGURATION.md'],
203
207
  'tests/context-index-sync.test.cjs': ['docs/CONTEXT-INDEX.json'],
204
208
  'tests/context-predicates-query.test.cjs': ['docs/contributor-standards.md'],
205
209
  // SCAN_DIRS includes 'docs' and recursively walks every .md file under it
@@ -272,6 +276,32 @@ const DOCS_GUARD_TESTS = {
272
276
  // seeding, so a content edit to any of them (e.g. renaming a landmark
273
277
  // heading/string a hostile-input test targets) can change this test's
274
278
  // fixture assumptions.
279
+ // #4728: pins the retirement of Gemini CLI prose from every localized
280
+ // how-to/ARCHITECTURE/USER-GUIDE/CONFIGURATION/context-monitor mirror, plus
281
+ // the PRESERVE (Antigravity) and MODEL-AXIS (Gemini 2.5 Pro) negative-space
282
+ // checks in the same locales.
283
+ 'tests/gemini-runtime-removed.test.cjs': [
284
+ 'docs/ja-JP/how-to/install-on-your-runtime.md',
285
+ 'docs/ko-KR/how-to/install-on-your-runtime.md',
286
+ 'docs/pt-BR/how-to/install-on-your-runtime.md',
287
+ 'docs/zh-CN/how-to/install-on-your-runtime.md',
288
+ 'docs/ja-JP/ARCHITECTURE.md',
289
+ 'docs/ko-KR/ARCHITECTURE.md',
290
+ 'docs/pt-BR/ARCHITECTURE.md',
291
+ 'docs/zh-CN/ARCHITECTURE.md',
292
+ 'docs/ja-JP/USER-GUIDE.md',
293
+ 'docs/ko-KR/USER-GUIDE.md',
294
+ 'docs/pt-BR/USER-GUIDE.md',
295
+ 'docs/zh-CN/USER-GUIDE.md',
296
+ 'docs/ja-JP/CONFIGURATION.md',
297
+ 'docs/ko-KR/CONFIGURATION.md',
298
+ 'docs/pt-BR/CONFIGURATION.md',
299
+ 'docs/zh-CN/CONFIGURATION.md',
300
+ 'docs/ja-JP/context-monitor.md',
301
+ 'docs/ko-KR/context-monitor.md',
302
+ 'docs/pt-BR/context-monitor.md',
303
+ 'docs/zh-CN/context-monitor.md',
304
+ ],
275
305
  'tests/gen-state-md-docs.test.cjs': [
276
306
  'docs/reference/state-md.md',
277
307
  'docs/ja-JP/reference/state-md.md',
@@ -376,6 +406,7 @@ const DOCS_GUARD_TESTS = {
376
406
  'docs/how-to/plan-a-phase.md',
377
407
  'docs/AGENTS.md',
378
408
  ],
409
+ 'tests/ui-interaction-capture.test.cjs': ['docs/CONFIGURATION.md'],
379
410
  'tests/ui-spec-inventory-provenance.test.cjs': [
380
411
  'docs/FEATURES.md',
381
412
  'docs/how-to/design-a-ui-phase.md',
@@ -390,6 +421,9 @@ const DOCS_GUARD_TESTS = {
390
421
  'tests/verifier-behavior-unverified.test.cjs': ['docs/reference/planning-artifacts.md'],
391
422
  'tests/verifier-coincidental-reliance.test.cjs': ['docs/AGENTS.md'],
392
423
  'tests/verify.test.cjs': ['docs/reference/plan-md.md'],
424
+ // #5118 V43: asserts the deleted execute-phase/steps/stale-reverification.md
425
+ // is absent from the generated inventory manifest.
426
+ 'tests/verify-lifecycle-writes-e2e.test.cjs': ['docs/INVENTORY-MANIFEST.json'],
393
427
  'tests/workflow-fragments.test.cjs': ['docs/reference/workflow-fragments.md'],
394
428
  };
395
429
 
@@ -66,6 +66,10 @@ const fs = require('node:fs');
66
66
  const path = require('node:path');
67
67
 
68
68
  const { ExitError, runMain } = require('./lib/cli-exit.cjs');
69
+ // The one frontmatter fence owner (src/frontmatter-fence.cts, built by build:lib).
70
+ // Every entry point runs after the build: lint:generated-sync inside lint:ci (CI
71
+ // builds bin/lib first), regen:derived (`npm run build` first), and the tests.
72
+ const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
69
73
 
70
74
  const ROOT = path.resolve(__dirname, '..');
71
75
  const FEATURES_DIR = path.join(ROOT, 'docs', 'features');
@@ -255,13 +259,12 @@ function renderFrontmatter(data, body) {
255
259
  */
256
260
  function parseFrontmatter(text) {
257
261
  const normalized = String(text).replace(/\r\n/g, '\n');
258
- if (!normalized.startsWith('---\n')) return { data: null, body: normalized };
259
-
260
- const end = normalized.indexOf('\n---\n', 3);
261
- if (end === -1) return { data: null, body: normalized };
262
+ // The block is the one the one fence owner finds (found while implementing #5105).
263
+ const fence = locateFrontmatterFence(normalized);
264
+ if (!fence || !fence.closed) return { data: null, body: normalized };
262
265
 
263
266
  const data = {};
264
- for (const line of normalized.slice(4, end + 1).split('\n')) {
267
+ for (const line of normalized.slice(fence.openEnd, fence.bodyEnd).split('\n')) {
265
268
  if (line.trim() === '') continue;
266
269
  const sep = line.indexOf(':');
267
270
  if (sep === -1) continue;
@@ -270,9 +273,11 @@ function parseFrontmatter(text) {
270
273
  data[key] = parseScalar(line.slice(sep + 1).trim());
271
274
  }
272
275
 
273
- // `+5` clears "\n---\n"; the blank line renderFrontmatter emits after the
274
- // closing fence is consumed here so body text starts at its first real line.
275
- let body = normalized.slice(end + 5);
276
+ // Past the closing fence line and its line ending; the blank line
277
+ // renderFrontmatter emits after the closing fence is consumed here so body
278
+ // text starts at its first real line.
279
+ let body = normalized.slice(fence.closingFenceEnd);
280
+ if (body.startsWith('\n')) body = body.slice(1);
276
281
  if (body.startsWith('\n')) body = body.slice(1);
277
282
  return { data, body };
278
283
  }
@@ -18,6 +18,17 @@
18
18
  * target (`.js`, not `.cjs`, to match the hooks/lib/*.js convention) would
19
19
  * tangle a script that is otherwise simple and single-purpose.
20
20
  *
21
+ * compileToTemp() itself IS shared (required from ./gen-scripts-cli-exit.cjs)
22
+ * rather than duplicated: it compiles only src/cli-exit.cts (via a throwaway
23
+ * tsconfig that extends tsconfig.build.json with files/include scoped to
24
+ * that one module) rather than the whole project, since the emit this
25
+ * script needs depends only on cli-exit.cts's own import closure. A
26
+ * whole-project `-p tsconfig.build.json` type-check of every .cts file under
27
+ * a fixed COMPILE_TIMEOUT_MS ceiling can get killed by that timeout on a
28
+ * CPU-contended shared bench even though the one file this script needs
29
+ * compiles in a fraction of the time — and the compile strategy itself has
30
+ * nothing sibling-specific to fork.
31
+ *
21
32
  * Usage:
22
33
  * node scripts/gen-hooks-cli-exit.cjs # same as --write
23
34
  * node scripts/gen-hooks-cli-exit.cjs --write # write hooks/lib/cli-exit.js
@@ -35,14 +46,12 @@
35
46
 
36
47
  'use strict';
37
48
 
38
- const { execFileSync } = require('node:child_process');
39
49
  const fs = require('node:fs');
40
- const os = require('node:os');
41
50
  const path = require('node:path');
51
+ const { compileToTemp } = require('./gen-scripts-cli-exit.cjs');
42
52
 
43
53
  const REPO_ROOT = path.resolve(__dirname, '..');
44
54
  const OUTPUT_PATH = path.join(REPO_ROOT, 'hooks', 'lib', 'cli-exit.js');
45
- const COMPILE_TIMEOUT_MS = 60_000;
46
55
 
47
56
  /** Frozen reason codes so tests assert on structure, not prose. */
48
57
  const REASON = Object.freeze({
@@ -78,31 +87,6 @@ const BANNER = [
78
87
  '',
79
88
  ].join('\n');
80
89
 
81
- /** Compile the whole project to a throwaway outDir so the work tree is untouched. */
82
- function compileToTemp() {
83
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-hooks-cli-exit-'));
84
- try {
85
- execFileSync(
86
- process.execPath,
87
- [
88
- path.join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'),
89
- '-p', path.join(REPO_ROOT, 'tsconfig.build.json'),
90
- '--outDir', tmp,
91
- // A throwaway outDir must not reuse the in-tree incremental state, or
92
- // tsc skips emit for files it believes are already current.
93
- '--incremental', 'false',
94
- '--tsBuildInfoFile', 'null',
95
- ],
96
- { cwd: REPO_ROOT, encoding: 'utf8', stdio: 'pipe', timeout: COMPILE_TIMEOUT_MS },
97
- );
98
- return { ok: true, dir: tmp };
99
- } catch (err) {
100
- fs.rmSync(tmp, { recursive: true, force: true });
101
- const detail = [err.stdout, err.stderr].filter(Boolean).join('\n').trim();
102
- return { ok: false, detail };
103
- }
104
- }
105
-
106
90
  /**
107
91
  * Compile src/cli-exit.cts to a throwaway outDir and return the expected
108
92
  * generated content (banner + compiled bytes, with the sibling registry
@@ -37,7 +37,16 @@ const STEP_WORKFLOWS = [
37
37
  { file: 'quick.md', point: 'plan:pre', kinds: ['contribution'], into: 'planner' },
38
38
  ],
39
39
  },
40
- { file: 'execute-phase.md', step: 'execute' },
40
+ {
41
+ file: 'execute-phase.md',
42
+ step: 'execute',
43
+ // #5118: execute:post's step AND gate dispatch (code_review_gate with
44
+ // its TDD escalation) lives in the ONE shared verification step that
45
+ // execute-phase includes (and verify-work includes too) — no per-caller copy.
46
+ auxiliaryHosts: [
47
+ { file: 'execute-phase/steps/verify-phase-goal.md', point: 'execute:post', kinds: ['step', 'gate'] },
48
+ ],
49
+ },
41
50
  { file: 'verify-work.md', step: 'verify' },
42
51
  { file: 'ship.md', step: 'ship' },
43
52
  ];
@@ -78,6 +87,23 @@ const ROLE_TO_AGENT = {
78
87
  verifier: 'gsd-verifier',
79
88
  };
80
89
 
90
+ // #4740 — Role → family mapping. Three families: orchestration, planning,
91
+ // execution. This constrains what a workflow may DECLARE in agent-roles for
92
+ // a given step (admissibility), a separate concern from ROLE_TO_AGENT's
93
+ // agent-file presence check above.
94
+ const ROLE_FAMILY = {
95
+ orchestrator: 'orchestration',
96
+ researcher: 'planning', planner: 'planning', checker: 'planning',
97
+ executor: 'execution', verifier: 'execution',
98
+ };
99
+ const EXPECTED_FAMILY_BY_STEP = {
100
+ discuss: 'orchestration',
101
+ plan: 'planning',
102
+ execute: 'execution',
103
+ verify: 'orchestration',
104
+ ship: 'orchestration',
105
+ };
106
+
81
107
  // ─── Parser ───────────────────────────────────────────────────────────────────
82
108
 
83
109
  /**
@@ -228,6 +254,52 @@ function crossCheckRoles(content, agentRoles, fileName) {
228
254
  return errors;
229
255
  }
230
256
 
257
+ // ─── Cross-check: declared roles vs. their permitted family (#4740) ──────────
258
+
259
+ /**
260
+ * For a step, verify every role declared in agentRoles belongs to that step's
261
+ * expected family (orchestration / planning / execution).
262
+ *
263
+ * Unlike `assertPointsCoverage`'s `if (!expected) continue // caught
264
+ * elsewhere` guard, an unknown step here fails CLOSED: for points
265
+ * there is a second net (the canonical-set and duplicate checks), but nothing
266
+ * else in the repo validates role families, so failing open on an unknown
267
+ * step would make it the one input that silently bypasses this gate — the
268
+ * "unknown resolving to a safe known" failure mode this check exists to close.
269
+ *
270
+ * Pure: never sorts, de-dupes, or otherwise mutates `agentRoles` — the same
271
+ * array `buildContract` puts into the generated contract.
272
+ *
273
+ * @param {string} step The step named in the marker block.
274
+ * @param {string[]} agentRoles Roles declared in the block.
275
+ * @param {string} fileName For error messages.
276
+ * @returns {string[]} Array of error strings; empty = OK.
277
+ */
278
+ function crossCheckRoleFamilies(step, agentRoles, fileName) {
279
+ const expectedFamily = EXPECTED_FAMILY_BY_STEP[step];
280
+ if (!expectedFamily) {
281
+ return [fileName + ': step "' + step + '" has no entry in EXPECTED_FAMILY_BY_STEP'];
282
+ }
283
+
284
+ const errors = [];
285
+ for (const role of agentRoles) {
286
+ const family = ROLE_FAMILY[role];
287
+ if (!family) {
288
+ errors.push(
289
+ fileName + ': declared agent-role "' + role + '" has no entry in ROLE_FAMILY mapping',
290
+ );
291
+ continue;
292
+ }
293
+ if (family !== expectedFamily) {
294
+ errors.push(
295
+ fileName + ': declared agent-role "' + role + '" (family "' + family +
296
+ '") is not permitted at step "' + step + '" (expected family "' + expectedFamily + '")',
297
+ );
298
+ }
299
+ }
300
+ return errors;
301
+ }
302
+
231
303
  // ─── 12-points coverage assertion ────────────────────────────────────────────
232
304
 
233
305
  /**
@@ -331,6 +403,10 @@ function buildContract(workflowsDir) {
331
403
  const roleErrors = crossCheckRoles(content, entry.agentRoles, file);
332
404
  allErrors.push(...roleErrors);
333
405
 
406
+ // Cross-check role families (#4740)
407
+ const roleFamilyErrors = crossCheckRoleFamilies(entry.step, entry.agentRoles, file);
408
+ allErrors.push(...roleFamilyErrors);
409
+
334
410
  for (const auxiliary of auxiliaryHosts) {
335
411
  let auxiliaryContent;
336
412
  try {
@@ -667,6 +743,7 @@ function getWiredLoopPoints(repoRoot) {
667
743
  module.exports = {
668
744
  parseLoopHostBlock,
669
745
  crossCheckRoles,
746
+ crossCheckRoleFamilies,
670
747
  assertPointsCoverage,
671
748
  buildContract,
672
749
  serializeContract,
@@ -676,6 +753,7 @@ module.exports = {
676
753
  CANONICAL_POINTS,
677
754
  EXPECTED_POINTS_BY_STEP,
678
755
  ROLE_TO_AGENT,
756
+ ROLE_FAMILY,
679
757
  scanWiredPoints,
680
758
  getWiredLoopPoints,
681
759
  coveredKindsInRegion,
@@ -97,6 +97,26 @@
97
97
  * point the CLI at a small temp fixture tree instead of this repo's real,
98
98
  * 900+-file tests/ tree. Production usage (package.json's lint:generated-sync
99
99
  * / regen:derived chains) passes no flags and gets the real repo paths.
100
+ *
101
+ * #5074 — the `.platform` sibling rule. A handful of the heaviest tier files
102
+ * carry their real Windows signal in only a few tests out of hundreds; the
103
+ * fix is to move those tests into a same-directory `X.platform.test.cjs`
104
+ * sibling of `X.test.cjs`, so only the sibling (not the whole base) needs
105
+ * real-OS coverage. That split is enforced here as a FAILING invariant
106
+ * (`findPlatformSplitViolations`, wired into `classifyTree`) rather than as a
107
+ * silent filename-based exclusion rule, for two reasons: epic #4589 Phase 2
108
+ * requires real-OS overrides to be centrally enumerated, not implied by a
109
+ * naming convention (a bare "base excluded whenever a sibling exists" rule
110
+ * would BE such a convention); and a platform-sensitive test later added
111
+ * back into a split base must not silently re-admit the whole base to
112
+ * Linux-only on the Windows tier — it must fail generation loudly instead, at
113
+ * the same `lint:generated-sync` gate this module already backs. This
114
+ * invariant is scoped to the Windows CATEGORIES and wired only into
115
+ * `classifyTree`; `classifyMacosTree` stays pure content selection by design,
116
+ * so a macOS-only signal (e.g. case-sensitivity wording) left in a split base
117
+ * re-admits that base to the macOS tier instead of failing — over-inclusion,
118
+ * the safe direction. See docs/how-to/split-platform-sensitive-tests.md for
119
+ * the authoring rules this invariant enforces.
100
120
  */
101
121
 
102
122
  const fs = require('node:fs');
@@ -316,6 +336,140 @@ function classifyMacosContent(content) {
316
336
  return { needsRealOs: signals.length > 0, signals };
317
337
  }
318
338
 
339
+ /**
340
+ * The `.platform` sibling suffix (#5074). A sibling of `X.test.cjs` is a file
341
+ * whose basename ENDS with this suffix and has a non-empty stem before it —
342
+ * `foo.platform.test.cjs` is a sibling of `foo.test.cjs`, but a bare
343
+ * `platform.test.cjs` (empty stem) is not, and neither is a hyphenated name
344
+ * like `x-platform.test.cjs` or `platform-conformance-tier.test.cjs` (this
345
+ * module's own test file) — the rule keys on the literal dotted suffix, not
346
+ * on the substring "platform" appearing anywhere in the name.
347
+ */
348
+ const PLATFORM_SIBLING_SUFFIX = '.platform.test.cjs';
349
+
350
+ /**
351
+ * Parse `rel` (a POSIX `tests/...`-relative path) as a `.platform` sibling.
352
+ * Returns `{ dir, stem }` when it is one (dir is the POSIX dirname, stem is
353
+ * the basename with `PLATFORM_SIBLING_SUFFIX` stripped), or `null` when it is
354
+ * not — either because the basename does not end with the suffix, or because
355
+ * the stem would be empty (the `platform.test.cjs` negative-space case).
356
+ *
357
+ * @param {string} rel
358
+ * @returns {{ dir: string, stem: string } | null}
359
+ */
360
+ function parsePlatformSibling(rel) {
361
+ const dir = path.posix.dirname(rel);
362
+ const base = path.posix.basename(rel);
363
+ if (!base.endsWith(PLATFORM_SIBLING_SUFFIX)) return null;
364
+ const stem = base.slice(0, -PLATFORM_SIBLING_SUFFIX.length);
365
+ if (stem.length === 0) return null;
366
+ return { dir, stem };
367
+ }
368
+
369
+ /** Resolve `X.test.cjs`'s rel path for a `.platform` sibling's `{ dir, stem }`. */
370
+ function baseRelFor({ dir, stem }) {
371
+ return (dir === '.' ? '' : dir + '/') + stem + '.test.cjs';
372
+ }
373
+
374
+ /** Resolve `X.platform.test.cjs`'s rel path for a base rel path (inverse of baseRelFor). */
375
+ function siblingRelForBase(baseRel) {
376
+ const dir = path.posix.dirname(baseRel);
377
+ const stem = path.posix.basename(baseRel).slice(0, -'.test.cjs'.length);
378
+ return (dir === '.' ? '' : dir + '/') + stem + PLATFORM_SIBLING_SUFFIX;
379
+ }
380
+
381
+ /**
382
+ * The `.platform` sibling invariant (#5074), pure over an explicit `entries`
383
+ * array so it can be driven directly against fixtures (and against the real
384
+ * `ALWAYS_REAL_OS` map, which no temp fixture tree can key into) without
385
+ * touching the filesystem. `entries` is the ELIGIBLE (unit-suite) test-file
386
+ * set — a suite-tagged base is never a member, which is what makes it
387
+ * "never eligible" per the design doc's negative space rather than something
388
+ * this function has to special-case.
389
+ *
390
+ * For every `.platform` sibling present in `entries` (see
391
+ * `parsePlatformSibling`), checks three independent things against its base
392
+ * (`X.test.cjs`, same directory):
393
+ * - `base-has-signal` — the base IS in `entries` and still carries a
394
+ * genuine Windows signal (`classifyContent` on its own content) — the
395
+ * split did not actually remove the signal from the base.
396
+ * - `sibling-without-signal` — the sibling's OWN content carries no
397
+ * Windows signal, regardless of whether its base exists — a `.platform`
398
+ * file whose header claims a platform reason the classifier cannot see
399
+ * in its own text.
400
+ * - `base-always-real-os` — the base is a key of `alwaysRealOs`: the
401
+ * escape hatch says the WHOLE base needs real-OS coverage, which
402
+ * contradicts splitting only some of its tests into a sibling.
403
+ *
404
+ * A base that is absent from `entries` (suite-tagged, deleted, or renamed)
405
+ * never triggers `base-has-signal` — there is nothing to read.
406
+ *
407
+ * @param {{rel: string, content: string}[]} entries
408
+ * @param {Map<string, string>} [alwaysRealOs]
409
+ * @returns {{file: string, kind: 'base-has-signal'|'sibling-without-signal'|'base-always-real-os', signals: string[]}[]}
410
+ * sorted by file, then kind.
411
+ */
412
+ function findPlatformSplitViolations(entries, alwaysRealOs = ALWAYS_REAL_OS) {
413
+ const byRel = new Map(entries.map((entry) => [entry.rel, entry]));
414
+ const violations = [];
415
+
416
+ for (const entry of entries) {
417
+ const parsed = parsePlatformSibling(entry.rel);
418
+ if (!parsed) continue;
419
+ const baseRel = baseRelFor(parsed);
420
+
421
+ const base = byRel.get(baseRel);
422
+ if (base !== undefined) {
423
+ const { signals } = classifyContent(base.content);
424
+ if (signals.length > 0) {
425
+ violations.push({ file: baseRel, kind: 'base-has-signal', signals });
426
+ }
427
+ }
428
+
429
+ const { signals: siblingSignals } = classifyContent(entry.content);
430
+ if (siblingSignals.length === 0) {
431
+ violations.push({ file: entry.rel, kind: 'sibling-without-signal', signals: [] });
432
+ }
433
+
434
+ if (alwaysRealOs.has(baseRel)) {
435
+ violations.push({ file: baseRel, kind: 'base-always-real-os', signals: [] });
436
+ }
437
+ }
438
+
439
+ violations.sort((a, b) => {
440
+ if (a.file !== b.file) return a.file < b.file ? -1 : 1;
441
+ if (a.kind !== b.kind) return a.kind < b.kind ? -1 : 1;
442
+ return 0;
443
+ });
444
+ return violations;
445
+ }
446
+
447
+ /**
448
+ * Render `findPlatformSplitViolations`' result as the Error message
449
+ * `classifyTree` throws: starts with the literal phrase callers/tests grep
450
+ * for, lists one line per violation (file, kind, residual signal names, and
451
+ * — for `base-has-signal` only — the sibling path those tests belong in),
452
+ * and points at the how-to doc for the authoring rules.
453
+ *
454
+ * @param {ReturnType<typeof findPlatformSplitViolations>} violations
455
+ * @returns {string}
456
+ */
457
+ function formatPlatformSplitViolations(violations) {
458
+ const lines = violations.map((v) => {
459
+ let line = ` - ${v.file} [${v.kind}]`;
460
+ if (v.signals.length > 0) line += ` residual signal(s): ${v.signals.join(', ')}`;
461
+ if (v.kind === 'base-has-signal') {
462
+ line += ` — move these tests into ${siblingRelForBase(v.file)}`;
463
+ }
464
+ return line;
465
+ });
466
+ return (
467
+ `platform split invariant violated (#5074) — ${violations.length} file(s):\n` +
468
+ lines.join('\n') +
469
+ '\nSee docs/how-to/split-platform-sensitive-tests.md for the split-invariant rules.\n'
470
+ );
471
+ }
472
+
319
473
  /**
320
474
  * Recursively collect every `*.test.cjs` file beneath `dir`.
321
475
  * @param {string} dir
@@ -355,10 +509,15 @@ function classifyTree(testsDir) {
355
509
  // tier — see the header doc-comment for why suite-tagged files are excluded
356
510
  // entirely rather than merely deprioritized.
357
511
  const unitFiles = absoluteFiles.filter((absPath) => suiteOf(absPath) === null);
512
+ const entries = [];
358
513
  const flagged = [];
359
514
  for (const absPath of unitFiles) {
360
515
  const rel = 'tests/' + path.relative(testsDir, absPath).replace(/\\/g, '/');
361
516
  const content = fs.readFileSync(absPath, 'utf8');
517
+ // Read once, reused below both for this file's own flagging AND (via
518
+ // `entries`) for the #5074 split-invariant check — a second read of the
519
+ // same file for that check would double the I/O for no reason.
520
+ entries.push({ rel, content });
362
521
  const { needsRealOs } = classifyContent(content);
363
522
  // The ALWAYS_REAL_OS escape hatch (Windows tier only — see its doc
364
523
  // comment) is unioned in HERE, keyed off a file that this walk actually
@@ -369,6 +528,19 @@ function classifyTree(testsDir) {
369
528
  flagged.push(rel);
370
529
  }
371
530
  }
531
+
532
+ // #5074: a `.platform` sibling pair that violates the split invariant
533
+ // (residual signal left in the base, a sibling with no signal of its own,
534
+ // or a base that ALWAYS_REAL_OS says needs the whole file on real OS) fails
535
+ // generation loudly rather than being silently included or excluded. This
536
+ // check is Windows-CATEGORIES-only and wired here, not into
537
+ // classifyMacosTree below — a macOS-only signal in a split base is handled
538
+ // there by ordinary content selection (over-inclusion), not this invariant.
539
+ const violations = findPlatformSplitViolations(entries, ALWAYS_REAL_OS);
540
+ if (violations.length > 0) {
541
+ throw new Error(formatPlatformSplitViolations(violations));
542
+ }
543
+
372
544
  const result = [...new Set(flagged)].sort();
373
545
  return { total: absoluteFiles.length, files: result };
374
546
  }
@@ -484,7 +656,19 @@ function main() {
484
656
  const isMacos = target === 'macos';
485
657
  const label = isMacos ? 'gen-platform-conformance-tier --target macos' : 'gen-platform-conformance-tier';
486
658
  const exportKey = isMacos ? 'MACOS_CONFORMANCE_TIER_FILES' : 'CONFORMANCE_TIER_FILES';
487
- const { total, files } = isMacos ? classifyMacosTree(testsDir) : classifyTree(testsDir);
659
+ // classifyTree throws a plain Error on a #5074 split-invariant violation
660
+ // (it has no ExitError/exit-code opinion of its own — it is also called
661
+ // in-process by tests). runMain only special-cases ExitError (stderr gets
662
+ // err.message; anything else gets the full err.stack); wrapping it here
663
+ // keeps the CLI's stderr output to the clean, purpose-built violation
664
+ // report instead of a raw stack trace.
665
+ let total, files;
666
+ try {
667
+ ({ total, files } = isMacos ? classifyMacosTree(testsDir) : classifyTree(testsDir));
668
+ } catch (err) {
669
+ if (err instanceof ExitError) throw err;
670
+ throw new ExitError(1, err && err.message ? err.message : String(err));
671
+ }
488
672
 
489
673
  if (mode === 'write') {
490
674
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
@@ -554,4 +738,6 @@ module.exports = {
554
738
  MACOS_CATEGORIES,
555
739
  classifyMacosTree,
556
740
  renderMacosGeneratedFile,
741
+ PLATFORM_SIBLING_SUFFIX,
742
+ findPlatformSplitViolations,
557
743
  };