import { END } from "@uncaged/nerve-core"; import type { Moderator } from "@uncaged/nerve-core"; import type { IntakeMeta } from "./roles/intake/index.js"; import type { IssueReaderMeta } from "./roles/issue-reader/index.js"; import type { PlannerMeta } from "./roles/planner/index.js"; import type { ImplementerMeta } from "./roles/implementer/index.js"; import type { ReviewerMeta } from "./roles/reviewer/index.js"; import type { TesterMeta } from "./roles/tester/index.js"; import type { PrPublisherMeta } from "./roles/pr-publisher/index.js"; export type WorkflowMeta = { intake: IntakeMeta; "issue-reader": IssueReaderMeta; planner: PlannerMeta; implementer: ImplementerMeta; reviewer: ReviewerMeta; tester: TesterMeta; "pr-publisher": PrPublisherMeta; }; const MAX_IMPLEMENTER_SELF_ITERATIONS = 5; const MAX_REVIEWER_REJECTIONS = 3; const MAX_TESTER_REJECTIONS = 3; function countRejections(steps: { role: string; meta: unknown }[], rejector: string, metaKey: string): number { return steps.filter((s) => s.role === rejector && !(s.meta as Record)[metaKey]).length; } function countImplementerSelfIterations(steps: { role: string; meta: unknown }[]): number { return steps.filter((s) => s.role === "implementer" && !(s.meta as Record).implementationOk).length; } export const moderator: Moderator = (context) => { if (context.steps.length === 0) { return "intake"; } const last = context.steps[context.steps.length - 1]; if (last.role === "intake") { const meta = last.meta as WorkflowMeta["intake"]; return meta.valid ? "issue-reader" : END; } if (last.role === "issue-reader") { const meta = last.meta as WorkflowMeta["issue-reader"]; return meta.fetchOk ? "planner" : END; } if (last.role === "planner") { const meta = last.meta as WorkflowMeta["planner"]; return meta.planningOk ? "implementer" : END; } if (last.role === "implementer") { const meta = last.meta as WorkflowMeta["implementer"]; if (meta.implementationOk) return "reviewer"; return countImplementerSelfIterations(context.steps) < MAX_IMPLEMENTER_SELF_ITERATIONS ? "implementer" : END; } if (last.role === "reviewer") { if (last.meta.approved) return "tester"; return countRejections(context.steps, "reviewer", "approved") < MAX_REVIEWER_REJECTIONS ? "implementer" : END; } if (last.role === "tester") { const meta = last.meta as WorkflowMeta["tester"]; if (meta.passed) return "pr-publisher"; return countRejections(context.steps, "tester", "passed") < MAX_TESTER_REJECTIONS ? "implementer" : END; } if (last.role === "pr-publisher") { return END; } return END; };