@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.9bb7453

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +167 -16
  10. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  11. package/dist/resources/extensions/gsd/auto/phases.js +4 -3
  12. package/dist/resources/extensions/gsd/auto-dashboard.js +92 -17
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +44 -0
  14. package/dist/resources/extensions/gsd/auto-post-unit.js +134 -10
  15. package/dist/resources/extensions/gsd/auto-prompts.js +68 -22
  16. package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
  17. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  18. package/dist/resources/extensions/gsd/auto-start.js +94 -15
  19. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  20. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
  21. package/dist/resources/extensions/gsd/auto.js +31 -6
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +83 -4
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +39 -14
  25. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
  26. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  27. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +9 -5
  30. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  31. package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
  32. package/dist/resources/extensions/gsd/commands-mcp-status.js +109 -60
  33. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
  34. package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
  35. package/dist/resources/extensions/gsd/config-overlay.js +2 -1
  36. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  37. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  38. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  39. package/dist/resources/extensions/gsd/error-classifier.js +2 -1
  40. package/dist/resources/extensions/gsd/escalation.js +4 -4
  41. package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
  42. package/dist/resources/extensions/gsd/forensics.js +74 -2
  43. package/dist/resources/extensions/gsd/gsd-db.js +42 -6
  44. package/dist/resources/extensions/gsd/guided-flow.js +30 -69
  45. package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
  46. package/dist/resources/extensions/gsd/mcp-project-config.js +76 -84
  47. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  48. package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
  49. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  50. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  51. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  52. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  53. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  54. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  55. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  56. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  57. package/dist/resources/extensions/gsd/prompts/run-uat.md +48 -24
  58. package/dist/resources/extensions/gsd/prompts/system.md +3 -1
  59. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  60. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  61. package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
  62. package/dist/resources/extensions/gsd/skill-activation.js +20 -3
  63. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
  64. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
  65. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
  66. package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
  67. package/dist/resources/extensions/gsd/state.js +17 -14
  68. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  69. package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
  70. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  71. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  72. package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
  73. package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
  74. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  75. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  76. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +403 -3
  77. package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
  78. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  79. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  80. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  81. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -1
  82. package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
  83. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
  84. package/dist/resources/extensions/mcp-client/manager.js +31 -1
  85. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  86. package/dist/rtk.d.ts +7 -1
  87. package/dist/rtk.js +27 -11
  88. package/dist/update-check.d.ts +15 -1
  89. package/dist/update-check.js +87 -12
  90. package/dist/update-cmd.d.ts +1 -0
  91. package/dist/update-cmd.js +53 -2
  92. package/dist/web/standalone/.next/BUILD_ID +1 -1
  93. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  94. package/dist/web/standalone/.next/build-manifest.json +2 -2
  95. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/index.html +1 -1
  114. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  121. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  122. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  124. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  125. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  126. package/package.json +5 -3
  127. package/packages/cloud-mcp-gateway/package.json +2 -2
  128. package/packages/contracts/dist/workflow.d.ts +14 -0
  129. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  130. package/packages/contracts/dist/workflow.js +16 -0
  131. package/packages/contracts/dist/workflow.js.map +1 -1
  132. package/packages/contracts/package.json +1 -1
  133. package/packages/daemon/package.json +4 -4
  134. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  135. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  136. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  137. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  138. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  139. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  140. package/packages/gsd-agent-core/dist/index.js +1 -0
  141. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  142. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  143. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  144. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  145. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  146. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  147. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  148. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  149. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  150. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  151. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  152. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  153. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  154. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  155. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  156. package/packages/gsd-agent-core/package.json +6 -6
  157. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  158. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  160. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  162. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  164. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  165. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  166. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +92 -31
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  173. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  174. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  175. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  176. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
  177. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  178. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
  179. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  180. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
  181. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  186. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
  187. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  188. package/packages/gsd-agent-modes/package.json +7 -7
  189. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  190. package/packages/mcp-server/dist/remote-questions.js +23 -9
  191. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  192. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  193. package/packages/mcp-server/dist/workflow-tools.js +84 -2
  194. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  195. package/packages/mcp-server/package.json +3 -3
  196. package/packages/native/package.json +1 -1
  197. package/packages/pi-agent-core/dist/agent-loop.js +38 -0
  198. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  199. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  200. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  201. package/packages/pi-agent-core/dist/agent.js +2 -0
  202. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  203. package/packages/pi-agent-core/dist/types.d.ts +3 -0
  204. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  205. package/packages/pi-agent-core/dist/types.js.map +1 -1
  206. package/packages/pi-agent-core/package.json +1 -1
  207. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  208. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  209. package/packages/pi-ai/dist/api-registry.js +23 -0
  210. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  211. package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
  212. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  213. package/packages/pi-ai/dist/image-models.generated.js +15 -0
  214. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  215. package/packages/pi-ai/dist/models.generated.d.ts +406 -17
  216. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  217. package/packages/pi-ai/dist/models.generated.js +484 -116
  218. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  219. package/packages/pi-ai/dist/stream.js +6 -6
  220. package/packages/pi-ai/dist/stream.js.map +1 -1
  221. package/packages/pi-ai/package.json +1 -1
  222. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  224. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  226. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  228. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  229. package/packages/pi-coding-agent/package.json +7 -7
  230. package/packages/pi-tui/dist/terminal.d.ts +1 -0
  231. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  232. package/packages/pi-tui/dist/terminal.js +8 -4
  233. package/packages/pi-tui/dist/terminal.js.map +1 -1
  234. package/packages/pi-tui/package.json +1 -1
  235. package/packages/rpc-client/package.json +2 -2
  236. package/pkg/package.json +1 -1
  237. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  238. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  239. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  240. package/src/resources/extensions/browser-tools/index.ts +60 -9
  241. package/src/resources/extensions/browser-tools/package.json +5 -1
  242. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  243. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  244. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +196 -16
  245. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
  246. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  247. package/src/resources/extensions/gsd/auto/phases.ts +5 -3
  248. package/src/resources/extensions/gsd/auto-dashboard.ts +98 -18
  249. package/src/resources/extensions/gsd/auto-dispatch.ts +53 -0
  250. package/src/resources/extensions/gsd/auto-post-unit.ts +166 -9
  251. package/src/resources/extensions/gsd/auto-prompts.ts +102 -15
  252. package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
  253. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  254. package/src/resources/extensions/gsd/auto-start.ts +112 -17
  255. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  256. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
  257. package/src/resources/extensions/gsd/auto.ts +47 -5
  258. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +90 -4
  259. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
  260. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +60 -19
  261. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
  262. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  263. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  264. package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
  265. package/src/resources/extensions/gsd/commands/handlers/ops.ts +9 -5
  266. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  267. package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
  268. package/src/resources/extensions/gsd/commands-mcp-status.ts +136 -58
  269. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
  270. package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
  271. package/src/resources/extensions/gsd/config-overlay.ts +3 -1
  272. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  273. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  274. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  275. package/src/resources/extensions/gsd/error-classifier.ts +2 -1
  276. package/src/resources/extensions/gsd/escalation.ts +4 -4
  277. package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
  278. package/src/resources/extensions/gsd/forensics.ts +99 -5
  279. package/src/resources/extensions/gsd/gsd-db.ts +46 -8
  280. package/src/resources/extensions/gsd/guided-flow.ts +91 -83
  281. package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
  282. package/src/resources/extensions/gsd/mcp-project-config.ts +105 -88
  283. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  284. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  285. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  286. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  287. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  288. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  289. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  290. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  291. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  292. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  294. package/src/resources/extensions/gsd/prompts/run-uat.md +48 -24
  295. package/src/resources/extensions/gsd/prompts/system.md +3 -1
  296. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  297. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  298. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  299. package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
  300. package/src/resources/extensions/gsd/skill-activation.ts +20 -2
  301. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
  302. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
  303. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
  304. package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
  305. package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
  306. package/src/resources/extensions/gsd/state.ts +18 -14
  307. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  308. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +156 -4
  309. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +123 -0
  310. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
  311. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
  312. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  313. package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
  314. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
  315. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  316. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  317. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  318. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  319. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
  320. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
  321. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  322. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
  323. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  324. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  325. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
  326. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
  327. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  328. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  329. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  330. package/src/resources/extensions/gsd/tests/gsd-rebuild.test.ts +199 -0
  331. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
  332. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  333. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  334. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  335. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  336. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  337. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
  338. package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
  339. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +100 -0
  340. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +179 -0
  341. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  342. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
  343. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  344. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
  345. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  346. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
  347. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  348. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  349. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  350. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +53 -1
  351. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  352. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
  353. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  354. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
  355. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
  356. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
  357. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  358. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  359. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
  360. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
  361. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  362. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
  363. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  364. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
  365. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  366. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
  367. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
  368. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  369. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  370. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
  371. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  372. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
  373. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +213 -0
  374. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
  375. package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
  376. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  377. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  378. package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
  379. package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
  380. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  381. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  382. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +489 -3
  383. package/src/resources/extensions/gsd/types.ts +69 -5
  384. package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
  385. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  386. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  387. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  388. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -1
  389. package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
  390. package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
  391. package/src/resources/extensions/mcp-client/manager.ts +33 -1
  392. package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
  393. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  394. /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
  395. /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
@@ -10,6 +10,9 @@ import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js"
10
10
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import { parseUnitId } from "./unit-id.js";
13
+ import { queryJournal } from "./journal.js";
14
+ import { readUnitRuntimeRecord } from "./unit-runtime.js";
15
+ import { extractFrontmatterVerdict } from "./verdict-parser.js";
13
16
  // ─── Artifact Path Resolution ──────────────────────────────────────────────
14
17
  export function resolveHookArtifactPath(basePath, unitId, artifactName) {
15
18
  const { milestone, slice, task } = parseUnitId(unitId);
@@ -38,6 +41,28 @@ export function convertDispatchRules(rules) {
38
41
  }
39
42
  // ─── RuleRegistry ─────────────────────────────────────────────────────────
40
43
  const HOOK_STATE_FILE = "hook-state.json";
44
+ const FAILED_HOOK_RUNTIME_PHASES = new Set([
45
+ "timeout",
46
+ "finalize-timeout",
47
+ "crashed",
48
+ "paused",
49
+ ]);
50
+ const HOOK_OUTCOME_VERDICTS = new Set([
51
+ "pass",
52
+ "advisory",
53
+ "needs-rework",
54
+ "needs-remediation",
55
+ "needs-attention",
56
+ ]);
57
+ function isBlockingHook(config) {
58
+ return config?.criticality === "blocking";
59
+ }
60
+ function hookMaxCycles(config) {
61
+ return config.max_cycles ?? 1;
62
+ }
63
+ function hookCycleKey(config, trigger) {
64
+ return `${config.name}/${trigger.triggerUnitType}/${trigger.triggerUnitId}`;
65
+ }
41
66
  export class RuleRegistry {
42
67
  /** Static dispatch rules provided at construction time. */
43
68
  dispatchRules;
@@ -47,6 +72,8 @@ export class RuleRegistry {
47
72
  cycleCounts = new Map();
48
73
  retryPending = false;
49
74
  retryTrigger = null;
75
+ hookFailure = null;
76
+ gateBlockPending = null;
50
77
  constructor(dispatchRules) {
51
78
  this.dispatchRules = dispatchRules;
52
79
  }
@@ -71,6 +98,7 @@ export class RuleRegistry {
71
98
  artifact: hook.artifact,
72
99
  retry_on: hook.retry_on,
73
100
  max_cycles: hook.max_cycles,
101
+ criticality: hook.criticality,
74
102
  },
75
103
  });
76
104
  }
@@ -117,7 +145,9 @@ export class RuleRegistry {
117
145
  evaluatePostUnit(completedUnitType, completedUnitId, basePath) {
118
146
  // If we just completed a hook unit, handle its result
119
147
  if (this.activeHook) {
120
- return this._handleHookCompletion(basePath);
148
+ const observedCleanExecution = completedUnitType === `hook/${this.activeHook.hookName}` &&
149
+ completedUnitId === this.activeHook.triggerUnitId;
150
+ return this._handleHookCompletion(basePath, observedCleanExecution);
121
151
  }
122
152
  // Don't trigger hooks for other hook units (prevent hook-on-hook chains)
123
153
  // Don't trigger hooks for triage units or quick-task units
@@ -141,75 +171,371 @@ export class RuleRegistry {
141
171
  _dequeueNextHook(basePath) {
142
172
  while (this.hookQueue.length > 0) {
143
173
  const entry = this.hookQueue.shift();
144
- const { config, triggerUnitType, triggerUnitId } = entry;
145
- // Check idempotency if artifact already exists, skip
146
- if (config.artifact) {
174
+ const { config, triggerUnitType, triggerUnitId, forceRun } = entry;
175
+ // Advisory hooks preserve existing idempotency: any configured artifact
176
+ // means the hook already ran. Blocking gates must verify outcome first.
177
+ if (config.artifact && !forceRun) {
147
178
  const artifactPath = resolveHookArtifactPath(basePath, triggerUnitId, config.artifact);
148
- if (existsSync(artifactPath))
149
- continue;
179
+ if (existsSync(artifactPath)) {
180
+ const completion = this._assessConfiguredHookCompletion(basePath, config.name, triggerUnitId);
181
+ if (completion.outcome === "failed") {
182
+ return this._handleFailedHookCompletion(basePath, {
183
+ hookName: config.name,
184
+ triggerUnitType,
185
+ triggerUnitId,
186
+ cycle: this.cycleCounts.get(hookCycleKey(config, { triggerUnitType, triggerUnitId })) ?? 0,
187
+ pendingRetry: false,
188
+ }, config, completion.reason);
189
+ }
190
+ if (!isBlockingHook(config))
191
+ continue;
192
+ const decision = this._handleExistingBlockingArtifact(config, { triggerUnitType, triggerUnitId }, basePath);
193
+ if (decision === "skip")
194
+ continue;
195
+ return decision;
196
+ }
197
+ }
198
+ const dispatch = this._startHook(config, triggerUnitType, triggerUnitId);
199
+ if (dispatch)
200
+ return dispatch;
201
+ if (isBlockingHook(config)) {
202
+ const cycleKey = hookCycleKey(config, { triggerUnitType, triggerUnitId });
203
+ const maxCycles = hookMaxCycles(config);
204
+ const currentCycle = this.cycleCounts.get(cycleKey) ?? 0;
205
+ if (currentCycle >= maxCycles) {
206
+ this._setGateBlock(config, { triggerUnitType, triggerUnitId }, {
207
+ action: "pause",
208
+ reason: `gate cycle budget exhausted before ${config.name} produced a passing outcome`,
209
+ cycle: currentCycle,
210
+ maxCycles,
211
+ });
212
+ return null;
213
+ }
150
214
  }
151
- // Check cycle limit
152
- const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
153
- const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
154
- const maxCycles = config.max_cycles ?? 1;
155
- if (currentCycle > maxCycles)
156
- continue;
157
- this.cycleCounts.set(cycleKey, currentCycle);
158
- this.activeHook = {
159
- hookName: config.name,
160
- triggerUnitType,
161
- triggerUnitId,
162
- cycle: currentCycle,
163
- pendingRetry: false,
164
- };
165
- // Build prompt with variable substitution
166
- const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
167
- let prompt = config.prompt
168
- .replace(/\{milestoneId\}/g, mid ?? "")
169
- .replace(/\{sliceId\}/g, sid ?? "")
170
- .replace(/\{taskId\}/g, tid ?? "");
171
- // Inject browser safety instruction
172
- prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
173
- return {
174
- hookName: config.name,
175
- prompt,
176
- model: config.model,
177
- unitType: `hook/${config.name}`,
178
- unitId: triggerUnitId,
179
- };
180
215
  }
181
216
  // No more hooks — clear active state
182
217
  this.activeHook = null;
183
218
  return null;
184
219
  }
185
- _handleHookCompletion(basePath) {
220
+ _handleHookCompletion(basePath, observedCleanExecution) {
186
221
  const hook = this.activeHook;
187
222
  const hooks = resolvePostUnitHooks(basePath);
188
223
  const config = hooks.find(h => h.name === hook.hookName);
224
+ if (!config) {
225
+ this.activeHook = null;
226
+ return this._dequeueNextHook(basePath);
227
+ }
228
+ const completion = this._assessHookCompletion(basePath, hook);
229
+ if (completion.outcome === "failed") {
230
+ return this._handleFailedHookCompletion(basePath, hook, config, completion.reason);
231
+ }
189
232
  // Check if retry was requested via retry_on artifact
190
- if (config?.retry_on) {
233
+ if (config.retry_on) {
191
234
  const retryArtifactPath = resolveHookArtifactPath(basePath, hook.triggerUnitId, config.retry_on);
192
235
  if (existsSync(retryArtifactPath)) {
193
- const cycleKey = `${config.name}/${hook.triggerUnitType}/${hook.triggerUnitId}`;
194
- const currentCycle = this.cycleCounts.get(cycleKey) ?? 1;
195
- const maxCycles = config.max_cycles ?? 1;
196
- if (currentCycle < maxCycles) {
236
+ if (this._requestTriggerRetry(config, hook, config.retry_on)) {
237
+ return null;
238
+ }
239
+ if (isBlockingHook(config)) {
240
+ this._setGateBlock(config, hook, {
241
+ action: "pause",
242
+ reason: `gate cycle budget exhausted after ${config.retry_on} requested rework`,
243
+ retryArtifact: config.retry_on,
244
+ });
197
245
  this.activeHook = null;
198
246
  this.hookQueue = [];
199
- this.retryPending = true;
200
- this.retryTrigger = {
201
- unitType: hook.triggerUnitType,
202
- unitId: hook.triggerUnitId,
203
- retryArtifact: config.retry_on,
204
- };
205
247
  return null;
206
248
  }
207
249
  }
208
250
  }
251
+ if (isBlockingHook(config)) {
252
+ return this._handleBlockingGateCompletion(config, hook, basePath, observedCleanExecution);
253
+ }
209
254
  // Hook completed normally — try next hook in queue
210
255
  this.activeHook = null;
211
256
  return this._dequeueNextHook(basePath);
212
257
  }
258
+ _startHook(config, triggerUnitType, triggerUnitId) {
259
+ const cycleKey = `${config.name}/${triggerUnitType}/${triggerUnitId}`;
260
+ const currentCycle = (this.cycleCounts.get(cycleKey) ?? 0) + 1;
261
+ const maxCycles = config.max_cycles ?? 1;
262
+ if (currentCycle > maxCycles)
263
+ return null;
264
+ this.cycleCounts.set(cycleKey, currentCycle);
265
+ this.activeHook = {
266
+ hookName: config.name,
267
+ triggerUnitType,
268
+ triggerUnitId,
269
+ cycle: currentCycle,
270
+ pendingRetry: false,
271
+ };
272
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
273
+ let prompt = config.prompt
274
+ .replace(/\{milestoneId\}/g, mid ?? "")
275
+ .replace(/\{sliceId\}/g, sid ?? "")
276
+ .replace(/\{taskId\}/g, tid ?? "");
277
+ prompt += "\n\n**Browser tool safety:** Do NOT use `browser_wait_for` with `condition: \"network_idle\"` — it hangs indefinitely when dev servers keep persistent connections (Vite HMR, WebSocket). Use `selector_visible`, `text_visible`, or `delay` instead.";
278
+ return {
279
+ hookName: config.name,
280
+ prompt,
281
+ model: config.model,
282
+ unitType: `hook/${config.name}`,
283
+ unitId: triggerUnitId,
284
+ };
285
+ }
286
+ _assessHookCompletion(basePath, hook) {
287
+ return this._assessConfiguredHookCompletion(basePath, hook.hookName, hook.triggerUnitId);
288
+ }
289
+ _assessConfiguredHookCompletion(basePath, hookName, unitId) {
290
+ const unitType = `hook/${hookName}`;
291
+ const latestUnitEnd = this._latestHookUnitEnd(basePath, unitType, unitId);
292
+ if (latestUnitEnd) {
293
+ const data = latestUnitEnd.data ?? {};
294
+ const status = data.status;
295
+ const artifactVerified = data.artifactVerified;
296
+ if (status === "completed" && artifactVerified !== false) {
297
+ return { outcome: "success" };
298
+ }
299
+ return {
300
+ outcome: "failed",
301
+ reason: this._formatHookFailureReason(status, artifactVerified, data.errorContext),
302
+ };
303
+ }
304
+ const runtime = readUnitRuntimeRecord(basePath, unitType, unitId);
305
+ if (runtime && FAILED_HOOK_RUNTIME_PHASES.has(runtime.phase)) {
306
+ return { outcome: "failed", reason: `runtime phase ${runtime.phase}` };
307
+ }
308
+ return { outcome: "unknown" };
309
+ }
310
+ _latestHookUnitEnd(basePath, unitType, unitId) {
311
+ const unitEnds = queryJournal(basePath, { eventType: "unit-end", unitId })
312
+ .filter(entry => entry.data?.unitType === unitType);
313
+ return unitEnds[unitEnds.length - 1] ?? null;
314
+ }
315
+ _formatHookFailureReason(status, artifactVerified, errorContext) {
316
+ const parts = [`status ${typeof status === "string" ? status : "unknown"}`];
317
+ if (artifactVerified === false) {
318
+ parts.push("artifact not verified");
319
+ }
320
+ if (typeof errorContext === "object" && errorContext !== null && "message" in errorContext) {
321
+ const message = errorContext.message;
322
+ if (typeof message === "string" && message.length > 0) {
323
+ parts.push(message);
324
+ }
325
+ }
326
+ return parts.join("; ");
327
+ }
328
+ _handleFailedHookCompletion(basePath, hook, config, reason) {
329
+ if (config) {
330
+ const retry = this._startHook(config, hook.triggerUnitType, hook.triggerUnitId);
331
+ if (retry)
332
+ return retry;
333
+ }
334
+ this.hookFailure = {
335
+ hookName: hook.hookName,
336
+ unitType: `hook/${hook.hookName}`,
337
+ unitId: hook.triggerUnitId,
338
+ reason,
339
+ };
340
+ this.activeHook = null;
341
+ this.hookQueue = [];
342
+ this.persistState(basePath);
343
+ return null;
344
+ }
345
+ _handleExistingBlockingArtifact(config, trigger, basePath) {
346
+ const outcome = this._readGateOutcome(config, trigger, basePath);
347
+ switch (outcome.verdict) {
348
+ case "pass":
349
+ case "advisory":
350
+ return "skip";
351
+ case "needs-rework":
352
+ return this._routeNeedsRework(config, trigger, outcome);
353
+ case "needs-remediation":
354
+ case "needs-attention":
355
+ this._pauseForGate(config, trigger, outcome, `gate reported ${outcome.verdict}`);
356
+ return null;
357
+ case "failed":
358
+ case undefined:
359
+ return this._rerunGateOrBlock(config, trigger, basePath, {
360
+ reason: outcome.reason ?? `gate artifact reported verdict=${outcome.verdict}`,
361
+ outcome,
362
+ });
363
+ }
364
+ return this._rerunGateOrBlock(config, trigger, basePath, {
365
+ reason: `gate artifact reported unsupported verdict=${String(outcome.verdict)}`,
366
+ outcome,
367
+ });
368
+ }
369
+ _handleBlockingGateCompletion(config, hook, basePath, observedCleanExecution) {
370
+ if (!observedCleanExecution) {
371
+ return this._rerunGateOrBlock(config, hook, basePath, {
372
+ reason: `hook/${config.name} did not complete cleanly before the trigger unit resumed`,
373
+ });
374
+ }
375
+ const outcome = this._readGateOutcome(config, hook, basePath);
376
+ switch (outcome.verdict) {
377
+ case "pass":
378
+ case "advisory":
379
+ this.activeHook = null;
380
+ return this._dequeueNextHook(basePath);
381
+ case "needs-rework":
382
+ return this._routeNeedsRework(config, hook, outcome);
383
+ case "needs-remediation":
384
+ case "needs-attention":
385
+ return this._pauseForGate(config, hook, outcome, `gate reported ${outcome.verdict}`);
386
+ case "failed":
387
+ case undefined:
388
+ return this._rerunGateOrBlock(config, hook, basePath, {
389
+ reason: outcome.reason ?? `gate artifact reported verdict=${outcome.verdict}`,
390
+ outcome,
391
+ });
392
+ }
393
+ return this._rerunGateOrBlock(config, hook, basePath, {
394
+ reason: `gate artifact reported unsupported verdict=${String(outcome.verdict)}`,
395
+ outcome,
396
+ });
397
+ }
398
+ _routeNeedsRework(config, trigger, outcome) {
399
+ const action = config.on_block?.action ?? "retry-unit";
400
+ if (action === "retry-task" || action === "retry-unit") {
401
+ if (this._requestTriggerRetry(config, trigger, config.on_block?.artifact)) {
402
+ return null;
403
+ }
404
+ this._setGateBlock(config, trigger, {
405
+ action: "pause",
406
+ reason: "gate cycle budget exhausted after needs-rework",
407
+ outcome,
408
+ retryArtifact: config.on_block?.artifact,
409
+ });
410
+ this.activeHook = null;
411
+ this.hookQueue = [];
412
+ return null;
413
+ }
414
+ return this._pauseForGate(config, trigger, outcome, `gate reported needs-rework; configured on_block action is ${action}`);
415
+ }
416
+ _pauseForGate(config, trigger, outcome, reason) {
417
+ this._setGateBlock(config, trigger, {
418
+ action: config.on_block?.action ?? "pause",
419
+ reason,
420
+ outcome,
421
+ retryArtifact: config.on_block?.artifact,
422
+ });
423
+ this.activeHook = null;
424
+ this.hookQueue = [];
425
+ return null;
426
+ }
427
+ _requestTriggerRetry(config, hook, retryArtifact) {
428
+ const cycleKey = hookCycleKey(config, hook);
429
+ const currentCycle = this.cycleCounts.get(cycleKey) ?? 1;
430
+ const maxCycles = hookMaxCycles(config);
431
+ if (currentCycle >= maxCycles)
432
+ return false;
433
+ this.activeHook = null;
434
+ this.hookQueue = [];
435
+ this.retryPending = true;
436
+ this.retryTrigger = {
437
+ unitType: hook.triggerUnitType,
438
+ unitId: hook.triggerUnitId,
439
+ };
440
+ if (retryArtifact !== undefined) {
441
+ this.retryTrigger.retryArtifact = retryArtifact;
442
+ }
443
+ return true;
444
+ }
445
+ _rerunGateOrBlock(config, trigger, basePath, opts) {
446
+ const cycleKey = hookCycleKey(config, trigger);
447
+ const currentCycle = this.cycleCounts.get(cycleKey) ?? 0;
448
+ const maxCycles = hookMaxCycles(config);
449
+ if (currentCycle < maxCycles) {
450
+ this.activeHook = null;
451
+ this.hookQueue.unshift({
452
+ config,
453
+ triggerUnitType: trigger.triggerUnitType,
454
+ triggerUnitId: trigger.triggerUnitId,
455
+ forceRun: true,
456
+ });
457
+ return this._dequeueNextHook(basePath);
458
+ }
459
+ this._setGateBlock(config, trigger, {
460
+ action: "pause",
461
+ reason: `${opts.reason}; gate cycle budget exhausted`,
462
+ outcome: opts.outcome,
463
+ cycle: currentCycle,
464
+ maxCycles,
465
+ });
466
+ this.activeHook = null;
467
+ this.hookQueue = [];
468
+ return null;
469
+ }
470
+ _readGateOutcome(config, trigger, basePath) {
471
+ if (!config.artifact) {
472
+ return { reason: "blocking gate has no configured artifact" };
473
+ }
474
+ const artifactPath = resolveHookArtifactPath(basePath, trigger.triggerUnitId, config.artifact);
475
+ if (!existsSync(artifactPath)) {
476
+ return {
477
+ artifact: config.artifact,
478
+ artifactPath,
479
+ reason: `missing required gate artifact ${config.artifact}`,
480
+ };
481
+ }
482
+ let content = "";
483
+ try {
484
+ content = readFileSync(artifactPath, "utf-8");
485
+ }
486
+ catch (e) {
487
+ return {
488
+ artifact: config.artifact,
489
+ artifactPath,
490
+ reason: `could not read gate artifact ${config.artifact}: ${e.message}`,
491
+ };
492
+ }
493
+ const rawVerdict = extractFrontmatterVerdict(content);
494
+ if (!rawVerdict) {
495
+ return {
496
+ artifact: config.artifact,
497
+ artifactPath,
498
+ reason: `gate artifact ${config.artifact} is missing frontmatter verdict`,
499
+ };
500
+ }
501
+ if (rawVerdict === "failed") {
502
+ return {
503
+ artifact: config.artifact,
504
+ artifactPath,
505
+ verdict: "failed",
506
+ reason: `gate artifact ${config.artifact} reported verdict=failed`,
507
+ };
508
+ }
509
+ if (!HOOK_OUTCOME_VERDICTS.has(rawVerdict)) {
510
+ return {
511
+ artifact: config.artifact,
512
+ artifactPath,
513
+ reason: `gate artifact ${config.artifact} has unsupported verdict=${rawVerdict}`,
514
+ };
515
+ }
516
+ return {
517
+ artifact: config.artifact,
518
+ artifactPath,
519
+ verdict: rawVerdict,
520
+ };
521
+ }
522
+ _setGateBlock(config, trigger, opts) {
523
+ const cycleKey = hookCycleKey(config, trigger);
524
+ const cycle = opts.cycle ?? this.cycleCounts.get(cycleKey) ?? 0;
525
+ this.gateBlockPending = {
526
+ hookName: config.name,
527
+ triggerUnitType: trigger.triggerUnitType,
528
+ triggerUnitId: trigger.triggerUnitId,
529
+ artifact: opts.outcome?.artifact ?? config.artifact,
530
+ artifactPath: opts.outcome?.artifactPath,
531
+ verdict: opts.outcome?.verdict,
532
+ action: opts.action,
533
+ reason: opts.reason,
534
+ cycle,
535
+ maxCycles: opts.maxCycles ?? hookMaxCycles(config),
536
+ retryArtifact: opts.retryArtifact,
537
+ };
538
+ }
213
539
  // ── Pre-dispatch hook evaluation (sync, all-matching with compose) ──
214
540
  /**
215
541
  * Replicate exact semantics of runPreDispatchHooks from post-unit-hooks.ts:
@@ -275,6 +601,16 @@ export class RuleRegistry {
275
601
  isRetryPending() {
276
602
  return this.retryPending;
277
603
  }
604
+ consumeHookFailure() {
605
+ if (!this.hookFailure)
606
+ return null;
607
+ const failure = { ...this.hookFailure };
608
+ this.hookFailure = null;
609
+ return failure;
610
+ }
611
+ isGateBlockPending() {
612
+ return this.gateBlockPending !== null;
613
+ }
278
614
  /**
279
615
  * Returns the trigger unit info for a pending retry, or null.
280
616
  * Clears the retry state after reading.
@@ -287,22 +623,42 @@ export class RuleRegistry {
287
623
  this.retryTrigger = null;
288
624
  return trigger;
289
625
  }
290
- /** Clear all mutable state (activeHook, hookQueue, cycleCounts, retryPending, retryTrigger). */
626
+ /**
627
+ * Returns a pending post-unit gate block, or null.
628
+ * Clears the block state after reading.
629
+ */
630
+ consumeGateBlock() {
631
+ if (!this.gateBlockPending)
632
+ return null;
633
+ const block = { ...this.gateBlockPending };
634
+ this.gateBlockPending = null;
635
+ return block;
636
+ }
637
+ /** Clear all mutable hook lifecycle state. */
291
638
  resetState() {
292
639
  this.activeHook = null;
293
640
  this.hookQueue = [];
294
641
  this.cycleCounts.clear();
295
642
  this.retryPending = false;
296
643
  this.retryTrigger = null;
644
+ this.hookFailure = null;
645
+ this.gateBlockPending = null;
297
646
  }
298
647
  // ── Persistence ─────────────────────────────────────────────────────
299
648
  _hookStatePath(basePath) {
300
649
  return join(basePath, ".gsd", HOOK_STATE_FILE);
301
650
  }
302
- /** Persist current hook cycle counts to disk. */
651
+ /** Persist current hook state to disk. */
303
652
  persistState(basePath) {
304
653
  const state = {
305
654
  cycleCounts: Object.fromEntries(this.cycleCounts),
655
+ activeHook: this.activeHook ? { ...this.activeHook } : null,
656
+ hookQueue: this.hookQueue.map(entry => ({
657
+ hookName: entry.config.name,
658
+ triggerUnitType: entry.triggerUnitType,
659
+ triggerUnitId: entry.triggerUnitId,
660
+ forceRun: entry.forceRun,
661
+ })),
306
662
  savedAt: new Date().toISOString(),
307
663
  };
308
664
  try {
@@ -315,7 +671,7 @@ export class RuleRegistry {
315
671
  logWarning("registry", `failed to persist hook state: ${e.message}`);
316
672
  }
317
673
  }
318
- /** Restore hook cycle counts from disk after a crash/restart. */
674
+ /** Restore hook state from disk after a crash/restart. */
319
675
  restoreState(basePath) {
320
676
  try {
321
677
  const filePath = this._hookStatePath(basePath);
@@ -331,6 +687,24 @@ export class RuleRegistry {
331
687
  }
332
688
  }
333
689
  }
690
+ this.activeHook = state.activeHook && typeof state.activeHook === "object"
691
+ ? { ...state.activeHook }
692
+ : null;
693
+ this.hookQueue = [];
694
+ if (Array.isArray(state.hookQueue)) {
695
+ const hooks = resolvePostUnitHooks(basePath);
696
+ for (const entry of state.hookQueue) {
697
+ const config = hooks.find(h => h.name === entry.hookName);
698
+ if (config) {
699
+ this.hookQueue.push({
700
+ config,
701
+ triggerUnitType: entry.triggerUnitType,
702
+ triggerUnitId: entry.triggerUnitId,
703
+ forceRun: entry.forceRun,
704
+ });
705
+ }
706
+ }
707
+ }
334
708
  }
335
709
  catch (e) {
336
710
  logWarning("registry", `failed to restore hook state: ${e.message}`);
@@ -341,7 +715,7 @@ export class RuleRegistry {
341
715
  try {
342
716
  const filePath = this._hookStatePath(basePath);
343
717
  if (existsSync(filePath)) {
344
- writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, savedAt: new Date().toISOString() }, null, 2), "utf-8");
718
+ writeFileSync(filePath, JSON.stringify({ cycleCounts: {}, activeHook: null, hookQueue: [], savedAt: new Date().toISOString() }, null, 2), "utf-8");
345
719
  }
346
720
  }
347
721
  catch (e) {
@@ -365,6 +739,7 @@ export class RuleRegistry {
365
739
  type: "post",
366
740
  enabled: hook.enabled !== false,
367
741
  targets: hook.after,
742
+ criticality: hook.criticality ?? "advisory",
368
743
  activeCycles,
369
744
  });
370
745
  }
@@ -436,9 +811,10 @@ export class RuleRegistry {
436
811
  lines.push("Post-Unit Hooks (run after unit completes):");
437
812
  for (const hook of postHooks) {
438
813
  const status = hook.enabled ? "enabled" : "disabled";
814
+ const criticality = hook.criticality ?? "advisory";
439
815
  const cycles = Object.keys(hook.activeCycles).length;
440
816
  const cycleInfo = cycles > 0 ? ` (${cycles} active cycle${cycles === 1 ? "" : "s"})` : "";
441
- lines.push(` ${hook.name} [${status}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
817
+ lines.push(` ${hook.name} [${status}, ${criticality}] → after: ${hook.targets.join(", ")}${cycleInfo}`);
442
818
  }
443
819
  lines.push("");
444
820
  }
@@ -16,6 +16,9 @@ const DESTRUCTIVE_PATTERNS = [
16
16
  { pattern: /\btruncate\s+table\b/i, label: "SQL truncate" },
17
17
  { pattern: /\bchmod\s+777\b/, label: "world-writable permissions" },
18
18
  { pattern: /\bcurl\s.*\|\s*(bash|sh|zsh)\b/, label: "pipe to shell" },
19
+ { pattern: /\bterra(form|grunt)\s+(apply|destroy)/i, label: "IaC apply/destroy" },
20
+ { pattern: /\baws\s+\w+\s+(delete|create|put|remove|terminate)\b/i, label: "AWS mutation" },
21
+ { pattern: /\bkubectl\s+(delete|apply)\b/i, label: "kubectl mutation" },
19
22
  ];
20
23
  /**
21
24
  * Classify a bash command for destructive operations.
@@ -41,6 +41,16 @@ function tokenizeSkillContext(...parts) {
41
41
  }
42
42
  return tokens;
43
43
  }
44
+ function tokenizeUnitType(unitType) {
45
+ const tokens = new Set();
46
+ const value = unitType?.trim().toLowerCase();
47
+ if (!value)
48
+ return tokens;
49
+ tokens.add(value);
50
+ tokens.add(value.replace(/[-_]+/g, " "));
51
+ tokens.add(value.replace(/[-_\s]+/g, ""));
52
+ return tokens;
53
+ }
44
54
  function skillMatchesContext(skill, contextTokens) {
45
55
  const haystacks = [
46
56
  skill.name.toLowerCase(),
@@ -63,13 +73,20 @@ function ruleMatchesContext(when, contextTokens) {
63
73
  const whenTokens = tokenizeSkillContext(when);
64
74
  return [...whenTokens].some(token => contextTokens.has(token) || [...contextTokens].some(ctx => ctx.includes(token) || token.includes(ctx)));
65
75
  }
66
- function resolveSkillRuleMatches(prefs, contextTokens, base) {
76
+ function ruleMatchesUnitType(when, unitType) {
77
+ if (!unitType)
78
+ return false;
79
+ const whenTokens = tokenizeSkillContext(when);
80
+ const unitTokens = tokenizeUnitType(unitType);
81
+ return [...unitTokens].some(token => whenTokens.has(token));
82
+ }
83
+ function resolveSkillRuleMatches(prefs, contextTokens, base, unitType) {
67
84
  if (!prefs?.skill_rules?.length)
68
85
  return { include: [], avoid: [] };
69
86
  const include = [];
70
87
  const avoid = [];
71
88
  for (const rule of prefs.skill_rules) {
72
- if (!ruleMatchesContext(rule.when, contextTokens))
89
+ if (!ruleMatchesContext(rule.when, contextTokens) && !ruleMatchesUnitType(rule.when, unitType))
73
90
  continue;
74
91
  include.push(...resolvePreferenceSkillNames([...(rule.use ?? []), ...(rule.prefer ?? [])], base));
75
92
  avoid.push(...resolvePreferenceSkillNames(rule.avoid ?? [], base));
@@ -141,7 +158,7 @@ export function buildSkillActivationBlock(params) {
141
158
  for (const name of resolvePreferenceSkillNames(prefs?.always_use_skills ?? [], params.base)) {
142
159
  matched.add(name);
143
160
  }
144
- const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base);
161
+ const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base, params.unitType);
145
162
  for (const name of ruleMatches.include)
146
163
  matched.add(name);
147
164
  for (const name of ruleMatches.avoid)
@@ -313,7 +313,8 @@ export function repairArtifactDbDrift(record, ctx) {
313
313
  throw new Error(`Artifact/DB status drift in ${record.milestoneId}` +
314
314
  `${record.sliceId ? `/${record.sliceId}` : ""}` +
315
315
  `${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
316
- "Runtime will not silently import completion artifacts into DB state; run explicit recovery/repair after review.");
316
+ "Runtime will not silently import completion artifacts into DB state. " +
317
+ "Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB.");
317
318
  }
318
319
  export function describeArtifactDbDriftBlocker(record) {
319
320
  if (record.kind === "disk-slice-id-divergence") {
@@ -329,7 +330,8 @@ export function describeArtifactDbDriftBlocker(record) {
329
330
  return (`Artifact/DB status drift in ${record.milestoneId}` +
330
331
  `${record.sliceId ? `/${record.sliceId}` : ""}` +
331
332
  `${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
332
- "Runtime will not silently import completion artifacts into DB state; run explicit recovery/repair after review.");
333
+ "Runtime will not silently import completion artifacts into DB state. " +
334
+ "Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB.");
333
335
  }
334
336
  export const diskSliceIdDivergenceHandler = {
335
337
  kind: "disk-slice-id-divergence",