/** * Bounded recovery classification (PLA-240). * * A verdict here is not an action. The controller decides whether to re-arm, * route, or leave the Todo on Needs you. The open run, the approval and the * clock arrive as inputs, so the replay suite can feed it history with no DB. */ export declare const RECOVERY_CLASSES: readonly ["transient", "code", "verification", "security", "operator"]; export type RecoveryClass = (typeof RECOVERY_CLASSES)[number]; export declare const ATTENTION_LANES: readonly ["recovering", "manager", "operator"]; export type AttentionLane = (typeof ATTENTION_LANES)[number]; export declare const TODO_RECOVERY_ACTOR = "todo-recovery"; export declare const MAX_RECOVERY_ATTEMPTS = 2; export declare const EXECUTION_TIMEOUT_MS: number; /** A pipeline between runs, not a stalled one. */ export declare function runIsFresh(endedAt: string | null | undefined, now: number): boolean; /** Generic fallback: classifyRecovery found no specific incident. */ export declare const GENERIC_OPERATOR_REASON = "no safe automatic recovery is known"; export declare function isGenericOperatorFallback(verdict: RecoveryClassification): boolean; /** * The recovery sweep is the only writer of a Todo's `work_item_recovery` row, * so successive verdicts on it are all this guard has to reconcile. * A later generic operator fallback cannot downgrade an unresolved specific * lane (manager / recovering). Specific verdicts (failure class, stalled run * or assignment, leftover manager, routed approval, operator-only) may * replace. Terminal status means the prior condition resolved. */ export declare function mayReplaceRecoveryLane(prior: { lane: AttentionLane; } | undefined, next: RecoveryClassification, itemStatus: string): boolean; export interface RecoveryClassification { class: RecoveryClass; lane: AttentionLane; reason: string; owningWorkflowId?: string; } export interface RecoveryIncidentInput { todo: { id: string; status: string; assignee: string | null; source: string; }; lastRun?: { id: string; outcome: string; error: string | null; endedAt: string | null; }; openRun?: { startedAt: string; sessionInFlight: boolean; }; approval?: { state: string; operatorOnly: boolean; }; verifyMode?: "trust" | "verify" | "thorough"; owningWorkflowId?: string; now?: Date; } export declare function classifyRecovery(input: RecoveryIncidentInput): RecoveryClassification; /** Additive: never a column on `work_items`. The exact-shape verifier refuses * drift in an existing table, so a new table is the only extension a deployed * database can survive. */ export declare const WORK_ITEM_RECOVERY_DDL = "\nCREATE TABLE IF NOT EXISTS work_item_recovery (\n work_item_id TEXT PRIMARY KEY REFERENCES work_items(id) ON DELETE CASCADE,\n incident_id TEXT NOT NULL,\n class TEXT NOT NULL CHECK (class IN ('transient','code','verification','security','operator')),\n lane TEXT NOT NULL CHECK (lane IN ('recovering','manager','operator')),\n attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0 AND attempts <= 2),\n last_attempt_at TEXT,\n last_run_id TEXT,\n reason TEXT NOT NULL,\n updated_at TEXT NOT NULL\n)"; export declare const WORK_ITEM_RECOVERY_TABLES: ReadonlyArray<{ name: string; ddl: string; }>; export interface WorkItemRecovery { workItemId: string; incidentId: string; class: RecoveryClass; lane: AttentionLane; attempts: number; lastAttemptAt: string | null; lastRunId: string | null; reason: string; updatedAt: string; } export interface UpsertRecoveryInput { workItemId: string; incidentId: string; class: RecoveryClass; lane: AttentionLane; reason: string; lastRunId?: string | null; /** When true, increment attempts for the same incident (capped at 2). A new * incident_id starts at 0. */ attempted?: boolean; now?: Date; } //# sourceMappingURL=recovery.d.ts.map