@opengsd/gsd-core 1.9.1 → 1.11.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 (426) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +27 -3
  5. package/agents/gsd-debug-session-manager.md +11 -0
  6. package/agents/gsd-debugger.md +12 -246
  7. package/agents/gsd-doc-synthesizer.md +2 -4
  8. package/agents/gsd-executor.md +12 -10
  9. package/agents/gsd-integration-checker.md +3 -0
  10. package/agents/gsd-mempalace-curator.md +5 -2
  11. package/agents/gsd-phase-researcher.md +20 -1
  12. package/agents/gsd-plan-checker.md +46 -0
  13. package/agents/gsd-planner.md +49 -54
  14. package/agents/gsd-roadmapper.md +21 -3
  15. package/agents/gsd-user-profiler.md +3 -0
  16. package/agents/gsd-verifier.md +26 -73
  17. package/bin/install.js +1272 -1238
  18. package/bin/lib/ui-safety-gate.cjs +2 -0
  19. package/commands/gsd/code-review.md +1 -1
  20. package/commands/gsd/execute-phase.md +1 -1
  21. package/commands/gsd/map-codebase.md +1 -1
  22. package/commands/gsd/mempalace-capture.md +2 -2
  23. package/commands/gsd/mempalace-recall.md +1 -1
  24. package/commands/gsd/new-milestone.md +2 -2
  25. package/commands/gsd/plan-phase.md +1 -1
  26. package/commands/gsd/quick.md +1 -1
  27. package/commands/gsd/review-backlog.md +2 -1
  28. package/commands/gsd/verify-work.md +1 -1
  29. package/gsd-core/bin/gsd-tools.cjs +1009 -115
  30. package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
  31. package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
  32. package/gsd-core/bin/lib/api-coverage.cjs +123 -5
  33. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  34. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  35. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  36. package/gsd-core/bin/lib/audit.cjs +926 -202
  37. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  38. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  39. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  40. package/gsd-core/bin/lib/capability-registry.cjs +608 -148
  41. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  42. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  43. package/gsd-core/bin/lib/capability-validator.cjs +507 -24
  44. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  45. package/gsd-core/bin/lib/check-command-router.cjs +114 -38
  46. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  47. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  48. package/gsd-core/bin/lib/command-aliases.cjs +94 -0
  49. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  50. package/gsd-core/bin/lib/commands.cjs +665 -99
  51. package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
  52. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  53. package/gsd-core/bin/lib/config-loader.cjs +76 -0
  54. package/gsd-core/bin/lib/config.cjs +22 -2
  55. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  56. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  57. package/gsd-core/bin/lib/core-utils.cjs +217 -40
  58. package/gsd-core/bin/lib/decisions.cjs +23 -0
  59. package/gsd-core/bin/lib/docs.cjs +3 -2
  60. package/gsd-core/bin/lib/external-job.cjs +19 -4
  61. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  62. package/gsd-core/bin/lib/frontmatter.cjs +239 -32
  63. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  64. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  65. package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
  66. package/gsd-core/bin/lib/graphify.cjs +142 -27
  67. package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
  68. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  69. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  72. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  73. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  74. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  75. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  76. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  77. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  78. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  79. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  80. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  81. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  82. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  83. package/gsd-core/bin/lib/init.cjs +1325 -169
  84. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  85. package/gsd-core/bin/lib/install-engine.cjs +805 -264
  86. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  87. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  88. package/gsd-core/bin/lib/install-profiles.cjs +160 -57
  89. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  90. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  91. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  92. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  93. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  94. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  95. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  96. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  97. package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
  98. package/gsd-core/bin/lib/io.cjs +38 -3
  99. package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
  100. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  101. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  102. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  103. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  104. package/gsd-core/bin/lib/milestone.cjs +821 -109
  105. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  106. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  107. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  108. package/gsd-core/bin/lib/pattern.cjs +122 -0
  109. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  110. package/gsd-core/bin/lib/phase-id.cjs +507 -36
  111. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  112. package/gsd-core/bin/lib/phase-locator.cjs +258 -58
  113. package/gsd-core/bin/lib/phase.cjs +891 -156
  114. package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
  115. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  116. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  117. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  118. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  119. package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
  120. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  121. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  122. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  123. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  124. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
  125. package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
  126. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  127. package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
  128. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  129. package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
  130. package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
  131. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  132. package/gsd-core/bin/lib/roadmap.cjs +405 -84
  133. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
  134. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  135. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
  136. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  137. package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
  138. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
  139. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  140. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  141. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  142. package/gsd-core/bin/lib/security.cjs +104 -5
  143. package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
  144. package/gsd-core/bin/lib/smart-entry.cjs +154 -22
  145. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  146. package/gsd-core/bin/lib/state-document.cjs +152 -8
  147. package/gsd-core/bin/lib/state-transition.cjs +424 -105
  148. package/gsd-core/bin/lib/state.cjs +1927 -401
  149. package/gsd-core/bin/lib/surface.cjs +35 -10
  150. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  151. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  152. package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
  153. package/gsd-core/bin/lib/uat.cjs +706 -64
  154. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  155. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  156. package/gsd-core/bin/lib/unusable-input.cjs +33 -0
  157. package/gsd-core/bin/lib/update-context.cjs +8 -2
  158. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  159. package/gsd-core/bin/lib/validate.cjs +20 -6
  160. package/gsd-core/bin/lib/vendor/README.md +37 -0
  161. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  162. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  163. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  164. package/gsd-core/bin/lib/verification.cjs +287 -20
  165. package/gsd-core/bin/lib/verify.cjs +368 -880
  166. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  167. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
  169. package/gsd-core/bin/lib/workstream.cjs +8 -2
  170. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  171. package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
  172. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  173. package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
  174. package/gsd-core/references/agent-contracts.md +43 -26
  175. package/gsd-core/references/artifact-types.md +10 -3
  176. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  177. package/gsd-core/references/checkpoints.md +2 -2
  178. package/gsd-core/references/context-budget.md +1 -1
  179. package/gsd-core/references/debugger-techniques.md +255 -0
  180. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  181. package/gsd-core/references/doc-conflict-engine.md +1 -1
  182. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  184. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  185. package/gsd-core/references/execute-phase-response-language.md +1 -1
  186. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  187. package/gsd-core/references/gate-prompts.md +1 -1
  188. package/gsd-core/references/git-planning-commit.md +2 -1
  189. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  190. package/gsd-core/references/model-profiles.md +12 -4
  191. package/gsd-core/references/mvp-concepts.md +9 -9
  192. package/gsd-core/references/planner-guidance.md +3 -9
  193. package/gsd-core/references/planner-preconditions.md +1 -1
  194. package/gsd-core/references/planner-reviews.md +1 -1
  195. package/gsd-core/references/planning-config.md +8 -6
  196. package/gsd-core/references/research-documentation-lookup.md +5 -3
  197. package/gsd-core/references/revision-loop.md +1 -1
  198. package/gsd-core/references/specless-probe-fallback.md +8 -7
  199. package/gsd-core/references/universal-anti-patterns.md +3 -3
  200. package/gsd-core/references/verifier-phase-gates.md +192 -0
  201. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  202. package/gsd-core/references/verify-mvp-mode.md +1 -1
  203. package/gsd-core/references/workstream-flag.md +22 -6
  204. package/gsd-core/references/worktree-branch-check.md +2 -2
  205. package/gsd-core/templates/discussion-log.md +1 -1
  206. package/gsd-core/templates/phase-prompt.md +2 -4
  207. package/gsd-core/templates/state.md +4 -4
  208. package/gsd-core/templates/summary-complex.md +2 -0
  209. package/gsd-core/templates/summary-minimal.md +2 -0
  210. package/gsd-core/templates/summary-standard.md +2 -0
  211. package/gsd-core/templates/summary.md +2 -0
  212. package/gsd-core/templates/verification-report.md +9 -1
  213. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  214. package/gsd-core/workflows/audit-milestone.md +3 -0
  215. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  216. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  217. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  218. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  219. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  220. package/gsd-core/workflows/autonomous.md +33 -70
  221. package/gsd-core/workflows/cleanup.md +62 -3
  222. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  223. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
  224. package/gsd-core/workflows/code-review-fix.md +37 -10
  225. package/gsd-core/workflows/code-review.md +74 -166
  226. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  227. package/gsd-core/workflows/complete-milestone.md +160 -95
  228. package/gsd-core/workflows/debug.md +16 -17
  229. package/gsd-core/workflows/diagnose-issues.md +56 -8
  230. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  231. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  232. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  233. package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
  234. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  235. package/gsd-core/workflows/docs-update.md +8 -51
  236. package/gsd-core/workflows/edit-phase.md +26 -1
  237. package/gsd-core/workflows/eval-review.md +3 -5
  238. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
  239. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  240. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  241. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  242. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
  243. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  245. package/gsd-core/workflows/execute-phase.md +103 -187
  246. package/gsd-core/workflows/execute-plan.md +36 -4
  247. package/gsd-core/workflows/explore.md +131 -4
  248. package/gsd-core/workflows/fast.md +10 -2
  249. package/gsd-core/workflows/health.md +73 -4
  250. package/gsd-core/workflows/help/modes/full.md +6 -1
  251. package/gsd-core/workflows/import.md +4 -4
  252. package/gsd-core/workflows/ingest-docs.md +7 -6
  253. package/gsd-core/workflows/mvp-phase.md +6 -3
  254. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  255. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  256. package/gsd-core/workflows/new-milestone.md +35 -47
  257. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  258. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  259. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  260. package/gsd-core/workflows/new-project.md +27 -240
  261. package/gsd-core/workflows/next.md +12 -0
  262. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  263. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  264. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  265. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  266. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  267. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  268. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  269. package/gsd-core/workflows/plan-phase.md +89 -209
  270. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  271. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  272. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  273. package/gsd-core/workflows/progress.md +45 -159
  274. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  275. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  276. package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
  277. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  278. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  279. package/gsd-core/workflows/quick.md +55 -405
  280. package/gsd-core/workflows/resume-project.md +3 -0
  281. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  282. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  283. package/gsd-core/workflows/review.md +41 -13
  284. package/gsd-core/workflows/section-manifest.json +219 -0
  285. package/gsd-core/workflows/secure-phase.md +1 -1
  286. package/gsd-core/workflows/session-report.md +2 -1
  287. package/gsd-core/workflows/settings.md +66 -2
  288. package/gsd-core/workflows/ship.md +104 -44
  289. package/gsd-core/workflows/sketch.md +1 -1
  290. package/gsd-core/workflows/spec-phase.md +41 -20
  291. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  292. package/gsd-core/workflows/spike.md +50 -16
  293. package/gsd-core/workflows/sync-skills.md +106 -13
  294. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  295. package/gsd-core/workflows/transition.md +53 -31
  296. package/gsd-core/workflows/ui-phase.md +13 -12
  297. package/gsd-core/workflows/ui-review.md +2 -2
  298. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  299. package/gsd-core/workflows/update.md +19 -8
  300. package/gsd-core/workflows/validate-phase.md +1 -1
  301. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  302. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  303. package/gsd-core/workflows/verify-work.md +17 -65
  304. package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
  305. package/hooks/dist/gsd-check-update-worker.js +64 -12
  306. package/hooks/dist/gsd-check-update.js +19 -1
  307. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  308. package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
  309. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  310. package/hooks/dist/gsd-prompt-guard.js +21 -20
  311. package/hooks/dist/gsd-read-injection-scanner.js +45 -24
  312. package/hooks/dist/gsd-statusline.js +90 -6
  313. package/hooks/dist/gsd-update-banner.js +22 -1
  314. package/hooks/dist/gsd-workflow-guard.js +134 -36
  315. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  316. package/hooks/dist/gsd-write-guard.js +359 -0
  317. package/hooks/dist/lib/git-cmd.js +92 -59
  318. package/hooks/dist/lib/injection-patterns.js +45 -0
  319. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  320. package/hooks/dist/lib/isolation-sentinel.js +277 -0
  321. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  322. package/hooks/gsd-agent-isolation-guard.js +517 -0
  323. package/hooks/gsd-check-update-worker.js +64 -12
  324. package/hooks/gsd-check-update.js +19 -1
  325. package/hooks/gsd-cursor-pre-tool.js +0 -3
  326. package/hooks/gsd-cursor-subagent-start.js +607 -26
  327. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  328. package/hooks/gsd-prompt-guard.js +21 -20
  329. package/hooks/gsd-read-injection-scanner.js +45 -24
  330. package/hooks/gsd-statusline.js +90 -6
  331. package/hooks/gsd-update-banner.js +22 -1
  332. package/hooks/gsd-workflow-guard.js +134 -36
  333. package/hooks/gsd-worktree-path-guard.js +2 -1
  334. package/hooks/gsd-write-guard.js +359 -0
  335. package/hooks/hooks.json +12 -0
  336. package/hooks/lib/git-cmd.js +92 -59
  337. package/hooks/lib/injection-patterns.js +45 -0
  338. package/hooks/lib/isolation-deny-reason.js +39 -0
  339. package/hooks/lib/isolation-sentinel.js +277 -0
  340. package/hooks/managed-hooks-registry.cjs +2 -0
  341. package/package.json +31 -10
  342. package/pi/gsd.cjs +71 -12
  343. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  344. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  345. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  346. package/scripts/build-hooks.js +9 -0
  347. package/scripts/changeset/lint.cjs +68 -6
  348. package/scripts/changeset/serialize.cjs +5 -1
  349. package/scripts/check-alias-drift.cjs +7 -43
  350. package/scripts/check-contract-drift.cjs +297 -0
  351. package/scripts/ci-test-scope.cjs +19 -2
  352. package/scripts/command-contract-helpers.cjs +903 -1
  353. package/scripts/gen-adr-index.cjs +728 -38
  354. package/scripts/gen-capability-matrix.cjs +1 -1
  355. package/scripts/gen-capability-registry.cjs +3 -15
  356. package/scripts/gen-context-index.cjs +439 -0
  357. package/scripts/gen-health-docs.cjs +390 -0
  358. package/scripts/gen-inventory-manifest.cjs +150 -4
  359. package/scripts/gen-loop-host-contract.cjs +4 -24
  360. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  361. package/scripts/gen-registry.cjs +3 -14
  362. package/scripts/gen-section-manifest.cjs +638 -0
  363. package/scripts/generate-package-identity.cjs +4 -2
  364. package/scripts/lib/alias-drift-families.cjs +46 -0
  365. package/scripts/lib/drift-scan.cjs +278 -0
  366. package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
  367. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  368. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  369. package/scripts/lint-canary-version-leak.cjs +73 -0
  370. package/scripts/lint-command-contract.cjs +96 -13
  371. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  372. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  373. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  374. package/scripts/lint-default-flip-documentation.cjs +193 -0
  375. package/scripts/lint-docs-command-form.cjs +195 -0
  376. package/scripts/lint-docs-required.cjs +9 -1
  377. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  378. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  379. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  380. package/scripts/lint-example-parser-parity.cjs +395 -0
  381. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  382. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  383. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  384. package/scripts/lint-milestone-window-drift.cjs +468 -0
  385. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  386. package/scripts/lint-plan-count-drift.cjs +318 -0
  387. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  388. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  389. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  390. package/scripts/lint-regression-test-names.cjs +15 -13
  391. package/scripts/lint-removed-but-needed.cjs +320 -0
  392. package/scripts/lint-state-field-drift.cjs +805 -0
  393. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  394. package/scripts/lint-test-file-count.allowlist.json +40 -3
  395. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  396. package/scripts/lint-vendored-deps.cjs +124 -0
  397. package/scripts/mutation-matrix.cjs +13 -0
  398. package/scripts/pr-changed-files.cjs +63 -0
  399. package/scripts/pr-template-policy.cjs +14 -4
  400. package/scripts/prompt-injection-scan.sh +52 -6
  401. package/scripts/require-issue-link-policy.cjs +192 -0
  402. package/scripts/state-write-path-drift-baseline.json +19 -0
  403. package/scripts/sync-runtime-launcher.cjs +2 -4
  404. package/skills/gsd-autonomous/SKILL.md +0 -1
  405. package/skills/gsd-code-review/SKILL.md +1 -1
  406. package/skills/gsd-execute-phase/SKILL.md +1 -2
  407. package/skills/gsd-map-codebase/SKILL.md +1 -1
  408. package/skills/gsd-mempalace-capture/SKILL.md +2 -2
  409. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  410. package/skills/gsd-new-milestone/SKILL.md +2 -2
  411. package/skills/gsd-next/SKILL.md +0 -1
  412. package/skills/gsd-plan-phase/SKILL.md +1 -2
  413. package/skills/gsd-progress/SKILL.md +0 -1
  414. package/skills/gsd-quick/SKILL.md +1 -1
  415. package/skills/gsd-review-backlog/SKILL.md +2 -1
  416. package/skills/gsd-stats/SKILL.md +0 -1
  417. package/skills/gsd-verify-work/SKILL.md +1 -1
  418. package/vscode/package.json +1 -1
  419. package/gsd-core/workflows/discovery-phase.md +0 -298
  420. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  421. package/gsd-core/workflows/verify-phase.md +0 -577
  422. package/scripts/affected-tests-lib.cjs +0 -554
  423. package/scripts/gen-emitted-baseline.cjs +0 -145
  424. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  425. package/scripts/run-affected-tests.cjs +0 -7
  426. package/scripts/run-tests.cjs +0 -1050
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Single source of truth for the command-alias drift surface.
5
+ *
6
+ * Two parallel surfaces consume this: `scripts/check-alias-drift.cjs`, which
7
+ * validates the built alias artifacts, and `.githooks/pre-commit`, which decides
8
+ * when to run that validation. Until #2725 the hook's watched-path list had
9
+ * drifted from the checker's family table until every pattern named either the
10
+ * retired `sdk/` tree (ADR-0174) or a gitignored build output, so the guard was
11
+ * silently inert. Both surfaces derive from this list now, and
12
+ * `tests/precommit-alias-drift-hook.test.cjs` fails if they diverge again.
13
+ */
14
+
15
+ /**
16
+ * Alias families the drift check validates. `router` is the module basename,
17
+ * shared by the `src/<router>.cts` source and the `gsd-core/bin/lib/<router>.cjs`
18
+ * artifact it compiles to.
19
+ */
20
+ const FAMILIES = Object.freeze([
21
+ { commandAliases: 'STATE_COMMAND_ALIASES', subcommands: 'STATE_SUBCOMMANDS', router: 'state-command-router' },
22
+ { commandAliases: 'VERIFY_COMMAND_ALIASES', subcommands: 'VERIFY_SUBCOMMANDS', router: 'verify-command-router' },
23
+ { commandAliases: 'INIT_COMMAND_ALIASES', subcommands: 'INIT_SUBCOMMANDS', router: 'init-command-router' },
24
+ { commandAliases: 'PHASE_COMMAND_ALIASES', subcommands: 'PHASE_SUBCOMMANDS', router: 'phase-command-router' },
25
+ { commandAliases: 'PHASES_COMMAND_ALIASES', subcommands: 'PHASES_SUBCOMMANDS', router: 'phases-command-router' },
26
+ { commandAliases: 'VALIDATE_COMMAND_ALIASES', subcommands: 'VALIDATE_SUBCOMMANDS', router: 'validate-command-router' },
27
+ { commandAliases: 'ROADMAP_COMMAND_ALIASES', subcommands: 'ROADMAP_SUBCOMMANDS', router: 'roadmap-command-router' },
28
+ { commandAliases: 'EVAL_COMMAND_ALIASES', subcommands: 'EVAL_SUBCOMMANDS', router: 'eval-command-router' },
29
+ ].map(Object.freeze));
30
+
31
+ /** Basename of the generated alias table every family is checked against. */
32
+ const ALIAS_TABLE = 'command-aliases';
33
+
34
+ /**
35
+ * Repo-relative TRACKED sources whose edit should re-run the drift check.
36
+ *
37
+ * Deliberately the `src/*.cts` sources, never the `gsd-core/bin/lib/*.cjs`
38
+ * build outputs they compile to: those are gitignored, so
39
+ * `git diff --cached --name-only` can never list them and a pre-commit guard
40
+ * keyed on them can never fire (#2725).
41
+ */
42
+ function stagedSourcePaths() {
43
+ return [`src/${ALIAS_TABLE}.cts`, ...FAMILIES.map((family) => `src/${family.router}.cts`)];
44
+ }
45
+
46
+ module.exports = { FAMILIES, ALIAS_TABLE, stagedSourcePaths };
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Shared anti-divergence scanner machinery (epic #3180, ADR-3180 Decision 4).
6
+ *
7
+ * Extracted from `scripts/lint-plan-count-drift.cjs` (#3183) so that
8
+ * `scripts/lint-milestone-window-drift.cjs` (#3184) and any future
9
+ * `scripts/lint-<derivation>-drift.cjs` guard consume ONE tree-walk /
10
+ * root-confinement / literal-tokenizer / sanitizer implementation instead of
11
+ * each copying it verbatim — the exact generative-fix-divergence class this
12
+ * epic exists to remove, now applied to the guards themselves (see
13
+ * `.gsd/phase/refactor-3184-milestone-window-single-owner/40-design.md`,
14
+ * "Rejected: let the new drift guard copy Phase 1's tree-walk /
15
+ * root-confinement / sanitizer").
16
+ *
17
+ * `lint-plan-count-drift.cjs` re-exports every symbol it exported before this
18
+ * extraction (`readRegexLiteralAt`, `MAX_REGEX_LITERAL_LEN`, `isInsideRoot`,
19
+ * `sanitizeForReport`), so `tests/plan-count-single-owner.test.cjs` — the
20
+ * ReDoS and root-confinement regression net for this exact machinery —
21
+ * continues to pass unchanged and is the regression net for this move too.
22
+ */
23
+
24
+ const fs = require('node:fs');
25
+ const path = require('node:path');
26
+
27
+ // Longest regex literal this scanner will consider, in characters. Real
28
+ // plan/summary filename filters (and milestone-window regex literals) are far
29
+ // shorter; the bound is what keeps the scan linear. The tokenizer restarts at
30
+ // every `/` on the line (so that a literal preceded by a stray unpaired `/`
31
+ // is still found, matching the previous regex's "find anywhere" behaviour),
32
+ // which without a per-literal bound would be quadratic on a pathological
33
+ // line. With it the whole-line cost is O(n * MAX_REGEX_LITERAL_LEN) with no
34
+ // backtracking at all.
35
+ const MAX_REGEX_LITERAL_LEN = 400;
36
+
37
+ // Directory names this scanner never descends into or reports out of —
38
+ // `.git` (repo internals, e.g. a persisted CI token in `.git/config`),
39
+ // `node_modules` (thousands of third-party files, none of them authored
40
+ // source), `dist` (build output). Named once and used at BOTH skip sites
41
+ // below: the cheap `entry.name` fast path in `walk`, and the resolved-path
42
+ // component check in `isUnderSkippedDir` — a symlink whose OWN name is not
43
+ // in this set but whose target resolves through a directory that IS (e.g.
44
+ // `src/g -> ../.git`, `src/nm -> ../node_modules`) must still be skipped, or
45
+ // the name-only check is a trivial bypass.
46
+ const SKIP_DIR_NAMES = new Set(['node_modules', 'dist', '.git']);
47
+
48
+ /**
49
+ * Read the JS regex literal starting at `line[start]` (which must be `/`).
50
+ * Returns `{ text, end }` — `text` includes the delimiters and any trailing
51
+ * flags, `end` is the index one past the literal — or null if no literal
52
+ * closes within MAX_REGEX_LITERAL_LEN characters.
53
+ *
54
+ * Single left-to-right pass, no backtracking. It models the two constructs a
55
+ * backtracking pattern gets wrong, which is why this is a tokenizer and not a
56
+ * regex:
57
+ * - `\x` escapes consume BOTH characters, so an escaped `\/` never
58
+ * terminates the literal;
59
+ * - inside a `[...]` character class a bare `/` does NOT terminate, so
60
+ * `/PLAN[\\/].*\.md$/` is one literal rather than two fragments. The
61
+ * previous regex silently MISSED every re-derivation using a
62
+ * cross-platform path-separator class for exactly this reason.
63
+ */
64
+ function readRegexLiteralAt(line, start) {
65
+ if (line[start] !== '/') return null;
66
+ const limit = Math.min(line.length, start + MAX_REGEX_LITERAL_LEN);
67
+ let inClass = false;
68
+ for (let i = start + 1; i < limit; i++) {
69
+ const ch = line[i];
70
+ if (ch === '\\') {
71
+ i++; // escape consumes the next character, whatever it is
72
+ continue;
73
+ }
74
+ if (ch === '\r' || ch === '\n') return null; // a literal cannot span lines
75
+ if (ch === '[') {
76
+ inClass = true;
77
+ } else if (ch === ']') {
78
+ inClass = false;
79
+ } else if (ch === '/' && !inClass) {
80
+ // Trailing flags are bounded by the SAME `limit` as the literal body
81
+ // itself (not `line.length`) — a literal followed by an unbounded run
82
+ // of lowercase letters must not make `text` grow past
83
+ // MAX_REGEX_LITERAL_LEN either.
84
+ let end = i + 1;
85
+ while (end < limit && line[end] >= 'a' && line[end] <= 'z') end++;
86
+ return { text: line.slice(start, end), end };
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+
92
+ // Symlinks report `isDirectory()`/`isFile()` as false on the Dirent from
93
+ // `readdirSync`, so a symlinked `.cts` (or a symlinked directory containing
94
+ // one) was previously invisible to this scanner — an evasion of a guard
95
+ // whose stated design principle (ADR-3180 Decision 4a) is whole-repo
96
+ // discovery with no allowlist. Resolve each entry with `fs.statSync` (which
97
+ // follows symlinks) to classify it, skipping broken links. `ctx.visitedRealDirs`
98
+ // guards against a symlink cycle sending `walk` into infinite recursion.
99
+ //
100
+ // Every sibling drift guard in `scripts/` that does NOT import this module
101
+ // (`lint-phase-id-drift.cjs`, `lint-package-identity-drift.cjs`,
102
+ // `lint-portable-timeout.cjs`, `lint-test-file-count.cjs`,
103
+ // `lint-allow-test-rule-refs.cjs`) uses the `Dirent` classification straight
104
+ // off `readdirSync` and does NOT follow symlinks at all. This scanner follows
105
+ // them so a symlinked source file cannot evade ADR-3180 Decision 4a's
106
+ // whole-repo discovery — root confinement (`isInsideRoot` below) is the price
107
+ // of doing so: without it, a symlink planted anywhere under a scan directory
108
+ // could walk this scanner out to read and report arbitrary files elsewhere on
109
+ // disk.
110
+ //
111
+ // DIRECTORY vs FILE symlinks are confined to two DIFFERENT roots, tracked as
112
+ // `ctx.scanDirRoot` (the realpath of the current top-level scan-dir entry,
113
+ // e.g. `<realRoot>/src`) vs `ctx.realRoot` (the whole repo):
114
+ // - a DIRECTORY symlink is descended ONLY if its resolved realpath is
115
+ // inside `ctx.scanDirRoot` — NOT merely inside `ctx.realRoot`. Without
116
+ // this, `src/up -> ..` (or `-> <realRoot>`) resolves inside the repo
117
+ // root and `walk` descends the ENTIRE repo, reporting violations under
118
+ // paths like `tests/not-src.cts` or `docs/other.cts` — files the caller's
119
+ // scan-dir list scopes it OUT of. This is a deliberate, fail-CLOSED
120
+ // trade-off: a directory symlink pointing elsewhere INSIDE the repo (but
121
+ // outside the scan directory) is simply not followed. The alternative —
122
+ // descending it — is exactly the whole-repo sweep this rule exists to
123
+ // prevent, and a fork PR could use that sweep to redden `lint:ci` on
124
+ // files this guard was never meant to read. The narrower rule is worth
125
+ // more than the missed edge case.
126
+ // - a FILE symlink is still scanned if its resolved realpath is inside
127
+ // `ctx.realRoot` (the whole repo, not just the scan directory) — this is
128
+ // what keeps `src/alias.cts -> vendor/real.cts` covered (test (f)): an
129
+ // aliased file genuinely is part of the compiled surface even when its
130
+ // real target lives outside `src/`, and it is still reported under its
131
+ // canonical (real) path.
132
+ //
133
+ // A resolved path is inside a root only if it IS that root or begins with
134
+ // root + separator — a plain `startsWith(root)` would also accept a sibling
135
+ // directory whose name merely starts with the root's name (`/repo-evil`).
136
+ function isInsideRoot(realPath, realRoot) {
137
+ return realPath === realRoot || realPath.startsWith(realRoot + path.sep);
138
+ }
139
+
140
+ // True when `realPath` (already confirmed inside `realRoot` by `isInsideRoot`)
141
+ // resolves THROUGH a skip-list directory anywhere along its path relative to
142
+ // the root — not just when `realPath` itself IS one. This is what closes the
143
+ // symlink bypass the `entry.name` fast path alone cannot: `walk` tests
144
+ // `entry.name` (the symlink's OWN name in its parent directory), but a
145
+ // symlink named something innocuous can still RESOLVE into `.git` /
146
+ // `node_modules` / `dist` (`src/g -> ../.git`, `src/leak.cts ->
147
+ // ../.git/config`, `src/nm -> ../node_modules`) — `isInsideRoot` alone admits
148
+ // all three, because every one of those real paths is still under the root.
149
+ function isUnderSkippedDir(realPath, realRoot) {
150
+ const rel = path.relative(realRoot, realPath);
151
+ return rel.split(path.sep).some((segment) => SKIP_DIR_NAMES.has(segment));
152
+ }
153
+
154
+ // `ctx.scanExt` is a `Set` of file extensions (e.g. `.cts`/`.ts`/`.mts`) the
155
+ // caller wants reported — threaded through `ctx` rather than as a positional
156
+ // parameter so recursive `walk(full, acc, ctx)` calls stay unchanged.
157
+ function walk(dir, acc, ctx) {
158
+ let entries;
159
+ try {
160
+ entries = fs.readdirSync(dir, { withFileTypes: true });
161
+ } catch {
162
+ return acc;
163
+ }
164
+ for (const entry of entries) {
165
+ const full = path.join(dir, entry.name);
166
+ if (SKIP_DIR_NAMES.has(entry.name)) continue; // cheap fast path
167
+ let stat;
168
+ try {
169
+ stat = entry.isSymbolicLink() ? fs.statSync(full) : entry;
170
+ } catch {
171
+ continue; // broken symlink target
172
+ }
173
+ let realPath;
174
+ try {
175
+ realPath = fs.realpathSync(full);
176
+ } catch {
177
+ continue; // broken symlink target (race, or a link stat() followed but realpath cannot)
178
+ }
179
+ if (stat.isDirectory()) {
180
+ // Directories (symlinked or real) are confined to the CURRENT scan
181
+ // directory root, not merely the repo root — see the comment above
182
+ // `isInsideRoot` for why (`src/up -> ..` whole-repo sweep).
183
+ if (!isInsideRoot(realPath, ctx.scanDirRoot)) continue;
184
+ if (isUnderSkippedDir(realPath, ctx.realRoot)) continue; // symlink resolves through a skipped dir
185
+ if (ctx.visitedRealDirs.has(realPath)) continue; // symlink cycle guard
186
+ ctx.visitedRealDirs.add(realPath);
187
+ walk(full, acc, ctx);
188
+ } else if (stat.isFile() && ctx.scanExt.has(path.extname(entry.name))) {
189
+ // Files are confined to the whole repo root — a symlinked FILE whose
190
+ // real target lives outside the scan directory but inside the repo
191
+ // (e.g. `src/alias.cts -> vendor/real.cts`) is still part of the
192
+ // compiled surface and must be scanned.
193
+ if (!isInsideRoot(realPath, ctx.realRoot)) continue;
194
+ if (isUnderSkippedDir(realPath, ctx.realRoot)) continue; // symlink resolves through a skipped dir
195
+ if (ctx.visitedRealFiles.has(realPath)) continue; // two symlinks, same real file
196
+ ctx.visitedRealFiles.add(realPath);
197
+ acc.push(realPath);
198
+ }
199
+ }
200
+ return acc;
201
+ }
202
+
203
+ /**
204
+ * Whole-repo tree-walk driver shared by every `lint-<derivation>-drift.cjs`
205
+ * guard. Resolves `root`, walks each of `scanDirs` filtered to `scanExt`
206
+ * (symlink-following, root-confined, cycle-guarded — see `walk` above), reads
207
+ * each discovered file, and calls `onFile(relPath, text)` for it — `relPath`
208
+ * is repo-relative and already the file's canonical (real) path, so a
209
+ * per-file exemption keyed on `relPath` matches consistently regardless of
210
+ * which symlink reached it.
211
+ *
212
+ * `onFile` returns an array of violation objects (or an empty array / null /
213
+ * undefined for "no violations in this file"); `scanTree` flattens them all
214
+ * into one returned array. Pure I/O orchestration — detection logic lives
215
+ * entirely in the caller's `onFile`.
216
+ */
217
+ function scanTree({ root, scanDirs, scanExt, onFile }) {
218
+ const violations = [];
219
+ let realRoot;
220
+ try {
221
+ realRoot = fs.realpathSync(root);
222
+ } catch {
223
+ return violations; // root itself does not exist / is unreadable
224
+ }
225
+ for (const dir of scanDirs) {
226
+ const scanDirPath = path.join(root, dir);
227
+ let scanDirRoot;
228
+ try {
229
+ scanDirRoot = fs.realpathSync(scanDirPath);
230
+ } catch {
231
+ continue; // scan directory itself does not exist / is unreadable
232
+ }
233
+ const ctx = { realRoot, scanDirRoot, scanExt, visitedRealDirs: new Set(), visitedRealFiles: new Set() };
234
+ for (const file of walk(scanDirPath, [], ctx)) {
235
+ const rel = path.relative(realRoot, file);
236
+ let text;
237
+ try {
238
+ text = fs.readFileSync(file, 'utf8');
239
+ } catch {
240
+ continue;
241
+ }
242
+ const found = onFile(rel, text);
243
+ if (found && found.length > 0) violations.push(...found);
244
+ }
245
+ }
246
+ return violations;
247
+ }
248
+
249
+ // A reported fragment AND a reported file path are both attacker-controlled
250
+ // source text on a fork PR (a repo can legally track a filename containing
251
+ // control bytes, so the path is exactly as attacker-controlled as the
252
+ // fragment), and both are written straight to a CI log. Replace C0/C1
253
+ // control bytes (ANSI escapes included) with a visible \xNN, AND the
254
+ // non-Latin-1 formatting/bidi/line-separator codepoints below with \uNNNN, so
255
+ // a crafted literal or filename cannot rewrite the terminal rendering of the
256
+ // report or hide/reorder its own text:
257
+ // - U+200B-U+200F: zero-width space/joiners and directional marks
258
+ // - U+2028/U+2029: Unicode LINE SEPARATOR / PARAGRAPH SEPARATOR (line
259
+ // breaks a `\n`-only log scan would not catch)
260
+ // - U+202A-U+202E: bidi embedding/override controls (RLO etc.)
261
+ // - U+2066-U+2069: bidi isolate controls
262
+ function sanitizeForReport(text) {
263
+ return text
264
+ // eslint-disable-next-line no-control-regex -- the control range IS the target
265
+ .replace(/[\x00-\x1f\x7f-\x9f]/g, (c) => '\\x' + c.charCodeAt(0).toString(16).padStart(2, '0'))
266
+ .replace(/[\u200B-\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]/g, (c) => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
267
+ }
268
+
269
+ module.exports = {
270
+ SKIP_DIR_NAMES,
271
+ isInsideRoot,
272
+ isUnderSkippedDir,
273
+ walk,
274
+ readRegexLiteralAt,
275
+ MAX_REGEX_LITERAL_LEN,
276
+ sanitizeForReport,
277
+ scanTree,
278
+ };
@@ -1,8 +1,7 @@
1
1
  [
2
- "tests/agent-classification-parity.test.cjs :: runtime-contract-is-the-product — docs/AGENTS.md section layout + docs/INVENTORY.md table ARE the classification surface being validated",
2
+ "tests/agent-classification-parity.test.cjs :: source-text-is-the-product",
3
3
  "tests/agent-frontmatter.test.cjs :: source-text-is-the-product",
4
4
  "tests/agent-required-reading-consistency.test.cjs :: source-text-is-the-product",
5
- "tests/agent-size-budget.test.cjs :: source-text-is-the-product",
6
5
  "tests/agent-skills-awareness.test.cjs :: source-text-is-the-product",
7
6
  "tests/ai-evals.test.cjs :: source-text-is-the-product",
8
7
  "tests/analyze-dependencies.test.cjs :: source-text-is-the-product",
@@ -15,25 +14,19 @@
15
14
  "tests/autonomous-interactive.test.cjs :: source-text-is-the-product",
16
15
  "tests/autonomous-to-flag.test.cjs :: source-text-is-the-product",
17
16
  "tests/chain-flag-plan-phase.test.cjs :: source-text-is-the-product",
18
- "tests/changeset-cli.test.cjs :: reads a product workflow .md file (not CJS source) to verify",
19
- "tests/check-update-config-dir.test.cjs :: structural-regression-guard",
20
- "tests/ci-test-scope.test.cjs :: CLI usage banner presence is a user-facing contract.",
21
- "tests/ci-test-scope.test.cjs :: CLI usage failure text is user-facing contract for this parser guard.",
17
+ "tests/changeset-cli.test.cjs :: source-text-is-the-product",
22
18
  "tests/claude-md.test.cjs :: source-text-is-the-product",
23
- "tests/claude-skills-migration.test.cjs :: source-text-is-the-product",
24
19
  "tests/cleanup-branch-pruning.test.cjs :: source-text-is-the-product",
25
20
  "tests/cline-install.test.cjs :: source-text-is-the-product",
26
21
  "tests/cline-support.test.cjs :: source-text-is-the-product",
27
- "tests/clock-seam.test.cjs :: line 159 reads the STATE.md temp file written by readModifyWriteStateMd — this is a runtime output file assertion, not a source-grep; the API returns void so a file read-back is the only way to verify the transform was applied",
28
22
  "tests/code-review-agent-skills.test.cjs :: source-text-is-the-product",
29
23
  "tests/code-review-command.test.cjs :: source-text-is-the-product",
30
24
  "tests/code-review-pipeline-regression.test.cjs :: source-text-is-the-product",
31
25
  "tests/code-review.test.cjs :: source-text-is-the-product",
32
26
  "tests/codebuddy-install.test.cjs :: source-text-is-the-product",
33
27
  "tests/codex-config.test.cjs :: source-text-is-the-product",
34
- "tests/command-contract.test.cjs :: source-text-is-the-product — commands/gsd/*.md files ARE the",
28
+ "tests/command-contract.test.cjs :: source-text-is-the-product",
35
29
  "tests/commands.test.cjs :: source-text-is-the-product",
36
- "tests/concurrency-safety.test.cjs :: source-text-is-the-product",
37
30
  "tests/config-field-docs.test.cjs :: docs-parity",
38
31
  "tests/context-enrichment.test.cjs :: source-text-is-the-product",
39
32
  "tests/contributor-standards.test.cjs :: source-text-is-the-product",
@@ -41,24 +34,16 @@
41
34
  "tests/cursor-hooks.test.cjs :: source-text-is-the-product",
42
35
  "tests/cursor-reviewer.test.cjs :: source-text-is-the-product",
43
36
  "tests/debug-session-management.test.cjs :: source-text-is-the-product",
44
- "tests/discuss-all-flag.test.cjs :: source-text-is-the-product",
45
37
  "tests/discuss-checkpoint.test.cjs :: source-text-is-the-product",
46
- "tests/discuss-mode.test.cjs :: structural-implementation-guard",
47
38
  "tests/discuss-phase-power.test.cjs :: source-text-is-the-product",
48
39
  "tests/docs-parity-live-registry.test.cjs :: source-text-is-the-product",
49
40
  "tests/drift-detection.test.cjs :: source-text-is-the-product",
50
- "tests/edge-probe-docs-fixtures.test.cjs :: runtime-contract-is-the-product — the rendered reference/SPEC/ADR vocab surfaces are the runtime contract; this pins their bijection to the code (docs-parity)",
51
- "tests/edge-probe-planner-contract.test.cjs :: runtime-contract-is-the-product — plan-phase.md's planner prompt is the deployed runtime contract under assertion",
52
- "tests/edge-probe-spec-phase-contract.test.cjs :: runtime-contract-is-the-product — spec-phase.md Step 5.5 is the deployed workflow runtime contract under assertion",
41
+ "tests/edge-probe-docs-fixtures.test.cjs :: source-text-is-the-product",
42
+ "tests/edge-probe-planner-contract.test.cjs :: source-text-is-the-product",
43
+ "tests/edge-probe-spec-phase-contract.test.cjs :: source-text-is-the-product",
53
44
  "tests/edit-phase.test.cjs :: source-text-is-the-product",
54
- "tests/eslint-rules.test.cjs :: <source-grep reason> must still error",
55
- "tests/eslint-rules.test.cjs :: pending migration",
56
- "tests/eslint-rules.test.cjs :: source-text-is-the-product",
57
45
  "tests/execute-phase-active-flags.test.cjs :: source-text-is-the-product",
58
46
  "tests/execute-phase-step-5-5-deviation-doc.test.cjs :: source-text-is-the-product",
59
- "tests/execute-phase-wave.test.cjs :: behavioral — calls gsd-tools and asserts structured output",
60
- "tests/execute-phase-wave.test.cjs :: behavioral — exercises config-set validation, not source text",
61
- "tests/execute-phase-wave.test.cjs :: behavioral — exercises gsd-tools wave-defaulting logic",
62
47
  "tests/execute-phase-wave.test.cjs :: source-text-is-the-product",
63
48
  "tests/execute-phase-worktree-artifacts.test.cjs :: source-text-is-the-product",
64
49
  "tests/explore-command.test.cjs :: source-text-is-the-product",
@@ -66,35 +51,21 @@
66
51
  "tests/forensics.test.cjs :: source-text-is-the-product",
67
52
  "tests/frontmatter-cli.test.cjs :: source-text-is-the-product",
68
53
  "tests/gates-taxonomy.test.cjs :: source-text-is-the-product",
69
- "tests/git-base-branch.test.cjs :: runtime-contract-is-the-product",
70
- "tests/git-base-branch.test.cjs :: runtime-contract-is-the-product — the workflow .md content IS",
71
- "tests/gsd-check-update-worker-platform-gate.test.cjs :: structural assertion on spawn-options shape; the behavior",
54
+ "tests/git-base-branch.test.cjs :: source-text-is-the-product",
55
+ "tests/gsd-check-update-worker-platform-gate.test.cjs :: structural-regression-guard",
72
56
  "tests/gsd-researcher-app-aware.test.cjs :: source-text-is-the-product",
73
57
  "tests/gsd-researcher-flow-diagram.test.cjs :: source-text-is-the-product",
74
58
  "tests/gsd-settings-advanced.test.cjs :: source-text-is-the-product",
75
- "tests/gsd2-import.test.cjs :: source-text-is-the-product",
59
+ "tests/helpers/live-command-registry.cjs :: source-text-is-the-product",
76
60
  "tests/hermes-skills-migration.test.cjs :: source-text-is-the-product",
77
61
  "tests/import-command.test.cjs :: source-text-is-the-product",
78
62
  "tests/ingest-docs.test.cjs :: source-text-is-the-product",
79
63
  "tests/inline-plan-threshold.test.cjs :: source-text-is-the-product",
80
- "tests/install-minimal-hooks.test.cjs :: source-text-is-the-product",
81
64
  "tests/install-nested-layout.test.cjs :: source-text-is-the-product",
82
65
  "tests/install-runtime-artifacts.test.cjs :: source-text-is-the-product",
83
- "tests/install.test.cjs :: runtime-contract-is-the-product",
84
- "tests/install.test.cjs :: source-text-is-the-product",
85
- "tests/intel.test.cjs :: source-text-is-the-product — agents/gsd-intel-updater.md IS the",
86
- "tests/intel.test.cjs :: source-text-is-the-product — readFileSync assertions target API-SURFACE.md, which is the generated product of intelApiSurface; asserting on its text content is the only way to verify correct generation.",
87
- "tests/inventory-headings-countfree.test.cjs :: runtime-contract-is-the-product — INVENTORY.md heading format is the shipped doc surface being locked",
66
+ "tests/intel.test.cjs :: source-text-is-the-product",
67
+ "tests/inventory-headings-countfree.test.cjs :: source-text-is-the-product",
88
68
  "tests/ios-scaffold-safety.test.cjs :: source-text-is-the-product",
89
- "tests/issue-2639-codex-toml-neutralization.test.cjs :: source-text-is-the-product",
90
- "tests/issue-429-comment-text-gate.test.cjs :: source-text-is-the-product",
91
- "tests/issue-498-update-backup-runtime-dir.test.cjs :: structural assertion on the deployed update.md backup bash;",
92
- "tests/issue-57-runtime-install-no-drift.test.cjs :: delegation-presence guard. Catches wholesale removal of the registry",
93
- "tests/issue-57-runtime-install-no-drift.test.cjs :: structural guard over bin/install.js source. Behavioral assertions",
94
- "tests/issue-607-installer-dry-run.install.test.cjs :: integration-test-input",
95
- "tests/issue-607-legacy-cleanup.test.cjs :: integration-test-input",
96
- "tests/issue-787-cline-hooks-agents.test.cjs :: source-text-is-the-product",
97
- "tests/issue-815-update-next-channel.test.cjs :: reads product workflow/command markdown to verify the --next RC channel contract — not a source-grep test",
98
69
  "tests/locking-bugs-1909-1916-1925-1927.test.cjs :: architectural-invariant",
99
70
  "tests/mcp-tool-inheritance.test.cjs :: source-text-is-the-product",
100
71
  "tests/milestone-summary.test.cjs :: source-text-is-the-product",
@@ -103,15 +74,11 @@
103
74
  "tests/next-safety-gates.test.cjs :: source-text-is-the-product",
104
75
  "tests/next-up-clear-order.test.cjs :: source-text-is-the-product",
105
76
  "tests/no-hardcoded-home-gsd-tools.test.cjs :: source-text-is-the-product",
106
- "tests/opencode-permissions.test.cjs :: architectural-invariant",
107
- "tests/orphan-worktree-detection.test.cjs :: architectural-invariant",
108
77
  "tests/orphaned-hooks.test.cjs :: structural-regression-guard",
109
78
  "tests/package-legitimacy-gate.test.cjs :: source-text-is-the-product",
110
79
  "tests/parallel-dependent-plans.test.cjs :: source-text-is-the-product",
111
80
  "tests/path-replacement.test.cjs :: source-text-is-the-product",
112
- "tests/phase-dependency-levels.test.cjs :: source-text-is-the-product",
113
81
  "tests/phase.test.cjs :: source-text-is-the-product",
114
- "tests/phase.test.cjs :: state-md-is-the-runtime-contract — regression tests for",
115
82
  "tests/phase6-capability-docs.test.cjs :: source-text-is-the-product",
116
83
  "tests/phase6-capstone-conformance.test.cjs :: source-text-is-the-product",
117
84
  "tests/phase6-planning-capabilities.test.cjs :: source-text-is-the-product",
@@ -127,9 +94,7 @@
127
94
  "tests/product-name-purity.test.cjs :: source-text-is-the-product",
128
95
  "tests/profile-output.test.cjs :: source-text-is-the-product",
129
96
  "tests/progress-forensic.test.cjs :: source-text-is-the-product",
130
- "tests/prompt-budget-cli.test.cjs :: prompt-content-is-the-product",
131
97
  "tests/prompt-thinning.test.cjs :: source-text-is-the-product",
132
- "tests/quick-session-management.test.cjs :: source-text-is-the-product",
133
98
  "tests/qwen-skills-migration.test.cjs :: source-text-is-the-product",
134
99
  "tests/read-guard.test.cjs :: source-text-is-the-product",
135
100
  "tests/reapply-patches.test.cjs :: source-text-is-the-product",
@@ -137,12 +102,10 @@
137
102
  "tests/release-coverage-scope.test.cjs :: source-text-is-the-product",
138
103
  "tests/release-tarball-smoke-workflow.test.cjs :: source-text-is-the-product",
139
104
  "tests/release-tarball-smoke.install.test.cjs :: integration-test-input",
140
- "tests/research-agent-profiles.test.cjs :: <runtime-contract-is-the-product> research agent .md content is the governed surface",
141
- "tests/review-default-reviewers-workflow.test.cjs :: source-text-is-the-product",
105
+ "tests/research-agent-profiles.test.cjs :: source-text-is-the-product",
142
106
  "tests/roadmap.test.cjs :: source-text-is-the-product",
143
- "tests/run-tests-harness.test.cjs :: run-tests.cjs is a CLI test harness whose only IR is its",
144
- "tests/runtime-launcher-parity.test.cjs :: structural parity/drift guard — asserts literal presence/absence of the canonical gsd_run launcher and the retired $GSD_SDK / `/gsd-tools` tokens across workflow markdown; there is no typed IR for \"this source file does not contain substring X\".",
145
- "tests/runtime-name-policy.test.cjs :: runtime-contract-is-the-product — FALLBACK_ALIASES source text IS the",
107
+ "tests/runtime-launcher-parity.test.cjs :: structural-regression-guard",
108
+ "tests/runtime-name-policy.test.cjs :: source-text-is-the-product",
146
109
  "tests/scan-command.test.cjs :: source-text-is-the-product",
147
110
  "tests/secret-scan-lint.security.test.cjs :: source-text-is-the-product",
148
111
  "tests/secure-phase.test.cjs :: source-text-is-the-product",
@@ -153,12 +116,10 @@
153
116
  "tests/settings-jsonc.test.cjs :: structural-regression-guard",
154
117
  "tests/skill-frontmatter-contract.test.cjs :: source-text-is-the-product",
155
118
  "tests/spawn-liveness-banner.test.cjs :: source-text-is-the-product",
156
- "tests/state-acquirestatelock-non-eexist.test.cjs :: architectural-invariant",
157
119
  "tests/state.test.cjs :: source-text-is-the-product",
158
120
  "tests/subagent-timeout.test.cjs :: source-text-is-the-product",
159
121
  "tests/template.test.cjs :: source-text-is-the-product",
160
122
  "tests/thinking-partner.test.cjs :: source-text-is-the-product",
161
- "tests/thread-session-management.test.cjs :: source-text-is-the-product",
162
123
  "tests/ultraplan-phase.test.cjs :: source-text-is-the-product",
163
124
  "tests/verify-health.test.cjs :: source-text-is-the-product",
164
125
  "tests/verify-test-quality.test.cjs :: source-text-is-the-product",
@@ -167,7 +128,7 @@
167
128
  "tests/workflow-compat.test.cjs :: source-text-is-the-product",
168
129
  "tests/workflow-guard-registration.test.cjs :: structural-regression-guard",
169
130
  "tests/workflow-maintainer-skip.test.cjs :: source-text-is-the-product",
170
- "tests/workflow-shell-pinning.test.cjs :: file-scope prefilter, not a test assertion — we need to",
131
+ "tests/workflow-shell-pinning.test.cjs :: source-text-is-the-product",
171
132
  "tests/workflow-size-budget.test.cjs :: source-text-is-the-product",
172
133
  "tests/workspace.test.cjs :: source-text-is-the-product",
173
134
  "tests/worktree-cleanup.test.cjs :: source-text-is-the-product",
@@ -0,0 +1,4 @@
1
+ {
2
+ "maxSites": 10,
3
+ "grace": 2
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "maxFiles": 280
3
+ }
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Canary-version-leak lint (DEFECT.CANARY-VERSION-LEAK, CONTEXT.md).
6
+ *
7
+ * `package.json` `.version` on `main` must never carry a `-canary.<N>`
8
+ * suffix — that suffix is a dev-branch/prerelease marker. Nothing published
9
+ * depends on the string at runtime, but every consumer of the version
10
+ * metadata (release flow, install banners, statusline) surfaces the
11
+ * dev-channel label as if it were the shipped release. The 2026-05-16 audit
12
+ * found `origin/main` at `"version": "1.50.0-canary.0"`, landed by a fix PR
13
+ * that accidentally carried a version bump from a dev-branch base (commit
14
+ * 2d32ad82, #3206).
15
+ *
16
+ * Modeled on scripts/lint-package-identity-drift.cjs / scripts/lint-table-schema-drift.cjs:
17
+ * a standalone node script (not a node:test), exit 0 clean / exit 1 + message
18
+ * on a leaked canary version. Wired to run only for PRs targeting `main`
19
+ * (.github/workflows/version-gate.yml) — a canary version is expected and
20
+ * harmless on every other branch.
21
+ */
22
+
23
+ const fs = require('node:fs');
24
+ const path = require('node:path');
25
+
26
+ const CANARY_RE = /-canary\.\d+/;
27
+
28
+ /**
29
+ * Pure: does this version string carry a `-canary.<N>` suffix?
30
+ * @param {string} version
31
+ * @returns {boolean}
32
+ */
33
+ function isCanaryVersion(version) {
34
+ return typeof version === 'string' && CANARY_RE.test(version);
35
+ }
36
+
37
+ /**
38
+ * Read `<root>/package.json` and return its `.version`, or null if the file
39
+ * is missing/unreadable/unparsable.
40
+ * @param {string} root
41
+ * @returns {string|null}
42
+ */
43
+ function readPackageVersion(root) {
44
+ try {
45
+ const raw = fs.readFileSync(path.join(root, 'package.json'), 'utf8');
46
+ const pkg = JSON.parse(raw);
47
+ return typeof pkg.version === 'string' ? pkg.version : null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function main() {
54
+ const root = path.join(__dirname, '..');
55
+ const version = readPackageVersion(root);
56
+ if (version == null) {
57
+ process.stderr.write('canary-version-leak: could not read/parse package.json .version\n');
58
+ process.exitCode = 1;
59
+ return;
60
+ }
61
+ if (!isCanaryVersion(version)) {
62
+ process.stdout.write(`ok canary-version-leak: package.json version '${version}' carries no -canary.<N> suffix\n`);
63
+ return;
64
+ }
65
+ process.stderr.write(`canary-version-leak: package.json version '${version}' carries a -canary.<N> suffix (DEFECT.CANARY-VERSION-LEAK).\n`);
66
+ process.stderr.write('A -canary.<N> version must never land on main. Reset .version to the canonical\n');
67
+ process.stderr.write('pre-canary stable before merging — see CONTEXT.md DEFECT.CANARY-VERSION-LEAK.\n');
68
+ process.exitCode = 1;
69
+ }
70
+
71
+ if (require.main === module) main();
72
+
73
+ module.exports = { isCanaryVersion, readPackageVersion, CANARY_RE };