@opengsd/gsd-core 1.11.0 → 1.12.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 (395) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +1 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-dom-verifier.md +169 -0
  7. package/agents/gsd-eval-auditor.md +1 -1
  8. package/agents/gsd-executor.md +17 -9
  9. package/agents/gsd-framework-selector.md +1 -3
  10. package/agents/gsd-intel-updater.md +1 -1
  11. package/agents/gsd-mempalace-curator.md +0 -1
  12. package/agents/gsd-pattern-mapper.md +11 -0
  13. package/agents/gsd-phase-researcher.md +3 -1
  14. package/agents/gsd-plan-checker.md +15 -55
  15. package/agents/gsd-planner.md +6 -4
  16. package/agents/gsd-project-researcher.md +1 -1
  17. package/agents/gsd-research-synthesizer.md +2 -2
  18. package/agents/gsd-roadmapper.md +15 -11
  19. package/agents/gsd-ui-checker.md +63 -4
  20. package/agents/gsd-ui-researcher.md +41 -3
  21. package/agents/gsd-verifier.md +1 -1
  22. package/bin/install.js +609 -134
  23. package/commands/gsd/discuss-phase.md +1 -1
  24. package/commands/gsd/import.md +1 -1
  25. package/commands/gsd/quick.md +8 -4
  26. package/gsd-core/bin/gsd-tools.cjs +567 -51
  27. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  28. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  29. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  30. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  31. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  32. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  33. package/gsd-core/bin/lib/audit.cjs +163 -41
  34. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  35. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  36. package/gsd-core/bin/lib/capability-registry.cjs +336 -95
  37. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  38. package/gsd-core/bin/lib/capability-validator.cjs +205 -18
  39. package/gsd-core/bin/lib/check-command-router.cjs +145 -5
  40. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  41. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  42. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  43. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  44. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  45. package/gsd-core/bin/lib/commands.cjs +543 -44
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  47. package/gsd-core/bin/lib/config-loader.cjs +118 -29
  48. package/gsd-core/bin/lib/config.cjs +92 -2
  49. package/gsd-core/bin/lib/configuration.cjs +129 -37
  50. package/gsd-core/bin/lib/core-utils.cjs +84 -7
  51. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  52. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  53. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  54. package/gsd-core/bin/lib/frontmatter.cjs +840 -305
  55. package/gsd-core/bin/lib/gap-checker.cjs +27 -3
  56. package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
  57. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  58. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
  59. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  60. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  61. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  62. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  63. package/gsd-core/bin/lib/init.cjs +120 -41
  64. package/gsd-core/bin/lib/install-engine.cjs +68 -3
  65. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
  66. package/gsd-core/bin/lib/install-profiles.cjs +78 -4
  67. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  68. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  69. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  70. package/gsd-core/bin/lib/intel.cjs +101 -26
  71. package/gsd-core/bin/lib/io.cjs +160 -15
  72. package/gsd-core/bin/lib/learnings.cjs +85 -14
  73. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  74. package/gsd-core/bin/lib/markdown-table.cjs +52 -4
  75. package/gsd-core/bin/lib/milestone.cjs +90 -5
  76. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  77. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  78. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  79. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  80. package/gsd-core/bin/lib/phase-id.cjs +70 -4
  81. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  82. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  83. package/gsd-core/bin/lib/phase.cjs +405 -84
  84. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  85. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  86. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  87. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  88. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
  89. package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
  90. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  91. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  92. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  93. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  94. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  95. package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
  96. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  97. package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
  98. package/gsd-core/bin/lib/roadmap.cjs +74 -19
  99. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
  100. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
  101. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
  102. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  103. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  104. package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
  105. package/gsd-core/bin/lib/smart-entry.cjs +12 -22
  106. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  107. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  108. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  109. package/gsd-core/bin/lib/state-document.cjs +186 -0
  110. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  111. package/gsd-core/bin/lib/state-transition.cjs +517 -101
  112. package/gsd-core/bin/lib/state.cjs +946 -163
  113. package/gsd-core/bin/lib/surface.cjs +10 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  115. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  116. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  117. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  118. package/gsd-core/bin/lib/uat.cjs +1376 -125
  119. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  120. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  121. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  122. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  123. package/gsd-core/bin/lib/vendor/README.md +43 -5
  124. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  125. package/gsd-core/bin/lib/verification.cjs +14 -1
  126. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  127. package/gsd-core/bin/lib/verify.cjs +95 -40
  128. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  129. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  130. package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
  131. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  132. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  133. package/gsd-core/bin/shared/exit-codes.json +8 -0
  134. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  135. package/gsd-core/bin/shared/model-catalog.json +8 -1
  136. package/gsd-core/references/agent-contracts.md +3 -2
  137. package/gsd-core/references/api-coverage.md +24 -2
  138. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  139. package/gsd-core/references/checkpoints.md +37 -19
  140. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  141. package/gsd-core/references/edge-probe.md +8 -0
  142. package/gsd-core/references/execute-mvp-tdd.md +1 -3
  143. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  144. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  145. package/gsd-core/references/failing-direction.md +78 -0
  146. package/gsd-core/references/gate-prompts.md +1 -1
  147. package/gsd-core/references/git-integration.md +5 -5
  148. package/gsd-core/references/git-planning-commit.md +3 -3
  149. package/gsd-core/references/gsd-run-resolver.md +1 -1
  150. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  151. package/gsd-core/references/model-profiles.md +1 -1
  152. package/gsd-core/references/nyquist-compliance.md +74 -0
  153. package/gsd-core/references/offer-next.md +3 -5
  154. package/gsd-core/references/phase-argument-parsing.md +3 -3
  155. package/gsd-core/references/planner-failing-direction.md +53 -0
  156. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  157. package/gsd-core/references/planner-revision.md +1 -1
  158. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  159. package/gsd-core/references/planning-config.md +37 -8
  160. package/gsd-core/references/reviewer-instances.md +31 -0
  161. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  162. package/gsd-core/references/tdd.md +1 -3
  163. package/gsd-core/references/ui-brand.md +65 -21
  164. package/gsd-core/references/ui-consideration-probe.md +1 -1
  165. package/gsd-core/references/universal-anti-patterns.md +2 -2
  166. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  167. package/gsd-core/references/verify-mvp-mode.md +1 -1
  168. package/gsd-core/references/workstream-flag.md +11 -11
  169. package/gsd-core/templates/README.md +1 -1
  170. package/gsd-core/templates/SECURITY.md +3 -3
  171. package/gsd-core/templates/UI-SPEC.md +25 -3
  172. package/gsd-core/templates/VALIDATION.md +3 -3
  173. package/gsd-core/templates/phase-prompt.md +3 -0
  174. package/gsd-core/templates/state.md +7 -0
  175. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  176. package/gsd-core/workflows/add-backlog.md +1 -1
  177. package/gsd-core/workflows/add-phase.md +3 -3
  178. package/gsd-core/workflows/add-tests.md +3 -8
  179. package/gsd-core/workflows/add-todo.md +1 -1
  180. package/gsd-core/workflows/ai-integration-phase.md +4 -9
  181. package/gsd-core/workflows/audit-fix.md +12 -3
  182. package/gsd-core/workflows/audit-milestone.md +9 -9
  183. package/gsd-core/workflows/audit-uat.md +17 -2
  184. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  185. package/gsd-core/workflows/autonomous.md +10 -26
  186. package/gsd-core/workflows/check-todos.md +1 -1
  187. package/gsd-core/workflows/cleanup.md +2 -2
  188. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
  189. package/gsd-core/workflows/code-review-fix.md +1 -1
  190. package/gsd-core/workflows/code-review.md +121 -40
  191. package/gsd-core/workflows/complete-milestone.md +15 -10
  192. package/gsd-core/workflows/debug.md +5 -3
  193. package/gsd-core/workflows/diagnose-issues.md +12 -6
  194. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  195. package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
  196. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  197. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  198. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
  199. package/gsd-core/workflows/discuss-phase.md +1 -1
  200. package/gsd-core/workflows/do.md +3 -6
  201. package/gsd-core/workflows/docs-update.md +5 -4
  202. package/gsd-core/workflows/edit-phase.md +1 -1
  203. package/gsd-core/workflows/eval-review.md +4 -9
  204. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  205. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
  206. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  207. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  208. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  209. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
  210. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  211. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  212. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  213. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  214. package/gsd-core/workflows/execute-phase.md +38 -54
  215. package/gsd-core/workflows/execute-plan.md +17 -12
  216. package/gsd-core/workflows/explore.md +1 -1
  217. package/gsd-core/workflows/extract-learnings.md +1 -1
  218. package/gsd-core/workflows/fast.md +2 -2
  219. package/gsd-core/workflows/forensics.md +1 -1
  220. package/gsd-core/workflows/graduation.md +5 -5
  221. package/gsd-core/workflows/health.md +3 -6
  222. package/gsd-core/workflows/import.md +14 -11
  223. package/gsd-core/workflows/inbox.md +4 -5
  224. package/gsd-core/workflows/ingest-docs.md +44 -11
  225. package/gsd-core/workflows/insert-phase.md +5 -5
  226. package/gsd-core/workflows/list-seeds.md +5 -3
  227. package/gsd-core/workflows/list-workspaces.md +1 -1
  228. package/gsd-core/workflows/manager.md +12 -23
  229. package/gsd-core/workflows/map-codebase.md +1 -1
  230. package/gsd-core/workflows/milestone-summary.md +1 -1
  231. package/gsd-core/workflows/mvp-phase.md +2 -2
  232. package/gsd-core/workflows/new-milestone.md +9 -21
  233. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  234. package/gsd-core/workflows/new-project.md +12 -26
  235. package/gsd-core/workflows/new-workspace.md +1 -1
  236. package/gsd-core/workflows/next.md +2 -2
  237. package/gsd-core/workflows/pause-work.md +1 -1
  238. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  239. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  240. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  241. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  242. package/gsd-core/workflows/plan-phase.md +121 -42
  243. package/gsd-core/workflows/plan-review-convergence.md +46 -9
  244. package/gsd-core/workflows/plant-seed.md +2 -2
  245. package/gsd-core/workflows/pr-branch.md +187 -51
  246. package/gsd-core/workflows/profile-user.md +16 -14
  247. package/gsd-core/workflows/progress.md +27 -12
  248. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  249. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
  250. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  251. package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
  252. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  253. package/gsd-core/workflows/quick.md +20 -29
  254. package/gsd-core/workflows/remove-phase.md +4 -4
  255. package/gsd-core/workflows/remove-workspace.md +2 -2
  256. package/gsd-core/workflows/resume-project.md +8 -12
  257. package/gsd-core/workflows/review.md +193 -15
  258. package/gsd-core/workflows/scan.md +1 -1
  259. package/gsd-core/workflows/secure-phase.md +2 -2
  260. package/gsd-core/workflows/settings-advanced.md +7 -9
  261. package/gsd-core/workflows/settings-integrations.md +64 -31
  262. package/gsd-core/workflows/settings.md +3 -5
  263. package/gsd-core/workflows/ship.md +12 -6
  264. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  265. package/gsd-core/workflows/sketch.md +12 -18
  266. package/gsd-core/workflows/smart-entry.md +3 -5
  267. package/gsd-core/workflows/spec-phase.md +23 -1
  268. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  269. package/gsd-core/workflows/spike.md +20 -31
  270. package/gsd-core/workflows/stats.md +2 -2
  271. package/gsd-core/workflows/sync-skills.md +1 -1
  272. package/gsd-core/workflows/thread.md +11 -7
  273. package/gsd-core/workflows/transition.md +5 -5
  274. package/gsd-core/workflows/ui-phase.md +10 -16
  275. package/gsd-core/workflows/ui-review.md +6 -10
  276. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  277. package/gsd-core/workflows/undo.md +8 -16
  278. package/gsd-core/workflows/update.md +6 -10
  279. package/gsd-core/workflows/validate-phase.md +2 -2
  280. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  281. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  282. package/gsd-core/workflows/verify-work.md +57 -18
  283. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  284. package/hooks/dist/gsd-config-reload.js +18 -12
  285. package/hooks/dist/gsd-context-monitor.js +19 -10
  286. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  287. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  288. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  289. package/hooks/dist/gsd-cursor-stop.js +2 -1
  290. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  291. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  292. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  293. package/hooks/dist/gsd-graphify-update.sh +22 -18
  294. package/hooks/dist/gsd-node-runner.sh +76 -0
  295. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  296. package/hooks/dist/gsd-prompt-guard.js +16 -7
  297. package/hooks/dist/gsd-read-guard.js +16 -7
  298. package/hooks/dist/gsd-read-injection-scanner.js +17 -8
  299. package/hooks/dist/gsd-session-state.sh +1 -0
  300. package/hooks/dist/gsd-statusline.js +215 -26
  301. package/hooks/dist/gsd-validate-commit.sh +80 -6
  302. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  303. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  304. package/hooks/dist/gsd-workflow-guard.js +34 -16
  305. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  306. package/hooks/dist/gsd-write-guard.js +35 -25
  307. package/hooks/dist/lib/cli-exit.js +560 -0
  308. package/hooks/dist/lib/exit-code-registry.js +98 -0
  309. package/hooks/dist/lib/git-probe.js +84 -0
  310. package/hooks/dist/lib/hook-exit.js +81 -0
  311. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  312. package/hooks/gsd-agent-isolation-guard.js +77 -38
  313. package/hooks/gsd-config-reload.js +18 -12
  314. package/hooks/gsd-context-monitor.js +19 -10
  315. package/hooks/gsd-cursor-post-tool.js +3 -1
  316. package/hooks/gsd-cursor-pre-tool.js +3 -1
  317. package/hooks/gsd-cursor-session-start.js +2 -1
  318. package/hooks/gsd-cursor-stop.js +2 -1
  319. package/hooks/gsd-cursor-subagent-start.js +28 -23
  320. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  321. package/hooks/gsd-ensure-canonical-path.js +2 -1
  322. package/hooks/gsd-graphify-update.sh +22 -18
  323. package/hooks/gsd-node-runner.sh +76 -0
  324. package/hooks/gsd-phase-boundary.sh +1 -0
  325. package/hooks/gsd-prompt-guard.js +16 -7
  326. package/hooks/gsd-read-guard.js +16 -7
  327. package/hooks/gsd-read-injection-scanner.js +17 -8
  328. package/hooks/gsd-session-state.sh +1 -0
  329. package/hooks/gsd-statusline.js +215 -26
  330. package/hooks/gsd-validate-commit.sh +80 -6
  331. package/hooks/gsd-windsurf-pre-command.js +16 -11
  332. package/hooks/gsd-windsurf-pre-write.js +22 -13
  333. package/hooks/gsd-workflow-guard.js +34 -16
  334. package/hooks/gsd-worktree-path-guard.js +36 -21
  335. package/hooks/gsd-write-guard.js +35 -25
  336. package/hooks/lib/cli-exit.js +560 -0
  337. package/hooks/lib/exit-code-registry.js +98 -0
  338. package/hooks/lib/git-probe.js +84 -0
  339. package/hooks/lib/hook-exit.js +81 -0
  340. package/hooks/managed-hooks-registry.cjs +3 -0
  341. package/package.json +12 -7
  342. package/scripts/base64-scan.sh +74 -12
  343. package/scripts/build-hooks.js +5 -0
  344. package/scripts/check-glossary-refs.cjs +77 -15
  345. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  346. package/scripts/ci-check-job-near-cap.cjs +49 -0
  347. package/scripts/ci-pr-mergeability.cjs +262 -0
  348. package/scripts/ci-test-scope.cjs +45 -12
  349. package/scripts/ci-timeout-report.cjs +230 -0
  350. package/scripts/docs-guard-registry.cjs +396 -0
  351. package/scripts/gen-capability-registry.cjs +8 -6
  352. package/scripts/gen-exit-code-docs.cjs +318 -0
  353. package/scripts/gen-exit-code-registry.cjs +891 -0
  354. package/scripts/gen-features.cjs +836 -0
  355. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  356. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  357. package/scripts/gen-loop-host-contract.cjs +134 -1
  358. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  359. package/scripts/gen-state-md-docs.cjs +727 -0
  360. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  361. package/scripts/lib/ci-job-timing.cjs +72 -0
  362. package/scripts/lib/cli-exit.cjs +546 -44
  363. package/scripts/lib/drift-scan.cjs +32 -2
  364. package/scripts/lib/exit-code-registry.cjs +98 -0
  365. package/scripts/lib/ndjson-reporter.cjs +119 -0
  366. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  367. package/scripts/lint-docs-guard-registration.cjs +495 -0
  368. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  369. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  370. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  371. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  372. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  373. package/scripts/lint-phase-enumeration-drift.cjs +21 -8
  374. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  375. package/scripts/lint-removed-but-needed.cjs +184 -16
  376. package/scripts/lint-seam-enforcement.cjs +182 -0
  377. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  378. package/scripts/lint-source-test-name-collision.cjs +241 -0
  379. package/scripts/lint-state-write-path-drift.cjs +337 -432
  380. package/scripts/lint-test-file-count.allowlist.json +122 -4
  381. package/scripts/lint-test-file-count.cjs +25 -3
  382. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  383. package/scripts/lint-vendored-deps.cjs +208 -35
  384. package/scripts/mutation-matrix.cjs +599 -50
  385. package/scripts/prompt-injection-scan.sh +75 -14
  386. package/scripts/secret-scan.sh +75 -13
  387. package/scripts/select-docs-guards.cjs +56 -0
  388. package/scripts/sync-runtime-launcher.cjs +22 -3
  389. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  390. package/skills/gsd-import/SKILL.md +1 -1
  391. package/skills/gsd-quick/SKILL.md +8 -4
  392. package/vscode/package.json +1 -1
  393. package/bin/lib/ui-safety-gate.cjs +0 -109
  394. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  395. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -0,0 +1,193 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Pinned baseline for scripts/lint-docs-guard-registration.cjs's
5
+ * `docs-guard-exempt` identity ratchet — mirrors
6
+ * scripts/lint-allow-test-rule-refs.cjs's `allow-test-rule` ratchet
7
+ * (scripts/lib/allowlist-ratchet.cjs, ADR-456's pattern).
8
+ *
9
+ * Every test file basename here is a KNOWN, already-reviewed
10
+ * `// docs-guard-exempt: <reason>` marker. Nothing caps how many files may
11
+ * carry the marker in the abstract, but ADDING a new one anywhere in
12
+ * tests/*.test.cjs fails `lint-docs-guard-registration` until this baseline
13
+ * is deliberately updated — so a new exemption always shows up as a
14
+ * reviewable diff here, instead of silently opting a file out of the
15
+ * docs-guard registration requirement with zero signal.
16
+ *
17
+ * A file listed here that no longer exists, or no longer carries a real
18
+ * `docs-guard-exempt:` marker in its first 20 lines, is reported STALE and
19
+ * must be pruned (ratchet-down, same as the sibling gate).
20
+ *
21
+ * Seeded 2026-08-23 (#3753 security follow-up FIX 2) from every real marker
22
+ * found via scripts/lint-docs-guard-registration.cjs's own `findExemption`
23
+ * scan of tests/*.test.cjs — derived, not retyped from memory.
24
+ *
25
+ * Extended 2026-08-23 (#3753 correctness follow-up): the reader-detection
26
+ * fixes (Defects A/B/C) surfaced 86 previously-undetected docs/ readers. 40
27
+ * were genuine docs-content guards (added to DOCS_GUARD_TESTS in
28
+ * scripts/docs-guard-registry.cjs); the 46 added here touch a docs/ path
29
+ * only incidentally (fixture data, external URL citations, comment-only
30
+ * mentions, metadata labels, or scan-exclusion lists) — see each file's own
31
+ * `docs-guard-exempt:` marker for its specific reason.
32
+ */
33
+ const DOCS_GUARD_EXEMPT_BASELINE = [
34
+ 'adr-parser.test.cjs',
35
+ 'adr-parser.unit.test.cjs',
36
+ 'agent-marker-documentation-guard.test.cjs',
37
+ 'antigravity-upgrades.test.cjs',
38
+ 'capability-cli.test.cjs',
39
+ 'capability-validator-task-content-resolver.test.cjs',
40
+ 'ci-docs-guard-registry.test.cjs',
41
+ 'ci-test-scope.test.cjs',
42
+ 'cline-install.test.cjs',
43
+ 'code-review-depth.test.cjs',
44
+ 'code-review-pipeline-regression.test.cjs',
45
+ 'codebuddy-upgrades.test.cjs',
46
+ 'commands.test.cjs',
47
+ 'commit-docs-bypass.test.cjs',
48
+ 'complexity-trigger.test.cjs',
49
+ 'concurrency-safety.test.cjs',
50
+ 'cursor-imperative-reference.test.cjs',
51
+ 'declarative-reference-antigravity.test.cjs',
52
+ 'declarative-reference-zcode.test.cjs',
53
+ 'emitted-attribution.test.cjs',
54
+ 'eslint-rules.test.cjs',
55
+ 'estimate-calibrate.test.cjs',
56
+ 'gen-context-index.test.cjs',
57
+ 'gen-registry.test.cjs',
58
+ 'gsd-agent-isolation-guard.test.cjs',
59
+ 'hermes-dispatch-upgrade.test.cjs',
60
+ 'install-minimal-hooks.test.cjs',
61
+ 'install-runtime-artifacts.test.cjs',
62
+ 'installer-migration-config-root-marker.test.cjs',
63
+ 'installer-migration-pi-extension-ext.test.cjs',
64
+ 'installer-migrations.test.cjs',
65
+ 'kimi-upgrades.test.cjs',
66
+ 'lint-allow-test-rule-refs.test.cjs',
67
+ 'lint-docs-command-form.test.cjs',
68
+ 'lint-docs-required.test.cjs',
69
+ 'manifest-version-sync.test.cjs',
70
+ 'milestone-archive.test.cjs',
71
+ 'model-resolver.test.cjs',
72
+ 'new-project-mvp-prompt.test.cjs',
73
+ 'onboard-command.test.cjs',
74
+ 'opencode-command-dir-plural.test.cjs',
75
+ 'phase.test.cjs',
76
+ 'pr-branch-planning-filter.test.cjs',
77
+ 'precommit-alias-drift-hook.test.cjs',
78
+ 'removed-but-needed-lint.test.cjs',
79
+ 'repo-invariants.test.cjs',
80
+ 'require-issue-link-policy.test.cjs',
81
+ 'reviewer-manifest-body.test.cjs',
82
+ 'run-tests-harness.test.cjs',
83
+ 'runtime-name-policy.test.cjs',
84
+ 'security-prompt-injection.security.test.cjs',
85
+ 'shipped-reference-cites.test.cjs',
86
+ 'state.test.cjs',
87
+ 'worktree-safety.test.cjs',
88
+ ];
89
+
90
+ /**
91
+ * Security follow-up FIX 3: a per-file fingerprint of every distinct
92
+ * `docs/...` path TOKEN referenced by each baselined file (derived via
93
+ * scripts/lint-docs-guard-registration.cjs's `extractDocsPathReferences`,
94
+ * never retyped from memory). A baselined file whose live fingerprint
95
+ * DIFFERS from the recorded one here fails the lint — the exemption's
96
+ * premise ("this file doesn't really guard shipped docs content") may no
97
+ * longer hold and a human must re-confirm it before the entry is updated.
98
+ * Keeping this a plain, sorted, diffable path list (not a hash) is
99
+ * deliberate: a reviewer can see exactly WHAT changed from the PR diff
100
+ * alone.
101
+ *
102
+ * Seeded 2026-08-23 alongside DOCS_GUARD_EXEMPT_BASELINE above, from the
103
+ * exact same scan.
104
+ */
105
+ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
106
+ 'adr-parser.test.cjs': ['docs/adr/0001.md', 'docs/adr/0002.md', 'docs/adr/0010.md', 'docs/adr/NNNN.md'],
107
+ 'adr-parser.unit.test.cjs': ['docs/adr/0001.md', 'docs/adr/0099.md', 'docs/adr/NNNN.md'],
108
+ 'agent-marker-documentation-guard.test.cjs': ['docs/reference', 'docs/reference/workflow-fragments.md'],
109
+ 'antigravity-upgrades.test.cjs': ['docs/cli', 'docs/cli/gcli-migration', 'docs/cli/permissions'],
110
+ 'capability-cli.test.cjs': ['docs/reference/gsd-capability-command.md'],
111
+ // #3970: cites docs/adr/3646-per-task-content-resolution-seam.md in an
112
+ // explanatory comment describing ADR-3646's Decision 3; the file never
113
+ // reads that (or any) docs/ file.
114
+ 'capability-validator-task-content-resolver.test.cjs': ['docs/adr/3646-per-task-content-resolution-seam.md'],
115
+ 'ci-docs-guard-registry.test.cjs': [
116
+ 'docs/AGENTS.md', 'docs/COMMANDS.md', 'docs/INVENTORY.md', 'docs/a.md', 'docs/adr',
117
+ 'docs/adr/0001-example.md', 'docs/adrenaline.md', 'docs/bar.md', 'docs/foo.md', 'docs/how-to/foo.md',
118
+ 'docs/how-to/some-unrelated-guide.md', 'docs/how-to/x.md', 'docs/some-unrelated-file.md',
119
+ 'docs/totally-unrelated.md',
120
+ ],
121
+ 'ci-test-scope.test.cjs': [
122
+ 'docs/a.md', 'docs/adr', 'docs/adr/22-plan-drift-guard.md', 'docs/how-to/configure-model-profiles.md',
123
+ 'docs/installer-migrations.md', 'docs/ja-JP', 'docs/ja-JP/USAGE.md', 'docs/usage.md', 'docs/x.md',
124
+ ],
125
+ 'cline-install.test.cjs': ['docs/guide.md'],
126
+ 'code-review-depth.test.cjs': ['docs/src/auth/x.ts'],
127
+ 'code-review-pipeline-regression.test.cjs': ['docs/DEVELOPMENT.md'],
128
+ 'codebuddy-upgrades.test.cjs': ['docs/cli/sub-agents'],
129
+ 'commands.test.cjs': ['docs/x.md'],
130
+ 'commit-docs-bypass.test.cjs': [
131
+ 'docs/40-design.md', 'docs/CONFIGURATION.md', 'docs/readme.md', 'docs/tracked-var-mentioning',
132
+ ],
133
+ 'complexity-trigger.test.cjs': ['docs/readme.md'],
134
+ // #3884: cites docs/CLI-TOOLS.md:736 in an explanatory comment describing
135
+ // the real `frontmatter get <file> [--field key]` CLI shape; the file
136
+ // never reads that (or any) docs/ file.
137
+ 'concurrency-safety.test.cjs': ['docs/CLI-TOOLS.md'],
138
+ 'cursor-imperative-reference.test.cjs': ['docs/sdk/typescript'],
139
+ 'declarative-reference-antigravity.test.cjs': ['docs/cli/features'],
140
+ 'declarative-reference-zcode.test.cjs': ['docs/reference/host-integration-capability-matrix.md'],
141
+ 'emitted-attribution.test.cjs': ['docs/README.md', 'docs/tests', 'docs/tests/helpers/install-shared.cjs'],
142
+ // #3914: cites docs/adr/3889-process-exit-contract.md ~:350-362 in an
143
+ // explanatory comment describing the intentional computed-property
144
+ // boundary move; the file never reads that (or any) docs/ file.
145
+ 'eslint-rules.test.cjs': ['docs/adr/3889-process-exit-contract.md', 'docs/readme.md'],
146
+ 'estimate-calibrate.test.cjs': [
147
+ 'docs/adr', 'docs/adr/2629-phase-effort-estimation-calibration.md', 'docs/reference',
148
+ 'docs/reference/planning-artifacts.md',
149
+ ],
150
+ 'gen-context-index.test.cjs': ['docs/CONTEXT-INDEX.json', 'docs/INVENTORY-MANIFEST.json'],
151
+ 'gen-registry.test.cjs': ['docs/registries', 'docs/registries/reviewers.json'],
152
+ 'gsd-agent-isolation-guard.test.cjs': ['docs/adr/1239-...md', 'docs/adr/1239-gsd-embeddable-orchestration-engine.md'],
153
+ 'hermes-dispatch-upgrade.test.cjs': ['docs/guides/delegation-patterns.md'],
154
+ 'install-minimal-hooks.test.cjs': ['docs/en/hooks', 'docs/en/users/features/hooks'],
155
+ 'install-runtime-artifacts.test.cjs': ['docs/CONFIGURATION.md', 'docs/adr/58-...md', 'docs/adr/58-runtime-install-policy-module.md', 'docs/cli/slash-commands'],
156
+ 'installer-migration-config-root-marker.test.cjs': ['docs/installer-migrations.md'],
157
+ 'installer-migration-pi-extension-ext.test.cjs': ['docs/installer-migrations.md'],
158
+ 'installer-migrations.test.cjs': ['docs/installer-migrations.md'],
159
+ 'kimi-upgrades.test.cjs': ['docs/reference/host-integration-capability-matrix.md'],
160
+ 'lint-allow-test-rule-refs.test.cjs': ['docs/readme.md'],
161
+ 'lint-docs-command-form.test.cjs': ['docs/adr', 'docs/adr/999-example.md', 'docs/how-to/example.md'],
162
+ 'lint-docs-required.test.cjs': [
163
+ 'docs/COMMANDS.md', 'docs/USER-GUIDE.md', 'docs/adr', 'docs/adr/0001-foo.md', 'docs/adr/0099-new.md',
164
+ 'docs/agents', 'docs/agents/triage-labels.md',
165
+ ],
166
+ 'manifest-version-sync.test.cjs': [],
167
+ // #3884: re-confirmed — the added docs/CLI-TOOLS.md:458 reference is the
168
+ // same class as the existing docs/TESTING-SUITES.md one (a placement-note
169
+ // / explanatory comment citing documented CLI behavior for context, never
170
+ // a read target); the exemption's premise still holds for both.
171
+ 'milestone-archive.test.cjs': ['docs/CLI-TOOLS.md', 'docs/TESTING-SUITES.md'],
172
+ 'model-resolver.test.cjs': ['docs/TESTING-SUITES.md'],
173
+ 'new-project-mvp-prompt.test.cjs': ['docs/CONFIGURATION.md'],
174
+ 'onboard-command.test.cjs': ['docs/adr/0001-runtime.md'],
175
+ 'opencode-command-dir-plural.test.cjs': ['docs/commands'],
176
+ 'phase.test.cjs': ['docs/adr/3524-...md', 'docs/adr/3524-cjs-sdk-hard-seam.md'],
177
+ 'pr-branch-planning-filter.test.cjs': ['docs/readme.md'],
178
+ 'precommit-alias-drift-hook.test.cjs': ['docs/adr/0174-...md', 'docs/adr/0174-retire-gsd-sdk-package-boundary.md'],
179
+ 'removed-but-needed-lint.test.cjs': [
180
+ 'docs/README.md', 'docs/getting-started.md', 'docs/gsd-new-workspace.md', 'docs/setup.md', 'docs/some-doc.md',
181
+ ],
182
+ 'repo-invariants.test.cjs': ['docs/FEATURES.md', 'docs/workflows/README'],
183
+ 'require-issue-link-policy.test.cjs': ['docs/-prefixed', 'docs/CONFIGURATION.md', 'docs/a.md', 'docs/b.md', 'docs/guide.md'],
184
+ 'reviewer-manifest-body.test.cjs': ['docs/how-to/ship-a-reviewer-lane.md'],
185
+ 'run-tests-harness.test.cjs': ['docs/TESTING-SUITES.md'],
186
+ 'runtime-name-policy.test.cjs': ['docs/customize/skills'],
187
+ 'security-prompt-injection.security.test.cjs': ['docs/notes.md'],
188
+ 'shipped-reference-cites.test.cjs': [],
189
+ 'state.test.cjs': ['docs/CONFIGURATION.md', 'docs/reference/state-md.md'],
190
+ 'worktree-safety.test.cjs': ['docs/SUMMARY.md'],
191
+ };
192
+
193
+ module.exports = { DOCS_GUARD_EXEMPT_BASELINE, DOCS_GUARD_EXEMPT_DOCS_PATHS };
@@ -7,6 +7,10 @@
7
7
  "path": "src/vendor/re2js.d.cts",
8
8
  "reason": "Vendored third-party type declaration (#3477): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored .cjs it describes is globally ignored as verbatim upstream output. Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules."
9
9
  },
10
+ {
11
+ "path": "src/vendor/js-yaml.d.cts",
12
+ "reason": "Vendored third-party type declaration (ADR-3473 §8.1, #3881): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored js-yaml .cjs it describes is globally ignored as verbatim upstream output (gsd-core/bin/lib/vendor/**). Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules — same precedent as src/vendor/re2js.d.cts above."
13
+ },
10
14
  {
11
15
  "path": "tests/fixtures/brand-typing/bad-calibrated-as-sample-basis.cts",
12
16
  "reason": "Deliberate MUST-NOT-COMPILE type-error fixture (#3059): type-aware linting would fail by design; it exists to prove the compiler rejects it."
@@ -2,10 +2,16 @@
2
2
  'use strict';
3
3
 
4
4
  /**
5
- * lint-fix-has-regression-test.cjs — gate: every fix(#NNNN) commit must
5
+ * lint-fix-has-regression-tests.cjs — gate: every fix(#NNNN) commit must
6
6
  * include at least one behavioral test file (tests/*.test.cjs) that is NOT
7
7
  * an auto-generated fixture/baseline.
8
8
  *
9
+ * Renamed (was `lint-fix-has-regression-test.cjs`, singular): the old
10
+ * basename matched Node's `*-test.EXT` test-collection pattern, so the
11
+ * remote push-gate runner collected and executed this SOURCE file as a
12
+ * test while GitHub CI (which globs only tests/**\/*.test.cjs) never saw
13
+ * it — see scripts/lint-source-test-name-collision.cjs.
14
+ *
9
15
  * ## Why
10
16
  *
11
17
  * CONTRIBUTING.md:47: "Fix it. Write a test that would have caught the bug."
@@ -82,7 +88,7 @@ function getChangedTestFiles(baseRef) {
82
88
 
83
89
  function main() {
84
90
  if (process.env.GSD_SKIP_REGRESSION_TEST_GATE === '1') {
85
- console.log('lint-fix-has-regression-test: SKIPPED (GSD_SKIP_REGRESSION_TEST_GATE=1)');
91
+ console.log('lint-fix-has-regression-tests: SKIPPED (GSD_SKIP_REGRESSION_TEST_GATE=1)');
86
92
  return;
87
93
  }
88
94
 
@@ -92,12 +98,12 @@ function main() {
92
98
  try {
93
99
  fixCommits = getFixCommits(baseRef);
94
100
  } catch {
95
- console.log(`lint-fix-has-regression-test: no fix/feat commits found vs ${baseRef}, skipping`);
101
+ console.log(`lint-fix-has-regression-tests: no fix/feat commits found vs ${baseRef}, skipping`);
96
102
  return;
97
103
  }
98
104
 
99
105
  if (fixCommits.length === 0) {
100
- console.log('lint-fix-has-regression-test: no fix/feat commits, passing');
106
+ console.log('lint-fix-has-regression-tests: no fix/feat commits, passing');
101
107
  return;
102
108
  }
103
109
 
@@ -113,7 +119,7 @@ function main() {
113
119
  .map((c) => ` ${c.sha} ${c.subject}`)
114
120
  .join('\n');
115
121
  throw new ExitError(1,
116
- `lint-fix-has-regression-test: ${fixCommits.length} fix/feat commit(s) but ZERO behavioral test files (*.test.cjs) in the diff.\n` +
122
+ `lint-fix-has-regression-tests: ${fixCommits.length} fix/feat commit(s) but ZERO behavioral test files (*.test.cjs) in the diff.\n` +
117
123
  `Auto-generated fixtures (tests/fixtures/, *-baseline.json) do NOT count.\n\n` +
118
124
  `Fix commits:\n${commitList}\n\n` +
119
125
  `CONTRIBUTING.md:47: "Write a test that would have caught the bug."\n` +
@@ -123,7 +129,7 @@ function main() {
123
129
  }
124
130
 
125
131
  console.log(
126
- `lint-fix-has-regression-test: PASS — ${fixCommits.length} fix/feat commit(s), ` +
132
+ `lint-fix-has-regression-tests: PASS — ${fixCommits.length} fix/feat commit(s), ` +
127
133
  `${testFiles.length} behavioral test file(s): ${testFiles.join(', ')}`
128
134
  );
129
135
  }
@@ -58,7 +58,7 @@
58
58
  * real, non-mocked `buildPlanningSnapshot(tmpCwd)` call (see
59
59
  * `tests/health-diagnostic-rules/root-existence.test.cjs`). This guard
60
60
  * therefore verifies the fixture-proof invariant STATICALLY against the
61
- * test files' own text — mirroring `scripts/lint-fix-has-regression-test.cjs`'s
61
+ * test files' own text — mirroring `scripts/lint-fix-has-regression-tests.cjs`'s
62
62
  * house style — rather than dynamically re-running fixture-building code
63
63
  * this guard does not own.
64
64
  */
@@ -82,14 +82,70 @@ const CONSISTENCY_TEST_FILE = path.join(REPO_ROOT, 'tests', 'health-diagnostic-r
82
82
  const CONSISTENCY_CODE_PREFIX_RE = /^C\d{3}$/;
83
83
 
84
84
  // Phase 12 (#3310, ADR-3180 §8.5 extension) — the S0NN namespace's own
85
- // fixture-proof pass. These 7 codes are NOT collected in any exported
86
- // `Rule[]` array: `cmdStateValidate` (src/state.cts) builds `Diagnostic[]`
87
- // directly via a local `stateDiagnostic()` helper, not via the rule-table
88
- // evaluator (out of scope for the RULES/CONSISTENCY_RULES-keyed passes
89
- // above). The list is therefore hardcoded here instead of read from the
90
- // compiled module.
85
+ // fixture-proof pass. These codes are NOT collected in any exported `Rule[]`
86
+ // array: `cmdStateValidate` (src/state.cts) builds `Diagnostic[]` directly via
87
+ // a local `stateDiagnostic()` helper, not via the rule-table evaluator (out of
88
+ // scope for the RULES/CONSISTENCY_RULES-keyed passes above).
89
+ //
90
+ // #3696: the list is DISCOVERED from the source, not hardcoded. It used to be a
91
+ // literal `['S001', ..., 'S007']`, which is precisely the shape ADR-3180
92
+ // Decision 4(a) forbids — "guards discover call sites by whole-repo scan, never
93
+ // by an allowlist of known files" — because such a guard can only ever be as
94
+ // complete as the author's recall. Adding S008/S009 to `cmdStateValidate` left
95
+ // this pass reporting a confident, green "7 code(s), all fixture-covered" while
96
+ // two new codes had no fixture requirement at all: a zero it did not earn. The
97
+ // scan below cannot report a code it has not read out of the source.
91
98
  const STATE_VALIDATE_TEST_FILE = path.join(REPO_ROOT, 'tests', 'state.test.cjs');
92
- const STATE_VALIDATE_CODES = ['S001', 'S002', 'S003', 'S004', 'S005', 'S006', 'S007'];
99
+ const STATE_VALIDATE_SOURCE_FILE = path.join(REPO_ROOT, 'src', 'state.cts');
100
+ // `g` is required by String.prototype.matchAll, which (unlike .exec) does not
101
+ // carry lastIndex across calls — so this constant is safe to share.
102
+ // #3696 review: `\s*` before `(` too. Requiring no space silently dropped a
103
+ // `stateDiagnostic ('S010', …)` call site from the discovered set, and since the
104
+ // fail-closed check below only fires on a FULLY empty result, a partial miss
105
+ // escaped the fixture-proof check entirely — the same "only as complete as the
106
+ // author's recall" failure this rewrite exists to prevent.
107
+ const STATE_DIAGNOSTIC_CALL_RE = /\bstateDiagnostic\s*\(\s*(['"`])(S\d{3})\1/g;
108
+
109
+ /**
110
+ * Every S0NN code `cmdStateValidate` can emit, read off its
111
+ * `stateDiagnostic(...)` call sites in `sourceText`.
112
+ *
113
+ * Takes the source TEXT rather than reading the path itself, so the discovery
114
+ * rule is exercisable against controlled fixtures — including the fail-closed
115
+ * path below, which is the branch that matters and which a path-reading
116
+ * function could only be tested on by mutating the real `src/state.cts`.
117
+ *
118
+ * Fails closed. An empty result means the helper was renamed or its call shape
119
+ * changed, and a guard that answers "0 codes, all covered" to that is worse than
120
+ * no guard — so it raises instead. Line/regex-based (not full AST) per this
121
+ * repo's existing lint-guard house style, same as TITLED_BLOCK_RE below.
122
+ */
123
+ function discoverStateValidateCodes(sourceText, sourceLabel = formatRepoRelative(STATE_VALIDATE_SOURCE_FILE)) {
124
+ const codes = [...sourceText.matchAll(STATE_DIAGNOSTIC_CALL_RE)].map((m) => m[2]);
125
+ const unique = [...new Set(codes)].sort();
126
+ if (unique.length === 0) {
127
+ throw new ExitError(
128
+ 1,
129
+ `lint-health-diagnostic-rule-table: found no stateDiagnostic() call sites in ${sourceLabel}.\n`
130
+ + ' This pass discovers the S0NN code set from those call sites (#3696), so an empty\n'
131
+ + ' result means the helper was renamed or its call shape changed — not that there are\n'
132
+ + ' no codes. Update STATE_DIAGNOSTIC_CALL_RE to match the new shape.\n',
133
+ );
134
+ }
135
+ return unique;
136
+ }
137
+
138
+ function readStateValidateSource() {
139
+ if (!fs.existsSync(STATE_VALIDATE_SOURCE_FILE)) {
140
+ throw new ExitError(
141
+ 1,
142
+ `lint-health-diagnostic-rule-table: cannot discover S0NN codes — ${formatRepoRelative(STATE_VALIDATE_SOURCE_FILE)} not found.\n`,
143
+ );
144
+ }
145
+ return fs.readFileSync(STATE_VALIDATE_SOURCE_FILE, 'utf8');
146
+ }
147
+
148
+ const STATE_VALIDATE_CODES = discoverStateValidateCodes(readStateValidateSource());
93
149
 
94
150
  // Matches `describe(`/`test(`/`it(` calls whose first argument is a string
95
151
  // literal, capturing that literal as the block's title. Line/regex-based
@@ -401,4 +457,5 @@ module.exports = {
401
457
  CONSISTENCY_TEST_FILE,
402
458
  STATE_VALIDATE_TEST_FILE,
403
459
  STATE_VALIDATE_CODES,
460
+ discoverStateValidateCodes,
404
461
  };
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * scripts/lint-mutation-test-derivation-drift.cjs
6
+ *
7
+ * #3881 follow-up ("one YAML parser" mutation-matrix piece 2): guards the
8
+ * derivation engine in scripts/mutation-matrix.cjs against the exact drift
9
+ * class PR #3888 shipped — a test file added on a branch that directly
10
+ * `require()`s a covered module's built artifact AND names itself after that
11
+ * module (`<module>.test.cjs` / `<module>.property.test.cjs` /
12
+ * `<module>-anything.test.cjs`), yet is never wired into that module's
13
+ * mutation shard.
14
+ *
15
+ * `scripts/mutation-matrix.cjs`'s `computeModuleTests` already derives most of
16
+ * a module's `tests` array automatically from exactly that (require + naming)
17
+ * signal, so a NEWLY drifting file of that shape is normally impossible — it
18
+ * would be auto-included the moment it exists. What this guard catches is the
19
+ * one case the derivation cannot self-correct: a file matching BOTH signals
20
+ * that the module's own `excludeTests` withholds. `excludeTests` entries are
21
+ * a deliberate, reasoned decision (see mutation-matrix.cjs's per-module
22
+ * comments) — this guard does not second-guess that decision, but it DOES
23
+ * verify every such file is EITHER accounted for in `tests` (auto-derived or
24
+ * `extraTests`) OR named in `excludeTests`. A file satisfying both signals
25
+ * that is in NEITHER list is exactly the drift #3888 shipped: nobody made a
26
+ * decision about it at all.
27
+ *
28
+ * Pure core (`findDrift`) so tests can drive it against a synthetic COVERED +
29
+ * requiring-files map without touching the real tests/ tree; `main()` wires
30
+ * it to the real repo for `npm run lint:ci`.
31
+ */
32
+
33
+ const path = require('node:path');
34
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
35
+
36
+ /**
37
+ * Pure: for every module in `covered`, every file in `requiringFiles[module]`
38
+ * that ALSO matches the module's naming rule must be present in either
39
+ * `covered[module].tests` (already `tests/`-prefixed, as computeModuleTests
40
+ * emits it) or `covered[module].excludeTests` (bare basenames). Anything
41
+ * matching both signals but present in neither is reported as drift.
42
+ *
43
+ * @param {object} covered - mutation-matrix.cjs's COVERED (post `computeModuleTests`)
44
+ * @param {(moduleName: string) => string[]} findRequiringTestFiles - basenames requiring the module's artifact
45
+ * @param {(moduleName: string, file: string) => boolean} matchesModuleNamingRule
46
+ * @returns {{module: string, file: string}[]}
47
+ */
48
+ function findDrift(covered, findRequiringTestFiles, matchesModuleNamingRule) {
49
+ const drift = [];
50
+ for (const [moduleName, entry] of Object.entries(covered)) {
51
+ const testsSet = new Set((entry.tests || []).map((t) => path.basename(t)));
52
+ const excludeSet = new Set(entry.excludeTests || []);
53
+ for (const file of findRequiringTestFiles(moduleName)) {
54
+ if (!matchesModuleNamingRule(moduleName, file)) continue;
55
+ if (testsSet.has(file) || excludeSet.has(file)) continue;
56
+ drift.push({ module: moduleName, file });
57
+ }
58
+ }
59
+ return drift;
60
+ }
61
+
62
+ function main() {
63
+ const {
64
+ COVERED,
65
+ findRequiringTestFiles,
66
+ matchesModuleNamingRule,
67
+ } = require('./mutation-matrix.cjs');
68
+
69
+ const drift = findDrift(COVERED, findRequiringTestFiles, matchesModuleNamingRule);
70
+
71
+ if (drift.length === 0) {
72
+ console.log(`ok mutation-test-derivation-drift: every module-named test file requiring a covered module has an explicit disposition (${Object.keys(COVERED).length} modules checked)`);
73
+ return;
74
+ }
75
+
76
+ console.error('mutation-test-derivation-drift: test file(s) require a covered module and match its naming rule, but have no disposition in scripts/mutation-matrix.cjs (neither auto-derived/extraTests nor excludeTests):');
77
+ for (const d of drift) {
78
+ console.error(` [${d.module}] tests/${d.file}`);
79
+ }
80
+ console.error('\n remedy: in scripts/mutation-matrix.cjs, either let it auto-derive (do nothing further if it already appears in COVERED[<module>].tests), or add it to extraTests, or add a REASONED excludeTests entry explaining why it is deliberately withheld from the mutation shard.');
81
+ throw new ExitError(1);
82
+ }
83
+
84
+ module.exports = { findDrift };
85
+
86
+ if (require.main === module) runMain(main);
@@ -62,11 +62,22 @@
62
62
  * outstanding WORK wants the live set"): a LOOKUP, DIAGNOSTIC or ARCHIVAL
63
63
  * pass wants the physical set; only "which phases belong to this milestone"
64
64
  * wants the scoped set.
65
- * - `src/roadmap.cts` `cmdRoadmapAnalyze`: builds `_phaseDirNames` as a
66
- * heading->directory LOOKUP INDEX, not a milestone enumeration. It must
67
- * see the PHYSICAL set so a heading already scoped by
68
- * `extractCurrentMilestoneScoped` can find its directory; filtering it
69
- * through the owner would scope the same set twice.
65
+ * - `src/roadmap.cts` `cmdRoadmapAnalyze` — MIGRATED (#3882, ADR-3473 §8.2):
66
+ * `_phaseDirNames`, a heading->directory LOOKUP INDEX (not a milestone
67
+ * enumeration; must see the PHYSICAL set so a heading already scoped by
68
+ * `extractCurrentMilestoneScoped` can find its directory), now calls
69
+ * `listAllPhaseDirs(phasesDir, { includeSentinels: true })` instead of a
70
+ * hand-rolled `readdirSync` — no longer exempted, since there is nothing
71
+ * left in this function for either detector to catch.
72
+ * - `src/init.cts` `cmdInitMilestoneOp`'s `diskPhaseDirs` — MIGRATED (#3882,
73
+ * ADR-3473 §8.2): the same heading->directory LOOKUP INDEX shape as
74
+ * `cmdRoadmapAnalyze`'s `_phaseDirNames` above, now calls
75
+ * `listAllPhaseDirs(phasesDir, { includeSentinels: true })`. Its sibling
76
+ * readdirSync (the no-ROADMAP-headings-found fallback) was already routed
77
+ * through `listMilestonePhaseDirs` before this phase and remains so — no
78
+ * longer exempted; the function's other former exemption reason
79
+ * (`detectHasPriorPhases`/`detectUiPhaseActive`, unrelated call sites in
80
+ * the same file) is unaffected.
70
81
  * - `src/verify.cts` `cmdValidateHealth`: a project-wide HEALTH-CHECK sweep
71
82
  * (config drift, phase-directory naming, duplicate-directory collisions,
72
83
  * unsummarized-plan detection) — same "sweep everything, report gaps"
@@ -280,11 +291,13 @@ const OWNER_FILES = new Set([
280
291
  // `lint-milestone-window-drift.cjs`'s FUNCTION_SCOPED_EXEMPTIONS mechanism.
281
292
  // See the header comment for the full written reason behind each entry.
282
293
  const FUNCTION_SCOPED_EXEMPTIONS = new Map([
283
- [path.join('src', 'roadmap.cts'), new Set(['cmdRoadmapAnalyze'])],
284
294
  [path.join('src', 'verify.cts'), new Set(['cmdValidateHealth', 'cmdVerifySchemaDrift'])],
285
- [path.join('src', 'init.cts'), new Set(['detectHasPriorPhases', 'detectUiPhaseActive', 'cmdInitMilestoneOp'])],
295
+ [path.join('src', 'init.cts'), new Set(['detectHasPriorPhases', 'detectUiPhaseActive'])],
286
296
  [path.join('src', 'milestone.cts'), new Set(['archivePhaseDirectories', 'cmdMilestoneComplete', 'cmdPhasesClear'])],
287
- [path.join('src', 'phase.cts'), new Set(['cmdPhasesList', 'cmdPhaseNextDecimal', 'cmdPhasePlanIndex', 'cmdPhaseInsert', 'renameDecimalPhases', 'renameIntegerPhases'])],
297
+ // #3849: collectSiblingWorktreePhaseNums reads a SIBLING worktree's phases dir —
298
+ // a different checkout listMilestonePhaseDirs (cwd-scoped) cannot serve; the
299
+ // allocators' own on-disk scans stay unexempted.
300
+ [path.join('src', 'phase.cts'), new Set(['cmdPhasesList', 'cmdPhaseNextDecimal', 'cmdPhasePlanIndex', 'cmdPhaseInsert', 'renameDecimalPhases', 'renameIntegerPhases', 'collectSiblingWorktreePhaseNums'])],
288
301
  [path.join('src', 'audit.cts'), new Set(['listAuditPhaseTargets'])],
289
302
  [path.join('src', 'commands.cts'), new Set(['cmdHistoryDigest'])],
290
303
  [path.join('src', 'state.cts'), new Set(['cmdStateValidate', 'cmdStateSync', 'cmdStateRebuild'])],
@@ -370,8 +370,45 @@ function writeBaseline(root, violations) {
370
370
  return entries;
371
371
  }
372
372
 
373
+ /**
374
+ * Resolve the scan root for a CLI run. Default: this repo — exactly what
375
+ * `lint:ci` invokes. `--root <dir>` overrides it (#3640) so the CLI
376
+ * end-to-end test can prove the guard's FAIL path against an isolated temp
377
+ * tree instead of writing its fixture into the shared `gsd-core/workflows/`
378
+ * that parallel test chunks observe mid-lifecycle (the emitted-provenance
379
+ * manifest build copies the transient file into all 19 runtime manifests;
380
+ * the #3333 TOCTOU ENOENT crash was the same writer's first symptom).
381
+ *
382
+ * Usage errors exit 2 — a code distinct from the guard's own findings exit
383
+ * (1), so tests and CI can tell "bad invocation" from "drift found" without
384
+ * parsing stderr. Fail-closed on every bad shape, because a wrong root is
385
+ * not harmless: `--root --update` would otherwise resolve the flag itself
386
+ * into a cwd-relative path and --update would happily `mkdir -p` a stray
387
+ * baseline tree inside the shared source directory (#3640's write class).
388
+ * A value that starts with `-` (another flag) or does not name an existing
389
+ * directory is therefore a usage error, never a scan root. With repeated
390
+ * `--root` flags the first wins (same first-match convention as the boolean
391
+ * `--update` parse).
392
+ */
393
+ function resolveScanRoot() {
394
+ const rootIdx = process.argv.indexOf('--root');
395
+ if (rootIdx === -1) return path.join(__dirname, '..');
396
+ const value = process.argv[rootIdx + 1];
397
+ const isDir = value !== undefined
398
+ && !value.startsWith('-')
399
+ && fs.existsSync(value)
400
+ && fs.statSync(value).isDirectory();
401
+ if (!isDir) {
402
+ process.stderr.write('planning-prompt-drift: --root requires an existing directory argument (usage: --root <dir>)\n');
403
+ process.exitCode = 2;
404
+ return null;
405
+ }
406
+ return path.resolve(value);
407
+ }
408
+
373
409
  function main() {
374
- const root = path.join(__dirname, '..');
410
+ const root = resolveScanRoot();
411
+ if (root === null) return;
375
412
  const update = process.argv.includes('--update');
376
413
  const violations = scanRepo(root);
377
414