@kenkaiiii/ggcoder 4.3.230 → 4.3.232

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 (255) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +1 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +7 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/config.test.d.ts +2 -0
  9. package/dist/config.test.d.ts.map +1 -0
  10. package/dist/config.test.js +29 -0
  11. package/dist/config.test.js.map +1 -0
  12. package/dist/core/goal-controller.d.ts +27 -6
  13. package/dist/core/goal-controller.d.ts.map +1 -1
  14. package/dist/core/goal-controller.js +246 -141
  15. package/dist/core/goal-controller.js.map +1 -1
  16. package/dist/core/goal-controller.test.js +210 -79
  17. package/dist/core/goal-controller.test.js.map +1 -1
  18. package/dist/core/goal-engine.d.ts +61 -0
  19. package/dist/core/goal-engine.d.ts.map +1 -0
  20. package/dist/core/goal-engine.js +123 -0
  21. package/dist/core/goal-engine.js.map +1 -0
  22. package/dist/core/goal-engine.test.d.ts +2 -0
  23. package/dist/core/goal-engine.test.d.ts.map +1 -0
  24. package/dist/core/goal-engine.test.js +295 -0
  25. package/dist/core/goal-engine.test.js.map +1 -0
  26. package/dist/core/goal-integration.d.ts +80 -0
  27. package/dist/core/goal-integration.d.ts.map +1 -0
  28. package/dist/core/goal-integration.js +296 -0
  29. package/dist/core/goal-integration.js.map +1 -0
  30. package/dist/core/goal-integration.test.d.ts +2 -0
  31. package/dist/core/goal-integration.test.d.ts.map +1 -0
  32. package/dist/core/goal-integration.test.js +369 -0
  33. package/dist/core/goal-integration.test.js.map +1 -0
  34. package/dist/core/goal-prerequisites.test.js +37 -1
  35. package/dist/core/goal-prerequisites.test.js.map +1 -1
  36. package/dist/core/goal-store.d.ts +80 -1
  37. package/dist/core/goal-store.d.ts.map +1 -1
  38. package/dist/core/goal-store.js +232 -65
  39. package/dist/core/goal-store.js.map +1 -1
  40. package/dist/core/goal-store.test.js +111 -2
  41. package/dist/core/goal-store.test.js.map +1 -1
  42. package/dist/core/goal-worker.d.ts.map +1 -1
  43. package/dist/core/goal-worker.js +55 -4
  44. package/dist/core/goal-worker.js.map +1 -1
  45. package/dist/core/goal-worker.test.js +75 -6
  46. package/dist/core/goal-worker.test.js.map +1 -1
  47. package/dist/core/goal-worktree.d.ts +55 -1
  48. package/dist/core/goal-worktree.d.ts.map +1 -1
  49. package/dist/core/goal-worktree.js +164 -1
  50. package/dist/core/goal-worktree.js.map +1 -1
  51. package/dist/core/goal-worktree.test.js +226 -4
  52. package/dist/core/goal-worktree.test.js.map +1 -1
  53. package/dist/core/ideal-review.d.ts +20 -0
  54. package/dist/core/ideal-review.d.ts.map +1 -0
  55. package/dist/core/ideal-review.js +55 -0
  56. package/dist/core/ideal-review.js.map +1 -0
  57. package/dist/core/ideal-review.test.d.ts +2 -0
  58. package/dist/core/ideal-review.test.d.ts.map +1 -0
  59. package/dist/core/ideal-review.test.js +59 -0
  60. package/dist/core/ideal-review.test.js.map +1 -0
  61. package/dist/core/settings-manager.d.ts +1 -0
  62. package/dist/core/settings-manager.d.ts.map +1 -1
  63. package/dist/core/settings-manager.js +2 -0
  64. package/dist/core/settings-manager.js.map +1 -1
  65. package/dist/system-prompt.d.ts.map +1 -1
  66. package/dist/system-prompt.js +9 -6
  67. package/dist/system-prompt.js.map +1 -1
  68. package/dist/system-prompt.test.js +1 -1
  69. package/dist/system-prompt.test.js.map +1 -1
  70. package/dist/tools/edit-diff.d.ts +17 -4
  71. package/dist/tools/edit-diff.d.ts.map +1 -1
  72. package/dist/tools/edit-diff.js +78 -16
  73. package/dist/tools/edit-diff.js.map +1 -1
  74. package/dist/tools/edit-diff.test.js +31 -1
  75. package/dist/tools/edit-diff.test.js.map +1 -1
  76. package/dist/tools/edit-robustness.test.d.ts +2 -0
  77. package/dist/tools/edit-robustness.test.d.ts.map +1 -0
  78. package/dist/tools/edit-robustness.test.js +415 -0
  79. package/dist/tools/edit-robustness.test.js.map +1 -0
  80. package/dist/tools/edit.d.ts.map +1 -1
  81. package/dist/tools/edit.js +15 -16
  82. package/dist/tools/edit.js.map +1 -1
  83. package/dist/tools/edit.test.js +23 -12
  84. package/dist/tools/edit.test.js.map +1 -1
  85. package/dist/tools/goals.d.ts +9 -1
  86. package/dist/tools/goals.d.ts.map +1 -1
  87. package/dist/tools/goals.js +78 -25
  88. package/dist/tools/goals.js.map +1 -1
  89. package/dist/tools/goals.test.js +142 -11
  90. package/dist/tools/goals.test.js.map +1 -1
  91. package/dist/tools/html-extract.d.ts +58 -0
  92. package/dist/tools/html-extract.d.ts.map +1 -0
  93. package/dist/tools/html-extract.js +130 -0
  94. package/dist/tools/html-extract.js.map +1 -0
  95. package/dist/tools/html-extract.test.d.ts +2 -0
  96. package/dist/tools/html-extract.test.d.ts.map +1 -0
  97. package/dist/tools/html-extract.test.js +60 -0
  98. package/dist/tools/html-extract.test.js.map +1 -0
  99. package/dist/tools/pdf-extract.d.ts +18 -0
  100. package/dist/tools/pdf-extract.d.ts.map +1 -0
  101. package/dist/tools/pdf-extract.js +43 -0
  102. package/dist/tools/pdf-extract.js.map +1 -0
  103. package/dist/tools/pdf-extract.test.d.ts +2 -0
  104. package/dist/tools/pdf-extract.test.d.ts.map +1 -0
  105. package/dist/tools/pdf-extract.test.js +15 -0
  106. package/dist/tools/pdf-extract.test.js.map +1 -0
  107. package/dist/tools/prompt-hints.d.ts.map +1 -1
  108. package/dist/tools/prompt-hints.js +2 -1
  109. package/dist/tools/prompt-hints.js.map +1 -1
  110. package/dist/tools/web-fetch.d.ts +16 -1
  111. package/dist/tools/web-fetch.d.ts.map +1 -1
  112. package/dist/tools/web-fetch.js +357 -45
  113. package/dist/tools/web-fetch.js.map +1 -1
  114. package/dist/tools/web-fetch.test.js +263 -2
  115. package/dist/tools/web-fetch.test.js.map +1 -1
  116. package/dist/tools/web-search.d.ts +14 -0
  117. package/dist/tools/web-search.d.ts.map +1 -1
  118. package/dist/tools/web-search.js +321 -35
  119. package/dist/tools/web-search.js.map +1 -1
  120. package/dist/tools/web-search.test.js +144 -1
  121. package/dist/tools/web-search.test.js.map +1 -1
  122. package/dist/ui/App.d.ts +11 -1
  123. package/dist/ui/App.d.ts.map +1 -1
  124. package/dist/ui/App.js +132 -17
  125. package/dist/ui/App.js.map +1 -1
  126. package/dist/ui/app-items.d.ts +14 -1
  127. package/dist/ui/app-items.d.ts.map +1 -1
  128. package/dist/ui/app-items.js +2 -0
  129. package/dist/ui/app-items.js.map +1 -1
  130. package/dist/ui/components/ChatLayout.d.ts +8 -1
  131. package/dist/ui/components/ChatLayout.d.ts.map +1 -1
  132. package/dist/ui/components/ChatLayout.js +4 -1
  133. package/dist/ui/components/ChatLayout.js.map +1 -1
  134. package/dist/ui/components/ChatScreen.d.ts +14 -1
  135. package/dist/ui/components/ChatScreen.d.ts.map +1 -1
  136. package/dist/ui/components/ChatScreen.js +3 -2
  137. package/dist/ui/components/ChatScreen.js.map +1 -1
  138. package/dist/ui/components/IdealHookMessage.d.ts +12 -0
  139. package/dist/ui/components/IdealHookMessage.d.ts.map +1 -0
  140. package/dist/ui/components/IdealHookMessage.js +24 -0
  141. package/dist/ui/components/IdealHookMessage.js.map +1 -0
  142. package/dist/ui/components/InputArea.d.ts +13 -1
  143. package/dist/ui/components/InputArea.d.ts.map +1 -1
  144. package/dist/ui/components/InputArea.js +96 -12
  145. package/dist/ui/components/InputArea.js.map +1 -1
  146. package/dist/ui/components/TranscriptViewport.d.ts +34 -0
  147. package/dist/ui/components/TranscriptViewport.d.ts.map +1 -0
  148. package/dist/ui/components/TranscriptViewport.js +45 -0
  149. package/dist/ui/components/TranscriptViewport.js.map +1 -0
  150. package/dist/ui/footer-jump-regression.test.d.ts +2 -0
  151. package/dist/ui/footer-jump-regression.test.d.ts.map +1 -0
  152. package/dist/ui/footer-jump-regression.test.js +167 -0
  153. package/dist/ui/footer-jump-regression.test.js.map +1 -0
  154. package/dist/ui/goal-events.d.ts +1 -1
  155. package/dist/ui/goal-events.d.ts.map +1 -1
  156. package/dist/ui/goal-events.js +2 -2
  157. package/dist/ui/goal-events.js.map +1 -1
  158. package/dist/ui/goal-events.test.js +2 -2
  159. package/dist/ui/goal-events.test.js.map +1 -1
  160. package/dist/ui/goal-lifecycle-orchestration.test.js +13 -97
  161. package/dist/ui/goal-lifecycle-orchestration.test.js.map +1 -1
  162. package/dist/ui/goal-progress.d.ts.map +1 -1
  163. package/dist/ui/goal-progress.js +0 -1
  164. package/dist/ui/goal-progress.js.map +1 -1
  165. package/dist/ui/goal-run-helpers.d.ts +0 -4
  166. package/dist/ui/goal-run-helpers.d.ts.map +1 -1
  167. package/dist/ui/goal-run-helpers.js +2 -21
  168. package/dist/ui/goal-run-helpers.js.map +1 -1
  169. package/dist/ui/hooks/useAgentLoop.d.ts +2 -0
  170. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  171. package/dist/ui/hooks/useAgentLoop.js +45 -1
  172. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  173. package/dist/ui/hooks/useChatLayoutMeasurements.d.ts +8 -0
  174. package/dist/ui/hooks/useChatLayoutMeasurements.d.ts.map +1 -1
  175. package/dist/ui/hooks/useChatLayoutMeasurements.js +2 -0
  176. package/dist/ui/hooks/useChatLayoutMeasurements.js.map +1 -1
  177. package/dist/ui/hooks/useGoalOrchestration.d.ts +2 -1
  178. package/dist/ui/hooks/useGoalOrchestration.d.ts.map +1 -1
  179. package/dist/ui/hooks/useGoalOrchestration.js +198 -80
  180. package/dist/ui/hooks/useGoalOrchestration.js.map +1 -1
  181. package/dist/ui/hooks/useTerminalSize.d.ts +8 -1
  182. package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -1
  183. package/dist/ui/hooks/useTerminalSize.js +12 -2
  184. package/dist/ui/hooks/useTerminalSize.js.map +1 -1
  185. package/dist/ui/hooks/useTranscriptHistory.d.ts +1 -1
  186. package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
  187. package/dist/ui/hooks/useTranscriptHistory.js +23 -9
  188. package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
  189. package/dist/ui/hooks/useTranscriptHistory.test.d.ts +2 -0
  190. package/dist/ui/hooks/useTranscriptHistory.test.d.ts.map +1 -0
  191. package/dist/ui/hooks/useTranscriptHistory.test.js +61 -0
  192. package/dist/ui/hooks/useTranscriptHistory.test.js.map +1 -0
  193. package/dist/ui/hooks/useTranscriptScroll.d.ts +30 -0
  194. package/dist/ui/hooks/useTranscriptScroll.d.ts.map +1 -0
  195. package/dist/ui/hooks/useTranscriptScroll.js +44 -0
  196. package/dist/ui/hooks/useTranscriptScroll.js.map +1 -0
  197. package/dist/ui/hooks/useTranscriptScroll.test.d.ts +2 -0
  198. package/dist/ui/hooks/useTranscriptScroll.test.d.ts.map +1 -0
  199. package/dist/ui/hooks/useTranscriptScroll.test.js +119 -0
  200. package/dist/ui/hooks/useTranscriptScroll.test.js.map +1 -0
  201. package/dist/ui/live-area-clamp.test.js +11 -2
  202. package/dist/ui/live-area-clamp.test.js.map +1 -1
  203. package/dist/ui/render.d.ts +11 -0
  204. package/dist/ui/render.d.ts.map +1 -1
  205. package/dist/ui/render.js +71 -7
  206. package/dist/ui/render.js.map +1 -1
  207. package/dist/ui/stores/transcript-scroll-store.d.ts +27 -0
  208. package/dist/ui/stores/transcript-scroll-store.d.ts.map +1 -0
  209. package/dist/ui/stores/transcript-scroll-store.js +73 -0
  210. package/dist/ui/stores/transcript-scroll-store.js.map +1 -0
  211. package/dist/ui/stores/transcript-scroll-store.test.d.ts +2 -0
  212. package/dist/ui/stores/transcript-scroll-store.test.d.ts.map +1 -0
  213. package/dist/ui/stores/transcript-scroll-store.test.js +73 -0
  214. package/dist/ui/stores/transcript-scroll-store.test.js.map +1 -0
  215. package/dist/ui/terminal-history.d.ts.map +1 -1
  216. package/dist/ui/terminal-history.js +8 -0
  217. package/dist/ui/terminal-history.js.map +1 -1
  218. package/dist/ui/testing/screen-recorder.d.ts +29 -0
  219. package/dist/ui/testing/screen-recorder.d.ts.map +1 -0
  220. package/dist/ui/testing/screen-recorder.js +179 -0
  221. package/dist/ui/testing/screen-recorder.js.map +1 -0
  222. package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
  223. package/dist/ui/transcript/TranscriptRenderer.js +4 -1
  224. package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
  225. package/dist/ui/transcript/spacing.d.ts +3 -2
  226. package/dist/ui/transcript/spacing.d.ts.map +1 -1
  227. package/dist/ui/transcript/spacing.js +8 -0
  228. package/dist/ui/transcript/spacing.js.map +1 -1
  229. package/dist/ui/transcript/spacing.test.js +31 -0
  230. package/dist/ui/transcript/spacing.test.js.map +1 -1
  231. package/dist/ui/transcript/transcript-lines.d.ts +32 -0
  232. package/dist/ui/transcript/transcript-lines.d.ts.map +1 -0
  233. package/dist/ui/transcript/transcript-lines.js +82 -0
  234. package/dist/ui/transcript/transcript-lines.js.map +1 -0
  235. package/dist/ui/transcript/transcript-lines.test.d.ts +2 -0
  236. package/dist/ui/transcript/transcript-lines.test.d.ts.map +1 -0
  237. package/dist/ui/transcript/transcript-lines.test.js +76 -0
  238. package/dist/ui/transcript/transcript-lines.test.js.map +1 -0
  239. package/dist/ui/transcript-viewport-pinning.test.d.ts +2 -0
  240. package/dist/ui/transcript-viewport-pinning.test.d.ts.map +1 -0
  241. package/dist/ui/transcript-viewport-pinning.test.js +148 -0
  242. package/dist/ui/transcript-viewport-pinning.test.js.map +1 -0
  243. package/dist/ui/tui-history-parity.test.js +8 -0
  244. package/dist/ui/tui-history-parity.test.js.map +1 -1
  245. package/dist/ui/tui-simulation.test.d.ts +2 -0
  246. package/dist/ui/tui-simulation.test.d.ts.map +1 -0
  247. package/dist/ui/tui-simulation.test.js +138 -0
  248. package/dist/ui/tui-simulation.test.js.map +1 -0
  249. package/dist/ui/tui-terminal-recorder.test.d.ts +2 -0
  250. package/dist/ui/tui-terminal-recorder.test.d.ts.map +1 -0
  251. package/dist/ui/tui-terminal-recorder.test.js +134 -0
  252. package/dist/ui/tui-terminal-recorder.test.js.map +1 -0
  253. package/dist/utils/format.js +7 -0
  254. package/dist/utils/format.js.map +1 -1
  255. package/package.json +12 -6
@@ -1,15 +1,28 @@
1
- import { formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, } from "./goal-store.js";
1
+ import { formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, goalHasUnmetLocalPrerequisites, unmetLocalGoalPrerequisites, } from "./goal-store.js";
2
2
  import { formatGoalReferencesForPrompt, referencesRequiringAcknowledgement, } from "./goal-references.js";
3
3
  export const DEFAULT_GOAL_TASK_ATTEMPT_LIMIT = 5;
4
4
  export const DEFAULT_GOAL_VERIFIER_FIX_LIMIT = 5;
5
+ export const DEFAULT_GOAL_STRATEGY_LIMIT = 2;
6
+ /**
7
+ * Hard ceiling on total controller decisions per run. A safety net that
8
+ * guarantees termination even if some unforeseen state oscillates without
9
+ * tripping the per-dimension attempt/strategy/verifier/audit limits. Set high
10
+ * enough that legitimate large goals never hit it.
11
+ */
12
+ export const DEFAULT_GOAL_DECISION_LIMIT = 300;
5
13
  export const APPLY_INTEGRATION_TO_MAIN_TASK_TITLE = "Apply integrated worktree to main";
6
- export const COMMIT_INTEGRATED_GOAL_CHANGES_TASK_TITLE = "Commit integrated goal changes";
7
- const FINAL_COMPLETION_AUDIT_TASK_TITLE = "Audit Goal completion evidence";
14
+ export const RESOLVE_LOCAL_PREREQUISITES_TASK_TITLE = "Resolve local Goal prerequisites";
15
+ export const RE_STRATEGIZE_GOAL_TASK_TITLE = "Re-strategize Goal approach";
16
+ export const FINAL_COMPLETION_AUDIT_TASK_TITLE = "Audit Goal completion evidence";
8
17
  const BUILD_GOAL_EVIDENCE_PATH_TASK_TITLE = "Build Goal evidence path";
9
18
  const BUILD_GOAL_VERIFICATION_HARNESS_TASK_TITLE = "Build Goal verification harness";
10
19
  const DEFINE_GOAL_VERIFIER_TASK_TITLE = "Define Goal verifier";
11
20
  const FIX_VERIFIER_FAILURE_TASK_TITLE = "Fix verifier failure";
12
21
  const DEFAULT_GOAL_COMPLETION_AUDIT_LIMIT = 3;
22
+ /** Count of controller decisions already recorded as durable evidence. */
23
+ export function goalDecisionCount(run) {
24
+ return run.evidence.filter((item) => item.label.startsWith("Goal decision:")).length;
25
+ }
13
26
  function needsHarnessInstrumentation(run) {
14
27
  return run.harness.some((item) => !item.command && !item.path);
15
28
  }
@@ -168,85 +181,57 @@ function finalAuditTaskCount(run) {
168
181
  function hasApplyIntegrationTask(run) {
169
182
  return run.tasks.some((task) => task.title === APPLY_INTEGRATION_TO_MAIN_TASK_TITLE);
170
183
  }
171
- function hasCommitIntegratedChangesTask(run) {
172
- return run.tasks.some((task) => task.title === COMMIT_INTEGRATED_GOAL_CHANGES_TASK_TITLE);
173
- }
174
- function pendingAfterDependenciesImplementationTasks(run) {
175
- return run.tasks.filter((task) => task.status === "done" && task.mergeStrategy === "after_dependencies" && !!task.worktree);
176
- }
177
- function appliedIntegrationEvidence(run) {
178
- return run.evidence.some((item) => item.label === "Integrated worktree applied to main" ||
179
- item.label === "Goal decision: apply_integration_to_main");
180
- }
181
- function committedIntegrationEvidence(run) {
182
- return run.evidence.some((item) => item.label === "Integrated Goal changes committed");
184
+ /**
185
+ * Done worktree tasks whose committed candidate changes must reach the main
186
+ * checkout. Ordering is expressed by dependsOn; this gate only excludes tasks
187
+ * explicitly marked integration="manual". Read-only tasks (audit, etc.)
188
+ * produce no candidate changes and are excluded.
189
+ */
190
+ function pendingWorktreeIntegrationTasks(run) {
191
+ return run.tasks.filter((task) => task.status === "done" &&
192
+ !!task.worktree &&
193
+ task.integration !== "manual" &&
194
+ (task.candidate?.changedFiles?.length ?? 0) > 0);
195
+ }
196
+ /** Typed integration gate: candidates reached main (applied or committed). */
197
+ function integrationApplied(run) {
198
+ return run.integration?.status === "applied" || run.integration?.status === "committed";
199
+ }
200
+ /** Typed integration gate: integrated changes are committed in main. */
201
+ function integrationCommitted(run) {
202
+ return run.integration?.status === "committed";
183
203
  }
184
204
  function hasIntegratedWorktreeChanges(run) {
185
- return (pendingAfterDependenciesImplementationTasks(run).length > 0 || appliedIntegrationEvidence(run));
205
+ return (pendingWorktreeIntegrationTasks(run).length > 0 ||
206
+ (run.integration != null && run.integration.status !== "none"));
207
+ }
208
+ /**
209
+ * True when the latest verifier result predates the most recent substantive
210
+ * (non-audit, non-integration) worker completion, so the verifier evidence is
211
+ * stale and must be re-run before the audit/completion gates can trust it.
212
+ */
213
+ function verifierStaleAfterWorker(run) {
214
+ return (!!run.verifier?.lastResult?.checkedAt &&
215
+ !!run.lastSubstantiveWorkerAt &&
216
+ run.lastSubstantiveWorkerAt > run.verifier.lastResult.checkedAt);
186
217
  }
187
218
  function needsMainIntegrationApplyTask(run) {
188
- return (pendingAfterDependenciesImplementationTasks(run).length > 0 &&
219
+ return (pendingWorktreeIntegrationTasks(run).length > 0 &&
189
220
  !hasApplyIntegrationTask(run) &&
190
- !appliedIntegrationEvidence(run));
191
- }
192
- function needsIntegratedGoalChangesCommitTask(run) {
193
- return (hasIntegratedWorktreeChanges(run) &&
194
- appliedIntegrationEvidence(run) &&
195
- run.verifier?.lastResult?.status === "pass" &&
196
- !latestNonAuditWorkerEvidenceAfterVerifier(run) &&
197
- !hasCommitIntegratedChangesTask(run) &&
198
- !committedIntegrationEvidence(run));
221
+ !integrationApplied(run));
199
222
  }
200
223
  function shouldCreateFinalAuditTask(run, limit = DEFAULT_GOAL_COMPLETION_AUDIT_LIMIT) {
201
224
  return finalAuditTaskCount(run) < limit;
202
225
  }
203
- function isFinalAuditWorkerEvidence(run, label) {
204
- const match = /^Worker\s+(\S+)\s+/.exec(label);
205
- const workerId = match?.[1];
206
- if (!workerId)
207
- return false;
208
- return run.tasks.some((task) => task.title === FINAL_COMPLETION_AUDIT_TASK_TITLE && task.workerId === workerId);
209
- }
210
- function isCompletionAuditDecision(label) {
211
- return label === "Goal decision: completion_audit";
212
- }
213
- function latestMatchingEvidence(evidence, predicate) {
214
- return evidence.filter(predicate).sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0];
215
- }
216
- function latestNonAuditWorkerEvidenceAfterVerifier(run) {
217
- const verifierCheckedAt = run.verifier?.lastResult?.checkedAt;
218
- if (!verifierCheckedAt)
219
- return undefined;
220
- return latestMatchingEvidence(run.evidence, (item) => item.createdAt > verifierCheckedAt &&
221
- item.label.startsWith("Worker ") &&
222
- !isFinalAuditWorkerEvidence(run, item.label));
223
- }
224
- function latestCompletionRelevantEvidenceAfterVerifier(run) {
225
- const verifierCheckedAt = run.verifier?.lastResult?.checkedAt;
226
- if (!verifierCheckedAt)
227
- return undefined;
228
- return latestMatchingEvidence(run.evidence, (item) => {
229
- if (item.createdAt <= verifierCheckedAt)
230
- return false;
231
- if (isFinalAuditWorkerEvidence(run, item.label))
232
- return false;
233
- if (isCompletionAuditDecision(item.label))
234
- return false;
235
- if (item.label === "Verifier result" || item.label.startsWith("Verifier "))
236
- return false;
237
- return item.label.startsWith("Worker ") || item.label.startsWith("Goal decision:");
238
- });
239
- }
240
226
  export function hasFreshGoalCompletionAudit(run) {
241
227
  const verifierResult = run.verifier?.lastResult;
242
228
  if (!verifierResult || verifierResult.status !== "pass") {
243
229
  return { ok: false, reason: "Goal has no passing verifier result to audit." };
244
230
  }
245
- const postVerifierWorkerEvidence = latestNonAuditWorkerEvidenceAfterVerifier(run);
246
- if (postVerifierWorkerEvidence) {
231
+ if (verifierStaleAfterWorker(run)) {
247
232
  return {
248
233
  ok: false,
249
- reason: `Latest verifier result is stale after later Goal worker evidence: ${postVerifierWorkerEvidence.label}.`,
234
+ reason: "Latest verifier result is stale after a later substantive Goal worker completion.",
250
235
  };
251
236
  }
252
237
  const audit = run.completionAudit;
@@ -256,19 +241,7 @@ export function hasFreshGoalCompletionAudit(run) {
256
241
  if (audit.status !== "pass") {
257
242
  return { ok: false, reason: `Final completion audit status is ${audit.status}.` };
258
243
  }
259
- if (!audit.summary.startsWith("FINAL_AUDIT_PASS")) {
260
- return {
261
- ok: false,
262
- reason: "Final completion audit pass summary must start with FINAL_AUDIT_PASS.",
263
- };
264
- }
265
- if (!audit.summary.includes(`verifier_checked_at=${verifierResult.checkedAt}`)) {
266
- return {
267
- ok: false,
268
- reason: "Final completion audit pass summary must include latest verifier_checked_at.",
269
- };
270
- }
271
- if (!audit.outputPath && !audit.summary.match(/(?:output|artifact|log|path)=\S+/)) {
244
+ if (!audit.outputPath) {
272
245
  return {
273
246
  ok: false,
274
247
  reason: "Final completion audit pass must reference verifier output or artifacts.",
@@ -286,13 +259,6 @@ export function hasFreshGoalCompletionAudit(run) {
286
259
  reason: "Final completion audit is older than the latest verifier result.",
287
260
  };
288
261
  }
289
- const newerEvidence = latestCompletionRelevantEvidenceAfterVerifier(run);
290
- if (newerEvidence && newerEvidence.createdAt > audit.checkedAt) {
291
- return {
292
- ok: false,
293
- reason: `Final completion audit is stale after later Goal evidence: ${newerEvidence.label}.`,
294
- };
295
- }
296
262
  return { ok: true, reason: "Final completion audit passed after latest verifier evidence." };
297
263
  }
298
264
  function buildEvidencePlanTaskPrompt(run) {
@@ -311,21 +277,14 @@ function buildVerifierTaskPrompt(run) {
311
277
  `Define and build a real end-to-end verifier for this Goal. Begin from the intended experience and required senses/signals already implied by the success criteria and evidence plan, including mandatory Goal references. Choose a proportional local/free verifier that observes those signals and catches the important goal-specific failures; do not add generic simulations, screenshots, benchmarks, or scripts unless they directly support that proof. Update the Goal with a verifier_command, verifier_description, and verifier_cwd when the command must run from an isolated worker worktree. The verifier must be runnable locally/free and produce durable command or file evidence, not narrative or human visual inspection. If an external prerequisite is missing, mark it missing with exact user instructions.`);
312
278
  }
313
279
  function buildApplyIntegrationToMainTaskPrompt(run) {
314
- const integrationTasks = pendingAfterDependenciesImplementationTasks(run)
280
+ const integrationTasks = pendingWorktreeIntegrationTasks(run)
315
281
  .map((task) => `- ${task.id} / ${task.title}: worktree=${task.worktree?.path ?? "unknown"}; branch=${task.worktree?.branchName ?? "unknown"}; base=${task.worktree?.baseRef ?? "unknown"}; summary=${task.lastSummary?.slice(0, 600) ?? "none"}`)
316
282
  .join("\n");
317
283
  return (`Goal: ${run.goal}\n\n` +
318
284
  referencePromptSection(run.references) +
319
- `Apply accepted integration worktree changes into the user's main checkout before any release, verifier, final audit, commit, or completion. This task intentionally runs in the main checkout, not a new isolated worktree.\n\n` +
320
- `Integrated/after-dependencies worker outputs to apply:\n${integrationTasks || "- none recorded"}\n\n` +
321
- `For each integrated worktree, inspect its candidate packet, patch, diffstat, changed files, base SHA, verification logs, and risk notes. Apply or port only accepted changes to the main checkout; reject stale/risky/unrelated artifacts with durable evidence. Preserve user work. Run targeted checks in the main checkout after applying. Record durable evidence with label "Integrated worktree applied to main" containing the source worktree(s), accepted/rejected artifacts, changed files, diffstat, commands/results, and restart-needed note. Do not commit changes in this task; the controller will schedule a separate commit task after main-checkout verification evidence exists. Do not mark the whole Goal complete.`);
322
- }
323
- function buildCommitIntegratedGoalChangesTaskPrompt(run) {
324
- return (`Goal: ${run.goal}\n\n` +
325
- referencePromptSection(run.references) +
326
- `Commit verified integrated Goal changes in the user's main checkout before final audit or completion. This task intentionally runs in the main checkout, not a new isolated worktree.\n\n` +
327
- `Before committing, inspect git status and recent durable evidence to confirm accepted worktree changes were applied to main and main-checkout verification passed. Preserve user work: commit only files that belong to this Goal's accepted integrated changes, and do not stage unrelated user edits. If unrelated dirty files exist, block with exact paths and instructions instead of committing them.\n\n` +
328
- `Run a targeted pre-commit check appropriate to the changed files if no fresh main-checkout verification evidence exists. Create one git commit with a concise message describing the Goal changes. Record durable evidence with label "Integrated Goal changes committed" containing the commit hash, staged/committed files, verification command/result used for confidence, and any restart-needed note. Do not mark the whole Goal complete.`);
285
+ `Apply accepted integration worktree changes into the user's main checkout before any release, verifier, final audit, or completion. This task intentionally runs in the main checkout, not a new isolated worktree.\n\n` +
286
+ `Integrated candidate worker outputs to apply:\n${integrationTasks || "- none recorded"}\n\n` +
287
+ `For each integrated worktree, inspect its candidate packet, patch, diffstat, changed files, base SHA, verification logs, and risk notes. Apply or port only accepted changes to the main checkout; reject stale/risky/unrelated artifacts with durable evidence. Preserve user work. Run targeted checks in the main checkout after applying. Record durable evidence with label "Integrated worktree applied to main" containing the source worktree(s), accepted/rejected artifacts, changed files, diffstat, commands/results, and restart-needed note. The orchestrator will deterministically commit/confirm accepted changes after this worker exits. Do not mark the whole Goal complete.`);
329
288
  }
330
289
  function incompleteTasks(run) {
331
290
  return run.tasks.filter((task) => task.status !== "done");
@@ -425,7 +384,7 @@ export function canCompleteGoalRun(run) {
425
384
  const requiredEvidence = hasRequiredGoalEvidence(run);
426
385
  if (!requiredEvidence.ok)
427
386
  return requiredEvidence;
428
- if (hasIntegratedWorktreeChanges(run) && !committedIntegrationEvidence(run)) {
387
+ if (hasIntegratedWorktreeChanges(run) && !integrationCommitted(run)) {
429
388
  return {
430
389
  ok: false,
431
390
  reason: "Integrated Goal changes have not been committed in the main checkout.",
@@ -481,7 +440,7 @@ function buildFinalCompletionAuditTaskPrompt(run) {
481
440
  `Latest verifier: status=${verifier?.status ?? "unknown"}; checkedAt=${verifier?.checkedAt ?? "unknown"}; command=${verifier?.command ?? run.verifier?.command ?? "not recorded"}; output=${verifier?.outputPath ?? "not recorded"}; summary=${verifier?.summary ?? "not recorded"}\n\n` +
482
441
  `Evidence plan:\n${evidencePlanItems || "- none"}\n\n` +
483
442
  `Recent durable evidence:\n${recentEvidence || "- none"}\n\n` +
484
- `Read the referenced report/log/source artifacts and compare them with the latest verifier result. The coordinator schedules and records decisions/state; the verifier path/UI/controller executes the configured verifier command as the final pre-audit gate and records goals verify evidence; this final audit records goals audit only after comparing the latest verifier output and references, including [original-goal-prompt] and durable GOAL_PLAN evidence. If an evidence-plan item is still planned but already matched by durable verifier/source/file evidence, update that evidence_plan item to status=ready with a concise evidence summary before recording the audit; if proof is missing, create a new pending Goal task with exact fix instructions and do not pass the audit. If everything matches, record a passing completion audit with the goals tool by using action=audit, verification_status=pass, output_path matching the verifier output when available, and a summary that starts with "FINAL_AUDIT_PASS" and includes "verifier_checked_at=${verifier?.checkedAt ?? "unknown"}", "original-goal-prompt", and "GOAL_PLAN". If anything is missing, stale, contradictory, or unverified, create a new pending Goal task with exact instructions to fix it, record evidence describing the mismatch, and leave the audit failing or absent so the coordinator resumes a worker until fixed.`);
443
+ `Read the referenced report/log/source artifacts and compare them with the latest verifier result. The coordinator schedules and records decisions/state; the verifier path/UI/controller executes the configured verifier command as the final pre-audit gate and records goals verify evidence; this final audit records goals audit only after comparing the latest verifier output and references, including [original-goal-prompt] and durable GOAL_PLAN evidence. If an evidence-plan item is still planned but already matched by durable verifier/source/file evidence, update that evidence_plan item to status=ready with a concise evidence summary before recording the audit; if proof is missing, create a new pending Goal task with exact fix instructions and do not pass the audit. If everything matches, record a passing completion audit with the goals tool using action=audit and verification_status=pass: write a plain-prose summary of which durable artifacts you compared and reference any mandatory non-prompt Goal references (and, when this Goal uses GOAL_PLAN planning, mention original-goal-prompt and GOAL_PLAN). The system auto-stamps FINAL_AUDIT_PASS and verifier_checked_at and fills output_path from the recorded verifier run, so do not transcribe the timestamp; any remaining contract gaps are returned all at once. If anything is missing, stale, contradictory, or unverified, create a new pending Goal task with exact instructions to fix it, record evidence describing the mismatch, and leave the audit failing or absent so the coordinator resumes a worker until fixed.`);
485
444
  }
486
445
  function buildVerifierFailureTaskPrompt(run) {
487
446
  const result = run.verifier?.lastResult;
@@ -501,11 +460,137 @@ function buildVerifierFailureTaskPrompt(run) {
501
460
  `Prior verifier summaries:\n${priorSummaries}\n\n` +
502
461
  `Run targeted diagnostics, fix the root cause, update durable Goal evidence with the goals tool, and rerun the exact verifier command. Do not mark the Goal complete.`);
503
462
  }
463
+ function buildResolveLocalPrerequisitesTaskPrompt(run) {
464
+ const items = unmetLocalGoalPrerequisites(run)
465
+ .map((item) => `- ${item.label} (${item.status})${item.checkCommand ? `; check: ${item.checkCommand}` : ""}${item.instructions ? `; instructions: ${item.instructions}` : ""}`)
466
+ .join("\n");
467
+ return (`Goal: ${run.goal}\n\n` +
468
+ referencePromptSection(run.references) +
469
+ `Resolve the following local Goal prerequisites so the Goal can proceed unattended. These are not user-supplied external inputs; satisfy each one locally with free tools.\n\n` +
470
+ `${items || "- none recorded"}\n\n` +
471
+ `For each prerequisite, make its check pass locally (install/configure/build as needed using local/free tooling), then record durable evidence and update the prerequisite to status=met with the goals tool (action="prerequisite"). If a prerequisite turns out to genuinely require user-supplied external input, mark it kind=external with exact user instructions instead of guessing.`);
472
+ }
473
+ function isReStrategizeTask(task) {
474
+ return task.title.startsWith(RE_STRATEGIZE_GOAL_TASK_TITLE);
475
+ }
476
+ export function goalStrategyTaskCount(run) {
477
+ return run.tasks.filter(isReStrategizeTask).length;
478
+ }
479
+ function workerEvidenceContentsForTask(run, task) {
480
+ if (!task.workerId)
481
+ return [];
482
+ return run.evidence
483
+ .filter((item) => {
484
+ const match = /^Worker\s+(\S+)\s+/.exec(item.label);
485
+ return match?.[1] === task.workerId;
486
+ })
487
+ .map((item) => (item.content ?? "").trim())
488
+ .filter(Boolean);
489
+ }
490
+ /**
491
+ * No-progress signal for an implementation task: the latest two worker evidence
492
+ * contents for the task's worker are identical, so re-running the same approach
493
+ * is unlikely to make progress.
494
+ */
495
+ export function taskFailureRepeatedWithoutProgress(run, task) {
496
+ const contents = workerEvidenceContentsForTask(run, task);
497
+ if (contents.length < 2)
498
+ return false;
499
+ return contents[contents.length - 1] === contents[contents.length - 2];
500
+ }
501
+ function buildReStrategizeTaskPrompt(run, focus) {
502
+ const priorContents = focus
503
+ ? workerEvidenceContentsForTask(run, focus)
504
+ : run.evidence
505
+ .filter((item) => item.label.startsWith("Verifier"))
506
+ .map((item) => (item.content ?? "").trim())
507
+ .filter(Boolean);
508
+ const priorSummaries = priorContents
509
+ .slice(-3)
510
+ .map((content) => `- ${content.slice(0, 500)}`)
511
+ .join("\n");
512
+ const what = focus ? `task "${focus.title}" (${focus.id})` : "the configured verifier";
513
+ return (`Original objective: ${run.goal}\n\n` +
514
+ referencePromptSection(run.references) +
515
+ `Prior attempts at ${what} repeatedly failed without progress.\n\n` +
516
+ `Prior attempt summaries:\n${priorSummaries || "- none recorded"}\n\n` +
517
+ `Analyze why the prior attempts failed, then take a fundamentally different approach to accomplish the original objective. Do not repeat the same strategy. Use local/free tools, record durable evidence with the goals tool, and update task status.${focus ? ` When you accomplish the objective, also mark the original failing task ${focus.id} done with the goals tool so the Goal can proceed.` : ""} If the objective genuinely requires user-supplied external input, record it as an external prerequisite with exact instructions.`);
518
+ }
519
+ /**
520
+ * Bounded re-strategy escalation: run an existing recoverable re-strategy task,
521
+ * else create one (up to the strategy limit), else terminate the run in
522
+ * `failed` with a structured diagnosis brief. Replaces the former pause/block
523
+ * dead-ends so the loop always either changes state or resolves.
524
+ */
525
+ function reStrategizeOrFailDecision(run, options, context) {
526
+ const attemptLimit = options.taskAttemptLimit ?? DEFAULT_GOAL_TASK_ATTEMPT_LIMIT;
527
+ // Run an existing recoverable re-strategy task, but only while it still has
528
+ // attempt budget left, so a perpetually-failing strategy task cannot loop.
529
+ const recoverableStrategy = run.tasks.find((item) => isReStrategizeTask(item) && recoverableTask(item) && item.attempts < attemptLimit);
530
+ if (recoverableStrategy) {
531
+ return {
532
+ kind: "start_worker",
533
+ task: recoverableStrategy,
534
+ attempts: recoverableStrategy.attempts + 1,
535
+ reason: context.startReason,
536
+ };
537
+ }
538
+ const strategyLimit = options.strategyLimit ?? DEFAULT_GOAL_STRATEGY_LIMIT;
539
+ const strategyCount = goalStrategyTaskCount(run);
540
+ if (strategyCount < strategyLimit) {
541
+ // Distinct titles per attempt so the orchestrator creates a fresh task
542
+ // instead of reusing an exhausted same-title one.
543
+ const title = strategyCount === 0
544
+ ? RE_STRATEGIZE_GOAL_TASK_TITLE
545
+ : `${RE_STRATEGIZE_GOAL_TASK_TITLE} (${strategyCount + 1})`;
546
+ return {
547
+ kind: "create_task",
548
+ title,
549
+ prompt: buildReStrategizeTaskPrompt(run, context.focus),
550
+ reason: `${context.startReason} (${strategyCount + 1}/${strategyLimit})`,
551
+ };
552
+ }
553
+ return {
554
+ kind: "terminal",
555
+ status: "failed",
556
+ reason: buildGoalFailureDiagnosis(run, context.failReason),
557
+ };
558
+ }
559
+ /**
560
+ * Structured diagnosis brief recorded when a Goal terminates in `failed` so the
561
+ * run ends resolved (not spinning, not silently paused) with an honest verdict.
562
+ */
563
+ export function buildGoalFailureDiagnosis(run, reason) {
564
+ const attempts = run.tasks
565
+ .map((task) => `- ${task.title} (${task.id}): status=${task.status}; attempts=${task.attempts}${task.lastSummary ? `; last=${task.lastSummary.slice(0, 240)}` : ""}`)
566
+ .join("\n");
567
+ const failureSignatures = Array.from(new Set(run.evidence
568
+ .filter((item) => item.label === "Verifier fail" || item.label === "Verifier result")
569
+ .map((item) => (item.content ?? "").trim())
570
+ .filter(Boolean)))
571
+ .slice(-5)
572
+ .map((signature) => `- ${signature.slice(0, 240)}`)
573
+ .join("\n");
574
+ const verifier = run.verifier?.lastResult;
575
+ const externalNeeds = run.prerequisites
576
+ .filter((item) => (item.status !== "met" || !item.evidence?.trim()) && item.kind === "external")
577
+ .map((item) => `- ${item.label}: ${item.instructions ?? "user-supplied input required"}`)
578
+ .join("\n");
579
+ return [
580
+ "GOAL_FAILURE_DIAGNOSIS",
581
+ `objective=${run.goal}`,
582
+ `reason=${reason}`,
583
+ `tasks:\n${attempts || "- none"}`,
584
+ `distinct_failure_signatures:\n${failureSignatures || "- none"}`,
585
+ `latest_verifier=${verifier ? `${verifier.status} (exit ${verifier.exitCode ?? "unknown"}): ${verifier.summary?.slice(0, 240) ?? ""}` : "none"}`,
586
+ `human_decision_needed:\n${externalNeeds || "- none"}`,
587
+ ].join("\n");
588
+ }
504
589
  export function formatGoalControllerDecision(decision) {
505
590
  const parts = [`kind=${decision.kind}`];
506
591
  if ("reason" in decision)
507
592
  parts.push(`reason=${decision.reason}`);
508
- if (decision.kind === "start_worker" || decision.kind === "pause") {
593
+ if (decision.kind === "start_worker") {
509
594
  parts.push(`task=${decision.task.id}`, `title=${decision.task.title}`, `attempts=${decision.attempts}`);
510
595
  if (decision.task.workerId)
511
596
  parts.push(`worker=${decision.task.workerId}`);
@@ -516,8 +601,8 @@ export function formatGoalControllerDecision(decision) {
516
601
  if (decision.task.expectedChangedScope?.length) {
517
602
  parts.push(`expected_changed_scope=${decision.task.expectedChangedScope.join(",")}`);
518
603
  }
519
- if (decision.task.mergeStrategy)
520
- parts.push(`merge_strategy=${decision.task.mergeStrategy}`);
604
+ if (decision.task.integration)
605
+ parts.push(`integration=${decision.task.integration}`);
521
606
  }
522
607
  if (decision.kind === "wait" && decision.workerId)
523
608
  parts.push(`worker=${decision.workerId}`);
@@ -541,6 +626,17 @@ export function decideGoalNextAction(run, options = {}) {
541
626
  }
542
627
  return { kind: "complete", reason: completion.reason };
543
628
  }
629
+ // Hard termination guarantee: if a run somehow keeps making decisions without
630
+ // completing or hitting a narrower limit, fail with a diagnosis rather than
631
+ // loop forever.
632
+ const decisionLimit = options.decisionLimit ?? DEFAULT_GOAL_DECISION_LIMIT;
633
+ if (goalDecisionCount(run) > decisionLimit) {
634
+ return {
635
+ kind: "terminal",
636
+ status: "failed",
637
+ reason: buildGoalFailureDiagnosis(run, `Goal exceeded the maximum of ${decisionLimit} controller decisions without completing; stopping to avoid an unbounded loop.`),
638
+ };
639
+ }
544
640
  if (goalHasBlockingPrerequisites(run)) {
545
641
  return { kind: "blocked", reason: formatGoalBlockingPrerequisites(run) };
546
642
  }
@@ -565,17 +661,38 @@ export function decideGoalNextAction(run, options = {}) {
565
661
  ...(runningTask.workerId ? { workerId: runningTask.workerId } : {}),
566
662
  };
567
663
  }
664
+ if (goalHasUnmetLocalPrerequisites(run)) {
665
+ const duplicateDecision = duplicateAutoTaskDecision(run, RESOLVE_LOCAL_PREREQUISITES_TASK_TITLE, "Local Goal prerequisites must be satisfied locally before implementation.");
666
+ if (duplicateDecision)
667
+ return duplicateDecision;
668
+ return {
669
+ kind: "create_task",
670
+ title: RESOLVE_LOCAL_PREREQUISITES_TASK_TITLE,
671
+ prompt: buildResolveLocalPrerequisitesTaskPrompt(run),
672
+ reason: `Resolving ${unmetLocalGoalPrerequisites(run).length} local Goal prerequisite(s) locally instead of blocking.`,
673
+ };
674
+ }
568
675
  const task = nextRunnableTask(run);
569
676
  if (task) {
570
677
  const attempts = task.attempts + 1;
571
678
  const limit = options.taskAttemptLimit ?? DEFAULT_GOAL_TASK_ATTEMPT_LIMIT;
572
679
  if (attempts > limit) {
573
- return {
574
- kind: "pause",
575
- task,
576
- attempts,
577
- reason: `Attempt limit reached for task ${task.title}.`,
578
- };
680
+ // Past the soft attempt limit: keep retrying while the failure signature
681
+ // keeps changing (still making progress); on a no-progress repeat,
682
+ // re-strategize a bounded number of times, then fail with a diagnosis.
683
+ if (!isReStrategizeTask(task) && !taskFailureRepeatedWithoutProgress(run, task)) {
684
+ return {
685
+ kind: "start_worker",
686
+ task,
687
+ attempts,
688
+ reason: `Goal task "${task.title}" passed the soft attempt limit but is still making progress; continuing attempt ${attempts}.`,
689
+ };
690
+ }
691
+ return reStrategizeOrFailDecision(run, options, {
692
+ focus: task,
693
+ startReason: `Re-strategizing "${task.title}" with a fundamentally different approach.`,
694
+ failReason: `Task "${task.title}" could not be completed after ${task.attempts} attempt(s) and the bounded re-strategy limit.`,
695
+ });
579
696
  }
580
697
  return {
581
698
  kind: "start_worker",
@@ -589,8 +706,9 @@ export function decideGoalNextAction(run, options = {}) {
589
706
  const missingDependencies = dependencyBlockedTask.dependencies.filter((dependencyId) => !run.tasks.some((item) => taskMatchesDependency(item, dependencyId)));
590
707
  if (missingDependencies.length > 0) {
591
708
  return {
592
- kind: "blocked",
593
- reason: `Goal task "${dependencyBlockedTask.task.title}" depends on missing task(s): ${missingDependencies.join(", ")}.`,
709
+ kind: "terminal",
710
+ status: "failed",
711
+ reason: buildGoalFailureDiagnosis(run, `Goal task "${dependencyBlockedTask.task.title}" depends on missing task(s) that cannot be synthesized: ${missingDependencies.join(", ")}.`),
594
712
  };
595
713
  }
596
714
  return {
@@ -610,16 +728,8 @@ export function decideGoalNextAction(run, options = {}) {
610
728
  reason: "Accepted integration worktree changes must be applied to the user's main checkout before verifier, final audit, release, commit, or completion.",
611
729
  };
612
730
  }
613
- if (needsIntegratedGoalChangesCommitTask(run)) {
614
- return {
615
- kind: "create_task",
616
- title: COMMIT_INTEGRATED_GOAL_CHANGES_TASK_TITLE,
617
- prompt: buildCommitIntegratedGoalChangesTaskPrompt(run),
618
- reason: "Verified integrated Goal changes must be committed in the user's main checkout before final audit or completion.",
619
- };
620
- }
621
731
  if (run.verifier?.lastResult?.status === "pass" &&
622
- latestNonAuditWorkerEvidenceAfterVerifier(run) &&
732
+ verifierStaleAfterWorker(run) &&
623
733
  run.verifier?.command) {
624
734
  return {
625
735
  kind: "run_verifier",
@@ -638,8 +748,9 @@ export function decideGoalNextAction(run, options = {}) {
638
748
  };
639
749
  }
640
750
  return {
641
- kind: "blocked",
642
- reason: "Verifier passed, but final completion audit did not reconcile the Goal evidence plan after bounded attempts.",
751
+ kind: "terminal",
752
+ status: "failed",
753
+ reason: buildGoalFailureDiagnosis(run, "Verifier passed, but the final completion audit could not reconcile the Goal evidence plan after bounded attempts."),
643
754
  };
644
755
  }
645
756
  const duplicateDecision = duplicateAutoTaskDecision(run, BUILD_GOAL_EVIDENCE_PATH_TASK_TITLE, "Goal evidence plan still requires local instrumentation or exact prerequisite handling before verification.");
@@ -665,18 +776,18 @@ export function decideGoalNextAction(run, options = {}) {
665
776
  }
666
777
  if (run.verifier?.lastResult?.status === "fail") {
667
778
  if (hasRepeatedVerifierFailure(run)) {
668
- return {
669
- kind: "blocked",
670
- reason: "Verifier produced the same failure repeatedly; pause for diagnosis before creating more fix tasks.",
671
- };
779
+ return reStrategizeOrFailDecision(run, options, {
780
+ startReason: "Verifier produced the same failure repeatedly; re-strategizing with a fundamentally different approach.",
781
+ failReason: "Verifier produced the same failure repeatedly and bounded re-strategy attempts were exhausted.",
782
+ });
672
783
  }
673
784
  const limit = options.verifierFixLimit ?? DEFAULT_GOAL_VERIFIER_FIX_LIMIT;
674
785
  const blockedFixTask = existingBlockedTaskWithTitle(run, FIX_VERIFIER_FAILURE_TASK_TITLE);
675
786
  if (blockedFixTask) {
676
- return {
677
- kind: "blocked",
678
- reason: "A blocked verifier-fix task already exists; not creating another fix worker until the existing blocker is reconciled.",
679
- };
787
+ return reStrategizeOrFailDecision(run, options, {
788
+ startReason: "A blocked verifier-fix task exists; re-strategizing the verifier fix with a different approach.",
789
+ failReason: "A verifier-fix task was blocked and bounded re-strategy attempts were exhausted.",
790
+ });
680
791
  }
681
792
  if (shouldCreateVerifierFixTask(run, limit)) {
682
793
  return {
@@ -687,16 +798,9 @@ export function decideGoalNextAction(run, options = {}) {
687
798
  };
688
799
  }
689
800
  return {
690
- kind: "pause",
691
- task: {
692
- id: "verifier-fix-limit",
693
- title: FIX_VERIFIER_FAILURE_TASK_TITLE,
694
- prompt: "Verifier fix attempt limit reached.",
695
- status: "blocked",
696
- attempts: limit,
697
- },
698
- attempts: limit,
699
- reason: `Verifier fix task limit reached (${limit}).`,
801
+ kind: "terminal",
802
+ status: "failed",
803
+ reason: buildGoalFailureDiagnosis(run, `Verifier failed and the bounded verifier-fix limit (${limit}) was reached without a pass.`),
700
804
  };
701
805
  }
702
806
  if (run.verifier?.lastResult?.status === "pass") {
@@ -709,8 +813,9 @@ export function decideGoalNextAction(run, options = {}) {
709
813
  };
710
814
  }
711
815
  return {
712
- kind: "blocked",
713
- reason: "Verifier passed, but final completion audit did not pass after bounded attempts.",
816
+ kind: "terminal",
817
+ status: "failed",
818
+ reason: buildGoalFailureDiagnosis(run, "Verifier passed, but the final completion audit did not pass after bounded attempts."),
714
819
  };
715
820
  }
716
821
  if (run.verifier?.command) {