@opengsd/gsd-core 1.11.0 → 1.13.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 (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -53,6 +53,35 @@ const TOKEN_DRIFT_RE = /(?:\\{1,2}d|\[0-9\])\+\[A-Z(?:a-z)?\]\??\(\?:(?:\\{1,2}\
53
53
  const OWNER_RE = /^\s*\/\/.*phase-id-owner:/;
54
54
  const CANON_REF = 'PHASE_NUMBER_TOKEN_SOURCE';
55
55
 
56
+ // #2761 M3 (trek-e review): the SECOND grammar this seam owns — the BRACKET
57
+ // project-code class of `[CODE.MM]`, spelled `[A-Z][A-Z0-9_]*` (with its
58
+ // case-widened `[A-Za-z]`/`[A-Za-z0-9_]` variant tolerated so a trivial rewrite
59
+ // does not evade the rule). The token guard above only ever knew the phase-
60
+ // NUMBER grammar, so three files re-typed this class verbatim — roadmap-parser's
61
+ // bracket-fallback selector, state's `isMilestoneBounded`, verify's
62
+ // `checkBracketCoherence` — and `check:phase-id-drift` reported clean the whole
63
+ // time. That is the blind spot which let #2761's own "no token literal outside
64
+ // src/phase-id.cts" gate pass while being violated. Build from
65
+ // `BRACKET_PROJECT_CODE_SRC`, `BRACKET_ID_SRC`, `bracketMilestoneIntroSrcFor`
66
+ // or `BRACKET_MILESTONE_INTRO_CAPTURING_SRC` instead.
67
+ const BRACKET_CODE_DRIFT_RE = /\[A-Z(?:a-z)?\]\[A-Z(?:a-z)?0-9_\]\*/;
68
+
69
+ // This rule has NO counterpart to the token rule's `line.includes(CANON_REF)`
70
+ // escape, and that omission is the point.
71
+ //
72
+ // That escape is LINE-level: a line naming the canonical source anywhere on it
73
+ // is taken as built-from-the-owner. verify.cts's copy read
74
+ //
75
+ // new RegExp(`^\\[[A-Z][A-Z0-9_]*\\.(${BRACKET_MILESTONE_NUMERIC_SRC})\\]`, 'i')
76
+ //
77
+ // — an owner reference for the MILESTONE field sharing a line with a re-typed
78
+ // PROJECT-CODE class. A line-level escape waves that through, so a bracket rule
79
+ // that copied it would have kept reporting clean on the very site under review.
80
+ // Partial ownership IS the drift. Only a `// phase-id-owner:` sanction
81
+ // suppresses this rule, and a sanction has to state which half is deliberate.
82
+ const BRACKET_OWNER_HINT =
83
+ 'BRACKET_PROJECT_CODE_SRC / BRACKET_ID_SRC / bracketMilestoneIntroSrcFor / BRACKET_MILESTONE_INTRO_CAPTURING_SRC';
84
+
56
85
  /**
57
86
  * Pure: find every literal re-derivation of the canonical phase-number token in
58
87
  * `text` that is NOT sanctioned. A site is sanctioned when the nearest preceding
@@ -79,6 +108,28 @@ function findPhaseIdRegexDrift(text) {
79
108
  return out;
80
109
  }
81
110
 
111
+ /**
112
+ * Pure: find every literal re-derivation of the BRACKET project-code grammar in
113
+ * `text` that is NOT sanctioned. Same sanction mechanism as the token rule — a
114
+ * dedicated `// phase-id-owner:` comment on the nearest preceding non-blank
115
+ * line — but deliberately WITHOUT its line-level owner-reference escape, so a
116
+ * site that references the owner for one field while re-typing the other is
117
+ * still reported (see BRACKET_CODE_DRIFT_RE's note). Returns [{ line, found }].
118
+ */
119
+ function findBracketGrammarDrift(text) {
120
+ const out = [];
121
+ const lines = text.split('\n');
122
+ for (let i = 0; i < lines.length; i++) {
123
+ const m = BRACKET_CODE_DRIFT_RE.exec(lines[i]);
124
+ if (!m) continue;
125
+ let j = i - 1;
126
+ while (j >= 0 && lines[j].trim() === '') j--; // nearest preceding non-blank line
127
+ if (j >= 0 && OWNER_RE.test(lines[j])) continue;
128
+ out.push({ line: i + 1, found: m[0] });
129
+ }
130
+ return out;
131
+ }
132
+
82
133
  // Authored TypeScript source only (the generated bin/lib/*.cjs mirror it).
83
134
  const SCAN_DIRS = ['src'];
84
135
  const SCAN_EXT = new Set(['.cts', '.ts', '.mts']);
@@ -104,6 +155,67 @@ function walk(dir, acc) {
104
155
  return acc;
105
156
  }
106
157
 
158
+ // ─── #2761 M4: the heading-baseline selector census ────────────────────────
159
+ //
160
+ // `phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.<MODE>)` is the other half
161
+ // of this seam: it decides which intro grammar a call site compiles, and the
162
+ // MODE argument is a fact about that site's history that no behavioural test
163
+ // can recover — flipping verify's milestone-complete site from LABEL_ONLY to
164
+ // ANY_BRACKET grants a tolerance it has never had, and every behavioural test
165
+ // still passes. Pinning it therefore requires reading the authored source.
166
+ //
167
+ // That reading lives HERE, not in the test suite. `tests/**` runs
168
+ // `local/no-source-grep` at ERROR, and its documented exemption
169
+ // (CONTEXT.md: RULESET.TESTS.no-source-grep.exemption) is reserved for tests
170
+ // whose subject is a runtime CONTRACT FILE — STATE.md, config.toml,
171
+ // hooks.json, agent .md — which `src/*.cts` is not. The suite had claimed that
172
+ // exemption anyway. Scripts are the sanctioned home for source scanning (the
173
+ // rule runs at `warn` in `scripts/**`, and this file already scans src/ for the
174
+ // grammar rules above), so the scan is exported as structured data and the test
175
+ // asserts on the returned census instead of on file text.
176
+ const SELECTOR_CALL_RE = /phaseHeadingPrefixSrcFor\(/g;
177
+ const SELECTOR_BASELINE_RE = /phaseHeadingPrefixSrcFor\(\s*PHASE_HEADING_BASELINE\.(ANY_BRACKET|LABEL_ONLY)/g;
178
+
179
+ /**
180
+ * Pure: census the heading-baseline selector calls in `text`.
181
+ *
182
+ * `total` counts EVERY invocation, so a call that does not name a
183
+ * `PHASE_HEADING_BASELINE` member shows up as `total > ANY_BRACKET +
184
+ * LABEL_ONLY` — a hole in the pin rather than a silently uncounted site.
185
+ * Returns { ANY_BRACKET, LABEL_ONLY, total }.
186
+ */
187
+ function countSelectorBaselines(text) {
188
+ const out = { ANY_BRACKET: 0, LABEL_ONLY: 0, total: 0 };
189
+ for (const m of text.matchAll(SELECTOR_BASELINE_RE)) out[m[1]] += 1;
190
+ out.total = (text.match(SELECTOR_CALL_RE) || []).length;
191
+ return out;
192
+ }
193
+
194
+ /**
195
+ * Scan the authored source tree and return the selector census keyed by
196
+ * repo-relative path, for every file that consumes the selector at least once.
197
+ * `phase-id.cts` is excluded: it DEFINES the selector, so its own occurrences
198
+ * are the declaration, not a consumer's choice of baseline.
199
+ */
200
+ function scanSelectorBaselines(root) {
201
+ const census = {};
202
+ for (const dir of SCAN_DIRS) {
203
+ for (const file of walk(path.join(root, dir), [])) {
204
+ const rel = path.relative(root, file);
205
+ if (EXEMPT.has(rel)) continue;
206
+ let text;
207
+ try {
208
+ text = fs.readFileSync(file, 'utf8');
209
+ } catch {
210
+ continue;
211
+ }
212
+ const counts = countSelectorBaselines(text);
213
+ if (counts.total > 0) census[path.basename(file)] = counts;
214
+ }
215
+ }
216
+ return census;
217
+ }
218
+
107
219
  /**
108
220
  * Scan the authored source tree and return every unsanctioned phase-token
109
221
  * re-derivation, each annotated with the repo-relative file path.
@@ -121,7 +233,11 @@ function scanRepo(root) {
121
233
  continue;
122
234
  }
123
235
  for (const d of findPhaseIdRegexDrift(text)) {
124
- violations.push({ file: rel, ...d });
236
+ violations.push({ file: rel, kind: 'token', ...d });
237
+ }
238
+ // #2761 M3: the bracket grammar is the second thing this seam owns.
239
+ for (const d of findBracketGrammarDrift(text)) {
240
+ violations.push({ file: rel, kind: 'bracket', ...d });
125
241
  }
126
242
  }
127
243
  }
@@ -132,19 +248,28 @@ function main() {
132
248
  const root = path.join(__dirname, '..');
133
249
  const violations = scanRepo(root);
134
250
  if (violations.length === 0) {
135
- process.stdout.write('ok phase-id-drift: no unsanctioned phase-token re-derivations outside phase-id.cts\n');
251
+ process.stdout.write('ok phase-id-drift: no unsanctioned phase-token or bracket-grammar re-derivations outside phase-id.cts\n');
136
252
  return;
137
253
  }
138
- process.stderr.write('phase-id-drift: literal re-derivation(s) of the canonical phase-number token found.\n');
139
- process.stderr.write('Build the regex from phase-id.cjs `PHASE_NUMBER_TOKEN_SOURCE` (or phaseMarkdownRegexSource for a\n');
140
- process.stderr.write('known number), or sanction the site with a dedicated `// phase-id-owner: <reason>`\n');
141
- process.stderr.write('comment on the line directly above the regex:\n');
254
+ process.stderr.write('phase-id-drift: literal re-derivation(s) of a canonical grammar found.\n');
255
+ process.stderr.write(`Build the regex from phase-id.cjs \`${CANON_REF}\` (or phaseMarkdownRegexSource for a\n`);
256
+ process.stderr.write(`known number) for the phase-number token, or from ${BRACKET_OWNER_HINT}\n`);
257
+ process.stderr.write('for the bracket grammar — or sanction the site with a dedicated\n');
258
+ process.stderr.write('`// phase-id-owner: <reason>` comment on the line directly above the regex:\n');
142
259
  for (const d of violations) {
143
- process.stderr.write(` ${d.file}:${d.line} ${d.found}\n`);
260
+ process.stderr.write(` [${d.kind}] ${d.file}:${d.line} ${d.found}\n`);
144
261
  }
145
262
  process.exitCode = 1;
146
263
  }
147
264
 
148
265
  if (require.main === module) main();
149
266
 
150
- module.exports = { findPhaseIdRegexDrift, scanRepo, TOKEN_DRIFT_RE };
267
+ module.exports = {
268
+ findPhaseIdRegexDrift,
269
+ findBracketGrammarDrift,
270
+ scanRepo,
271
+ countSelectorBaselines,
272
+ scanSelectorBaselines,
273
+ TOKEN_DRIFT_RE,
274
+ BRACKET_CODE_DRIFT_RE,
275
+ };
@@ -370,8 +370,45 @@ function writeBaseline(root, violations) {
370
370
  return entries;
371
371
  }
372
372
 
373
+ /**
374
+ * Resolve the scan root for a CLI run. Default: this repo — exactly what
375
+ * `lint:ci` invokes. `--root <dir>` overrides it (#3640) so the CLI
376
+ * end-to-end test can prove the guard's FAIL path against an isolated temp
377
+ * tree instead of writing its fixture into the shared `gsd-core/workflows/`
378
+ * that parallel test chunks observe mid-lifecycle (the emitted-provenance
379
+ * manifest build copies the transient file into all 19 runtime manifests;
380
+ * the #3333 TOCTOU ENOENT crash was the same writer's first symptom).
381
+ *
382
+ * Usage errors exit 2 — a code distinct from the guard's own findings exit
383
+ * (1), so tests and CI can tell "bad invocation" from "drift found" without
384
+ * parsing stderr. Fail-closed on every bad shape, because a wrong root is
385
+ * not harmless: `--root --update` would otherwise resolve the flag itself
386
+ * into a cwd-relative path and --update would happily `mkdir -p` a stray
387
+ * baseline tree inside the shared source directory (#3640's write class).
388
+ * A value that starts with `-` (another flag) or does not name an existing
389
+ * directory is therefore a usage error, never a scan root. With repeated
390
+ * `--root` flags the first wins (same first-match convention as the boolean
391
+ * `--update` parse).
392
+ */
393
+ function resolveScanRoot() {
394
+ const rootIdx = process.argv.indexOf('--root');
395
+ if (rootIdx === -1) return path.join(__dirname, '..');
396
+ const value = process.argv[rootIdx + 1];
397
+ const isDir = value !== undefined
398
+ && !value.startsWith('-')
399
+ && fs.existsSync(value)
400
+ && fs.statSync(value).isDirectory();
401
+ if (!isDir) {
402
+ process.stderr.write('planning-prompt-drift: --root requires an existing directory argument (usage: --root <dir>)\n');
403
+ process.exitCode = 2;
404
+ return null;
405
+ }
406
+ return path.resolve(value);
407
+ }
408
+
373
409
  function main() {
374
- const root = path.join(__dirname, '..');
410
+ const root = resolveScanRoot();
411
+ if (root === null) return;
375
412
  const update = process.argv.includes('--update');
376
413
  const violations = scanRepo(root);
377
414
 
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * lint-portable-grep.cjs — ban GNU-only `grep -P`/`--perl-regexp` in gsd
6
+ * workflow / agent / reference / command markdown (#4112 macOS regression).
7
+ *
8
+ * ## Why
9
+ *
10
+ * `-P`/`--perl-regexp` selects GNU grep's PCRE engine. Stock macOS ships BSD
11
+ * grep, which does not implement `-P` at all — the call errors out ("grep:
12
+ * invalid option -- P" or "unknown option") on that host, and a pipeline like
13
+ * `... | grep -oP '...' || true` swallows that error and silently resolves to
14
+ * an empty string instead of failing loudly.
15
+ *
16
+ * This is exactly what happened in `gsd-core/workflows/pause-work.md`'s
17
+ * Context Detection step (#4112, merged as #4140): the fix for the `$((`
18
+ * shell-syntax bug left `grep -oP 'phases/\K[^/]+'` in place. That construct
19
+ * had never been exercised on macOS before (the syntax error masked it), so
20
+ * fixing the syntax bug newly exposed a second, pre-existing macOS-only
21
+ * defect — phase/spike/sketch detection silently resolving to "" on macOS —
22
+ * which the PR's own new regression test then caught, but only in the
23
+ * post-merge `full test (macos-latest, ...)` matrix. That lane runs on push
24
+ * to the base branch, not on the pull_request event, so nothing pre-merge
25
+ * (gsd-test's Linux-only benches included) could have caught it before `next`
26
+ * went red.
27
+ *
28
+ * This is the same class of defect `lint-portable-timeout.cjs` (#2351) exists
29
+ * for: a GNU-coreutils assumption baked into workflow markdown that every
30
+ * pre-merge gate is blind to because none of them run on macOS. The fix here
31
+ * is the same shape — a static ratchet that catches the assumption in the
32
+ * markdown itself, before a shell ever executes it on the platform that lacks
33
+ * the flag.
34
+ *
35
+ * ## What PASSES
36
+ *
37
+ * - `grep -o '...'`, `grep -E '...'`, `egrep`/`fgrep` without `-P`.
38
+ * - `-P` appearing as part of an unrelated long option (`--path`, `--perl`) —
39
+ * only a `-P`/short-cluster-containing-`P` token or the literal
40
+ * `--perl-regexp` counts.
41
+ * - Prose that merely mentions "grep -P" outside of a fenced command context
42
+ * is still flagged (this lint does not parse fences) — the roots below are
43
+ * restricted to directive markdown where a `grep -P` mention is always
44
+ * either a real invocation or a specimen to fix, never neutral prose.
45
+ *
46
+ * ## What FAILS
47
+ *
48
+ * A `grep`/`egrep`/`fgrep` invocation whose flag cluster includes `P`
49
+ * (`-P`, `-oP`, `-Po`, `-iP`, ...) or the long form `--perl-regexp`.
50
+ *
51
+ * ## Portable replacements
52
+ *
53
+ * - Extracting a path component: `basename "$(dirname "$path")")` instead of
54
+ * `grep -oP 'seam/\K[^/]+'`.
55
+ * - Extracting a token after a flag: `sed -E 's#.*--flag[[:space:]]+([^[:space:]]+).*#\1#'`
56
+ * instead of `grep -oP '(?<=--flag )\S+'` — POSIX ERE (`-E`) has no
57
+ * lookbehind, so capture the group and let `sed` emit just it. `-E` (not
58
+ * GNU-only `-r`) is supported by both BSD and GNU `sed`. (A `/`-delimited
59
+ * sed command ending in a star-quantifier would place a star immediately
60
+ * before a slash, closing this very JSDoc block comment early — hence the
61
+ * `#`-delimited form used above.)
62
+ */
63
+
64
+ const fs = require('fs');
65
+ const path = require('path');
66
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
67
+
68
+ const ROOT = path.join(__dirname, '..');
69
+
70
+ // Surfaces whose markdown carries agent-executed bash. Mirrors
71
+ // lint-portable-timeout.cjs's roots — the same files that can ship shell
72
+ // snippets which a bash/zsh host on macOS actually executes.
73
+ const DEFAULT_ROOTS = ['gsd-core/workflows', 'gsd-core/references', 'agents', 'commands'];
74
+
75
+ // A `grep`/`egrep`/`fgrep` token, anchored to a command position (line start,
76
+ // right after `| & ; ( \` {`, or right after a shell keyword that opens a new
77
+ // command — `then`/`do`/`else`/`elif`) so prose mentioning "grep -P" in
78
+ // running text is still caught in these directive-markdown roots (see file
79
+ // header) without also matching an unrelated word ending in "grep". The
80
+ // keyword alternative is `\b`-bounded on both sides, so a bare mention of the
81
+ // word "then"/"do"/etc. with no following grep never matches on its own.
82
+ const GREP_INVOCATION_RE = /(?:^|[|&;(`{]|\b(?:then|do|else|elif)\b)[ \t]*(?:e|f)?grep\b/g;
83
+
84
+ // A `-P` short-flag cluster (e.g. `-P`, `-oP`, `-Po`, `-iP`) or the long form
85
+ // `--perl-regexp`, as a standalone token.
86
+ const PERL_FLAG_RE = /(?:^|\s)-[a-zA-Z]*P[a-zA-Z]*(?=\s|$)|--perl-regexp\b/;
87
+
88
+ /**
89
+ * Locate `grep -P`/`--perl-regexp` invocations in a block of text.
90
+ *
91
+ * Pure (no I/O): callers pass file contents; the caller reads files. For each
92
+ * `grep`-family invocation on a line, inspects only the segment from that
93
+ * invocation to the next `|`/`;`/end-of-line, so a `-P`-bearing token in a
94
+ * later, unrelated command on the same line is never mistaken for a grep flag.
95
+ *
96
+ * @param {string} text file contents
97
+ * @returns {{ line: number, snippet: string }[]} findings (empty array = clean)
98
+ */
99
+ function findPerlGrepInvocations(text) {
100
+ const findings = [];
101
+ const lines = String(text).split(/\r?\n/);
102
+ for (let i = 0; i < lines.length; i += 1) {
103
+ const line = lines[i];
104
+ GREP_INVOCATION_RE.lastIndex = 0;
105
+ let match;
106
+ while ((match = GREP_INVOCATION_RE.exec(line)) !== null) {
107
+ const startAt = match.index + match[0].length;
108
+ const rest = line.slice(startAt);
109
+ const nextPipe = rest.search(/[|;`]/);
110
+ const segment = nextPipe === -1 ? rest : rest.slice(0, nextPipe);
111
+ if (PERL_FLAG_RE.test(segment)) {
112
+ findings.push({ line: i + 1, snippet: line.trim() });
113
+ break; // one finding per line is enough context to fix it
114
+ }
115
+ }
116
+ }
117
+ return findings;
118
+ }
119
+
120
+ function walkMarkdown(dir) {
121
+ const out = [];
122
+ let entries;
123
+ try {
124
+ entries = fs.readdirSync(dir, { withFileTypes: true });
125
+ } catch {
126
+ return out; // a missing root is not an error — some surfaces are optional
127
+ }
128
+ for (const entry of entries) {
129
+ const full = path.join(dir, entry.name);
130
+ if (entry.isDirectory()) out.push(...walkMarkdown(full));
131
+ else if (entry.isFile() && entry.name.endsWith('.md')) out.push(full);
132
+ }
133
+ return out;
134
+ }
135
+
136
+ /**
137
+ * Scan the given roots (repo-relative) for `grep -P`/`--perl-regexp` invocations.
138
+ * @param {string[]} roots
139
+ * @returns {{ file: string, line: number, snippet: string }[]}
140
+ */
141
+ function scan(roots = DEFAULT_ROOTS) {
142
+ const offenders = [];
143
+ for (const rel of roots) {
144
+ const abs = path.isAbsolute(rel) ? rel : path.join(ROOT, rel);
145
+ for (const file of walkMarkdown(abs)) {
146
+ const findings = findPerlGrepInvocations(fs.readFileSync(file, 'utf8'));
147
+ for (const f of findings) {
148
+ offenders.push({ file: path.relative(ROOT, file), line: f.line, snippet: f.snippet });
149
+ }
150
+ }
151
+ }
152
+ return offenders;
153
+ }
154
+
155
+ function main() {
156
+ const rootsEnv = process.env.GSD_LINT_PORTABLE_GREP_ROOTS;
157
+ const roots = rootsEnv ? rootsEnv.split(path.delimiter).filter(Boolean) : DEFAULT_ROOTS;
158
+ const offenders = scan(roots);
159
+ if (offenders.length > 0) {
160
+ const detail = offenders.map((o) => ` ${o.file}:${o.line} ${o.snippet}`).join('\n');
161
+ throw new ExitError(
162
+ 1,
163
+ 'lint-portable-grep: `grep -P`/`--perl-regexp` is not portable — stock macOS ships\n' +
164
+ 'BSD grep, which has no `-P`, so the call errors and a `|| true`/`$(...)` fallback\n' +
165
+ 'silently resolves to empty output instead of failing loudly (#4112). Use\n' +
166
+ '`basename "$(dirname "$path")")` for path-component extraction, or\n' +
167
+ '`sed -E \'s/.../\\1/\'` (POSIX ERE, no lookbehind needed) for token extraction:\n' +
168
+ detail,
169
+ );
170
+ }
171
+ console.log(`ok lint-portable-grep: no GNU-only grep -P invocations in ${roots.length} root(s)`);
172
+ }
173
+
174
+ module.exports = { findPerlGrepInvocations, scan, DEFAULT_ROOTS };
175
+
176
+ if (require.main === module) runMain(main);