@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21

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 (321) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +7 -2
  3. package/board/client/board-look.ts +31 -1
  4. package/board/client/board-panel-parts.tsx +150 -0
  5. package/board/client/board-panel-stages.ts +99 -0
  6. package/board/client/board-panel-styles.ts +223 -0
  7. package/board/client/board-panel.tsx +77 -1275
  8. package/board/client/engine-life.ts +48 -0
  9. package/board/client/final-review-stack.tsx +144 -0
  10. package/board/client/heartbeat.tsx +61 -0
  11. package/board/client/helper-lines.tsx +69 -0
  12. package/board/client/live-feed.tsx +86 -0
  13. package/board/client/live-line.ts +73 -0
  14. package/board/client/model-line.tsx +178 -0
  15. package/board/client/needs-you-section.tsx +133 -0
  16. package/board/client/one-line.tsx +193 -0
  17. package/board/client/repo-wait-banner.tsx +69 -0
  18. package/board/client/run-footer.tsx +68 -0
  19. package/board/client/run-header.tsx +189 -0
  20. package/board/client/run-picker.tsx +98 -0
  21. package/board/client/status-bar.tsx +131 -0
  22. package/board/client/step-dots.tsx +127 -0
  23. package/board/client/stuck-actions.tsx +269 -0
  24. package/board/client/ticket-card-view.tsx +238 -0
  25. package/board/client/ticket-detail.tsx +87 -0
  26. package/board/client/ticket-stack.tsx +119 -0
  27. package/board/client/ticket-wait.ts +188 -0
  28. package/board/client/timeline-rows.tsx +41 -14
  29. package/board/client/usage-line.tsx +51 -0
  30. package/board/client/wait-line.tsx +39 -0
  31. package/board/index.client.tsx +1 -1
  32. package/board/index.server.ts +7 -1
  33. package/board/package.json +1 -1
  34. package/board/server/board-panel-state-helpers.ts +67 -0
  35. package/board/server/board-server-check.ts +294 -0
  36. package/board/server/board-server-engine.ts +129 -0
  37. package/board/server/board-server-events.ts +134 -0
  38. package/board/server/board-server-read.ts +190 -0
  39. package/board/server/board-server-resume.ts +164 -0
  40. package/board/server/board-server-start.ts +174 -0
  41. package/board/server/board-server-store.ts +281 -0
  42. package/board/server/board-server-types.ts +114 -0
  43. package/board/server/board-server-util.ts +4 -0
  44. package/board/server/board-server.ts +52 -943
  45. package/board/server/board-vocabulary-engine.ts +144 -0
  46. package/board/server/board-vocabulary-review.ts +115 -0
  47. package/board/server/board-vocabulary-run.ts +145 -0
  48. package/board/server/board-vocabulary-shapes.ts +153 -0
  49. package/board/server/board-vocabulary.ts +20 -375
  50. package/board/server/current-step-panel-helpers.ts +108 -0
  51. package/board/server/describe-record.ts +403 -0
  52. package/board/server/engine-launch.ts +20 -3
  53. package/board/server/engine-restart-helpers.ts +105 -0
  54. package/board/server/engine-watch.ts +45 -5
  55. package/board/server/journal-rebuild-helpers.ts +169 -0
  56. package/board/server/live-file.ts +130 -0
  57. package/board/server/luca-version.ts +1 -1
  58. package/board/server/make-rows.ts +14 -629
  59. package/board/server/record-texts.ts +350 -0
  60. package/board/server/reduce-board-agent-start.ts +224 -0
  61. package/board/server/reduce-board-agents.ts +160 -0
  62. package/board/server/reduce-board-counters.ts +138 -0
  63. package/board/server/reduce-board-create.ts +148 -0
  64. package/board/server/reduce-board-final-review.ts +174 -0
  65. package/board/server/reduce-board-needs-you.ts +283 -0
  66. package/board/server/reduce-board-reasons.ts +59 -0
  67. package/board/server/reduce-board-run.ts +255 -0
  68. package/board/server/reduce-board-settle.ts +88 -0
  69. package/board/server/reduce-board-steps.ts +226 -0
  70. package/board/server/reduce-board-text.ts +139 -0
  71. package/board/server/reduce-board-ticket-checks.ts +168 -0
  72. package/board/server/reduce-board-ticket-join.ts +144 -0
  73. package/board/server/reduce-board-ticket-start.ts +132 -0
  74. package/board/server/reduce-board-update.ts +117 -0
  75. package/board/server/reduce-board-usage.ts +247 -0
  76. package/board/server/reduce-board.ts +100 -1917
  77. package/board/server/reply-post.ts +407 -0
  78. package/board/server/run-registry.ts +8 -4
  79. package/board/server/ticket-stages-panel-helpers.ts +296 -0
  80. package/board/shared/board-rows.ts +9 -0
  81. package/board/shared/board-rpc.ts +42 -0
  82. package/board/shared/board-state-counts.ts +77 -0
  83. package/board/shared/board-state-live.ts +99 -0
  84. package/board/shared/board-state-review.ts +59 -0
  85. package/board/shared/board-state-run.ts +236 -0
  86. package/board/shared/board-state-ticket.ts +257 -0
  87. package/board/shared/board-state-usage.ts +107 -0
  88. package/board/shared/board-state.ts +97 -567
  89. package/board/shared/reply-actions.ts +207 -0
  90. package/engine/agents/agent-launcher.ts +27 -4
  91. package/engine/agents/claude-launcher-helpers.ts +233 -0
  92. package/engine/agents/claude-launcher.ts +28 -669
  93. package/engine/agents/claude-messages.ts +212 -0
  94. package/engine/agents/claude-options.ts +43 -8
  95. package/engine/agents/common-instructions.ts +138 -0
  96. package/engine/agents/input-channel.ts +36 -0
  97. package/engine/agents/launch-paths.ts +29 -0
  98. package/engine/agents/launch-session.ts +227 -0
  99. package/engine/agents/learner-instructions.ts +31 -0
  100. package/engine/agents/open-session.ts +210 -0
  101. package/engine/agents/prompt-sections.ts +95 -0
  102. package/engine/agents/rejoin-prompts.ts +135 -0
  103. package/engine/agents/reviewer-instructions.ts +99 -0
  104. package/engine/agents/role-instructions.ts +31 -244
  105. package/engine/agents/role-prompts.ts +39 -109
  106. package/engine/agents/role-results.ts +106 -7
  107. package/engine/agents/scripted-launcher.ts +21 -6
  108. package/engine/agents/session-pool.ts +124 -0
  109. package/engine/agents/turn-progress.ts +283 -0
  110. package/engine/agents/writer-instructions.ts +151 -0
  111. package/engine/cli/computer-adapters-real.ts +15 -3
  112. package/engine/cli/computer-adapters.ts +2 -0
  113. package/engine/cli/computer-checks.ts +87 -4
  114. package/engine/cli/config-reload-text.ts +39 -0
  115. package/engine/cli/decision-model-setup.ts +70 -0
  116. package/engine/cli/doctor-command.ts +1 -0
  117. package/engine/cli/doctor.ts +21 -3
  118. package/engine/cli/going-runs.ts +192 -36
  119. package/engine/cli/init-command.ts +1 -0
  120. package/engine/cli/init.ts +23 -1
  121. package/engine/cli/live-runs.ts +73 -0
  122. package/engine/cli/luca-run.ts +42 -7
  123. package/engine/cli/luca-skills.ts +183 -0
  124. package/engine/cli/luca.ts +8 -1
  125. package/engine/cli/models-command.ts +119 -0
  126. package/engine/cli/read-run.ts +81 -0
  127. package/engine/cli/run-args.ts +33 -6
  128. package/engine/cli/run-demo.ts +143 -0
  129. package/engine/cli/run-end.ts +184 -0
  130. package/engine/cli/run-modes-helpers.ts +81 -0
  131. package/engine/cli/run-modes.ts +62 -439
  132. package/engine/cli/setup.ts +5 -48
  133. package/engine/cli/unfinished-runs.ts +122 -0
  134. package/engine/cli/upgrade-command.ts +212 -30
  135. package/engine/cli/upgrade-finish.ts +149 -0
  136. package/engine/cli/upgrade.ts +112 -71
  137. package/engine/config/config-fields.ts +127 -0
  138. package/engine/config/config-reload.ts +250 -0
  139. package/engine/config/engine-config-schema.ts +235 -0
  140. package/engine/config/engine-config.ts +72 -142
  141. package/engine/config/test-commands.ts +102 -0
  142. package/engine/core/agent-guards-helpers.ts +84 -0
  143. package/engine/core/agent-turn.ts +395 -0
  144. package/engine/core/already-done.ts +44 -1
  145. package/engine/core/build-action.ts +214 -0
  146. package/engine/core/build-context.ts +135 -0
  147. package/engine/core/build-launch.ts +259 -0
  148. package/engine/core/commit-in.ts +282 -0
  149. package/engine/core/decide-build-early.ts +108 -0
  150. package/engine/core/decide-build-ending.ts +145 -0
  151. package/engine/core/decide-build-helpers.ts +87 -0
  152. package/engine/core/decide-build-tickets.ts +207 -0
  153. package/engine/core/decide-build-view.ts +103 -0
  154. package/engine/core/decide-build.ts +101 -1368
  155. package/engine/core/decide-final-fix.ts +203 -0
  156. package/engine/core/decide-final-review.ts +55 -310
  157. package/engine/core/decide-red-green.ts +398 -0
  158. package/engine/core/decide-rejoin.ts +259 -0
  159. package/engine/core/decide-repo-wait.ts +129 -0
  160. package/engine/core/decide-run-budget.ts +86 -12
  161. package/engine/core/decide-stuck-helpers.ts +127 -0
  162. package/engine/core/decide-stuck.ts +60 -8
  163. package/engine/core/decide-ticket-review.ts +95 -0
  164. package/engine/core/decide-ticket.ts +267 -0
  165. package/engine/core/decide-waits.ts +47 -0
  166. package/engine/core/decide.ts +74 -23
  167. package/engine/core/engine-chores.ts +163 -0
  168. package/engine/core/environment-rerun.ts +83 -0
  169. package/engine/core/evidence-text.ts +84 -0
  170. package/engine/core/execute-action.ts +303 -0
  171. package/engine/core/execute-already-done-check.ts +133 -0
  172. package/engine/core/execute-build.ts +68 -944
  173. package/engine/core/execute-changeset.ts +5 -7
  174. package/engine/core/execute-final-review.ts +125 -13
  175. package/engine/core/execute-gates.ts +182 -0
  176. package/engine/core/execute-intake.ts +130 -0
  177. package/engine/core/execute-join.ts +276 -0
  178. package/engine/core/execute-memory.ts +12 -0
  179. package/engine/core/execute-pull-request.ts +84 -0
  180. package/engine/core/execute-red-check.ts +219 -0
  181. package/engine/core/execute-repo-wait.ts +129 -0
  182. package/engine/core/execute-stuck.ts +25 -10
  183. package/engine/core/execute-waits.ts +158 -0
  184. package/engine/core/execute-with-jev.ts +118 -0
  185. package/engine/core/execute.ts +124 -831
  186. package/engine/core/final-failure-cap.ts +38 -0
  187. package/engine/core/final-review-action.ts +125 -0
  188. package/engine/core/final-review-text.ts +63 -17
  189. package/engine/core/final-stuck-text.ts +86 -0
  190. package/engine/core/fix-loop-text.ts +49 -2
  191. package/engine/core/left-out.ts +120 -0
  192. package/engine/core/live-file.ts +372 -0
  193. package/engine/core/loop-caps.ts +25 -2
  194. package/engine/core/not-in-run.ts +7 -3
  195. package/engine/core/pull-request-text.ts +255 -6
  196. package/engine/core/repo-wait-text.ts +81 -0
  197. package/engine/core/review-text.ts +55 -1
  198. package/engine/core/run-crash-recovery-helpers.ts +16 -0
  199. package/engine/core/run-session-close-helpers.ts +96 -0
  200. package/engine/core/run-start.ts +148 -0
  201. package/engine/core/run-stuck-text.ts +86 -0
  202. package/engine/core/schedule-rules.ts +193 -0
  203. package/engine/core/stuck-detail-text.ts +89 -0
  204. package/engine/core/stuck-format.ts +22 -0
  205. package/engine/core/stuck-text.ts +29 -332
  206. package/engine/core/ticket-stuck-text.ts +192 -0
  207. package/engine/core/worktree-changes.ts +106 -0
  208. package/engine/gates/already-done-check.ts +120 -0
  209. package/engine/gates/feature-map.ts +96 -0
  210. package/engine/gates/gate-runner.ts +108 -108
  211. package/engine/gates/gate-schemas.ts +61 -2
  212. package/engine/gates/leftover-scan.ts +154 -31
  213. package/engine/gates/new-tests.ts +45 -0
  214. package/engine/gates/own-files-check.ts +98 -0
  215. package/engine/gates/prepare-check.ts +60 -0
  216. package/engine/gates/prepare-slots.ts +99 -0
  217. package/engine/gates/red-check.ts +184 -16
  218. package/engine/gates/shell-check.ts +64 -0
  219. package/engine/gates/slot-waits.ts +55 -0
  220. package/engine/gates/slow-test-runner.ts +59 -0
  221. package/engine/gates/test-checks.ts +161 -0
  222. package/engine/gates/test-runner.ts +164 -5
  223. package/engine/gates/test-slots.ts +48 -0
  224. package/engine/git/git-adapter.ts +55 -396
  225. package/engine/git/git-commits.ts +132 -0
  226. package/engine/git/git-join.ts +139 -0
  227. package/engine/git/git-read.ts +123 -0
  228. package/engine/git/git-run.ts +32 -0
  229. package/engine/git/git-worktrees.ts +143 -0
  230. package/engine/git/outside-links.ts +55 -0
  231. package/engine/guards/after-turn-check.ts +31 -5
  232. package/engine/guards/guard-hook.ts +60 -12
  233. package/engine/guards/guard-roles.ts +68 -0
  234. package/engine/guards/helper-rules.ts +92 -0
  235. package/engine/guards/permission-rules.ts +161 -0
  236. package/engine/guards/role-rules-helpers.ts +48 -0
  237. package/engine/guards/role-rules.ts +34 -606
  238. package/engine/guards/shell-rules.ts +344 -0
  239. package/engine/guards/worktree-git.ts +208 -0
  240. package/engine/guards/worktree-snapshot.ts +412 -0
  241. package/engine/guards/worktree-state.ts +28 -825
  242. package/engine/guards/worktree-undo.ts +260 -0
  243. package/engine/guards/write-rules.ts +149 -0
  244. package/engine/index.ts +30 -9
  245. package/engine/intake/criteria-checks.ts +114 -0
  246. package/engine/intake/intake-checks.ts +164 -30
  247. package/engine/intake/intake-schemas.ts +37 -2
  248. package/engine/jev/clef-client.ts +259 -0
  249. package/engine/jev/decision-model-credentials.ts +212 -0
  250. package/engine/jev/jev-jobs.ts +144 -60
  251. package/engine/jev/jev-schemas.ts +86 -5
  252. package/engine/jev/jev-shadow-helpers.ts +218 -0
  253. package/engine/jev/jev-shadow.ts +109 -64
  254. package/engine/jev/model-pick.ts +275 -0
  255. package/engine/journal/agent-entries.ts +167 -0
  256. package/engine/journal/agent-message-entries.ts +72 -0
  257. package/engine/journal/agent-turn-replay.ts +203 -0
  258. package/engine/journal/already-done-entries.ts +55 -0
  259. package/engine/journal/commit-entries.ts +56 -0
  260. package/engine/journal/cross-ticket-replay.ts +222 -0
  261. package/engine/journal/decision-model-entries.ts +86 -0
  262. package/engine/journal/entry-fields.ts +10 -0
  263. package/engine/journal/final-review-entries.ts +128 -0
  264. package/engine/journal/final-review-replay.ts +324 -0
  265. package/engine/journal/final-review-round.ts +320 -0
  266. package/engine/journal/final-review-state.ts +180 -0
  267. package/engine/journal/gate-entries.ts +81 -0
  268. package/engine/journal/intake-entries.ts +87 -0
  269. package/engine/journal/join-entries.ts +126 -0
  270. package/engine/journal/journal-entry-schema.ts +218 -0
  271. package/engine/journal/journal-kinds.ts +97 -0
  272. package/engine/journal/journal-record-schema.ts +218 -0
  273. package/engine/journal/journal-record.ts +43 -1400
  274. package/engine/journal/journal.ts +6 -1
  275. package/engine/journal/limit-entries.ts +90 -0
  276. package/engine/journal/memory-entries.ts +124 -0
  277. package/engine/journal/memory-replay.ts +157 -0
  278. package/engine/journal/plan-replay.ts +139 -0
  279. package/engine/journal/pull-request-entries.ts +41 -0
  280. package/engine/journal/replay.ts +157 -2144
  281. package/engine/journal/reply-replay.ts +144 -0
  282. package/engine/journal/repo-wait-entries.ts +53 -0
  283. package/engine/journal/run-entries.ts +172 -0
  284. package/engine/journal/run-state.ts +264 -0
  285. package/engine/journal/shared-failure-replay.ts +133 -0
  286. package/engine/journal/stuck-entries.ts +193 -0
  287. package/engine/journal/ticket-progress.ts +392 -0
  288. package/engine/journal/ticket-replay.ts +411 -0
  289. package/engine/journal/ticket-result-replay.ts +364 -0
  290. package/engine/journal/ticket-retry-replay.ts +142 -0
  291. package/engine/journal/worktree-entries.ts +122 -0
  292. package/engine/models/model-score.ts +512 -0
  293. package/engine/shell/run-command.ts +187 -18
  294. package/engine/testing/agent-turn-fixtures.ts +287 -0
  295. package/engine/testing/already-done-fixtures.ts +97 -0
  296. package/engine/testing/build-fixtures.ts +68 -973
  297. package/engine/testing/checkouts-fixtures.ts +48 -0
  298. package/engine/testing/final-review-fixtures.ts +62 -1
  299. package/engine/testing/final-review-steps.ts +143 -0
  300. package/engine/testing/limit-fixtures.ts +85 -0
  301. package/engine/testing/live-file-fixtures.ts +57 -0
  302. package/engine/testing/practice-repo.ts +10 -4
  303. package/engine/testing/practice-run.ts +1 -1
  304. package/engine/testing/reply-fixtures.ts +88 -0
  305. package/engine/testing/run-budget-fixtures.ts +165 -0
  306. package/engine/testing/run-fixtures.ts +219 -0
  307. package/engine/testing/sdk-play.ts +177 -0
  308. package/engine/testing/setup-repo.ts +194 -0
  309. package/engine/testing/step-record-fixtures.ts +55 -0
  310. package/engine/testing/ticket-step-fixtures.ts +272 -0
  311. package/engine/testing/wait-scenarios.ts +186 -0
  312. package/engine/tracker/gh-retry.ts +217 -0
  313. package/engine/tracker/github-tracker.ts +310 -90
  314. package/engine/tracker/in-memory-tracker.ts +8 -4
  315. package/engine/tracker/tracker.ts +131 -6
  316. package/package.json +2 -1
  317. package/skills/luca-retro/SKILL.md +74 -0
  318. package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
  319. package/skills/luca-unstick/SKILL.md +153 -0
  320. package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
  321. package/engine/jev/jev-client.ts +0 -173
@@ -0,0 +1,411 @@
1
+ /**
2
+ * A ticket's own records in replay: its progress after each one, a rebase
3
+ * onto the run branch, and a baseline reused from another ticket.
4
+ */
5
+ import omit from 'lodash/omit'
6
+ import uniqBy from 'lodash/uniqBy'
7
+
8
+ import {
9
+ assumptionsOf,
10
+ outOfTurnsAfter,
11
+ sessionsAfter,
12
+ } from './agent-turn-replay'
13
+ import type { JournalRecord } from './journal-record'
14
+ import type { RunState } from './run-state'
15
+ import { EMPTY_TICKET_PROGRESS, type TicketProgress } from './ticket-progress'
16
+ import {
17
+ alreadyDoneCheckedChange,
18
+ bouncedBy,
19
+ failedTryChange,
20
+ resultChange,
21
+ setupChangeOf,
22
+ } from './ticket-result-replay'
23
+ import { retriedChange } from './ticket-retry-replay'
24
+
25
+ type TicketRecord = Exclude<
26
+ JournalRecord,
27
+ {
28
+ kind:
29
+ | 'run_started'
30
+ | 'intake_read'
31
+ | 'intake_refused'
32
+ | 'nothing_to_do'
33
+ | 'spec_snapshot'
34
+ | 'ticket_snapshot'
35
+ | 'run_branch_created'
36
+ | 'worktrees_removed'
37
+ | 'changeset_written'
38
+ | 'pull_request_opened'
39
+ | 'jev_asked'
40
+ | 'jev_answered'
41
+ | 'jev_failed'
42
+ | 'decision_model_off'
43
+ | 'agent_session'
44
+ | 'agent_session_closed'
45
+ | 'run_stopped'
46
+ | 'limit_wait_started'
47
+ | 'limit_wait_ended'
48
+ | 'usage_line_wait_started'
49
+ | 'usage_line_wait_ended'
50
+ | 'repo_wait_started'
51
+ | 'repo_wait_ended'
52
+ | 'repo_wait_skipped'
53
+ | 'repo_wait_ps_failed'
54
+ | 'usage_recorded'
55
+ | 'agent_message'
56
+ | 'agent_message_delivered'
57
+ | 'shared_git_changed'
58
+ | 'chores_done'
59
+ | 'changeset_dropped'
60
+ | 'final_review_started'
61
+ | 'lens_started'
62
+ | 'lens_finished'
63
+ | 'final_review_fixing'
64
+ | 'final_review_stuck'
65
+ | 'final_review_passed'
66
+ | 'final_review_shipped'
67
+ | 'comment_read'
68
+ | 'run_stuck'
69
+ | 'reply_ignored'
70
+ | 'final_review_retried'
71
+ | 'memory_recalled'
72
+ | 'memories_saved'
73
+ | 'learning_skipped'
74
+ | 'memories_reported'
75
+ | 'step_started'
76
+ | 'step_ended'
77
+ | 'ticket_waiting'
78
+ | 'run_resumed'
79
+ | 'engine_resumed'
80
+ | 'config_reloaded'
81
+ | 'config_reload_refused'
82
+ | 'join_started'
83
+ | 'memory_write_started'
84
+ | 'memory_write_done'
85
+ | 'baseline_reused'
86
+ | 'prepare_made'
87
+ }
88
+ >
89
+
90
+ const progressChange = ({
91
+ progress,
92
+ record,
93
+ }: {
94
+ progress: TicketProgress
95
+ record: TicketRecord
96
+ }): Partial<TicketProgress> => {
97
+ switch (record.kind) {
98
+ case 'ticket_worktree_created':
99
+ return { worktree: record.content }
100
+ case 'dependencies_installed':
101
+ return { install: { check: record.content.check } }
102
+ case 'baseline_prepared':
103
+ return { prepare: record.content.check }
104
+ case 'baseline_tests':
105
+ return {
106
+ baseline: record.content,
107
+ baseline_sha: progress.worktree?.base_sha ?? null,
108
+ }
109
+ case 'agent_started':
110
+ return {
111
+ retried: null,
112
+ crashed_turn: null,
113
+ launched_on: {
114
+ ...progress.launched_on,
115
+ [record.content.role]: record.content.model,
116
+ },
117
+ }
118
+ case 'agent_finished': {
119
+ const finished = record.content
120
+ return {
121
+ setup_change: setupChangeOf({ finished }),
122
+ agent_failure: null,
123
+ engine_failures: 0,
124
+ sessions: sessionsAfter({
125
+ sessions: progress.sessions,
126
+ role: finished.role,
127
+ session_id: finished.session_id,
128
+ }),
129
+ assumptions: [
130
+ ...progress.assumptions,
131
+ ...assumptionsOf(finished),
132
+ ],
133
+ // Only a new `environment` answer opens another rerun.
134
+ environment: null,
135
+ ...resultChange({ progress, finished, seq: record.seq }),
136
+ }
137
+ }
138
+ case 'agent_failed': {
139
+ const { role, error, failure, session_id } = record.content
140
+ const agent_failure = { role, error, failure, session_id }
141
+ const out_of_turns = outOfTurnsAfter({
142
+ counts: progress.out_of_turns,
143
+ failed: record.content,
144
+ })
145
+ if (failure === 'engine') {
146
+ return {
147
+ agent_failure,
148
+ out_of_turns,
149
+ engine_failures: progress.engine_failures + 1,
150
+ }
151
+ }
152
+ // The engine's side worked this time, so the run of engine
153
+ // failures is over.
154
+ return {
155
+ agent_failure,
156
+ out_of_turns,
157
+ engine_failures: 0,
158
+ ...failedTryChange({ progress, role }),
159
+ }
160
+ }
161
+ case 'red_check': {
162
+ const { ok, problems, notes, tests, exclusive } = record.content
163
+ const red_check = {
164
+ ok,
165
+ problems,
166
+ notes,
167
+ output: tests.output,
168
+ ...(exclusive === true ? { exclusive } : {}),
169
+ }
170
+ const lone =
171
+ exclusive === true
172
+ ? {
173
+ lone_reruns: [
174
+ ...progress.lone_reruns,
175
+ 'red_check' as const,
176
+ ],
177
+ }
178
+ : {}
179
+ if (!ok) return { red_check, ...lone }
180
+ // The tests it proved fail first, for the PR's Evidence (#513).
181
+ const red_tests = uniqBy(
182
+ (progress.test_writer?.criteria ?? []).flatMap(
183
+ (criterion) => criterion.tests
184
+ ),
185
+ ({ file, name }) => `${file}\u0000${name}`
186
+ )
187
+ return { red_check, red_tests, ...lone }
188
+ }
189
+ case 'already_done_checked':
190
+ return alreadyDoneCheckedChange({
191
+ progress,
192
+ checked: record.content,
193
+ })
194
+ case 'worktree_reset':
195
+ // A fresh test-writer and implementer start over on a clean
196
+ // worktree; the bad test and its count stay. A reset that carries
197
+ // a bad test is the engine's own finding (#581): it sets the bad
198
+ // test and counts as a bounce, with the engine as its source.
199
+ return {
200
+ ...(record.content.bad_test === undefined
201
+ ? {}
202
+ : {
203
+ bad_test: record.content.bad_test,
204
+ bad_test_source: 'engine',
205
+ bad_test_sources: bouncedBy({
206
+ progress,
207
+ by: 'engine',
208
+ }),
209
+ bad_test_bounces: progress.bad_test_bounces + 1,
210
+ }),
211
+ test_writer: null,
212
+ red_check: null,
213
+ red_tests: [],
214
+ red_fix_rounds: 0,
215
+ implementer: null,
216
+ gates: null,
217
+ gate_fix_rounds: 0,
218
+ environment: null,
219
+ leftovers: { red: null, green: null, fix: null },
220
+ commits: { ...progress.commits, red: null },
221
+ sessions: omit(progress.sessions, [
222
+ 'test-writer',
223
+ 'implementer',
224
+ ]),
225
+ }
226
+ case 'leftover_scan':
227
+ return {
228
+ leftovers: {
229
+ ...progress.leftovers,
230
+ [record.content.stage]: record.content.hits,
231
+ },
232
+ }
233
+ case 'commit_made':
234
+ return {
235
+ commits: {
236
+ ...progress.commits,
237
+ [record.content.stage]: record.content.sha,
238
+ },
239
+ commit_files: {
240
+ ...progress.commit_files,
241
+ [record.content.stage]: record.content.files,
242
+ },
243
+ }
244
+ case 'gates_run': {
245
+ const { ok, checks, cases, exclusive } = record.content
246
+ if (record.content.target !== 'ticket') {
247
+ return { join_gates: { ok, checks } }
248
+ }
249
+ // Only a ticket's own gates keep their cases (#581), and only
250
+ // they get a lone rerun (#544).
251
+ return {
252
+ gates: {
253
+ ok,
254
+ checks,
255
+ ...(cases === undefined ? {} : { cases }),
256
+ ...(exclusive === true ? { exclusive } : {}),
257
+ },
258
+ ...(exclusive === true
259
+ ? {
260
+ lone_reruns: [
261
+ ...progress.lone_reruns,
262
+ 'gates' as const,
263
+ ],
264
+ }
265
+ : {}),
266
+ }
267
+ }
268
+ case 'ticket_joined':
269
+ return { joined: record.content }
270
+ case 'ticket_rebased':
271
+ return rebasedChange({ progress, rebased: record.content })
272
+ case 'tests_sent_back':
273
+ return {
274
+ test_updates: progress.test_updates + 1,
275
+ test_update: { ...record.content, result: null },
276
+ }
277
+ case 'run_branch_pushed':
278
+ return { pushed: record.content.sha }
279
+ case 'ticket_stuck':
280
+ return { stuck: record.content, stuck_report: null, reply: null }
281
+ case 'stuck_reported':
282
+ return { stuck_report: { comment_id: record.content.comment_id } }
283
+ case 'reply_received': {
284
+ const { word, comment_id } = record.content
285
+ // `stop` is the run's, and `ship` the final review's.
286
+ return word === 'retry' || word === 'skip'
287
+ ? { reply: { word, comment_id } }
288
+ : {}
289
+ }
290
+ case 'ticket_retried':
291
+ return retriedChange({ progress, retried: record.content })
292
+ case 'ticket_skipped':
293
+ return {
294
+ skipped: { because: record.content.because },
295
+ reply: null,
296
+ }
297
+ case 'ticket_already_done':
298
+ return { already_done: { shas: record.content.shas } }
299
+ case 'ticket_closed':
300
+ return { closed: true }
301
+ case 'join_undone':
302
+ return { joined: null, join_gates: null }
303
+ }
304
+ }
305
+
306
+ type RebasedContent = Extract<
307
+ JournalRecord,
308
+ { kind: 'ticket_rebased' }
309
+ >['content']
310
+
311
+ /**
312
+ * A ticket sent back onto the run branch: its change now sits uncommitted on
313
+ * the run branch's tip. Its tests and code are fixed there, then the gates,
314
+ * one green commit, a fresh review of the new changes (with its own fix
315
+ * rounds), and a new join. The review loop starts over, but its nits and
316
+ * declined findings stay for the PR. The implementer keeps its session; the
317
+ * test-writer and reviewer start fresh. A worktree whose dependencies moved
318
+ * under it is installed again.
319
+ */
320
+ const rebasedChange = ({
321
+ progress,
322
+ rebased,
323
+ }: {
324
+ progress: TicketProgress
325
+ rebased: RebasedContent
326
+ }): Partial<TicketProgress> => {
327
+ const { cause, base_sha, tests, code, reinstall } = rebased
328
+ return {
329
+ worktree:
330
+ progress.worktree === null
331
+ ? null
332
+ : { ...progress.worktree, base_sha },
333
+ install: reinstall ? null : progress.install,
334
+ rejoins: progress.rejoins + 1,
335
+ rejoin: {
336
+ cause,
337
+ base_sha,
338
+ tests,
339
+ code,
340
+ tests_pending: tests.length > 0,
341
+ code_pending: code.length > 0,
342
+ from_sha: progress.worktree?.base_sha ?? base_sha,
343
+ earlier_findings: [
344
+ ...(progress.review?.findings ?? []),
345
+ ...progress.declined.map(({ finding }) => finding),
346
+ ],
347
+ },
348
+ gates: null,
349
+ gate_fix_rounds: 0,
350
+ leftovers: { ...progress.leftovers, green: null, fix: null },
351
+ commits: { ...progress.commits, green: null, fix: null },
352
+ commit_files: { ...progress.commit_files, green: [], fix: [] },
353
+ review: null,
354
+ review_rounds: 0,
355
+ reviewed_sha: null,
356
+ review_fix: null,
357
+ approved_seq: null,
358
+ joined: null,
359
+ join_gates: null,
360
+ pushed: null,
361
+ agent_failure: null,
362
+ engine_failures: 0,
363
+ sessions: omit(progress.sessions, ['test-writer', 'ticket-reviewer']),
364
+ }
365
+ }
366
+
367
+ export const applyTicketRecord = ({
368
+ state,
369
+ record,
370
+ }: {
371
+ state: RunState
372
+ record: TicketRecord
373
+ }): RunState => {
374
+ if (record.ticket === null) return state
375
+ const progress = state.tickets[record.ticket] ?? EMPTY_TICKET_PROGRESS
376
+ return {
377
+ ...state,
378
+ tickets: {
379
+ ...state.tickets,
380
+ [record.ticket]: {
381
+ ...progress,
382
+ ...progressChange({ progress, record }),
383
+ },
384
+ },
385
+ }
386
+ }
387
+
388
+ /**
389
+ * A ticket took another ticket's baseline, as it stood when the reuse was
390
+ * journaled, so a replay gives it the same one.
391
+ */
392
+ export const reusedBaselineAfter = ({
393
+ state,
394
+ record,
395
+ }: {
396
+ state: RunState
397
+ record: Extract<JournalRecord, { kind: 'baseline_reused' }>
398
+ }): RunState => {
399
+ if (record.ticket === null) return state
400
+ const { from_ticket, base_sha } = record.content
401
+ const baseline = state.tickets[from_ticket]?.baseline ?? null
402
+ if (baseline === null) return state
403
+ const progress = state.tickets[record.ticket] ?? EMPTY_TICKET_PROGRESS
404
+ return {
405
+ ...state,
406
+ tickets: {
407
+ ...state.tickets,
408
+ [record.ticket]: { ...progress, baseline, baseline_sha: base_sha },
409
+ },
410
+ }
411
+ }