catherd-cli 1.4.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 (320) hide show
  1. package/README.md +188 -169
  2. package/THIRD_PARTY_NOTICES.md +217 -35
  3. package/catalog/jev.json +27 -1
  4. package/catalog/models.json +56 -239
  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 -356
  11. package/plugin/skills/catherd-setup/SKILL.md +21 -20
  12. package/src/adapters/all.ts +0 -4
  13. package/src/adapters/backend.ts +19 -4
  14. package/src/adapters/claude-code/index.ts +126 -14
  15. package/src/adapters/codex/events.ts +10 -0
  16. package/src/adapters/codex/index.ts +17 -5
  17. package/src/adapters/cursor/home.ts +1 -1
  18. package/src/adapters/cursor/index.ts +10 -4
  19. package/src/adapters/opencode/index.ts +38 -1
  20. package/src/cli.ts +27 -27
  21. package/src/domain/agents.ts +3 -1
  22. package/src/domain/bars.ts +33 -107
  23. package/src/domain/blocked.ts +72 -0
  24. package/src/domain/branch.ts +51 -0
  25. package/src/domain/brief.ts +106 -0
  26. package/src/domain/catalog.ts +69 -313
  27. package/src/domain/changes.ts +23 -1
  28. package/src/domain/cost.ts +19 -29
  29. package/src/domain/errors.ts +6 -2
  30. package/src/domain/fail-lines.ts +26 -0
  31. package/src/domain/failover.ts +37 -75
  32. package/src/domain/findings.ts +47 -0
  33. package/src/domain/gate-brief.ts +53 -0
  34. package/src/domain/hints.ts +13 -2
  35. package/src/domain/ids.ts +13 -2
  36. package/src/domain/jev.ts +47 -2
  37. package/src/domain/judge.ts +124 -0
  38. package/src/domain/lane.ts +118 -3
  39. package/src/domain/milestone-memory.ts +53 -0
  40. package/src/domain/notice.ts +32 -8
  41. package/src/domain/notify.ts +32 -0
  42. package/src/domain/plan-doc.ts +198 -0
  43. package/src/domain/plan-lint.ts +141 -0
  44. package/src/domain/profile-rules.ts +175 -138
  45. package/src/domain/profile.ts +140 -63
  46. package/src/domain/record.ts +68 -3
  47. package/src/domain/reply.ts +29 -0
  48. package/src/domain/role-prompts.ts +182 -56
  49. package/src/domain/role-scope.ts +76 -0
  50. package/src/domain/role-tools.ts +34 -0
  51. package/src/domain/roles.ts +2 -0
  52. package/src/domain/route.ts +90 -0
  53. package/src/domain/rules.ts +33 -0
  54. package/src/domain/run-events.ts +298 -0
  55. package/src/domain/run-machine.ts +668 -0
  56. package/src/domain/run-state.ts +1310 -0
  57. package/src/domain/scorecard.ts +197 -0
  58. package/src/domain/select.ts +229 -72
  59. package/src/domain/slopalytics.ts +72 -0
  60. package/src/domain/state.ts +5 -0
  61. package/src/domain/util.ts +22 -0
  62. package/src/domain/workspace.ts +130 -0
  63. package/src/entry/answer-command.ts +39 -0
  64. package/src/entry/catalog-command.ts +24 -165
  65. package/src/entry/check-bin.ts +17 -0
  66. package/src/entry/cli-kit.ts +18 -2
  67. package/src/entry/daemon-bin.ts +22 -0
  68. package/src/entry/deps.ts +2 -0
  69. package/src/entry/doctor-command.ts +20 -2
  70. package/src/entry/glyphs.ts +15 -2
  71. package/src/entry/host-arg.ts +28 -0
  72. package/src/entry/init-command.ts +20 -69
  73. package/src/entry/knowledge-command.ts +121 -5
  74. package/src/entry/lock-command.ts +103 -8
  75. package/src/entry/mcp/handshake.ts +6 -2
  76. package/src/entry/mcp/role-bin.ts +20 -0
  77. package/src/entry/mcp/role-server.ts +134 -0
  78. package/src/entry/mcp/server.ts +85 -21
  79. package/src/entry/mcp/session-tools.ts +149 -0
  80. package/src/entry/panel/openapi.ts +189 -0
  81. package/src/entry/panel/server.ts +410 -0
  82. package/src/entry/panel-command.ts +68 -0
  83. package/src/entry/pause-command.ts +67 -0
  84. package/src/entry/profile-command.ts +33 -18
  85. package/src/entry/role-cli-command.ts +159 -0
  86. package/src/entry/runs-command.ts +81 -27
  87. package/src/entry/scorecard-command.ts +187 -0
  88. package/src/entry/supervise-bin.ts +1 -1
  89. package/src/entry/supervise-command.ts +30 -3
  90. package/src/entry/workspace-command.ts +44 -0
  91. package/src/infra/boot-lock.ts +25 -0
  92. package/src/infra/delivery.ts +3 -0
  93. package/src/infra/dispatch-dir.ts +2 -0
  94. package/src/infra/env.ts +11 -3
  95. package/src/infra/git.ts +70 -0
  96. package/src/infra/heavy-lock.ts +133 -6
  97. package/src/infra/host-probe.ts +16 -0
  98. package/src/infra/launch.ts +63 -7
  99. package/src/infra/lock-activity.ts +88 -0
  100. package/src/infra/login-env.ts +48 -0
  101. package/src/infra/memory.ts +45 -0
  102. package/src/infra/ntfy.ts +40 -0
  103. package/src/infra/open-url.ts +42 -0
  104. package/src/infra/paths.ts +37 -2
  105. package/src/infra/role-mcp.ts +69 -0
  106. package/src/infra/slopalytics.ts +30 -0
  107. package/src/infra/store.ts +6 -1
  108. package/src/infra/supervisor.ts +107 -16
  109. package/src/services/admission.ts +133 -31
  110. package/src/services/backends.ts +4 -6
  111. package/src/services/budget.ts +26 -6
  112. package/src/services/capture.ts +1 -22
  113. package/src/services/catalog-service.ts +155 -254
  114. package/src/services/credentials.ts +15 -26
  115. package/src/services/daemon.ts +181 -0
  116. package/src/services/dispatch-service.ts +209 -17
  117. package/src/services/dispatches.ts +30 -2
  118. package/src/services/doctor-access.ts +8 -3
  119. package/src/services/doctor-backends.ts +1 -1
  120. package/src/services/doctor-checks.ts +47 -0
  121. package/src/services/doctor-docker.ts +221 -0
  122. package/src/services/doctor-role-mcp.ts +65 -0
  123. package/src/services/doctor.ts +72 -15
  124. package/src/services/end-push.ts +157 -0
  125. package/src/services/failover-rungs.ts +109 -0
  126. package/src/services/finalize.ts +24 -2
  127. package/src/services/gate-env.ts +217 -0
  128. package/src/services/gate-service.ts +231 -45
  129. package/src/services/global-install.ts +46 -13
  130. package/src/services/jev-service.ts +2 -4
  131. package/src/services/lane-edit.ts +154 -0
  132. package/src/services/lane-service.ts +291 -55
  133. package/src/services/milestone-check.ts +433 -0
  134. package/src/services/milestone-tree.ts +170 -0
  135. package/src/services/milestones.ts +167 -29
  136. package/src/services/notifier.ts +124 -4
  137. package/src/services/notify-service.ts +26 -0
  138. package/src/services/orchestrator-wait.ts +82 -0
  139. package/src/services/panel-setup.ts +116 -0
  140. package/src/services/pause.ts +161 -0
  141. package/src/services/peek.ts +79 -17
  142. package/src/services/plan-input.ts +151 -0
  143. package/src/services/ports.ts +60 -5
  144. package/src/services/preflight.ts +232 -24
  145. package/src/services/profile-service.ts +31 -3
  146. package/src/services/profile-store.ts +18 -6
  147. package/src/services/protocol.ts +123 -25
  148. package/src/services/provenance.ts +34 -89
  149. package/src/services/questions.ts +24 -0
  150. package/src/services/reconcile.ts +33 -1
  151. package/src/services/reentry.ts +7 -6
  152. package/src/services/role-access.ts +27 -0
  153. package/src/services/routing-service.ts +161 -42
  154. package/src/services/run-briefs.ts +477 -0
  155. package/src/services/run-client.ts +217 -0
  156. package/src/services/run-effects.ts +837 -0
  157. package/src/services/run-events-store.ts +174 -0
  158. package/src/services/run-evidence.ts +1 -1
  159. package/src/services/run-pin.ts +125 -0
  160. package/src/services/run-read.ts +221 -0
  161. package/src/services/run-report.ts +176 -0
  162. package/src/services/run-service.ts +204 -23
  163. package/src/services/run-store.ts +159 -18
  164. package/src/services/scorecard-metrics.ts +86 -0
  165. package/src/services/scorecard.ts +382 -0
  166. package/src/services/scratch.ts +60 -0
  167. package/src/services/sessions.ts +3 -1
  168. package/src/services/state.ts +21 -0
  169. package/src/services/summary.ts +76 -12
  170. package/src/services/tree-path.ts +27 -0
  171. package/src/services/verifier-brief.ts +23 -0
  172. package/src/services/verifier-step.ts +48 -0
  173. package/src/services/workspace-admission.ts +259 -0
  174. package/src/services/workspace-service.ts +259 -0
  175. package/src/services/workspace-store.ts +95 -0
  176. package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
  177. package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
  178. package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
  179. package/web/dist/assets/Modal-Dxc5so53.js +1 -0
  180. package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
  181. package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
  182. package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
  183. package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
  184. package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
  185. package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
  186. package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
  187. package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
  188. package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
  189. package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
  190. package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
  191. package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
  192. package/web/dist/assets/drawer-Du3cdlok.js +1 -0
  193. package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
  194. package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
  195. package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  196. package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  197. package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  198. package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  199. package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  200. package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  201. package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  202. package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  203. package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  204. package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  205. package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  206. package/web/dist/assets/index-B6GiwDkh.js +11 -0
  207. package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
  208. package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
  209. package/web/dist/assets/index-C5IbylvM.css +2 -0
  210. package/web/dist/assets/index-C5IbylvM.css.br +0 -0
  211. package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
  212. package/web/dist/assets/label-DFSYBc62.js +1 -0
  213. package/web/dist/assets/label-DFSYBc62.js.br +0 -0
  214. package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
  215. package/web/dist/assets/link-D8EcKZUt.js +1 -0
  216. package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
  217. package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
  218. package/web/dist/assets/login-DaN5HPPW.js +1 -0
  219. package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
  220. package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
  221. package/web/dist/assets/query-Cu--ccT3.js +1 -0
  222. package/web/dist/assets/questions-shoO9OVj.js +1 -0
  223. package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
  224. package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
  225. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
  226. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
  227. package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
  228. package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  229. package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
  230. package/web/dist/assets/runs-C3rg2zZc.js +1 -0
  231. package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
  232. package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
  233. package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
  234. package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
  235. package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
  236. package/web/dist/assets/table-BOxXhqS4.js +1 -0
  237. package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
  238. package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
  239. package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
  240. package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
  241. package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
  242. package/web/dist/assets/time--9kSQbbp.js +1 -0
  243. package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
  244. package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
  245. package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
  246. package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
  247. package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
  248. package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
  249. package/web/dist/index.html +15 -0
  250. package/catalog/ATTRIBUTION.md +0 -36
  251. package/catalog/scores.json +0 -3202
  252. package/catalog/sources.json +0 -97
  253. package/src/adapters/antigravity/events.ts +0 -131
  254. package/src/adapters/antigravity/home.ts +0 -46
  255. package/src/adapters/antigravity/index.ts +0 -320
  256. package/src/adapters/antigravity/models.ts +0 -37
  257. package/src/adapters/grok/events.ts +0 -112
  258. package/src/adapters/grok/home.ts +0 -132
  259. package/src/adapters/grok/index.ts +0 -339
  260. package/src/adapters/grok/models.ts +0 -48
  261. package/src/domain/calibration.ts +0 -125
  262. package/src/domain/sources.ts +0 -144
  263. package/src/entry/mcp/dispatch-tools.ts +0 -52
  264. package/src/entry/mcp/lane-tools.ts +0 -84
  265. package/src/entry/mcp/protocol-tools.ts +0 -57
  266. package/src/entry/mcp/run-tools.ts +0 -148
  267. package/src/entry/mcp/setup-tools.ts +0 -108
  268. package/src/entry/tui/commands.ts +0 -563
  269. package/src/entry/tui/effects.ts +0 -381
  270. package/src/entry/tui/fixtures.ts +0 -637
  271. package/src/entry/tui/profile-edits.ts +0 -146
  272. package/src/entry/tui/profile-tree.ts +0 -429
  273. package/src/entry/tui/providers/app.tsx +0 -162
  274. package/src/entry/tui/providers/boundary.tsx +0 -57
  275. package/src/entry/tui/providers/data.tsx +0 -160
  276. package/src/entry/tui/providers/keymap.tsx +0 -166
  277. package/src/entry/tui/providers/root.tsx +0 -45
  278. package/src/entry/tui/providers/theme.tsx +0 -17
  279. package/src/entry/tui/providers/toast.tsx +0 -77
  280. package/src/entry/tui/run.tsx +0 -114
  281. package/src/entry/tui/state.ts +0 -373
  282. package/src/entry/tui/stories.ts +0 -114
  283. package/src/entry/tui/text.ts +0 -130
  284. package/src/entry/tui/theme.ts +0 -140
  285. package/src/entry/tui/views/app.tsx +0 -208
  286. package/src/entry/tui/views/dialogs.tsx +0 -41
  287. package/src/entry/tui/views/profile-actions.ts +0 -331
  288. package/src/entry/tui/views/profiles.tsx +0 -520
  289. package/src/entry/tui/views/runs.tsx +0 -573
  290. package/src/entry/tui/views/save-dialog.tsx +0 -340
  291. package/src/entry/tui/views/status.tsx +0 -209
  292. package/src/entry/tui/widgets/chrome.tsx +0 -114
  293. package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
  294. package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
  295. package/src/entry/tui/widgets/dialog-select.tsx +0 -212
  296. package/src/entry/tui/widgets/dialog.tsx +0 -68
  297. package/src/entry/tui/widgets/line.tsx +0 -92
  298. package/src/entry/tui/widgets/list.tsx +0 -197
  299. package/src/entry/tui/widgets/spinner.tsx +0 -49
  300. package/src/entry/tui/widgets/toast.tsx +0 -62
  301. package/src/infra/sources/arena.ts +0 -87
  302. package/src/infra/sources/artificial-analysis.ts +0 -112
  303. package/src/infra/sources/cache.ts +0 -124
  304. package/src/infra/sources/csv.ts +0 -38
  305. package/src/infra/sources/epoch.ts +0 -82
  306. package/src/infra/sources/http.ts +0 -75
  307. package/src/infra/sources/litellm.ts +0 -58
  308. package/src/infra/sources/models-dev.ts +0 -59
  309. package/src/infra/sources/openrouter-endpoints.ts +0 -52
  310. package/src/infra/sources/openrouter-models.ts +0 -60
  311. package/src/infra/sources/rows.ts +0 -38
  312. package/src/infra/sources/vectara.ts +0 -69
  313. package/src/infra/sources/zip.ts +0 -48
  314. package/src/services/catalog-refresh.ts +0 -201
  315. package/src/services/doctor-sources.ts +0 -76
  316. package/src/services/runs-page.ts +0 -255
  317. package/src/services/source-derive.ts +0 -374
  318. package/src/services/source-sync.ts +0 -305
  319. package/src/services/standins.ts +0 -204
  320. package/src/services/treat-likes.ts +0 -97
@@ -0,0 +1,33 @@
1
+ import { ROLES, type Role } from "./roles.ts";
2
+
3
+ /** A `[role, role]` group right after `- <date> <source>: `, or at the start of a hand-edited line. */
4
+ export const RULE_TAG =
5
+ /^(\s*(?:[-*]\s+)?(?:\d{4}-\d{2}-\d{2}\s+[^\n]*?:\s+)?)\[([a-z-]+(?:\s*,\s*[a-z-]+)*)\]\s*/;
6
+
7
+ /** The names of a tag-shaped group, whatever they are; null when the line has none. */
8
+ export function tagNames(line: string): string[] | null {
9
+ const m = RULE_TAG.exec(line);
10
+ return m ? (m[2] as string).split(",").map((n) => n.trim()) : null;
11
+ }
12
+
13
+ /** The roles of a tag whose every name is a role; null when the line has no tag, or a bracket group that is no tag. */
14
+ export function ruleRoles(line: string): Role[] | null {
15
+ const names = tagNames(line);
16
+ return names?.every((n) => (ROLES as readonly string[]).includes(n)) ? (names as Role[]) : null;
17
+ }
18
+
19
+ /** The lines of `text` that reach `role`: untagged ones whole, tagged ones for their roles with the tag removed. */
20
+ export function rulesFor(text: string, role: Role): string {
21
+ const out: string[] = [];
22
+ for (const line of text.split("\n")) {
23
+ const roles = ruleRoles(line);
24
+ if (!roles) out.push(line);
25
+ else if (roles.includes(role)) out.push(line.replace(RULE_TAG, "$1"));
26
+ }
27
+ return out.join("\n").trim();
28
+ }
29
+
30
+ /** The standing rules as a brief section; "" when the role has none. */
31
+ export function rulesBlock(rules: string, role: Role): string {
32
+ return rules.trim() ? `Standing rules for the ${role} role in this repo (its knowledge.md):\n${rules}` : "";
33
+ }
@@ -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
+ }