/**
 * CredentialRebalancer — the §2.4 balancer ORCHESTRATOR (Increment B, step B3a).
 *
 * Spec: docs/specs/live-credential-repointing-rebalancer.md §2.4.
 *
 * ── What this is ──
 * Wraps the pure `CredentialRebalancerPolicy.decidePass()` decision core in a stateful
 * pass loop: on each `tick()` it builds the read-only pass snapshot from injected
 * providers (ledger/verify → slots, quota poller → accounts, config resolver), asks the
 * policy for the zero-or-more swaps, and ACTUATES each accepted swap through the injected
 * executor — but ONLY under the feature's dark/dry-run gate. It carries the hysteresis
 * state the pure policy cannot (cooldown timestamps across passes) and the §2.4 P19
 * breaker (N consecutive FAILED swaps opens it; it retries on the next quota-fresh pass).
 *
 * ── Safety contract (the autonomous-write surface — this is the risky layer) ──
 *   1. DARK = STRICT NO-OP. When `isEnabled()` is false the tick returns immediately
 *      having built NOTHING and called the executor ZERO times (the §2.4 "a pass with no
 *      actuation performs zero keychain/CLI operations" invariant, extended to the whole
 *      dark state).
 *   2. DRY-RUN actuates the DECISION but not the WRITE. The executor itself enforces
 *      dryRun (Step 5), so a dry-run pass audits what it WOULD swap and advances cooldown
 *      state so the simulated cadence is realistic, but moves no credential.
 *   3. The executor is the ONLY write path. This class never touches a keychain directly;
 *      it calls `deps.swap()` (a thin wrapper over the gated, oracle-verified, staged
 *      CredentialSwapExecutor.swap). A swap rejection/`ok:false` increments the breaker.
 *   4. EVERY pass is auditable. The decision, the actuation outcome, the breaker state,
 *      and the surfaced degraded/attention entries are all recorded in `status()`.
 *
 * Pure orchestration over injected deps → unit-testable without a keychain. The server
 * wiring (the setInterval pass) + the live `GET /credentials/rebalancer` status are B3b.
 */
import { type RebalancerPolicyConfig, type AccountState, type SlotState, type SwapDecision } from './CredentialRebalancerPolicy.js';
export interface RebalancerResolvedConfig {
    policy: RebalancerPolicyConfig;
    /** A slot's verify counts as recent within this window. */
    auditCadenceMs: number;
    /** The account that should serve `~/.claude` (objective-0), or null. */
    desiredDefaultAccountId: string | null;
    /** Ceiling for forced wall-overrides per rolling window. */
    maxForcedOverridesPerWindow: number;
    /** N consecutive FAILED swaps that open the P19 breaker. */
    breakerThreshold: number;
}
export interface RebalancerActuationResult {
    ok: boolean;
    detail?: string;
}
export interface CredentialRebalancerDeps {
    /** Live feature gate — read per tick so a restartless flip is honored. */
    isEnabled: () => boolean;
    /** Live dry-run gate. When true the executor audits but writes nothing. */
    isDryRun: () => boolean;
    /** Build the per-slot snapshot (ledger tenancy + verify/quarantine/activity). */
    listSlots: () => SlotState[];
    /** Build the per-account snapshot (quota + reset proximity from the poller). */
    listAccounts: () => AccountState[];
    /** Resolve the clamped config for this pass. */
    resolveConfig: () => RebalancerResolvedConfig;
    /** The ONLY write path: the gated, oracle-verified CredentialSwapExecutor.swap wrapper. */
    swap: (slotA: string, slotB: string) => Promise<RebalancerActuationResult>;
    /** Optional sinks. */
    emitAudit?: (record: PassAudit) => void;
    emitDegraded?: (message: string) => void;
    emitAttention?: (message: string) => void;
    now?: () => number;
}
export interface PassAudit {
    at: number;
    enabled: boolean;
    dryRun: boolean;
    decisions: SwapDecision[];
    actuated: Array<{
        decision: SwapDecision;
        result: RebalancerActuationResult;
    }>;
    degraded: string[];
    attention: string[];
    noActuationReason?: string;
    breakerOpen: boolean;
}
export interface RebalancerStatus {
    enabled: boolean;
    breaker: {
        open: boolean;
        consecutiveFailures: number;
        threshold: number;
    };
    lastPass: PassAudit | null;
    /** Count of pairs/tenants currently under cooldown (for the status surface). */
    cooldownPairs: number;
    cooldownTenants: number;
}
export declare class CredentialRebalancer {
    private readonly deps;
    private readonly now;
    private lastActuationByPair;
    private lastActuationByTenant;
    private forcedOverridesInWindow;
    private forcedWindowStart;
    private consecutiveFailures;
    private breakerOpen;
    private lastPass;
    constructor(deps: CredentialRebalancerDeps);
    /**
     * Run one balancer pass. DARK ⇒ strict no-op. Returns the pass audit.
     * The breaker, when open, still runs the pass (to re-probe) but the spec retries on a
     * quota-fresh pass — here we simply attempt and let a continued failure keep it open;
     * a success resets it.
     */
    tick(): Promise<PassAudit>;
    /** Record cooldown timestamps for the tenants the decision exchanged. */
    private recordActuation;
    status(): RebalancerStatus;
    private safeBreakerThreshold;
}
//# sourceMappingURL=CredentialRebalancer.d.ts.map