Files
fusion/packages/engine/src/error-classifier.ts
gsxdsm 22fee60aee chore(FN-4350): import dependency content from fusion/fn-4326
Squash-imported the working tree of fusion/fn-4326 as a single commit so this branch carries the dep's content without inheriting its individual commits. If the dep is later squash-merged to main, this commit's patch-id should match the merge and rebase cleanly.

Fusion-Task-Id: FN-4350
Fusion-Task-Lineage: 11fff2c2-1cd1-4562-9b01-032a15217479
2026-05-13 12:30:45 -07:00

60 lines
1.8 KiB
TypeScript

import { BranchConflictError } from "./branch-conflicts.js";
export type ErrorClass =
| "branch-conflict-stale"
| "branch-conflict-live-other"
| "branch-conflict-reclaimable"
| "branch-conflict-unrecoverable"
| "worktree-missing"
| "worktree-locked"
| "merge-conflict"
| "audit-failure"
| "unknown";
export interface TaskErrorClassification {
class: ErrorClass;
recoverable: "auto" | "sticky";
retryAfterMs?: number;
}
function getErrorMessage(err: unknown): string {
if (err instanceof Error) return err.message;
return String(err ?? "");
}
export function classifyTaskError(err: unknown): TaskErrorClassification {
if (err instanceof BranchConflictError) {
const kind = (err as { kind?: string }).kind;
if (kind === "stale" || kind === "stale-resolved") {
return { class: "branch-conflict-stale", recoverable: "auto" };
}
if (kind === "reclaimable") {
return { class: "branch-conflict-reclaimable", recoverable: "auto" };
}
if (kind === "live-foreign") {
return { class: "branch-conflict-live-other", recoverable: "auto" };
}
return { class: "branch-conflict-unrecoverable", recoverable: "sticky" };
}
const message = getErrorMessage(err);
if (/is not a working tree|No such file or directory/i.test(message)) {
return { class: "worktree-missing", recoverable: "auto" };
}
if (/worktree is locked/i.test(message)) {
return { class: "worktree-locked", recoverable: "auto", retryAfterMs: 2000 };
}
if (err instanceof Error && err.name === "SquashAuditError") {
return { class: "audit-failure", recoverable: "sticky" };
}
if (/merge conflict|CONFLICT \(/i.test(message)) {
return { class: "merge-conflict", recoverable: "auto" };
}
return { class: "unknown", recoverable: "sticky" };
}