@opengsd/gsd-core 1.12.0 → 1.14.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 (455) 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-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -0,0 +1,530 @@
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|detail>/<name>.md`, the
26
+ * shape the #1671 fragment epic extracts — `detail/` is the fourth such
27
+ * subdirectory kind, added by the #4403/ADR-4139 spine+detail split) additionally
28
+ * passes when its parent workflow
29
+ * names that exact fragment path and is itself covered — see
30
+ * `inheritsParentCoverage`, which proves the inheritance per file instead of
31
+ * granting it to a directory.
32
+ *
33
+ * Exit 0 only when workflows were actually found AND every one of them is
34
+ * covered; exit 1 with a per-file listing if not. "No violations" alone is not
35
+ * a pass: a run that inspected nothing has established nothing.
36
+ */
37
+
38
+ 'use strict';
39
+
40
+ const fs = require('fs');
41
+ const path = require('path');
42
+
43
+ const ROOT = path.join(__dirname, '..');
44
+ const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
45
+ // A reference resolves as a sibling of the workflow catalog, so a fixture tree
46
+ // and the real one resolve by one rule.
47
+ const REFERENCE_ROOT = path.join(ROOT, 'gsd-core');
48
+ const DIRECTIVE_REFS = [
49
+ 'references/response-language-directive.md',
50
+ 'references/execute-phase-response-language.md',
51
+ ];
52
+ // Names the narration class explicitly for the same reason the inline directives
53
+ // in the workflow bodies do (see NARRATION_CLASS_RE): "user-facing prose" alone
54
+ // reads, in practice, as the question/answer surface, which is the half of the
55
+ // output #2529 was never about.
56
+ const INLINE_RESPONSE_LANGUAGE_DIRECTIVE =
57
+ 'Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.';
58
+ // THE RULE THAT DECIDES THIS SET (#2529, restated in review round 29).
59
+ // A lazy-loaded mode/step/template carries its own directive only when it cannot
60
+ // PROVE inheritance -- no parent dispatches it from a read/execute context, or the
61
+ // parent is itself uncovered. Where inheritance is proven, the parent's directive is
62
+ // already in the loaded context by the time the fragment is read, so a second copy
63
+ // buys no coverage and adds a sentence that can drift. Pinning the exact wording is
64
+ // what makes the first case safe: these files cannot take the eager @-reference (an
65
+ // @-line inside a later Read is inert), so the sentence lives inline, and a partial
66
+ // typo or rewording would otherwise split the contract silently.
67
+ // enforces the rule in both directions:
68
+ // no member of this set may be one that would inherit anyway.
69
+ const EXACT_INLINE_DIRECTIVE_WORKFLOWS = new Set([
70
+ 'discuss-phase/modes/advisor.md',
71
+ 'discuss-phase/modes/all.md',
72
+ 'discuss-phase/modes/analyze.md',
73
+ 'discuss-phase/modes/auto.md',
74
+ 'discuss-phase/modes/batch.md',
75
+ 'discuss-phase/modes/chain.md',
76
+ 'discuss-phase/modes/default.md',
77
+ 'discuss-phase/modes/power.md',
78
+ 'discuss-phase/modes/text.md',
79
+ 'discuss-phase/templates/context.md',
80
+ 'discuss-phase/templates/discussion-log.md',
81
+ 'execute-phase/steps/codebase-drift-gate.md',
82
+ 'execute-phase/steps/regression-gate-run.md',
83
+ 'execute-phase/steps/worktree-recovery-policy.md',
84
+ 'help/modes/brief.md',
85
+ 'help/modes/default.md',
86
+ 'help/modes/full.md',
87
+ 'help/modes/topic.md',
88
+ 'plan-phase/steps/prd-express-path.md',
89
+ 'quick-batch/steps/plan-checker-loop.md',
90
+ 'settings-advanced.md',
91
+ ]);
92
+ // This lint once carried a third coverage form, for `verify-phase.md`: a
93
+ // workflow file nothing entered directly, covered instead by the directive
94
+ // execute-phase.md injects into its `gsd-verifier` dispatch prompt. `next`
95
+ // deleted that workflow in #3421 (an orphan that shipped ~40 KB to every runtime
96
+ // and was never loaded) and migrated its live gates into the verifier, so the
97
+ // form has no subject left and is gone with it.
98
+ //
99
+ // The dispatch contract itself is NOT gone — the verifier subagent still runs and
100
+ // still emits user-facing prose, so `references/execute-phase-response-language.md`
101
+ // still tells the orchestrator to carry the directive into that prompt. It is
102
+ // simply no longer a statement about any file in this catalog, which is all this
103
+ // lint reads. `tests/response-language-coverage.test.cjs` pins both ends of it
104
+ // (the reference's directive text and the `Create VERIFICATION.md.` anchor it
105
+ // positions against in execute-phase.md) directly against the real tree.
106
+ // Fragment directories produced by the workflow-fragment epic (#1671). A file
107
+ // under one of these is a SECTION of its parent workflow, never an entry point:
108
+ // it is reached by a `read and execute gsd-core/workflows/<path>` stub, which
109
+ // fires with the parent — and therefore the parent's response-language
110
+ // directive — already loaded.
111
+ //
112
+ // The stub is USUALLY in the top-level parent, which is the only case
113
+ // `inheritsParentCoverage` can prove, and it deliberately proves no more. Four
114
+ // fragments arrive by a different route today, and none of them relies on this
115
+ // function: `execute-phase/steps/regression-gate-run.md`,
116
+ // `plan-phase/steps/prd-express-path.md` and
117
+ // `quick-batch/steps/plan-checker-loop.md` are dispatched by a SIBLING fragment
118
+ // (`regression-gate.md`, `prd-express-gate.md`, `planner-wave.md`), and
119
+ // `help/modes/topic.md` is routed from a table in `help.md` that names the path
120
+ // without a dispatch verb at all. All four carry the pinned inline directive and
121
+ // are listed in `EXACT_INLINE_DIRECTIVE_WORKFLOWS`, so `findViolations` settles
122
+ // them before inheritance is ever consulted.
123
+ //
124
+ // Left as one hop on purpose. Walking the sibling chain would let a fragment
125
+ // inherit through a file this lint has not proven reachable, trading a loud
126
+ // failure for a quiet assumption; as it stands, extracting a fragment-of-a-
127
+ // fragment without pinning it turns the lint RED, which is the correct answer
128
+ // and names the file to fix.
129
+ // `detail` (ADR-4139 §6 Decision 6 / #4403) is the fourth fragment-directory kind,
130
+ // alongside modes/steps/templates from #1671: a spine's `detail/<part>.md` is
131
+ // reached the same way -- a `read and execute` stub in the top-level parent -- so
132
+ // it inherits coverage through the exact same mechanism, not a parallel one.
133
+ const FRAGMENT_DIRS = new Set(['modes', 'steps', 'templates', 'detail']);
134
+ const DIRECTIVE_ACTION_RE = /\b(?:apply|present|render|respond|translate|use|write|must|should)\b/i;
135
+ const USER_OUTPUT_RE = /\b(?:explanations?|language|narration|outputs?|prompts?|prose|questions?|templates?|user-facing)\b/i;
136
+ // The defect #2529 reports is NARRATION, not the question/answer surface: a
137
+ // directive worded as "all user-facing questions, prompts, and explanations" is
138
+ // read as covering what the user is asked and told at a turn boundary, and the
139
+ // running commentary the model emits between tool calls stays in English beside
140
+ // it. A line that names only that surface therefore certifies the exact gap the
141
+ // issue exists to close, so coverage additionally requires a narration-class
142
+ // token — narration, status, progress, findings, or output "between tool calls".
143
+ // Round 21 tightening: the accepted forms are the two that NAME the class — the
144
+ // word "narration", or output described as running "between tool calls". The
145
+ // earlier list also accepted a bare "status", "progress" or "findings", which
146
+ // are words that merely APPEAR in the canonical phrasing: a line reading "Use
147
+ // response_language for all user-facing output; report status." passed while
148
+ // naming nothing about inter-tool narration, i.e. weaker than REQ-LANG-04
149
+ // requires. Verified before tightening: all 79 catalog files that passed under
150
+ // the old list still pass under this one, so no workflow needed rewording — the
151
+ // dropped tokens were reachable only by directives nobody ships.
152
+ // Still deliberately not a phrase match: pinning one sentence would push authors
153
+ // toward copying wording instead of stating the rule (`tests/response-language-coverage.test.cjs`
154
+ // pins the old weak wording as a FAILING case so this cannot silently loosen).
155
+ const NARRATION_CLASS_RE = /\bnarration\b|\bbetween tool calls\b/i;
156
+ // The catalog's extensions, matched case-insensitively and by whole extension.
157
+ // `endsWith('.md')` failed in the one direction this lint cannot afford: it is
158
+ // silent UNDER-enforcement, the same vacuous pass `main()` refuses when
159
+ // discovery returns nothing. `SETTINGS.MD` is the same file to Windows and
160
+ // macOS and a different one to Linux, so a case-sensitive test exempts it on
161
+ // the only platform whose verdict gates the merge — a workflow certified by
162
+ // having gone unseen.
163
+ // `.mdx` stays out deliberately. Admitting an extension states what a workflow
164
+ // IS, and that claim has a second half: `inheritsParentCoverage` resolves a
165
+ // fragment's parent as `<workflow>.md`. If the repo ever emits an `.mdx`
166
+ // workflow, the entry belongs here next to the parent resolution it must move
167
+ // with, not ahead of it.
168
+ const WORKFLOW_EXTENSIONS = new Set(['.md']);
169
+
170
+ function isWorkflowFile(name) {
171
+ return WORKFLOW_EXTENSIONS.has(path.extname(name).toLowerCase());
172
+ }
173
+
174
+ /**
175
+ * Walk the catalog, FOLLOWING symlinked subtrees.
176
+ *
177
+ * `Dirent` uses lstat semantics, so a symlinked directory answers false to both
178
+ * `isDirectory()` and `isFile()`. Branching on those alone skipped a symlinked
179
+ * subtree in silence while `files.length > 0` kept the run green — the exact
180
+ * "reported OK while coverage regressed" outcome `main()` claims to make
181
+ * impossible. Each symlink is resolved with `statSync` and followed.
182
+ *
183
+ * Cycles are bounded by the `seen` set of resolved real paths: a link pointing
184
+ * at an ancestor is entered once and not re-entered. A broken link resolves to
185
+ * nothing and is skipped — it contributes no file to inspect, and no coverage
186
+ * claim rides on it.
187
+ */
188
+ function findMarkdownFilesRecursive(dir, seen = new Set()) {
189
+ const files = [];
190
+ let real;
191
+ try { real = fs.realpathSync(dir); } catch { real = dir; }
192
+ if (seen.has(real)) return files;
193
+ seen.add(real);
194
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
195
+ const full = path.join(dir, entry.name);
196
+ let isDirectory = entry.isDirectory();
197
+ let isFile = entry.isFile();
198
+ if (entry.isSymbolicLink()) {
199
+ let stat;
200
+ try { stat = fs.statSync(full); } catch { continue; }
201
+ isDirectory = stat.isDirectory();
202
+ isFile = stat.isFile();
203
+ }
204
+ if (isDirectory) files.push(...findMarkdownFilesRecursive(full, seen));
205
+ else if (isFile && isWorkflowFile(entry.name)) files.push(full);
206
+ }
207
+ return files.sort();
208
+ }
209
+
210
+ /**
211
+ * Does the file LOAD a shared directive, rather than merely name one?
212
+ *
213
+ * The distinction is the same one `namesFragmentAsEntryPoint` draws a level
214
+ * down, and it matters for the same reason: only a real `@`-import puts the
215
+ * directive in context. A changelog line ("#2529: added the shared
216
+ * response-language-directive.md reference"), a deprecation note, or any prose
217
+ * naming the path proves nothing about what loads — but a bare substring test
218
+ * cannot tell those apart, so such a workflow would be certified covered while
219
+ * shipping the very defect #2529 exists to close.
220
+ *
221
+ * The catalog emits the import exactly one way: an `@`-path occupying the whole
222
+ * line — line 1 for the 43 top-level workflows, line 96 for `execute-phase.md`.
223
+ * Both `@~/` and `@$HOME/` spellings are accepted because the repo writes both
224
+ * (see `scripts/strip-prose-atrefs.cjs`).
225
+ *
226
+ * LIMITATION, stated rather than papered over: like the fragment check, this
227
+ * reads Markdown as text. An import inside a fenced block quoted as an example
228
+ * still counts, and one whose line carries a trailing `<!-- ... -->` no longer
229
+ * does. Neither shape occurs in the catalog today, and both would be visible in
230
+ * review as a quoted or edited import.
231
+ */
232
+ const DIRECTIVE_IMPORT_RES = DIRECTIVE_REFS.map(
233
+ (ref) => new RegExp(`^@(?:~|\\$HOME)/\\S*${ref.replaceAll('.', '\\.')}$`),
234
+ );
235
+
236
+ function importsDirectiveReference(content) {
237
+ return importedDirectiveReference(content) !== null;
238
+ }
239
+
240
+ /** Which known reference does this file import, if any? */
241
+ function importedDirectiveReference(content) {
242
+ const lines = content.split(/\r?\n/).map((line) => line.trim());
243
+ for (const [index, ref] of DIRECTIVE_REFS.entries()) {
244
+ if (lines.some((line) => DIRECTIVE_IMPORT_RES[index].test(line))) return ref;
245
+ }
246
+ return null;
247
+ }
248
+
249
+ /**
250
+ * The four-predicate test for an actionable directive on a single line.
251
+ *
252
+ * LIMITATION (text, not AST): the four hits are independent, so the predicate
253
+ * reads vocabulary and not polarity. A line that negates the instruction —
254
+ * "Do not translate narration; apply response_language to code only." — satisfies
255
+ * all four and reads as coverage. Contrived rather than plausible: the wording is
256
+ * pinned byte-for-byte in the files that cannot take the reference, and the shared
257
+ * reference is validated against this same predicate, so a negated line would have
258
+ * to be authored deliberately in one of the 44 files that carry their own sentence.
259
+ * Named here because a predicate that cannot see "not" should say so.
260
+ */
261
+ function carriesInlineDirective(content) {
262
+ return content.split(/\r?\n/).some((line) =>
263
+ /\bresponse_language\b/i.test(line) &&
264
+ DIRECTIVE_ACTION_RE.test(line) &&
265
+ USER_OUTPUT_RE.test(line) &&
266
+ NARRATION_CLASS_RE.test(line)
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Does the SHARED FILE a workflow imports itself carry an actionable directive?
272
+ *
273
+ * Checking only that the `@`-import LINE exists is a hole the size of the
274
+ * bucket: 43 workflows take their entire coverage from one file, and nothing in
275
+ * the repo pinned that file's text. Rewriting it back to the pre-#2529 wording —
276
+ * the sentence this PR's own round-10 finding calls the DEFECT — left the lint
277
+ * green and every workflow "covered". That is the same "the gate certifies the
278
+ * defect" failure the round-10 fix closed, one level up.
279
+ *
280
+ * So an import is coverage only when the referenced file passes the very
281
+ * predicate an inline directive must pass. A missing, unreadable, or weakened
282
+ * reference covers nobody, and `main()` reports it as one systemic failure
283
+ * rather than as 43 identical per-file violations.
284
+ */
285
+ // Keyed by identity AND version, not by path alone. A path-only key is correct
286
+ // for a one-shot CLI and for the tests, where every fixture gets a fresh mkdtemp
287
+ // root — but it makes the verdict for a path permanent within the process, so a
288
+ // caller that rewrites a reference and re-asks gets the stale answer. Stamping
289
+ // size and mtime into the key costs one stat and removes the class rather than
290
+ // documenting it.
291
+ const referenceDirectiveCache = new Map();
292
+ function referenceCarriesDirective(ref, refRoot) {
293
+ const file = path.join(refRoot, ref);
294
+ let stamp = 'missing';
295
+ try {
296
+ const stat = fs.statSync(file);
297
+ stamp = `${stat.size}:${stat.mtimeMs}`;
298
+ } catch { /* keep the missing stamp; the read below decides the verdict */ }
299
+ const key = [refRoot, ref, stamp].join('\u0000');
300
+ if (!referenceDirectiveCache.has(key)) {
301
+ let ok = false;
302
+ try { ok = carriesInlineDirective(fs.readFileSync(file, 'utf8')); }
303
+ catch { ok = false; }
304
+ referenceDirectiveCache.set(key, ok);
305
+ }
306
+ return referenceDirectiveCache.get(key);
307
+ }
308
+
309
+ /**
310
+ * Of the references these files actually import, which are missing, unreadable,
311
+ * or no longer actionable?
312
+ *
313
+ * Scoped to imported references on purpose: a reference nobody imports cannot
314
+ * uncover anybody, so failing on it would red a run over a file it never
315
+ * consulted. Every reference that IS imported is load-bearing for every workflow
316
+ * that imports it.
317
+ */
318
+ function findBrokenDirectiveReferences(files, refRoot = REFERENCE_ROOT) {
319
+ const imported = new Set();
320
+ for (const file of files) {
321
+ let content;
322
+ try { content = fs.readFileSync(file, 'utf8'); } catch { continue; }
323
+ const ref = importedDirectiveReference(content);
324
+ if (ref !== null) imported.add(ref);
325
+ }
326
+ return [...imported].filter((ref) => !referenceCarriesDirective(ref, refRoot)).sort();
327
+ }
328
+
329
+ function hasResponseLanguageCoverage(content, refRoot = REFERENCE_ROOT) {
330
+ const ref = importedDirectiveReference(content);
331
+ if (ref !== null) return referenceCarriesDirective(ref, refRoot);
332
+
333
+ return carriesInlineDirective(content);
334
+ }
335
+
336
+ /**
337
+ * How far a read/execute verb may sit from the fragment path it governs on the
338
+ * same line. Sized from the widest shape the catalog actually emits —
339
+ * ``read and execute `gsd-core/workflows/...` `` (14 characters between verb and
340
+ * path) — with room for a variant, and deliberately far short of a sentence, so
341
+ * a verb belonging to a different clause cannot reach across and vouch for a
342
+ * path it never dispatches.
343
+ */
344
+ const ENTRY_POINT_VERB_WINDOW = 40;
345
+ const ENTRY_POINT_VERB_RE = new RegExp(`\\b(?:read|execute|run)\\b.{0,${ENTRY_POINT_VERB_WINDOW}}$`, 'i');
346
+
347
+ /**
348
+ * Does the parent name this fragment as a live ENTRY POINT, rather than merely
349
+ * mentioning its path?
350
+ *
351
+ * The distinction is the whole basis of inherited coverage: the inheritance is
352
+ * sound only because the fragment cannot be reached except through the parent's
353
+ * dispatch stub, which fires with the parent's directive already loaded. A path
354
+ * that appears in a changelog line, a deprecation note, or a comment proves
355
+ * nothing about how the fragment is reached — but a bare substring test cannot
356
+ * tell those apart, so before this check any mention at all granted coverage.
357
+ *
358
+ * The catalog emits exactly one shape, in five spellings: ``read and execute
359
+ * `<path>` ``, ``Read+execute `<path>` ``, ``Read `<path>` if <condition>``,
360
+ * ``run `<path>` to <effect>`` (the spelling #1689's per-plan executor routing
361
+ * introduced), and the same stub written with the path RELATIVE to the catalog
362
+ * rather than rooted at `gsd-core/workflows/` (#3552's `branching_strategy:
363
+ * none` arm). A read/execute/run verb within `ENTRY_POINT_VERB_WINDOW`
364
+ * characters ahead of the path on the SAME LINE covers all five.
365
+ *
366
+ * LIMITATION, stated rather than papered over: this reads Markdown as text, not
367
+ * as a parsed document. A dispatch stub that a future edit comments out with
368
+ * `<!-- ... -->`, or moves inside a fenced block quoted as an example, still
369
+ * matches — the verb and the path are both still there. Distinguishing those
370
+ * needs a Markdown parser, and the cheap approximations (tracking fence state,
371
+ * skipping `<!--` lines) buy little: neither shape occurs in the catalog today,
372
+ * and both would be visible in review as a deleted dispatch. What this DOES
373
+ * close is the common, invisible case — a path mentioned in prose that never
374
+ * dispatches anything. `tests/response-language-coverage.test.cjs` pins both the
375
+ * accepted spellings and the rejected mention forms.
376
+ *
377
+ * SECOND LIMITATION, same class: a dispatch line may sit inside a
378
+ * `<!-- gsd:section id="X" when="…" -->` guard, and nothing here checks that the
379
+ * guard is satisfiable or that its `id` still matches the fragment it gates.
380
+ * Every such pairing lines up today. If one drifts, the fragment becomes
381
+ * unreachable while this function still calls it dispatched — an orphan the lint
382
+ * reports as covered. That is not the defect #2529 is about: a dispatch that
383
+ * never fires renders nothing, so no English prose reaches the user through it.
384
+ * The gap is a soundness one, worth naming rather than implying.
385
+ */
386
+ function namesFragmentAsEntryPoint(parent, relative) {
387
+ // Two spellings of the same dispatch: catalog-rooted and catalog-RELATIVE.
388
+ // #3552's `"none"` arm introduced the second one — `Read and execute
389
+ // `execute-phase/steps/protected-branch.md`` — and a rooted-only needle read
390
+ // that live dispatch as no dispatch at all.
391
+ const needles = [`gsd-core/workflows/${relative}`, relative];
392
+ return parent.split(/\r?\n/).some((line) => needles.some((needle) => {
393
+ const at = line.indexOf(needle);
394
+ // A path character immediately before the match means this is the TAIL of
395
+ // some longer path, not the fragment itself: `vendor/<relative>` must not
396
+ // grant `<relative>` coverage. The rooted needle carries its own prefix and
397
+ // is matched on its own, so nothing legitimate is lost here.
398
+ if (at === -1 || (at > 0 && /[\w/.-]/.test(line.charAt(at - 1)))) return false;
399
+ return ENTRY_POINT_VERB_RE.test(line.slice(0, at));
400
+ }));
401
+ }
402
+
403
+ /**
404
+ * A fragment inherits its parent workflow's coverage, but only when the
405
+ * inheritance is PROVEN per file rather than assumed from the directory:
406
+ * 1. the path is `<workflow>/<fragment-dir>/<name>.md`,
407
+ * 2. `<workflow>.md` exists and dispatches this exact fragment path from a
408
+ * read/execute context — i.e. the parent really is the way in, not just a
409
+ * file that happens to spell the name (see `namesFragmentAsEntryPoint`), and
410
+ * 3. the parent is itself covered.
411
+ * A fragment nobody dispatches, or one hanging off an uncovered parent, stays a
412
+ * violation. Without this the lint reds on every new fragment the #1671 epic
413
+ * extracts, even though the extraction moved prose that was already covered.
414
+ */
415
+ function inheritsParentCoverage(workflowsDir, relative, refRoot = REFERENCE_ROOT) {
416
+ const segments = relative.split('/');
417
+ if (segments.length !== 3 || !FRAGMENT_DIRS.has(segments[1])) return false;
418
+ const parentPath = path.join(workflowsDir, `${segments[0]}.md`);
419
+ if (!fs.existsSync(parentPath)) return false;
420
+ const parent = fs.readFileSync(parentPath, 'utf8');
421
+ if (!namesFragmentAsEntryPoint(parent, relative)) return false;
422
+ return hasResponseLanguageCoverage(parent, refRoot);
423
+ }
424
+
425
+ function findViolations(workflowsDir, refRoot = REFERENCE_ROOT) {
426
+ return findMarkdownFilesRecursive(workflowsDir).filter((file) => {
427
+ const relative = path.relative(workflowsDir, file).replaceAll(path.sep, '/');
428
+ const content = fs.readFileSync(file, 'utf8');
429
+ if (EXACT_INLINE_DIRECTIVE_WORKFLOWS.has(relative)) {
430
+ if (content.split(/\r?\n/).includes(INLINE_RESPONSE_LANGUAGE_DIRECTIVE)) return false;
431
+ // The pin exists because these files cannot take the eager @-reference, not
432
+ // because the reference is worse. If one ever CAN take it -- a fragment that
433
+ // becomes eagerly loaded -- that conversion is strictly better than the pin and
434
+ // must not read as a violation. Only the reference form is admitted here: its
435
+ // wording is validated in turn by findBrokenDirectiveReferences, so the contract
436
+ // survives the swap. An arbitrary reworded inline line stays a violation, which
437
+ // is the whole point of pinning.
438
+ return !importsDirectiveReference(content) || !hasResponseLanguageCoverage(content, refRoot);
439
+ }
440
+ if (hasResponseLanguageCoverage(content, refRoot)) return false;
441
+ return !inheritsParentCoverage(workflowsDir, relative, refRoot);
442
+ });
443
+ }
444
+
445
+ function main(workflowsDir = WORKFLOWS_DIR, io = console, refRoot = REFERENCE_ROOT) {
446
+ // The catalog is discovered, not declared, so an unreadable or empty
447
+ // directory yields an empty violation list — indistinguishable from full
448
+ // coverage if the only success condition is `violations.length === 0`. Both
449
+ // discovery failures below are therefore lint failures in their own right:
450
+ // a stripped install tree, a `__dirname`-relative path typo, or an
451
+ // unfollowed symlink must not be able to report OK while coverage silently
452
+ // regresses to the pre-#2529 state.
453
+ let files;
454
+ try {
455
+ files = findMarkdownFilesRecursive(workflowsDir);
456
+ } catch (error) {
457
+ if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') throw error;
458
+ io.error(
459
+ `lint-response-language-coverage: cannot read the workflow directory ${workflowsDir} (${error.code}).\n` +
460
+ `Coverage cannot be established, so this is a failure and not a pass (#2529).`,
461
+ );
462
+ return 1;
463
+ }
464
+
465
+ if (files.length === 0) {
466
+ io.error(
467
+ `lint-response-language-coverage: no workflow files found under ${workflowsDir}.\n` +
468
+ `A run that inspected zero workflows cannot establish coverage (#2529).`,
469
+ );
470
+ return 1;
471
+ }
472
+
473
+ // The shared references are checked ONCE, before the per-file pass. 43
474
+ // workflows hold no directive of their own, so a reference that has gone
475
+ // missing or been reworded back to the pre-#2529 sentence is a single
476
+ // systemic failure, not 43 identical ones — and reporting it as 43 would bury
477
+ // the cause under its symptoms.
478
+ const brokenReferences = findBrokenDirectiveReferences(files, refRoot);
479
+ if (brokenReferences.length > 0) {
480
+ io.error(
481
+ 'lint-response-language-coverage: ' + brokenReferences.length +
482
+ ' shared directive reference(s) no longer carry an actionable directive (#2529, REQ-LANG-04).\n' +
483
+ 'Workflows that @-import a reference take ALL of their coverage from it, so a missing,\n' +
484
+ 'unreadable, or reworded reference silently uncovers every one of them:\n\n' +
485
+ brokenReferences.map((ref) => ' - ' + ref).join('\n') + '\n\n' +
486
+ 'The file must itself name the narration class on the same line as response_language\n' +
487
+ '— the same rule an inline directive must satisfy.',
488
+ );
489
+ return 1;
490
+ }
491
+
492
+ const violations = findViolations(workflowsDir, refRoot);
493
+
494
+ if (violations.length > 0) {
495
+ io.error(
496
+ `lint-response-language-coverage: ${violations.length} workflow(s) have no response-language coverage (#2529).\n` +
497
+ `Each workflow must either @-reference a recognized response-language directive\n` +
498
+ `or carry its own inline \`response_language\` directive (unless its parent dispatches it).\n` +
499
+ `An inline directive names the narration class with the word "narration" or the phrase\n` +
500
+ `"between tool calls" (REQ-LANG-04); enumerating status updates, progress notes or\n` +
501
+ `findings without naming the class does not satisfy it:\n\n` +
502
+ violations.map((file) => ` - ${path.relative(workflowsDir, file).replaceAll(path.sep, '/')}`).join('\n'),
503
+ );
504
+ return 1;
505
+ }
506
+
507
+ io.log(`lint-response-language-coverage: OK (${files.length} workflows covered)`);
508
+ return 0;
509
+ }
510
+
511
+ if (require.main === module) process.exitCode = main();
512
+
513
+ module.exports = {
514
+ EXACT_INLINE_DIRECTIVE_WORKFLOWS,
515
+ INLINE_RESPONSE_LANGUAGE_DIRECTIVE,
516
+ NARRATION_CLASS_RE,
517
+ WORKFLOW_EXTENSIONS,
518
+ WORKFLOWS_DIR,
519
+ REFERENCE_ROOT,
520
+ carriesInlineDirective,
521
+ findBrokenDirectiveReferences,
522
+ findMarkdownFilesRecursive,
523
+ findViolations,
524
+ hasResponseLanguageCoverage,
525
+ importedDirectiveReference,
526
+ importsDirectiveReference,
527
+ inheritsParentCoverage,
528
+ namesFragmentAsEntryPoint,
529
+ main,
530
+ };
@@ -114,7 +114,7 @@ function toPosix(p) {
114
114
  function isGeneratedOutput(relPath) {
115
115
  const posixRel = toPosix(relPath);
116
116
  return GENERATED_OUTPUT_PREFIXES.some(
117
- (prefix) => posixRel === prefix || posixRel.startsWith(`${prefix}/`)
117
+ (prefix) => posixRel === prefix || posixRel.startsWith(`${prefix}/`) // allow-handrolled-containment: scan-exclusion membership test against a fixed generated-output prefix list, not a filesystem root-confinement gate
118
118
  );
119
119
  }
120
120
 
@@ -81,6 +81,7 @@
81
81
  "phase-dependency-levels.test.cjs",
82
82
  "phase-resolution-parity.test.cjs",
83
83
  "phase-structure.test.cjs",
84
+ "phase-tdd-applicable.test.cjs",
84
85
  "phase.test.cjs"
85
86
  ],
86
87
  "issue": "3186"
@@ -122,6 +123,7 @@
122
123
  "state-prune.test.cjs",
123
124
  "state-rebuild-cli.test.cjs",
124
125
  "state-rebuild.test.cjs",
126
+ "state-todos-render.test.cjs",
125
127
  "state-write-path-drift-guard.test.cjs",
126
128
  "state.test.cjs"
127
129
  ],
@@ -260,9 +262,10 @@
260
262
  "audit-milestone-filename-guard.test.cjs",
261
263
  "audit-open-remainder-count.test.cjs",
262
264
  "audit-uat-acknowledged.test.cjs",
265
+ "audit-uat-summary-segmentation.test.cjs",
263
266
  "audit-workstream-layouts.test.cjs"
264
267
  ],
265
- "issue": "#3804/#3805 \u2014 audit-layout and acknowledged-marker suites drive the real audit-uat CLI against on-disk fixtures | #3817 \u2014 regression suite proving countReal counts the truncation remainder marker instead of treating it as one phantom item"
268
+ "issue": "#3804/#3805 \u2014 audit-layout and acknowledged-marker suites drive the real audit-uat CLI against on-disk fixtures | #3817 \u2014 regression suite proving countReal counts the truncation remainder marker instead of treating it as one phantom item | #3783 \u2014 dedicated suite for the summary.current_milestone/archived segmentation, matching this module's one-file-per-feature-slice precedent (e.g. audit-uat-acknowledged.test.cjs) rather than growing the unrelated 6900+ line tests/uat.test.cjs (a different module, keyed off uat.cjs)"
266
269
  },
267
270
  "shell-command-projection": {
268
271
  "files": [