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
60 lines
1.8 KiB
TypeScript
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" };
|
|
}
|