/**
 * Hold the gates declaration against the ruleset that enforces it.
 *
 * Three surfaces answer "what must pass before this merges" — the project's
 * `gates` declarations, the shipped ruleset template, and the live GitHub
 * ruleset — and until this module existed no comparator joined the first to
 * either of the others. `contextsFor()` derived contexts from declarations,
 * `lisa health` compared template against live, and the two halves never met,
 * so a declaration and the protection enforcing it could disagree indefinitely
 * with every check green.
 *
 * Two properties are load-bearing, and both are structural rather than
 * conventional:
 *
 * 1. **`off` and undeclared are different findings.** `off` is a decision that
 *    a property is not proved here, so protection requiring it is a live
 *    contradiction. Undeclared is silence — the registry still has gates whose
 *    jobs run with no declaration at all, so silence must never be read as
 *    "not required", and must never justify removing live protection.
 * 2. **Agreement is not proof.** A `matched` verdict means the declaration and
 *    the ruleset name the same required context. It says nothing about whether
 *    that context proves anything: one required context in this repository
 *    ships a skip step printing "This job going green does NOT mean any
 *    ast-grep rule has test coverage", and a required review context has gone
 *    green carrying "Review skipped". A drift report that could not tell
 *    "agreed and proved" from "agreed and unproved" would itself be a control
 *    reporting more than it measured, so the word this module uses is
 *    `matched` — the two surfaces agree — and never `enforced` or `proved`.
 * 3. **No remedy in this module can say "remove the context".** Third-party
 *    contexts are enforced by construction and declared by nobody; a
 *    comparator whose vocabulary contained a removal would eventually propose
 *    deleting one. The `DriftRemedy` union simply has no such member, so the
 *    guarantee holds for every caller rather than for the careful ones.
 * @module core/gate-declaration-drift
 */
/**
 * What the settings file says about one gate at one moment.
 *
 * `off` and `not-declared` are deliberately separate. Collapsing them is what
 * let a declaration govern nothing: the CI façade read both as
 * `configured=false` and ran its built-in fallback, so `off` could not turn a
 * job off.
 */
export type DeclarationState = "required" | "optional" | "off" | "not-declared";
/** Which enforcing surface a comparison was made against. */
export type DriftSurface = "ruleset-templates" | "live-ruleset";
/**
 * One context's verdict, in the vocabulary an operator can act on.
 *
 * Six members rather than a matched/unmatched pair, because the four unmatched
 * cases need four different actions and one of them is not a defect at all.
 */
export type DriftVerdict = 
/** Declared `required`, and the enforcing surface requires it. */
"matched"
/** Declared `required`; nothing on the enforcing surface requires it. */
 | "declared-not-enforced"
/** Enforced, while the declaration says `optional`. */
 | "enforced-declared-optional"
/** Enforced, while the declaration says `off` — a live contradiction. */
 | "enforced-declared-off"
/** Enforced, and a registry gate produces it, but nothing declares it. */
 | "enforced-undeclared"
/** Enforced, and no registry gate produces it — a third-party check. */
 | "enforced-not-lisa-owned";
/**
 * What to do about one verdict.
 *
 * Deliberately closed, and deliberately without a removal. See the module note.
 */
export type DriftRemedy = "none" | "declare-the-gate" | "enforce-the-context" | "resolve-the-contradiction" | "decide-which-surface-wins";
/** One required status context, and where the requirement was read from. */
export interface EnforcedContext {
    /** The context string, exactly as the ruleset spells it. */
    readonly context: string;
    /** The ruleset requiring it. */
    readonly ruleset: string;
    /**
     * Where the requirement was read. A package-relative template path or a
     * settings-file key — never an absolute path, which differs per machine and
     * would break the byte-identical property the report is built on.
     */
    readonly source: string;
}
/** The gate a context belongs to, and what the settings file says about it. */
export interface ContextOwner {
    /** Registry gate id. */
    readonly gateId: string;
    /** The declaration at the merge moment. */
    readonly declaration: DeclarationState;
    /** Whether the registry permits declaring this gate at the merge moment. */
    readonly legalAtMerge: boolean;
}
/** One context, its verdict, and the evidence behind it. */
export interface DeclarationDriftEntry {
    /** The context string. */
    readonly context: string;
    /** The verdict. */
    readonly verdict: DriftVerdict;
    /** The action this verdict calls for. Never a removal. */
    readonly remedy: DriftRemedy;
    /** The registry gate producing this context, when one does. */
    readonly gateId: string | null;
    /** What the settings file says, or null when no gate owns the context. */
    readonly declaration: DeclarationState | null;
    /** The rulesets requiring it, sorted. Empty when nothing enforces it. */
    readonly rulesets: readonly string[];
    /** Where the requirement was read from, sorted. */
    readonly sources: readonly string[];
    /** One operator-readable sentence. */
    readonly detail: string;
}
/** The whole comparison against one enforcing surface. */
export interface DeclarationDriftReport {
    /** Which surface the declarations were held against. */
    readonly surface: DriftSurface;
    /** One entry per context, sorted by context. */
    readonly entries: readonly DeclarationDriftEntry[];
    /** How many entries carry each verdict. Every key is present. */
    readonly counts: Readonly<Record<DriftVerdict, number>>;
    /** Entries whose verdict is a contradiction rather than a gap. */
    readonly contradictions: number;
    /** Entries whose verdict is a gap the declaration surface could close. */
    readonly gaps: number;
}
/** The slice of the shipped registry this comparison reads. */
export interface MergeContextRegistry {
    readonly REGISTRY: Readonly<Record<string, {
        readonly label: string;
        readonly moments: readonly string[];
    }>>;
    readonly resolveMoment: (options: {
        gates: Record<string, unknown>;
        moment: string;
        includeOff?: boolean;
    }) => readonly {
        readonly id: string;
        readonly level: string;
        readonly mode: string;
        readonly awaits: string | null;
    }[];
    readonly momentFamily: (moment: string) => string;
}
/** The moment a branch ruleset guards. */
export declare const MERGE_MOMENT = "pull-request";
/**
 * Map every context a registry gate can produce to that gate.
 *
 * Built from the whole registry rather than from the declared gates, which is
 * the difference between "protection requires a context whose gate nobody
 * declared" and "protection requires a context Lisa knows nothing about". Those
 * are different findings with different remedies, and a map built only from
 * declarations could not tell them apart.
 * @param options - Inputs
 * @param options.registry - The shipped registry
 * @param options.gates - The project's gates block
 * @param options.workflowName - The workflow whose name prefixes a context
 * @returns Context string to owning gate
 */
export declare function contextOwners(options: {
    registry: MergeContextRegistry;
    gates: Record<string, unknown>;
    workflowName: string;
}): ReadonlyMap<string, ContextOwner>;
/**
 * The contexts a `required` declaration promises protection will require.
 * @param owners - Context to owning gate
 * @returns The contexts, sorted
 */
export declare function declaredRequiredContexts(owners: ReadonlyMap<string, ContextOwner>): readonly string[];
/**
 * Compare a project's declarations with one enforcing surface.
 *
 * Total and pure — the same inputs always produce the same entries in the same
 * order. Reaching the surface is the caller's job, and a caller that could not
 * reach it must report an unknown rather than call this with an empty list: a
 * comparison against nothing would report every declaration as unenforced,
 * which is a different and false claim.
 * @param options - Inputs
 * @param options.surface - Which surface `enforced` was read from
 * @param options.owners - Context to owning gate
 * @param options.enforced - Contexts the surface requires
 * @returns The comparison
 */
export declare function classifyDeclarationDrift(options: {
    surface: DriftSurface;
    owners: ReadonlyMap<string, ContextOwner>;
    enforced: readonly EnforcedContext[];
}): DeclarationDriftReport;
//# sourceMappingURL=gate-declaration-drift.d.ts.map