@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 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 (259) 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/gsd/auto/orchestrator.js +0 -1
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +59 -22
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  16. package/dist/resources/extensions/gsd/auto.js +9 -2
  17. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -5
  19. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  20. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  21. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  22. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  25. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  26. package/dist/resources/extensions/gsd/escalation.js +4 -4
  27. package/dist/resources/extensions/gsd/forensics.js +74 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  29. package/dist/resources/extensions/gsd/guided-flow.js +29 -68
  30. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  31. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  32. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  33. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  34. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  35. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  36. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  37. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  38. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  40. package/dist/resources/extensions/gsd/prompts/run-uat.md +40 -22
  41. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  42. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  43. package/dist/resources/extensions/gsd/state.js +2 -2
  44. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  47. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  48. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +51 -14
  49. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  50. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  51. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  52. package/dist/rtk.d.ts +7 -1
  53. package/dist/rtk.js +27 -11
  54. package/dist/update-check.d.ts +15 -1
  55. package/dist/update-check.js +87 -12
  56. package/dist/update-cmd.d.ts +1 -0
  57. package/dist/update-cmd.js +53 -2
  58. package/dist/web/standalone/.next/BUILD_ID +1 -1
  59. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  60. package/dist/web/standalone/.next/build-manifest.json +2 -2
  61. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  62. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/index.html +1 -1
  80. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  87. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  88. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  90. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  91. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  92. package/package.json +4 -2
  93. package/packages/cloud-mcp-gateway/package.json +2 -2
  94. package/packages/contracts/package.json +1 -1
  95. package/packages/daemon/package.json +4 -4
  96. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  97. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  98. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  99. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  100. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  101. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  102. package/packages/gsd-agent-core/dist/index.js +1 -0
  103. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  104. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  105. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  106. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  107. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  108. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  109. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  111. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  112. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  113. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  114. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  115. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  116. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  117. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  118. package/packages/gsd-agent-core/package.json +6 -6
  119. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  120. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  121. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  123. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  124. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  129. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  132. package/packages/gsd-agent-modes/package.json +7 -7
  133. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  134. package/packages/mcp-server/dist/remote-questions.js +23 -9
  135. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  136. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  137. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  138. package/packages/mcp-server/package.json +3 -3
  139. package/packages/native/package.json +1 -1
  140. package/packages/pi-agent-core/dist/agent-loop.js +38 -0
  141. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  142. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  143. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  144. package/packages/pi-agent-core/dist/agent.js +2 -0
  145. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  146. package/packages/pi-agent-core/dist/types.d.ts +3 -0
  147. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  148. package/packages/pi-agent-core/dist/types.js.map +1 -1
  149. package/packages/pi-agent-core/package.json +1 -1
  150. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  151. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  152. package/packages/pi-ai/dist/api-registry.js +23 -0
  153. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  154. package/packages/pi-ai/dist/models.generated.d.ts +68 -0
  155. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  156. package/packages/pi-ai/dist/models.generated.js +72 -4
  157. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  158. package/packages/pi-ai/dist/stream.js +6 -6
  159. package/packages/pi-ai/dist/stream.js.map +1 -1
  160. package/packages/pi-ai/package.json +1 -1
  161. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  163. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  164. package/packages/pi-coding-agent/package.json +7 -7
  165. package/packages/pi-tui/package.json +1 -1
  166. package/packages/rpc-client/package.json +2 -2
  167. package/pkg/package.json +1 -1
  168. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  169. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  170. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  171. package/src/resources/extensions/browser-tools/index.ts +60 -9
  172. package/src/resources/extensions/browser-tools/package.json +5 -1
  173. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  174. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  175. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  176. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  177. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  178. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  179. package/src/resources/extensions/gsd/auto-prompts.ts +93 -15
  180. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  181. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  182. package/src/resources/extensions/gsd/auto.ts +12 -2
  183. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -4
  184. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +9 -5
  185. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  186. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  187. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  188. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  189. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  190. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  191. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  192. package/src/resources/extensions/gsd/escalation.ts +4 -4
  193. package/src/resources/extensions/gsd/forensics.ts +99 -5
  194. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  195. package/src/resources/extensions/gsd/guided-flow.ts +90 -82
  196. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  197. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  198. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  199. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  200. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  201. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  202. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  203. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  204. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  205. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  206. package/src/resources/extensions/gsd/prompts/run-uat.md +40 -22
  207. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  208. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  209. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  210. package/src/resources/extensions/gsd/state.ts +2 -2
  211. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  212. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  213. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  214. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  215. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  216. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  217. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  218. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  219. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  220. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  221. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  222. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  223. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  224. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  225. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  226. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  227. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  228. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  229. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  230. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  231. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  232. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  233. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  234. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  235. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  236. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  237. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  238. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +43 -1
  239. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  240. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  241. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  242. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  243. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  244. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  245. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  246. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  247. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  248. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  249. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +130 -0
  250. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  251. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  252. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  253. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +63 -15
  254. package/src/resources/extensions/gsd/types.ts +69 -5
  255. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  256. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  257. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  258. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
  259. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
@@ -227,21 +227,22 @@ async function dispatchDiscussForNextMilestoneWithBacklog(
227
227
  nextId: string,
228
228
  ): Promise<void> {
229
229
  const backlogContext = buildRequirementsBacklogDiscussContext(nextId);
230
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
231
230
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
232
- const basePrompt = loadPrompt("guided-discuss-milestone", {
233
- workingDirectory: basePath,
234
- milestoneId: nextId,
235
- milestoneTitle: `New milestone ${nextId}`,
236
- inlinedTemplates: discussMilestoneTemplates,
231
+ const basePrompt = await buildDiscussMilestonePrompt(
232
+ nextId,
233
+ `New milestone ${nextId}`,
234
+ basePath,
237
235
  structuredQuestionsAvailable,
238
- commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
239
- fastPathInstruction: [
240
- "> **Requirements backlog active.**",
241
- "> Map unmapped active requirements to this milestone before finalizing context.",
242
- "> Confirm ownership with the user when scope is ambiguous.",
243
- ].join("\n"),
244
- });
236
+ {
237
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
238
+ includeContextMode: false,
239
+ fastPathInstruction: [
240
+ "> **Requirements backlog active.**",
241
+ "> Map unmapped active requirements to this milestone before finalizing context.",
242
+ "> Confirm ownership with the user when scope is ambiguous.",
243
+ ].join("\n"),
244
+ },
245
+ );
245
246
  const prompt = backlogContext ? `${basePrompt}\n\n${backlogContext}` : basePrompt;
246
247
  await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
247
248
  }
@@ -1371,6 +1372,7 @@ async function buildDiscussPreparationContext(
1371
1372
  ctx: ExtensionCommandContext,
1372
1373
  basePath: string,
1373
1374
  mode: "greenfield" | "milestone" = "greenfield",
1375
+ skipPriorContext = false,
1374
1376
  ): Promise<string> {
1375
1377
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
1376
1378
  if (prefs.discuss_preparation === false) return "";
@@ -1388,7 +1390,7 @@ async function buildDiscussPreparationContext(
1388
1390
  const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
1389
1391
  const parts: string[] = [];
1390
1392
  if (codebaseBrief) parts.push(`### Codebase Brief\n\n${codebaseBrief}`);
1391
- if (priorContextBrief) parts.push(`### Prior Context Brief\n\n${priorContextBrief}`);
1393
+ if (priorContextBrief && !skipPriorContext) parts.push(`### Prior Context Brief\n\n${priorContextBrief}`);
1392
1394
  if (parts.length === 0) return "";
1393
1395
 
1394
1396
  const guidance = mode === "milestone"
@@ -1441,18 +1443,18 @@ async function dispatchNewMilestoneDiscuss(
1441
1443
  return;
1442
1444
  }
1443
1445
 
1444
- const preparationContext = await buildDiscussPreparationContext(ctx, basePath, "milestone");
1445
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1446
+ const preparationContext = await buildDiscussPreparationContext(ctx, basePath, "milestone", true);
1446
1447
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1447
- let prompt = loadPrompt("guided-discuss-milestone", {
1448
- workingDirectory: basePath,
1449
- milestoneId: nextId,
1450
- milestoneTitle: `New milestone ${nextId}`,
1451
- inlinedTemplates: discussMilestoneTemplates,
1448
+ let prompt = await buildDiscussMilestonePrompt(
1449
+ nextId,
1450
+ `New milestone ${nextId}`,
1451
+ basePath,
1452
1452
  structuredQuestionsAvailable,
1453
- commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
1454
- fastPathInstruction: "",
1455
- });
1453
+ {
1454
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
1455
+ includeContextMode: false,
1456
+ },
1457
+ );
1456
1458
  if (preparationContext) prompt += preparationContext;
1457
1459
  await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1458
1460
  }
@@ -1759,9 +1761,6 @@ export async function showDiscuss(
1759
1761
  // Special case: milestone is in needs-discussion phase (has CONTEXT-DRAFT.md but no roadmap yet).
1760
1762
  // Route to the draft discussion flow instead of erroring — the discussion IS how the roadmap gets created.
1761
1763
  if (state.phase === "needs-discussion") {
1762
- const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
1763
- const draftContent = draftFile ? await loadFile(draftFile) : null;
1764
-
1765
1764
  const choice = await showNextAction(ctx, {
1766
1765
  title: `GSD — ${mid}: ${milestoneTitle}`,
1767
1766
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
@@ -1787,29 +1786,34 @@ export async function showDiscuss(
1787
1786
  });
1788
1787
 
1789
1788
  if (choice === "discuss_draft") {
1790
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1791
1789
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1792
- const basePrompt = loadPrompt("guided-discuss-milestone", {
1793
- workingDirectory: basePath,
1794
- milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1795
- commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1796
- fastPathInstruction: "",
1797
- });
1798
- const seed = draftContent
1799
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
1800
- : basePrompt;
1790
+ const seed = await buildDiscussMilestonePrompt(
1791
+ mid,
1792
+ milestoneTitle,
1793
+ basePath,
1794
+ structuredQuestionsAvailable,
1795
+ {
1796
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1797
+ includeContextMode: false,
1798
+ },
1799
+ );
1801
1800
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: true });
1802
1801
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1803
1802
  } else if (choice === "discuss_fresh") {
1804
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1805
1803
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1804
+ const prompt = await buildDiscussMilestonePrompt(
1805
+ mid,
1806
+ milestoneTitle,
1807
+ basePath,
1808
+ structuredQuestionsAvailable,
1809
+ {
1810
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1811
+ includeContextMode: false,
1812
+ includeDraftSeed: false,
1813
+ },
1814
+ );
1806
1815
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: true });
1807
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1808
- workingDirectory: basePath,
1809
- milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1810
- commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1811
- fastPathInstruction: "",
1812
- }), "gsd-discuss", ctx, "discuss-milestone", { basePath });
1816
+ await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1813
1817
  } else if (choice === "skip_milestone") {
1814
1818
  const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
1815
1819
  await ensureDbOpen(basePath);
@@ -2068,20 +2072,18 @@ async function dispatchDiscussForMilestone(
2068
2072
  "> Ask only questions where the answer would materially change scope.",
2069
2073
  ].join("\n")
2070
2074
  : "";
2071
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2072
2075
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2073
- const basePrompt = loadPrompt("guided-discuss-milestone", {
2074
- workingDirectory: basePath,
2075
- milestoneId: mid,
2076
+ const prompt = await buildDiscussMilestonePrompt(
2077
+ mid,
2076
2078
  milestoneTitle,
2077
- inlinedTemplates: discussMilestoneTemplates,
2079
+ basePath,
2078
2080
  structuredQuestionsAvailable,
2079
- commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
2080
- fastPathInstruction,
2081
- });
2082
- const prompt = draftContent
2083
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
2084
- : basePrompt;
2081
+ {
2082
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
2083
+ includeContextMode: false,
2084
+ fastPathInstruction,
2085
+ },
2086
+ );
2085
2087
  await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
2086
2088
  }
2087
2089
 
@@ -2612,9 +2614,6 @@ export async function showSmartEntry(
2612
2614
 
2613
2615
  // ── Draft milestone — needs discussion before planning ────────────────
2614
2616
  if (state.phase === "needs-discussion") {
2615
- const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
2616
- const draftContent = draftFile ? await loadFile(draftFile) : null;
2617
-
2618
2617
  const choice = await showNextAction(ctx, {
2619
2618
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
2620
2619
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
@@ -2640,29 +2639,34 @@ export async function showSmartEntry(
2640
2639
  });
2641
2640
 
2642
2641
  if (choice === "discuss_draft") {
2643
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2644
2642
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2645
- const basePrompt = loadPrompt("guided-discuss-milestone", {
2646
- workingDirectory: basePath,
2647
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2648
- commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2649
- fastPathInstruction: "",
2650
- });
2651
- const seed = draftContent
2652
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
2653
- : basePrompt;
2643
+ const seed = await buildDiscussMilestonePrompt(
2644
+ milestoneId,
2645
+ milestoneTitle,
2646
+ basePath,
2647
+ structuredQuestionsAvailable,
2648
+ {
2649
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2650
+ includeContextMode: false,
2651
+ },
2652
+ );
2654
2653
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2655
2654
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
2656
2655
  } else if (choice === "discuss_fresh") {
2657
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2658
2656
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2657
+ const prompt = await buildDiscussMilestonePrompt(
2658
+ milestoneId,
2659
+ milestoneTitle,
2660
+ basePath,
2661
+ structuredQuestionsAvailable,
2662
+ {
2663
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2664
+ includeContextMode: false,
2665
+ includeDraftSeed: false,
2666
+ },
2667
+ );
2659
2668
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2660
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
2661
- workingDirectory: basePath,
2662
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2663
- commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2664
- fastPathInstruction: "",
2665
- }), "gsd-discuss", ctx, "discuss-milestone", { basePath });
2669
+ await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
2666
2670
  } else if (choice === "skip_milestone") {
2667
2671
  const milestoneIds = findMilestoneIds(basePath);
2668
2672
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
@@ -2783,14 +2787,18 @@ export async function showSmartEntry(
2783
2787
  { basePath },
2784
2788
  );
2785
2789
  } else if (choice === "discuss") {
2786
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2787
2790
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2788
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
2789
- workingDirectory: basePath,
2790
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2791
- commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2792
- fastPathInstruction: "",
2793
- }), "gsd-run", ctx, "discuss-milestone", { basePath });
2791
+ const prompt = await buildDiscussMilestonePrompt(
2792
+ milestoneId,
2793
+ milestoneTitle,
2794
+ basePath,
2795
+ structuredQuestionsAvailable,
2796
+ {
2797
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2798
+ includeContextMode: false,
2799
+ },
2800
+ );
2801
+ await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone", { basePath });
2794
2802
  } else if (choice === "skip_milestone") {
2795
2803
  const milestoneIds = findMilestoneIds(basePath);
2796
2804
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
@@ -1,14 +1,17 @@
1
- import { createHash } from "node:crypto";
2
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { basename, resolve } from "node:path";
2
+ import { resolve } from "node:path";
5
3
  import { fileURLToPath } from "node:url";
6
4
 
5
+ import {
6
+ GSD_BROWSER_MCP_SERVER_NAME,
7
+ resolveBundledGsdBrowserCliPath,
8
+ resolveGsdBrowserMcpLaunchConfig,
9
+ } from "../shared/gsd-browser-cli.js";
7
10
  import { assertSafeDirectory } from "./validate-directory.js";
8
11
  import { detectWorkflowMcpLaunchConfig } from "./workflow-mcp.js";
9
12
 
10
13
  export const GSD_WORKFLOW_MCP_SERVER_NAME = "gsd-workflow";
11
- export const GSD_BROWSER_MCP_SERVER_NAME = "gsd-browser";
14
+ export { GSD_BROWSER_MCP_SERVER_NAME, resolveBundledGsdBrowserCliPath };
12
15
 
13
16
  export interface ProjectMcpServerConfig {
14
17
  command?: string;
@@ -59,31 +62,6 @@ export function resolveBundledGsdCliPath(env: NodeJS.ProcessEnv = process.env):
59
62
  return null;
60
63
  }
61
64
 
62
- export function resolveBundledGsdBrowserCliPath(env: NodeJS.ProcessEnv = process.env): string | null {
63
- const explicit = env.GSD_BROWSER_CLI_PATH?.trim() || env.GSD_BROWSER_BIN_PATH?.trim();
64
- if (explicit) return explicit;
65
-
66
- try {
67
- const requireFromHere = createRequire(import.meta.url);
68
- const packageJsonPath = requireFromHere.resolve("@opengsd/gsd-browser/package.json");
69
- const candidate = resolve(packageJsonPath, "..", "bin", "gsd-browser");
70
- if (existsSync(candidate)) return candidate;
71
- } catch {
72
- // Fall through to path candidates for source/dist layouts.
73
- }
74
-
75
- const candidates = [
76
- resolve(fileURLToPath(new URL("../../../../node_modules/@opengsd/gsd-browser/bin/gsd-browser", import.meta.url))),
77
- resolve(fileURLToPath(new URL("../../../../node_modules/.bin/gsd-browser", import.meta.url))),
78
- ];
79
-
80
- for (const candidate of candidates) {
81
- if (existsSync(candidate)) return candidate;
82
- }
83
-
84
- return null;
85
- }
86
-
87
65
  export function buildProjectWorkflowMcpServerConfig(
88
66
  projectRoot: string,
89
67
  env: NodeJS.ProcessEnv = process.env,
@@ -119,31 +97,12 @@ function buildProjectWorkflowMcpServerSpec(
119
97
  };
120
98
  }
121
99
 
122
- function parseJsonEnv<T>(env: NodeJS.ProcessEnv, name: string): T | undefined {
123
- const raw = env[name];
124
- if (!raw) return undefined;
125
- try {
126
- return JSON.parse(raw) as T;
127
- } catch {
128
- throw new Error(`Invalid JSON in ${name}`);
129
- }
130
- }
131
-
132
100
  function isEnvDisabled(value: string | undefined): boolean {
133
101
  if (!value) return false;
134
102
  const normalized = value.trim().toLowerCase();
135
103
  return normalized === "0" || normalized === "false" || normalized === "off";
136
104
  }
137
105
 
138
- function buildBrowserSessionName(projectRoot: string): string {
139
- const resolvedProjectRoot = resolve(projectRoot);
140
- const base = basename(resolvedProjectRoot)
141
- .replace(/[^a-zA-Z0-9._-]+/g, "-")
142
- .replace(/^-+|-+$/g, "") || "project";
143
- const hash = createHash("sha1").update(resolvedProjectRoot).digest("hex").slice(0, 8);
144
- return `gsd-${base}-${hash}`;
145
- }
146
-
147
106
  export function buildProjectBrowserMcpServerConfig(
148
107
  projectRoot: string,
149
108
  env: NodeJS.ProcessEnv = process.env,
@@ -157,39 +116,15 @@ function buildProjectBrowserMcpServerSpec(
157
116
  ): ProjectMcpServerSpec | null {
158
117
  if (isEnvDisabled(env.GSD_BROWSER_MCP_ENABLED)) return null;
159
118
 
160
- const resolvedProjectRoot = resolve(projectRoot);
161
- const serverName = env.GSD_BROWSER_MCP_NAME?.trim() || GSD_BROWSER_MCP_SERVER_NAME;
162
- const explicitArgs = parseJsonEnv<unknown>(env, "GSD_BROWSER_MCP_ARGS");
163
- const explicitEnv = parseJsonEnv<Record<string, string>>(env, "GSD_BROWSER_MCP_ENV");
164
- const explicitCommand = env.GSD_BROWSER_MCP_COMMAND?.trim();
165
- const explicitCliPath = env.GSD_BROWSER_CLI_PATH?.trim() || env.GSD_BROWSER_BIN_PATH?.trim();
166
- const bundledCliPath = !explicitCommand && !explicitCliPath ? resolveBundledGsdBrowserCliPath(env) : null;
167
- const command =
168
- explicitCommand
169
- || explicitCliPath
170
- || (bundledCliPath ? process.execPath : undefined)
171
- || "gsd-browser";
172
- const args = Array.isArray(explicitArgs) && explicitArgs.length > 0
173
- ? explicitArgs.map(String)
174
- : [
175
- ...(bundledCliPath ? [bundledCliPath] : []),
176
- "mcp",
177
- "--session",
178
- buildBrowserSessionName(resolvedProjectRoot),
179
- "--identity-scope",
180
- "project",
181
- "--identity-project",
182
- resolvedProjectRoot,
183
- ];
184
- const cwd = env.GSD_BROWSER_MCP_CWD?.trim() || resolvedProjectRoot;
119
+ const launch = resolveGsdBrowserMcpLaunchConfig(projectRoot, env);
185
120
 
186
121
  return {
187
- serverName,
122
+ serverName: launch.serverName,
188
123
  server: {
189
- command,
190
- args,
191
- cwd,
192
- ...(explicitEnv ? { env: explicitEnv } : {}),
124
+ command: launch.command,
125
+ args: launch.args,
126
+ cwd: launch.cwd,
127
+ ...(launch.env ? { env: launch.env } : {}),
193
128
  },
194
129
  };
195
130
  }
@@ -777,7 +777,10 @@ export function decayStaleMemories(thresholdUnits = 20): string[] {
777
777
  const cutoff = row['processed_at'] as string;
778
778
  const affected = adapter.prepare(
779
779
  `SELECT id FROM memories
780
- WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`,
780
+ WHERE superseded_by IS NULL
781
+ AND updated_at < :cutoff
782
+ AND confidence > 0.1
783
+ AND (structured_fields IS NULL OR structured_fields NOT LIKE '%"sourceDecisionId"%')`,
781
784
  ).all({ ':cutoff': cutoff }).map((r) => r['id'] as string);
782
785
 
783
786
  decayMemoriesBefore(cutoff, new Date().toISOString());
@@ -9,6 +9,7 @@ import type {
9
9
  HookDispatchResult,
10
10
  PreDispatchResult,
11
11
  HookStatusEntry,
12
+ PostUnitGateBlock,
12
13
  } from "./types.js";
13
14
  import { getOrCreateRegistry, resolveHookArtifactPath } from "./rule-registry.js";
14
15
 
@@ -33,10 +34,22 @@ export function isRetryPending(): boolean {
33
34
  return getOrCreateRegistry().isRetryPending();
34
35
  }
35
36
 
36
- export function consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact: string } | null {
37
+ export function consumeRetryTrigger(): { unitType: string; unitId: string; retryArtifact?: string } | null {
37
38
  return getOrCreateRegistry().consumeRetryTrigger();
38
39
  }
39
40
 
41
+ export function consumeHookFailure(): { hookName: string; unitType: string; unitId: string; reason: string } | null {
42
+ return getOrCreateRegistry().consumeHookFailure();
43
+ }
44
+
45
+ export function isGateBlockPending(): boolean {
46
+ return getOrCreateRegistry().isGateBlockPending();
47
+ }
48
+
49
+ export function consumeGateBlock(): PostUnitGateBlock | null {
50
+ return getOrCreateRegistry().consumeGateBlock();
51
+ }
52
+
40
53
  export function resetHookState(): void {
41
54
  getOrCreateRegistry().resetState();
42
55
  }
@@ -29,6 +29,14 @@ const VALID_UOK_TURN_ACTIONS = new Set<"commit" | "snapshot" | "status-only">([
29
29
  "snapshot",
30
30
  "status-only",
31
31
  ]);
32
+ const VALID_POST_UNIT_HOOK_CRITICALITIES = new Set(["advisory", "blocking"]);
33
+ const VALID_POST_UNIT_HOOK_ON_BLOCK_ACTIONS = new Set([
34
+ "retry-unit",
35
+ "retry-task",
36
+ "queue-task",
37
+ "queue-slice",
38
+ "pause",
39
+ ]);
32
40
 
33
41
  export function validatePreferences(preferences: GSDPreferences): {
34
42
  preferences: GSDPreferences;
@@ -486,9 +494,37 @@ export function validatePreferences(preferences: GSDPreferences): {
486
494
  if (typeof hook.artifact === "string" && hook.artifact.trim()) {
487
495
  validHook.artifact = hook.artifact.trim();
488
496
  }
497
+ if (hook.criticality !== undefined) {
498
+ const criticality = typeof hook.criticality === "string" ? hook.criticality.trim() : "";
499
+ if (VALID_POST_UNIT_HOOK_CRITICALITIES.has(criticality)) {
500
+ validHook.criticality = criticality as PostUnitHookConfig["criticality"];
501
+ } else {
502
+ errors.push(`post_unit_hooks "${name}" invalid criticality: ${String(hook.criticality)}`);
503
+ }
504
+ }
489
505
  if (typeof hook.retry_on === "string" && hook.retry_on.trim()) {
490
506
  validHook.retry_on = hook.retry_on.trim();
491
507
  }
508
+ if (hook.on_block !== undefined) {
509
+ if (!hook.on_block || typeof hook.on_block !== "object") {
510
+ errors.push(`post_unit_hooks "${name}" on_block must be an object`);
511
+ } else {
512
+ const onBlock = hook.on_block as unknown as Record<string, unknown>;
513
+ const action = typeof onBlock.action === "string" ? onBlock.action.trim() : "";
514
+ if (!VALID_POST_UNIT_HOOK_ON_BLOCK_ACTIONS.has(action)) {
515
+ errors.push(`post_unit_hooks "${name}" invalid on_block action: ${String(onBlock.action)}`);
516
+ } else {
517
+ validHook.on_block = { action: action as NonNullable<PostUnitHookConfig["on_block"]>["action"] };
518
+ if (typeof onBlock.artifact === "string" && onBlock.artifact.trim()) {
519
+ validHook.on_block.artifact = onBlock.artifact.trim();
520
+ }
521
+ }
522
+ }
523
+ }
524
+ if (validHook.criticality === "blocking" && !validHook.artifact) {
525
+ errors.push(`post_unit_hooks "${name}" criticality blocking requires artifact`);
526
+ continue;
527
+ }
492
528
  if (typeof hook.agent === "string" && hook.agent.trim()) {
493
529
  validHook.agent = hook.agent.trim();
494
530
  }
@@ -33,6 +33,10 @@ function hasRequiredExtensionAssets(rootDir: string, exists: ExistsFn = existsSy
33
33
  );
34
34
  }
35
35
 
36
+ function isSourceExtensionDir(moduleDir: string): boolean {
37
+ return moduleDir.replaceAll("\\", "/").endsWith("/src/resources/extensions/gsd");
38
+ }
39
+
36
40
  export function resolveExtensionDirFromCandidates(
37
41
  moduleDir: string,
38
42
  agentGsdDir: string,
@@ -41,6 +45,10 @@ export function resolveExtensionDirFromCandidates(
41
45
  const moduleUsable = hasRequiredExtensionAssets(moduleDir, exists);
42
46
  const agentUsable = hasRequiredExtensionAssets(agentGsdDir, exists);
43
47
 
48
+ // Source checkouts must use their own prompt tree. Otherwise local tests and
49
+ // dev runs can silently render stale prompts from ~/.gsd/agent/extensions/gsd.
50
+ if (moduleUsable && isSourceExtensionDir(moduleDir)) return moduleDir;
51
+
44
52
  // Prefer the user-local extension tree when both are valid. This avoids
45
53
  // leaking npm/global-install paths into prompts on Windows.
46
54
  if (agentUsable) return agentGsdDir;
@@ -12,6 +12,8 @@ Debug GSD itself. Trace the symptom to root cause in current source and produce
12
12
 
13
13
  GSD extension source: `{{gsdSourceDir}}`
14
14
 
15
+ {{toolingSection}}
16
+
15
17
  ### Source Map by Domain
16
18
 
17
19
  | Domain | Files |
@@ -101,7 +103,7 @@ Then **offer GitHub issue creation**: "Would you like me to create a GitHub issu
101
103
 
102
104
  **CRITICAL:** The `github_issues` tool targets only the current user's repository and has no `repo` parameter. Use `gh issue create --repo open-gsd/gsd-pi` via `bash`. Do NOT use the `github_issues` tool.
103
105
 
104
- If yes, create using the `bash` tool:
106
+ If yes and `bash` is available, create using the `bash` tool:
105
107
 
106
108
  ```bash
107
109
  ISSUE_BODY_FILE="${TMPDIR:-${TEMP:-${TMP:-.}}}/gsd-forensic-issue.md"
@@ -142,6 +144,64 @@ TYPE_ID=$(gh api graphql -f query='{ repository(owner:"open-gsd",name:"gsd-pi")
142
144
  gh api graphql -f query='mutation { updateIssue(input:{id:"'"$ISSUE_ID"'",issueTypeId:"'"$TYPE_ID"'"}) { issue { number } } }'
143
145
  ```
144
146
 
147
+ If `bash` is unavailable, do not attempt `bash`, `write`, or `github_issues` tool calls. Instead, provide exactly one paste-once shell script for the user to run locally and say that the live duplicate check / issue creation must be run by the user:
148
+
149
+ ```bash
150
+ KEYWORDS="..."
151
+ echo "Searching closed issues for possible duplicates..."
152
+ gh issue list --repo open-gsd/gsd-pi --state closed --search "$KEYWORDS" --limit 20
153
+
154
+ echo "Searching open PRs for possible fixes..."
155
+ gh pr list --repo open-gsd/gsd-pi --state open --search "$KEYWORDS" --limit 10
156
+
157
+ echo "Searching merged PRs for possible fixes..."
158
+ gh pr list --repo open-gsd/gsd-pi --state merged --search "$KEYWORDS" --limit 10
159
+
160
+ read -r -p "Review the duplicate search above. Continue filing a new issue? [y/N] " SHOULD_FILE
161
+ case "$SHOULD_FILE" in
162
+ y|Y|yes|YES) ;;
163
+ *) echo "Issue filing aborted."; exit 0 ;;
164
+ esac
165
+
166
+ ISSUE_BODY_FILE="${TMPDIR:-${TEMP:-${TMP:-.}}}/gsd-forensic-issue.md"
167
+ cat > "$ISSUE_BODY_FILE" << 'GSD_ISSUE_BODY'
168
+ ## Problem
169
+ [1-2 sentence summary]
170
+
171
+ ## Root Cause
172
+ [Specific file:line in GSD source, with code snippet showing the bug]
173
+
174
+ ## Expected Behavior
175
+ [What the code should do instead — concrete fix suggestion]
176
+
177
+ ## Environment
178
+ - GSD version: [from report]
179
+ - Model: [from report]
180
+ - Unit: [type/id that failed]
181
+
182
+ ## Reproduction Context
183
+ [Phase, milestone, slice, what was happening when it failed]
184
+
185
+ ## Forensic Evidence
186
+ [Key anomalies, error traces, relevant tool call sequences from the report]
187
+
188
+ ---
189
+ *Auto-generated by `/gsd forensics`*
190
+ GSD_ISSUE_BODY
191
+
192
+ ISSUE_URL=$(gh issue create --repo open-gsd/gsd-pi \
193
+ --title "..." \
194
+ --label "auto-generated" \
195
+ --body-file "$ISSUE_BODY_FILE")
196
+ rm -f "$ISSUE_BODY_FILE"
197
+
198
+ ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
199
+ ISSUE_ID=$(gh api graphql -f query='{ repository(owner:"open-gsd",name:"gsd-pi") { issue(number:'"$ISSUE_NUM"') { id } } }' --jq '.data.repository.issue.id')
200
+ TYPE_ID=$(gh api graphql -f query='{ repository(owner:"open-gsd",name:"gsd-pi") { issueTypes(first:20) { nodes { id name } } } }' --jq '.data.repository.issueTypes.nodes[] | select(.name=="Bug") | .id')
201
+ gh api graphql -f query='mutation { updateIssue(input:{id:"'"$ISSUE_ID"'",issueTypeId:"'"$TYPE_ID"'"}) { issue { number } } }'
202
+ echo "$ISSUE_URL"
203
+ ```
204
+
145
205
  ### Redaction Rules (CRITICAL)
146
206
 
147
207
  Before creating the issue, you MUST:
@@ -8,6 +8,8 @@
8
8
 
9
9
  You are evaluating **quality gates in parallel** for this slice. Each gate is an independent question that must be answered before task execution begins. Use the `subagent` tool to dispatch all gate evaluations simultaneously.
10
10
 
11
+ **Tool call format:** Call `subagent` with `tasks: [...]` as a **native JSON array** — one object per gate. Do NOT JSON.stringify the array into a string; the tool validates that `tasks` is an array, and a serialized string will be rejected with "must be array".
12
+
11
13
  ## Slice Plan Context
12
14
 
13
15
  {{slicePlanContent}}
@@ -20,7 +22,7 @@ You are evaluating **quality gates in parallel** for this slice. Each gate is an
20
22
 
21
23
  ## Execution Protocol
22
24
 
23
- 1. **Dispatch all gates** using `subagent` in parallel mode. Each subagent prompt is provided below.
25
+ 1. **Dispatch all gates** using `subagent` in parallel mode. Call `subagent` with `tasks: [{ agent: "tester", task: "<prompt>" }, ...]` — one object per gate. Each subagent prompt is provided below.
24
26
  Pass `tasks` as a **JSON array**, not a string. Example shape:
25
27
 
26
28
  ```json
@@ -12,9 +12,11 @@ You are dispatching parallel research agents for **{{sliceCount}} slices** in mi
12
12
 
13
13
  Dispatch ALL slices simultaneously using the `subagent` tool in **parallel mode**. Each subagent will independently research its slice and write a RESEARCH file.
14
14
 
15
+ **Tool call format:** Call `subagent` with `tasks: [...]` as a **native JSON array** — one object per slice. Do NOT JSON.stringify the array into a string; the tool validates that `tasks` is an array, and a serialized string will be rejected with "must be array".
16
+
15
17
  ## Execution Protocol
16
18
 
17
- 1. Call `subagent` with `tasks: [...]` containing one entry per slice below
19
+ 1. Call `subagent` with `tasks: [{ agent: "scout", task: "<prompt>" }, ...]` containing one entry per slice below
18
20
  2. Wait for ALL subagents to complete
19
21
  3. Verify each slice's RESEARCH file was written (check `.gsd/milestones/{{mid}}/slices/<slice-id>/`)
20
22
  4. If a subagent failed to write its RESEARCH file, retry it **once** individually
@@ -43,7 +43,7 @@ If slice research is inlined, trust its architectural findings, but verify every
43
43
  5. Define slice verification before tasks. Non-trivial slices need real tests or executable assertions; boundary contracts need contract-exercising checks. Tests must not read .gitignore/gitignored paths such as `.gsd/`, `.planning/`, or `.audits/`.
44
44
  6. Include Threat Surface (Q3), Requirement Impact (Q4), proof level, observability, integration closure, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) only where applicable.
45
45
  7. Right-size tasks. Simple slices can be one task; split only when context, ownership, or verification boundaries justify it.
46
- 8. Task `verify` commands must be safe, simple commands. Do not use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`. If multiple checks are needed, create a small test file and run it with `node --test` or a package test script, or use separate simple commands joined only with `&&`.
46
+ 8. Task `verify` commands must be safe, simple commands. Do not use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`. If multiple checks are needed, create a small test file and run it with `node --test` or a package test script, or use separate simple commands joined only with `&&`. For absence checks, verify a pattern does not exist with `! grep -q 'pattern' file` or `! rg -q 'pattern' file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches, and the verification gate treats that as failure.
47
47
  9. Each task needs the exact `gsd_plan_slice.tasks[]` shape: `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. `description` should contain the Why / Do / Done-when narrative. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even when there is only one path (for example, `"inputs": ["src/index.ts"]`, never `"inputs": "src/index.ts"`). Use paths relative to `{{workingDirectory}}`; do not put absolute paths to the original checkout or any directory outside `{{workingDirectory}}` in `files`, `inputs`, `expectedOutput`, or verification commands. **`expectedOutput` must only list files the task actually creates or overwrites on disk.** Do NOT include files the task merely reads, verifies, or tests — those belong only in `inputs`. If a task is a pure verification or test task that produces no new files, `expectedOutput` may be `[]` or limited to test-result artifacts (e.g. a log or assertion output). A file that does not yet exist on disk and is needed as an `input` must be produced by an earlier task's `expectedOutput` — if no prior task creates it, add a task before this one that does.
48
48
  10. Persist with `gsd_plan_slice` using `milestoneId`, `sliceId`, `goal`, optional `successCriteria`/`proofLevel`/`integrationClosure`/`observabilityImpact`, and `tasks`. `gsd_plan_slice` handles task persistence transactionally and renders `{{outputPath}}` plus task plans; do not call `gsd_plan_task`. The DB-backed tool is the canonical write path. Do **not** rely on direct `PLAN.md` writes as the source of truth.
49
49
  11. Self-audit before finishing: goal/demo closure, requirement coverage, deliverable coverage audit (cross-check every file listed in CONTEXT.md `## Scope` / `### In Scope` against task `files` or `expectedOutput`), locked decisions, concrete paths, dependency order, wiring, scope size, proof truthfulness, feature completeness, and quality gates. Quality gates: non-trivial slices/tasks include specific Q3-Q7 coverage where applicable.