@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,277 @@
1
+ 'use strict';
2
+ // hooks/lib/isolation-sentinel.js — shared sentinel reader for the #3045
3
+ // agent-dispatch isolation guards (hooks/gsd-agent-isolation-guard.js,
4
+ // hooks/gsd-cursor-subagent-start.js).
5
+ //
6
+ // #3045 BLOCKER: the guards previously keyed enforcement on the capability
7
+ // REGISTRY's `dispatch.isolation` ("this host CAN isolate"), not the
8
+ // workflow's resolved per-dispatch ISOLATION ("this dispatch SHOULD be
9
+ // isolated"). Sequential ISOLATION=none legitimately happens on a
10
+ // harness-worktree-capable host — project-level `workflow.use_worktrees:
11
+ // false`, the #2474 per-plan submodule degrade, and the #683/#3060
12
+ // base-check auto-degrade all resolve to `none` and are NOT bugs
13
+ // (gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md:
14
+ // "Sequential mode … Omit isolation=\"worktree\" from the Agent call").
15
+ //
16
+ // The workflow already computes ISOLATION deterministically in shell before
17
+ // any executor dispatch. CORE REDESIGN (two-review follow-up): the PRIMARY
18
+ // write path is now `dispatch-isolation` itself (gsd-tools.cjs
19
+ // routeDispatchIsolation) — it persists mode + harnessFlag + phase/plan
20
+ // identifiers to the sentinel as an unconditional side effect of resolving
21
+ // them, since the workflow must call it to learn ISOLATION at all.
22
+ // `record-dispatch-isolation` (routeRecordDispatchIsolation) remains as an
23
+ // explicit fallback for the per-plan submodule-degrade override and shares
24
+ // the exact same atomic-write implementation.
25
+ //
26
+ // Sentinel path: `<cwd>/.gsd/dispatch-isolation-sentinel.json`. `.gsd` is
27
+ // gitignored (root `.gitignore`'s bare `.gsd` entry matches at any depth),
28
+ // and lives inside the checkout that ran the workflow — concurrent worktrees
29
+ // of the same repo are separate directories on disk, so each gets its own
30
+ // sentinel with no cross-worktree collision, no lock file needed.
31
+ //
32
+ // #3045 SECURITY F3 (LOW, accepted risk — documented, not redesigned for):
33
+ // the sentinel lives at a fixed, predictable, gitignored path inside the
34
+ // PRIMARY checkout. An agent already running inside a legitimately isolated
35
+ // worktree (i.e. one that has shell/filesystem access to the primary
36
+ // checkout's absolute path, which every worktree-isolated agent does by
37
+ // construction) could write a fabricated fresh `{isolation:"none"}` sentinel
38
+ // into the primary checkout to silence this guard for that checkout's next
39
+ // dispatch.
40
+ // - This grants NO NEW write privilege: that same agent could already edit
41
+ // any file in the primary checkout directly (worktree isolation protects
42
+ // the primary checkout from an UNTRUSTED executor's own dispatch, not
43
+ // from a trusted orchestrator process choosing to reach across).
44
+ // - It DOES add an evasion path: silencing the guard via the sentinel
45
+ // leaves no trace in `git status` (`.gsd/` is gitignored), where a direct
46
+ // edit to a tracked file would.
47
+ // Accepted because the threat model this guard defends against is an
48
+ // UNCONSENTED, UNVERIFIED dispatch — not a deliberately adversarial
49
+ // orchestrator process, which already has strictly more direct means to
50
+ // cause harm than forging this one file. If that threat model changes (e.g.
51
+ // executors become mutually distrusting / sandboxed from the orchestrator's
52
+ // own filesystem), the hardening path is a SESSION-KEYED sentinel written
53
+ // outside any worktree the executor can reach (e.g. under the harness's own
54
+ // config dir, keyed by a session/run id neither the executor nor a forged
55
+ // file can predict) rather than a path derivable from `cwd`.
56
+
57
+ const fs = require('fs');
58
+ const path = require('path');
59
+
60
+ // Isolation modes ADR-1239 declares (mirrors gsd-tools.cjs
61
+ // routeDispatchIsolation / routeRecordDispatchIsolation).
62
+ const VALID_ISOLATION = new Set(['harness-worktree', 'orchestrator-worktree', 'none']);
63
+
64
+ const SENTINEL_RELATIVE_PATH = path.join('.gsd', 'dispatch-isolation-sentinel.json');
65
+
66
+ // #3045 SECURITY F2 fix: how long a written sentinel is trusted as "this
67
+ // dispatch's decision" before a reader falls back to the conservative
68
+ // registry+config check.
69
+ //
70
+ // Previously 4h, on the theory that a slow multi-wave phase execution could
71
+ // span well over an hour. That reasoning no longer holds: the #3045 CORE
72
+ // REDESIGN makes `dispatch-isolation` (gsd-tools.cjs routeDispatchIsolation)
73
+ // the sole write path, called as a side effect of resolving ISOLATION — and
74
+ // the workflow now re-resolves (and therefore re-records) immediately before
75
+ // EVERY plan's dispatch, at the per-plan worktree gate
76
+ // (execute-phase/steps/per-plan-worktree-gate.md), not once per phase. A long
77
+ // trust window no longer buys the workflow anything and only widens the
78
+ // window in which a stale sentinel from an EARLIER, DIFFERENT phase/plan
79
+ // (e.g. one that legitimately degraded to `none`) could be misread as
80
+ // authorizing a LATER dispatch that never got its own fresh record (a model
81
+ // skipping the kwarg on a harness-worktree phase while a same-session
82
+ // same-project stale `none` from a prior phase is still "fresh" by the old
83
+ // 4h window).
84
+ //
85
+ // 10 minutes generously covers the real latency between a per-plan gate's
86
+ // resolve call and that same plan's `Agent()`/`Task()` dispatch (worktree
87
+ // creation, orphan-worktree sweep, base-check, prompt composition) — all
88
+ // bounded, sub-minute operations per their own repo-mandated subprocess
89
+ // timeouts — while being far too short for a sentinel to survive into a
90
+ // later, unrelated phase.
91
+ const SENTINEL_STALE_MS = 10 * 60 * 1000; // 10 minutes
92
+
93
+ function sentinelPath(cwd) {
94
+ return path.join(cwd, SENTINEL_RELATIVE_PATH);
95
+ }
96
+
97
+ /**
98
+ * Resolve the project root a sentinel should be read from/written to, using
99
+ * the SAME derivation gsd-tools.cjs's dispatcher applies to every `--cwd`
100
+ * before invoking a route handler: `findProjectRoot(resolveMainWorktreeCwd(cwd))`
101
+ * (gsd-core/bin/gsd-tools.cjs main(), :3506/:3603 — `record-dispatch-isolation`
102
+ * and `dispatch-isolation` are not in SKIP_ROOT_RESOLUTION, so every write
103
+ * goes through both steps).
104
+ *
105
+ * #3045 MINOR fix: the guard hooks previously read the sentinel from the raw
106
+ * `data.cwd` / `workspace_roots[i]` the harness reports, with NO equivalent
107
+ * resolution. For a linked worktree that does not itself own a `.planning/`
108
+ * (the common shape — `.planning/` lives in the main worktree only), the
109
+ * writer resolves up to the MAIN worktree and writes there, while the reader
110
+ * checked `.planning/config.json` at the raw (unresolved) linked-worktree
111
+ * path, found nothing, and silently treated the dispatch as "not a GSD
112
+ * project" (inert allow) — the guard was reading a sentinel that was never
113
+ * written where it looked. Deriving both sides through this one function
114
+ * closes that divergence.
115
+ *
116
+ * `findProjectRoot`/`resolveWorktreeRoot` are read from the sibling
117
+ * `gsd-core/bin/lib/*.cjs` modules staged alongside these hooks at install
118
+ * time (same pattern the guard hooks already use for
119
+ * capability-registry.cjs/runtime-name-policy.cjs) — two directories up from
120
+ * `hooks/lib/` (`hooks/lib/isolation-sentinel.js` -> `hooks/` -> repo/install
121
+ * root -> `gsd-core/bin/lib/`), mirroring the one-directory-up requires the
122
+ * top-level `hooks/*.js` guard scripts already use successfully.
123
+ *
124
+ * Never throws; any resolution failure (module missing, git unavailable,
125
+ * git timeout) degrades to the raw `cwd` unchanged — the caller's existing
126
+ * "sentinel absent -> conservative fallback" path already covers that safely.
127
+ */
128
+ function resolveSentinelRoot(cwd) {
129
+ try {
130
+ if (fs.existsSync(path.join(cwd, '.planning'))) {
131
+ return cwd;
132
+ }
133
+ // #3582: worktree-safety.cjs / project-root.cjs are tsc build artifacts
134
+ // (ADR-457), gitignored and absent on a raw plugin-marketplace / git-clone
135
+ // install that never ran `npm run build:lib`. Self-heal before either
136
+ // require below; a RuntimeBuildError (or any other failure) falls through
137
+ // to the existing catch's degrade-to-raw-`cwd` — unchanged behavior, just
138
+ // now attempted-healed-first rather than silently degrading on the first
139
+ // cold-tree encounter.
140
+ const { ensureRuntimeBuild } = require('../../gsd-core/bin/ensure-runtime-build.cjs');
141
+ ensureRuntimeBuild();
142
+ const { resolveWorktreeRoot } = require('../../gsd-core/bin/lib/worktree-safety.cjs');
143
+ const { root } = resolveWorktreeRoot(cwd);
144
+ const { findProjectRoot } = require('../../gsd-core/bin/lib/project-root.cjs');
145
+ return findProjectRoot(root);
146
+ } catch {
147
+ return cwd;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Read and validate the dispatch-isolation sentinel for `cwd`. Never throws.
153
+ * `cwd` is resolved through `resolveSentinelRoot` first (#3045 MINOR — see
154
+ * its doc comment), so callers may pass the raw, unresolved dispatch cwd
155
+ * directly.
156
+ *
157
+ * Returns one of:
158
+ * { present: false }
159
+ * { present: true, stale: true, malformed: true }
160
+ * { present: true, stale: true, malformed: false, isolation, harnessFlag, phase, plan, writtenAt }
161
+ * { present: true, stale: false, malformed: false, isolation, harnessFlag, phase, plan, writtenAt }
162
+ *
163
+ * A malformed/unparseable sentinel is treated as STALE, never fatal — the
164
+ * caller's conservative fallback path covers both "absent" and "stale"
165
+ * identically.
166
+ *
167
+ * `clock` is injectable (`{ now(): number }`, defaults to the real `Date`)
168
+ * per the repo's clock-seam convention, so staleness is testable without
169
+ * asserting on wall-clock time.
170
+ */
171
+ function readSentinel(cwd, { clock = Date } = {}) {
172
+ const root = resolveSentinelRoot(cwd);
173
+ let raw;
174
+ try {
175
+ raw = fs.readFileSync(sentinelPath(root), 'utf-8');
176
+ } catch {
177
+ return { present: false };
178
+ }
179
+
180
+ let parsed;
181
+ try {
182
+ parsed = JSON.parse(raw);
183
+ } catch {
184
+ return { present: true, stale: true, malformed: true };
185
+ }
186
+
187
+ if (
188
+ !parsed || typeof parsed !== 'object' ||
189
+ !VALID_ISOLATION.has(parsed.isolation) ||
190
+ typeof parsed.written_at !== 'number' || !Number.isFinite(parsed.written_at)
191
+ ) {
192
+ return { present: true, stale: true, malformed: true };
193
+ }
194
+
195
+ const harnessFlag = typeof parsed.harness_flag === 'string' && parsed.harness_flag.length > 0
196
+ ? parsed.harness_flag
197
+ : null;
198
+ const phase = typeof parsed.phase === 'string' && parsed.phase.length > 0 ? parsed.phase : null;
199
+ // #3045 SECURITY F2: `plan` was not previously part of the sentinel shape.
200
+ // Recorded so a phase-level-only sentinel (plan: null) is distinguishable
201
+ // from a plan-scoped one — see the guards' dispatch-matching logic, which
202
+ // treats a plan/phase MISMATCH (both sides present and disagreeing) as "no
203
+ // applicable sentinel", not an allow.
204
+ const plan = typeof parsed.plan === 'string' && parsed.plan.length > 0 ? parsed.plan : null;
205
+
206
+ const now = clock.now();
207
+ const age = now - parsed.written_at;
208
+ // Negative age beyond a small tolerance means the sentinel claims to be
209
+ // written in the future — never trust it, but still surface the parsed
210
+ // fields so callers can log an actionable reason.
211
+ const stale = age >= SENTINEL_STALE_MS || age < -5000;
212
+
213
+ return {
214
+ present: true,
215
+ stale,
216
+ malformed: false,
217
+ isolation: parsed.isolation,
218
+ harnessFlag,
219
+ phase,
220
+ plan,
221
+ writtenAt: parsed.written_at,
222
+ };
223
+ }
224
+
225
+ /**
226
+ * #3045 SECURITY F2: extract the `{plan, phase}` a specific Agent()/Task()
227
+ * dispatch is FOR, from the one place that data is reliably embedded today —
228
+ * the dispatch prompt/description text (`execute-phase.md`'s Agent() block
229
+ * uses the literal shape `description="Execute plan {plan_number} of phase
230
+ * {phase_number}"`, and the prompt body's `<objective>` repeats "Execute plan
231
+ * {plan_number} of phase {phase_number}-{phase_name}." verbatim — the SAME
232
+ * text the orchestrator-worktree EXECUTOR_PROMPT template and Cursor's `task`
233
+ * field carry, since Cursor dispatches the same prompt content). There is no
234
+ * structured per-dispatch kwarg carrying plan/phase identifiers today (#3045
235
+ * would need a larger dispatch-protocol change to add one) — this is
236
+ * therefore a best-effort, NOT a guaranteed, extraction: a dispatch whose
237
+ * text doesn't match the expected shape returns `{ plan: null, phase: null }`
238
+ * and the caller must NOT treat that as a mismatch (see
239
+ * `sentinelAppliesToDispatch`).
240
+ */
241
+ function extractDispatchIdentifiers(text) {
242
+ if (typeof text !== 'string' || text.length === 0) return { plan: null, phase: null };
243
+ const m = /execute\s+plan\s+(\S+)\s+of\s+phase\s+(\S+)/i.exec(text);
244
+ if (!m) return { plan: null, phase: null };
245
+ return { plan: m[1], phase: m[2] };
246
+ }
247
+
248
+ /**
249
+ * #3045 SECURITY F2: does a fresh, non-malformed sentinel apply to THIS
250
+ * dispatch? `dispatchIds` is the `{plan, phase}` extracted from the
251
+ * dispatch's own text via `extractDispatchIdentifiers` (or manually supplied
252
+ * by a caller with a more reliable source).
253
+ *
254
+ * Returns false (mismatch — "no applicable sentinel") ONLY when both sides
255
+ * carry a value for the SAME identifier and they disagree. Any side missing
256
+ * a value (sentinel predates this fix, or the dispatch text didn't match the
257
+ * expected shape) is treated as "cannot compare" and does NOT itself produce
258
+ * a mismatch — this stays a defense-in-depth narrowing of an otherwise-fresh
259
+ * sentinel's applicability, not a new fail-open/fail-closed axis on its own.
260
+ */
261
+ function sentinelAppliesToDispatch(sentinel, dispatchIds) {
262
+ if (!sentinel || !dispatchIds) return true;
263
+ if (sentinel.phase && dispatchIds.phase && sentinel.phase !== dispatchIds.phase) return false;
264
+ if (sentinel.plan && dispatchIds.plan && sentinel.plan !== dispatchIds.plan) return false;
265
+ return true;
266
+ }
267
+
268
+ module.exports = {
269
+ VALID_ISOLATION,
270
+ SENTINEL_RELATIVE_PATH,
271
+ SENTINEL_STALE_MS,
272
+ sentinelPath,
273
+ resolveSentinelRoot,
274
+ readSentinel,
275
+ extractDispatchIdentifiers,
276
+ sentinelAppliesToDispatch,
277
+ };
@@ -16,6 +16,7 @@
16
16
  * stale warnings for users who haven't cleaned up manually (#1750).
17
17
  */
18
18
  const MANAGED_HOOKS = [
19
+ 'gsd-agent-isolation-guard.js',
19
20
  'gsd-check-update-worker.js',
20
21
  'gsd-check-update.js',
21
22
  'gsd-config-reload.js',
@@ -40,6 +41,7 @@ const MANAGED_HOOKS = [
40
41
  'gsd-windsurf-pre-write.js',
41
42
  'gsd-workflow-guard.js',
42
43
  'gsd-worktree-path-guard.js',
44
+ 'gsd-write-guard.js',
43
45
  ];
44
46
 
45
47
  module.exports = { MANAGED_HOOKS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/gsd-core",
3
- "version": "1.9.1",
3
+ "version": "1.11.0",
4
4
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
5
5
  "main": ".opencode/plugins/gsd-core.js",
6
6
  "bin": {
@@ -21,6 +21,14 @@
21
21
  "GEMINI.md",
22
22
  "hooks",
23
23
  "scripts",
24
+ "!scripts/gen-emitted-baseline.cjs",
25
+ "!scripts/qa-smell-ratchet.cjs",
26
+ "!scripts/live-config-guard.cjs",
27
+ "!scripts/run-tests.cjs",
28
+ "!scripts/affected-tests-lib.cjs",
29
+ "!scripts/run-affected-tests.cjs",
30
+ "!scripts/lint-no-adhoc-regex-escape.cjs",
31
+ "!scripts/lint-allow-test-rule-refs.cjs",
24
32
  "pi",
25
33
  "vscode"
26
34
  ],
@@ -48,7 +56,7 @@
48
56
  "access": "public"
49
57
  },
50
58
  "engines": {
51
- "node": ">=22.0.0",
59
+ "node": ">=24.0.0",
52
60
  "npm": ">=10.0.0"
53
61
  },
54
62
  "dependencies": {
@@ -63,9 +71,11 @@
63
71
  "eslint": "^9.39.4",
64
72
  "eslint-plugin-n": "^17.24.0",
65
73
  "eslint-plugin-no-only-tests": "^3.4.0",
74
+ "espree": "^10.4.0",
66
75
  "fast-check": "^4.8.0",
67
76
  "globals": "^16.5.0",
68
- "js-yaml": "^4.2.1",
77
+ "js-yaml": "^4.3.1",
78
+ "re2js": "^2.8.6",
69
79
  "typescript": "^6.0.3",
70
80
  "typescript-eslint": "^8.60.0"
71
81
  },
@@ -79,21 +89,24 @@
79
89
  },
80
90
  "scripts": {
81
91
  "sync:launcher": "node scripts/sync-runtime-launcher.cjs",
92
+ "check:contract-drift": "node scripts/check-contract-drift.cjs",
82
93
  "check:env": "node scripts/check-env.cjs",
83
94
  "check:alias-drift": "node scripts/check-alias-drift.cjs",
84
95
  "check:identity-drift": "node scripts/lint-package-identity-drift.cjs",
85
96
  "check:phase-id-drift": "node scripts/lint-phase-id-drift.cjs",
86
97
  "check:integrity": "node scripts/check-npm-integrity.cjs",
87
- "build": "npm run generate:identity && npm run build:lib && npm run gen:plugin-skills && npm run gen:loop-host-contract && npm run gen:capability-registry && npm run build:hooks",
98
+ "build": "npm run generate:identity && npm run build:lib && npm run gen:section-manifest && npm run gen:context-index && npm run gen:plugin-skills && npm run gen:loop-host-contract && npm run gen:capability-registry && npm run build:hooks",
88
99
  "build:hooks": "node scripts/build-hooks.js",
89
100
  "build:lib": "tsc -p tsconfig.build.json",
90
101
  "generate:identity": "node scripts/generate-package-identity.cjs",
102
+ "gen:context-index": "node scripts/gen-context-index.cjs --write",
91
103
  "gen:loop-host-contract": "node scripts/gen-loop-host-contract.cjs --write",
92
104
  "gen:plugin-skills": "node scripts/gen-plugin-skills.cjs --write",
93
105
  "gen:capability-registry": "node scripts/gen-capability-registry.cjs --write",
94
106
  "gen:registry": "node scripts/gen-registry.cjs --write",
95
107
  "gen:install-tree": "node scripts/gen-install-tree-fixtures.cjs",
96
- "regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree",
108
+ "gen:section-manifest": "node scripts/gen-section-manifest.cjs --write",
109
+ "regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/gen-context-index.cjs --write && npm run gen:section-manifest && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree",
97
110
  "validate:registry": "node scripts/validate-registry.cjs",
98
111
  "prepack": "npm run build:lib",
99
112
  "prepare": "npm run build:lib",
@@ -101,10 +114,12 @@
101
114
  "prepublishOnly": "npm run build:lib && npm run build:hooks",
102
115
  "pretest": "npm run build:lib && npm run lint:skill-deps",
103
116
  "pretest:coverage": "npm run build:lib && npm run lint:skill-deps",
104
- "lint": "eslint . --cache --cache-location node_modules/.cache/eslint/",
117
+ "lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings 0",
105
118
  "lint:fix": "eslint . --fix",
106
119
  "lint:table-schema-drift": "node scripts/lint-table-schema-drift.cjs",
107
- "lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-emitted-drift-ack.cjs && node scripts/lint-portable-timeout.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-fix-has-regression-test.cjs",
120
+ "lint:frontmatter-scalar-broad-grep": "node scripts/lint-frontmatter-scalar-broad-grep.cjs",
121
+ "lint:removed-but-needed": "node scripts/lint-removed-but-needed.cjs",
122
+ "lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-emitted-drift-ack.cjs && node scripts/lint-portable-timeout.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-fix-has-regression-test.cjs && node scripts/lint-example-parser-parity.cjs && node scripts/lint-docs-command-form.cjs && node scripts/lint-plan-count-drift.cjs && node scripts/lint-milestone-window-drift.cjs && node scripts/lint-phase-enumeration-drift.cjs && node scripts/lint-planning-prompt-drift.cjs && node scripts/lint-unreachable-guard-drift.cjs && node scripts/lint-completion-ratio-drift.cjs && node scripts/lint-state-field-drift.cjs && node scripts/lint-state-write-path-drift.cjs && node scripts/lint-completion-predicate-drift.cjs && node scripts/lint-planning-snapshot-bypass-drift.cjs && node scripts/lint-health-diagnostic-rule-table.cjs && node scripts/lint-planning-artifact-writer-drift.cjs && node scripts/lint-frontmatter-scalar-broad-grep.cjs && node scripts/lint-removed-but-needed.cjs && node scripts/lint-no-adhoc-regex-escape.cjs && node scripts/lint-vendored-deps.cjs && npm run lint:hooks-runtime-build-seam && node scripts/check-contract-drift.cjs",
108
123
  "lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
109
124
  "lint:regression-names": "node scripts/lint-regression-test-names.cjs",
110
125
  "lint:descriptions": "node scripts/lint-descriptions.cjs",
@@ -112,9 +127,12 @@
112
127
  "lint:test-file-count": "node scripts/lint-test-file-count.cjs",
113
128
  "lint:pr-checks": "node scripts/lint-pr-check-project-dir.cjs",
114
129
  "lint:changeset": "node scripts/changeset/lint.cjs",
115
- "lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check",
130
+ "lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check",
116
131
  "lint:docs": "node scripts/lint-docs-required.cjs",
132
+ "lint:qa-smells": "node scripts/qa-smell-ratchet.cjs",
117
133
  "lint:legacy-name": "node scripts/lint-legacy-dir-name.cjs",
134
+ "lint:docs-command-form": "node scripts/lint-docs-command-form.cjs",
135
+ "lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
118
136
  "ci:test-scope": "node scripts/ci-test-scope.cjs",
119
137
  "changeset": "node scripts/changeset/new.cjs",
120
138
  "changelog:render": "node scripts/changeset/cli.cjs render",
@@ -124,10 +142,13 @@
124
142
  "test:install": "node scripts/run-tests.cjs --suite install",
125
143
  "test:security": "node scripts/run-tests.cjs --suite security",
126
144
  "test:slow": "node scripts/run-tests.cjs --suite slow",
145
+ "test:qa": "node scripts/run-tests.cjs --suite qa",
127
146
  "test:affected": "node scripts/run-affected-tests.cjs",
128
- "test:coverage": "c8 --check-coverage --lines 70 --branches 60 --reporter text --include 'gsd-core/bin/lib/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs",
147
+ "test:coverage": "c8 --check-coverage --lines 70 --branches 60 --reporter text --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs",
129
148
  "test:coverage:scripts-floor": "c8 check-coverage --lines 55 --include 'scripts/**/*.cjs' --exclude 'tests/**' --all",
130
- "test:coverage:unit": "c8 --reporter text --reporter json-summary --include 'gsd-core/bin/lib/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs --suite unit && node scripts/check-coverage-gate.cjs",
149
+ "test:coverage:unit": "c8 --reporter text --reporter json-summary --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs --suite unit && node scripts/check-coverage-gate.cjs",
150
+ "test:coverage:unit:raw": "c8 --reporter none node scripts/run-tests.cjs --suite unit",
151
+ "test:coverage:report": "c8 report --reporter text --reporter json-summary --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all && node scripts/check-coverage-gate.cjs",
131
152
  "test:coverage:all": "npm run test:coverage",
132
153
  "test:mutation": "stryker run",
133
154
  "test:mutation:since": "stryker run --incremental --since origin/next"
package/pi/gsd.cjs CHANGED
@@ -63,6 +63,43 @@ function resolveEngineRoot(startDir) {
63
63
  const ENGINE_ROOT = resolveEngineRoot(__dirname);
64
64
  const GSD_CORE = path.join(ENGINE_ROOT, 'gsd-core');
65
65
 
66
+ // #3023: pi reserves `hooks/` as its (now deprecated) extension directory and
67
+ // warns on every startup when one exists, so the installer stages GSD's shared
68
+ // hook bundle under `gsd-hooks/` for pi. Probe in preference order rather than
69
+ // hardcoding either name: an installed pi tree has `gsd-hooks/`, a dev checkout
70
+ // has the repo-root `hooks/`, and a half-upgraded tree can transiently have both
71
+ // — in which case the renamed bundle is the current one and wins.
72
+ const SHARED_HOOKS_DIR_CANDIDATES = ['gsd-hooks', 'hooks'];
73
+
74
+ /**
75
+ * Absolute path to the staged shared-hook bundle, or null when none is present.
76
+ * Never throws — a stat failure on any candidate is treated as "not this one".
77
+ *
78
+ * A candidate qualifies only when it is a directory AND non-empty. An install
79
+ * can be interrupted between `mkdirSync(gsd-hooks)` and the file copy, leaving
80
+ * a directory that exists but is empty; `gsd-hooks` is probed FIRST (it is the
81
+ * preferred, current name), so an empty `gsd-hooks/` would otherwise win over
82
+ * a fully-staged legacy `hooks/` and every hook would silently no-op —
83
+ * `runHook`'s `fs.existsSync(hookPath)` guard degrades per-file, so binding to
84
+ * an empty bundle produces no error at all, just silent inaction. A
85
+ * partially-staged bundle must lose to a fully-staged one for that reason.
86
+ * @param {string} engineRoot
87
+ * @returns {string|null}
88
+ */
89
+ function resolveSharedHooksDir(engineRoot) {
90
+ for (const name of SHARED_HOOKS_DIR_CANDIDATES) {
91
+ const candidate = path.join(engineRoot, name);
92
+ try {
93
+ if (!fs.statSync(candidate).isDirectory()) continue;
94
+ if (fs.readdirSync(candidate).length === 0) continue;
95
+ return candidate;
96
+ } catch { /* absent or unreadable — try the next candidate */ }
97
+ }
98
+ return null;
99
+ }
100
+
101
+ const SHARED_HOOKS_DIR = resolveSharedHooksDir(ENGINE_ROOT);
102
+
66
103
  // ── curated top-level command families (gsd-tools.cjs TOP_LEVEL_USAGE) ──────
67
104
  // readCmdNames() (scripts/fix-slash-commands.cjs) reads commands/, which pi
68
105
  // does NOT install (it ships a single native-extension file, no shared
@@ -95,8 +132,8 @@ function getArgumentCompletions(prefix) {
95
132
  /**
96
133
  * Tokenize the raw `/gsd <args>` string into { family, subcommand, args }.
97
134
  * Reuses the quote-aware whitespace tokenizer already shipped for hooks
98
- * (hooks/lib/git-cmd.js's `tokenize`) rather than re-implementing shell-word
99
- * splitting a second time. #2102 Stage 2: pi's capability descriptor no
135
+ * (the staged shared-hook bundle's `lib/git-cmd.js`'s `tokenize`) rather than
136
+ * re-implementing shell-word splitting a second time. #2102 Stage 2: pi's capability descriptor no
100
137
  * longer sets `hostBehaviors.skipSharedHooksInstall` (adversarial-review
101
138
  * finding #1/#2 — pi ships NO hooks/ with that flag set, so this require was
102
139
  * dead in a real install), so the shared hooks/ bundle — including
@@ -112,7 +149,8 @@ function getArgumentCompletions(prefix) {
112
149
  function parseGsdCommandArgs(rawArgs) {
113
150
  let tokenize;
114
151
  try {
115
- ({ tokenize } = require(path.join(ENGINE_ROOT, 'hooks', 'lib', 'git-cmd.js')));
152
+ if (!SHARED_HOOKS_DIR) throw new Error('no staged hook bundle');
153
+ ({ tokenize } = require(path.join(SHARED_HOOKS_DIR, 'lib', 'git-cmd.js')));
116
154
  } catch {
117
155
  tokenize = (s) => String(s || '').split(/\s+/).filter(Boolean);
118
156
  }
@@ -148,11 +186,10 @@ function buildGsdInvokeParameters() {
148
186
  });
149
187
  }
150
188
  } catch {
151
- // typebox is not installed in this environment — fall through.
189
+ // typebox is not installed in this environment — fall through to the
190
+ // plain JSON-Schema fallback below. This is the normal path (typebox is
191
+ // NOT a gsd-core dependency), so no warning is emitted (#3022).
152
192
  }
153
- process.stderr.write(
154
- 'gsd: typebox unavailable — gsd_invoke "parameters" falling back to a plain JSON-schema object.\n',
155
- );
156
193
  return {
157
194
  type: 'object',
158
195
  properties: {
@@ -245,13 +282,14 @@ function buildBeforeProviderRequestHandler({ tier = 'sonnet' } = {}) {
245
282
  * `node <hooks/hookFile>` with the payload piped to stdin, on a bounded
246
283
  * timeout. NEVER throws — a missing hook file, a spawn error, or a timeout
247
284
  * all degrade to a silent-allow result so a hook problem can never block pi.
248
- * @param {string} hookFile filename under hooks/, e.g. "gsd-context-monitor.js"
285
+ * @param {string} hookFile filename under the resolved shared-hook bundle, e.g. "gsd-context-monitor.js"
249
286
  * @param {object} payload
250
287
  * @param {{ timeout?: number, cwd?: string }} [opts]
251
288
  * @returns {{ stdout: string, exitCode: number, timedOut: boolean }}
252
289
  */
253
290
  function runHook(hookFile, payload, opts = {}) {
254
- const hookPath = path.join(ENGINE_ROOT, 'hooks', hookFile);
291
+ if (!SHARED_HOOKS_DIR) return { stdout: '', exitCode: 0, timedOut: false };
292
+ const hookPath = path.join(SHARED_HOOKS_DIR, hookFile);
255
293
  if (!fs.existsSync(hookPath)) return { stdout: '', exitCode: 0, timedOut: false };
256
294
  const timeout = opts.timeout || 8000;
257
295
  let result;
@@ -282,16 +320,35 @@ module.exports = function gsdPiExtension(pi) {
282
320
  getArgumentCompletions,
283
321
  handler: async (args, ctx) => {
284
322
  const cwd = (ctx && ctx.cwd) || process.cwd();
323
+ // #3456: Pi's command dispatcher awaits the handler and DISCARDS its
324
+ // return value (agent-session.ts: `await command.handler(args, ctx);
325
+ // return true;`) — #3097's `{ content: [...] }` return was silently
326
+ // dropped exactly like the pre-#3097 bare string. The ONLY command-
327
+ // output mechanism Pi consumes is `ctx.ui.notify(message, type)`
328
+ // (pi docs: extensions.md#piregistercommandname-options), so the output
329
+ // is pushed as a side effect on both the success and error paths.
330
+ // Guarded so a host ctx without `ui` degrades silently instead of
331
+ // crashing the command.
332
+ const notify = (ctx && ctx.ui && typeof ctx.ui.notify === 'function')
333
+ ? (message, type) => ctx.ui.notify(message, type)
334
+ : null;
285
335
  const { family, subcommand, args: rest } = parseGsdCommandArgs(args);
286
336
  let dispatchGsdCommand;
287
337
  try {
288
338
  ({ dispatchGsdCommand } = require(path.join(GSD_CORE, 'bin', 'lib', 'shell-command-projection.cjs')));
289
339
  } catch (e) {
290
- return `GSD engine unavailable: ${e && e.message ? e.message : String(e)}`;
340
+ if (notify) {
341
+ notify(`GSD engine unavailable: ${e && e.message ? e.message : String(e)}`, 'error');
342
+ }
343
+ return;
291
344
  }
292
345
  const result = dispatchGsdCommand({ family, subcommand, args: rest, cwd });
293
- if (result.ok) return result.stdout;
294
- return `GSD error: ${result.stderr || result.stdout || `dispatch failed (exit ${result.code})`}`;
346
+ const text = result.ok
347
+ ? result.stdout
348
+ : `GSD error: ${result.stderr || result.stdout || `dispatch failed (exit ${result.code})`}`;
349
+ if (notify) {
350
+ notify(text, result.ok ? 'info' : 'error');
351
+ }
295
352
  },
296
353
  });
297
354
 
@@ -376,6 +433,8 @@ module.exports = function gsdPiExtension(pi) {
376
433
  // resolution WITHOUT a live pi runtime.
377
434
  module.exports._internals = {
378
435
  resolveEngineRoot,
436
+ resolveSharedHooksDir,
437
+ SHARED_HOOKS_DIR_CANDIDATES,
379
438
  parseGsdCommandArgs,
380
439
  getArgumentCompletions,
381
440
  PI_COMMAND_FAMILIES,
@@ -0,0 +1,4 @@
1
+ {
2
+ "$comment": "ADR-3180 Decision 4(e) ratchet, owned by Phase 8 (#3218). See scripts/lint-planning-prompt-drift.cjs. SHRINK-ONLY: entries are removed as sites migrate to the gsd-core CLI; new or changed entries fail lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site — a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy. Emptied by #3218: all 6 recorded sites (7 occurrences) migrated to `gsd_run query find-phase` — see gsd-core/workflows/{execute-plan,plan-phase,plan-review-convergence,progress}.md.",
3
+ "entries": []
4
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$comment": "ADR-3180 §8.1 rule 2 ratchet, owned by Phase 11 (#3309). See scripts/lint-planning-snapshot-bypass-drift.cjs. SHRINK-ONLY: entries are removed as cmdValidateHealth migrates onto src/planning-snapshot.cts; new or changed entries fail lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site — a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy.",
3
+ "entries": [
4
+ {
5
+ "file": "src/verify.cts",
6
+ "text": "const stateContent = fs.readFileSync(statePath, 'utf-8');",
7
+ "derivation": "planning-snapshot-bypass",
8
+ "owner_issue": "#3309",
9
+ "count": 1
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$comment": "#3409 unreachable-shell-guard ratchet. See scripts/lint-unreachable-guard-drift.cjs. SHRINK-ONLY: entries are removed as sites migrate off the unreachable-arm shape; new or changed entries fail lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site — a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy.",
3
+ "entries": []
4
+ }
@@ -51,6 +51,13 @@ const HOOKS_TO_COPY = [
51
51
  // .planning/config.json changes mid-session. Must ship to dist so the
52
52
  // installer can copy it to the target hooks/ dir and register FileChanged.
53
53
  'gsd-config-reload.js',
54
+ // Agent-dispatch isolation guard (#3045): blocks an executor Agent()
55
+ // dispatch missing its harness isolation parameter when the project
56
+ // resolves to harness-worktree. Requires the sibling
57
+ // gsd-core/bin/lib/{runtime-name-policy,capability-registry}.cjs modules
58
+ // at runtime — those ship as part of the full gsd-core/ tree, not via this
59
+ // list.
60
+ 'gsd-agent-isolation-guard.js',
54
61
  'gsd-prompt-guard.js',
55
62
  'gsd-read-guard.js',
56
63
  'gsd-read-injection-scanner.js',
@@ -58,6 +65,8 @@ const HOOKS_TO_COPY = [
58
65
  'gsd-update-banner.js',
59
66
  'gsd-workflow-guard.js',
60
67
  'gsd-worktree-path-guard.js',
68
+ // Catastrophic-shrink guard for curated .planning/ artifacts (#2255, fix 3 of #973)
69
+ 'gsd-write-guard.js',
61
70
  // Community hooks (bash, opt-in via .planning/config.json hooks.community)
62
71
  'gsd-session-state.sh',
63
72
  'gsd-validate-commit.sh',