@kenkaiiii/ggcoder 4.3.232 → 4.3.233

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 (348) hide show
  1. package/README.md +1 -2
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +7 -89
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/agent-session.d.ts.map +1 -1
  6. package/dist/core/agent-session.js +2 -2
  7. package/dist/core/agent-session.js.map +1 -1
  8. package/dist/core/prompt-commands.d.ts.map +1 -1
  9. package/dist/core/prompt-commands.js +8 -16
  10. package/dist/core/prompt-commands.js.map +1 -1
  11. package/dist/core/prompt-commands.test.js +14 -44
  12. package/dist/core/prompt-commands.test.js.map +1 -1
  13. package/dist/core/runtime-mode.d.ts +0 -11
  14. package/dist/core/runtime-mode.d.ts.map +1 -1
  15. package/dist/core/runtime-mode.js +0 -9
  16. package/dist/core/runtime-mode.js.map +1 -1
  17. package/dist/core/session-restore-display.test.js +4 -110
  18. package/dist/core/session-restore-display.test.js.map +1 -1
  19. package/dist/interactive.d.ts.map +1 -1
  20. package/dist/interactive.js +1 -1
  21. package/dist/interactive.js.map +1 -1
  22. package/dist/system-prompt.d.ts +1 -2
  23. package/dist/system-prompt.d.ts.map +1 -1
  24. package/dist/system-prompt.js +11 -58
  25. package/dist/system-prompt.js.map +1 -1
  26. package/dist/system-prompt.test.js +3 -158
  27. package/dist/system-prompt.test.js.map +1 -1
  28. package/dist/tools/bash.d.ts +1 -4
  29. package/dist/tools/bash.d.ts.map +1 -1
  30. package/dist/tools/bash.js +3 -12
  31. package/dist/tools/bash.js.map +1 -1
  32. package/dist/tools/checkpoint-hook.test.js +3 -3
  33. package/dist/tools/checkpoint-hook.test.js.map +1 -1
  34. package/dist/tools/edit.d.ts +1 -4
  35. package/dist/tools/edit.d.ts.map +1 -1
  36. package/dist/tools/edit.js +5 -13
  37. package/dist/tools/edit.js.map +1 -1
  38. package/dist/tools/enter-plan.js +1 -1
  39. package/dist/tools/enter-plan.js.map +1 -1
  40. package/dist/tools/index.d.ts +0 -9
  41. package/dist/tools/index.d.ts.map +1 -1
  42. package/dist/tools/index.js +4 -8
  43. package/dist/tools/index.js.map +1 -1
  44. package/dist/tools/prompt-hints.d.ts.map +1 -1
  45. package/dist/tools/prompt-hints.js +0 -2
  46. package/dist/tools/prompt-hints.js.map +1 -1
  47. package/dist/tools/screenshot.d.ts +1 -1
  48. package/dist/tools/subagent.d.ts +1 -4
  49. package/dist/tools/subagent.d.ts.map +1 -1
  50. package/dist/tools/subagent.js +2 -5
  51. package/dist/tools/subagent.js.map +1 -1
  52. package/dist/tools/write.d.ts +1 -4
  53. package/dist/tools/write.d.ts.map +1 -1
  54. package/dist/tools/write.js +5 -13
  55. package/dist/tools/write.js.map +1 -1
  56. package/dist/ui/App.d.ts +5 -20
  57. package/dist/ui/App.d.ts.map +1 -1
  58. package/dist/ui/App.js +71 -309
  59. package/dist/ui/App.js.map +1 -1
  60. package/dist/ui/app-items.d.ts +23 -25
  61. package/dist/ui/app-items.d.ts.map +1 -1
  62. package/dist/ui/app-items.js +37 -0
  63. package/dist/ui/app-items.js.map +1 -1
  64. package/dist/ui/app-items.test.d.ts +2 -0
  65. package/dist/ui/app-items.test.d.ts.map +1 -0
  66. package/dist/ui/app-items.test.js +38 -0
  67. package/dist/ui/app-items.test.js.map +1 -0
  68. package/dist/ui/app-state-persistence.test.js +14 -318
  69. package/dist/ui/app-state-persistence.test.js.map +1 -1
  70. package/dist/ui/chat-layout-pinning.test.js +74 -4
  71. package/dist/ui/chat-layout-pinning.test.js.map +1 -1
  72. package/dist/ui/components/AssistantMessage.test.js +6 -9
  73. package/dist/ui/components/AssistantMessage.test.js.map +1 -1
  74. package/dist/ui/components/Banner.js +1 -1
  75. package/dist/ui/components/Banner.js.map +1 -1
  76. package/dist/ui/components/ChatFooterPane.d.ts +1 -5
  77. package/dist/ui/components/ChatFooterPane.d.ts.map +1 -1
  78. package/dist/ui/components/ChatFooterPane.js +3 -4
  79. package/dist/ui/components/ChatFooterPane.js.map +1 -1
  80. package/dist/ui/components/ChatInputStack.d.ts +3 -1
  81. package/dist/ui/components/ChatInputStack.d.ts.map +1 -1
  82. package/dist/ui/components/ChatInputStack.js +4 -3
  83. package/dist/ui/components/ChatInputStack.js.map +1 -1
  84. package/dist/ui/components/ChatScreen.d.ts +3 -16
  85. package/dist/ui/components/ChatScreen.d.ts.map +1 -1
  86. package/dist/ui/components/ChatScreen.js +2 -3
  87. package/dist/ui/components/ChatScreen.js.map +1 -1
  88. package/dist/ui/components/Footer.d.ts +3 -8
  89. package/dist/ui/components/Footer.d.ts.map +1 -1
  90. package/dist/ui/components/Footer.js +5 -25
  91. package/dist/ui/components/Footer.js.map +1 -1
  92. package/dist/ui/components/InputArea.d.ts +1 -9
  93. package/dist/ui/components/InputArea.d.ts.map +1 -1
  94. package/dist/ui/components/InputArea.js +2 -44
  95. package/dist/ui/components/InputArea.js.map +1 -1
  96. package/dist/ui/components/LiveToolPanel.d.ts +30 -0
  97. package/dist/ui/components/LiveToolPanel.d.ts.map +1 -0
  98. package/dist/ui/components/LiveToolPanel.js +66 -0
  99. package/dist/ui/components/LiveToolPanel.js.map +1 -0
  100. package/dist/ui/components/ToolExecution.js +0 -2
  101. package/dist/ui/components/ToolExecution.js.map +1 -1
  102. package/dist/ui/components/TranscriptViewport.d.ts +1 -1
  103. package/dist/ui/components/TranscriptViewport.js +1 -1
  104. package/dist/ui/footer-jump-regression.test.js +12 -2
  105. package/dist/ui/footer-jump-regression.test.js.map +1 -1
  106. package/dist/ui/footer-status-layout.test.js +4 -12
  107. package/dist/ui/footer-status-layout.test.js.map +1 -1
  108. package/dist/ui/hooks/useChatLayoutMeasurements.d.ts +3 -4
  109. package/dist/ui/hooks/useChatLayoutMeasurements.d.ts.map +1 -1
  110. package/dist/ui/hooks/useChatLayoutMeasurements.js +26 -4
  111. package/dist/ui/hooks/useChatLayoutMeasurements.js.map +1 -1
  112. package/dist/ui/hooks/useModeState.d.ts +4 -19
  113. package/dist/ui/hooks/useModeState.d.ts.map +1 -1
  114. package/dist/ui/hooks/useModeState.js +5 -45
  115. package/dist/ui/hooks/useModeState.js.map +1 -1
  116. package/dist/ui/hooks/usePixelFixFlow.js +2 -2
  117. package/dist/ui/layout-decisions.d.ts +11 -34
  118. package/dist/ui/layout-decisions.d.ts.map +1 -1
  119. package/dist/ui/layout-decisions.js +15 -42
  120. package/dist/ui/layout-decisions.js.map +1 -1
  121. package/dist/ui/live-area-height.d.ts.map +1 -1
  122. package/dist/ui/live-area-height.js +6 -1
  123. package/dist/ui/live-area-height.js.map +1 -1
  124. package/dist/ui/live-frame-height.test.js +26 -1
  125. package/dist/ui/live-frame-height.test.js.map +1 -1
  126. package/dist/ui/plan-overlay.test.js +1 -1
  127. package/dist/ui/plan-overlay.test.js.map +1 -1
  128. package/dist/ui/prompt-routing.d.ts +1 -19
  129. package/dist/ui/prompt-routing.d.ts.map +1 -1
  130. package/dist/ui/prompt-routing.js +1 -79
  131. package/dist/ui/prompt-routing.js.map +1 -1
  132. package/dist/ui/render.d.ts +3 -18
  133. package/dist/ui/render.d.ts.map +1 -1
  134. package/dist/ui/render.js +0 -10
  135. package/dist/ui/render.js.map +1 -1
  136. package/dist/ui/scroll-stabilization.test.js +1 -7
  137. package/dist/ui/scroll-stabilization.test.js.map +1 -1
  138. package/dist/ui/slash-command-images.test.js +11 -86
  139. package/dist/ui/slash-command-images.test.js.map +1 -1
  140. package/dist/ui/streaming-flush-bounce.test.d.ts +2 -0
  141. package/dist/ui/streaming-flush-bounce.test.d.ts.map +1 -0
  142. package/dist/ui/streaming-flush-bounce.test.js +156 -0
  143. package/dist/ui/streaming-flush-bounce.test.js.map +1 -0
  144. package/dist/ui/submit-prompt-command.d.ts +1 -16
  145. package/dist/ui/submit-prompt-command.d.ts.map +1 -1
  146. package/dist/ui/submit-prompt-command.js +4 -54
  147. package/dist/ui/submit-prompt-command.js.map +1 -1
  148. package/dist/ui/submit-slash-commands.d.ts +0 -1
  149. package/dist/ui/submit-slash-commands.d.ts.map +1 -1
  150. package/dist/ui/submit-slash-commands.js +0 -4
  151. package/dist/ui/submit-slash-commands.js.map +1 -1
  152. package/dist/ui/terminal-history-status-renderers.d.ts +0 -1
  153. package/dist/ui/terminal-history-status-renderers.d.ts.map +1 -1
  154. package/dist/ui/terminal-history-status-renderers.js +0 -3
  155. package/dist/ui/terminal-history-status-renderers.js.map +1 -1
  156. package/dist/ui/terminal-history.d.ts.map +1 -1
  157. package/dist/ui/terminal-history.js +11 -56
  158. package/dist/ui/terminal-history.js.map +1 -1
  159. package/dist/ui/terminal-history.test.js +6 -33
  160. package/dist/ui/terminal-history.test.js.map +1 -1
  161. package/dist/ui/tool-line-summary.d.ts +29 -0
  162. package/dist/ui/tool-line-summary.d.ts.map +1 -0
  163. package/dist/ui/tool-line-summary.js +153 -0
  164. package/dist/ui/tool-line-summary.js.map +1 -0
  165. package/dist/ui/transcript/TranscriptRenderer.d.ts +1 -1
  166. package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
  167. package/dist/ui/transcript/TranscriptRenderer.js +18 -12
  168. package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
  169. package/dist/ui/transcript/presentation.d.ts +1 -17
  170. package/dist/ui/transcript/presentation.d.ts.map +1 -1
  171. package/dist/ui/transcript/presentation.js +0 -26
  172. package/dist/ui/transcript/presentation.js.map +1 -1
  173. package/dist/ui/transcript/spacing.d.ts +2 -2
  174. package/dist/ui/transcript/spacing.d.ts.map +1 -1
  175. package/dist/ui/transcript/spacing.js +14 -9
  176. package/dist/ui/transcript/spacing.js.map +1 -1
  177. package/dist/ui/transcript/spacing.test.js +5 -8
  178. package/dist/ui/transcript/spacing.test.js.map +1 -1
  179. package/dist/ui/transcript/tool-presentation.js +1 -1
  180. package/dist/ui/transcript/tool-presentation.js.map +1 -1
  181. package/dist/ui/transcript/transcript-lines.d.ts.map +1 -1
  182. package/dist/ui/transcript/transcript-lines.js +4 -0
  183. package/dist/ui/transcript/transcript-lines.js.map +1 -1
  184. package/dist/ui/tui-history-parity.test.js +0 -47
  185. package/dist/ui/tui-history-parity.test.js.map +1 -1
  186. package/dist/ui/tui-simulation.test.js +2 -1
  187. package/dist/ui/tui-simulation.test.js.map +1 -1
  188. package/package.json +4 -4
  189. package/dist/core/goal-controller.d.ts +0 -82
  190. package/dist/core/goal-controller.d.ts.map +0 -1
  191. package/dist/core/goal-controller.js +0 -838
  192. package/dist/core/goal-controller.js.map +0 -1
  193. package/dist/core/goal-controller.test.d.ts +0 -2
  194. package/dist/core/goal-controller.test.d.ts.map +0 -1
  195. package/dist/core/goal-controller.test.js +0 -1279
  196. package/dist/core/goal-controller.test.js.map +0 -1
  197. package/dist/core/goal-engine.d.ts +0 -61
  198. package/dist/core/goal-engine.d.ts.map +0 -1
  199. package/dist/core/goal-engine.js +0 -123
  200. package/dist/core/goal-engine.js.map +0 -1
  201. package/dist/core/goal-engine.test.d.ts +0 -2
  202. package/dist/core/goal-engine.test.d.ts.map +0 -1
  203. package/dist/core/goal-engine.test.js +0 -295
  204. package/dist/core/goal-engine.test.js.map +0 -1
  205. package/dist/core/goal-integration.d.ts +0 -80
  206. package/dist/core/goal-integration.d.ts.map +0 -1
  207. package/dist/core/goal-integration.js +0 -296
  208. package/dist/core/goal-integration.js.map +0 -1
  209. package/dist/core/goal-integration.test.d.ts +0 -2
  210. package/dist/core/goal-integration.test.d.ts.map +0 -1
  211. package/dist/core/goal-integration.test.js +0 -369
  212. package/dist/core/goal-integration.test.js.map +0 -1
  213. package/dist/core/goal-lifecycle-smoke.test.d.ts +0 -2
  214. package/dist/core/goal-lifecycle-smoke.test.d.ts.map +0 -1
  215. package/dist/core/goal-lifecycle-smoke.test.js +0 -248
  216. package/dist/core/goal-lifecycle-smoke.test.js.map +0 -1
  217. package/dist/core/goal-overhead-harness.d.ts +0 -33
  218. package/dist/core/goal-overhead-harness.d.ts.map +0 -1
  219. package/dist/core/goal-overhead-harness.js +0 -268
  220. package/dist/core/goal-overhead-harness.js.map +0 -1
  221. package/dist/core/goal-prerequisites.d.ts +0 -23
  222. package/dist/core/goal-prerequisites.d.ts.map +0 -1
  223. package/dist/core/goal-prerequisites.js +0 -114
  224. package/dist/core/goal-prerequisites.js.map +0 -1
  225. package/dist/core/goal-prerequisites.test.d.ts +0 -2
  226. package/dist/core/goal-prerequisites.test.d.ts.map +0 -1
  227. package/dist/core/goal-prerequisites.test.js +0 -154
  228. package/dist/core/goal-prerequisites.test.js.map +0 -1
  229. package/dist/core/goal-references.d.ts +0 -14
  230. package/dist/core/goal-references.d.ts.map +0 -1
  231. package/dist/core/goal-references.js +0 -153
  232. package/dist/core/goal-references.js.map +0 -1
  233. package/dist/core/goal-references.test.d.ts +0 -2
  234. package/dist/core/goal-references.test.d.ts.map +0 -1
  235. package/dist/core/goal-references.test.js +0 -77
  236. package/dist/core/goal-references.test.js.map +0 -1
  237. package/dist/core/goal-store.d.ts +0 -289
  238. package/dist/core/goal-store.d.ts.map +0 -1
  239. package/dist/core/goal-store.js +0 -1156
  240. package/dist/core/goal-store.js.map +0 -1
  241. package/dist/core/goal-store.test.d.ts +0 -2
  242. package/dist/core/goal-store.test.d.ts.map +0 -1
  243. package/dist/core/goal-store.test.js +0 -530
  244. package/dist/core/goal-store.test.js.map +0 -1
  245. package/dist/core/goal-verifier.d.ts +0 -17
  246. package/dist/core/goal-verifier.d.ts.map +0 -1
  247. package/dist/core/goal-verifier.js +0 -87
  248. package/dist/core/goal-verifier.js.map +0 -1
  249. package/dist/core/goal-verifier.test.d.ts +0 -2
  250. package/dist/core/goal-verifier.test.d.ts.map +0 -1
  251. package/dist/core/goal-verifier.test.js +0 -131
  252. package/dist/core/goal-verifier.test.js.map +0 -1
  253. package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts +0 -2
  254. package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts.map +0 -1
  255. package/dist/core/goal-worker-dev-server-lifecycle.test.js +0 -68
  256. package/dist/core/goal-worker-dev-server-lifecycle.test.js.map +0 -1
  257. package/dist/core/goal-worker.d.ts +0 -61
  258. package/dist/core/goal-worker.d.ts.map +0 -1
  259. package/dist/core/goal-worker.js +0 -467
  260. package/dist/core/goal-worker.js.map +0 -1
  261. package/dist/core/goal-worker.test.d.ts +0 -2
  262. package/dist/core/goal-worker.test.d.ts.map +0 -1
  263. package/dist/core/goal-worker.test.js +0 -493
  264. package/dist/core/goal-worker.test.js.map +0 -1
  265. package/dist/core/goal-worktree.d.ts +0 -108
  266. package/dist/core/goal-worktree.d.ts.map +0 -1
  267. package/dist/core/goal-worktree.js +0 -300
  268. package/dist/core/goal-worktree.js.map +0 -1
  269. package/dist/core/goal-worktree.test.d.ts +0 -2
  270. package/dist/core/goal-worktree.test.d.ts.map +0 -1
  271. package/dist/core/goal-worktree.test.js +0 -448
  272. package/dist/core/goal-worktree.test.js.map +0 -1
  273. package/dist/tools/goal-mode.test.d.ts +0 -2
  274. package/dist/tools/goal-mode.test.d.ts.map +0 -1
  275. package/dist/tools/goal-mode.test.js +0 -121
  276. package/dist/tools/goal-mode.test.js.map +0 -1
  277. package/dist/tools/goals.d.ts +0 -143
  278. package/dist/tools/goals.d.ts.map +0 -1
  279. package/dist/tools/goals.js +0 -1038
  280. package/dist/tools/goals.js.map +0 -1
  281. package/dist/tools/goals.test.d.ts +0 -2
  282. package/dist/tools/goals.test.d.ts.map +0 -1
  283. package/dist/tools/goals.test.js +0 -1444
  284. package/dist/tools/goals.test.js.map +0 -1
  285. package/dist/ui/components/GoalOverlay.d.ts +0 -83
  286. package/dist/ui/components/GoalOverlay.d.ts.map +0 -1
  287. package/dist/ui/components/GoalOverlay.js +0 -710
  288. package/dist/ui/components/GoalOverlay.js.map +0 -1
  289. package/dist/ui/components/GoalPickerMenu.d.ts +0 -9
  290. package/dist/ui/components/GoalPickerMenu.d.ts.map +0 -1
  291. package/dist/ui/components/GoalPickerMenu.js +0 -37
  292. package/dist/ui/components/GoalPickerMenu.js.map +0 -1
  293. package/dist/ui/components/GoalStatusBar.d.ts +0 -26
  294. package/dist/ui/components/GoalStatusBar.d.ts.map +0 -1
  295. package/dist/ui/components/GoalStatusBar.js +0 -130
  296. package/dist/ui/components/GoalStatusBar.js.map +0 -1
  297. package/dist/ui/components/GoalStatusBar.test.d.ts +0 -2
  298. package/dist/ui/components/GoalStatusBar.test.d.ts.map +0 -1
  299. package/dist/ui/components/GoalStatusBar.test.js +0 -17
  300. package/dist/ui/components/GoalStatusBar.test.js.map +0 -1
  301. package/dist/ui/goal-events.d.ts +0 -125
  302. package/dist/ui/goal-events.d.ts.map +0 -1
  303. package/dist/ui/goal-events.js +0 -407
  304. package/dist/ui/goal-events.js.map +0 -1
  305. package/dist/ui/goal-events.test.d.ts +0 -2
  306. package/dist/ui/goal-events.test.d.ts.map +0 -1
  307. package/dist/ui/goal-events.test.js +0 -416
  308. package/dist/ui/goal-events.test.js.map +0 -1
  309. package/dist/ui/goal-lifecycle-orchestration.test.d.ts +0 -2
  310. package/dist/ui/goal-lifecycle-orchestration.test.d.ts.map +0 -1
  311. package/dist/ui/goal-lifecycle-orchestration.test.js +0 -555
  312. package/dist/ui/goal-lifecycle-orchestration.test.js.map +0 -1
  313. package/dist/ui/goal-overlay.test.d.ts +0 -2
  314. package/dist/ui/goal-overlay.test.d.ts.map +0 -1
  315. package/dist/ui/goal-overlay.test.js +0 -384
  316. package/dist/ui/goal-overlay.test.js.map +0 -1
  317. package/dist/ui/goal-progress.d.ts +0 -31
  318. package/dist/ui/goal-progress.d.ts.map +0 -1
  319. package/dist/ui/goal-progress.js +0 -149
  320. package/dist/ui/goal-progress.js.map +0 -1
  321. package/dist/ui/goal-run-helpers.d.ts +0 -12
  322. package/dist/ui/goal-run-helpers.d.ts.map +0 -1
  323. package/dist/ui/goal-run-helpers.js +0 -42
  324. package/dist/ui/goal-run-helpers.js.map +0 -1
  325. package/dist/ui/goal-status-bar.test.d.ts +0 -2
  326. package/dist/ui/goal-status-bar.test.d.ts.map +0 -1
  327. package/dist/ui/goal-status-bar.test.js +0 -174
  328. package/dist/ui/goal-status-bar.test.js.map +0 -1
  329. package/dist/ui/goal-summary.d.ts +0 -14
  330. package/dist/ui/goal-summary.d.ts.map +0 -1
  331. package/dist/ui/goal-summary.js +0 -194
  332. package/dist/ui/goal-summary.js.map +0 -1
  333. package/dist/ui/hooks/useGoalOrchestration.d.ts +0 -82
  334. package/dist/ui/hooks/useGoalOrchestration.d.ts.map +0 -1
  335. package/dist/ui/hooks/useGoalOrchestration.js +0 -863
  336. package/dist/ui/hooks/useGoalOrchestration.js.map +0 -1
  337. package/dist/ui/hooks/useGoalPickerController.d.ts +0 -22
  338. package/dist/ui/hooks/useGoalPickerController.d.ts.map +0 -1
  339. package/dist/ui/hooks/useGoalPickerController.js +0 -35
  340. package/dist/ui/hooks/useGoalPickerController.js.map +0 -1
  341. package/dist/ui/prompt-routing.test.d.ts +0 -2
  342. package/dist/ui/prompt-routing.test.d.ts.map +0 -1
  343. package/dist/ui/prompt-routing.test.js +0 -48
  344. package/dist/ui/prompt-routing.test.js.map +0 -1
  345. package/dist/ui/transcript/GoalRows.d.ts +0 -10
  346. package/dist/ui/transcript/GoalRows.d.ts.map +0 -1
  347. package/dist/ui/transcript/GoalRows.js +0 -35
  348. package/dist/ui/transcript/GoalRows.js.map +0 -1
@@ -1,1279 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { buildGoalFailureDiagnosis, canCompleteGoalRun, decideGoalNextAction, formatGoalControllerDecision, hasFreshGoalCompletionAudit, hasRequiredGoalEvidence, shouldClearGoalContinuation, } from "./goal-controller.js";
3
- const durablePromptReference = {
4
- id: "original-goal-prompt",
5
- kind: "prompt",
6
- label: "Original Goal prompt",
7
- content: "Original /goal prompt requiring durable references and.",
8
- source: "user",
9
- };
10
- const durablePlanEvidence = {
11
- id: "planner-plan",
12
- kind: "summary",
13
- label: "Planner GOAL_PLAN",
14
- content: "GOAL_PLAN\nresearch=local\nfacts=goal-controller.ts\nsuccess=durable prompt and durable plan\nproof=contract test\nsetup=references verifier audit\nEND_GOAL_PLAN",
15
- createdAt: "2024-01-01T00:00:00.000Z",
16
- };
17
- function goalRun(overrides = {}) {
18
- return {
19
- id: "goal-a",
20
- title: "Programmatic loop",
21
- goal: "Make the loop deterministic",
22
- status: "ready",
23
- createdAt: "2024-01-01T00:00:00.000Z",
24
- updatedAt: "2024-01-01T00:00:00.000Z",
25
- projectPath: "/tmp/project",
26
- successCriteria: ["Verifier passes"],
27
- prerequisites: [],
28
- harness: [],
29
- evidencePlan: [
30
- {
31
- id: "proof",
32
- label: "Proof",
33
- mechanism: "command",
34
- description: "Run verifier",
35
- status: "ready",
36
- evidence: "verified",
37
- command: "npm test",
38
- },
39
- ],
40
- verifier: {
41
- description: "Verifier",
42
- command: "npm test",
43
- lastResult: {
44
- status: "pass",
45
- summary: "passed",
46
- command: "npm test",
47
- outputPath: "out.log",
48
- checkedAt: "2024-01-01T00:00:02.000Z",
49
- },
50
- },
51
- references: [durablePromptReference],
52
- tasks: [],
53
- evidence: [durablePlanEvidence],
54
- blockers: [],
55
- ...overrides,
56
- };
57
- }
58
- function withPassingCompletionAudit(run) {
59
- const verifier = run.verifier?.lastResult;
60
- if (!verifier)
61
- throw new Error("run must have verifier result");
62
- return {
63
- ...run,
64
- completionAudit: {
65
- status: "pass",
66
- summary: `FINAL_AUDIT_PASS verifier_checked_at=${verifier.checkedAt} output=${verifier.outputPath ?? "inline"} original-goal-prompt GOAL_PLAN`,
67
- checkedAt: "2024-01-01T00:00:03.000Z",
68
- verifierCheckedAt: verifier.checkedAt,
69
- ...(verifier.outputPath ? { outputPath: verifier.outputPath } : {}),
70
- },
71
- };
72
- }
73
- describe("goal controller", () => {
74
- it("blocks completion when mandatory non-prompt references are silently ignored", () => {
75
- const ignored = withPassingCompletionAudit(goalRun({
76
- references: [
77
- {
78
- id: "original-goal-prompt",
79
- kind: "prompt",
80
- label: "Original Goal prompt",
81
- content: "Fix feature based off X, Y, Z with supplied references.",
82
- source: "user",
83
- },
84
- {
85
- id: "repo-reference",
86
- kind: "repo",
87
- label: "Reference repository https://github.com/acme/product-reference",
88
- value: "https://github.com/acme/product-reference",
89
- },
90
- {
91
- id: "image-reference",
92
- kind: "image",
93
- label: "Attached image reference liked-ui.png",
94
- path: ".gg/goal-references/image-liked-ui.png",
95
- },
96
- {
97
- id: "text-reference",
98
- kind: "text",
99
- label: "Attached text reference feature-fix-x-y-z.md",
100
- path: ".gg/goal-references/text-feature-fix-x-y-z.md",
101
- content: "X: keyboard flow, Y: empty state copy, Z: error recovery.",
102
- },
103
- ],
104
- }));
105
- expect(canCompleteGoalRun(ignored)).toMatchObject({
106
- ok: false,
107
- reason: expect.stringContaining("Goal references are not covered"),
108
- });
109
- expect(canCompleteGoalRun(ignored).reason).toContain("Reference repository");
110
- expect(canCompleteGoalRun(ignored).reason).toContain("Attached image reference");
111
- expect(canCompleteGoalRun(ignored).reason).toContain("Attached text reference");
112
- const covered = withPassingCompletionAudit(goalRun({
113
- successCriteria: [
114
- "Worker task prompts and setup criteria must mention repo-reference, image-reference, text-reference, and the X/Y/Z feature-fix document.",
115
- ],
116
- evidencePlan: [
117
- {
118
- id: "reference-proof",
119
- label: "repo-reference/image-reference/text-reference evidence plan paths",
120
- mechanism: "test",
121
- description: "Proves mandatory URL/repo, screenshot/image, attached text document, and X/Y/Z feature-fix references are carried into proof paths.",
122
- status: "ready",
123
- evidence: "reference-proof ready",
124
- path: ".goal-evidence/reference-proof.log",
125
- },
126
- ],
127
- verifier: {
128
- description: "Verifier covers repo-reference, image-reference, text-reference, and feature-fix-x-y-z.md.",
129
- command: "pnpm vitest goal-references.test.ts goal-controller.test.ts",
130
- lastResult: {
131
- status: "pass",
132
- summary: "passed repo-reference image-reference text-reference feature-fix-x-y-z.md",
133
- command: "pnpm vitest goal-references.test.ts goal-controller.test.ts",
134
- outputPath: ".goal-evidence/reference-proof.log",
135
- checkedAt: "2024-01-01T00:00:02.000Z",
136
- },
137
- },
138
- tasks: [
139
- {
140
- id: "worker-task",
141
- title: "Use mandatory references",
142
- prompt: "Implement using repo-reference, image-reference at .gg/goal-references/image-liked-ui.png, and text-reference at .gg/goal-references/text-feature-fix-x-y-z.md for X/Y/Z.",
143
- status: "done",
144
- attempts: 1,
145
- },
146
- ],
147
- references: ignored.references,
148
- }));
149
- expect(canCompleteGoalRun(covered)).toEqual({
150
- ok: true,
151
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
152
- });
153
- });
154
- it("starts the next pending worker task deterministically", () => {
155
- const task = {
156
- id: "task-a",
157
- title: "Implement loop",
158
- prompt: "Do work",
159
- status: "pending",
160
- attempts: 1,
161
- };
162
- expect(decideGoalNextAction(goalRun({ tasks: [task] }))).toEqual({
163
- kind: "start_worker",
164
- task,
165
- attempts: 2,
166
- reason: 'Goal task "Implement loop" is ready for worker attempt 2.',
167
- });
168
- });
169
- it("waits instead of starting duplicate work when a worker or task is active", () => {
170
- expect(decideGoalNextAction(goalRun({ activeWorkerId: "worker-a" }))).toEqual({
171
- kind: "wait",
172
- reason: "Goal already has an active worker.",
173
- workerId: "worker-a",
174
- });
175
- expect(decideGoalNextAction(goalRun({
176
- tasks: [
177
- {
178
- id: "task-a",
179
- title: "Running task",
180
- prompt: "Do work",
181
- status: "running",
182
- workerId: "worker-a",
183
- attempts: 1,
184
- },
185
- ],
186
- }))).toMatchObject({ kind: "wait", workerId: "worker-a" });
187
- });
188
- it("waits for typed task dependencies before starting dependent DAG nodes", () => {
189
- const schemaTask = {
190
- id: "schema-task",
191
- title: "Change schema",
192
- prompt: "Update schema",
193
- status: "pending",
194
- attempts: 0,
195
- parallelGroup: "model",
196
- expectedChangedScope: ["packages/ggcoder/src/core/**"],
197
- integration: "candidate",
198
- };
199
- const uiTask = {
200
- id: "ui-task",
201
- title: "Change UI",
202
- prompt: "Update UI after schema",
203
- status: "pending",
204
- attempts: 0,
205
- dependsOn: ["schema-task"],
206
- parallelGroup: "frontend",
207
- expectedChangedScope: ["packages/ggcoder/src/ui/**"],
208
- integration: "candidate",
209
- };
210
- const missingDepDecision = decideGoalNextAction(goalRun({ tasks: [uiTask] }));
211
- expect(missingDepDecision).toMatchObject({ kind: "terminal", status: "failed" });
212
- expect(missingDepDecision.reason).toContain("depends on missing task(s) that cannot be synthesized: schema-task");
213
- expect(decideGoalNextAction(goalRun({ tasks: [uiTask, schemaTask] }))).toEqual({
214
- kind: "start_worker",
215
- task: schemaTask,
216
- attempts: 1,
217
- reason: 'Goal task "Change schema" is ready for worker attempt 1.',
218
- });
219
- expect(decideGoalNextAction(goalRun({ tasks: [{ ...schemaTask, status: "done" }, uiTask] }))).toEqual({
220
- kind: "start_worker",
221
- task: uiTask,
222
- attempts: 1,
223
- reason: 'Goal task "Change UI" is ready for worker attempt 1.',
224
- });
225
- });
226
- it("reproduces blocked-after-pass shape by completing from durable evidence despite stale blocked status and planned items", () => {
227
- const run = goalRun({
228
- status: "blocked",
229
- blockers: ["Verifier was interrupted"],
230
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
231
- evidencePlan: [
232
- {
233
- id: "targeted-tests",
234
- label: "Targeted regression tests",
235
- mechanism: "test",
236
- description: "Run the focused local regression command.",
237
- status: "planned",
238
- command: "pnpm vitest run src/core/goal-controller.test.ts",
239
- },
240
- {
241
- id: "verifier-log",
242
- label: "Verifier log artifact",
243
- mechanism: "log",
244
- description: "Persist the verifier output log.",
245
- status: "planned",
246
- path: ".goal-evidence/blocked-after-pass.log",
247
- },
248
- ],
249
- evidence: [
250
- {
251
- id: "evidence-targeted-tests",
252
- createdAt: "2024-01-01T00:00:00.000Z",
253
- kind: "command",
254
- label: "Targeted regression tests",
255
- content: "pnpm vitest run src/core/goal-controller.test.ts passed",
256
- },
257
- {
258
- id: "evidence-verifier-log",
259
- createdAt: "2024-01-01T00:00:01.000Z",
260
- kind: "log",
261
- label: "Verifier log artifact",
262
- path: ".goal-evidence/blocked-after-pass.log",
263
- content: "Verifier passed after interruption.",
264
- },
265
- ],
266
- verifier: {
267
- description: "Full check",
268
- command: "pnpm vitest run src/core/goal-controller.test.ts",
269
- lastResult: {
270
- status: "pass",
271
- summary: "Verifier passed after earlier interruption.",
272
- command: "pnpm vitest run src/core/goal-controller.test.ts",
273
- outputPath: ".goal-evidence/blocked-after-pass.log",
274
- checkedAt: "2024-01-01T00:00:02.000Z",
275
- },
276
- },
277
- });
278
- expect(canCompleteGoalRun(run)).toEqual({
279
- ok: false,
280
- reason: "Goal has no final completion audit.",
281
- });
282
- expect(decideGoalNextAction(run)).toMatchObject({
283
- kind: "create_task",
284
- title: "Audit Goal completion evidence",
285
- reason: "Verifier passed; creating final read-only completion audit before the Goal can pass (1/3).",
286
- });
287
- const audited = withPassingCompletionAudit(run);
288
- expect(canCompleteGoalRun(audited)).toEqual({
289
- ok: true,
290
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
291
- });
292
- expect(decideGoalNextAction(audited)).toEqual({
293
- kind: "complete",
294
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
295
- });
296
- });
297
- it("treats closure evidence and ready evidence-plan items as satisfied after verifier pass", () => {
298
- const run = goalRun({
299
- evidencePlan: [
300
- {
301
- id: "ready-proof",
302
- label: "Ready proof",
303
- mechanism: "test",
304
- description: "Ready proof was produced by the harness.",
305
- status: "ready",
306
- evidence: "Regression harness artifact was recorded.",
307
- },
308
- {
309
- id: "closure-proof",
310
- label: "Closure proof",
311
- mechanism: "browser",
312
- description: "Browser closure evidence proves the flow works.",
313
- status: "planned",
314
- evidence: "Browser closure evidence proves the flow works.",
315
- },
316
- {
317
- id: "verifier-output-proof",
318
- label: "Verifier output proof",
319
- mechanism: "screenshot",
320
- description: "Capture final verifier artifact.",
321
- status: "planned",
322
- path: "artifacts/final-verifier.log",
323
- },
324
- ],
325
- evidence: [
326
- {
327
- id: "evidence-closure-proof",
328
- createdAt: "2024-01-01T00:00:00.000Z",
329
- kind: "summary",
330
- label: "Closure proof",
331
- content: "Closure evidence recorded after the worker finished.",
332
- },
333
- ],
334
- verifier: {
335
- description: "Full check",
336
- command: "pnpm test",
337
- lastResult: {
338
- status: "pass",
339
- summary: "Verifier passed and wrote final artifact.",
340
- command: "pnpm test",
341
- outputPath: "artifacts/final-verifier.log",
342
- checkedAt: "2024-01-01T00:00:00.000Z",
343
- },
344
- },
345
- });
346
- expect(canCompleteGoalRun(run)).toEqual({
347
- ok: false,
348
- reason: "Goal has no final completion audit.",
349
- });
350
- expect(decideGoalNextAction(run)).toMatchObject({
351
- kind: "create_task",
352
- title: "Audit Goal completion evidence",
353
- reason: "Verifier passed; creating final read-only completion audit before the Goal can pass (1/3).",
354
- });
355
- const audited = withPassingCompletionAudit(run);
356
- expect(canCompleteGoalRun(audited)).toEqual({
357
- ok: true,
358
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
359
- });
360
- expect(decideGoalNextAction(audited)).toEqual({
361
- kind: "complete",
362
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
363
- });
364
- });
365
- it("uses final audit to reconcile planned evidence after verifier success", () => {
366
- const decision = decideGoalNextAction(goalRun({
367
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
368
- evidencePlan: [
369
- {
370
- id: "proof",
371
- label: "Unmatched proof",
372
- mechanism: "browser",
373
- description: "Needs screenshot",
374
- status: "planned",
375
- },
376
- ],
377
- verifier: {
378
- description: "Full check",
379
- command: "pnpm test",
380
- lastResult: {
381
- status: "pass",
382
- summary: "tests passed",
383
- command: "pnpm test",
384
- checkedAt: "2024-01-01T00:00:00.000Z",
385
- },
386
- },
387
- }));
388
- expect(decision).toMatchObject({
389
- kind: "create_task",
390
- title: "Audit Goal completion evidence",
391
- reason: "Verifier passed; final read-only audit must reconcile 1 evidence-plan item(s) before the Goal can pass (1/3).",
392
- });
393
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("final pre-audit gate");
394
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("update that evidence_plan item to status=ready");
395
- });
396
- it("explains ready evidence-plan items that have no durable proof anchor", () => {
397
- const run = goalRun({
398
- evidencePlan: [
399
- {
400
- id: "plan",
401
- label: "GOAL_PLAN setup evidence",
402
- mechanism: "log",
403
- description: "The exact planner output must be durable.",
404
- status: "ready",
405
- },
406
- ],
407
- });
408
- expect(hasRequiredGoalEvidence(run)).toEqual({
409
- ok: false,
410
- reason: "Goal evidence plan is not satisfied: GOAL_PLAN setup evidence (ready but no durable evidence, path, or command recorded).",
411
- });
412
- });
413
- it("rejects evidence-plan false positives before final audit", () => {
414
- const run = goalRun({
415
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
416
- evidencePlan: [
417
- {
418
- id: "proof",
419
- label: "Screenshot proof",
420
- mechanism: "screenshot",
421
- description: "Needs a real screenshot artifact.",
422
- status: "planned",
423
- path: "artifacts/screenshot.png",
424
- },
425
- ],
426
- evidence: [
427
- {
428
- id: "narrative-only",
429
- kind: "summary",
430
- label: "Screenshot proof",
431
- content: "I looked at the screen and it seemed fine.",
432
- createdAt: "2024-01-01T00:00:00.000Z",
433
- },
434
- ],
435
- verifier: {
436
- description: "Full check",
437
- command: "pnpm test",
438
- lastResult: {
439
- status: "pass",
440
- summary: "tests passed without screenshot path",
441
- command: "pnpm test",
442
- checkedAt: "2024-01-01T00:00:01.000Z",
443
- },
444
- },
445
- });
446
- expect(hasRequiredGoalEvidence(run)).toEqual({
447
- ok: false,
448
- reason: "Goal evidence plan is not satisfied: Screenshot proof (missing durable evidence for path artifacts/screenshot.png).",
449
- });
450
- expect(decideGoalNextAction(run)).toMatchObject({
451
- kind: "create_task",
452
- title: "Audit Goal completion evidence",
453
- });
454
- });
455
- it("does not create zero-item evidence reconciliation before final audit", () => {
456
- const run = goalRun({
457
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
458
- evidencePlan: [
459
- {
460
- id: "proof",
461
- label: "Matched proof",
462
- mechanism: "test",
463
- description: "Matched by verifier output.",
464
- status: "planned",
465
- command: "pnpm test",
466
- },
467
- ],
468
- verifier: {
469
- description: "Full check",
470
- command: "pnpm test",
471
- lastResult: {
472
- status: "pass",
473
- summary: "Matched proof passed.",
474
- command: "pnpm test",
475
- checkedAt: "2024-01-01T00:00:00.000Z",
476
- },
477
- },
478
- });
479
- expect(decideGoalNextAction(run)).toMatchObject({
480
- kind: "create_task",
481
- title: "Audit Goal completion evidence",
482
- });
483
- });
484
- it("uses final audit for real blocked-after-pass evidence-plan labels", () => {
485
- const decision = decideGoalNextAction(goalRun({
486
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
487
- evidencePlan: [
488
- {
489
- id: "slash-wrapper-evidence",
490
- label: "/goal slash wrapper evidence",
491
- mechanism: "test",
492
- description: "Assertions prove /goal prompt is short and delegates deep policy to active Goal setup system instructions.",
493
- status: "planned",
494
- command: "pnpm --filter @kenkaiiii/ggcoder test -- prompt-commands.test.ts slash-command-images.test.ts",
495
- },
496
- {
497
- id: "context-wiring-evidence",
498
- label: "Context wiring source audit",
499
- mechanism: "source",
500
- description: "Inspect App/render/cli/tools wiring to document exactly which context is shared through refs/session state and which context is isolated to workers.",
501
- status: "planned",
502
- path: "packages/ggcoder/src/ui/App.tsx; packages/ggcoder/src/ui/render.ts; packages/ggcoder/src/cli.ts; packages/ggcoder/src/tools/index.ts",
503
- },
504
- ],
505
- evidence: [
506
- {
507
- id: "focused-tests",
508
- kind: "command",
509
- label: "Focused Goal-mode test coverage command",
510
- content: "Command passed: pnpm --filter @kenkaiiii/ggcoder test -- system-prompt.test.ts prompt-commands.test.ts goal-mode.test.ts slash-command-images.test.ts footer-status-layout.test.ts goal-lifecycle-orchestration.test.ts",
511
- createdAt: "2024-01-01T00:00:00.000Z",
512
- },
513
- {
514
- id: "audit",
515
- kind: "summary",
516
- label: "Goal-mode architecture audit",
517
- content: "Inspected runtime-mode.ts, system-prompt.ts, prompt-commands.ts, cli.ts, ui/render.ts, ui/App.tsx, and tools/index.ts.",
518
- createdAt: "2024-01-01T00:00:01.000Z",
519
- },
520
- ],
521
- verifier: {
522
- description: "Full check",
523
- command: "pnpm check && pnpm lint && pnpm format:check && pnpm build",
524
- lastResult: {
525
- status: "pass",
526
- summary: "Focused tests, Goal E2E harness, and quality gates passed.",
527
- command: "pnpm check && pnpm lint && pnpm format:check && pnpm build",
528
- checkedAt: "2024-01-01T00:00:02.000Z",
529
- },
530
- },
531
- }));
532
- expect(decision).toMatchObject({
533
- kind: "create_task",
534
- title: "Audit Goal completion evidence",
535
- });
536
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("slash-wrapper-evidence / /goal slash wrapper evidence");
537
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("context-wiring-evidence / Context wiring source audit");
538
- });
539
- it("blocks after bounded final audit attempts fail to reconcile evidence", () => {
540
- const auditTask = {
541
- id: "audit-a",
542
- title: "Audit Goal completion evidence",
543
- prompt: "Audit evidence",
544
- status: "done",
545
- attempts: 1,
546
- };
547
- const decision = decideGoalNextAction(goalRun({
548
- tasks: [auditTask, { ...auditTask, id: "audit-b" }, { ...auditTask, id: "audit-c" }],
549
- evidencePlan: [
550
- {
551
- id: "proof",
552
- label: "Unmatched proof",
553
- mechanism: "browser",
554
- description: "Needs screenshot",
555
- status: "planned",
556
- },
557
- ],
558
- verifier: {
559
- description: "Full check",
560
- command: "pnpm test",
561
- lastResult: {
562
- status: "pass",
563
- summary: "tests passed",
564
- command: "pnpm test",
565
- checkedAt: "2024-01-01T00:00:00.000Z",
566
- },
567
- },
568
- }));
569
- expect(decision).toMatchObject({ kind: "terminal", status: "failed" });
570
- expect(decision.reason).toContain("Verifier passed, but the final completion audit could not reconcile the Goal evidence plan after bounded attempts.");
571
- });
572
- it("creates a main-checkout apply task before verifier when integration worktree changes exist", () => {
573
- expect(decideGoalNextAction(goalRun({
574
- tasks: [
575
- {
576
- id: "integrate",
577
- title: "Integrate candidates and verify",
578
- prompt: "Integrate accepted candidates",
579
- status: "done",
580
- attempts: 1,
581
- integration: "candidate",
582
- worktree: {
583
- baseRef: "base-sha",
584
- branchName: "goal/a/integrate-worker",
585
- path: "/tmp/worktrees/integrate-worker",
586
- status: "created",
587
- },
588
- candidate: {
589
- baseRef: "base-sha",
590
- headSha: "head-sha",
591
- branchName: "goal/a/integrate-worker",
592
- changedFiles: ["src/x.ts"],
593
- committed: true,
594
- },
595
- lastSummary: "Integrated accepted candidate patches and wrote integration.patch.",
596
- },
597
- ],
598
- verifier: { description: "Full check", command: "pnpm test" },
599
- }))).toMatchObject({
600
- kind: "create_task",
601
- title: "Apply integrated worktree to main",
602
- reason: "Accepted integration worktree changes must be applied to the user's main checkout before verifier, final audit, release, commit, or completion.",
603
- });
604
- });
605
- it("runs verifier after main integration, then completes when typed integration is committed", () => {
606
- const integratedTask = {
607
- id: "integrate",
608
- title: "Integrate candidates and verify",
609
- prompt: "Integrate accepted candidates",
610
- status: "done",
611
- attempts: 1,
612
- integration: "candidate",
613
- worktree: {
614
- baseRef: "base-sha",
615
- branchName: "goal/a/integrate-worker",
616
- path: "/tmp/worktrees/integrate-worker",
617
- status: "created",
618
- },
619
- candidate: {
620
- baseRef: "base-sha",
621
- headSha: "head-sha",
622
- branchName: "goal/a/integrate-worker",
623
- changedFiles: ["src/x.ts"],
624
- committed: true,
625
- },
626
- };
627
- const appliedEvidence = {
628
- id: "applied",
629
- kind: "summary",
630
- label: "Integrated worktree applied to main",
631
- content: "Accepted integration diff applied to main and checks passed.",
632
- createdAt: "2024-01-01T00:00:01.000Z",
633
- };
634
- expect(decideGoalNextAction(goalRun({
635
- tasks: [
636
- integratedTask,
637
- {
638
- id: "apply",
639
- title: "Apply integrated worktree to main",
640
- prompt: "Apply accepted changes",
641
- status: "done",
642
- attempts: 1,
643
- },
644
- ],
645
- verifier: { description: "Full check", command: "pnpm test" },
646
- evidence: [durablePlanEvidence, appliedEvidence],
647
- }))).toEqual({
648
- kind: "run_verifier",
649
- command: "pnpm test",
650
- reason: "All Goal tasks are done; running configured verifier for real completion evidence.",
651
- });
652
- const committed = withPassingCompletionAudit(goalRun({
653
- tasks: [
654
- integratedTask,
655
- {
656
- id: "apply",
657
- title: "Apply integrated worktree to main",
658
- prompt: "Apply accepted changes",
659
- status: "done",
660
- attempts: 1,
661
- },
662
- ],
663
- evidence: [durablePlanEvidence, appliedEvidence],
664
- integration: {
665
- status: "committed",
666
- headSha: "abc1234",
667
- updatedAt: "2024-01-01T00:00:03.000Z",
668
- },
669
- verifier: {
670
- description: "Full check",
671
- command: "pnpm test",
672
- lastResult: {
673
- status: "pass",
674
- summary: "main checkout verifier passed",
675
- command: "pnpm test",
676
- outputPath: ".goal-evidence/verifier.log",
677
- checkedAt: "2024-01-01T00:00:02.000Z",
678
- },
679
- },
680
- }));
681
- expect(decideGoalNextAction(committed)).toEqual({
682
- kind: "complete",
683
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
684
- });
685
- });
686
- it("creates a verifier-building task when done tasks have no verifier command", () => {
687
- expect(decideGoalNextAction(goalRun({
688
- verifier: undefined,
689
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
690
- }))).toMatchObject({
691
- kind: "create_task",
692
- title: "Define Goal verifier",
693
- reason: "No pending Goal task or verifier command is configured.",
694
- });
695
- });
696
- it("creates a mobile/UI evidence-path task before verifier execution when proof is only planned", () => {
697
- const decision = decideGoalNextAction(goalRun({
698
- goal: "Make the mobile checkout screen render correctly on small viewports",
699
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
700
- evidencePlan: [
701
- {
702
- id: "mobile-ui-proof",
703
- label: "iOS simulator screenshot comparison",
704
- mechanism: "screenshot",
705
- description: "Capture the mobile checkout screen in a local simulator or browser viewport and compare the image/frame output.",
706
- status: "planned",
707
- path: "artifacts/mobile-checkout-diff.png",
708
- },
709
- ],
710
- verifier: { description: "Full check", command: "pnpm test:e2e" },
711
- }));
712
- expect(decision).toMatchObject({
713
- kind: "create_task",
714
- title: "Build Goal evidence path",
715
- reason: "Goal evidence plan requires local instrumentation or exact prerequisite handling before verification.",
716
- });
717
- const prompt = decision.kind === "create_task" ? decision.prompt : "";
718
- expect(prompt).toContain("iOS simulator screenshot comparison (screenshot)");
719
- expect(prompt).toContain("goal-specific sensory intent");
720
- expect(prompt).toContain("what experience is being observed");
721
- expect(prompt).toContain("what failure it catches");
722
- expect(prompt).toContain("what signal proves it");
723
- expect(prompt).toContain("Build only the proportional instrument needed");
724
- expect(prompt).toContain("If the verifier artifact exists only in your isolated worker worktree");
725
- expect(prompt).toContain("set verifier_cwd to that worktree path");
726
- expect(prompt).toContain("copy/integrate the verifier artifact into the main checkout");
727
- expect(prompt).toContain("not use narrative-only verification or human visual inspection");
728
- });
729
- it("blocks when an evidence plan item requires a true external prerequisite", () => {
730
- expect(decideGoalNextAction(goalRun({
731
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
732
- evidencePlan: [
733
- {
734
- id: "device-proof",
735
- label: "Physical iPhone capture",
736
- mechanism: "device",
737
- description: "Run on a real phone.",
738
- status: "blocked",
739
- instructions: "Connect an unlocked iPhone with Developer Mode enabled.",
740
- },
741
- ],
742
- verifier: { description: "Full check", command: "pnpm test:e2e" },
743
- }))).toEqual({
744
- kind: "blocked",
745
- reason: "Physical iPhone capture: Connect an unlocked iPhone with Developer Mode enabled.",
746
- });
747
- });
748
- it("creates a harness-building task before verifier execution when instrumentation is missing", () => {
749
- const decision = decideGoalNextAction(goalRun({
750
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
751
- harness: [{ id: "harness-a", label: "Browser fixture", description: "Create fixture" }],
752
- verifier: { description: "Full check", command: "pnpm test:e2e" },
753
- }));
754
- expect(decision).toMatchObject({
755
- kind: "create_task",
756
- title: "Build Goal verification harness",
757
- reason: "Goal harness requires local instrumentation before verification.",
758
- });
759
- const prompt = decision.kind === "create_task" ? decision.prompt : "";
760
- expect(prompt).toContain("Build only the missing local/free harness instrumentation");
761
- expect(prompt).toContain("intended experience");
762
- expect(prompt).toContain("relevant failure modes");
763
- expect(prompt).toContain("senses/signals this harness must observe");
764
- expect(prompt).toContain("do not default to generic tests");
765
- });
766
- it("blocks missing prerequisites with exact user instructions", () => {
767
- expect(decideGoalNextAction(goalRun({
768
- prerequisites: [
769
- {
770
- id: "api-key",
771
- label: "Demo API key",
772
- status: "missing",
773
- instructions: "Provide DEMO_API_KEY in the local environment.",
774
- },
775
- ],
776
- }))).toEqual({
777
- kind: "blocked",
778
- reason: "Demo API key: Provide DEMO_API_KEY in the local environment.",
779
- });
780
- });
781
- it("creates a bounded fix task for verifier failure when resumed", () => {
782
- const run = goalRun({
783
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
784
- verifier: {
785
- description: "Full check",
786
- command: "pnpm test",
787
- lastResult: {
788
- status: "fail",
789
- summary: "tests failed",
790
- checkedAt: "2024-01-01T00:00:00.000Z",
791
- exitCode: 1,
792
- outputPath: ".gg/log.log",
793
- },
794
- },
795
- evidence: [
796
- {
797
- id: "evidence-a",
798
- kind: "command",
799
- label: "Verifier fail",
800
- content: "tests failed",
801
- path: ".gg/log.log",
802
- createdAt: "2024-01-01T00:00:00.000Z",
803
- },
804
- ],
805
- });
806
- const decision = decideGoalNextAction(run);
807
- expect(decision).toMatchObject({
808
- kind: "create_task",
809
- title: "Fix verifier failure",
810
- reason: "Verifier failed; creating bounded fix task 1/5.",
811
- });
812
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("Output path: .gg/log.log");
813
- });
814
- it("blocks repeated identical verifier failures instead of looping forever", () => {
815
- const run = goalRun({
816
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
817
- verifier: {
818
- description: "Full check",
819
- command: "pnpm test",
820
- lastResult: {
821
- status: "fail",
822
- summary: "same",
823
- checkedAt: "2024-01-01T00:00:00.000Z",
824
- exitCode: 1,
825
- },
826
- },
827
- evidence: [
828
- {
829
- id: "e1",
830
- kind: "command",
831
- label: "Verifier fail",
832
- content: "same",
833
- createdAt: "2024-01-01T00:00:00.000Z",
834
- },
835
- {
836
- id: "e2",
837
- kind: "command",
838
- label: "Verifier fail",
839
- content: "same",
840
- createdAt: "2024-01-01T00:00:01.000Z",
841
- },
842
- ],
843
- });
844
- expect(decideGoalNextAction(run)).toMatchObject({
845
- kind: "create_task",
846
- title: "Re-strategize Goal approach",
847
- reason: "Verifier produced the same failure repeatedly; re-strategizing with a fundamentally different approach. (1/2)",
848
- });
849
- });
850
- it("treats failed status as terminal unless a later tool action revives it to ready", () => {
851
- const run = goalRun({
852
- status: "failed",
853
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
854
- verifier: {
855
- description: "Full check",
856
- command: "pnpm test",
857
- lastResult: {
858
- status: "fail",
859
- summary: "tests failed",
860
- checkedAt: "2024-01-01T00:00:00.000Z",
861
- exitCode: 1,
862
- },
863
- },
864
- evidence: [
865
- {
866
- id: "evidence-a",
867
- kind: "command",
868
- label: "Verifier result",
869
- content: "tests failed",
870
- createdAt: "2024-01-01T00:00:00.000Z",
871
- },
872
- ],
873
- });
874
- expect(canCompleteGoalRun(run)).toEqual({ ok: false, reason: "Verifier status is fail." });
875
- expect(decideGoalNextAction(run)).toEqual({
876
- kind: "terminal",
877
- status: "failed",
878
- reason: "Goal is failed.",
879
- });
880
- });
881
- it("rejects spoofed final audit pass summaries", () => {
882
- const run = goalRun({
883
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
884
- verifier: {
885
- description: "Full check",
886
- command: "pnpm test",
887
- lastResult: {
888
- status: "pass",
889
- summary: "passed original-goal-prompt GOAL_PLAN",
890
- checkedAt: "2024-01-01T00:00:02.000Z",
891
- outputPath: "artifacts/verifier.log",
892
- },
893
- },
894
- completionAudit: {
895
- status: "pass",
896
- summary: "FINAL_AUDIT_PASS verifier_checked_at=2024-01-01T00:00:01.000Z artifact=artifacts/verifier.log original-goal-prompt GOAL_PLAN",
897
- checkedAt: "2024-01-01T00:00:03.000Z",
898
- verifierCheckedAt: "2024-01-01T00:00:01.000Z",
899
- outputPath: "artifacts/verifier.log",
900
- },
901
- });
902
- expect(hasFreshGoalCompletionAudit(run)).toEqual({
903
- ok: false,
904
- reason: "Final completion audit does not match the latest verifier result.",
905
- });
906
- expect(canCompleteGoalRun(run)).toEqual({
907
- ok: false,
908
- reason: "Final completion audit does not match the latest verifier result.",
909
- });
910
- });
911
- it("completes only with all tasks done and pass verifier evidence", () => {
912
- const run = goalRun({
913
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
914
- verifier: {
915
- description: "Full check",
916
- command: "pnpm test",
917
- lastResult: {
918
- status: "pass",
919
- summary: "passed original-goal-prompt GOAL_PLAN",
920
- checkedAt: "2024-01-01T00:00:00.000Z",
921
- outputPath: "out.log",
922
- },
923
- },
924
- });
925
- expect(canCompleteGoalRun(run)).toEqual({
926
- ok: false,
927
- reason: "Goal has no final completion audit.",
928
- });
929
- expect(decideGoalNextAction(run)).toMatchObject({
930
- kind: "create_task",
931
- title: "Audit Goal completion evidence",
932
- reason: "Verifier passed; creating final read-only completion audit before the Goal can pass (1/3).",
933
- });
934
- const audited = withPassingCompletionAudit(run);
935
- expect(canCompleteGoalRun(audited)).toEqual({
936
- ok: true,
937
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
938
- });
939
- expect(decideGoalNextAction(audited)).toEqual({
940
- kind: "complete",
941
- reason: "All tasks are done, verifier evidence passed, and final completion audit passed.",
942
- });
943
- });
944
- it("reruns the verifier when a substantive worker completed after the latest verifier pass", () => {
945
- const run = goalRun({
946
- tasks: [{ id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 }],
947
- lastSubstantiveWorkerAt: "2024-01-01T00:00:02.000Z",
948
- verifier: {
949
- description: "Full check",
950
- command: "pnpm test",
951
- lastResult: {
952
- status: "pass",
953
- summary: "passed original-goal-prompt GOAL_PLAN",
954
- command: "pnpm test",
955
- checkedAt: "2024-01-01T00:00:00.000Z",
956
- outputPath: "out.log",
957
- },
958
- },
959
- completionAudit: {
960
- status: "pass",
961
- summary: "FINAL_AUDIT_PASS verifier_checked_at=2024-01-01T00:00:00.000Z original-goal-prompt GOAL_PLAN",
962
- checkedAt: "2024-01-01T00:00:03.000Z",
963
- verifierCheckedAt: "2024-01-01T00:00:00.000Z",
964
- outputPath: "out.log",
965
- },
966
- });
967
- expect(hasFreshGoalCompletionAudit(run)).toEqual({
968
- ok: false,
969
- reason: "Latest verifier result is stale after a later substantive Goal worker completion.",
970
- });
971
- expect(decideGoalNextAction(run)).toEqual({
972
- kind: "run_verifier",
973
- command: "pnpm test",
974
- reason: "Latest verifier result is stale after later Goal worker evidence; rerunning configured verifier as the final pre-audit gate.",
975
- });
976
- });
977
- it("does not complete when verifier passed but tasks remain", () => {
978
- const run = goalRun({
979
- tasks: [
980
- { id: "done", title: "Done", prompt: "Done", status: "done", attempts: 1 },
981
- { id: "pending", title: "Pending", prompt: "Pending", status: "pending", attempts: 0 },
982
- ],
983
- verifier: {
984
- description: "Full check",
985
- command: "pnpm test",
986
- lastResult: {
987
- status: "pass",
988
- summary: "passed original-goal-prompt GOAL_PLAN",
989
- checkedAt: "2024-01-01T00:00:00.000Z",
990
- },
991
- },
992
- });
993
- expect(canCompleteGoalRun(run)).toEqual({ ok: false, reason: "1 Goal task is not done." });
994
- expect(decideGoalNextAction(run)).toMatchObject({ kind: "start_worker" });
995
- });
996
- it("retries a failed task below the attempt limit for corrective work", () => {
997
- const task = {
998
- id: "task-a",
999
- title: "Repair verifier failure",
1000
- prompt: "Fix the verifier failure using persisted evidence",
1001
- status: "failed",
1002
- attempts: 1,
1003
- lastSummary: "Verifier failed: assertion mismatch",
1004
- };
1005
- expect(decideGoalNextAction(goalRun({ tasks: [task] }))).toEqual({
1006
- kind: "start_worker",
1007
- task,
1008
- attempts: 2,
1009
- reason: 'Goal task "Repair verifier failure" is ready for worker attempt 2.',
1010
- });
1011
- });
1012
- it("formats durable-readable controller decisions", () => {
1013
- const decision = decideGoalNextAction(goalRun({ tasks: [] }));
1014
- const formatted = formatGoalControllerDecision(decision);
1015
- expect(formatted.label).toBe(`Goal decision: ${decision.kind}`);
1016
- expect(formatted.content).toContain(`kind=${decision.kind}`);
1017
- expect(shouldClearGoalContinuation({ kind: "wait", reason: "active" })).toBe(false);
1018
- expect(shouldClearGoalContinuation(decision)).toBe(true);
1019
- });
1020
- it("re-strategizes on no-progress repeats, then fails with a diagnosis past the strategy limit", () => {
1021
- const task = {
1022
- id: "task-a",
1023
- title: "Flaky repair",
1024
- prompt: "Do work",
1025
- status: "failed",
1026
- attempts: 5,
1027
- workerId: "worker-a",
1028
- };
1029
- const repeatedEvidence = [
1030
- {
1031
- id: "e1",
1032
- kind: "log",
1033
- label: "Worker worker-a failed",
1034
- content: "same crash",
1035
- createdAt: "2024-01-01T00:00:00.000Z",
1036
- },
1037
- {
1038
- id: "e2",
1039
- kind: "log",
1040
- label: "Worker worker-a failed",
1041
- content: "same crash",
1042
- createdAt: "2024-01-01T00:00:01.000Z",
1043
- },
1044
- ];
1045
- // No-progress repeat below the strategy limit -> create a re-strategy task.
1046
- const reStrategy = decideGoalNextAction(goalRun({ tasks: [task], evidence: repeatedEvidence }));
1047
- expect(reStrategy).toMatchObject({
1048
- kind: "create_task",
1049
- title: "Re-strategize Goal approach",
1050
- });
1051
- // Past the bounded strategy limit (with done re-strategy tasks) -> terminal failed.
1052
- const exhausted = decideGoalNextAction(goalRun({
1053
- tasks: [
1054
- task,
1055
- {
1056
- id: "s1",
1057
- title: "Re-strategize Goal approach",
1058
- prompt: "x",
1059
- status: "done",
1060
- attempts: 1,
1061
- },
1062
- {
1063
- id: "s2",
1064
- title: "Re-strategize Goal approach",
1065
- prompt: "x",
1066
- status: "done",
1067
- attempts: 1,
1068
- },
1069
- ],
1070
- evidence: repeatedEvidence,
1071
- }));
1072
- expect(exhausted).toMatchObject({ kind: "terminal", status: "failed" });
1073
- expect(exhausted.reason).toContain("GOAL_FAILURE_DIAGNOSIS");
1074
- expect(exhausted.reason).toContain("Flaky repair");
1075
- });
1076
- it("keeps retrying past the soft attempt limit while still making progress", () => {
1077
- const task = {
1078
- id: "task-a",
1079
- title: "Improving repair",
1080
- prompt: "Do work",
1081
- status: "failed",
1082
- attempts: 5,
1083
- workerId: "worker-a",
1084
- };
1085
- const progressingEvidence = [
1086
- {
1087
- id: "e1",
1088
- kind: "log",
1089
- label: "Worker worker-a failed",
1090
- content: "error A",
1091
- createdAt: "2024-01-01T00:00:00.000Z",
1092
- },
1093
- {
1094
- id: "e2",
1095
- kind: "log",
1096
- label: "Worker worker-a failed",
1097
- content: "error B (different)",
1098
- createdAt: "2024-01-01T00:00:01.000Z",
1099
- },
1100
- ];
1101
- expect(decideGoalNextAction(goalRun({ tasks: [task], evidence: progressingEvidence }))).toMatchObject({ kind: "start_worker", attempts: 6 });
1102
- });
1103
- it("does not create duplicate auto evidence or harness tasks", () => {
1104
- const evidenceTask = {
1105
- id: "evidence-task",
1106
- title: "Build Goal evidence path",
1107
- prompt: "Build proof",
1108
- status: "done",
1109
- attempts: 1,
1110
- };
1111
- expect(decideGoalNextAction(goalRun({
1112
- tasks: [evidenceTask],
1113
- evidencePlan: [
1114
- {
1115
- id: "missing-proof",
1116
- label: "Missing proof",
1117
- mechanism: "command",
1118
- description: "Needs a command artifact.",
1119
- status: "planned",
1120
- command: "pnpm missing-proof",
1121
- },
1122
- ],
1123
- verifier: { description: "Verifier", command: "pnpm verify" },
1124
- }))).toEqual({
1125
- kind: "blocked",
1126
- reason: 'Goal auto-task "Build Goal evidence path" already exists with status done; not creating a duplicate. Reconcile its evidence or update the existing task before continuing. Goal evidence plan still requires local instrumentation or exact prerequisite handling before verification.',
1127
- });
1128
- const harnessTask = {
1129
- id: "harness-task",
1130
- title: "Build Goal verification harness",
1131
- prompt: "Build harness",
1132
- status: "blocked",
1133
- attempts: 1,
1134
- };
1135
- expect(decideGoalNextAction(goalRun({
1136
- tasks: [harnessTask],
1137
- harness: [{ id: "harness", label: "Harness", description: "Needs instrumentation" }],
1138
- verifier: { description: "Verifier" },
1139
- }))).toEqual({
1140
- kind: "blocked",
1141
- reason: 'Goal auto-task "Build Goal verification harness" already exists with status blocked; not creating a duplicate. Reconcile its evidence or update the existing task before continuing. Goal harness still requires local instrumentation before verification.',
1142
- });
1143
- });
1144
- it("reuses existing pending auto tasks instead of creating duplicates", () => {
1145
- const verifierTask = {
1146
- id: "verifier-task",
1147
- title: "Define Goal verifier",
1148
- prompt: "Define verifier",
1149
- status: "pending",
1150
- attempts: 1,
1151
- };
1152
- expect(decideGoalNextAction(goalRun({ tasks: [verifierTask], verifier: { description: "Verifier" } }))).toEqual({
1153
- kind: "start_worker",
1154
- task: verifierTask,
1155
- attempts: 2,
1156
- reason: 'Goal task "Define Goal verifier" is ready for worker attempt 2.',
1157
- });
1158
- });
1159
- it("blocks rather than creating another verifier-fix task when one is already blocked", () => {
1160
- const run = goalRun({
1161
- tasks: [
1162
- { id: "task-a", title: "Done", prompt: "Done", status: "done", attempts: 1 },
1163
- {
1164
- id: "fix-a",
1165
- title: "Fix verifier failure",
1166
- prompt: "Fix it",
1167
- status: "blocked",
1168
- attempts: 1,
1169
- },
1170
- ],
1171
- verifier: {
1172
- description: "Full check",
1173
- command: "pnpm test",
1174
- lastResult: {
1175
- status: "fail",
1176
- summary: "tests failed",
1177
- checkedAt: "2024-01-01T00:00:00.000Z",
1178
- exitCode: 1,
1179
- },
1180
- },
1181
- });
1182
- expect(decideGoalNextAction(run)).toMatchObject({
1183
- kind: "create_task",
1184
- title: "Re-strategize Goal approach",
1185
- reason: "A blocked verifier-fix task exists; re-strategizing the verifier fix with a different approach. (1/2)",
1186
- });
1187
- });
1188
- it("builds a structured failure diagnosis brief", () => {
1189
- const diagnosis = buildGoalFailureDiagnosis(goalRun({
1190
- goal: "Ship the widget",
1191
- tasks: [
1192
- { id: "task-a", title: "Build widget", prompt: "x", status: "failed", attempts: 6 },
1193
- ],
1194
- prerequisites: [
1195
- {
1196
- id: "key",
1197
- label: "Vendor API key",
1198
- status: "missing",
1199
- kind: "external",
1200
- instructions: "Provide VENDOR_KEY.",
1201
- },
1202
- ],
1203
- verifier: {
1204
- description: "check",
1205
- command: "pnpm test",
1206
- lastResult: {
1207
- status: "fail",
1208
- summary: "2 failing tests",
1209
- checkedAt: "2024-01-01T00:00:00.000Z",
1210
- exitCode: 1,
1211
- },
1212
- },
1213
- evidence: [
1214
- {
1215
- id: "e1",
1216
- kind: "command",
1217
- label: "Verifier fail",
1218
- content: "2 failing tests",
1219
- createdAt: "2024-01-01T00:00:00.000Z",
1220
- },
1221
- ],
1222
- }), "Bounded re-strategy exhausted.");
1223
- expect(diagnosis).toContain("GOAL_FAILURE_DIAGNOSIS");
1224
- expect(diagnosis).toContain("objective=Ship the widget");
1225
- expect(diagnosis).toContain("reason=Bounded re-strategy exhausted.");
1226
- expect(diagnosis).toContain("Build widget (task-a): status=failed; attempts=6");
1227
- expect(diagnosis).toContain("2 failing tests");
1228
- expect(diagnosis).toContain("Vendor API key: Provide VENDOR_KEY.");
1229
- });
1230
- it("resolves unmet local prerequisites with a worker task instead of blocking", () => {
1231
- const decision = decideGoalNextAction(goalRun({
1232
- prerequisites: [
1233
- {
1234
- id: "node",
1235
- label: "Node toolchain",
1236
- status: "missing",
1237
- checkCommand: "node --version",
1238
- },
1239
- ],
1240
- }));
1241
- expect(decision).toMatchObject({
1242
- kind: "create_task",
1243
- title: "Resolve local Goal prerequisites",
1244
- });
1245
- expect(decision.kind === "create_task" ? decision.prompt : "").toContain("Node toolchain");
1246
- });
1247
- it("hard-fails with a diagnosis once the decision ceiling is exceeded", () => {
1248
- const decisionEvidence = Array.from({ length: 4 }, (_unused, index) => ({
1249
- id: `d${index}`,
1250
- kind: "summary",
1251
- label: "Goal decision: start_worker",
1252
- content: `decision ${index}`,
1253
- createdAt: `2024-01-01T00:00:0${index}.000Z`,
1254
- }));
1255
- const decision = decideGoalNextAction(goalRun({
1256
- tasks: [{ id: "t", title: "T", prompt: "x", status: "pending", attempts: 0 }],
1257
- evidence: decisionEvidence,
1258
- }), { decisionLimit: 3 });
1259
- expect(decision).toMatchObject({ kind: "terminal", status: "failed" });
1260
- expect(decision.reason).toContain("maximum of 3 controller decisions");
1261
- });
1262
- it("still blocks on unmet external prerequisites", () => {
1263
- expect(decideGoalNextAction(goalRun({
1264
- prerequisites: [
1265
- {
1266
- id: "api-key",
1267
- label: "Demo API key",
1268
- status: "missing",
1269
- kind: "external",
1270
- instructions: "Provide DEMO_API_KEY in the local environment.",
1271
- },
1272
- ],
1273
- }))).toEqual({
1274
- kind: "blocked",
1275
- reason: "Demo API key: Provide DEMO_API_KEY in the local environment.",
1276
- });
1277
- });
1278
- });
1279
- //# sourceMappingURL=goal-controller.test.js.map