catherd-cli 1.5.0 → 2.0.0-beta.20261022113309

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 (275) hide show
  1. package/README.md +164 -208
  2. package/THIRD_PARTY_NOTICES.md +217 -35
  3. package/catalog/jev.json +27 -1
  4. package/catalog/models.json +56 -247
  5. package/package.json +6 -8
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/plugin/.codex-plugin/plugin.json +1 -1
  8. package/plugin/bin/catherd-mcp +1 -1
  9. package/plugin/commands/catherd.md +2 -2
  10. package/plugin/skills/catherd/SKILL.md +97 -421
  11. package/plugin/skills/catherd-setup/SKILL.md +20 -19
  12. package/src/adapters/all.ts +0 -4
  13. package/src/adapters/backend.ts +1 -2
  14. package/src/adapters/cursor/home.ts +1 -1
  15. package/src/adapters/cursor/index.ts +4 -4
  16. package/src/cli.ts +22 -27
  17. package/src/domain/bars.ts +33 -107
  18. package/src/domain/blocked.ts +72 -0
  19. package/src/domain/branch.ts +51 -0
  20. package/src/domain/brief.ts +21 -6
  21. package/src/domain/catalog.ts +69 -324
  22. package/src/domain/cost.ts +15 -30
  23. package/src/domain/errors.ts +2 -2
  24. package/src/domain/fail-lines.ts +26 -0
  25. package/src/domain/failover.ts +37 -87
  26. package/src/domain/findings.ts +47 -0
  27. package/src/domain/gate-brief.ts +1 -0
  28. package/src/domain/ids.ts +13 -2
  29. package/src/domain/jev.ts +47 -2
  30. package/src/domain/judge.ts +124 -0
  31. package/src/domain/lane.ts +22 -3
  32. package/src/domain/milestone-memory.ts +53 -0
  33. package/src/domain/notice.ts +25 -8
  34. package/src/domain/notify.ts +32 -0
  35. package/src/domain/plan-doc.ts +198 -0
  36. package/src/domain/plan-lint.ts +141 -0
  37. package/src/domain/profile-rules.ts +101 -160
  38. package/src/domain/profile.ts +116 -63
  39. package/src/domain/record.ts +53 -2
  40. package/src/domain/role-prompts.ts +131 -45
  41. package/src/domain/role-scope.ts +4 -34
  42. package/src/domain/role-tools.ts +2 -2
  43. package/src/domain/roles.ts +2 -0
  44. package/src/domain/route.ts +17 -0
  45. package/src/domain/rules.ts +33 -0
  46. package/src/domain/run-events.ts +298 -0
  47. package/src/domain/run-machine.ts +668 -0
  48. package/src/domain/run-state.ts +1310 -0
  49. package/src/domain/scorecard.ts +197 -0
  50. package/src/domain/select.ts +151 -146
  51. package/src/domain/slopalytics.ts +72 -0
  52. package/src/entry/answer-command.ts +39 -0
  53. package/src/entry/catalog-command.ts +25 -174
  54. package/src/entry/check-bin.ts +17 -0
  55. package/src/entry/cli-kit.ts +1 -1
  56. package/src/entry/daemon-bin.ts +22 -0
  57. package/src/entry/glyphs.ts +15 -2
  58. package/src/entry/init-command.ts +18 -68
  59. package/src/entry/knowledge-command.ts +45 -4
  60. package/src/entry/lock-command.ts +6 -7
  61. package/src/entry/mcp/role-server.ts +18 -0
  62. package/src/entry/mcp/server.ts +13 -40
  63. package/src/entry/mcp/session-tools.ts +149 -0
  64. package/src/entry/panel/openapi.ts +189 -0
  65. package/src/entry/panel/server.ts +410 -0
  66. package/src/entry/panel-command.ts +68 -0
  67. package/src/entry/profile-command.ts +10 -14
  68. package/src/entry/role-cli-command.ts +47 -3
  69. package/src/entry/runs-command.ts +3 -19
  70. package/src/entry/scorecard-command.ts +187 -0
  71. package/src/entry/supervise-bin.ts +1 -1
  72. package/src/infra/env.ts +0 -2
  73. package/src/infra/git.ts +64 -0
  74. package/src/infra/heavy-lock.ts +45 -7
  75. package/src/infra/launch.ts +63 -7
  76. package/src/infra/memory.ts +45 -0
  77. package/src/infra/ntfy.ts +40 -0
  78. package/src/infra/open-url.ts +42 -0
  79. package/src/infra/paths.ts +2 -2
  80. package/src/infra/slopalytics.ts +30 -0
  81. package/src/infra/store.ts +6 -1
  82. package/src/services/admission.ts +24 -9
  83. package/src/services/backends.ts +4 -6
  84. package/src/services/capture.ts +1 -22
  85. package/src/services/catalog-service.ts +155 -262
  86. package/src/services/credentials.ts +4 -20
  87. package/src/services/daemon.ts +181 -0
  88. package/src/services/dispatch-service.ts +5 -1
  89. package/src/services/doctor-backends.ts +1 -1
  90. package/src/services/doctor-checks.ts +47 -0
  91. package/src/services/doctor.ts +4 -9
  92. package/src/services/failover-rungs.ts +109 -0
  93. package/src/services/finalize.ts +12 -0
  94. package/src/services/gate-env.ts +1 -2
  95. package/src/services/gate-service.ts +21 -6
  96. package/src/services/jev-service.ts +2 -4
  97. package/src/services/lane-service.ts +34 -3
  98. package/src/services/milestone-check.ts +433 -0
  99. package/src/services/milestone-tree.ts +170 -0
  100. package/src/services/milestones.ts +35 -8
  101. package/src/services/notifier.ts +69 -2
  102. package/src/services/notify-service.ts +26 -0
  103. package/src/services/panel-setup.ts +116 -0
  104. package/src/services/plan-input.ts +151 -0
  105. package/src/services/ports.ts +30 -4
  106. package/src/services/preflight.ts +82 -8
  107. package/src/services/profile-service.ts +30 -3
  108. package/src/services/profile-store.ts +8 -3
  109. package/src/services/protocol.ts +4 -0
  110. package/src/services/provenance.ts +31 -90
  111. package/src/services/questions.ts +9 -0
  112. package/src/services/reconcile.ts +11 -0
  113. package/src/services/routing-service.ts +26 -15
  114. package/src/services/run-briefs.ts +477 -0
  115. package/src/services/run-client.ts +217 -0
  116. package/src/services/run-effects.ts +837 -0
  117. package/src/services/run-events-store.ts +174 -0
  118. package/src/services/run-evidence.ts +1 -1
  119. package/src/services/run-read.ts +221 -0
  120. package/src/services/run-report.ts +176 -0
  121. package/src/services/run-service.ts +55 -6
  122. package/src/services/run-store.ts +54 -7
  123. package/src/services/scorecard-metrics.ts +86 -0
  124. package/src/services/scorecard.ts +382 -0
  125. package/src/services/summary.ts +27 -3
  126. package/src/services/tree-path.ts +27 -0
  127. package/src/services/workspace-admission.ts +5 -13
  128. package/src/services/workspace-service.ts +5 -0
  129. package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
  130. package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
  131. package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
  132. package/web/dist/assets/Modal-Dxc5so53.js +1 -0
  133. package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
  134. package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
  135. package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
  136. package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
  137. package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
  138. package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
  139. package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
  140. package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
  141. package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
  142. package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
  143. package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
  144. package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
  145. package/web/dist/assets/drawer-Du3cdlok.js +1 -0
  146. package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
  147. package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
  148. package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  149. package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  150. package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  151. package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  152. package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  153. package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  154. package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  155. package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  156. package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  157. package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  158. package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  159. package/web/dist/assets/index-B6GiwDkh.js +11 -0
  160. package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
  161. package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
  162. package/web/dist/assets/index-C5IbylvM.css +2 -0
  163. package/web/dist/assets/index-C5IbylvM.css.br +0 -0
  164. package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
  165. package/web/dist/assets/label-DFSYBc62.js +1 -0
  166. package/web/dist/assets/label-DFSYBc62.js.br +0 -0
  167. package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
  168. package/web/dist/assets/link-D8EcKZUt.js +1 -0
  169. package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
  170. package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
  171. package/web/dist/assets/login-DaN5HPPW.js +1 -0
  172. package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
  173. package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
  174. package/web/dist/assets/query-Cu--ccT3.js +1 -0
  175. package/web/dist/assets/questions-shoO9OVj.js +1 -0
  176. package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
  177. package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
  178. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
  179. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
  180. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
  181. package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  182. package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
  183. package/web/dist/assets/runs-C3rg2zZc.js +1 -0
  184. package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
  185. package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
  186. package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
  187. package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
  188. package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
  189. package/web/dist/assets/table-BOxXhqS4.js +1 -0
  190. package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
  191. package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
  192. package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
  193. package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
  194. package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
  195. package/web/dist/assets/time--9kSQbbp.js +1 -0
  196. package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
  197. package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
  198. package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
  199. package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
  200. package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
  201. package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
  202. package/web/dist/index.html +15 -0
  203. package/catalog/ATTRIBUTION.md +0 -44
  204. package/catalog/scores.json +0 -3202
  205. package/catalog/sources.json +0 -97
  206. package/src/adapters/antigravity/events.ts +0 -131
  207. package/src/adapters/antigravity/home.ts +0 -46
  208. package/src/adapters/antigravity/index.ts +0 -335
  209. package/src/adapters/antigravity/models.ts +0 -37
  210. package/src/adapters/grok/events.ts +0 -112
  211. package/src/adapters/grok/home.ts +0 -187
  212. package/src/adapters/grok/index.ts +0 -358
  213. package/src/adapters/grok/models.ts +0 -48
  214. package/src/domain/calibration.ts +0 -132
  215. package/src/domain/sources.ts +0 -144
  216. package/src/entry/mcp/dispatch-tools.ts +0 -52
  217. package/src/entry/mcp/lane-tools.ts +0 -131
  218. package/src/entry/mcp/protocol-tools.ts +0 -63
  219. package/src/entry/mcp/push-tools.ts +0 -16
  220. package/src/entry/mcp/run-tools.ts +0 -164
  221. package/src/entry/mcp/setup-tools.ts +0 -109
  222. package/src/entry/mcp/workspace-tools.ts +0 -142
  223. package/src/entry/tui/commands.ts +0 -563
  224. package/src/entry/tui/effects.ts +0 -385
  225. package/src/entry/tui/fixtures.ts +0 -637
  226. package/src/entry/tui/profile-edits.ts +0 -146
  227. package/src/entry/tui/profile-tree.ts +0 -429
  228. package/src/entry/tui/providers/app.tsx +0 -162
  229. package/src/entry/tui/providers/boundary.tsx +0 -57
  230. package/src/entry/tui/providers/data.tsx +0 -160
  231. package/src/entry/tui/providers/keymap.tsx +0 -166
  232. package/src/entry/tui/providers/root.tsx +0 -45
  233. package/src/entry/tui/providers/theme.tsx +0 -17
  234. package/src/entry/tui/providers/toast.tsx +0 -77
  235. package/src/entry/tui/run.tsx +0 -114
  236. package/src/entry/tui/state.ts +0 -400
  237. package/src/entry/tui/stories.ts +0 -114
  238. package/src/entry/tui/text.ts +0 -130
  239. package/src/entry/tui/theme.ts +0 -140
  240. package/src/entry/tui/views/app.tsx +0 -208
  241. package/src/entry/tui/views/dialogs.tsx +0 -41
  242. package/src/entry/tui/views/profile-actions.ts +0 -331
  243. package/src/entry/tui/views/profiles.tsx +0 -520
  244. package/src/entry/tui/views/runs.tsx +0 -619
  245. package/src/entry/tui/views/save-dialog.tsx +0 -344
  246. package/src/entry/tui/views/status.tsx +0 -213
  247. package/src/entry/tui/widgets/chrome.tsx +0 -114
  248. package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
  249. package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
  250. package/src/entry/tui/widgets/dialog-select.tsx +0 -212
  251. package/src/entry/tui/widgets/dialog.tsx +0 -68
  252. package/src/entry/tui/widgets/line.tsx +0 -92
  253. package/src/entry/tui/widgets/list.tsx +0 -197
  254. package/src/entry/tui/widgets/spinner.tsx +0 -49
  255. package/src/entry/tui/widgets/toast.tsx +0 -62
  256. package/src/infra/sources/arena.ts +0 -95
  257. package/src/infra/sources/artificial-analysis.ts +0 -119
  258. package/src/infra/sources/cache.ts +0 -126
  259. package/src/infra/sources/csv.ts +0 -38
  260. package/src/infra/sources/epoch.ts +0 -82
  261. package/src/infra/sources/http.ts +0 -75
  262. package/src/infra/sources/litellm.ts +0 -58
  263. package/src/infra/sources/models-dev.ts +0 -59
  264. package/src/infra/sources/openrouter-endpoints.ts +0 -52
  265. package/src/infra/sources/openrouter-models.ts +0 -60
  266. package/src/infra/sources/rows.ts +0 -38
  267. package/src/infra/sources/vectara.ts +0 -69
  268. package/src/infra/sources/zip.ts +0 -48
  269. package/src/services/catalog-refresh.ts +0 -201
  270. package/src/services/doctor-sources.ts +0 -76
  271. package/src/services/runs-page.ts +0 -290
  272. package/src/services/source-derive.ts +0 -397
  273. package/src/services/source-sync.ts +0 -312
  274. package/src/services/standins.ts +0 -238
  275. package/src/services/treat-likes.ts +0 -113
@@ -0,0 +1,298 @@
1
+ import { z } from "zod";
2
+ import { BLOCKED_REASONS } from "./blocked.ts";
3
+ import { JUDGE_KINDS, JudgeDecisionSchema } from "./judge.ts";
4
+ import { REPLY_STATUSES, RUN_STATUSES } from "./record.ts";
5
+ import { ROLES } from "./roles.ts";
6
+
7
+ /** Every line of a run's events.jsonl carries `v`; a newer catherd's events are refused (E_RUNTIME_TOO_OLD). */
8
+ export const EVENTS_SCHEMA = 1;
9
+
10
+ /** The unit a run-level question or failure belongs to; a milestone is `M1`, a lane `M1.L2`. */
11
+ export const RUN_UNIT = "run";
12
+
13
+ export const PARK_REASONS = [
14
+ ...BLOCKED_REASONS,
15
+ "environment",
16
+ "preflight",
17
+ "confirm",
18
+ "approval",
19
+ "blocker",
20
+ "verifier",
21
+ "commit",
22
+ "land",
23
+ "budget",
24
+ "judge",
25
+ "effect",
26
+ "push",
27
+ ] as const;
28
+ export type ParkReason = (typeof PARK_REASONS)[number];
29
+
30
+ export const REPORT_KINDS = ["started", "parked", "landed", "finished", "cancelled", "failed"] as const;
31
+ export type ReportKind = (typeof REPORT_KINDS)[number];
32
+
33
+ /** The machine's action types, so `effect.failed` can name one without the events importing the machine. */
34
+ export const ACTION_TYPES = [
35
+ "dispatch",
36
+ "watch",
37
+ "readPlan",
38
+ "route",
39
+ "fastCheck",
40
+ "commit",
41
+ "climb",
42
+ "judge",
43
+ "classify",
44
+ "park",
45
+ "land",
46
+ "checkMerged",
47
+ "integrate",
48
+ "claimTree",
49
+ "pushBranch",
50
+ "cancel",
51
+ "report",
52
+ "notify",
53
+ "finish",
54
+ ] as const;
55
+ export type ActionType = (typeof ACTION_TYPES)[number];
56
+
57
+ export const FindingSchema = z.object({
58
+ label: z.enum(["BLOCKER", "BUG", "NIT"]),
59
+ text: z.string(),
60
+ file: z.string().nullable(),
61
+ });
62
+ export type Finding = z.infer<typeof FindingSchema>;
63
+
64
+ /** A finding as it is quoted in the known issues: its text, with the label put back when the text lost it. */
65
+ export function issueLine(f: Finding): string {
66
+ return new RegExp(`^[\\s\\-*_\\d.)]*${f.label}\\b`, "i").test(f.text) ? f.text : `${f.label} ${f.text}`;
67
+ }
68
+
69
+ export const MilestonePlanSchema = z.object({
70
+ id: z.string(),
71
+ title: z.string(),
72
+ after: z.array(z.string()),
73
+ lanes: z.array(
74
+ z.object({
75
+ id: z.string(),
76
+ role: z.enum(ROLES),
77
+ kind: z.string().nullable(),
78
+ after: z.array(z.string()),
79
+ owns: z.array(z.string()),
80
+ }),
81
+ ),
82
+ });
83
+ export type MilestonePlan = z.infer<typeof MilestonePlanSchema>;
84
+
85
+ const base = {
86
+ v: z.literal(EVENTS_SCHEMA),
87
+ seq: z.number().int().min(1),
88
+ at: z.string(),
89
+ };
90
+ const ev = <T extends string, S extends z.ZodRawShape>(type: T, shape: S) =>
91
+ z.object({ ...base, type: z.literal(type), ...shape });
92
+
93
+ const strings = z.array(z.string());
94
+ const role = z.enum(ROLES);
95
+ const reason = z.enum(PARK_REASONS);
96
+ const outcome = z.enum(["done", "cancelled", "failed"]);
97
+
98
+ /** One fact about a run. Only effects and clients append events; the machine never does. */
99
+ export const RunEventSchema = z.discriminatedUnion("type", [
100
+ ev("run.started", {
101
+ repo: z.string(),
102
+ title: z.string(),
103
+ aLines: strings,
104
+ plan: strings,
105
+ base: z.string().nullable(),
106
+ engine: z.literal("2.0"),
107
+ /** the roles the run's profile turns off, so the machine never waits on one (absent before 2.0.x) */
108
+ rolesOff: z.array(role).optional(),
109
+ }),
110
+ ev("daemon.started", { pid: z.number().int(), version: z.string() }),
111
+ ev("plan.ready", { milestones: z.array(MilestonePlanSchema) }),
112
+ ev("plan.approved", { by: z.enum(["owner", "input"]) }),
113
+ ev("lanes.routed", {
114
+ milestone: z.string(),
115
+ lanes: z.array(z.object({ lane: z.string(), rung: z.string(), ladder: strings })),
116
+ }),
117
+ ev("preflight.done", {
118
+ milestone: z.string(),
119
+ blocking: z.array(z.object({ lane: z.string(), note: z.string() })),
120
+ confirm: strings.nullable(),
121
+ /** plan 34: the plan lint's warnings; older lines have none */
122
+ lint: strings.default([]),
123
+ }),
124
+ ev("dispatch.started", {
125
+ dispatchId: z.string(),
126
+ name: z.string(),
127
+ role,
128
+ unit: z.string(),
129
+ lane: z.string().nullable(),
130
+ rung: z.string(),
131
+ thread: z.string().nullable(),
132
+ /** a judge call's kind: the machine asks the same question again from it when the reply is unreadable */
133
+ judgeKind: z.enum(JUDGE_KINDS).nullable().optional(),
134
+ }),
135
+ ev("dispatch.refused", { name: z.string(), unit: z.string(), code: z.string(), message: z.string() }),
136
+ ev("dispatch.ended", {
137
+ dispatchId: z.string(),
138
+ name: z.string(),
139
+ unit: z.string(),
140
+ status: z.enum(RUN_STATUSES),
141
+ replyStatus: z.enum(REPLY_STATUSES).nullable(),
142
+ reason: z.enum(BLOCKED_REASONS).nullable(),
143
+ why: z.string(),
144
+ thread: z.string().nullable(),
145
+ head: z.string().nullable(),
146
+ environment: z.string().nullable(),
147
+ verdict: z.enum(["pass", "fail", "blocked"]).nullable(),
148
+ findings: z.array(FindingSchema),
149
+ foreign: strings,
150
+ replyPath: z.string(),
151
+ }),
152
+ ev("check.ran", {
153
+ lane: z.string(),
154
+ exit: z.number().int().nullable(),
155
+ tail: z.string(),
156
+ foreign: strings,
157
+ }),
158
+ ev("commit.made", { milestone: z.string(), round: z.number().int().min(0), commit: z.string() }),
159
+ ev("commit.failed", { milestone: z.string(), round: z.number().int().min(0), tail: z.string() }),
160
+ ev("lane.climbed", { lane: z.string(), rung: z.string(), top: z.boolean() }),
161
+ ev("stop.classified", { dispatchId: z.string(), reason: z.enum(BLOCKED_REASONS).nullable() }),
162
+ ev("judge.decided", {
163
+ id: z.string(),
164
+ unit: z.string(),
165
+ kind: z.enum(JUDGE_KINDS),
166
+ decision: JudgeDecisionSchema,
167
+ dispatchId: z.string(),
168
+ }),
169
+ ev("judge.failed", { id: z.string(), unit: z.string(), why: z.string() }),
170
+ ev("question.parked", { unit: z.string(), question: z.string(), reason }),
171
+ ev("question.answered", { unit: z.string(), answer: z.string(), by: z.enum(["owner", "judge"]) }),
172
+ ev("milestone.landed", {
173
+ milestone: z.string(),
174
+ commit: z.string(),
175
+ knownIssues: strings,
176
+ digestPath: z.string(),
177
+ /** the branch the run works on, named in the owner's push question; absent in a run without a base */
178
+ branch: z.string().nullable().optional(),
179
+ }),
180
+ ev("land.refused", { milestone: z.string(), code: z.string(), message: z.string(), fix: z.string() }),
181
+ ev("milestone.merged", { milestone: z.string() }),
182
+ ev("base.behind", { milestone: z.string() }),
183
+ ev("integrate.done", { milestone: z.string(), commit: z.string() }),
184
+ ev("integrate.conflict", { milestone: z.string(), files: strings }),
185
+ /** plan 35: a base run's milestone has its worktree (the tree, its branch, what the claim noted) */
186
+ ev("tree.ready", {
187
+ milestone: z.string(),
188
+ tree: z.string(),
189
+ branch: z.string(),
190
+ base: z.string(),
191
+ hints: strings,
192
+ }),
193
+ /** plan 35: no room in memory for another milestone yet; the claim is tried again later */
194
+ ev("tree.deferred", { milestone: z.string(), availableMb: z.number(), needMb: z.number() }),
195
+ /** plan 35: catherd pushed the milestone's branch on the owner's word; `mr` is the command that opens its MR */
196
+ ev("branch.pushed", {
197
+ milestone: z.string(),
198
+ branch: z.string(),
199
+ remote: z.literal("origin"),
200
+ mr: z.string(),
201
+ }),
202
+ ev("cancel.requested", { by: z.literal("owner") }),
203
+ ev("effect.failed", { action: z.enum(ACTION_TYPES), unit: z.string(), message: z.string() }),
204
+ ev("report.posted", { kind: z.enum(REPORT_KINDS), unit: z.string(), text: z.string() }),
205
+ ev("tick", {}),
206
+ ev("run.ended", { outcome, reportPath: z.string().nullable() }),
207
+ ]);
208
+ export type RunEvent = z.infer<typeof RunEventSchema>;
209
+ export type RunEventType = RunEvent["type"];
210
+ /** An event as an effect or client writes it: the store adds `v`, `seq` and `at`. */
211
+ export type RunEventInput = RunEvent extends infer E
212
+ ? E extends RunEvent
213
+ ? Omit<E, "v" | "seq" | "at">
214
+ : never
215
+ : never;
216
+
217
+ const one = (s: string): string => s.split("\n")[0]?.trim() ?? "";
218
+
219
+ /**
220
+ * One line about an event, for the timeline and the report; never a reply body. `rungOf` names the rung a
221
+ * dispatch ran on (the ended event does not carry it): the state's dispatch table gives it.
222
+ */
223
+ export function describeEvent(e: RunEvent, rungOf?: (dispatchId: string) => string | undefined): string {
224
+ switch (e.type) {
225
+ case "run.started":
226
+ return `run started: ${e.title}`;
227
+ case "daemon.started":
228
+ return `daemon started (pid ${e.pid}, catherd ${e.version})`;
229
+ case "plan.ready":
230
+ return `plan ready: ${e.milestones.map((m) => m.id).join(", ") || "no milestones"}`;
231
+ case "plan.approved":
232
+ return `plan approved by the ${e.by}`;
233
+ case "lanes.routed":
234
+ return `${e.milestone} routed: ${e.lanes.map((l) => `${l.lane} on ${l.rung}`).join(", ") || "no lanes"}`;
235
+ case "preflight.done": {
236
+ const lint = e.lint.length ? ` · ${e.lint.length} lint warning(s)` : "";
237
+ return e.blocking.length
238
+ ? `${e.milestone} preflight blocked: ${e.blocking.map((b) => b.lane).join(", ")}${lint}`
239
+ : e.confirm
240
+ ? `${e.milestone} preflight waits for confirmation${lint}`
241
+ : `${e.milestone} preflight passed${lint}`;
242
+ }
243
+ case "dispatch.started":
244
+ return `${e.name} started on ${e.rung}`;
245
+ case "dispatch.refused":
246
+ return `${e.name} refused: ${e.code}`;
247
+ case "dispatch.ended": {
248
+ const rung = rungOf?.(e.dispatchId);
249
+ return `${e.name} ended ${e.status} (${e.replyStatus ?? "no status"})${rung ? ` on ${rung}` : ""}`;
250
+ }
251
+ case "check.ran":
252
+ return `${e.lane} fast check ${e.exit === 0 ? "passed" : `failed (exit ${e.exit ?? "timeout"})`}`;
253
+ case "commit.made":
254
+ return `${e.milestone} committed ${e.commit.slice(0, 7)} (round ${e.round})`;
255
+ case "commit.failed":
256
+ return `${e.milestone} commit failed (round ${e.round})`;
257
+ case "lane.climbed":
258
+ return `${e.lane} climbed to ${e.rung}${e.top ? " (top rung)" : ""}`;
259
+ case "stop.classified":
260
+ return `dispatch ${e.dispatchId} stop classified: ${e.reason ?? "none"}`;
261
+ case "judge.decided":
262
+ return `judge ${e.id} on ${e.unit} (${e.kind}): ${e.decision.verb}`;
263
+ case "judge.failed":
264
+ return `judge ${e.id} on ${e.unit} failed: ${one(e.why)}`;
265
+ case "question.parked":
266
+ return `${e.unit} parked (${e.reason}): ${one(e.question)}`;
267
+ case "question.answered":
268
+ return `${e.unit} answered by the ${e.by}`;
269
+ case "milestone.landed":
270
+ return `${e.milestone} landed at ${e.commit.slice(0, 7)}${e.knownIssues.length ? ` with ${e.knownIssues.length} known issue(s)` : ""}`;
271
+ case "land.refused":
272
+ return `${e.milestone} land refused: ${e.code}`;
273
+ case "milestone.merged":
274
+ return `${e.milestone} merged`;
275
+ case "base.behind":
276
+ return `${e.milestone} is behind its base`;
277
+ case "integrate.done":
278
+ return `${e.milestone} integrated at ${e.commit.slice(0, 7)}`;
279
+ case "integrate.conflict":
280
+ return `${e.milestone} integration conflict in ${e.files.length} file(s)`;
281
+ case "tree.ready":
282
+ return `${e.milestone} has its worktree on branch ${e.branch}`;
283
+ case "tree.deferred":
284
+ return `${e.milestone} waits for memory: ${e.availableMb} MB available, ${e.needMb} MB needed`;
285
+ case "branch.pushed":
286
+ return `${e.milestone}'s branch ${e.branch} is pushed to ${e.remote}`;
287
+ case "cancel.requested":
288
+ return `cancel requested by the ${e.by}`;
289
+ case "effect.failed":
290
+ return `${e.action} on ${e.unit} failed: ${one(e.message)}`;
291
+ case "report.posted":
292
+ return `report (${e.kind}) on ${e.unit}`;
293
+ case "tick":
294
+ return "tick";
295
+ case "run.ended":
296
+ return `run ended ${e.outcome}`;
297
+ }
298
+ }