@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,133 @@
1
+ /**
2
+ * Shared failures in replay (#544): the run-wide list of old tests that
3
+ * failed, and whether a ticket's failed red check or gates failed only on
4
+ * old tests another ticket failed shortly before, so a lone rerun is due.
5
+ */
6
+ import type { JournalRecord } from './journal-record'
7
+ import type { OldTestFailure, RunState } from './run-state'
8
+ import type { TicketProgress } from './ticket-progress'
9
+
10
+ import type { TestCaseRef } from '../gates/gate-schemas'
11
+
12
+ /** How close in time two tickets' failures of one old test must be. */
13
+ export const SHARED_FAILURE_WINDOW_MS = 10 * 60_000
14
+
15
+ type CheckRecord = Extract<JournalRecord, { kind: 'red_check' | 'gates_run' }>
16
+
17
+ /** The test cases a check's record says failed: none in old gates records. */
18
+ const failedCasesOf = (record: CheckRecord): TestCaseRef[] =>
19
+ record.kind === 'red_check'
20
+ ? record.content.tests.cases
21
+ .filter(({ status }) => status === 'failed')
22
+ .map(({ file, full_name }) => ({ file, full_name }))
23
+ : (record.content.failed_cases ?? [])
24
+
25
+ const sameTest = (a: TestCaseRef, b: TestCaseRef): boolean =>
26
+ a.file === b.file && a.full_name === b.full_name
27
+
28
+ /**
29
+ * Whether every one of `old` (non-empty) failed in another ticket, as
30
+ * journaled before `time`'s record, within `SHARED_FAILURE_WINDOW_MS` of it.
31
+ */
32
+ const sharedWithOthers = ({
33
+ old,
34
+ ticket,
35
+ time,
36
+ failures,
37
+ }: {
38
+ old: TestCaseRef[]
39
+ ticket: number
40
+ time: string
41
+ failures: OldTestFailure[]
42
+ }): boolean =>
43
+ old.length > 0 &&
44
+ old.every((test) =>
45
+ failures.some(
46
+ (failure) =>
47
+ failure.ticket !== ticket &&
48
+ sameTest(failure, test) &&
49
+ Math.abs(Date.parse(failure.time) - Date.parse(time)) <=
50
+ SHARED_FAILURE_WINDOW_MS
51
+ )
52
+ )
53
+
54
+ /**
55
+ * A ticket's progress with the check `record` journaled marked `shared`:
56
+ * its red check, or its own gates. A join's gates get no lone rerun.
57
+ */
58
+ const markedShared = ({
59
+ progress,
60
+ record,
61
+ }: {
62
+ progress: TicketProgress
63
+ record: CheckRecord
64
+ }): TicketProgress => {
65
+ if (record.kind === 'red_check' && progress.red_check !== null) {
66
+ return {
67
+ ...progress,
68
+ red_check: { ...progress.red_check, shared: true },
69
+ }
70
+ }
71
+ if (
72
+ record.kind === 'gates_run' &&
73
+ record.content.target === 'ticket' &&
74
+ progress.gates !== null
75
+ ) {
76
+ return { ...progress, gates: { ...progress.gates, shared: true } }
77
+ }
78
+ return progress
79
+ }
80
+
81
+ /**
82
+ * The state after a ticket's red check or gates record, once its own
83
+ * progress took it in: the old tests it failed (ones that passed at the
84
+ * ticket's baseline) join `old_test_failures`, and a failed red check or
85
+ * ticket gates is marked `shared` when every old test it failed also failed
86
+ * in another ticket within 10 minutes, by record time. Only failures
87
+ * journaled earlier count, so of two tickets failing at once the later one
88
+ * gets the lone rerun. A check that failed no old test (only new ones) is
89
+ * never shared.
90
+ */
91
+ export const sharedFailuresAfter = ({
92
+ state,
93
+ record,
94
+ }: {
95
+ state: RunState
96
+ record: CheckRecord
97
+ }): RunState => {
98
+ const { ticket } = record
99
+ if (ticket === null || record.content.ok) return state
100
+ const progress = state.tickets[ticket]
101
+ const baseline = progress?.baseline
102
+ if (progress === undefined || baseline === undefined || baseline === null) {
103
+ return state
104
+ }
105
+ const old = failedCasesOf(record).filter((test) =>
106
+ baseline.cases.some(
107
+ (base) => base.status === 'passed' && sameTest(base, test)
108
+ )
109
+ )
110
+ if (old.length === 0) return state
111
+ const shared = sharedWithOthers({
112
+ old,
113
+ ticket,
114
+ time: record.time,
115
+ failures: state.old_test_failures,
116
+ })
117
+ return {
118
+ ...state,
119
+ tickets: {
120
+ ...state.tickets,
121
+ [ticket]: shared ? markedShared({ progress, record }) : progress,
122
+ },
123
+ old_test_failures: [
124
+ ...state.old_test_failures,
125
+ ...old.map(({ file, full_name }) => ({
126
+ ticket,
127
+ file,
128
+ full_name,
129
+ time: record.time,
130
+ })),
131
+ ],
132
+ }
133
+ }
@@ -0,0 +1,193 @@
1
+ import { z } from 'zod'
2
+
3
+ import { ENTRY_FIELDS } from './entry-fields'
4
+
5
+ /**
6
+ * The journal entries of stuck work and the spec owner's replies: stuck
7
+ * tickets and runs, the report, comments read, replies taken or ignored,
8
+ * and tickets retried or skipped.
9
+ */
10
+
11
+ /**
12
+ * Why a ticket (or the final review) is stuck. A failed red check or gate is only stuck once its
13
+ * fix loop reaches its cap, and a bad test only on its second bounce.
14
+ * `undo_refused`: undoing its join would have rewound the run branch over
15
+ * other work, so the engine left the run branch alone (#519).
16
+ * `environment_failed`: a writer said a failure was not its ticket's fault,
17
+ * and the rerun failed the same way, or its reruns ran out (#613).
18
+ */
19
+ export const StuckReasonSchema = z.enum([
20
+ 'agent_failed',
21
+ 'red_check_failed',
22
+ 'nothing_new_to_test',
23
+ 'leftovers_found',
24
+ 'gates_failed',
25
+ 'bad_test',
26
+ 'changes_requested',
27
+ 'join_failed',
28
+ 'join_gates_failed',
29
+ 'install_failed',
30
+ 'prepare_failed',
31
+ 'setup_change_needed',
32
+ 'crashed',
33
+ 'undo_refused',
34
+ 'environment_failed',
35
+ ])
36
+
37
+ export type StuckReason = z.infer<typeof StuckReasonSchema>
38
+
39
+ export const TicketStuckEntrySchema = z.object({
40
+ ...ENTRY_FIELDS,
41
+ kind: z.literal('ticket_stuck'),
42
+ content: z.object({ reason: StuckReasonSchema, detail: z.string() }),
43
+ })
44
+
45
+ /**
46
+ * Why the whole run is stuck: it used up its run budget of tokens, its
47
+ * pull request didn't open (`gh pr create` failed, #508), or its repo wait
48
+ * lasted 24 hours (`repo_busy`, #615).
49
+ */
50
+ export const RunStuckReasonSchema = z.enum([
51
+ 'run_budget',
52
+ 'pull_request_failed',
53
+ 'repo_busy',
54
+ ])
55
+
56
+ export type RunStuckReason = z.infer<typeof RunStuckReasonSchema>
57
+
58
+ /**
59
+ * The whole run is stuck (`ticket: null`): nothing new starts until the
60
+ * spec owner replies `retry` (a top-up of the run budget, another try at
61
+ * the PR, or another repo wait) or `stop`.
62
+ */
63
+ export const RunStuckEntrySchema = z.object({
64
+ ...ENTRY_FIELDS,
65
+ kind: z.literal('run_stuck'),
66
+ content: z.object({ reason: RunStuckReasonSchema, detail: z.string() }),
67
+ })
68
+
69
+ /**
70
+ * The engine told the spec issue that a ticket (or, with `ticket: null`,
71
+ * the stuck run, else the final review) is stuck, in its comment
72
+ * `comment_id`, and now waits for the spec owner's reply.
73
+ */
74
+ export const StuckReportedEntrySchema = z.object({
75
+ ...ENTRY_FIELDS,
76
+ kind: z.literal('stuck_reported'),
77
+ content: z.object({
78
+ comment_id: z.number().int(),
79
+ body: z.string(),
80
+ }),
81
+ })
82
+
83
+ /**
84
+ * A comment the engine read on the spec issue while it waited for a reply,
85
+ * word for word, whoever wrote it. Only the spec owner's count as replies.
86
+ */
87
+ export const CommentReadEntrySchema = z.object({
88
+ ...ENTRY_FIELDS,
89
+ kind: z.literal('comment_read'),
90
+ content: z.object({
91
+ comment_id: z.number().int(),
92
+ author: z.string(),
93
+ body: z.string(),
94
+ }),
95
+ })
96
+
97
+ /**
98
+ * The one-word replies the spec owner can give to stuck work: `retry`,
99
+ * `skip`, and `stop` for a ticket; `retry`, `stop`, and `ship` for the
100
+ * final review.
101
+ */
102
+ export const ReplyWordSchema = z.enum(['retry', 'skip', 'stop', 'ship'])
103
+
104
+ export type ReplyWord = z.infer<typeof ReplyWordSchema>
105
+
106
+ /**
107
+ * The engine took the spec owner's reply: `retry` or `skip` for `ticket`,
108
+ * `stop` for the whole run, `retry` for the run stuck on its budget, or
109
+ * `retry` or `ship` for the stuck final review (`ticket` is `null` for
110
+ * these). A `retry` of the run stuck on its budget carries the tokens it
111
+ * added (`top_up`, #543); one journaled before top-ups has none, and counts
112
+ * as one whole budget.
113
+ */
114
+ export const ReplyReceivedEntrySchema = z.object({
115
+ ...ENTRY_FIELDS,
116
+ kind: z.literal('reply_received'),
117
+ content: z.object({
118
+ word: ReplyWordSchema,
119
+ ticket: z.number().int().positive().nullable(),
120
+ comment_id: z.number().int(),
121
+ author: z.string(),
122
+ top_up: z.number().int().positive().optional(),
123
+ }),
124
+ })
125
+
126
+ /**
127
+ * Why the spec owner's reply could not be used: it named no ticket while
128
+ * more than one is stuck, named a ticket that isn't stuck, or was `ship`
129
+ * (the final review's word) while no final review is stuck, or was `skip`
130
+ * for the final review, which can't be skipped. A `retry +<amount>` is only
131
+ * for the run stuck on its budget (`amount_only_for_run_budget`), and its
132
+ * amount must be a whole number above zero (`bad_amount`).
133
+ */
134
+ export const ReplyProblemSchema = z.enum([
135
+ 'no_ticket_named',
136
+ 'not_stuck',
137
+ 'nothing_stuck',
138
+ 'ship_needs_final_review',
139
+ 'skip_not_for_final_review',
140
+ 'amount_only_for_run_budget',
141
+ 'bad_amount',
142
+ ])
143
+
144
+ export type ReplyProblem = z.infer<typeof ReplyProblemSchema>
145
+
146
+ /**
147
+ * The spec owner's reply could not be used; the engine said why on the spec
148
+ * issue, in its comment `answer_id`.
149
+ */
150
+ export const ReplyIgnoredEntrySchema = z.object({
151
+ ...ENTRY_FIELDS,
152
+ kind: z.literal('reply_ignored'),
153
+ content: z.object({
154
+ comment_id: z.number().int(),
155
+ reason: ReplyProblemSchema,
156
+ answer_id: z.number().int().nullable(),
157
+ }),
158
+ })
159
+
160
+ /**
161
+ * How a `retry` went:
162
+ * - `resume`: the ticket's text and labels are unchanged, so it picks up
163
+ * where it stopped, with a fresh agent and fresh counts, keeping any
164
+ * code the user changed in its worktree.
165
+ * - `restart`: the ticket's text or labels changed, so its new copy (a
166
+ * `ticket_snapshot` just before) is built from scratch, in its worktree
167
+ * reset to the run branch's tip `base_sha`.
168
+ * - `refused`: the changed ticket is not ready to build (`problems`); the
169
+ * engine said so on the spec issue (`answer_id`), and it stays stuck.
170
+ */
171
+ export const TicketRetriedEntrySchema = z.object({
172
+ ...ENTRY_FIELDS,
173
+ kind: z.literal('ticket_retried'),
174
+ content: z.object({
175
+ mode: z.enum(['resume', 'restart', 'refused']),
176
+ base_sha: z.string().nullable(),
177
+ problems: z.array(z.string()),
178
+ answer_id: z.number().int().nullable(),
179
+ }),
180
+ })
181
+
182
+ /**
183
+ * A ticket left out of the run: the stuck ticket the owner skipped
184
+ * (`because` is `null`), or one that waits on a skipped ticket. It stays
185
+ * open, with a comment saying why.
186
+ */
187
+ export const TicketSkippedEntrySchema = z.object({
188
+ ...ENTRY_FIELDS,
189
+ kind: z.literal('ticket_skipped'),
190
+ content: z.object({
191
+ because: z.number().int().positive().nullable(),
192
+ }),
193
+ })