@opengsd/gsd-core 1.10.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 (544) 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 +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -0,0 +1,461 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * lint-health-diagnostic-rule-table.cjs — gate: enforces ADR-3180 §8.2's 1:1
6
+ * rule-code invariant and §8.5's fixture-proof invariant for
7
+ * `src/health-diagnostic.cts`'s RULES table (Phase 11, #3309).
8
+ *
9
+ * ## What this enforces
10
+ *
11
+ * 1. (§8.2 rule 1 — 1:1 code invariant) Every `rule.code` in RULES (exported
12
+ * from the compiled `gsd-core/bin/lib/health-diagnostic.cjs`) is unique,
13
+ * and every rule's `severity` is one of `SEVERITY`'s values. The severity
14
+ * check exists only to confirm the compiled artifact was not hand-edited
15
+ * to bypass the `Rule.severity` required field TypeScript already
16
+ * enforces at compile time — "severity is a property of the RULE, never
17
+ * the emit call."
18
+ * 2. (§8.5 — fixture-proof invariant) Every code in RULES has a paired test:
19
+ * the code string (e.g. `'W001'`) appears as a literal AND within a
20
+ * `describe(`/`test(` block whose title also names that exact code, in
21
+ * one of the health-diagnostic test files
22
+ * (`tests/health-diagnostic-rules/*.test.cjs`,
23
+ * `tests/health-diagnostic.test.cjs`). A mere comment/string mention
24
+ * outside a titled block does not count as coverage.
25
+ *
26
+ * EXCEPTION — `PERMANENTLY_INERT_CODES` (below): a rule whose `check`
27
+ * always returns `[]` BY DESIGN (the real check lives outside the rule
28
+ * table entirely, because it needs ambient I/O `Rule.check` cannot
29
+ * perform — §8.1 rule 1) can never satisfy a real fixture-proof, no
30
+ * matter how many tests reference its code. Before this exception
31
+ * existed, W024 "passed" this guard only because an unrelated test title
32
+ * (the RULES-array shape assertion, "exports exactly 5 rules: W024, ...")
33
+ * happened to contain the string "W024" — accidental coverage, not proof
34
+ * the rule can fire. `PERMANENTLY_INERT_CODES` makes that exemption
35
+ * explicit and auditable instead of relying on a coincidental title
36
+ * match, and the PASS output now reports exempted codes SEPARATELY from
37
+ * genuinely fixture-covered ones rather than folding them together.
38
+ * 3. (Phase 12, #3310 — S0NN pass) `cmdStateValidate` (src/state.cts) emits
39
+ * 7 coded diagnostics (S001-S007) built inline via a local
40
+ * `stateDiagnostic()` helper — NOT `Rule`-table entries, so they are not
41
+ * read from the compiled module the way RULES/CONSISTENCY_RULES are.
42
+ * This third pass hardcodes that code list (`STATE_VALIDATE_CODES`,
43
+ * below) and applies the SAME §8.5 fixture-proof check against
44
+ * `tests/state.test.cjs`, reported in its own PASS/FAIL section —
45
+ * mirroring the C0NN pass's structure, just with a hardcoded list
46
+ * instead of a `Rule[]` array as the code source.
47
+ *
48
+ * Design: .gsd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md
49
+ * ("The lint guard (§8.2 1:1 invariant + §8.5 fixture proof)").
50
+ *
51
+ * ## Deviation from the design doc's original plan
52
+ *
53
+ * The design doc assumed fixtures would live as separate files at
54
+ * `tests/fixtures/health-diagnostic/<code>.*`. That did not happen during
55
+ * implementation — all 8 rule-group test files
56
+ * (`tests/health-diagnostic-rules/*.test.cjs`) build fixtures INLINE via
57
+ * real temp directories (`createTempDir()` from `tests/helpers.cjs`) and a
58
+ * real, non-mocked `buildPlanningSnapshot(tmpCwd)` call (see
59
+ * `tests/health-diagnostic-rules/root-existence.test.cjs`). This guard
60
+ * therefore verifies the fixture-proof invariant STATICALLY against the
61
+ * test files' own text — mirroring `scripts/lint-fix-has-regression-tests.cjs`'s
62
+ * house style — rather than dynamically re-running fixture-building code
63
+ * this guard does not own.
64
+ */
65
+
66
+ const fs = require('node:fs');
67
+ const path = require('node:path');
68
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
69
+
70
+ const REPO_ROOT = path.join(__dirname, '..');
71
+ const COMPILED_MODULE_REL = 'gsd-core/bin/lib/health-diagnostic.cjs';
72
+ const COMPILED_MODULE_PATH = path.join(REPO_ROOT, COMPILED_MODULE_REL);
73
+ const TEST_GROUP_DIR = path.join(REPO_ROOT, 'tests', 'health-diagnostic-rules');
74
+ const SKELETON_TEST_FILE = path.join(REPO_ROOT, 'tests', 'health-diagnostic.test.cjs');
75
+
76
+ // Phase 12 (#3310, ADR-3180 §8.4) — the C0NN namespace's own fixture-proof
77
+ // test file. §8.5 extends to `CONSISTENCY_RULES`'s NEW codes only (C001-C004
78
+ // — W006/W007 are already fixture-proofed above, against the SAME `Rule`
79
+ // objects; re-checking them here would be redundant, not additional
80
+ // coverage).
81
+ const CONSISTENCY_TEST_FILE = path.join(REPO_ROOT, 'tests', 'health-diagnostic-rules', 'consistency.test.cjs');
82
+ const CONSISTENCY_CODE_PREFIX_RE = /^C\d{3}$/;
83
+
84
+ // Phase 12 (#3310, ADR-3180 §8.5 extension) — the S0NN namespace's own
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.
98
+ const STATE_VALIDATE_TEST_FILE = path.join(REPO_ROOT, 'tests', 'state.test.cjs');
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());
149
+
150
+ // Matches `describe(`/`test(`/`it(` calls whose first argument is a string
151
+ // literal, capturing that literal as the block's title. Line/regex-based
152
+ // (not full AST) per this repo's existing lint-guard house style
153
+ // (scripts/lint-planning-snapshot-bypass-drift.cjs's scanCode precedent).
154
+ const TITLED_BLOCK_RE = /\b(describe|test|it)\(\s*(['"`])((?:\\.|(?!\2)[^\\])*)\2/g;
155
+
156
+ // Rule codes whose `check` is a documented PERMANENT no-op (always returns
157
+ // `[]`) because the real check requires ambient I/O forbidden inside a
158
+ // `Rule.check(snapshot)` (§8.1 rule 1) — the real check runs elsewhere,
159
+ // outside the rule table. These can never be proven via a real
160
+ // diagnostic-firing fixture, so they are exempted from the §8.5 fixture-proof
161
+ // invariant explicitly here rather than via an accidental test-title match.
162
+ // Adding an entry is a deliberate, reviewed decision — see each reason.
163
+ const PERMANENTLY_INERT_CODES = new Map([
164
+ [
165
+ 'W024',
166
+ 'readStateHeadFreshness requires a git-log shell-out, forbidden ambient I/O for Rule.check ' +
167
+ '(§8.1 rule 1) — the real check runs in cmdValidateHealth itself, outside the rule table ' +
168
+ '(src/verify.cts). This rule-table entry is a permanent no-op by design, not a fixture gap.',
169
+ ],
170
+ ]);
171
+
172
+ /**
173
+ * Load the compiled health-diagnostic module. Throws a clear ExitError
174
+ * (not a raw MODULE_NOT_FOUND) if `npm run build:lib` has not run.
175
+ */
176
+ function loadCompiledModule(compiledPath = COMPILED_MODULE_PATH) {
177
+ if (!fs.existsSync(compiledPath)) {
178
+ throw new ExitError(
179
+ 2,
180
+ `lint-health-diagnostic-rule-table: compiled artifact not found at ${COMPILED_MODULE_REL}.\n` +
181
+ 'Run `npm run build:lib` first.',
182
+ );
183
+ }
184
+ return require(compiledPath);
185
+ }
186
+
187
+ /**
188
+ * §8.2 rule 1 — 1:1 code invariant: every rule.code is unique, and every
189
+ * rule's severity is a member of SEVERITY's values.
190
+ *
191
+ * @param {Array<{code: string, severity: string}>} rules
192
+ * @param {Record<string, string>} severity SEVERITY export (code -> value)
193
+ * @returns {{duplicates: Array<{code: string, count: number}>, badSeverities: Array<{code: string, severity: unknown}>}}
194
+ */
195
+ function checkOneToOneInvariant(rules, severity) {
196
+ const severityValues = new Set(Object.values(severity));
197
+ const counts = new Map();
198
+ const badSeverities = [];
199
+
200
+ for (const rule of rules) {
201
+ counts.set(rule.code, (counts.get(rule.code) || 0) + 1);
202
+ if (!severityValues.has(rule.severity)) {
203
+ badSeverities.push({ code: rule.code, severity: rule.severity });
204
+ }
205
+ }
206
+
207
+ const duplicates = [...counts.entries()]
208
+ .filter(([, count]) => count > 1)
209
+ .map(([code, count]) => ({ code, count }));
210
+
211
+ return { duplicates, badSeverities };
212
+ }
213
+
214
+ /**
215
+ * Extracts every `describe(`/`test(`/`it(` block title found in `text`.
216
+ *
217
+ * @param {string} text
218
+ * @returns {string[]}
219
+ */
220
+ function extractTitledBlocks(text) {
221
+ const titles = [];
222
+ TITLED_BLOCK_RE.lastIndex = 0;
223
+ let match;
224
+ while ((match = TITLED_BLOCK_RE.exec(text)) !== null) {
225
+ titles.push(match[3]);
226
+ }
227
+ return titles;
228
+ }
229
+
230
+ /**
231
+ * True iff `code` appears verbatim, as a whole token, inside at least one of
232
+ * `titles`. Whole-token match guards against a shorter code accidentally
233
+ * substring-matching inside an unrelated longer token.
234
+ *
235
+ * @param {string} code
236
+ * @param {string[]} titles
237
+ */
238
+ function codeAppearsInTitle(code, titles) {
239
+ const codeRe = new RegExp(`(?:^|[^A-Za-z0-9])${code}(?:$|[^A-Za-z0-9])`);
240
+ return titles.some((title) => codeRe.test(`|${title}|`));
241
+ }
242
+
243
+ /**
244
+ * Locates every health-diagnostic test file this guard scans for §8.5
245
+ * fixture-proof coverage.
246
+ *
247
+ * @param {string} repoRoot
248
+ * @returns {string[]} absolute paths, sorted
249
+ */
250
+ function findHealthDiagnosticTestFiles(repoRoot = REPO_ROOT) {
251
+ const groupDir = path.join(repoRoot, 'tests', 'health-diagnostic-rules');
252
+ const files = [];
253
+ if (fs.existsSync(groupDir)) {
254
+ for (const entry of fs.readdirSync(groupDir)) {
255
+ if (entry.endsWith('.test.cjs')) {
256
+ files.push(path.join(groupDir, entry));
257
+ }
258
+ }
259
+ }
260
+ const skeletonTestFile = path.join(repoRoot, 'tests', 'health-diagnostic.test.cjs');
261
+ if (fs.existsSync(skeletonTestFile)) {
262
+ files.push(skeletonTestFile);
263
+ }
264
+ return files.sort();
265
+ }
266
+
267
+ /**
268
+ * §8.5 — fixture-proof invariant: for every code in `rules`, confirm at
269
+ * least one test file in `testFiles` has a `describe(`/`test(`/`it(` block
270
+ * whose title names that exact code — UNLESS the code is listed in
271
+ * `PERMANENTLY_INERT_CODES`, in which case it is reported separately as
272
+ * `exempted` (visibly, not folded into "covered") and never fails the guard
273
+ * regardless of test coverage.
274
+ *
275
+ * @param {Array<{code: string}>} rules
276
+ * @param {string[]} testFiles absolute paths to *.test.cjs files to scan
277
+ * @param {Map<string, string>} inertCodes PERMANENTLY_INERT_CODES (injectable for tests)
278
+ * @returns {{uncovered: string[], exempted: string[], testFilesScanned: string[]}}
279
+ */
280
+ function checkFixtureProofInvariant(rules, testFiles, inertCodes = PERMANENTLY_INERT_CODES) {
281
+ const allTitles = [];
282
+ for (const file of testFiles) {
283
+ const text = fs.readFileSync(file, 'utf8');
284
+ allTitles.push(...extractTitledBlocks(text));
285
+ }
286
+
287
+ const uncovered = [];
288
+ const exempted = [];
289
+ for (const rule of rules) {
290
+ if (inertCodes.has(rule.code)) {
291
+ exempted.push(rule.code);
292
+ continue;
293
+ }
294
+ if (!codeAppearsInTitle(rule.code, allTitles)) {
295
+ uncovered.push(rule.code);
296
+ }
297
+ }
298
+
299
+ return { uncovered, exempted, testFilesScanned: testFiles };
300
+ }
301
+
302
+ function formatRepoRelative(absPath) {
303
+ return path.relative(REPO_ROOT, absPath).split(path.sep).join('/');
304
+ }
305
+
306
+ function main() {
307
+ const { RULES, CONSISTENCY_RULES, SEVERITY } = loadCompiledModule();
308
+
309
+ const { duplicates, badSeverities } = checkOneToOneInvariant(RULES, SEVERITY);
310
+
311
+ const testFiles = findHealthDiagnosticTestFiles(REPO_ROOT);
312
+ const { uncovered, exempted } = checkFixtureProofInvariant(RULES, testFiles);
313
+
314
+ // ─── C0NN check pass (Phase 12, #3310) — separate from the W/E/I pass
315
+ // above, own reporting section, does NOT re-check W006/W007's fixture
316
+ // proof (already covered above against the same `Rule` objects).
317
+ const consistencyNewRules = CONSISTENCY_RULES.filter((r) => CONSISTENCY_CODE_PREFIX_RE.test(r.code));
318
+ const { duplicates: consistencyDuplicates, badSeverities: consistencyBadSeverities } = checkOneToOneInvariant(
319
+ consistencyNewRules,
320
+ SEVERITY,
321
+ );
322
+ const consistencyTestFiles = fs.existsSync(CONSISTENCY_TEST_FILE) ? [CONSISTENCY_TEST_FILE] : [];
323
+ const { uncovered: consistencyUncovered } = checkFixtureProofInvariant(
324
+ consistencyNewRules,
325
+ consistencyTestFiles,
326
+ new Map(),
327
+ );
328
+
329
+ // ─── S0NN check pass (Phase 12, #3310) — separate from the W/E/I and C0NN
330
+ // passes above, own reporting section. Hardcoded code list (see the
331
+ // STATE_VALIDATE_CODES comment above) rather than read from a Rule[] array.
332
+ const stateValidateTestFiles = fs.existsSync(STATE_VALIDATE_TEST_FILE) ? [STATE_VALIDATE_TEST_FILE] : [];
333
+ const { uncovered: stateValidateUncovered } = checkFixtureProofInvariant(
334
+ STATE_VALIDATE_CODES.map((code) => ({ code })),
335
+ stateValidateTestFiles,
336
+ new Map(),
337
+ );
338
+
339
+ const problems = [];
340
+
341
+ if (duplicates.length > 0) {
342
+ const list = duplicates.map((d) => ` ${d.code} (${d.count} occurrences)`).join('\n');
343
+ problems.push(
344
+ `§8.2 rule 1 violated: ${duplicates.length} duplicated rule code(s) in RULES ` +
345
+ `(${COMPILED_MODULE_REL}):\n${list}\n` +
346
+ ' remedy: codes are append-only and 1:1 with a single Rule — rename or remove the duplicate.',
347
+ );
348
+ }
349
+
350
+ if (badSeverities.length > 0) {
351
+ const list = badSeverities
352
+ .map((b) => ` ${b.code}: severity=${JSON.stringify(b.severity)}`)
353
+ .join('\n');
354
+ problems.push(
355
+ `§8.2 rule 3 violated: ${badSeverities.length} rule(s) with a severity not in SEVERITY's values:\n${list}\n` +
356
+ ' remedy: severity is a property of the RULE — set it to SEVERITY.ERROR/WARNING/INFO.',
357
+ );
358
+ }
359
+
360
+ if (uncovered.length > 0) {
361
+ const scannedList = testFiles.map(formatRepoRelative).join('\n ');
362
+ problems.push(
363
+ `§8.5 violated: ${uncovered.length} rule code(s) with no describe()/test() block naming them ` +
364
+ `(a comment or bare string mention does not count):\n ${uncovered.join(', ')}\n\n` +
365
+ ` Searched these test files:\n ${scannedList}\n\n` +
366
+ ' remedy: add or extend a describe()/test() title in the matching ' +
367
+ 'tests/health-diagnostic-rules/<group>.test.cjs file so the block title ' +
368
+ `names the code verbatim (e.g. describe('${uncovered[0]} — ...', () => { ... })), ` +
369
+ 'and drive the rule to fire against a real fixture built via createTempDir() + buildPlanningSnapshot() ' +
370
+ '(see tests/health-diagnostic-rules/root-existence.test.cjs).',
371
+ );
372
+ }
373
+
374
+ if (consistencyDuplicates.length > 0) {
375
+ const list = consistencyDuplicates.map((d) => ` ${d.code} (${d.count} occurrences)`).join('\n');
376
+ problems.push(
377
+ `§8.2 rule 1 violated (C0NN namespace): ${consistencyDuplicates.length} duplicated rule code(s) in ` +
378
+ `CONSISTENCY_RULES (${COMPILED_MODULE_REL}):\n${list}\n` +
379
+ ' remedy: codes are append-only and 1:1 with a single Rule — rename or remove the duplicate.',
380
+ );
381
+ }
382
+
383
+ if (consistencyBadSeverities.length > 0) {
384
+ const list = consistencyBadSeverities
385
+ .map((b) => ` ${b.code}: severity=${JSON.stringify(b.severity)}`)
386
+ .join('\n');
387
+ problems.push(
388
+ `§8.2 rule 3 violated (C0NN namespace): ${consistencyBadSeverities.length} rule(s) with a severity not ` +
389
+ `in SEVERITY's values:\n${list}\n` +
390
+ ' remedy: severity is a property of the RULE — set it to SEVERITY.ERROR/WARNING/INFO.',
391
+ );
392
+ }
393
+
394
+ if (consistencyUncovered.length > 0) {
395
+ problems.push(
396
+ `§8.5 violated (C0NN namespace): ${consistencyUncovered.length} rule code(s) with no describe()/test() ` +
397
+ `block naming them (a comment or bare string mention does not count):\n ${consistencyUncovered.join(', ')}\n\n` +
398
+ ` Searched: ${formatRepoRelative(CONSISTENCY_TEST_FILE)}` +
399
+ (consistencyTestFiles.length === 0 ? ' (file not found)' : '') +
400
+ '\n\n remedy: add or extend a describe()/test() title in tests/health-diagnostic-rules/consistency.test.cjs ' +
401
+ `so the block title names the code verbatim (e.g. describe('${consistencyUncovered[0]} — ...', () => { ... })), ` +
402
+ 'and drive the rule to fire against a real fixture built via createTempDir() + buildPlanningSnapshot().',
403
+ );
404
+ }
405
+
406
+ if (stateValidateUncovered.length > 0) {
407
+ problems.push(
408
+ `§8.5 violated (S0NN namespace): ${stateValidateUncovered.length} code(s) with no describe()/test() ` +
409
+ `block naming them (a comment or bare string mention does not count):\n ${stateValidateUncovered.join(', ')}\n\n` +
410
+ ` Searched: ${formatRepoRelative(STATE_VALIDATE_TEST_FILE)}` +
411
+ (stateValidateTestFiles.length === 0 ? ' (file not found)' : '') +
412
+ '\n\n remedy: add or extend a describe()/test() title in tests/state.test.cjs ' +
413
+ `so the block title names the code verbatim (e.g. test('${stateValidateUncovered[0]}: ...', () => { ... })), ` +
414
+ 'mirroring the existing `#3310 state validate — S0NN coded diagnostics` describe block.',
415
+ );
416
+ }
417
+
418
+ if (problems.length > 0) {
419
+ throw new ExitError(1, `${problems.join('\n\n')}\n`);
420
+ }
421
+
422
+ const coveredCount = RULES.length - exempted.length;
423
+ const exemptedDetail = exempted
424
+ .map((code) => `${code} (${PERMANENTLY_INERT_CODES.get(code)})`)
425
+ .join('; ');
426
+
427
+ console.log(
428
+ `lint-health-diagnostic-rule-table: PASS — ${RULES.length} rule code(s): ${coveredCount} covered by a ` +
429
+ `real fixture, ${exempted.length} exempted across ${testFiles.length} test file(s).` +
430
+ (exempted.length > 0 ? `\n Exempted: ${exemptedDetail}` : ''),
431
+ );
432
+ console.log(
433
+ `lint-health-diagnostic-rule-table: PASS (C0NN namespace) — ${consistencyNewRules.length} new rule code(s) ` +
434
+ `(${consistencyNewRules.map((r) => r.code).join(', ')}), all fixture-covered in ` +
435
+ `${formatRepoRelative(CONSISTENCY_TEST_FILE)}.`,
436
+ );
437
+ console.log(
438
+ `lint-health-diagnostic-rule-table: PASS (S0NN namespace) — ${STATE_VALIDATE_CODES.length} code(s) ` +
439
+ `(${STATE_VALIDATE_CODES.join(', ')}), all fixture-covered in ` +
440
+ `${formatRepoRelative(STATE_VALIDATE_TEST_FILE)}.`,
441
+ );
442
+ }
443
+
444
+ runMain(main);
445
+
446
+ module.exports = {
447
+ loadCompiledModule,
448
+ checkOneToOneInvariant,
449
+ extractTitledBlocks,
450
+ codeAppearsInTitle,
451
+ findHealthDiagnosticTestFiles,
452
+ checkFixtureProofInvariant,
453
+ PERMANENTLY_INERT_CODES,
454
+ COMPILED_MODULE_PATH,
455
+ TEST_GROUP_DIR,
456
+ SKELETON_TEST_FILE,
457
+ CONSISTENCY_TEST_FILE,
458
+ STATE_VALIDATE_TEST_FILE,
459
+ STATE_VALIDATE_CODES,
460
+ discoverStateValidateCodes,
461
+ };