@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,128 @@
1
+ import { z } from 'zod'
2
+
3
+ import { ENTRY_FIELDS } from './entry-fields'
4
+ import { StuckReasonSchema } from './stuck-entries'
5
+
6
+ import { LensNameSchema } from '../agents/role-results'
7
+
8
+ /**
9
+ * The journal entries of the final review: its rounds, each lens's
10
+ * reviewer, fix rounds, and how it ends (stuck, retried, passed, shipped).
11
+ */
12
+
13
+ /**
14
+ * A final review round started: the run branch is reviewed from `from_sha`
15
+ * (round 1 and the fresh pass: where the run branch started; later rounds:
16
+ * the previous round's `head_sha`) to `head_sha`, its HEAD now. `lenses` are
17
+ * the lenses due this round (every lens in round 1 and the fresh pass, then
18
+ * only those that had blocking findings), `files` the files that diff changes, and `rules` the rule files
19
+ * of the engine config, read by the engine word for word (`text` is `null`
20
+ * for a file it could not read), for the rules lens. Written with
21
+ * `ticket: null`.
22
+ */
23
+ export const FinalReviewStartedEntrySchema = z.object({
24
+ ...ENTRY_FIELDS,
25
+ kind: z.literal('final_review_started'),
26
+ content: z.object({
27
+ round: z.number().int().min(1),
28
+ from_sha: z.string().min(1),
29
+ head_sha: z.string().min(1),
30
+ lenses: z.array(LensNameSchema),
31
+ files: z.array(z.string()).default([]),
32
+ rules: z.array(
33
+ z.object({ path: z.string(), text: z.string().nullable() })
34
+ ),
35
+ /**
36
+ * Round 1 with a slow test command: its slow tests are due once its
37
+ * lenses are in (#563). An older engine's record has none.
38
+ */
39
+ slow_due: z.boolean().optional(),
40
+ /**
41
+ * The fresh pass (#548): every lens reads the whole run branch again,
42
+ * without earlier findings, after the last fix round. `false` for
43
+ * any other round, and in older journals.
44
+ */
45
+ fresh: z.boolean().default(false),
46
+ }),
47
+ })
48
+
49
+ /** A lens's reviewer is about to start, right before its `agent_started`. */
50
+ export const LensStartedEntrySchema = z.object({
51
+ ...ENTRY_FIELDS,
52
+ kind: z.literal('lens_started'),
53
+ content: z.object({
54
+ lens: LensNameSchema,
55
+ round: z.number().int().min(1),
56
+ }),
57
+ })
58
+
59
+ /**
60
+ * A lens's reviewer finished, right after its `agent_finished`: its findings
61
+ * counted by severity. Not written for a failed turn.
62
+ */
63
+ export const LensFinishedEntrySchema = z.object({
64
+ ...ENTRY_FIELDS,
65
+ kind: z.literal('lens_finished'),
66
+ content: z.object({
67
+ lens: LensNameSchema,
68
+ round: z.number().int().min(1),
69
+ findings: z.object({
70
+ blocker: z.number().int().min(0),
71
+ should_fix: z.number().int().min(0),
72
+ nit: z.number().int().min(0),
73
+ }),
74
+ }),
75
+ })
76
+
77
+ /** A final review fix round starts, before its first fixer. */
78
+ export const FinalReviewFixingEntrySchema = z.object({
79
+ ...ENTRY_FIELDS,
80
+ kind: z.literal('final_review_fixing'),
81
+ content: z.object({ round: z.number().int().min(1) }),
82
+ })
83
+
84
+ /** The final review is stuck: the PR waits for a person's reply (#366). */
85
+ export const FinalReviewStuckEntrySchema = z.object({
86
+ ...ENTRY_FIELDS,
87
+ kind: z.literal('final_review_stuck'),
88
+ content: z.object({ reason: StuckReasonSchema, detail: z.string() }),
89
+ })
90
+
91
+ /**
92
+ * The owner replied `retry` to the stuck final review: its open fix round
93
+ * starts over as round 1 with fresh fixers and fresh counts, keeping any
94
+ * edits the owner made in the run branch's worktree.
95
+ */
96
+ export const FinalReviewRetriedEntrySchema = z.object({
97
+ ...ENTRY_FIELDS,
98
+ kind: z.literal('final_review_retried'),
99
+ content: z.object({}),
100
+ })
101
+
102
+ /** Every lens due in the latest round approved; the PR opens next. */
103
+ export const FinalReviewPassedEntrySchema = z.object({
104
+ ...ENTRY_FIELDS,
105
+ kind: z.literal('final_review_passed'),
106
+ content: z.object({}),
107
+ })
108
+
109
+ /**
110
+ * The person replied `ship` to the stuck final review: the PR opens with the
111
+ * findings still open listed at the top. Appended by the reply reader (#366)
112
+ * through `shipFinalReview`. `commit` is the commit of the edits left
113
+ * uncommitted in the run branch's worktree at ship time (#509), listed in
114
+ * the PR as not reviewed; with none, it is left out.
115
+ */
116
+ export const FinalReviewShippedEntrySchema = z.object({
117
+ ...ENTRY_FIELDS,
118
+ kind: z.literal('final_review_shipped'),
119
+ content: z.object({
120
+ commit: z
121
+ .object({
122
+ sha: z.string(),
123
+ message: z.string(),
124
+ files: z.array(z.string()),
125
+ })
126
+ .optional(),
127
+ }),
128
+ })
@@ -0,0 +1,324 @@
1
+ /**
2
+ * The final review's records in replay: which records are the final
3
+ * review's, its state after each, its stuck report and reply, and its
4
+ * `retry`.
5
+ */
6
+ import omit from 'lodash/omit'
7
+
8
+ import {
9
+ assumptionsOf,
10
+ isFixerRole,
11
+ outOfTurnsAfter,
12
+ sessionsAfter,
13
+ } from './agent-turn-replay'
14
+ import { finalResultChange, roundDone, settleRound } from './final-review-round'
15
+ import type { FinalReviewState } from './final-review-state'
16
+ import type { JournalRecord } from './journal-record'
17
+ import type { RunState } from './run-state'
18
+ import type { CrashCounts } from './step-records'
19
+
20
+ /** The final review's own kinds, and the kinds it shares with tickets. */
21
+ type FinalRecord = Extract<
22
+ JournalRecord,
23
+ {
24
+ kind:
25
+ | 'final_review_started'
26
+ | 'lens_started'
27
+ | 'lens_finished'
28
+ | 'final_review_fixing'
29
+ | 'final_review_stuck'
30
+ | 'final_review_passed'
31
+ | 'final_review_shipped'
32
+ | 'agent_started'
33
+ | 'agent_finished'
34
+ | 'agent_failed'
35
+ | 'agent_session'
36
+ | 'gates_run'
37
+ | 'leftover_scan'
38
+ | 'commit_made'
39
+ | 'run_branch_pushed'
40
+ }
41
+ >
42
+
43
+ const FINAL_KINDS = new Set<JournalRecord['kind']>([
44
+ 'final_review_started',
45
+ 'lens_started',
46
+ 'lens_finished',
47
+ 'final_review_fixing',
48
+ 'final_review_stuck',
49
+ 'final_review_passed',
50
+ 'final_review_shipped',
51
+ ])
52
+
53
+ /** Kinds tickets journal too; ticket-less ones are the final review's once it started. */
54
+ const SHARED_KINDS = new Set<JournalRecord['kind']>([
55
+ 'agent_started',
56
+ 'agent_finished',
57
+ 'agent_failed',
58
+ 'agent_session',
59
+ 'gates_run',
60
+ 'leftover_scan',
61
+ 'commit_made',
62
+ 'run_branch_pushed',
63
+ ])
64
+
65
+ /**
66
+ * The record if it is the final review's, else `null`: one of its own
67
+ * kinds, or a ticket-less agent, gate, scan, commit, or push record once
68
+ * the final review started. Before that, ticket-less records keep their old
69
+ * meaning (such as the run branch's gates after a join).
70
+ */
71
+ export const finalRecordOf = ({
72
+ state,
73
+ record,
74
+ }: {
75
+ state: RunState
76
+ record: JournalRecord
77
+ }): FinalRecord | null => {
78
+ const mine =
79
+ FINAL_KINDS.has(record.kind) ||
80
+ (record.ticket === null &&
81
+ state.final_review.round > 0 &&
82
+ SHARED_KINDS.has(record.kind))
83
+ return mine ? (record as FinalRecord) : null
84
+ }
85
+
86
+ /** The final review after one of its records. */
87
+ export const finalReviewAfter = ({
88
+ review,
89
+ record,
90
+ }: {
91
+ review: FinalReviewState
92
+ record: FinalRecord
93
+ }): FinalReviewState => {
94
+ switch (record.kind) {
95
+ case 'final_review_started': {
96
+ const { round, from_sha, head_sha, lenses, files, rules, fresh } =
97
+ record.content
98
+ return {
99
+ ...review,
100
+ round,
101
+ from_sha,
102
+ head_sha,
103
+ files,
104
+ lenses_due: lenses,
105
+ rules,
106
+ results: {},
107
+ slow_due: record.content.slow_due ?? false,
108
+ slow_tests: null,
109
+ fresh,
110
+ fresh_started: review.fresh_started || fresh,
111
+ // The fresh pass sees no earlier findings or answers (#548).
112
+ ...(fresh ? { fix: null } : {}),
113
+ }
114
+ }
115
+ case 'agent_started':
116
+ return isFixerRole(record.content.role)
117
+ ? { ...review, crashed_turn: null }
118
+ : review
119
+ case 'lens_started':
120
+ case 'lens_finished':
121
+ case 'agent_session':
122
+ return review
123
+ case 'final_review_fixing':
124
+ return review.fix !== null &&
125
+ record.content.round === review.fix.round
126
+ ? { ...review, fixing_started: true }
127
+ : review
128
+ case 'final_review_stuck':
129
+ return { ...review, stuck: record.content }
130
+ case 'final_review_passed':
131
+ return { ...review, passed: true }
132
+ case 'final_review_shipped':
133
+ return review.stuck === null
134
+ ? review
135
+ : {
136
+ ...review,
137
+ shipped: true,
138
+ shipped_edits: record.content.commit ?? null,
139
+ }
140
+ case 'agent_finished': {
141
+ const finished = record.content
142
+ const { role } = finished
143
+ return finalResultChange({
144
+ review: {
145
+ ...review,
146
+ agent_failures: omit(review.agent_failures, role),
147
+ engine_failures: omit(review.engine_failures, role),
148
+ sessions: sessionsAfter({
149
+ sessions: review.sessions,
150
+ role,
151
+ session_id: finished.session_id,
152
+ }),
153
+ assumptions: [
154
+ ...review.assumptions,
155
+ ...assumptionsOf(finished),
156
+ ],
157
+ // Only a new `environment` answer opens another rerun.
158
+ environment: null,
159
+ },
160
+ finished,
161
+ })
162
+ }
163
+ case 'agent_failed': {
164
+ const { role, error, failure, session_id } = record.content
165
+ const agent_failures = {
166
+ ...review.agent_failures,
167
+ [role]: { role, error, failure, session_id },
168
+ }
169
+ const out_of_turns = outOfTurnsAfter({
170
+ counts: review.out_of_turns,
171
+ failed: record.content,
172
+ })
173
+ if (failure === 'engine') {
174
+ return {
175
+ ...review,
176
+ agent_failures,
177
+ out_of_turns,
178
+ engine_failures: {
179
+ ...review.engine_failures,
180
+ [role]: (review.engine_failures[role] ?? 0) + 1,
181
+ },
182
+ }
183
+ }
184
+ return {
185
+ ...review,
186
+ agent_failures,
187
+ out_of_turns,
188
+ engine_failures: omit(review.engine_failures, role),
189
+ failed_tries: {
190
+ ...review.failed_tries,
191
+ [role]: (review.failed_tries[role] ?? 0) + 1,
192
+ },
193
+ }
194
+ }
195
+ case 'gates_run': {
196
+ const { ok, checks, target } = record.content
197
+ // Round 1's slow tests (#563): not the fix round's gates.
198
+ if (target === 'final_slow') {
199
+ const next = { ...review, slow_tests: { ok, checks } }
200
+ // They run once the lenses are in, so they settle the round;
201
+ // a run no round was waiting for settles none.
202
+ const awaited = review.slow_due && review.slow_tests === null
203
+ return awaited && roundDone({ review: next })
204
+ ? settleRound({ review: next })
205
+ : next
206
+ }
207
+ return { ...review, gates: { ok, checks } }
208
+ }
209
+ case 'leftover_scan':
210
+ return { ...review, leftovers: record.content.hits }
211
+ case 'commit_made':
212
+ return {
213
+ ...review,
214
+ commit: {
215
+ sha: record.content.sha,
216
+ files: record.content.files,
217
+ },
218
+ }
219
+ case 'run_branch_pushed':
220
+ return {
221
+ ...review,
222
+ pushed: record.content.sha,
223
+ fix_pushed: true,
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * The final review's report and reply after one of its records: a new
230
+ * stuck is told afresh, and a ship settles the reply.
231
+ */
232
+ export const finalRepliesAfter = ({
233
+ state,
234
+ record,
235
+ }: {
236
+ state: RunState
237
+ record: JournalRecord
238
+ }): Pick<RunState, 'final_stuck_report' | 'final_reply'> => {
239
+ switch (record.kind) {
240
+ case 'final_review_stuck':
241
+ return { final_stuck_report: null, final_reply: null }
242
+ case 'final_review_shipped':
243
+ return {
244
+ final_stuck_report: state.final_stuck_report,
245
+ final_reply: null,
246
+ }
247
+ default:
248
+ return {
249
+ final_stuck_report: state.final_stuck_report,
250
+ final_reply: state.final_reply,
251
+ }
252
+ }
253
+ }
254
+
255
+ /**
256
+ * The stuck final review resumed by `retry`, like a resumed ticket: fresh
257
+ * fixers (no session kept) and fresh counts, and the owner's edits in the
258
+ * run branch's worktree kept. A leftover scan, failed gates (or gates an
259
+ * `environment` answer blamed), or a step crashes cut off just run again; a failed lens gets a fresh one; otherwise the open fix round
260
+ * starts over as round 1, so the fixes (and the owner's edits) are gated,
261
+ * committed, pushed, and re-reviewed.
262
+ */
263
+ export const resumedFinalReview = ({
264
+ review,
265
+ }: {
266
+ review: FinalReviewState
267
+ }): FinalReviewState => {
268
+ const base: FinalReviewState = {
269
+ ...review,
270
+ stuck: null,
271
+ shipped: false,
272
+ sessions: {},
273
+ agent_failures: {},
274
+ failed_tries: {},
275
+ engine_failures: {},
276
+ gate_fix_rounds: 0,
277
+ setup_change: null,
278
+ environment_reruns: 0,
279
+ environment: null,
280
+ gates: review.commit === null ? null : review.gates,
281
+ leftovers: review.commit === null ? null : review.leftovers,
282
+ }
283
+ const { fix, stuck } = review
284
+ if (
285
+ fix === null ||
286
+ stuck?.reason === 'leftovers_found' ||
287
+ stuck?.reason === 'gates_failed' ||
288
+ stuck?.reason === 'environment_failed' ||
289
+ stuck?.reason === 'agent_failed' ||
290
+ stuck?.reason === 'crashed'
291
+ ) {
292
+ return base
293
+ }
294
+ return {
295
+ ...base,
296
+ round: 1,
297
+ fix: {
298
+ ...fix,
299
+ round: 1,
300
+ tests_answered: !fix.findings.some(({ kind }) => kind === 'test'),
301
+ code_answered: !fix.findings.some(({ kind }) => kind === 'code'),
302
+ responses: [],
303
+ handed_off: {},
304
+ bad_test: null,
305
+ },
306
+ fixing_started: false,
307
+ commit: null,
308
+ pushed: null,
309
+ }
310
+ }
311
+
312
+ /** The final review's scheduler keys: `final`, and `lens:<lens>`. */
313
+ export const isFinalKey = (key: string): boolean =>
314
+ key === 'final' || key.startsWith('lens:')
315
+
316
+ /** The crash counts without the final review's keys, for its `retry`. */
317
+ export const omitFinalKeys = ({
318
+ crashes,
319
+ }: {
320
+ crashes: CrashCounts
321
+ }): CrashCounts =>
322
+ Object.fromEntries(
323
+ Object.entries(crashes).filter(([key]) => !isFinalKey(key))
324
+ )