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.
- package/README.md +188 -169
- package/THIRD_PARTY_NOTICES.md +217 -35
- package/catalog/jev.json +27 -1
- package/catalog/models.json +56 -239
- package/package.json +6 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/bin/catherd-mcp +1 -1
- package/plugin/commands/catherd.md +2 -2
- package/plugin/skills/catherd/SKILL.md +97 -356
- package/plugin/skills/catherd-setup/SKILL.md +21 -20
- package/src/adapters/all.ts +0 -4
- package/src/adapters/backend.ts +19 -4
- package/src/adapters/claude-code/index.ts +126 -14
- package/src/adapters/codex/events.ts +10 -0
- package/src/adapters/codex/index.ts +17 -5
- package/src/adapters/cursor/home.ts +1 -1
- package/src/adapters/cursor/index.ts +10 -4
- package/src/adapters/opencode/index.ts +38 -1
- package/src/cli.ts +27 -27
- package/src/domain/agents.ts +3 -1
- package/src/domain/bars.ts +33 -107
- package/src/domain/blocked.ts +72 -0
- package/src/domain/branch.ts +51 -0
- package/src/domain/brief.ts +106 -0
- package/src/domain/catalog.ts +69 -313
- package/src/domain/changes.ts +23 -1
- package/src/domain/cost.ts +19 -29
- package/src/domain/errors.ts +6 -2
- package/src/domain/fail-lines.ts +26 -0
- package/src/domain/failover.ts +37 -75
- package/src/domain/findings.ts +47 -0
- package/src/domain/gate-brief.ts +53 -0
- package/src/domain/hints.ts +13 -2
- package/src/domain/ids.ts +13 -2
- package/src/domain/jev.ts +47 -2
- package/src/domain/judge.ts +124 -0
- package/src/domain/lane.ts +118 -3
- package/src/domain/milestone-memory.ts +53 -0
- package/src/domain/notice.ts +32 -8
- package/src/domain/notify.ts +32 -0
- package/src/domain/plan-doc.ts +198 -0
- package/src/domain/plan-lint.ts +141 -0
- package/src/domain/profile-rules.ts +175 -138
- package/src/domain/profile.ts +140 -63
- package/src/domain/record.ts +68 -3
- package/src/domain/reply.ts +29 -0
- package/src/domain/role-prompts.ts +182 -56
- package/src/domain/role-scope.ts +76 -0
- package/src/domain/role-tools.ts +34 -0
- package/src/domain/roles.ts +2 -0
- package/src/domain/route.ts +90 -0
- package/src/domain/rules.ts +33 -0
- package/src/domain/run-events.ts +298 -0
- package/src/domain/run-machine.ts +668 -0
- package/src/domain/run-state.ts +1310 -0
- package/src/domain/scorecard.ts +197 -0
- package/src/domain/select.ts +229 -72
- package/src/domain/slopalytics.ts +72 -0
- package/src/domain/state.ts +5 -0
- package/src/domain/util.ts +22 -0
- package/src/domain/workspace.ts +130 -0
- package/src/entry/answer-command.ts +39 -0
- package/src/entry/catalog-command.ts +24 -165
- package/src/entry/check-bin.ts +17 -0
- package/src/entry/cli-kit.ts +18 -2
- package/src/entry/daemon-bin.ts +22 -0
- package/src/entry/deps.ts +2 -0
- package/src/entry/doctor-command.ts +20 -2
- package/src/entry/glyphs.ts +15 -2
- package/src/entry/host-arg.ts +28 -0
- package/src/entry/init-command.ts +20 -69
- package/src/entry/knowledge-command.ts +121 -5
- package/src/entry/lock-command.ts +103 -8
- package/src/entry/mcp/handshake.ts +6 -2
- package/src/entry/mcp/role-bin.ts +20 -0
- package/src/entry/mcp/role-server.ts +134 -0
- package/src/entry/mcp/server.ts +85 -21
- package/src/entry/mcp/session-tools.ts +149 -0
- package/src/entry/panel/openapi.ts +189 -0
- package/src/entry/panel/server.ts +410 -0
- package/src/entry/panel-command.ts +68 -0
- package/src/entry/pause-command.ts +67 -0
- package/src/entry/profile-command.ts +33 -18
- package/src/entry/role-cli-command.ts +159 -0
- package/src/entry/runs-command.ts +81 -27
- package/src/entry/scorecard-command.ts +187 -0
- package/src/entry/supervise-bin.ts +1 -1
- package/src/entry/supervise-command.ts +30 -3
- package/src/entry/workspace-command.ts +44 -0
- package/src/infra/boot-lock.ts +25 -0
- package/src/infra/delivery.ts +3 -0
- package/src/infra/dispatch-dir.ts +2 -0
- package/src/infra/env.ts +11 -3
- package/src/infra/git.ts +70 -0
- package/src/infra/heavy-lock.ts +133 -6
- package/src/infra/host-probe.ts +16 -0
- package/src/infra/launch.ts +63 -7
- package/src/infra/lock-activity.ts +88 -0
- package/src/infra/login-env.ts +48 -0
- package/src/infra/memory.ts +45 -0
- package/src/infra/ntfy.ts +40 -0
- package/src/infra/open-url.ts +42 -0
- package/src/infra/paths.ts +37 -2
- package/src/infra/role-mcp.ts +69 -0
- package/src/infra/slopalytics.ts +30 -0
- package/src/infra/store.ts +6 -1
- package/src/infra/supervisor.ts +107 -16
- package/src/services/admission.ts +133 -31
- package/src/services/backends.ts +4 -6
- package/src/services/budget.ts +26 -6
- package/src/services/capture.ts +1 -22
- package/src/services/catalog-service.ts +155 -254
- package/src/services/credentials.ts +15 -26
- package/src/services/daemon.ts +181 -0
- package/src/services/dispatch-service.ts +209 -17
- package/src/services/dispatches.ts +30 -2
- package/src/services/doctor-access.ts +8 -3
- package/src/services/doctor-backends.ts +1 -1
- package/src/services/doctor-checks.ts +47 -0
- package/src/services/doctor-docker.ts +221 -0
- package/src/services/doctor-role-mcp.ts +65 -0
- package/src/services/doctor.ts +72 -15
- package/src/services/end-push.ts +157 -0
- package/src/services/failover-rungs.ts +109 -0
- package/src/services/finalize.ts +24 -2
- package/src/services/gate-env.ts +217 -0
- package/src/services/gate-service.ts +231 -45
- package/src/services/global-install.ts +46 -13
- package/src/services/jev-service.ts +2 -4
- package/src/services/lane-edit.ts +154 -0
- package/src/services/lane-service.ts +291 -55
- package/src/services/milestone-check.ts +433 -0
- package/src/services/milestone-tree.ts +170 -0
- package/src/services/milestones.ts +167 -29
- package/src/services/notifier.ts +124 -4
- package/src/services/notify-service.ts +26 -0
- package/src/services/orchestrator-wait.ts +82 -0
- package/src/services/panel-setup.ts +116 -0
- package/src/services/pause.ts +161 -0
- package/src/services/peek.ts +79 -17
- package/src/services/plan-input.ts +151 -0
- package/src/services/ports.ts +60 -5
- package/src/services/preflight.ts +232 -24
- package/src/services/profile-service.ts +31 -3
- package/src/services/profile-store.ts +18 -6
- package/src/services/protocol.ts +123 -25
- package/src/services/provenance.ts +34 -89
- package/src/services/questions.ts +24 -0
- package/src/services/reconcile.ts +33 -1
- package/src/services/reentry.ts +7 -6
- package/src/services/role-access.ts +27 -0
- package/src/services/routing-service.ts +161 -42
- package/src/services/run-briefs.ts +477 -0
- package/src/services/run-client.ts +217 -0
- package/src/services/run-effects.ts +837 -0
- package/src/services/run-events-store.ts +174 -0
- package/src/services/run-evidence.ts +1 -1
- package/src/services/run-pin.ts +125 -0
- package/src/services/run-read.ts +221 -0
- package/src/services/run-report.ts +176 -0
- package/src/services/run-service.ts +204 -23
- package/src/services/run-store.ts +159 -18
- package/src/services/scorecard-metrics.ts +86 -0
- package/src/services/scorecard.ts +382 -0
- package/src/services/scratch.ts +60 -0
- package/src/services/sessions.ts +3 -1
- package/src/services/state.ts +21 -0
- package/src/services/summary.ts +76 -12
- package/src/services/tree-path.ts +27 -0
- package/src/services/verifier-brief.ts +23 -0
- package/src/services/verifier-step.ts +48 -0
- package/src/services/workspace-admission.ts +259 -0
- package/src/services/workspace-service.ts +259 -0
- package/src/services/workspace-store.ts +95 -0
- package/web/dist/assets/Brand-PKlUX5TE.js +1 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.br +0 -0
- package/web/dist/assets/Brand-PKlUX5TE.js.gz +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js +1 -0
- package/web/dist/assets/Modal-Dxc5so53.js.br +0 -0
- package/web/dist/assets/Modal-Dxc5so53.js.gz +0 -0
- package/web/dist/assets/Pressable-C8-T6Ed_.js +1 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js +153 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.br +0 -0
- package/web/dist/assets/StatusChip-B5xfI_Mn.js.gz +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js +1 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.br +0 -0
- package/web/dist/assets/_app-Ea5cpyKe.js.gz +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js +1 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.br +0 -0
- package/web/dist/assets/_runId-pTG0fXVo.js.gz +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js +1 -0
- package/web/dist/assets/drawer-Du3cdlok.js.br +0 -0
- package/web/dist/assets/drawer-Du3cdlok.js.gz +0 -0
- package/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/web/dist/assets/index-B6GiwDkh.js +11 -0
- package/web/dist/assets/index-B6GiwDkh.js.br +0 -0
- package/web/dist/assets/index-B6GiwDkh.js.gz +0 -0
- package/web/dist/assets/index-C5IbylvM.css +2 -0
- package/web/dist/assets/index-C5IbylvM.css.br +0 -0
- package/web/dist/assets/index-C5IbylvM.css.gz +0 -0
- package/web/dist/assets/label-DFSYBc62.js +1 -0
- package/web/dist/assets/label-DFSYBc62.js.br +0 -0
- package/web/dist/assets/label-DFSYBc62.js.gz +0 -0
- package/web/dist/assets/link-D8EcKZUt.js +1 -0
- package/web/dist/assets/link-D8EcKZUt.js.br +0 -0
- package/web/dist/assets/link-D8EcKZUt.js.gz +0 -0
- package/web/dist/assets/login-DaN5HPPW.js +1 -0
- package/web/dist/assets/login-DaN5HPPW.js.br +0 -0
- package/web/dist/assets/login-DaN5HPPW.js.gz +0 -0
- package/web/dist/assets/query-Cu--ccT3.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js +1 -0
- package/web/dist/assets/questions-shoO9OVj.js.br +0 -0
- package/web/dist/assets/questions-shoO9OVj.js.gz +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js +12 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.br +0 -0
- package/web/dist/assets/react-query.gen-Bu2yV-qJ.js.gz +0 -0
- package/web/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/web/dist/assets/run-status-C0DRuV7U.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js +1 -0
- package/web/dist/assets/runs-C3rg2zZc.js.br +0 -0
- package/web/dist/assets/runs-C3rg2zZc.js.gz +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js +1 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.br +0 -0
- package/web/dist/assets/setup-5I4Vl9fR.js.gz +0 -0
- package/web/dist/assets/table-BOxXhqS4.js +1 -0
- package/web/dist/assets/table-BOxXhqS4.js.br +0 -0
- package/web/dist/assets/table-BOxXhqS4.js.gz +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js +1 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.br +0 -0
- package/web/dist/assets/textfield-BzRFkxBF.js.gz +0 -0
- package/web/dist/assets/time--9kSQbbp.js +1 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js +5 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.br +0 -0
- package/web/dist/assets/use-css-variable-CpnN9WtA.js.gz +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js +1 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.br +0 -0
- package/web/dist/assets/useMutation-CVPKlR34.js.gz +0 -0
- package/web/dist/index.html +15 -0
- package/catalog/ATTRIBUTION.md +0 -36
- package/catalog/scores.json +0 -3202
- package/catalog/sources.json +0 -97
- package/src/adapters/antigravity/events.ts +0 -131
- package/src/adapters/antigravity/home.ts +0 -46
- package/src/adapters/antigravity/index.ts +0 -320
- package/src/adapters/antigravity/models.ts +0 -37
- package/src/adapters/grok/events.ts +0 -112
- package/src/adapters/grok/home.ts +0 -132
- package/src/adapters/grok/index.ts +0 -339
- package/src/adapters/grok/models.ts +0 -48
- package/src/domain/calibration.ts +0 -125
- package/src/domain/sources.ts +0 -144
- package/src/entry/mcp/dispatch-tools.ts +0 -52
- package/src/entry/mcp/lane-tools.ts +0 -84
- package/src/entry/mcp/protocol-tools.ts +0 -57
- package/src/entry/mcp/run-tools.ts +0 -148
- package/src/entry/mcp/setup-tools.ts +0 -108
- package/src/entry/tui/commands.ts +0 -563
- package/src/entry/tui/effects.ts +0 -381
- package/src/entry/tui/fixtures.ts +0 -637
- package/src/entry/tui/profile-edits.ts +0 -146
- package/src/entry/tui/profile-tree.ts +0 -429
- package/src/entry/tui/providers/app.tsx +0 -162
- package/src/entry/tui/providers/boundary.tsx +0 -57
- package/src/entry/tui/providers/data.tsx +0 -160
- package/src/entry/tui/providers/keymap.tsx +0 -166
- package/src/entry/tui/providers/root.tsx +0 -45
- package/src/entry/tui/providers/theme.tsx +0 -17
- package/src/entry/tui/providers/toast.tsx +0 -77
- package/src/entry/tui/run.tsx +0 -114
- package/src/entry/tui/state.ts +0 -373
- package/src/entry/tui/stories.ts +0 -114
- package/src/entry/tui/text.ts +0 -130
- package/src/entry/tui/theme.ts +0 -140
- package/src/entry/tui/views/app.tsx +0 -208
- package/src/entry/tui/views/dialogs.tsx +0 -41
- package/src/entry/tui/views/profile-actions.ts +0 -331
- package/src/entry/tui/views/profiles.tsx +0 -520
- package/src/entry/tui/views/runs.tsx +0 -573
- package/src/entry/tui/views/save-dialog.tsx +0 -340
- package/src/entry/tui/views/status.tsx +0 -209
- package/src/entry/tui/widgets/chrome.tsx +0 -114
- package/src/entry/tui/widgets/dialog-confirm.tsx +0 -58
- package/src/entry/tui/widgets/dialog-prompt.tsx +0 -52
- package/src/entry/tui/widgets/dialog-select.tsx +0 -212
- package/src/entry/tui/widgets/dialog.tsx +0 -68
- package/src/entry/tui/widgets/line.tsx +0 -92
- package/src/entry/tui/widgets/list.tsx +0 -197
- package/src/entry/tui/widgets/spinner.tsx +0 -49
- package/src/entry/tui/widgets/toast.tsx +0 -62
- package/src/infra/sources/arena.ts +0 -87
- package/src/infra/sources/artificial-analysis.ts +0 -112
- package/src/infra/sources/cache.ts +0 -124
- package/src/infra/sources/csv.ts +0 -38
- package/src/infra/sources/epoch.ts +0 -82
- package/src/infra/sources/http.ts +0 -75
- package/src/infra/sources/litellm.ts +0 -58
- package/src/infra/sources/models-dev.ts +0 -59
- package/src/infra/sources/openrouter-endpoints.ts +0 -52
- package/src/infra/sources/openrouter-models.ts +0 -60
- package/src/infra/sources/rows.ts +0 -38
- package/src/infra/sources/vectara.ts +0 -69
- package/src/infra/sources/zip.ts +0 -48
- package/src/services/catalog-refresh.ts +0 -201
- package/src/services/doctor-sources.ts +0 -76
- package/src/services/runs-page.ts +0 -255
- package/src/services/source-derive.ts +0 -374
- package/src/services/source-sync.ts +0 -305
- package/src/services/standins.ts +0 -204
- 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
|
+
}
|