export type CheckState = "success" | "failure" | "error" | "pending" | "missing" | "untrusted" | "stale"; /** Polls of the fix commit's CI before giving up: 30 x 2 minutes covers an hour of CI. */ export const MAX_CI_POLLS = 30; /** With no statuses at all after this many polls, the repo is assumed to have no CI for the branch. */ export const NO_CI_GRACE_POLLS = 3; /** Attempts to put the pull back in the queue when enqueueing itself fails (for example the queue is full). */ export const MAX_ENQUEUE_ATTEMPTS = 3; export const NO_CI_REASON = "No CI reported on the AI-resolved commit; a human must review and re-queue it"; export type Verdict = { kind: "green" } | { kind: "wait" } | { kind: "red"; reason: string }; /** * Decides whether the fix commit is green. Required checks (from base branch protection) must all succeed; * without required checks every reported status must succeed; with no statuses at all after a grace period * the machine-resolved commit is unverified, so it is handed back to a human instead of re-entering the queue. */ export function ciVerdict(input: { required: string[]; requiredResults: CheckState[]; statuses: Array<{ context: string; state: CheckState }>; poll: number; }): Verdict { if (input.required.length > 0) { const bad = input.required.findIndex((_, index) => ["failure", "error", "untrusted", "stale"].includes(input.requiredResults[index]), ); if (bad >= 0) return { kind: "red", reason: `Required check "${input.required[bad]}" did not pass on the fix` }; if (input.requiredResults.every((state) => state === "success")) return { kind: "green" }; } else if (input.statuses.length > 0) { const bad = input.statuses.find((status) => ["failure", "error", "untrusted"].includes(status.state)); if (bad) return { kind: "red", reason: `Check "${bad.context}" failed on the fix` }; if (input.statuses.every((status) => status.state === "success")) return { kind: "green" }; } else if (input.poll >= NO_CI_GRACE_POLLS) { return { kind: "red", reason: NO_CI_REASON }; } if (input.poll >= MAX_CI_POLLS) return { kind: "red", reason: "Timed out waiting for CI on the fix" }; return { kind: "wait" }; }