@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
@@ -4,7 +4,7 @@
4
4
  "version": "1.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "node --test tests/*.test.mjs"
7
+ "test": "node --import ../gsd/tests/resolve-ts.mjs --experimental-strip-types --test tests/*.test.mjs"
8
8
  },
9
9
  "pi": {
10
10
  "extensions": [
@@ -12,10 +12,14 @@
12
12
  ]
13
13
  },
14
14
  "peerDependencies": {
15
+ "@opengsd/gsd-browser": ">=0.1.27",
15
16
  "playwright": ">=1.40.0",
16
17
  "sharp": ">=0.33.0"
17
18
  },
18
19
  "peerDependenciesMeta": {
20
+ "@opengsd/gsd-browser": {
21
+ "optional": true
22
+ },
19
23
  "playwright": {
20
24
  "optional": true
21
25
  },
@@ -256,7 +256,6 @@ export class AutoOrchestrator {
256
256
  this.status.activeUnit = { unitType: decision.unitType, unitId: decision.unitId };
257
257
  this.status.phase = "running";
258
258
  this.lastAdvanceKey = nextKey;
259
- this.lastFinalizedUnitKey = null;
260
259
  this.bumpTransition();
261
260
  await this.deps.runtime.journalTransition({
262
261
  name: "advance",
@@ -1,6 +1,6 @@
1
1
  // gsd-pi + src/resources/extensions/gsd/auto-dashboard.ts - Auto-mode progress widget rendering and dashboard helpers.
2
2
  import { getActiveHook } from "./post-unit-hooks.js";
3
- import { getLedger } from "./metrics.js";
3
+ import { getLedger, getProjectTotals } from "./metrics.js";
4
4
  import { getErrorMessage } from "./error-utils.js";
5
5
  import { nativeIsRepo } from "./native-git-bridge.js";
6
6
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
@@ -196,6 +196,39 @@ export function formatRuntimeHealthSignal(record, now = Date.now()) {
196
196
  export function shouldRenderRoadmapProgress(progress) {
197
197
  return !!progress && progress.total > 0;
198
198
  }
199
+ function widgetGridLabel(theme, text, color = "borderAccent") {
200
+ return theme.fg(color, theme.bold(text.toUpperCase()));
201
+ }
202
+ function widgetGridColumn(content, width) {
203
+ return padRightVisible(truncateToWidth(content, width, "…"), width);
204
+ }
205
+ function widgetGridColumns(theme, width, parts) {
206
+ if (parts.length === 0)
207
+ return "";
208
+ const gap = theme.fg("dim", " │ ");
209
+ const gapWidth = visibleWidth(gap) * (parts.length - 1);
210
+ const available = Math.max(parts.length * 8, width - gapWidth);
211
+ const base = Math.floor(available / parts.length);
212
+ let remaining = available - base * parts.length;
213
+ const columns = parts.map((part) => {
214
+ const columnWidth = base + (remaining > 0 ? 1 : 0);
215
+ remaining--;
216
+ return widgetGridColumn(part, columnWidth);
217
+ });
218
+ return truncateToWidth(columns.join(gap), width, "…");
219
+ }
220
+ function formatSmallWidgetSpend() {
221
+ const ledger = getLedger();
222
+ if (!ledger || ledger.units.length === 0)
223
+ return "--";
224
+ const totals = getProjectTotals(ledger.units);
225
+ const parts = [];
226
+ if (totals.tokens.total > 0)
227
+ parts.push(formatWidgetTokens(totals.tokens.total));
228
+ if (totals.cost > 0)
229
+ parts.push(`$${totals.cost.toFixed(2)}`);
230
+ return parts.length > 0 ? parts.join(" · ") : "--";
231
+ }
199
232
  // ─── ETA Estimation ──────────────────────────────────────────────────────────
200
233
  /**
201
234
  * Estimate remaining time based on average unit duration from the metrics ledger.
@@ -642,26 +675,57 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
642
675
  cachedWidth = width;
643
676
  return lines;
644
677
  }
645
- // ── Mode: small — header + active work progress ───────────────
678
+ // ── Mode: small — dense horizontal grid ───────────────────────
646
679
  if (widgetMode === "small") {
647
- lines.push("");
648
- // Action line
649
- const target = task ? `${task.id}: ${task.title}` : unitId;
650
- const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
651
- lines.push(rightAlign(actionLeft, theme.fg("dim", phaseLabel), width));
652
- // Progress bar
680
+ lines.length = 0;
681
+ lines.push(...ui.bar());
653
682
  const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
683
+ const unitLabel = unitId || [mid?.id, slice?.id, task?.id].filter(Boolean).join("/");
684
+ const statusParts = [
685
+ spinner,
686
+ theme.fg("success", modeTag),
687
+ theme.fg(stateColor, activeState),
688
+ ];
689
+ if (runtimeSignal?.summary) {
690
+ statusParts.push(theme.fg(healthColor, healthSummary));
691
+ }
692
+ else if (healthLevel !== "green") {
693
+ statusParts.push(`${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`);
694
+ }
695
+ const timeValue = [elapsed, etaShort].filter(Boolean).join(" · ") || "--";
696
+ const rowOne = widgetGridColumns(theme, width, [
697
+ `${widgetGridLabel(theme, "status", "border")} ${statusParts.join(" ")}`,
698
+ `${widgetGridLabel(theme, "unit")} ${theme.fg("text", unitLabel || "--")}`,
699
+ `${widgetGridLabel(theme, "spend", "border")} ${theme.fg("dim", formatSmallWidgetSpend())}`,
700
+ `${widgetGridLabel(theme, "time")} ${theme.fg("dim", timeValue)}`,
701
+ ]);
702
+ const target = task
703
+ ? `${task.id}: ${task.title}`
704
+ : slice
705
+ ? `${slice.id}: ${slice.title}`
706
+ : unitId;
707
+ let taskValue = task?.id ?? "--";
708
+ let sliceValue = slice?.id ?? "--";
654
709
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
655
710
  const { done, total, activeSliceTasks } = roadmapSlices;
656
- const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
711
+ const barWidth = Math.max(4, Math.min(14, Math.floor(width * 0.12)));
657
712
  const bar = renderProgressBar(theme, done, total, barWidth);
658
- let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
713
+ sliceValue = `${bar} ${theme.fg("text", `${done}/${total}`)}`;
659
714
  if (activeSliceTasks && activeSliceTasks.total > 0) {
660
- const tn = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
661
- meta += `${theme.fg("dim", " · task ")}${theme.fg("accent", `${tn}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
715
+ const taskNum = isHook
716
+ ? Math.max(activeSliceTasks.done, 1)
717
+ : Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
718
+ taskValue = `${theme.fg("accent", `${taskNum}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
662
719
  }
663
- lines.push(`${pad}${bar} ${meta}`);
664
720
  }
721
+ const rowTwo = widgetGridColumns(theme, width, [
722
+ `${widgetGridLabel(theme, "phase", "border")} ${theme.fg("dim", unitType)}`,
723
+ `${widgetGridLabel(theme, "work")} ${theme.fg("text", target || "--")}`,
724
+ `${widgetGridLabel(theme, "task", "border")} ${taskValue}`,
725
+ `${widgetGridLabel(theme, "slice")} ${sliceValue}`,
726
+ ]);
727
+ lines.push(rowOne);
728
+ lines.push(rowTwo);
665
729
  lines.push(...ui.bar());
666
730
  cachedLines = lines;
667
731
  cachedWidth = width;
@@ -443,6 +443,7 @@ export const DISPATCH_RULES = [
443
443
  unitType: "discuss-milestone",
444
444
  unitId: mid,
445
445
  prompt: await buildDiscussMilestonePrompt(mid, midTitle, basePath, structuredQuestionsAvailable, { headless: !!process.env.GSD_HEADLESS }),
446
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
446
447
  };
447
448
  },
448
449
  },
@@ -578,6 +579,7 @@ export const DISPATCH_RULES = [
578
579
  unitType: "discuss-milestone",
579
580
  unitId: mid,
580
581
  prompt: await buildDiscussMilestonePrompt(mid, midTitle, basePath, structuredQuestionsAvailable, { headless: !!process.env.GSD_HEADLESS }),
582
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
581
583
  };
582
584
  },
583
585
  },
@@ -617,6 +619,7 @@ export const DISPATCH_RULES = [
617
619
  unitType: "discuss-project",
618
620
  unitId: "PROJECT",
619
621
  prompt: await buildDiscussProjectPrompt(basePath, structuredQuestionsAvailable),
622
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
620
623
  };
621
624
  },
622
625
  },
@@ -642,6 +645,7 @@ export const DISPATCH_RULES = [
642
645
  unitType: "discuss-requirements",
643
646
  unitId: "REQUIREMENTS",
644
647
  prompt: await buildDiscussRequirementsPrompt(basePath, structuredQuestionsAvailable),
648
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
645
649
  };
646
650
  },
647
651
  },
@@ -757,6 +761,7 @@ export const DISPATCH_RULES = [
757
761
  unitType: "discuss-milestone",
758
762
  unitId: mid,
759
763
  prompt: await buildDiscussMilestonePrompt(mid, midTitle, basePath, structuredQuestionsAvailable, { headless: !!process.env.GSD_HEADLESS }),
764
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
760
765
  };
761
766
  },
762
767
  },
@@ -32,7 +32,7 @@ import { isDbAvailable, getDbPath, refreshOpenDatabaseFromDisk, getTask, getSlic
32
32
  import { renderPlanCheckboxes, renderRoadmapFromDb } from "./markdown-renderer.js";
33
33
  import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
34
34
  import { consumeSignal } from "./session-status-io.js";
35
- import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
35
+ import { checkPostUnitHooks, consumeHookFailure, isRetryPending, consumeRetryTrigger, consumeGateBlock, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
36
36
  import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
37
37
  import { debugLog } from "./debug-logger.js";
38
38
  import { runSafely } from "./auto-utils.js";
@@ -1860,18 +1860,25 @@ export async function postUnitPostVerification(pctx) {
1860
1860
  // ── Post-unit hooks ──
1861
1861
  if (s.currentUnit && !s.stepMode) {
1862
1862
  const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);
1863
+ persistHookState(s.basePath);
1863
1864
  if (hookUnit) {
1864
1865
  if (s.currentUnit) {
1865
1866
  await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
1866
1867
  }
1867
- persistHookState(s.basePath);
1868
1868
  return enqueueSidecar(s, ctx, { kind: "hook", unitType: hookUnit.unitType, unitId: hookUnit.unitId, prompt: hookUnit.prompt, model: hookUnit.model }, { hookName: hookUnit.hookName });
1869
1869
  }
1870
+ const hookFailure = consumeHookFailure();
1871
+ if (hookFailure) {
1872
+ ctx.ui.notify(`Post-unit hook ${hookFailure.hookName} failed for ${hookFailure.unitId}: ${hookFailure.reason}. Pausing auto-mode.`, "warning");
1873
+ await pauseAuto(ctx, pi);
1874
+ return "stopped";
1875
+ }
1870
1876
  // Check if a hook requested a retry of the trigger unit
1871
1877
  if (isRetryPending()) {
1872
1878
  const trigger = consumeRetryTrigger();
1873
1879
  if (trigger) {
1874
- ctx.ui.notify(`Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting task state.`, "info");
1880
+ persistHookState(s.basePath);
1881
+ ctx.ui.notify(`Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting trigger unit state.`, "info");
1875
1882
  await s.orchestration?.retryActiveUnit({
1876
1883
  unitType: trigger.unitType,
1877
1884
  unitId: trigger.unitId,
@@ -1918,6 +1925,17 @@ export async function postUnitPostVerification(pctx) {
1918
1925
  // Fall through to normal dispatch — deriveState will re-derive the unit
1919
1926
  }
1920
1927
  }
1928
+ const gateBlock = consumeGateBlock();
1929
+ if (gateBlock) {
1930
+ persistHookState(s.basePath);
1931
+ const verdict = gateBlock.verdict ? ` verdict=${gateBlock.verdict};` : "";
1932
+ const artifact = gateBlock.artifact ? ` artifact=${gateBlock.artifact};` : "";
1933
+ const message = `Post-unit gate "${gateBlock.hookName}" blocked ${gateBlock.triggerUnitType} ${gateBlock.triggerUnitId}:` +
1934
+ `${verdict}${artifact} ${gateBlock.reason}. Run /gsd status to inspect, then /gsd auto after recovery.`;
1935
+ ctx.ui.notify(message, "warning");
1936
+ await pauseAuto(ctx, pi);
1937
+ return "stopped";
1938
+ }
1921
1939
  }
1922
1940
  // ── Fast-path stop detection (#3487) ──
1923
1941
  // Before waiting for triage, check if any PENDING captures contain explicit
@@ -30,6 +30,7 @@ import { classifyProject } from "./detection.js";
30
30
  import { hasBrowserRequiredText } from "./browser-evidence.js";
31
31
  import { debugLog } from "./debug-logger.js";
32
32
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
33
+ import { findMilestoneIds } from "./milestone-ids.js";
33
34
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
34
35
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
35
36
  /**
@@ -1270,7 +1271,7 @@ export async function checkNeedsRunUat(base, mid, state, prefs) {
1270
1271
  if (hasVerdict(uatContent))
1271
1272
  continue;
1272
1273
  // Also check the ASSESSMENT file — the run-uat prompt writes the verdict
1273
- // there (via gsd_summary_save artifact_type:"ASSESSMENT"), not into the
1274
+ // there (via gsd_uat_result_save), not into the
1274
1275
  // UAT spec file. Without this check the unit re-dispatches indefinitely.
1275
1276
  const assessmentFile = resolveSliceFile(base, mid, sid, "ASSESSMENT");
1276
1277
  if (assessmentFile) {
@@ -1325,21 +1326,44 @@ export async function checkNeedsRunUat(base, mid, state, prefs) {
1325
1326
  }
1326
1327
  return null;
1327
1328
  }
1328
- // ─── Prompt Builders ──────────────────────────────────────────────────────
1329
- /**
1330
- * Build a prompt for the discuss-milestone unit type.
1331
- * Loads the guided-discuss-milestone template and inlines the CONTEXT-DRAFT
1332
- * as a seed when present. The discussion agent interviews the user, writes
1333
- * a full CONTEXT.md, and the phase transitions to pre-planning automatically.
1334
- */
1335
- export async function buildDiscussMilestonePrompt(mid, midTitle, base, structuredQuestionsAvailable = "false", { headless = false } = {}) {
1336
- const discussTemplates = inlineTemplate("context", "Context");
1329
+ export async function buildDiscussMilestoneInlinedContext(mid, base) {
1330
+ const inlined = [];
1331
+ const roadmapInline = await inlineFileOptional(resolveMilestoneFile(base, mid, "ROADMAP"), relMilestoneFile(base, mid, "ROADMAP"), "Milestone Roadmap");
1332
+ if (roadmapInline)
1333
+ inlined.push(roadmapInline);
1334
+ const contextInline = await inlineFileOptional(resolveMilestoneFile(base, mid, "CONTEXT"), relMilestoneFile(base, mid, "CONTEXT"), "Milestone Context");
1335
+ if (contextInline)
1336
+ inlined.push(contextInline);
1337
+ const researchInline = await inlineFileOptional(resolveMilestoneFile(base, mid, "RESEARCH"), relMilestoneFile(base, mid, "RESEARCH"), "Milestone Research");
1338
+ if (researchInline)
1339
+ inlined.push(researchInline);
1340
+ const decisionsPath = resolveGsdRootFile(base, "DECISIONS");
1341
+ if (existsSync(decisionsPath)) {
1342
+ const decisionsContent = await loadFile(decisionsPath);
1343
+ if (decisionsContent) {
1344
+ inlined.push(`### Decisions Register\nSource: \`${relGsdRootFile("DECISIONS")}\`\n\n${decisionsContent.trim()}`);
1345
+ }
1346
+ }
1347
+ const milestoneIds = findMilestoneIds(base);
1348
+ const currentIndex = milestoneIds.indexOf(mid);
1349
+ const priorMilestoneIds = currentIndex >= 0 ? milestoneIds.slice(0, currentIndex) : milestoneIds;
1350
+ for (const priorMid of priorMilestoneIds) {
1351
+ const summaryInline = await inlineFileOptional(resolveMilestoneFile(base, priorMid, "SUMMARY"), relMilestoneFile(base, priorMid, "SUMMARY"), `${priorMid} Prior Milestone Summary`);
1352
+ if (summaryInline)
1353
+ inlined.push(summaryInline);
1354
+ }
1355
+ return inlined.length > 0
1356
+ ? `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`
1357
+ : "## Inlined Context\n\n_(no milestone context files found yet — go in blind and ask broad questions)_";
1358
+ }
1359
+ export async function buildDiscussMilestonePrompt(mid, midTitle, base, structuredQuestionsAvailable = "false", { headless = false, commitInstruction = "Do not commit planning artifacts — .gsd/ is managed externally.", fastPathInstruction = "", includeDraftSeed = true, includeContextMode = true, } = {}) {
1360
+ const contextTemplate = inlineTemplate("context", "Context");
1337
1361
  if (headless) {
1338
1362
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1339
1363
  const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
1340
1364
  return loadPrompt("discuss-headless", {
1341
1365
  seedContext: roadmapContent ?? "",
1342
- inlinedTemplates: discussTemplates,
1366
+ inlinedTemplates: contextTemplate,
1343
1367
  workingDirectory: base,
1344
1368
  milestoneId: mid,
1345
1369
  contextPath: relMilestoneFile(base, mid, "CONTEXT"),
@@ -1347,24 +1371,28 @@ export async function buildDiscussMilestonePrompt(mid, midTitle, base, structure
1347
1371
  multiMilestoneCommitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1348
1372
  });
1349
1373
  }
1350
- const contextModeInstructions = renderContextModeForPrompt("discuss-milestone", base);
1374
+ const rawInlinedContext = await buildDiscussMilestoneInlinedContext(mid, base);
1375
+ const cappedInlinedContext = capPreamble(rawInlinedContext);
1376
+ const discussTemplates = [cappedInlinedContext, contextTemplate].join("\n\n---\n\n");
1351
1377
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1352
1378
  workingDirectory: base,
1353
1379
  milestoneId: mid,
1354
1380
  milestoneTitle: midTitle,
1355
1381
  inlinedTemplates: discussTemplates,
1356
1382
  structuredQuestionsAvailable,
1357
- commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1358
- fastPathInstruction: "",
1383
+ commitInstruction,
1384
+ fastPathInstruction,
1359
1385
  });
1360
- const promptWithContextMode = prependContextModeToBlock("discuss-milestone", base, basePrompt);
1386
+ const promptWithContextMode = includeContextMode
1387
+ ? prependContextModeToBlock("discuss-milestone", base, basePrompt)
1388
+ : basePrompt;
1361
1389
  // If a CONTEXT-DRAFT.md exists, append it as seed material
1362
1390
  const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
1363
1391
  const draftContent = draftPath ? await loadFile(draftPath) : null;
1364
- if (draftContent) {
1392
+ if (includeDraftSeed && draftContent) {
1365
1393
  return `${promptWithContextMode}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
1366
1394
  }
1367
- return contextModeInstructions ? promptWithContextMode : basePrompt;
1395
+ return promptWithContextMode;
1368
1396
  }
1369
1397
  /**
1370
1398
  * Build a prompt for the workflow-preferences unit type (deep mode).
@@ -2540,17 +2568,26 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
2540
2568
  if (isDbAvailable()) {
2541
2569
  const milestone = getMilestone(mid);
2542
2570
  if (milestone) {
2571
+ const escapeCell = (value) => value.replace(/[\\|]/g, (char) => `\\${char}`).replace(/\r?\n/g, " ");
2543
2572
  const classes = [];
2544
2573
  if (milestone.verification_contract)
2545
- classes.push(`- **Contract:** ${milestone.verification_contract}`);
2574
+ classes.push(`| Contract | ${escapeCell(milestone.verification_contract)} |`);
2546
2575
  if (milestone.verification_integration)
2547
- classes.push(`- **Integration:** ${milestone.verification_integration}`);
2576
+ classes.push(`| Integration | ${escapeCell(milestone.verification_integration)} |`);
2548
2577
  if (milestone.verification_operational)
2549
- classes.push(`- **Operational:** ${milestone.verification_operational}`);
2578
+ classes.push(`| Operational | ${escapeCell(milestone.verification_operational)} |`);
2550
2579
  if (milestone.verification_uat)
2551
- classes.push(`- **UAT:** ${milestone.verification_uat}`);
2580
+ classes.push(`| UAT | ${escapeCell(milestone.verification_uat)} |`);
2552
2581
  if (classes.length > 0) {
2553
- const verificationClasses = `### Verification Classes (from planning)\n\nThese verification tiers were defined during milestone planning. Each non-empty class must be checked for evidence during validation.\n\n${classes.join("\n")}`;
2582
+ const verificationClasses = [
2583
+ "### Verification Classes (from planning)",
2584
+ "",
2585
+ "These verification tiers were defined during milestone planning. Every row in this table must appear in `verificationClasses` with the same canonical class name.",
2586
+ "",
2587
+ "| Class | Planned Check |",
2588
+ "| --- | --- |",
2589
+ ...classes,
2590
+ ].join("\n");
2554
2591
  inlined.push(verificationClasses);
2555
2592
  trackPromptContext(contextTelemetry, "verification-classes", "inline", verificationClasses);
2556
2593
  }
@@ -20,6 +20,9 @@ export function isAutoActive() {
20
20
  export function isAutoPaused() {
21
21
  return autoSession.paused;
22
22
  }
23
+ export function isAutoCompletionStopInProgress() {
24
+ return autoSession.completionStopInProgress;
25
+ }
23
26
  export function markToolStart(toolCallId, toolName) {
24
27
  markTrackedToolStart(toolCallId, autoSession.active, toolName);
25
28
  }
@@ -83,7 +83,7 @@ export function clearInFlightTools() {
83
83
  * from the tool handler. When these errors occur, retrying the same unit will
84
84
  * produce the same failure, so the retry loop must be broken.
85
85
  */
86
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON|does not provide an export named|Named export .* not found|Cannot find module|ERR_MODULE_NOT_FOUND|ERR_MODULE_NOT_EXPORTED|ERR_PACKAGE_PATH_NOT_EXPORTED/i;
86
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Input validation error|Invalid arguments for tool|MCP error -32602|No such tool available|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON|does not provide an export named|Named export .* not found|Cannot find module|ERR_MODULE_NOT_FOUND|ERR_MODULE_NOT_EXPORTED|ERR_PACKAGE_PATH_NOT_EXPORTED/i;
87
87
  const DETERMINISTIC_POLICY_ERROR_RE = /(?:^|\b)(?:HARD BLOCK:|Blocked: \/gsd queue is a planning tool|Direct writes to \.gsd\/STATE\.md and \.gsd\/gsd\.db are blocked|This is a mechanical gate)/i;
88
88
  /**
89
89
  * Returns true if the error message indicates a deterministic invocation or
@@ -1774,10 +1774,17 @@ export function createWiredAutoOrchestrationModule(ctx, pi, dispatchBasePath, ru
1774
1774
  async reconcileBeforeDispatch() {
1775
1775
  const activeBasePath = getLiveDispatchBasePath();
1776
1776
  const result = await reconcileBeforeDispatch(activeBasePath);
1777
- if (result.blockers.length > 0) {
1777
+ // Failure-path summaries written by gsd_summary_save create
1778
+ // artifact-db-status-divergence blockers for tasks that are still
1779
+ // pending (gsd_task_complete never ran). These tasks can still be
1780
+ // dispatched and the drift self-heals once they complete successfully.
1781
+ const hardBlockers = result.blockers.filter((b) => !b.includes("has SUMMARY artifact while DB status is") &&
1782
+ !b.includes("has SUMMARY on disk while DB status is") &&
1783
+ !b.includes("has task SUMMARY artifacts but no DB tasks"));
1784
+ if (hardBlockers.length > 0) {
1778
1785
  return {
1779
1786
  ok: false,
1780
- reason: result.blockers[0],
1787
+ reason: hardBlockers[0],
1781
1788
  stateSnapshot: result.stateSnapshot,
1782
1789
  };
1783
1790
  }
@@ -788,7 +788,7 @@ export function registerDbTools(pi) {
788
788
  recommendation: Type.String({ description: "Option id the executor recommends." }),
789
789
  recommendationRationale: Type.String({ description: "Why the recommendation — 1–2 sentences." }),
790
790
  continueWithDefault: Type.Boolean({
791
- description: "When true, loop continues (artifact logged for later review). When false, auto-mode pauses until the user resolves via /gsd escalate resolve.",
791
+ description: "When true, the recommendation is recorded as the default, but auto-mode still pauses until the user resolves via /gsd escalate resolve.",
792
792
  }),
793
793
  }, { description: "ADR-011 Phase 2: optional escalation payload. Only honored when phases.mid_execution_escalation is true." })),
794
794
  verificationEvidence: Type.Optional(Type.Array(Type.Object({
@@ -829,7 +829,7 @@ export function registerDbTools(pi) {
829
829
  sliceTitle: Type.String({ description: "Title of the slice" }),
830
830
  oneLiner: Type.String({ description: "One-line summary of what the slice accomplished" }),
831
831
  narrative: Type.String({ description: "Detailed narrative of what happened across all tasks" }),
832
- verification: Type.String({ description: "What was verified across all tasks" }),
832
+ verification: Type.Optional(Type.String({ description: "What was verified across all tasks — if omitted, summary records verification as passed without detail." })),
833
833
  uatContent: Type.String({ description: "UAT test content (markdown body)" }),
834
834
  // ── Enrichment metadata (optional — defaults to empty) ────────────
835
835
  deviations: Type.Optional(Type.String({ description: "Deviations from the slice plan, or 'None.'" })),
@@ -1010,7 +1010,7 @@ export function registerDbTools(pi) {
1010
1010
  promptGuidelines: [
1011
1011
  "Use gsd_validate_milestone when all slices are done and the milestone needs validation before completion.",
1012
1012
  "Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verificationClasses (optional), verdictRationale, remediationPlan (optional).",
1013
- "If verification classes were planned, verificationClasses must include canonical class rows using the exact class names Contract, Integration, Operational, and UAT when present in planning.",
1013
+ "If verification classes were planned, verificationClasses must be a complete canonical table with one row for every applicable planned class using the exact class names Contract, Integration, Operational, and UAT. Do not submit a partial table.",
1014
1014
  "Planned verification text marked as none/not required/not applicable/N/A (including suffixed variants such as 'not required - backend-only') is treated as not applicable and does not require a class row.",
1015
1015
  "If verdict is 'needs-remediation', also provide remediationPlan and use gsd_reassess_roadmap to add remediation slices to the roadmap.",
1016
1016
  "On success, returns validationPath where VALIDATION.md was written.",
@@ -1023,7 +1023,7 @@ export function registerDbTools(pi) {
1023
1023
  sliceDeliveryAudit: Type.String({ description: "Markdown table auditing each slice's claimed vs delivered output" }),
1024
1024
  crossSliceIntegration: Type.String({ description: "Markdown describing any cross-slice boundary mismatches" }),
1025
1025
  requirementCoverage: Type.String({ description: "Markdown describing any unaddressed requirements" }),
1026
- verificationClasses: Type.Optional(Type.String({ description: "Markdown describing verification class compliance and gaps using canonical class names (Contract, Integration, Operational, UAT) for each applicable planned class" })),
1026
+ verificationClasses: Type.Optional(Type.String({ description: "Complete markdown table describing verification class compliance and gaps; include one canonical row for every applicable planned class (Contract, Integration, Operational, UAT)" })),
1027
1027
  verdictRationale: Type.String({ description: "Why this verdict was chosen" }),
1028
1028
  remediationPlan: Type.Optional(Type.String({ description: "Remediation plan (required if verdict is needs-remediation)" })),
1029
1029
  }),
@@ -11,7 +11,7 @@ import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer,
11
11
  import { resolveManifest } from "../unit-context-manifest.js";
12
12
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
13
13
  import { loadFile, saveFile, formatContinue } from "../files.js";
14
- import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
14
+ import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
15
15
  import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
16
16
  import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
17
17
  import { saveActivityLog } from "../activity-log.js";
@@ -424,8 +424,9 @@ export function registerHooks(pi, ecosystemHandlers) {
424
424
  registerPlanMilestoneSchemaRecovery(pi);
425
425
  pi.on("session_start", async (_event, ctx) => {
426
426
  const basePath = contextBasePath(ctx);
427
+ const preserveCloseoutSurface = isAutoCompletionStopInProgress();
427
428
  initSessionNotifications(ctx);
428
- if (!isAutoActive()) {
429
+ if (!isAutoActive() && !preserveCloseoutSurface) {
429
430
  const { initHealthWidget } = await import("../health-widget.js");
430
431
  initHealthWidget(ctx);
431
432
  }
@@ -445,14 +446,17 @@ export function registerHooks(pi, ecosystemHandlers) {
445
446
  process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
446
447
  }
447
448
  catch { /* non-fatal */ }
448
- await installWelcomeHeader(ctx);
449
+ if (!preserveCloseoutSurface) {
450
+ await installWelcomeHeader(ctx);
451
+ }
449
452
  await loadToolApiKeysForSession();
450
- if (isAutoActive()) {
453
+ if (isAutoActive() || preserveCloseoutSurface) {
451
454
  ctx.ui.setWidget("gsd-health", undefined);
452
455
  }
453
456
  });
454
457
  pi.on("session_switch", async (_event, ctx) => {
455
458
  const basePath = contextBasePath(ctx);
459
+ const preserveCloseoutSurface = isAutoCompletionStopInProgress();
456
460
  initSessionNotifications(ctx);
457
461
  resetWriteGateState(basePath);
458
462
  resetToolCallLoopGuard();
@@ -464,7 +468,7 @@ export function registerHooks(pi, ecosystemHandlers) {
464
468
  await applyCompactionThresholdOverride(ctx);
465
469
  await prepareWorkflowMcpForHookContext(ctx, basePath);
466
470
  await loadToolApiKeysForSession();
467
- if (!isAutoActive()) {
471
+ if (!isAutoActive() && !preserveCloseoutSurface) {
468
472
  ctx.ui.setWidget("gsd-progress", undefined);
469
473
  ctx.ui.setWidget("gsd-outcome", undefined);
470
474
  const { initHealthWidget } = await import("../health-widget.js");
@@ -1,17 +1,44 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Shared browser-observable UAT requirement and evidence detection.
3
- export const BROWSER_REQUIREMENT_RE = /\b(?:browser|file:\/\/|localhost|dom|localstorage|click(?:ing|ed)?|button|screenshot|snapshot|reload(?:ed)?|page refresh|user-visible|strikethrough|search box)\b/i;
3
+ export const BROWSER_REQUIREMENT_RE = /\b(?:file:\/\/|localhost|playwright|chrome|screenshot|snapshot|browser_(?:assert|batch|find|verify|snapshot_refs))\b|\b(?:open|launch|navigate|load|visit|serve|start)\b.{0,80}\b(?:browser|page|localhost|file:\/\/)\b|\bbrowser\s+(?:check|session|test|uat|tool|automation|interaction|flow)\b/i;
4
4
  export const NO_BROWSER_EVIDENCE_RE = /\b(?:no|without|not|wasn'?t|isn'?t)\s+(?:automated\s+)?(?:live\s+)?browser(?:\s+(?:session|test|uat))?|\bno\s+automated\s+browser\b|\bnot\s+conducted\b/i;
5
5
  export const BROWSER_RUNTIME_RE = /\b(?:browser|playwright|chrome|camoufox|browser_(?:assert|batch|find|verify|snapshot_refs)|screenshot|snapshot|file:\/\/|localhost)\b/i;
6
6
  export const BROWSER_ACTION_RE = /\b(?:open(?:ed)?|navigate(?:d)?|click(?:ed)?|type(?:d)?|reload(?:ed)?|capture(?:d)?|screenshot|snapshot)\b/i;
7
7
  export const BROWSER_ASSERTION_RE = /\b(?:assert(?:ed|ion)?|observed|confirmed|verified|expected|visible|text|count|label|strikethrough|localstorage|screenshot|snapshot|passed)\b/i;
8
+ const NON_REQUIREMENT_BROWSER_HEADING_RE = /^(?:not\s+proven|not\s+covered|out\s+of\s+scope|deferred|follow-?ups?|known\s+limitations|notes\s+for\s+tester)\b/i;
9
+ const NON_REQUIREMENT_BROWSER_LINE_RE = /\b(?:deferred|not\s+proven|not\s+covered|out\s+of\s+scope|future\s+slice|follow-?up|no\s+(?:live\s+)?browser|without\s+(?:a\s+)?browser|not\s+(?:a\s+)?browser)\b/i;
8
10
  export function compactTextParts(parts) {
9
11
  return parts.flatMap((part) => Array.isArray(part) ? part : [part])
10
12
  .filter((part) => typeof part === "string" && part.trim().length > 0)
11
13
  .join("\n");
12
14
  }
13
15
  export function hasBrowserRequiredText(text) {
14
- return BROWSER_REQUIREMENT_RE.test(text);
16
+ let inNonRequirementSection = false;
17
+ let nonRequirementDepth = 0;
18
+ for (const line of text.split(/\r?\n/)) {
19
+ const headingMatch = line.match(/^(#{1,6})\s+(.+?)\s*$/);
20
+ if (headingMatch) {
21
+ const depth = headingMatch[1].length;
22
+ const title = headingMatch[2] ?? "";
23
+ // Only update section context when at the same or higher level than the
24
+ // heading that opened the non-requirement zone. A sub-heading deeper than
25
+ // the opening heading must not escape or re-enter the zone on its own.
26
+ if (!inNonRequirementSection || depth <= nonRequirementDepth) {
27
+ inNonRequirementSection = NON_REQUIREMENT_BROWSER_HEADING_RE.test(title);
28
+ nonRequirementDepth = inNonRequirementSection ? depth : 0;
29
+ }
30
+ // Check the heading title itself — section state is already updated, so
31
+ // we correctly skip headings that opened a non-requirement zone.
32
+ if (!inNonRequirementSection && BROWSER_REQUIREMENT_RE.test(title))
33
+ return true;
34
+ continue;
35
+ }
36
+ if (inNonRequirementSection || NON_REQUIREMENT_BROWSER_LINE_RE.test(line))
37
+ continue;
38
+ if (BROWSER_REQUIREMENT_RE.test(line))
39
+ return true;
40
+ }
41
+ return false;
15
42
  }
16
43
  export function hasBrowserEvidenceText(text) {
17
44
  if (!text.trim())
@@ -266,8 +266,8 @@ Examples:
266
266
  await handleInspect(ctx);
267
267
  return true;
268
268
  }
269
- if (trimmed === "update" || trimmed === "upgrade") {
270
- await handleUpdate(ctx);
269
+ if (trimmed === "update" || trimmed.startsWith("update ") || trimmed === "upgrade" || trimmed.startsWith("upgrade ")) {
270
+ await handleUpdate(ctx, trimmed.replace(/^(?:update|upgrade)\s*/, "").trim());
271
271
  return true;
272
272
  }
273
273
  if (trimmed === "fast" || trimmed.startsWith("fast ")) {