Files
projectbrain-Public/backend/test/recoveryPolicy.test.ts
T

41 lines
2.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { decideRecovery } from "../src/services/recoveryPolicy.js";
import { normalizeAutonomyState, retryAutonomyBlockerIfDue } from "../src/repositories/autonomyRepository.js";
describe("bounded Auto Mode recovery policy", () => {
it("walks through retry, cooldown and alternate recovery before escalation", () => {
const sequence = [1, 2, 3, 4, 5, 6].map((recoveryAttempts) => decideRecovery({
failureCount: recoveryAttempts,
recoveryAttempts,
recoveryStartedAt: "2026-09-12T10:00:00.000Z",
maxRecoveryMinutes: 120,
now: Date.parse("2026-09-12T10:10:00.000Z"),
}).stage);
expect(sequence).toEqual(["retry", "retry", "cooldown", "retry", "alternate_recovery", "escalated"]);
});
it("stops on the wall-clock budget even if attempts are low", () => {
expect(decideRecovery({
failureCount: 1,
recoveryAttempts: 1,
recoveryStartedAt: "2026-09-12T08:00:00.000Z",
maxRecoveryMinutes: 30,
now: Date.parse("2026-09-12T08:31:00.000Z"),
})).toMatchObject({ stage: "escalated", exhausted: true, reason: "recovery_wall_clock_limit" });
});
it("escalates a persisted blocker when its cooldown wakes after the wall-clock budget", () => {
const state = normalizeAutonomyState({
enabled: true,
maxRecoveryMinutes: 30,
recoveryStartedAt: "2026-09-12T08:00:00.000Z",
blocker: { at: "2026-09-12T08:00:00.000Z", phaseId: "phase-1", reason: "Runner unavailable", kind: "retryable", retryAt: "2026-09-12T08:31:00.000Z", recoveryStage: "retry", recoveryAttempts: 1, recoveryStartedAt: "2026-09-12T08:00:00.000Z" },
roadmap: { id: "roadmap-1", title: "Release", status: "blocked", currentPhaseIndex: 0, importedAt: "2026-09-12T08:00:00.000Z", phases: [{ id: "phase-1", title: "Build", objective: "Build", status: "blocked", items: [] }] },
});
const result = retryAutonomyBlockerIfDue(state, Date.parse("2026-09-12T08:31:00.000Z"));
expect(result.blocker).toMatchObject({ recoveryStage: "escalated" });
expect(result.status).toBe("blocked");
expect(result.roadmap?.status).toBe("blocked");
});
});