/**
 * cutoverReadiness.ts — the cutover-READINESS checker (coordination-mandate spec §7
 * G2.4, scoped by decision 1A): everything UP TO the door, never the door.
 *
 * The Phase-4 cutover flip is Justin's one manual click — NOT an autonomously
 * fireable action (decision 1A/3B). What CAN and SHOULD be autonomous is knowing,
 * from REAL durable state, whether the flip is safe: this module composes the two
 * objective conditions the spec names —
 *
 *   - `integrity-gate-pass`     ← the persisted Phase-4 import IntegrityReport
 *                                 (`runIntegrityGate(...).passed`, written server-side
 *                                 by the import tooling — never asserted by an agent)
 *   - `parity-zero-divergence`  ← `DurableParityMonitor.gate(now).cleared` over the
 *                                 durable zero-divergence window, PLUS a readiness-layer
 *                                 freshness bound (a streak that stopped being fed is
 *                                 visible staleness, not silent readiness)
 *
 * — and exposes them as one read-only status. The T7 discipline (conditions resolve
 * from real state, never an agent's assertion) is structural here: the ONLY write
 * paths are (a) `runParityPass()`, which the agent may TRIGGER but whose result is
 * computed server-side from a live fetch+compare, and (b) `recordIntegrityReport()`,
 * called by the server-side import tooling. There is no "set the condition" input.
 */
import type { DurableParityMonitor } from './monitor/parityMonitorStore.js';
import type { CutoverGateStatus } from './monitor/parityMonitor.js';
import type { IntegrityReport } from './migration/importIntegrity.js';
import type { ImportRunResult } from './migration/importRunner.js';
import type { ParityResult } from './processor/parity.js';
export interface IntegrityStatus {
    /** Has an import integrity report been recorded at all? */
    ran: boolean;
    /** The report's single gate boolean (false when never ran — deny-safe). */
    passed: boolean;
    generatedAt: string | null;
    /** Compact counts for display; null when never ran. */
    summary: {
        fingerprintCollisions: number;
        schemaDivergences: number;
        checksumMismatches: number;
        danglingRefs: number;
    } | null;
}
export interface ParityReadiness extends CutoverGateStatus {
    /** ISO of the most recent recorded pass (clean or divergent), or null. */
    lastPassAt: string | null;
    /** True when the last pass is older than `maxPassStalenessMs` — a cleared-but-stale
     *  window is NOT readiness (the gate has no max-staleness by design; this layer adds it). */
    stale: boolean;
}
/** The last import DRY-RUN's verdict — informational only, NEVER a `ready` input. */
export interface ImportDryRunStatus {
    ran: boolean;
    passed: boolean;
    generatedAt: string | null;
    /** What the rehearsal imported (in-memory; zero durable writes). Null when never ran. */
    imported: {
        clusters: number;
        feedback: number;
    } | null;
    /** Non-null when the rehearsal aborted pre-import (fingerprint collisions to resolve). */
    abortedPreImport: string | null;
}
export interface CutoverReadinessStatus {
    /** True only when integrity passed AND the parity window is cleared AND fresh.
     *  This is the everything-up-to-the-door signal — it never fires anything. */
    ready: boolean;
    /** Decision 1A, restated machine-readably so no consumer can misread the scope. */
    door: 'manual-operator-click';
    integrity: IntegrityStatus;
    parity: ParityReadiness;
    /**
     * The import REHEARSAL (dry-run against an in-memory target on live source data).
     * Readiness honesty: this is visibility into whether the import pipeline is
     * proven — it does NOT contribute to `ready`. Only the REAL import's persisted
     * IntegrityReport (the `integrity` leg) can.
     */
    importDryRun: ImportDryRunStatus;
}
export type ParityPassOutcome = {
    ok: true;
    pass: {
        at: string;
        clustersCompared: number;
        divergences: number;
        divergent: boolean;
    };
    gate: CutoverGateStatus;
} | {
    ok: false;
    reason: string;
};
export type ImportDryRunOutcome = {
    ok: true;
    result: ImportRunResult;
    generatedAt: string;
} | {
    ok: false;
    reason: string;
};
/**
 * Outcome of the REAL pre-click integrity pass. Unlike the dry-run, a passing (or
 * failing) report is recorded to the CANONICAL integrity path via recordIntegrityReport
 * — so this outcome IS load-bearing on `ready`. `recorded` says whether a report landed
 * (false only on a pre-import abort, where there is no report to record).
 */
export type ImportIntegrityPassOutcome = {
    ok: true;
    result: ImportRunResult;
    generatedAt: string;
    recorded: boolean;
} | {
    ok: false;
    reason: string;
};
export interface CutoverReadinessDeps {
    parityMonitor: DurableParityMonitor;
    /** Where the import tooling's IntegrityReport persists (JSON envelope). */
    integrityReportPath: string;
    /** SERVER-SIDE parity check (live fetch + compare). Null when no parity source is
     *  configured — runParityPass() then refuses, and the parity condition can only
     *  clear via passes recorded by other server-side paths. */
    runParityCheck: (() => Promise<ParityResult>) | null;
    /** Where the import DRY-RUN envelope persists. MUST differ from
     *  `integrityReportPath` — a rehearsal must never be able to green the canonical
     *  integrity condition (enforced in the constructor). Optional for callers that
     *  never wire the dry-run. */
    importDryRunReportPath?: string;
    /** SERVER-SIDE import rehearsal (live source fetch → in-memory AS-IS import →
     *  integrity gate over readback). Null/absent when no source is configured. */
    runImportDryRun?: (() => Promise<ImportRunResult>) | null;
    /** SERVER-SIDE REAL pre-click integrity pass (live source fetch → AS-IS import into
     *  a PERSISTED shadow → integrity gate over readback). Distinct from the dry-run:
     *  its passing report greens the canonical integrity condition via
     *  recordIntegrityReport. Run OFF the event loop (AgentServer spawns a child process)
     *  because the full-corpus fetch+import cannot settle in-process (#948). Null/absent
     *  when no source is configured. */
    runIntegrityImport?: (() => Promise<ImportRunResult>) | null;
    /** Freshness bound for the parity window (default 6h). */
    maxPassStalenessMs?: number;
    /**
     * Wall-clock max-hold for a single live fetch (parity pass OR import dry-run),
     * enforced at the single-flight-lock boundary (default 12m). This is a
     * defense-in-depth backstop ON TOP OF whatever internal timeout the injected
     * fetch carries: if that fetch ever fails to settle (e.g. an AbortSignal that
     * doesn't fire on a stalled socket, or a compare step with no timeout of its
     * own), the lock would otherwise be held forever and every subsequent pass +
     * dry-run is refused with "already in flight" — exactly the ~85-minute stuck
     * lock observed in #948. The max-hold guarantees the lock releases within a
     * bounded time so the next sweep can retry; it sits comfortably above
     * HttpParitySource's 600s total budget so it only fires when that inner budget
     * has genuinely failed, never on a legitimately-slow-but-working pass. */
    maxLiveFetchMs?: number;
    now?: () => number;
}
export declare class CutoverReadiness {
    private readonly d;
    private readonly maxStaleMs;
    private readonly maxLiveFetchMs;
    /**
     * Single-flight guard over the LIVE SOURCE FETCH — shared by the parity pass
     * and the import dry-run because both page the full live source. Without it,
     * repeated triggers pile CONCURRENT full fetches onto the source: the route's
     * response window can elapse while the handler keeps running (the always-logged
     * outcome contract), so a caller that retries on a 408 spawns a new fetch next
     * to the live one (observed live 2026-06-05 12:16Z — four concurrent passes
     * against an already-degraded Portal). One live fetch at a time; a concurrent
     * trigger is refused immediately (409 at the route) and records nothing.
     */
    private liveFetchInFlight;
    constructor(deps: CutoverReadinessDeps);
    private nowMs;
    /**
     * Race an injected live-fetch promise against a wall-clock max-hold so the
     * single-flight lock can NEVER be held indefinitely by a fetch that fails to
     * settle (#948). On timeout this rejects (the caller's `finally` then releases
     * the lock and the next sweep retries); the orphaned work is abandoned — a
     * re-attempt is cheap and idempotent, an unreleasable lock is not. Uses real
     * wall-clock time (NOT `nowMs()`), since the bug is a promise that never
     * settles in real time regardless of an injected clock. */
    private withMaxHold;
    /** Persist an import IntegrityReport (server-side import tooling only). */
    recordIntegrityReport(report: IntegrityReport, generatedAt?: string): void;
    integrityStatus(): IntegrityStatus;
    parityStatus(): ParityReadiness;
    /**
     * TRIGGER a server-side parity pass: live fetch + compare via the injected check,
     * record the result (clean OR divergent) into the durable window. The caller
     * contributes nothing to the result — only the request to run it (T7). A FAILED
     * check records nothing: a fetch error is absence of evidence, not evidence of
     * divergence — and it cannot extend the clean window either.
     */
    runParityPass(): Promise<ParityPassOutcome>;
    /**
     * TRIGGER a server-side import rehearsal (T7: the agent may ask; the server
     * computes — live source fetch, in-memory AS-IS import, integrity gate over the
     * readback). The envelope persists to the SEPARATE dry-run path; the canonical
     * integrity report is structurally out of reach of this method.
     */
    runImportDryRunPass(): Promise<ImportDryRunOutcome>;
    /**
     * TRIGGER the REAL pre-click integrity pass (T7: the agent may ask; the server
     * computes — live source fetch → AS-IS import into a PERSISTED shadow → integrity
     * gate over the readback). UNLIKE the dry-run, the resulting report is recorded to
     * the CANONICAL integrity path via recordIntegrityReport: a PASSING report greens
     * `ready`, a FAILING report flips it closed (the door always reflects the LATEST
     * real verdict — never a stale green). A pre-import abort (fingerprint collision →
     * null report) records nothing and leaves prior state; the abort is returned for the
     * operator to resolve. A FAILED fetch records nothing (absence of evidence, not a
     * verdict). Shares the single-flight live-fetch guard + the max-hold backstop.
     */
    runIntegrityPass(): Promise<ImportIntegrityPassOutcome>;
    importDryRunStatus(): ImportDryRunStatus;
    /** The everything-up-to-the-door readiness signal. Read-only; never fires anything. */
    status(): CutoverReadinessStatus;
}
//# sourceMappingURL=cutoverReadiness.d.ts.map