/** * M4 scheduler: active-slot admission + deterministic eligible ordering + * persistent bounded exponential backoff + attempt/retry counter separation. * ADR-0004 #3/#4/#8/#9. */ export type TaskPriority = "high" | "medium" | "low"; export interface SchedulableTask { id: string; priority?: TaskPriority; /** Monotonic attempt counter; incremented per admission. */ attemptNo: number; /** Business retry counter; incremented only on retryable exec failure after cleanup confirmed. */ retryCount: number; /** Infra retry counter (L1 transient startup failures); independent of business budget. */ infraRetryCount: number; /** ISO timestamp when this task may next be admitted (backoff). */ nextRetryAt?: string; /** Declaration order in tasks.jsonl (tie-break). */ declaredOrder: number; /** Task declares it requires tmux (no L1 fallback allowed). */ requiresTmux?: boolean; } export type AdmissionState = "open" | "frozen" | "paused"; export interface AdmissionSnapshot { state: AdmissionState; freezeReason?: string; inFlight: string[]; pending: string[]; blocked: string[]; } /** * Deterministic eligible ordering: priority(high→medium→low) → next_retry_at(asc, missing=now) * → declared order. Backoff tasks (next_retry_at in future) are excluded but listed as blocked. */ export declare function rankEligibleTasks(tasks: SchedulableTask[], now?: number): { eligible: SchedulableTask[]; backoff: SchedulableTask[]; }; /** * Active-slot admission controller. `parallel` bounds the number of in-flight * workers over launch→readiness→execute→wait. Unslotted tasks stay pending and * must NOT create resources (caller checks before spawn). */ export declare class AdmissionController { private parallel; private state; private freezeReason?; private inFlight; constructor(parallel: number); get capacity(): number; getSnapshot(): AdmissionSnapshot; isOpen(): boolean; freeze(reason: string): void; pause(reason: string): void; /** Explicit resume — no auto-unfreeze anywhere. */ resume(): void; get freeSlots(): number; /** Admit a task: occupies a slot. Returns false when no slot or admission closed. */ admit(taskId: string, requiresTmux: boolean, hasSpawner: boolean): boolean; /** Release a slot — only after cleanup-confirmed (or completed terminal). */ release(taskId: string): boolean; get inFlightCount(): number; } /** * Bounded exponential backoff with full jitter, deterministic per * (taskId, attempt): same inputs → same output (seeded jitter). * base 5s, max 300s. cap = min(max, base * 2^attempt). */ export declare function nextRetryAtMs(taskId: string, attempt: number, opts?: { baseMs?: number; maxMs?: number; now?: number; }): number; /** * Counter separation rule: a retryable execution failure increments business * retry_count only after cleanup-confirmed; an L1 transient startup failure * (before resource creation) increments infra_retry_count; a cleanup incident * never auto-retries. */ export declare function classifyFailure(kind: "execution" | "infra_transient" | "cleanup_incident"): { increment: "retry_count" | "infra_retry_count" | "none"; retryAllowed: boolean; };