feat(FN-4545): complete Step 1 — extend auto-recovery failure taxonomy
Fusion-Task-Id: FN-4545 Fusion-Task-Lineage: 95108429-618e-4a6d-9fa9-7ac2596665a2
This commit is contained in:
@@ -234,7 +234,8 @@ export type AutoRecoveryFailureClass =
|
|||||||
| "branch-cross-contamination"
|
| "branch-cross-contamination"
|
||||||
| "branch-conflict-tripwire"
|
| "branch-conflict-tripwire"
|
||||||
| "branch-conflict-recovery-exhausted"
|
| "branch-conflict-recovery-exhausted"
|
||||||
| "branch-conflict-unrecoverable";
|
| "branch-conflict-unrecoverable"
|
||||||
|
| "message-delivery-failure";
|
||||||
|
|
||||||
export interface AutoRecoverySettings {
|
export interface AutoRecoverySettings {
|
||||||
mode: AutoRecoveryMode;
|
mode: AutoRecoveryMode;
|
||||||
@@ -265,6 +266,7 @@ export function normalizeAutoRecovery(value: unknown): AutoRecoverySettings {
|
|||||||
"branch-conflict-tripwire",
|
"branch-conflict-tripwire",
|
||||||
"branch-conflict-recovery-exhausted",
|
"branch-conflict-recovery-exhausted",
|
||||||
"branch-conflict-unrecoverable",
|
"branch-conflict-unrecoverable",
|
||||||
|
"message-delivery-failure",
|
||||||
].includes(k)
|
].includes(k)
|
||||||
&& typeof v === "string"
|
&& typeof v === "string"
|
||||||
&& (AUTO_RECOVERY_MODES as readonly string[]).includes(v)
|
&& (AUTO_RECOVERY_MODES as readonly string[]).includes(v)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const classes: AutoRecoveryFailure["class"][] = [
|
|||||||
"branch-conflict-tripwire",
|
"branch-conflict-tripwire",
|
||||||
"branch-conflict-recovery-exhausted",
|
"branch-conflict-recovery-exhausted",
|
||||||
"branch-conflict-unrecoverable",
|
"branch-conflict-unrecoverable",
|
||||||
|
"message-delivery-failure",
|
||||||
];
|
];
|
||||||
|
|
||||||
describe("auto-recovery dispatcher", () => {
|
describe("auto-recovery dispatcher", () => {
|
||||||
@@ -68,6 +69,21 @@ describe("auto-recovery dispatcher", () => {
|
|||||||
expect(decision.rationale).toBe("destructive-ambiguity");
|
expect(decision.rationale).toBe("destructive-ambiguity");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
["off", "pause"],
|
||||||
|
["deterministic-only", "pause"],
|
||||||
|
["programmatic", "retry"],
|
||||||
|
["ai-assisted", "retry"],
|
||||||
|
] as const)("message-delivery-failure routes %s to %s", (mode, expectedAction) => {
|
||||||
|
const { dispatcher } = createDispatcher();
|
||||||
|
const decision = dispatcher.classify({ class: "message-delivery-failure", taskId: "FN-1", pausedReason: "message-delivery-failure" }, {
|
||||||
|
task,
|
||||||
|
retryCount: 0,
|
||||||
|
settings: { mode, maxRetries: 3 },
|
||||||
|
});
|
||||||
|
expect(decision.action).toBe(expectedAction);
|
||||||
|
});
|
||||||
|
|
||||||
it("dispatch falls back to pause when handler missing", async () => {
|
it("dispatch falls back to pause when handler missing", async () => {
|
||||||
const { dispatcher, database } = createDispatcher();
|
const { dispatcher, database } = createDispatcher();
|
||||||
const decision = await dispatcher.dispatch({ class: "branch-conflict-unrecoverable", taskId: "FN-1", pausedReason: "branch-conflict-unrecoverable" }, {
|
const decision = await dispatcher.dispatch({ class: "branch-conflict-unrecoverable", taskId: "FN-1", pausedReason: "branch-conflict-unrecoverable" }, {
|
||||||
|
|||||||
Reference in New Issue
Block a user