@opengsd/gsd-pi 1.2.0-dev.9ad8ae33 → 1.2.0-dev.a6376d75

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 (264) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/loader.js +6 -4
  5. package/dist/register-agent-bundles.d.ts +11 -2
  6. package/dist/register-agent-bundles.js +18 -4
  7. package/dist/resources/.managed-resources-content-hash +1 -1
  8. package/dist/resources/extensions/ask-user-questions.js +3 -2
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  10. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  11. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  12. package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
  13. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  14. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  15. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  16. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  17. package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
  18. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  19. package/dist/resources/extensions/gsd/auto/phases.js +17 -2372
  20. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  21. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  22. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  23. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  24. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  25. package/dist/resources/extensions/gsd/auto.js +15 -1
  26. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
  27. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  28. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  29. package/dist/resources/extensions/gsd/db/queries.js +30 -0
  30. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  31. package/dist/resources/extensions/gsd/guided-flow.js +88 -2
  32. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  33. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  34. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  35. package/dist/resources/extensions/gsd/notifications.js +12 -7
  36. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  37. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -1
  38. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
  39. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  40. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  41. package/dist/resources/extensions/gsd/state.js +6 -2
  42. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  43. package/dist/resources/extensions/gsd/tools/complete-task.js +62 -0
  44. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  45. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  46. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  47. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  48. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  49. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  50. package/dist/runtime-checks.d.ts +10 -0
  51. package/dist/runtime-checks.js +27 -0
  52. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  53. package/dist/web/standalone/.next/BUILD_ID +1 -1
  54. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  55. package/dist/web/standalone/.next/build-manifest.json +2 -2
  56. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  57. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.html +1 -1
  74. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  81. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  83. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  84. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  85. package/package.json +2 -2
  86. package/packages/cloud-mcp-gateway/package.json +2 -2
  87. package/packages/contracts/package.json +1 -1
  88. package/packages/daemon/package.json +4 -4
  89. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  90. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  91. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  92. package/packages/gsd-agent-core/package.json +5 -5
  93. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  94. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  95. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  96. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  97. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  98. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  99. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
  100. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  101. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  102. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  103. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  104. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  105. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  106. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +12 -0
  108. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  110. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  111. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  112. package/packages/gsd-agent-modes/package.json +7 -7
  113. package/packages/mcp-server/README.md +12 -3
  114. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  115. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  116. package/packages/mcp-server/dist/cli-runner.js +137 -0
  117. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  118. package/packages/mcp-server/dist/cli.js +2 -58
  119. package/packages/mcp-server/dist/cli.js.map +1 -1
  120. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  121. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  122. package/packages/mcp-server/dist/pid-registry.js +452 -0
  123. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  124. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  125. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  126. package/packages/mcp-server/dist/probe-mode.js +10 -0
  127. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  128. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  129. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  130. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  131. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  132. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  133. package/packages/mcp-server/dist/workflow-tools.js +62 -43
  134. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  135. package/packages/mcp-server/package.json +5 -5
  136. package/packages/native/package.json +1 -1
  137. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  138. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  139. package/packages/pi-agent-core/package.json +1 -1
  140. package/packages/pi-ai/package.json +1 -1
  141. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  142. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  144. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  147. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  148. package/packages/pi-coding-agent/package.json +7 -7
  149. package/packages/pi-tui/dist/index.d.ts +1 -1
  150. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  151. package/packages/pi-tui/dist/index.js +1 -1
  152. package/packages/pi-tui/dist/index.js.map +1 -1
  153. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  154. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  155. package/packages/pi-tui/dist/terminal-image.js +54 -2
  156. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  157. package/packages/pi-tui/dist/tui.d.ts +8 -0
  158. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  159. package/packages/pi-tui/dist/tui.js +63 -18
  160. package/packages/pi-tui/dist/tui.js.map +1 -1
  161. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  162. package/packages/pi-tui/dist/utils.js +110 -36
  163. package/packages/pi-tui/dist/utils.js.map +1 -1
  164. package/packages/pi-tui/package.json +2 -2
  165. package/packages/rpc-client/package.json +2 -2
  166. package/pkg/dist/theme/theme.d.ts.map +1 -1
  167. package/pkg/dist/theme/theme.js +45 -17
  168. package/pkg/dist/theme/theme.js.map +1 -1
  169. package/pkg/package.json +1 -1
  170. package/src/resources/extensions/ask-user-questions.ts +7 -2
  171. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  172. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  173. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  174. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  175. package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
  176. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  177. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  178. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  179. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  180. package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
  181. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  182. package/src/resources/extensions/gsd/auto/phases.ts +58 -3061
  183. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  184. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  185. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  186. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  187. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  188. package/src/resources/extensions/gsd/auto.ts +20 -1
  189. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
  190. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  191. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  192. package/src/resources/extensions/gsd/db/queries.ts +29 -0
  193. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  194. package/src/resources/extensions/gsd/guided-flow.ts +128 -2
  195. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  196. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  197. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  198. package/src/resources/extensions/gsd/notifications.ts +13 -6
  199. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  200. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -1
  201. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
  202. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  203. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  204. package/src/resources/extensions/gsd/state.ts +7 -1
  205. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  206. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  207. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  208. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
  209. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  210. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  211. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  212. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  213. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  214. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
  215. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +36 -0
  216. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
  217. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  218. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  219. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  220. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +16 -0
  221. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  222. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  223. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
  224. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  225. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  226. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  227. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  228. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  229. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  230. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  231. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  232. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  233. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
  234. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  235. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +1 -1
  236. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  237. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  238. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  239. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  240. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -1
  241. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  242. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  243. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  244. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
  245. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  246. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  248. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  249. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  250. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  251. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  252. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  253. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  254. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  255. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  256. package/src/resources/extensions/gsd/tools/complete-task.ts +87 -0
  257. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  258. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  259. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  260. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  261. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  262. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  263. /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_buildManifest.js +0 -0
  264. /package/dist/web/standalone/.next/static/{FBNo5cT_chy7YNoAQsU3o → xyMkEaICFHJoa98VgJyzY}/_ssgManifest.js +0 -0
@@ -0,0 +1,445 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Auto-loop finalize phase — post-unit verification and UAT pause.
3
+
4
+ import type { SidecarItem } from "./session.js";
5
+ import {
6
+ type PostUnitContext,
7
+ type PreVerificationOpts,
8
+ } from "../auto-post-unit.js";
9
+ import { clearCurrentPhase } from "../../shared/gsd-phase-state.js";
10
+ import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
11
+ import { writeUnitRuntimeRecord } from "../unit-runtime.js";
12
+ import { buildManualValidationGuidance } from "../worktree-manager.js";
13
+ import { relSliceFile } from "../paths.js";
14
+ import {
15
+ detectRootWriteLeak,
16
+ formatRootWriteLeakMessage,
17
+ } from "../root-write-leak-guard.js";
18
+ import {
19
+ logWarning,
20
+ drainLogs,
21
+ drainAndSummarize,
22
+ formatForNotification,
23
+ hasAnyIssues,
24
+ } from "../workflow-logger.js";
25
+ import { debugLog } from "../debug-logger.js";
26
+ import { buildPhaseHandoffOutcome, setAutoOutcomeWidget } from "../auto-dashboard.js";
27
+ import {
28
+ applyVerificationRetryPolicy,
29
+ rememberRetryDispatch,
30
+ _resolveCurrentUnitStartedAtForTest,
31
+ isIsolatedWorktreeSession,
32
+ } from "./phase-helpers.js";
33
+ import { _runMilestoneMergeOnceWithStashRestore } from "./closeout.js";
34
+ import type { IterationContext, IterationData, LoopState, PhaseResult } from "./types.js";
35
+ import { MAX_FINALIZE_TIMEOUTS } from "./types.js";
36
+
37
+ export async function failClosedOnFinalizeTimeout(
38
+ ic: IterationContext,
39
+ iterData: IterationData,
40
+ loopState: LoopState,
41
+ stage: "pre" | "post",
42
+ startedAt: number,
43
+ ): Promise<PhaseResult> {
44
+ const { ctx, pi, s, deps } = ic;
45
+ const now = Date.now();
46
+ const unitType = iterData.unitType;
47
+ const unitId = iterData.unitId;
48
+ const timeoutMs = stage === "pre" ? FINALIZE_PRE_TIMEOUT_MS : FINALIZE_POST_TIMEOUT_MS;
49
+ const progressKind = stage === "pre" ? "finalize-pre-timeout" : "finalize-post-timeout";
50
+
51
+ writeUnitRuntimeRecord(s.basePath, unitType, unitId, startedAt, {
52
+ phase: "finalize-timeout",
53
+ timeoutAt: now,
54
+ lastProgressAt: now,
55
+ lastProgressKind: progressKind,
56
+ });
57
+
58
+ deps.emitJournalEvent({
59
+ ts: new Date(now).toISOString(),
60
+ flowId: ic.flowId,
61
+ seq: ic.nextSeq(),
62
+ eventType: "unit-end",
63
+ data: {
64
+ unitType,
65
+ unitId,
66
+ status: "timed-out-finalize",
67
+ artifactVerified: false,
68
+ finalizeStage: stage,
69
+ },
70
+ });
71
+
72
+ loopState.consecutiveFinalizeTimeouts++;
73
+ debugLog("autoLoop", {
74
+ phase: progressKind,
75
+ iteration: ic.iteration,
76
+ unitType,
77
+ unitId,
78
+ consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
79
+ });
80
+
81
+ ctx.ui.notify(
82
+ `${stage === "pre" ? "postUnitPreVerification" : "postUnitPostVerification"} timed out after ${timeoutMs / 1000}s for ${unitType} ${unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — pausing auto-mode for recovery.`,
83
+ "warning",
84
+ );
85
+
86
+ await deps.pauseAuto(ctx, pi);
87
+ s.clearCurrentUnit();
88
+ clearCurrentPhase();
89
+ drainLogs();
90
+ return { action: "break", reason: progressKind };
91
+ }
92
+
93
+ /**
94
+ * Phase 5: Post-unit finalize — pre/post verification, UAT pause, step-wizard.
95
+ * Returns break/continue/next to control the outer loop.
96
+ */
97
+ export async function runFinalize(
98
+ ic: IterationContext,
99
+ iterData: IterationData,
100
+ loopState: LoopState,
101
+ sidecarItem?: SidecarItem,
102
+ ): Promise<PhaseResult> {
103
+ const { ctx, pi, s, deps } = ic;
104
+ const { pauseAfterUatDispatch } = iterData;
105
+
106
+ debugLog("autoLoop", { phase: "finalize", iteration: ic.iteration });
107
+
108
+ // Clear unit timeout (unit completed)
109
+ deps.clearUnitTimeout();
110
+
111
+ // Post-unit context for pre/post verification
112
+ const postUnitCtx: PostUnitContext = {
113
+ s,
114
+ ctx,
115
+ pi,
116
+ buildSnapshotOpts: deps.buildSnapshotOpts,
117
+ lockBase: deps.lockBase,
118
+ stopAuto: deps.stopAuto,
119
+ pauseAuto: deps.pauseAuto,
120
+ updateProgressWidget: deps.updateProgressWidget,
121
+ };
122
+
123
+ // Pre-verification processing (commit, doctor, state rebuild, etc.)
124
+ // Timeout guard: if postUnitPreVerification hangs (e.g., safety harness
125
+ // deadlock, browser teardown hang, worktree sync stall), force-continue
126
+ // after timeout so the auto-loop is not permanently frozen (#3757).
127
+ //
128
+ // On timeout, null out s.currentUnit so the timed-out task's late async
129
+ // mutations are harmless — postUnitPreVerification guards all side effects
130
+ // behind `if (s.currentUnit)`. The next iteration sets a fresh currentUnit.
131
+ // Sidecar items use lightweight pre-verification opts
132
+ const preVerificationOpts: PreVerificationOpts = sidecarItem
133
+ ? sidecarItem.kind === "hook"
134
+ ? { skipSettleDelay: true, skipWorktreeSync: true, agentEndMessages: s.lastUnitAgentEndMessages ?? undefined }
135
+ : { skipSettleDelay: true, agentEndMessages: s.lastUnitAgentEndMessages ?? undefined }
136
+ : { agentEndMessages: s.lastUnitAgentEndMessages ?? undefined };
137
+ const preUnitSnapshot = s.currentUnit
138
+ ? { type: s.currentUnit.type, id: s.currentUnit.id, startedAt: s.currentUnit.startedAt }
139
+ : null;
140
+ const clearFinalizingUnit = () => {
141
+ if (
142
+ preUnitSnapshot &&
143
+ s.currentUnit?.type === preUnitSnapshot.type &&
144
+ s.currentUnit?.id === preUnitSnapshot.id &&
145
+ s.currentUnit?.startedAt === preUnitSnapshot.startedAt
146
+ ) {
147
+ s.clearCurrentUnit();
148
+ }
149
+ s.rootWriteBaseline = null;
150
+ };
151
+ clearCurrentPhase();
152
+ const preResultGuard = await withTimeout(
153
+ deps.postUnitPreVerification(postUnitCtx, preVerificationOpts),
154
+ FINALIZE_PRE_TIMEOUT_MS,
155
+ "postUnitPreVerification",
156
+ );
157
+
158
+ if (preResultGuard.timedOut) {
159
+ return failClosedOnFinalizeTimeout(
160
+ ic,
161
+ iterData,
162
+ loopState,
163
+ "pre",
164
+ preUnitSnapshot?.startedAt ?? Date.now(),
165
+ );
166
+ }
167
+
168
+ const preResult = preResultGuard.value;
169
+ if (preResult === "dispatched") {
170
+ const dispatchedReason = s.lastGitActionFailure
171
+ ? "git-closeout-failure"
172
+ : "pre-verification-dispatched";
173
+ debugLog("autoLoop", {
174
+ phase: "exit",
175
+ reason: dispatchedReason,
176
+ gitError: s.lastGitActionFailure ?? undefined,
177
+ });
178
+ clearFinalizingUnit();
179
+ return { action: "break", reason: dispatchedReason };
180
+ }
181
+ if (preResult === "retry") {
182
+ if (sidecarItem) {
183
+ // Sidecar artifact retries are skipped — just continue
184
+ debugLog("autoLoop", { phase: "sidecar-artifact-retry-skipped", iteration: ic.iteration });
185
+ } else {
186
+ // s.pendingVerificationRetry was set by postUnitPreVerification.
187
+ // Emit a dedicated journal event so forensics can distinguish bounded
188
+ // verification retries from genuine stuck-loop dispatch repetitions (#4540).
189
+ const retryInfo = s.pendingVerificationRetry;
190
+ deps.emitJournalEvent({
191
+ ts: new Date().toISOString(),
192
+ flowId: ic.flowId,
193
+ seq: ic.nextSeq(),
194
+ eventType: "artifact-verification-retry",
195
+ data: {
196
+ unitType: preUnitSnapshot?.type,
197
+ unitId: retryInfo?.unitId,
198
+ attempt: retryInfo?.attempt,
199
+ },
200
+ });
201
+ const retryPolicyResult = await applyVerificationRetryPolicy(
202
+ ic,
203
+ preUnitSnapshot?.type,
204
+ "artifact-verification-retry",
205
+ );
206
+ if (retryPolicyResult) {
207
+ clearFinalizingUnit();
208
+ return retryPolicyResult;
209
+ }
210
+ // Continue the loop — next iteration will inject the retry context into the prompt.
211
+ rememberRetryDispatch(s, preUnitSnapshot, iterData);
212
+ debugLog("autoLoop", { phase: "artifact-verification-retry", iteration: ic.iteration });
213
+ clearFinalizingUnit();
214
+ return { action: "continue" };
215
+ }
216
+ }
217
+
218
+ if (pauseAfterUatDispatch) {
219
+ const pauseMid = iterData.mid;
220
+ const pauseSliceId = pauseMid && iterData.unitId.startsWith(`${pauseMid}/`)
221
+ ? iterData.unitId.slice(pauseMid.length + 1)
222
+ : undefined;
223
+ const guidance = pauseMid
224
+ ? buildManualValidationGuidance(s.basePath, pauseMid, {
225
+ uatPath: pauseSliceId
226
+ ? relSliceFile(s.basePath, pauseMid, pauseSliceId, "UAT")
227
+ : undefined,
228
+ })
229
+ : null;
230
+ const pauseMessage = guidance
231
+ ? `UAT requires human execution. Auto-mode will pause after this unit writes the result file.\n\n${guidance}`
232
+ : "UAT requires human execution. Auto-mode will pause after this unit writes the result file.";
233
+ ctx.ui.notify(pauseMessage, "info");
234
+ await deps.pauseAuto(ctx, pi);
235
+ debugLog("autoLoop", { phase: "exit", reason: "uat-pause" });
236
+ clearFinalizingUnit();
237
+ return { action: "break", reason: "uat-pause" };
238
+ }
239
+
240
+ // Verification gate
241
+ // Hook sidecar items skip verification entirely.
242
+ // Non-hook sidecar items run verification but skip retries (just continue).
243
+ const skipVerification = sidecarItem?.kind === "hook";
244
+ if (!skipVerification) {
245
+ const verificationResult = await deps.runPostUnitVerification(
246
+ { s, ctx, pi },
247
+ deps.pauseAuto,
248
+ );
249
+
250
+ if (verificationResult === "pause") {
251
+ debugLog("autoLoop", { phase: "exit", reason: "verification-pause" });
252
+ clearFinalizingUnit();
253
+ return { action: "break", reason: "verification-pause" };
254
+ }
255
+
256
+ if (verificationResult === "retry") {
257
+ if (sidecarItem) {
258
+ // Sidecar verification retries are skipped — just continue
259
+ debugLog("autoLoop", { phase: "sidecar-verification-retry-skipped", iteration: ic.iteration });
260
+ } else {
261
+ // s.pendingVerificationRetry was set by runPostUnitVerification.
262
+ const retryPolicyResult = await applyVerificationRetryPolicy(
263
+ ic,
264
+ iterData.unitType,
265
+ "verification-retry",
266
+ );
267
+ if (retryPolicyResult) {
268
+ clearFinalizingUnit();
269
+ return retryPolicyResult;
270
+ }
271
+ // Continue the loop — next iteration will inject the retry context into the prompt.
272
+ rememberRetryDispatch(s, preUnitSnapshot, iterData);
273
+ debugLog("autoLoop", { phase: "verification-retry", iteration: ic.iteration });
274
+ clearFinalizingUnit();
275
+ return { action: "continue" };
276
+ }
277
+ }
278
+ }
279
+
280
+ // Post-verification processing (DB dual-write, hooks, triage, quick-tasks)
281
+ // Timeout guard: if postUnitPostVerification hangs (e.g., module import
282
+ // deadlock, SQLite transaction hang), force-continue after timeout so the
283
+ // auto-loop is not permanently frozen (#2344).
284
+ const postResultGuard = await withTimeout(
285
+ deps.postUnitPostVerification(postUnitCtx),
286
+ FINALIZE_POST_TIMEOUT_MS,
287
+ "postUnitPostVerification",
288
+ );
289
+
290
+ if (postResultGuard.timedOut) {
291
+ return failClosedOnFinalizeTimeout(
292
+ ic,
293
+ iterData,
294
+ loopState,
295
+ "post",
296
+ preUnitSnapshot?.startedAt ?? Date.now(),
297
+ );
298
+ }
299
+
300
+ const postResult = postResultGuard.value;
301
+
302
+ if (postResult === "retry") {
303
+ if (sidecarItem) {
304
+ debugLog("autoLoop", { phase: "sidecar-pre-execution-retry-skipped", iteration: ic.iteration });
305
+ } else {
306
+ const retryInfo = s.pendingVerificationRetry;
307
+ deps.emitJournalEvent({
308
+ ts: new Date().toISOString(),
309
+ flowId: ic.flowId,
310
+ seq: ic.nextSeq(),
311
+ eventType: "pre-execution-retry",
312
+ data: {
313
+ unitType: preUnitSnapshot?.type,
314
+ unitId: retryInfo?.unitId,
315
+ attempt: retryInfo?.attempt,
316
+ },
317
+ });
318
+ const retryPolicyResult = await applyVerificationRetryPolicy(
319
+ ic,
320
+ preUnitSnapshot?.type,
321
+ "pre-execution-retry",
322
+ );
323
+ if (retryPolicyResult) {
324
+ clearFinalizingUnit();
325
+ return retryPolicyResult;
326
+ }
327
+ rememberRetryDispatch(s, preUnitSnapshot, iterData);
328
+ debugLog("autoLoop", {
329
+ phase: "pre-execution-retry",
330
+ iteration: ic.iteration,
331
+ unitType: preUnitSnapshot?.type,
332
+ unitId: retryInfo?.unitId,
333
+ attempt: retryInfo?.attempt,
334
+ });
335
+ clearFinalizingUnit();
336
+ return { action: "continue" };
337
+ }
338
+ }
339
+
340
+ if (postResult === "stopped") {
341
+ debugLog("autoLoop", {
342
+ phase: "exit",
343
+ reason: "post-verification-stopped",
344
+ });
345
+ clearFinalizingUnit();
346
+ return { action: "break", reason: "post-verification-stopped" };
347
+ }
348
+
349
+ if (postResult === "step-wizard") {
350
+ // Step mode — exit the loop (caller handles wizard)
351
+ debugLog("autoLoop", { phase: "exit", reason: "step-wizard" });
352
+ clearFinalizingUnit();
353
+ return { action: "break", reason: "step-wizard" };
354
+ }
355
+
356
+ if (preUnitSnapshot && isIsolatedWorktreeSession(s)) {
357
+ const leak = detectRootWriteLeak({
358
+ rootPath: s.originalBasePath,
359
+ worktreePath: s.basePath,
360
+ unitType: preUnitSnapshot.type,
361
+ unitId: preUnitSnapshot.id,
362
+ before: s.rootWriteBaseline,
363
+ });
364
+ s.rootWriteBaseline = null;
365
+ if (leak) {
366
+ const message = formatRootWriteLeakMessage(leak);
367
+ debugLog("autoLoop", {
368
+ phase: "root-write-leak",
369
+ unitType: preUnitSnapshot.type,
370
+ unitId: preUnitSnapshot.id,
371
+ rootPath: leak.rootPath,
372
+ worktreePath: leak.worktreePath,
373
+ files: leak.files.map((file) => ({ path: file.path, status: file.status })),
374
+ });
375
+ ctx.ui.notify(message, "error");
376
+ await deps.stopAuto(ctx, pi, "Root-write leak during isolated auto-mode", {
377
+ preserveCompletedMilestoneBranch: true,
378
+ });
379
+ clearFinalizingUnit();
380
+ return { action: "break", reason: "root-write-leak" };
381
+ }
382
+ } else {
383
+ s.rootWriteBaseline = null;
384
+ }
385
+
386
+ if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
387
+ const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId, {
388
+ preserveCloseoutTranscript: true,
389
+ });
390
+ if (stop) {
391
+ clearFinalizingUnit();
392
+ return stop;
393
+ }
394
+ }
395
+
396
+ // Both pre and post verification completed without timeout — reset counter
397
+ loopState.consecutiveFinalizeTimeouts = 0;
398
+ if (preUnitSnapshot) {
399
+ writeUnitRuntimeRecord(s.basePath, preUnitSnapshot.type, preUnitSnapshot.id, preUnitSnapshot.startedAt, {
400
+ phase: "finalized",
401
+ lastProgressAt: Date.now(),
402
+ lastProgressKind: "finalize-success",
403
+ });
404
+ if (
405
+ !preUnitSnapshot.type.startsWith("hook/") &&
406
+ preUnitSnapshot.type !== "custom-step" &&
407
+ preUnitSnapshot.type !== "complete-milestone"
408
+ ) {
409
+ setAutoOutcomeWidget(ctx, {
410
+ ...buildPhaseHandoffOutcome({
411
+ unitType: preUnitSnapshot.type,
412
+ unitId: preUnitSnapshot.id,
413
+ agentEndMessages: s.lastUnitAgentEndMessages,
414
+ }),
415
+ startedAt: s.autoStartTime,
416
+ });
417
+ }
418
+ }
419
+ clearFinalizingUnit();
420
+ // Surface accumulated workflow-logger issues for this unit to the user.
421
+ // Warnings/errors logged during the unit are buffered in the logger and
422
+ // drained here so the user sees a single consolidated post-unit alert.
423
+ if (hasAnyIssues()) {
424
+ const { logs } = drainAndSummarize();
425
+ if (logs.length > 0) {
426
+ const severity = logs.some((e) => e.severity === "error") ? "error" : "warning";
427
+ ctx.ui.notify(formatForNotification(logs), severity);
428
+ }
429
+ }
430
+
431
+ if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
432
+ // cleanupAfterLoopExit skips gsd-progress when preserveCompletionSurface is true, so clear stale controls here.
433
+ ctx.ui.setStatus?.("gsd-step", undefined);
434
+ ctx.ui.setWidget?.("gsd-progress", undefined);
435
+ await deps.stopAuto(ctx, pi, `Milestone ${s.currentMilestoneId} complete`, {
436
+ completionWidget: {
437
+ milestoneId: s.currentMilestoneId,
438
+ milestoneTitle: iterData.midTitle,
439
+ },
440
+ });
441
+ return { action: "break", reason: "milestone-complete" };
442
+ }
443
+
444
+ return { action: "next", data: undefined as void };
445
+ }
@@ -25,7 +25,9 @@ import {
25
25
  type IterationData,
26
26
  } from "./types.js";
27
27
  import { _clearCurrentResolve } from "./resolve.js";
28
- import { runGuards, runFinalize } from "./phases.js";
28
+ import { runGuards } from "./phases.js";
29
+ import { runFinalize } from "./finalize.js";
30
+ import { resetSessionTimeoutState } from "./unit-phase.js";
29
31
  import { STUCK_WINDOW_SIZE } from "./dispatch-history.js";
30
32
  import { debugLog } from "../debug-logger.js";
31
33
  import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
@@ -385,6 +387,7 @@ export async function autoLoop(
385
387
  options?: AutoLoopOptions,
386
388
  ): Promise<void> {
387
389
  debugLog("autoLoop", { phase: "enter" });
390
+ resetSessionTimeoutState();
388
391
  let iteration = 0;
389
392
  const dispatchContract = options?.dispatchContract ?? "legacy-direct";
390
393
  const unitDispatchDeps = createExecutionGraphUnitDispatchDeps();
@@ -0,0 +1,74 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Recover missing milestone lease state for resumed isolated workers.
3
+
4
+ import { debugLog } from "../debug-logger.js";
5
+ import { claimMilestoneLease } from "../db/milestone-leases.js";
6
+ import type { getIsolationMode } from "../preferences.js";
7
+
8
+ type IsolationMode = ReturnType<typeof getIsolationMode>;
9
+
10
+ export interface MilestoneLeaseSession {
11
+ workerId: string | null;
12
+ currentMilestoneId: string | null;
13
+ milestoneLeaseToken: number | null;
14
+ }
15
+
16
+ export function hasHeldMilestoneLease(
17
+ session: MilestoneLeaseSession,
18
+ milestoneId: string | null | undefined,
19
+ ): boolean {
20
+ return (
21
+ Boolean(milestoneId) &&
22
+ session.currentMilestoneId === milestoneId &&
23
+ typeof session.milestoneLeaseToken === "number"
24
+ );
25
+ }
26
+
27
+ export function reclaimMissingMilestoneLease(
28
+ session: MilestoneLeaseSession,
29
+ milestoneId: string | null | undefined,
30
+ isolationMode: IsolationMode,
31
+ phase: string,
32
+ ): void {
33
+ if (isolationMode === "none") return;
34
+ if (!session.workerId || !milestoneId) return;
35
+ if (hasHeldMilestoneLease(session, milestoneId)) return;
36
+ // Note: we intentionally do NOT bail just because the session already holds a
37
+ // lease for a *different* milestone. When dispatch advances to a new
38
+ // milestone the session's stale `currentMilestoneId`/token are for the prior
39
+ // one, and the active milestone's lease must still be claimed — otherwise
40
+ // worktree safety sees `held: false` and fails dispatch (#760). Claiming is
41
+ // safe: claimMilestoneLease refuses to steal a lease another worker holds.
42
+
43
+ try {
44
+ const claim = claimMilestoneLease(session.workerId, milestoneId);
45
+ if (claim.ok) {
46
+ session.currentMilestoneId = milestoneId;
47
+ session.milestoneLeaseToken = claim.token;
48
+ debugLog("worktreeSafety", {
49
+ phase: "lease-reclaimed",
50
+ source: phase,
51
+ milestoneId,
52
+ workerId: session.workerId,
53
+ token: claim.token,
54
+ });
55
+ } else {
56
+ debugLog("worktreeSafety", {
57
+ phase: "lease-reclaim-blocked",
58
+ source: phase,
59
+ milestoneId,
60
+ workerId: session.workerId,
61
+ holderWorkerId: claim.byWorker,
62
+ expiresAt: claim.expiresAt,
63
+ });
64
+ }
65
+ } catch (err) {
66
+ debugLog("worktreeSafety", {
67
+ phase: "lease-reclaim-failed",
68
+ source: phase,
69
+ milestoneId,
70
+ workerId: session.workerId,
71
+ error: err instanceof Error ? err.message : String(err),
72
+ });
73
+ }
74
+ }