@opengsd/gsd-core 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -9,12 +9,29 @@
9
9
  # scripts/base64-scan.sh --file path/to/file # Scan a single file
10
10
  # scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
11
11
  #
12
- # Exit codes:
13
- # 0 = clean
14
- # 1 = findings detected
15
- # 2 = usage error
12
+ # Exit codes (ADR-3889, #3908 — registered in gsd-core/bin/shared/exit-codes.json):
13
+ # 0 = clean
14
+ # 1 = findings detected
15
+ # $EXIT_USAGE (64) = usage error (bad argv, missing --file/--dir target)
16
+ # $EXIT_NO_INPUT (66) = ran; scope established; zero files in scope (genuinely empty)
17
+ # $EXIT_UNAVAILABLE (69) = could not establish scope (bad ref, not a repo, unreadable dir)
16
18
  set -euo pipefail
17
19
 
20
+ # ─── Exit-code registry (ADR-3889, #3908) ────────────────────────────────────
21
+ # Resolved relative to THIS script's location, not the caller's cwd. Loud,
22
+ # non-zero failure if the fragment is missing — never fall back to a guessed
23
+ # literal integer, and never let a missing registry silently degrade to
24
+ # exit 0.
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ EXIT_CODES_SH="$SCRIPT_DIR/../gsd-core/bin/shared/exit-codes.sh"
27
+ if [[ ! -f "$EXIT_CODES_SH" ]]; then
28
+ echo "base64-scan: FATAL: exit-code registry not found at $EXIT_CODES_SH" >&2
29
+ echo " Regenerate with: node scripts/gen-exit-code-registry.cjs --write" >&2
30
+ exit 1
31
+ fi
32
+ # shellcheck disable=SC1090
33
+ . "$EXIT_CODES_SH"
34
+
18
35
  # ── Locale hardening (#116) ───────────────────────────────────────────────────
19
36
  # BSD tr (macOS) treats input bytes as multi-byte characters under any UTF-8
20
37
  # locale. When the input to `tr -cd '[:print:]'` contains bytes that are not
@@ -180,7 +197,31 @@ collect_files() {
180
197
  case "$mode" in
181
198
  --diff)
182
199
  local base="${1:-origin/main}"
183
- git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
200
+ # Run git separately from the filter pipe so its OWN exit status (not
201
+ # grep's) decides whether the diff could be established. stdout and
202
+ # stderr are captured SEPARATELY (never merged with `2>&1`) so that a
203
+ # warning git writes to stderr on an otherwise successful diff can
204
+ # never be mistaken for a filename in the file list. On failure the
205
+ # captured stderr is emitted as the diagnostic; on success it is
206
+ # forwarded as a warning, never folded into the file list.
207
+ # `|| true` on the filter below is CORRECT (not gratuitous): `grep -v`
208
+ # exits 1 when every line is filtered out (e.g. an all-images diff),
209
+ # which is a legitimate empty result, not a failure to run.
210
+ local raw status err_file
211
+ err_file=$(mktemp)
212
+ raw=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>"$err_file")
213
+ status=$?
214
+ if (( status != 0 )); then
215
+ cat "$err_file" >&2
216
+ rm -f "$err_file"
217
+ exit "$EXIT_UNAVAILABLE"
218
+ fi
219
+ if [[ -s "$err_file" ]]; then
220
+ echo "Warning: git diff emitted stderr output:" >&2
221
+ cat "$err_file" >&2
222
+ fi
223
+ rm -f "$err_file"
224
+ printf '%s\n' "$raw" \
184
225
  | grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
185
226
  ;;
186
227
  --file)
@@ -188,24 +229,41 @@ collect_files() {
188
229
  echo "$1"
189
230
  else
190
231
  echo "Error: file not found: $1" >&2
191
- exit 2
232
+ exit "$EXIT_USAGE"
192
233
  fi
193
234
  ;;
194
235
  --dir)
195
236
  local dir="$1"
196
237
  if [[ ! -d "$dir" ]]; then
197
238
  echo "Error: directory not found: $dir" >&2
198
- exit 2
239
+ exit "$EXIT_USAGE"
240
+ fi
241
+ # Same treatment as --diff: a `find` that fails (e.g. permission
242
+ # denied) must not be reported as an empty directory, and stdout/stderr
243
+ # are captured separately so a stderr warning never enters the file list.
244
+ local raw status err_file
245
+ err_file=$(mktemp)
246
+ raw=$(find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
247
+ ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>"$err_file")
248
+ status=$?
249
+ if (( status != 0 )); then
250
+ cat "$err_file" >&2
251
+ rm -f "$err_file"
252
+ exit "$EXIT_UNAVAILABLE"
253
+ fi
254
+ if [[ -s "$err_file" ]]; then
255
+ echo "Warning: find emitted stderr output:" >&2
256
+ cat "$err_file" >&2
199
257
  fi
200
- find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
201
- ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
258
+ rm -f "$err_file"
259
+ printf '%s\n' "$raw"
202
260
  ;;
203
261
  --stdin)
204
262
  cat
205
263
  ;;
206
264
  *)
207
265
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
208
- exit 2
266
+ exit "$EXIT_USAGE"
209
267
  ;;
210
268
  esac
211
269
  }
@@ -309,7 +367,7 @@ extract_and_check_blobs() {
309
367
  main() {
310
368
  if [[ $# -eq 0 ]]; then
311
369
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
312
- exit 2
370
+ exit "$EXIT_USAGE"
313
371
  fi
314
372
 
315
373
  load_ignorelist
@@ -321,8 +379,12 @@ main() {
321
379
  files=$(collect_files "$mode" "$@")
322
380
 
323
381
  if [[ -z "$files" ]]; then
382
+ # collect_files already exited (UNAVAILABLE/USAGE) for anything that
383
+ # could not establish scope. Reaching here with an empty result means
384
+ # scope WAS established and is genuinely empty (e.g. an all-images
385
+ # diff) — that is NO_INPUT, not a silent clean pass.
324
386
  echo "base64-scan: no files to scan"
325
- exit 0
387
+ exit "$EXIT_NO_INPUT"
326
388
  fi
327
389
 
328
390
  local total=0
@@ -61,6 +61,8 @@ const HOOKS_TO_COPY = [
61
61
  'gsd-prompt-guard.js',
62
62
  'gsd-read-guard.js',
63
63
  'gsd-read-injection-scanner.js',
64
+ // Secret-file read guard (#4221) — replaces the installer's Read(.env*) deny rules
65
+ 'gsd-secret-read-guard.js',
64
66
  'gsd-statusline.js',
65
67
  'gsd-update-banner.js',
66
68
  'gsd-workflow-guard.js',
@@ -71,6 +73,16 @@ const HOOKS_TO_COPY = [
71
73
  'gsd-session-state.sh',
72
74
  'gsd-validate-commit.sh',
73
75
  'gsd-phase-boundary.sh',
76
+ // Portable node resolver (#3662). Managed JS hook commands under
77
+ // --portable-hooks route through it (bash <resolver> <baked-node>
78
+ // <script>) so node resolves at hook-fire time in every environment
79
+ // sharing the config root. It IS registered in MANAGED_HOOKS
80
+ // (managed-hooks-registry.cjs) for staleness tracking like every other
81
+ // shipped .sh hook, and install.js stamps its {{GSD_VERSION}} header the
82
+ // same way (#4076 — the prior comment here claimed the opposite on both
83
+ // counts, which is why the header was missing and staleness detection was
84
+ // permanently broken for this file).
85
+ 'gsd-node-runner.sh',
74
86
  // Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
75
87
  // .planning/config.json graphify.auto_update; off by default.
76
88
  'gsd-graphify-update.sh'
@@ -65,7 +65,7 @@ const TRACKED_PREFIXES = [
65
65
  const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
66
66
 
67
67
  /**
68
- * Paths CONTEXT.md documents whose ABSENCE is the healthy steady state (#2778).
68
+ * Paths CONTEXT.md documents whose ABSENCE is the healthy steady state.
69
69
  *
70
70
  * `TRACKED_PREFIXES` skips claims this gate *cannot* check. This is the narrower
71
71
  * third case: a claim it must not check, because "does not exist" is the correct
@@ -78,14 +78,43 @@ const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
78
78
  * shipping an empty stub. So the file is absent on a healthy `next` and present
79
79
  * only inside a PR that needs it, and asserting either way is wrong.
80
80
  *
81
- * Until #2778 this passed only by accident: CONTEXT.md's `RULESET.` entries are
82
- * themselves backtick-wrapped and contain backticks, so the sequential pairing in
83
- * `extractTrackedRefs` happened to leave this token outside a code span. Any edit
84
- * that shifted the parity — such as #2778's own — exposed it. A gate that passes
85
- * by luck is not passing; naming the exemption makes the intent explicit and
86
- * survives the next edit.
81
+ * The emitted-attribution family (`tests/fixtures/golden-install-parity`,
82
+ * `tests/golden-install-parity.test.cjs`, `scripts/gen-golden-install-parity-zcode.cjs`,
83
+ * `tests/agent-size-baseline.json`, `tests/workflow-size-baseline.json`,
84
+ * `scripts/git-merge-regen-driver.cjs`, `scripts/update-size-baseline.cjs`) was RETIRED
85
+ * by the #2724 cutover — the differential attribution check replaced the committed
86
+ * baselines and their generator/bridge tooling. CONTEXT.md's RULESET.EMITTED_ATTRIBUTION
87
+ * predicate documents that retirement ("Historically …"), so the mentions are history,
88
+ * not live claims, and their absence is exactly the healthy state the retirement
89
+ * produced. The whole documented family is listed, not just the members today's tick
90
+ * parity happens to hide — the two tooling paths were invisible only because of where
91
+ * line 585's inner backticks sat, which is the #2778 luck this gate must not rely on.
92
+ *
93
+ * `scripts/eslint-rules` appears only inside a CONTRASTIVE mention ("the local
94
+ * plugin lives at `eslint-rules/` (repo root, NOT `scripts/eslint-rules/`)") — the
95
+ * predicate asserts where the directory is NOT, so non-existence is the claim
96
+ * being made, not drift away from one.
97
+ *
98
+ * Until #2778 this set's first entry passed only by accident: CONTEXT.md's
99
+ * `RULESET.` entries are themselves backtick-wrapped and contain backticks, so the
100
+ * sequential pairing in `extractTrackedRefs` happened to leave this token outside a
101
+ * code span. Any edit that shifted the parity — such as #2778's own — exposed it.
102
+ * A gate that passes by luck is not passing; naming the exemption makes the intent
103
+ * explicit and survives the next edit. #3604 removed the luck itself (pairing is
104
+ * per line), which is what surfaced the entries above: each names a path whose
105
+ * absence is deliberate, so each is exempted by name for the same reason.
87
106
  */
88
- const INTENTIONALLY_ABSENT = new Set(['tests/emitted-drift-ack.json']);
107
+ const INTENTIONALLY_ABSENT = new Set([
108
+ 'tests/emitted-drift-ack.json',
109
+ 'tests/fixtures/golden-install-parity',
110
+ 'tests/golden-install-parity.test.cjs',
111
+ 'scripts/gen-golden-install-parity-zcode.cjs',
112
+ 'tests/agent-size-baseline.json',
113
+ 'tests/workflow-size-baseline.json',
114
+ 'scripts/git-merge-regen-driver.cjs',
115
+ 'scripts/update-size-baseline.cjs',
116
+ 'scripts/eslint-rules',
117
+ ]);
89
118
 
90
119
  /**
91
120
  * Shape a backticked token must have to even be considered a path candidate:
@@ -119,18 +148,51 @@ function isWithinRoot(token) {
119
148
  /**
120
149
  * Every distinct, trackable file-path token referenced in `text`, with any
121
150
  * trailing `:<line>` suffix stripped.
151
+ *
152
+ * #3604: pairing is per LINE, not over the whole text. A single whole-text pass
153
+ * `[^`]+` crosses newlines, so one odd-backtick line (the `RULESET.*` predicate
154
+ * format is backtick-wrapped and its values sometimes contain backticks) shifted
155
+ * the pairing of every later line — a tracked token's visibility depended on
156
+ * where it sat, which is how a renamed test file stayed invisible for weeks.
157
+ *
158
+ * The fact-store predicate lines (one `CLASS.subkey=value` fact per line,
159
+ * backtick-wrapped as a whole) carry REAL paths in their values; the span itself
160
+ * is not path-shaped (spaces, `=`), so a second pass harvests path-shaped tracked
161
+ * tokens from inside any predicate-shaped span rather than letting the outer
162
+ * wrapper hide them.
163
+ *
164
+ * Fragment guards: a real path in this repo never ends in `-` or `.` — those are
165
+ * remnants of glob/template mentions (`scripts/gen-*.cjs`, `tests/foo.*.test.cjs`)
166
+ * split at the `*` — and `NNNN` is the ADR filename template token
167
+ * (CONTRIBUTING's "Do not compute a next number locally"), never a real path.
122
168
  */
123
169
  function extractTrackedRefs(text) {
124
170
  const tokens = new Set();
125
- const re = /`([^`]+)`/g;
126
- let m;
127
- while ((m = re.exec(text)) !== null) {
128
- const raw = m[1];
129
- if (!PATH_TOKEN_RE.test(raw)) continue;
171
+ const add = (raw) => {
172
+ if (!PATH_TOKEN_RE.test(raw)) return;
130
173
  const token = raw.replace(/:\d+$/, '');
131
- if (!isTracked(token)) continue;
132
- if (!isWithinRoot(token)) continue;
174
+ // Fragment guard, on the EMITTED token (after the :line strip, so
175
+ // `src/foo-:12` is judged on `src/foo-`): glob/template remnants end in `-`
176
+ // or `.`, a real path in this repo never does.
177
+ if (!/[A-Za-z0-9_]$/.test(token)) return;
178
+ if (token.includes('NNNN')) return;
179
+ if (!isTracked(token)) return;
180
+ if (!isWithinRoot(token)) return;
133
181
  tokens.add(token);
182
+ };
183
+ const subTokenRe = /[\w.-]+(?:\/[\w.-]+)*/g;
184
+ for (const line of text.split(/\r?\n/)) {
185
+ const re = /`([^`]+)`/g;
186
+ let m;
187
+ while ((m = re.exec(line)) !== null) {
188
+ const span = m[1];
189
+ if (PATH_TOKEN_RE.test(span)) {
190
+ add(span);
191
+ continue;
192
+ }
193
+ if (!/^[A-Z][A-Za-z0-9_.-]*=/.test(span)) continue;
194
+ for (const sub of span.matchAll(subTokenRe)) add(sub[0]);
195
+ }
134
196
  }
135
197
  return tokens;
136
198
  }
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * scripts/check-mutation-score-ratchet.cjs
6
+ *
7
+ * #3881 follow-up ("one YAML parser" mutation-matrix piece 3): makes each
8
+ * module's `minScore` floor (scripts/mutation-matrix.cjs COVERED) ratchet
9
+ * UPWARD instead of sitting wherever it was last hand-set.
10
+ *
11
+ * Two halves of "never lowerable without a reasoned marker":
12
+ * 1. `tests/mutation-matrix-ratchet.test.cjs`'s RATCHET_BASELINE already
13
+ * enforces the DOWN side: any change to a module's minScore — up or
14
+ * down — must land in the same diff as a matching RATCHET_BASELINE
15
+ * edit, making the change visible in code review (mirrors this repo's
16
+ * other shrink-only baselines, e.g. scripts/lint-unreachable-guard-drift.cjs's
17
+ * $comment convention: a number cannot move silently).
18
+ * 2. THIS script enforces the UP side: after a real CI Stryker run, if the
19
+ * module's ACHIEVED mutation score exceeds its declared floor by more
20
+ * than RATCHET_SLACK points, that is not "run-to-run variance" (this
21
+ * file's own header documents variance as 1-2 points; see
22
+ * scripts/mutation-matrix.cjs's "Floors are measured scores minus 1-2
23
+ * pts") — it is unclaimed headroom, and the floor should have been
24
+ * raised. Exits 1 telling the author exactly what to raise it to, so a
25
+ * module that improves cannot silently keep a stale, low bar forever.
26
+ *
27
+ * Consumes Stryker's `json` reporter output (mutation-testing-report-schema
28
+ * document; stryker.config.mjs's `reporters` includes `'json'`, default path
29
+ * `reports/mutation/mutation.json`) via `mutation-testing-metrics` — the same
30
+ * package Stryker itself uses internally to compute a report's score — rather
31
+ * than re-deriving killed/survived counts by hand.
32
+ *
33
+ * Wired into `.github/workflows/mutation.yml`'s `mutate` job as a step AFTER
34
+ * `npx stryker run`, gated on the shard having passed (a shard that failed
35
+ * MUTATION_BREAK is a floor problem, not a ratchet problem — Stryker's own
36
+ * exit code already reports that).
37
+ *
38
+ * CLI:
39
+ * node scripts/check-mutation-score-ratchet.cjs --module <name> [--report <path>] [--matrix <path>]
40
+ * `--report` defaults to reports/mutation/mutation.json (Stryker's own default).
41
+ * `--matrix` defaults to this repo's own scripts/mutation-matrix.cjs; it is an injectable seam
42
+ * so tests can point the CLI at a synthetic COVERED fixture instead of the real, live-ratcheting
43
+ * config (see tests/mutation-score-ratchet.test.cjs — a test asserting this script's fail/pass
44
+ * behaviour must never hardcode a real module's numeric floor, because that floor is exactly
45
+ * what this mechanism exists to move).
46
+ */
47
+
48
+ const fs = require('node:fs');
49
+ const path = require('node:path');
50
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
51
+
52
+ // Slack, in mutation-score points, above a module's declared minScore floor before this
53
+ // ratchet demands the floor be raised. Deliberately larger than the 1-2 point run-to-run
54
+ // TIMEOUT variance margin scripts/mutation-matrix.cjs's floors are already set with (its own
55
+ // header: "Floors are measured scores minus 1-2 pts for run-to-run variance") — that smaller
56
+ // margin is calibrated for the noise BETWEEN two runs of the SAME test list against the SAME
57
+ // source. RATCHET_SLACK instead has to tolerate the noise this file's own history already
58
+ // shows for the frontmatter module across genuine measurement events (63.35 -> 66.67 ->
59
+ // 60.58 -> pending, each a real CI measurement, not pure jitter, spanning >3 points on its
60
+ // own). 5 points sits comfortably above that observed band without being so wide that a
61
+ // module sitting well above TARGET_MUTATION_SCORE (80) could coast for years without its
62
+ // floor ever being asked to move.
63
+ const RATCHET_SLACK = 5;
64
+
65
+ /**
66
+ * Pure: does `achievedScore` (0-100) exceed `floor` (minScore) by more than `slack`?
67
+ * Returns `{ shouldRatchet, suggestedFloor }` — `suggestedFloor` follows the SAME formula
68
+ * scripts/mutation-matrix.cjs's own header documents for setting a floor from a measured
69
+ * score: `floor(measured) - 1`.
70
+ */
71
+ function evaluateRatchet(achievedScore, floor, slack = RATCHET_SLACK) {
72
+ const shouldRatchet = achievedScore - floor > slack;
73
+ return {
74
+ shouldRatchet,
75
+ suggestedFloor: shouldRatchet ? Math.floor(achievedScore) - 1 : floor,
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Extract the achieved mutation score from a Stryker `json` reporter document via
81
+ * `mutation-testing-metrics` (the same library Stryker itself uses to compute scores).
82
+ * Throws a descriptive error if the document has no scoreable mutants at all (an empty
83
+ * `files` map, or every file having zero mutants) — that is a wiring bug in the caller
84
+ * (wrong report path, or the shard mutated nothing), never a score of 0.
85
+ */
86
+ function extractAchievedScore(report) {
87
+ // Lazy require: this dependency is only needed on the actual score-extraction path, not
88
+ // when this module is required purely for evaluateRatchet (used by unit tests without the
89
+ // mutation-testing-metrics package's schema-validation overhead).
90
+ const { calculateMutationTestMetrics } = require('mutation-testing-metrics');
91
+ const metrics = calculateMutationTestMetrics(report);
92
+ const score = metrics && metrics.systemUnderTestMetrics && metrics.systemUnderTestMetrics.metrics
93
+ ? metrics.systemUnderTestMetrics.metrics.mutationScore
94
+ : undefined;
95
+ if (typeof score !== 'number' || Number.isNaN(score)) {
96
+ throw new Error('mutation report produced no scoreable mutants — check --report points at the right shard\'s reports/mutation/mutation.json');
97
+ }
98
+ return score;
99
+ }
100
+
101
+ function parseArgs(argv) {
102
+ const out = { module: null, report: 'reports/mutation/mutation.json', matrix: null };
103
+ for (let i = 0; i < argv.length; i++) {
104
+ const arg = argv[i];
105
+ if (arg === '--module') {
106
+ out.module = argv[++i];
107
+ } else if (arg === '--report') {
108
+ out.report = argv[++i];
109
+ } else if (arg === '--matrix') {
110
+ out.matrix = argv[++i];
111
+ } else {
112
+ throw new Error(`unknown argument: ${arg}`);
113
+ }
114
+ }
115
+ if (!out.module) throw new Error('--module <name> is required');
116
+ return out;
117
+ }
118
+
119
+ function main() {
120
+ const args = parseArgs(process.argv.slice(2));
121
+ const matrixPath = args.matrix ? path.resolve(args.matrix) : path.join(__dirname, 'mutation-matrix.cjs');
122
+ const { COVERED } = require(matrixPath);
123
+ const entry = COVERED[args.module];
124
+ if (!entry) {
125
+ throw new ExitError(1, `check-mutation-score-ratchet: unknown module '${args.module}' — not in ${args.matrix ? matrixPath : 'scripts/mutation-matrix.cjs'} COVERED`);
126
+ }
127
+
128
+ const reportPath = path.resolve(args.report);
129
+ if (!fs.existsSync(reportPath)) {
130
+ throw new ExitError(1, `check-mutation-score-ratchet: report not found at ${reportPath} — run \`npx stryker run\` first (with the 'json' reporter enabled)`);
131
+ }
132
+ let report;
133
+ try {
134
+ report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
135
+ } catch (err) {
136
+ throw new ExitError(1, `check-mutation-score-ratchet: ${reportPath} is not valid JSON: ${err.message}`);
137
+ }
138
+
139
+ const achieved = extractAchievedScore(report);
140
+ const { shouldRatchet, suggestedFloor } = evaluateRatchet(achieved, entry.minScore);
141
+
142
+ if (!shouldRatchet) {
143
+ console.log(`ok mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}% against floor ${entry.minScore} (within ${RATCHET_SLACK}-point slack)`);
144
+ return;
145
+ }
146
+
147
+ console.error(`mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}%, more than ${RATCHET_SLACK} points above its declared floor (${entry.minScore}).`);
148
+ console.error(` This is unclaimed headroom, not run-to-run variance — raise the floor.`);
149
+ console.error(` remedy: in scripts/mutation-matrix.cjs, set COVERED['${args.module}'].minScore = ${suggestedFloor} (floor(achieved) - 1, this file's own convention),`);
150
+ console.error(` and update the matching entry in tests/mutation-matrix-ratchet.test.cjs's RATCHET_BASELINE in the same diff.`);
151
+ throw new ExitError(1);
152
+ }
153
+
154
+ module.exports = { RATCHET_SLACK, evaluateRatchet, extractAchievedScore };
155
+
156
+ if (require.main === module) runMain(main);
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const { computeElapsedPct, isNearCap, formatNearCapNotice } = require('./lib/ci-job-timing.cjs');
6
+
7
+ function run(env = process.env, nowMs = Date.now(), appendFileSync = fs.appendFileSync) {
8
+ const label = env.CI_JOB_LABEL;
9
+ const startEpochMs = Number(env.CI_JOB_START_EPOCH_MS);
10
+ const timeoutMinutes = Number(env.CI_JOB_TIMEOUT_MINUTES);
11
+
12
+ if (!label || !Number.isFinite(startEpochMs) || !Number.isFinite(timeoutMinutes) || timeoutMinutes <= 0) {
13
+ console.error(
14
+ 'ci-check-job-near-cap: missing or invalid CI_JOB_LABEL / CI_JOB_START_EPOCH_MS / '
15
+ + 'CI_JOB_TIMEOUT_MINUTES — skipping near-cap check (advisory only, not a failure).',
16
+ );
17
+ return { skipped: true };
18
+ }
19
+
20
+ const { elapsedMs, capMs, pct } = computeElapsedPct({
21
+ startedAt: new Date(startEpochMs).toISOString(),
22
+ completedAt: new Date(nowMs).toISOString(),
23
+ timeoutMinutes,
24
+ });
25
+
26
+ if (!isNearCap(pct)) {
27
+ return { skipped: false, nearCap: false, pct };
28
+ }
29
+
30
+ const { warningLine, summaryMarkdown } = formatNearCapNotice({ label, pct, elapsedMs, capMs });
31
+ console.log(warningLine);
32
+
33
+ if (env.GITHUB_STEP_SUMMARY) {
34
+ try {
35
+ appendFileSync(env.GITHUB_STEP_SUMMARY, `${summaryMarkdown}\n`);
36
+ } catch (err) {
37
+ console.error(`ci-check-job-near-cap: could not write GITHUB_STEP_SUMMARY: ${err.message}`);
38
+ }
39
+ }
40
+
41
+ return { skipped: false, nearCap: true, pct };
42
+ }
43
+
44
+ module.exports = { run };
45
+
46
+ if (require.main === module) {
47
+ run();
48
+ process.exitCode = 0;
49
+ }