@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
@@ -0,0 +1,524 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-response-language-coverage.cjs
4
+ *
5
+ * Enforces #2529: every workflow must be covered by the response-language
6
+ * contract, so a workflow can never ship English-only when the user has
7
+ * configured `response_language`.
8
+ *
9
+ * A workflow file passes when it contains EITHER:
10
+ * - a reference to the shared directive
11
+ * (`references/response-language-directive.md`), OR
12
+ * - its own inline `response_language` directive (the ~half of the catalog
13
+ * that already carried one before #2529, plus workflow-specific extracts
14
+ * like `references/execute-phase-response-language.md`).
15
+ *
16
+ * A bare config-field mention is not coverage: the same line must direct how
17
+ * user-facing output is rendered, or the workflow must load a known directive.
18
+ *
19
+ * Nor is a directive that names only the question/answer surface. #2529's stated
20
+ * defect is that inter-tool NARRATION stays in English while the answers around
21
+ * it are translated, so a line saying "all user-facing questions, prompts, and
22
+ * explanations" describes the gap rather than closing it. Coverage therefore
23
+ * requires a narration-class token as well — see `NARRATION_CLASS_RE`.
24
+ *
25
+ * A workflow FRAGMENT (`<workflow>/<modes|steps|templates>/<name>.md`, the shape
26
+ * the #1671 fragment epic extracts) additionally passes when its parent workflow
27
+ * names that exact fragment path and is itself covered — see
28
+ * `inheritsParentCoverage`, which proves the inheritance per file instead of
29
+ * granting it to a directory.
30
+ *
31
+ * Exit 0 only when workflows were actually found AND every one of them is
32
+ * covered; exit 1 with a per-file listing if not. "No violations" alone is not
33
+ * a pass: a run that inspected nothing has established nothing.
34
+ */
35
+
36
+ 'use strict';
37
+
38
+ const fs = require('fs');
39
+ const path = require('path');
40
+
41
+ const ROOT = path.join(__dirname, '..');
42
+ const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
43
+ // A reference resolves as a sibling of the workflow catalog, so a fixture tree
44
+ // and the real one resolve by one rule.
45
+ const REFERENCE_ROOT = path.join(ROOT, 'gsd-core');
46
+ const DIRECTIVE_REFS = [
47
+ 'references/response-language-directive.md',
48
+ 'references/execute-phase-response-language.md',
49
+ ];
50
+ // Names the narration class explicitly for the same reason the inline directives
51
+ // in the workflow bodies do (see NARRATION_CLASS_RE): "user-facing prose" alone
52
+ // reads, in practice, as the question/answer surface, which is the half of the
53
+ // output #2529 was never about.
54
+ const INLINE_RESPONSE_LANGUAGE_DIRECTIVE =
55
+ 'Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.';
56
+ // THE RULE THAT DECIDES THIS SET (#2529, restated in review round 29).
57
+ // A lazy-loaded mode/step/template carries its own directive only when it cannot
58
+ // PROVE inheritance -- no parent dispatches it from a read/execute context, or the
59
+ // parent is itself uncovered. Where inheritance is proven, the parent's directive is
60
+ // already in the loaded context by the time the fragment is read, so a second copy
61
+ // buys no coverage and adds a sentence that can drift. Pinning the exact wording is
62
+ // what makes the first case safe: these files cannot take the eager @-reference (an
63
+ // @-line inside a later Read is inert), so the sentence lives inline, and a partial
64
+ // typo or rewording would otherwise split the contract silently.
65
+ // enforces the rule in both directions:
66
+ // no member of this set may be one that would inherit anyway.
67
+ const EXACT_INLINE_DIRECTIVE_WORKFLOWS = new Set([
68
+ 'discuss-phase/modes/advisor.md',
69
+ 'discuss-phase/modes/all.md',
70
+ 'discuss-phase/modes/analyze.md',
71
+ 'discuss-phase/modes/auto.md',
72
+ 'discuss-phase/modes/batch.md',
73
+ 'discuss-phase/modes/chain.md',
74
+ 'discuss-phase/modes/default.md',
75
+ 'discuss-phase/modes/power.md',
76
+ 'discuss-phase/modes/text.md',
77
+ 'discuss-phase/templates/context.md',
78
+ 'discuss-phase/templates/discussion-log.md',
79
+ 'execute-phase/steps/codebase-drift-gate.md',
80
+ 'execute-phase/steps/regression-gate-run.md',
81
+ 'execute-phase/steps/worktree-recovery-policy.md',
82
+ 'help/modes/brief.md',
83
+ 'help/modes/default.md',
84
+ 'help/modes/full.md',
85
+ 'help/modes/topic.md',
86
+ 'plan-phase/steps/prd-express-path.md',
87
+ 'quick-batch/steps/plan-checker-loop.md',
88
+ 'settings-advanced.md',
89
+ ]);
90
+ // This lint once carried a third coverage form, for `verify-phase.md`: a
91
+ // workflow file nothing entered directly, covered instead by the directive
92
+ // execute-phase.md injects into its `gsd-verifier` dispatch prompt. `next`
93
+ // deleted that workflow in #3421 (an orphan that shipped ~40 KB to every runtime
94
+ // and was never loaded) and migrated its live gates into the verifier, so the
95
+ // form has no subject left and is gone with it.
96
+ //
97
+ // The dispatch contract itself is NOT gone — the verifier subagent still runs and
98
+ // still emits user-facing prose, so `references/execute-phase-response-language.md`
99
+ // still tells the orchestrator to carry the directive into that prompt. It is
100
+ // simply no longer a statement about any file in this catalog, which is all this
101
+ // lint reads. `tests/response-language-coverage.test.cjs` pins both ends of it
102
+ // (the reference's directive text and the `Create VERIFICATION.md.` anchor it
103
+ // positions against in execute-phase.md) directly against the real tree.
104
+ // Fragment directories produced by the workflow-fragment epic (#1671). A file
105
+ // under one of these is a SECTION of its parent workflow, never an entry point:
106
+ // it is reached by a `read and execute gsd-core/workflows/<path>` stub, which
107
+ // fires with the parent — and therefore the parent's response-language
108
+ // directive — already loaded.
109
+ //
110
+ // The stub is USUALLY in the top-level parent, which is the only case
111
+ // `inheritsParentCoverage` can prove, and it deliberately proves no more. Four
112
+ // fragments arrive by a different route today, and none of them relies on this
113
+ // function: `execute-phase/steps/regression-gate-run.md`,
114
+ // `plan-phase/steps/prd-express-path.md` and
115
+ // `quick-batch/steps/plan-checker-loop.md` are dispatched by a SIBLING fragment
116
+ // (`regression-gate.md`, `prd-express-gate.md`, `planner-wave.md`), and
117
+ // `help/modes/topic.md` is routed from a table in `help.md` that names the path
118
+ // without a dispatch verb at all. All four carry the pinned inline directive and
119
+ // are listed in `EXACT_INLINE_DIRECTIVE_WORKFLOWS`, so `findViolations` settles
120
+ // them before inheritance is ever consulted.
121
+ //
122
+ // Left as one hop on purpose. Walking the sibling chain would let a fragment
123
+ // inherit through a file this lint has not proven reachable, trading a loud
124
+ // failure for a quiet assumption; as it stands, extracting a fragment-of-a-
125
+ // fragment without pinning it turns the lint RED, which is the correct answer
126
+ // and names the file to fix.
127
+ const FRAGMENT_DIRS = new Set(['modes', 'steps', 'templates']);
128
+ const DIRECTIVE_ACTION_RE = /\b(?:apply|present|render|respond|translate|use|write|must|should)\b/i;
129
+ const USER_OUTPUT_RE = /\b(?:explanations?|language|narration|outputs?|prompts?|prose|questions?|templates?|user-facing)\b/i;
130
+ // The defect #2529 reports is NARRATION, not the question/answer surface: a
131
+ // directive worded as "all user-facing questions, prompts, and explanations" is
132
+ // read as covering what the user is asked and told at a turn boundary, and the
133
+ // running commentary the model emits between tool calls stays in English beside
134
+ // it. A line that names only that surface therefore certifies the exact gap the
135
+ // issue exists to close, so coverage additionally requires a narration-class
136
+ // token — narration, status, progress, findings, or output "between tool calls".
137
+ // Round 21 tightening: the accepted forms are the two that NAME the class — the
138
+ // word "narration", or output described as running "between tool calls". The
139
+ // earlier list also accepted a bare "status", "progress" or "findings", which
140
+ // are words that merely APPEAR in the canonical phrasing: a line reading "Use
141
+ // response_language for all user-facing output; report status." passed while
142
+ // naming nothing about inter-tool narration, i.e. weaker than REQ-LANG-04
143
+ // requires. Verified before tightening: all 79 catalog files that passed under
144
+ // the old list still pass under this one, so no workflow needed rewording — the
145
+ // dropped tokens were reachable only by directives nobody ships.
146
+ // Still deliberately not a phrase match: pinning one sentence would push authors
147
+ // toward copying wording instead of stating the rule (`tests/response-language-coverage.test.cjs`
148
+ // pins the old weak wording as a FAILING case so this cannot silently loosen).
149
+ const NARRATION_CLASS_RE = /\bnarration\b|\bbetween tool calls\b/i;
150
+ // The catalog's extensions, matched case-insensitively and by whole extension.
151
+ // `endsWith('.md')` failed in the one direction this lint cannot afford: it is
152
+ // silent UNDER-enforcement, the same vacuous pass `main()` refuses when
153
+ // discovery returns nothing. `SETTINGS.MD` is the same file to Windows and
154
+ // macOS and a different one to Linux, so a case-sensitive test exempts it on
155
+ // the only platform whose verdict gates the merge — a workflow certified by
156
+ // having gone unseen.
157
+ // `.mdx` stays out deliberately. Admitting an extension states what a workflow
158
+ // IS, and that claim has a second half: `inheritsParentCoverage` resolves a
159
+ // fragment's parent as `<workflow>.md`. If the repo ever emits an `.mdx`
160
+ // workflow, the entry belongs here next to the parent resolution it must move
161
+ // with, not ahead of it.
162
+ const WORKFLOW_EXTENSIONS = new Set(['.md']);
163
+
164
+ function isWorkflowFile(name) {
165
+ return WORKFLOW_EXTENSIONS.has(path.extname(name).toLowerCase());
166
+ }
167
+
168
+ /**
169
+ * Walk the catalog, FOLLOWING symlinked subtrees.
170
+ *
171
+ * `Dirent` uses lstat semantics, so a symlinked directory answers false to both
172
+ * `isDirectory()` and `isFile()`. Branching on those alone skipped a symlinked
173
+ * subtree in silence while `files.length > 0` kept the run green — the exact
174
+ * "reported OK while coverage regressed" outcome `main()` claims to make
175
+ * impossible. Each symlink is resolved with `statSync` and followed.
176
+ *
177
+ * Cycles are bounded by the `seen` set of resolved real paths: a link pointing
178
+ * at an ancestor is entered once and not re-entered. A broken link resolves to
179
+ * nothing and is skipped — it contributes no file to inspect, and no coverage
180
+ * claim rides on it.
181
+ */
182
+ function findMarkdownFilesRecursive(dir, seen = new Set()) {
183
+ const files = [];
184
+ let real;
185
+ try { real = fs.realpathSync(dir); } catch { real = dir; }
186
+ if (seen.has(real)) return files;
187
+ seen.add(real);
188
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
189
+ const full = path.join(dir, entry.name);
190
+ let isDirectory = entry.isDirectory();
191
+ let isFile = entry.isFile();
192
+ if (entry.isSymbolicLink()) {
193
+ let stat;
194
+ try { stat = fs.statSync(full); } catch { continue; }
195
+ isDirectory = stat.isDirectory();
196
+ isFile = stat.isFile();
197
+ }
198
+ if (isDirectory) files.push(...findMarkdownFilesRecursive(full, seen));
199
+ else if (isFile && isWorkflowFile(entry.name)) files.push(full);
200
+ }
201
+ return files.sort();
202
+ }
203
+
204
+ /**
205
+ * Does the file LOAD a shared directive, rather than merely name one?
206
+ *
207
+ * The distinction is the same one `namesFragmentAsEntryPoint` draws a level
208
+ * down, and it matters for the same reason: only a real `@`-import puts the
209
+ * directive in context. A changelog line ("#2529: added the shared
210
+ * response-language-directive.md reference"), a deprecation note, or any prose
211
+ * naming the path proves nothing about what loads — but a bare substring test
212
+ * cannot tell those apart, so such a workflow would be certified covered while
213
+ * shipping the very defect #2529 exists to close.
214
+ *
215
+ * The catalog emits the import exactly one way: an `@`-path occupying the whole
216
+ * line — line 1 for the 43 top-level workflows, line 96 for `execute-phase.md`.
217
+ * Both `@~/` and `@$HOME/` spellings are accepted because the repo writes both
218
+ * (see `scripts/strip-prose-atrefs.cjs`).
219
+ *
220
+ * LIMITATION, stated rather than papered over: like the fragment check, this
221
+ * reads Markdown as text. An import inside a fenced block quoted as an example
222
+ * still counts, and one whose line carries a trailing `<!-- ... -->` no longer
223
+ * does. Neither shape occurs in the catalog today, and both would be visible in
224
+ * review as a quoted or edited import.
225
+ */
226
+ const DIRECTIVE_IMPORT_RES = DIRECTIVE_REFS.map(
227
+ (ref) => new RegExp(`^@(?:~|\\$HOME)/\\S*${ref.replaceAll('.', '\\.')}$`),
228
+ );
229
+
230
+ function importsDirectiveReference(content) {
231
+ return importedDirectiveReference(content) !== null;
232
+ }
233
+
234
+ /** Which known reference does this file import, if any? */
235
+ function importedDirectiveReference(content) {
236
+ const lines = content.split(/\r?\n/).map((line) => line.trim());
237
+ for (const [index, ref] of DIRECTIVE_REFS.entries()) {
238
+ if (lines.some((line) => DIRECTIVE_IMPORT_RES[index].test(line))) return ref;
239
+ }
240
+ return null;
241
+ }
242
+
243
+ /**
244
+ * The four-predicate test for an actionable directive on a single line.
245
+ *
246
+ * LIMITATION (text, not AST): the four hits are independent, so the predicate
247
+ * reads vocabulary and not polarity. A line that negates the instruction —
248
+ * "Do not translate narration; apply response_language to code only." — satisfies
249
+ * all four and reads as coverage. Contrived rather than plausible: the wording is
250
+ * pinned byte-for-byte in the files that cannot take the reference, and the shared
251
+ * reference is validated against this same predicate, so a negated line would have
252
+ * to be authored deliberately in one of the 44 files that carry their own sentence.
253
+ * Named here because a predicate that cannot see "not" should say so.
254
+ */
255
+ function carriesInlineDirective(content) {
256
+ return content.split(/\r?\n/).some((line) =>
257
+ /\bresponse_language\b/i.test(line) &&
258
+ DIRECTIVE_ACTION_RE.test(line) &&
259
+ USER_OUTPUT_RE.test(line) &&
260
+ NARRATION_CLASS_RE.test(line)
261
+ );
262
+ }
263
+
264
+ /**
265
+ * Does the SHARED FILE a workflow imports itself carry an actionable directive?
266
+ *
267
+ * Checking only that the `@`-import LINE exists is a hole the size of the
268
+ * bucket: 43 workflows take their entire coverage from one file, and nothing in
269
+ * the repo pinned that file's text. Rewriting it back to the pre-#2529 wording —
270
+ * the sentence this PR's own round-10 finding calls the DEFECT — left the lint
271
+ * green and every workflow "covered". That is the same "the gate certifies the
272
+ * defect" failure the round-10 fix closed, one level up.
273
+ *
274
+ * So an import is coverage only when the referenced file passes the very
275
+ * predicate an inline directive must pass. A missing, unreadable, or weakened
276
+ * reference covers nobody, and `main()` reports it as one systemic failure
277
+ * rather than as 43 identical per-file violations.
278
+ */
279
+ // Keyed by identity AND version, not by path alone. A path-only key is correct
280
+ // for a one-shot CLI and for the tests, where every fixture gets a fresh mkdtemp
281
+ // root — but it makes the verdict for a path permanent within the process, so a
282
+ // caller that rewrites a reference and re-asks gets the stale answer. Stamping
283
+ // size and mtime into the key costs one stat and removes the class rather than
284
+ // documenting it.
285
+ const referenceDirectiveCache = new Map();
286
+ function referenceCarriesDirective(ref, refRoot) {
287
+ const file = path.join(refRoot, ref);
288
+ let stamp = 'missing';
289
+ try {
290
+ const stat = fs.statSync(file);
291
+ stamp = `${stat.size}:${stat.mtimeMs}`;
292
+ } catch { /* keep the missing stamp; the read below decides the verdict */ }
293
+ const key = [refRoot, ref, stamp].join('\u0000');
294
+ if (!referenceDirectiveCache.has(key)) {
295
+ let ok = false;
296
+ try { ok = carriesInlineDirective(fs.readFileSync(file, 'utf8')); }
297
+ catch { ok = false; }
298
+ referenceDirectiveCache.set(key, ok);
299
+ }
300
+ return referenceDirectiveCache.get(key);
301
+ }
302
+
303
+ /**
304
+ * Of the references these files actually import, which are missing, unreadable,
305
+ * or no longer actionable?
306
+ *
307
+ * Scoped to imported references on purpose: a reference nobody imports cannot
308
+ * uncover anybody, so failing on it would red a run over a file it never
309
+ * consulted. Every reference that IS imported is load-bearing for every workflow
310
+ * that imports it.
311
+ */
312
+ function findBrokenDirectiveReferences(files, refRoot = REFERENCE_ROOT) {
313
+ const imported = new Set();
314
+ for (const file of files) {
315
+ let content;
316
+ try { content = fs.readFileSync(file, 'utf8'); } catch { continue; }
317
+ const ref = importedDirectiveReference(content);
318
+ if (ref !== null) imported.add(ref);
319
+ }
320
+ return [...imported].filter((ref) => !referenceCarriesDirective(ref, refRoot)).sort();
321
+ }
322
+
323
+ function hasResponseLanguageCoverage(content, refRoot = REFERENCE_ROOT) {
324
+ const ref = importedDirectiveReference(content);
325
+ if (ref !== null) return referenceCarriesDirective(ref, refRoot);
326
+
327
+ return carriesInlineDirective(content);
328
+ }
329
+
330
+ /**
331
+ * How far a read/execute verb may sit from the fragment path it governs on the
332
+ * same line. Sized from the widest shape the catalog actually emits —
333
+ * ``read and execute `gsd-core/workflows/...` `` (14 characters between verb and
334
+ * path) — with room for a variant, and deliberately far short of a sentence, so
335
+ * a verb belonging to a different clause cannot reach across and vouch for a
336
+ * path it never dispatches.
337
+ */
338
+ const ENTRY_POINT_VERB_WINDOW = 40;
339
+ const ENTRY_POINT_VERB_RE = new RegExp(`\\b(?:read|execute|run)\\b.{0,${ENTRY_POINT_VERB_WINDOW}}$`, 'i');
340
+
341
+ /**
342
+ * Does the parent name this fragment as a live ENTRY POINT, rather than merely
343
+ * mentioning its path?
344
+ *
345
+ * The distinction is the whole basis of inherited coverage: the inheritance is
346
+ * sound only because the fragment cannot be reached except through the parent's
347
+ * dispatch stub, which fires with the parent's directive already loaded. A path
348
+ * that appears in a changelog line, a deprecation note, or a comment proves
349
+ * nothing about how the fragment is reached — but a bare substring test cannot
350
+ * tell those apart, so before this check any mention at all granted coverage.
351
+ *
352
+ * The catalog emits exactly one shape, in five spellings: ``read and execute
353
+ * `<path>` ``, ``Read+execute `<path>` ``, ``Read `<path>` if <condition>``,
354
+ * ``run `<path>` to <effect>`` (the spelling #1689's per-plan executor routing
355
+ * introduced), and the same stub written with the path RELATIVE to the catalog
356
+ * rather than rooted at `gsd-core/workflows/` (#3552's `branching_strategy:
357
+ * none` arm). A read/execute/run verb within `ENTRY_POINT_VERB_WINDOW`
358
+ * characters ahead of the path on the SAME LINE covers all five.
359
+ *
360
+ * LIMITATION, stated rather than papered over: this reads Markdown as text, not
361
+ * as a parsed document. A dispatch stub that a future edit comments out with
362
+ * `<!-- ... -->`, or moves inside a fenced block quoted as an example, still
363
+ * matches — the verb and the path are both still there. Distinguishing those
364
+ * needs a Markdown parser, and the cheap approximations (tracking fence state,
365
+ * skipping `<!--` lines) buy little: neither shape occurs in the catalog today,
366
+ * and both would be visible in review as a deleted dispatch. What this DOES
367
+ * close is the common, invisible case — a path mentioned in prose that never
368
+ * dispatches anything. `tests/response-language-coverage.test.cjs` pins both the
369
+ * accepted spellings and the rejected mention forms.
370
+ *
371
+ * SECOND LIMITATION, same class: a dispatch line may sit inside a
372
+ * `<!-- gsd:section id="X" when="…" -->` guard, and nothing here checks that the
373
+ * guard is satisfiable or that its `id` still matches the fragment it gates.
374
+ * Every such pairing lines up today. If one drifts, the fragment becomes
375
+ * unreachable while this function still calls it dispatched — an orphan the lint
376
+ * reports as covered. That is not the defect #2529 is about: a dispatch that
377
+ * never fires renders nothing, so no English prose reaches the user through it.
378
+ * The gap is a soundness one, worth naming rather than implying.
379
+ */
380
+ function namesFragmentAsEntryPoint(parent, relative) {
381
+ // Two spellings of the same dispatch: catalog-rooted and catalog-RELATIVE.
382
+ // #3552's `"none"` arm introduced the second one — `Read and execute
383
+ // `execute-phase/steps/protected-branch.md`` — and a rooted-only needle read
384
+ // that live dispatch as no dispatch at all.
385
+ const needles = [`gsd-core/workflows/${relative}`, relative];
386
+ return parent.split(/\r?\n/).some((line) => needles.some((needle) => {
387
+ const at = line.indexOf(needle);
388
+ // A path character immediately before the match means this is the TAIL of
389
+ // some longer path, not the fragment itself: `vendor/<relative>` must not
390
+ // grant `<relative>` coverage. The rooted needle carries its own prefix and
391
+ // is matched on its own, so nothing legitimate is lost here.
392
+ if (at === -1 || (at > 0 && /[\w/.-]/.test(line.charAt(at - 1)))) return false;
393
+ return ENTRY_POINT_VERB_RE.test(line.slice(0, at));
394
+ }));
395
+ }
396
+
397
+ /**
398
+ * A fragment inherits its parent workflow's coverage, but only when the
399
+ * inheritance is PROVEN per file rather than assumed from the directory:
400
+ * 1. the path is `<workflow>/<fragment-dir>/<name>.md`,
401
+ * 2. `<workflow>.md` exists and dispatches this exact fragment path from a
402
+ * read/execute context — i.e. the parent really is the way in, not just a
403
+ * file that happens to spell the name (see `namesFragmentAsEntryPoint`), and
404
+ * 3. the parent is itself covered.
405
+ * A fragment nobody dispatches, or one hanging off an uncovered parent, stays a
406
+ * violation. Without this the lint reds on every new fragment the #1671 epic
407
+ * extracts, even though the extraction moved prose that was already covered.
408
+ */
409
+ function inheritsParentCoverage(workflowsDir, relative, refRoot = REFERENCE_ROOT) {
410
+ const segments = relative.split('/');
411
+ if (segments.length !== 3 || !FRAGMENT_DIRS.has(segments[1])) return false;
412
+ const parentPath = path.join(workflowsDir, `${segments[0]}.md`);
413
+ if (!fs.existsSync(parentPath)) return false;
414
+ const parent = fs.readFileSync(parentPath, 'utf8');
415
+ if (!namesFragmentAsEntryPoint(parent, relative)) return false;
416
+ return hasResponseLanguageCoverage(parent, refRoot);
417
+ }
418
+
419
+ function findViolations(workflowsDir, refRoot = REFERENCE_ROOT) {
420
+ return findMarkdownFilesRecursive(workflowsDir).filter((file) => {
421
+ const relative = path.relative(workflowsDir, file).replaceAll(path.sep, '/');
422
+ const content = fs.readFileSync(file, 'utf8');
423
+ if (EXACT_INLINE_DIRECTIVE_WORKFLOWS.has(relative)) {
424
+ if (content.split(/\r?\n/).includes(INLINE_RESPONSE_LANGUAGE_DIRECTIVE)) return false;
425
+ // The pin exists because these files cannot take the eager @-reference, not
426
+ // because the reference is worse. If one ever CAN take it -- a fragment that
427
+ // becomes eagerly loaded -- that conversion is strictly better than the pin and
428
+ // must not read as a violation. Only the reference form is admitted here: its
429
+ // wording is validated in turn by findBrokenDirectiveReferences, so the contract
430
+ // survives the swap. An arbitrary reworded inline line stays a violation, which
431
+ // is the whole point of pinning.
432
+ return !importsDirectiveReference(content) || !hasResponseLanguageCoverage(content, refRoot);
433
+ }
434
+ if (hasResponseLanguageCoverage(content, refRoot)) return false;
435
+ return !inheritsParentCoverage(workflowsDir, relative, refRoot);
436
+ });
437
+ }
438
+
439
+ function main(workflowsDir = WORKFLOWS_DIR, io = console, refRoot = REFERENCE_ROOT) {
440
+ // The catalog is discovered, not declared, so an unreadable or empty
441
+ // directory yields an empty violation list — indistinguishable from full
442
+ // coverage if the only success condition is `violations.length === 0`. Both
443
+ // discovery failures below are therefore lint failures in their own right:
444
+ // a stripped install tree, a `__dirname`-relative path typo, or an
445
+ // unfollowed symlink must not be able to report OK while coverage silently
446
+ // regresses to the pre-#2529 state.
447
+ let files;
448
+ try {
449
+ files = findMarkdownFilesRecursive(workflowsDir);
450
+ } catch (error) {
451
+ if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') throw error;
452
+ io.error(
453
+ `lint-response-language-coverage: cannot read the workflow directory ${workflowsDir} (${error.code}).\n` +
454
+ `Coverage cannot be established, so this is a failure and not a pass (#2529).`,
455
+ );
456
+ return 1;
457
+ }
458
+
459
+ if (files.length === 0) {
460
+ io.error(
461
+ `lint-response-language-coverage: no workflow files found under ${workflowsDir}.\n` +
462
+ `A run that inspected zero workflows cannot establish coverage (#2529).`,
463
+ );
464
+ return 1;
465
+ }
466
+
467
+ // The shared references are checked ONCE, before the per-file pass. 43
468
+ // workflows hold no directive of their own, so a reference that has gone
469
+ // missing or been reworded back to the pre-#2529 sentence is a single
470
+ // systemic failure, not 43 identical ones — and reporting it as 43 would bury
471
+ // the cause under its symptoms.
472
+ const brokenReferences = findBrokenDirectiveReferences(files, refRoot);
473
+ if (brokenReferences.length > 0) {
474
+ io.error(
475
+ 'lint-response-language-coverage: ' + brokenReferences.length +
476
+ ' shared directive reference(s) no longer carry an actionable directive (#2529, REQ-LANG-04).\n' +
477
+ 'Workflows that @-import a reference take ALL of their coverage from it, so a missing,\n' +
478
+ 'unreadable, or reworded reference silently uncovers every one of them:\n\n' +
479
+ brokenReferences.map((ref) => ' - ' + ref).join('\n') + '\n\n' +
480
+ 'The file must itself name the narration class on the same line as response_language\n' +
481
+ '— the same rule an inline directive must satisfy.',
482
+ );
483
+ return 1;
484
+ }
485
+
486
+ const violations = findViolations(workflowsDir, refRoot);
487
+
488
+ if (violations.length > 0) {
489
+ io.error(
490
+ `lint-response-language-coverage: ${violations.length} workflow(s) have no response-language coverage (#2529).\n` +
491
+ `Each workflow must either @-reference a recognized response-language directive\n` +
492
+ `or carry its own inline \`response_language\` directive (unless its parent dispatches it).\n` +
493
+ `An inline directive names the narration class with the word "narration" or the phrase\n` +
494
+ `"between tool calls" (REQ-LANG-04); enumerating status updates, progress notes or\n` +
495
+ `findings without naming the class does not satisfy it:\n\n` +
496
+ violations.map((file) => ` - ${path.relative(workflowsDir, file).replaceAll(path.sep, '/')}`).join('\n'),
497
+ );
498
+ return 1;
499
+ }
500
+
501
+ io.log(`lint-response-language-coverage: OK (${files.length} workflows covered)`);
502
+ return 0;
503
+ }
504
+
505
+ if (require.main === module) process.exitCode = main();
506
+
507
+ module.exports = {
508
+ EXACT_INLINE_DIRECTIVE_WORKFLOWS,
509
+ INLINE_RESPONSE_LANGUAGE_DIRECTIVE,
510
+ NARRATION_CLASS_RE,
511
+ WORKFLOW_EXTENSIONS,
512
+ WORKFLOWS_DIR,
513
+ REFERENCE_ROOT,
514
+ carriesInlineDirective,
515
+ findBrokenDirectiveReferences,
516
+ findMarkdownFilesRecursive,
517
+ findViolations,
518
+ hasResponseLanguageCoverage,
519
+ importedDirectiveReference,
520
+ importsDirectiveReference,
521
+ inheritsParentCoverage,
522
+ namesFragmentAsEntryPoint,
523
+ main,
524
+ };