/**
 * M-phase rollout ratchet (Phase 16).
 *
 * Phase 15 shipped the rollout resolver. Phase 12 shipped the
 * observability surface that lets us measure conformance. This module
 * is the bridge: it takes a window of daily conformance numbers and
 * answers "given the current rollout phase, should we promote, hold,
 * or demote?". The answer is a recommendation, not an action. An
 * operator-driven follow-up wires the actual `OD_CRITIQUE_ROLLOUT_PHASE`
 * flip into a deploy pipeline; this module's job is to make the
 * recommendation explicit and auditable so the operator does not have
 * to read a Grafana panel to know what to do.
 *
 * The function is pure: it takes data in and returns a decision out,
 * with no I/O. That lets the same evaluator drive both the nightly
 * recommendation log line and the `GET /api/critique/conformance`
 * endpoint, and lets vitest pin every cell of the decision matrix
 * without standing up a daemon.
 *
 * Spec source: `specs/current/critique-theater.md` § Rollout, which
 * states the M3 threshold as "14 consecutive days at >= 90% adapter
 * conformance across the fleet". The clean-parse threshold (>= 95%
 * runs free of parser_warning) is a sibling gate; both must hold.
 */
import type { RolloutPhase } from './rollout.js';
/**
 * One day of conformance numbers for one adapter. The nightly cron
 * collapses every (adapter, brief-template) run into one of these
 * rows; the evaluator aggregates across adapters per day.
 */
export interface ConformanceDay {
    /** ISO date `YYYY-MM-DD`. */
    date: string;
    /** Adapter id (matches `agentId` in the spawn handler). */
    adapter: string;
    /** Fraction of runs that hit the `shipped` terminal status, 0..1. */
    shippedRate: number;
    /** Fraction of runs with zero parser_warning events, 0..1. */
    cleanParseRate: number;
    /** Total runs the day's two rates were computed from. */
    totalRuns: number;
}
export type RatchetDecision = {
    kind: 'hold';
    current: RolloutPhase;
    /** Human-readable reason the rollout is holding at `current`. */
    reason: string;
    /** Days in the rolling window that passed every threshold. */
    passingDays: number;
    /** Days in the rolling window that have at least one row. */
    observedDays: number;
} | {
    kind: 'promote';
    from: RolloutPhase;
    to: RolloutPhase;
    /** Days in the rolling window that passed every threshold. */
    evidenceDays: number;
} | {
    kind: 'demote';
    from: RolloutPhase;
    to: RolloutPhase;
    /** Human-readable reason the rollout is being walked back. */
    reason: string;
};
export interface EvaluateRolloutParams {
    current: RolloutPhase;
    /** Rolling window of daily conformance rows. Order does not matter. */
    history: ConformanceDay[];
    /** Window size in days; defaults to 14 (the spec value). */
    windowDays?: number;
    /** Minimum shipped-rate per day; defaults to 0.90 (the spec value). */
    shippedThreshold?: number;
    /** Minimum clean-parse-rate per day; defaults to 0.95 (the spec value). */
    cleanParseThreshold?: number;
    /**
     * Optional clock for deterministic tests. Production callers omit;
     * tests pass `() => new Date('2026-05-13T00:00:00Z')` or similar so
     * the "today" anchor is fixed.
     */
    now?: () => Date;
}
/**
 * Evaluate the rollout. Returns one of three decisions:
 *
 *   * `promote`: every day in the rolling window had at least one row
 *     and every per-day fleet aggregate cleared both thresholds.
 *   * `demote`: at least one day in the rolling window had a fleet
 *     shipped-rate below the demote-floor (half of `shippedThreshold`).
 *     The demote-floor is intentionally lower than the promote threshold
 *     so a single noisy day at, say, 0.88 does not bounce the rollout
 *     back; the demote signal is for sustained breakage.
 *   * `hold`: anything else (data gaps, near-misses, or M3 with no
 *     headroom).
 *
 * The function is total: it always returns a decision; M0 cannot demote
 * (already at the floor) and M3 cannot promote (already at the ceiling).
 * Both of those collapse to `hold` with a reason-string that explains
 * why.
 */
export declare function evaluateRollout(params: EvaluateRolloutParams): RatchetDecision;
