import { z } from 'zod';
import { type PrMeta, type PrSetDiff } from './selection-rule.js';
/**
 * ADR-111 §5 — the frozen split artifact, committed before extraction. The
 * concrete `cutIndex` is the deferred window-open scalar (Deferred Decisions);
 * the field is required in the committed artifact, only its value is deferred.
 */
export declare const SplitArtifactSchema: z.ZodEffects<z.ZodObject<{
    asOfCommit: z.ZodString;
    /** The OLDER ancestry segment — the mining slice. */
    trainPrs: z.ZodArray<z.ZodNumber, "many">;
    /** The NEWER held-out segment — control evaluation. Controls are tags within this. */
    heldOutPrs: z.ZodArray<z.ZodNumber, "many">;
    /** Explicitly enumerated drops (the atomic revert pairs). */
    excludedPrs: z.ZodArray<z.ZodNumber, "many">;
    /** Positive controls — a designated subset (tag) of `heldOutPrs`, never a separate cover bucket. */
    positiveControlPrs: z.ZodArray<z.ZodNumber, "many">;
    /** Negative controls — a designated subset (tag) of `heldOutPrs`. */
    negativeControlPrs: z.ZodArray<z.ZodNumber, "many">;
    splitRule: z.ZodObject<{
        /** Human-readable predicate expression that generated the corpus (mirrors the windtunnel lock's `selectionRule.predicate`). */
        predicate: z.ZodEffects<z.ZodString, string, string>;
        /**
         * Forward-chronological ancestry cut: `trainPrs` = the `cutIndex` OLDEST
         * corpus PRs (ancestry order), `heldOutPrs` = the newer remainder. Concrete
         * value deferred to window-open (ADR-111 Deferred Decisions).
         */
        cutIndex: z.ZodNumber;
    }, "strip", z.ZodTypeAny, {
        predicate: string;
        cutIndex: number;
    }, {
        predicate: string;
        cutIndex: number;
    }>;
    /**
     * ADR-112 §5.1 Slice D5 — the mechanical FREEZE INSTANT, stamped by the authored
     * producer at materialize. Full ISO-8601 (offset-bearing); the Q3 temporal gate
     * (`assertAuthoredFreezePreconditions`) asserts this precedes every effective
     * authoring-ledger `authoredAt` — §5.1's "frozen BEFORE authoring" embargo made
     * MECHANICAL, not attestational. Dereferenced from the split artifact (Tenet-20),
     * never recomputed. Additive-optional, no `.default()`: a mined/legacy split omits
     * it and parses/serializes BYTE-UNCHANGED.
     */
    frozenAt: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
    asOfCommit: string;
    trainPrs: number[];
    heldOutPrs: number[];
    excludedPrs: number[];
    positiveControlPrs: number[];
    negativeControlPrs: number[];
    splitRule: {
        predicate: string;
        cutIndex: number;
    };
    frozenAt?: string | undefined;
}, {
    asOfCommit: string;
    trainPrs: number[];
    heldOutPrs: number[];
    excludedPrs: number[];
    positiveControlPrs: number[];
    negativeControlPrs: number[];
    splitRule: {
        predicate: string;
        cutIndex: number;
    };
    frozenAt?: string | undefined;
}>, {
    asOfCommit: string;
    trainPrs: number[];
    heldOutPrs: number[];
    excludedPrs: number[];
    positiveControlPrs: number[];
    negativeControlPrs: number[];
    splitRule: {
        predicate: string;
        cutIndex: number;
    };
    frozenAt?: string | undefined;
}, {
    asOfCommit: string;
    trainPrs: number[];
    heldOutPrs: number[];
    excludedPrs: number[];
    positiveControlPrs: number[];
    negativeControlPrs: number[];
    splitRule: {
        predicate: string;
        cutIndex: number;
    };
    frozenAt?: string | undefined;
}>;
export type SplitArtifact = z.infer<typeof SplitArtifactSchema>;
/** Thrown when `resolveSplit` cannot produce a clean three-way disjoint cover (Tenet 4, fail-loud). */
export declare class SplitCoverError extends Error {
    readonly result: SplitCoverResult;
    constructor(result: SplitCoverResult);
}
/** Structured result of the §5/§8 split-ledger mechanical check. `ok` iff every diagnostic list is empty. */
export interface SplitCoverResult {
    ok: boolean;
    /**
     * Cover vs the frozen corpus, direction-disambiguated so each side falsifies
     * its own clause: `missing` = a corpus PR assigned to no slice (FM(g), silent
     * drop); `extra` = a slice PR outside the corpus (FM(d), out-of-corpus member).
     */
    cover: PrSetDiff;
    /** Pairwise PR# intersections of {train, heldOut, excluded} that must be empty (the `⊎` disjointness). */
    overlaps: {
        trainHeldOut: number[];
        trainExcluded: number[];
        heldOutExcluded: number[];
    };
    /** Control PRs not contained in `heldOutPrs` — controls must be tags WITHIN heldOut, not a separate bucket. */
    controlsOutsideHeldOut: number[];
    /** PRs tagged as BOTH a positive and a negative control — a PR cannot be both (per-rule control coherence). */
    controlOverlap: number[];
    /** Merge-commit SHAs appearing in more than one slice — disjoint by merge-commit, not only PR# (revert/target straddle guard). */
    mergeCommitCollisions: string[];
}
/**
 * The §8 split-ledger check: verifies the split is a three-way disjoint cover of
 * `corpus` (= `selectionRule(asOfCommit)`), that the controls are tags within
 * `heldOutPrs`, and that the slices are disjoint by merge-commit (not only PR#).
 * Pure; the harness asserts `ok` and reads the per-field diffs to pin the exact
 * Falsifying-Metric clause (FM(d) extra / FM(g) missing).
 */
export declare function validateSplitCover(split: SplitArtifact, corpus: number[], mergeCommitByPr: ReadonlyMap<number, string>): SplitCoverResult;
/**
 * Produce a frozen split from the resolved corpus by the forward-chronological
 * ancestry cut. `corpus` is `selectionRule(asOfCommit)` (the cover base);
 * `orderedNewestFirst` is the same ancestry enumeration `resolveSelectionRule`
 * consumes (`git log --topo-order`, newest-first) and MUST cover the corpus.
 * `excludedPrs` (the atomic revert pairs) are removed from train/heldOut but
 * remain in the cover. Validates the result and throws `SplitCoverError` on any
 * cover/disjointness violation (Tenet 4) — a malformed split never freezes.
 *
 * CONTRACT (corpus-scope model A, confirmed by totem-codex's #2200 review; pending
 * strategy-claude's final §5 word): `corpus` is the cover BASE that INCLUDES the
 * PRs later assigned to `excludedPrs` — i.e. `selectionRule` is resolved here with
 * reverts retained, and `excludedPrs` enumerates them explicitly, so
 * `train ⊎ heldOut ⊎ excluded == corpus` holds and every `excludedPr` is a corpus
 * member. (The alternative — `selectionRule(asOfCommit)` pre-excludes reverts yet
 * the split still records them in `excludedPrs` — is NOT supported by the current
 * schema, which rejects excluded PRs outside the corpus; revisit only if strategy
 * pins that model.) The fail-loud `excludedPrs ⊆ corpus` guard below enforces this.
 */
export declare function resolveSplit(params: {
    asOfCommit: string;
    corpus: number[];
    orderedNewestFirst: number[];
    excludedPrs: number[];
    cutIndex: number;
    positiveControlPrs?: number[];
    negativeControlPrs?: number[];
    predicate: string;
    mergeCommitByPr: ReadonlyMap<number, string>;
    /** ADR-112 §5.1 D5 — the mechanical freeze instant (full ISO-8601), stamped by the authored producer. Omitted ⇒ mined/legacy split (byte-unchanged). */
    frozenAt?: string;
}): SplitArtifact;
/** Re-export for callers building the `mergeCommitByPr` map from enumerated metas. */
export declare function mergeCommitMap(metas: PrMeta[]): Map<number, string>;
//# sourceMappingURL=split.d.ts.map