/**
 * CredentialRebalancerPolicy — the §2.4 "stock-trader loop" decision core, as a PURE
 * function (Increment B, step B1 of live credential re-pointing).
 *
 * Spec: docs/specs/live-credential-repointing-rebalancer.md §2.4 (the balancer).
 *
 * ── What this is ──
 * Given a read-only snapshot of a pass (per-account quota + reset proximity, per-slot
 * tenancy/verify/activity, cooldown state, resolved+clamped config), this computes the
 * ZERO-OR-MORE swap decisions for ONE pass. It performs NO IO and holds NO authority:
 * it DECIDES; a separate actuator (step B3) routes an accepted decision through the
 * Step-5 CredentialSwapExecutor under the dark/dry-run gate. Splitting the decision out
 * makes the entire policy — every threshold, cap, cooldown, and eligibility rule —
 * exhaustively unit-testable without a keychain (Tier-0 supervision, §2.4: a
 * deterministic policy over enumerable numeric thresholds).
 *
 * ── Priority of objectives (§2.4) ──
 *   1. Wall avoidance  — a tenant over the high-water mark gets the highest-headroom
 *      eligible account. A tenant over the CRITICAL mark triggers a wall-OVERRIDE that
 *      bypasses the cooldowns + the 1-swap/pass cap, bounded by its own caps
 *      (fresh-data gate, maxForcedSwapsPerPass, maxForcedOverridesPerWindow).
 *   2. Use-it-or-lose-it drain — an account whose WEEKLY window resets soon with unused
 *      headroom is dealt to the busiest slot (weekly only; 5h windows regenerate).
 *   3. Default-slot preference — keep the designated default account in `~/.claude`.
 *
 * Non-forced objectives are bounded to ONE swap per pass (acting twice on one sensor
 * reading is noise). Only the wall-override may emit up to `maxForcedSwapsPerPass`.
 *
 * Dead-default eviction, the correlated-oracle-outage floor, quarantine-exit, the P19
 * breaker, and the scheduled identity audit are step B2 (this module decides swaps; B2
 * adds the degraded-state machinery). This module surfaces the terminal "nothing to do"
 * / "no eligible target" states so B2/B3 can act on them.
 */
/** Resolved + clamped knobs the pure policy operates on (the resolver lives in B3). */
export interface RebalancerPolicyConfig {
    /** Wall-avoidance high-water utilization % (either window). Clamp [50,99]. */
    highWaterPct: number;
    /** Critical mark % for the wall-override. Clamp [85,99]. */
    criticalPct: number;
    /** Drain horizon — a weekly window resetting within this many hours is drainable. */
    drainHorizonHours: number;
    /** Minimum unused weekly headroom % for a drain to be worth it. */
    drainHeadroomMinPct: number;
    /** Minimum score delta to justify a NON-forced move (urgency-clamped). */
    minScoreDelta: number;
    /** Max forced (wall-override) swaps a single pass may emit. Clamp [1, N-slots]. */
    maxForcedSwapsPerPass: number;
    /** Per-pair cooldown ms (≥1× poll interval). */
    perPairCooldownMs: number;
    /** Per-tenant cooldown ms (≥2× poll interval) — defeats the 3-way rotation attack. */
    perTenantCooldownMs: number;
    /** Quota older than this is SOURCE-only (never a swap-in target). */
    staleQuotaMs: number;
    /** Urgency `1/hoursUntilReset` is clamped at this many hours so the delta floor stays meaningful. */
    urgencyClampHours: number;
}
export interface AccountState {
    accountId: string;
    status: 'ok' | 'needs-reauth' | 'disabled';
    /** Utilization % on the 5h window (0..100), or null when unknown. */
    fiveHrPct: number | null;
    /** Utilization % on the weekly window (0..100), or null when unknown. */
    weeklyPct: number | null;
    /** Hours until the weekly window resets, or null when unknown. */
    weeklyResetsInHours: number | null;
    /** When this account's quota was last measured (for the staleness gate). */
    measuredAt: number;
}
export interface SlotState {
    slot: string;
    tenantAccountId: string | null;
    isDefault: boolean;
    quarantined: boolean;
    /** Last oracle identity-verify time, or null if never. */
    lastVerifiedAt: number | null;
    /** True if the last scheduled audit flagged this slot's identity as divergent. */
    lastAuditDivergent: boolean;
    /** Per-slot "drain in progress" hold — exempt from being a drain DESTINATION (§2.4 obj 2). */
    drainInProgress: boolean;
    /** Recent activity score (higher = busier); drain targets the busiest slot. */
    busyness: number;
    /** The most-recently-previously-verified account for this slot (the correlated-outage
     *  floor's "last known good"); used only to keep the DEFAULT slot non-empty when the
     *  oracle is down for every slot. Optional — absent on a never-verified slot. */
    lastKnownGoodAccountId?: string | null;
}
export interface CooldownState {
    /** key = sortedPair(a,b) → last actuation ms. */
    lastActuationByPair: Record<string, number>;
    /** accountId → last actuation ms (the tenant cooldown). */
    lastActuationByTenant: Record<string, number>;
    /** Forced wall-overrides already spent in the current rolling window. */
    forcedOverridesInWindow: number;
    /** The ceiling for forced overrides in a window; at/over it, the override STOPS (surfaced). */
    maxForcedOverridesPerWindow: number;
}
export interface RebalancePassInput {
    now: number;
    slots: SlotState[];
    accounts: AccountState[];
    cooldowns: CooldownState;
    config: RebalancerPolicyConfig;
    /** A slot's identity verify counts as "recent" if within this window (the audit cadence). */
    auditCadenceMs: number;
    /** The account that SHOULD serve `~/.claude` (so manual `claude` is predictable). When the
     *  default slot's tenant dies/quarantines, a healthy verified tenant is dealt in; when the
     *  desired default is itself dead, any healthy verified tenant keeps the slot alive. Null =
     *  no default preference configured (the default-eviction objective is then inert). */
    desiredDefaultAccountId?: string | null;
}
export interface SwapDecision {
    /** The slot being acted on (rescued, or the drain destination, or the default slot). */
    targetSlot: string;
    /** The slot whose tenant is exchanged in. */
    sourceSlot: string;
    objective: 'wall' | 'drain' | 'default' | 'default-eviction';
    /** Set when this is a cooldown/cap-bypassing wall-override or a default-slot rescue. */
    forced: 'wall-override' | 'default-eviction' | null;
    reason: string;
}
export interface PassResult {
    /** 0..maxForcedSwapsPerPass decisions (normal objectives emit ≤1; wall-override may emit more). */
    decisions: SwapDecision[];
    /** DegradationReporter-bound entries (terminal "stuck" states). */
    degraded: string[];
    /** Attention-queue-bound entries (operator-surfaced terminal states). */
    attention: string[];
    /** Human reason when zero decisions were made (for the audited no-op pass). */
    noActuationReason?: string;
}
/**
 * Decide the swap(s) for one pass. Pure: same input → same output, no IO.
 * A pass with no actuation returns `decisions: []` + a `noActuationReason` (the caller
 * audits the no-op pass and performs ZERO keychain/CLI operations — the §2.4 invariant).
 */
export declare function decidePass(input: RebalancePassInput): PassResult;
//# sourceMappingURL=CredentialRebalancerPolicy.d.ts.map