/**
 * Frozen code-path classifier. A changed file is code-touching iff it matches
 * ≥1 `includeGlobs` AND no `excludeGlobs` — exclude wins at the FILE level.
 */
export interface CodePathClassifier {
    includeGlobs: string[];
    excludeGlobs: string[];
}
/** Frozen selectionRule config, read from `lock.corpus.selectionRule`. */
export interface SelectionRuleConfig {
    /** Required at certifying resolve (caller hard-errors if absent). */
    codePathClassifier: CodePathClassifier;
    /** Exclude a revert PR AND its reverted target. Manifest flag, default true. */
    excludeRevertPairs: boolean;
    /** Exclude `[bot]`-authored PRs. Manifest flag, default true. */
    excludeBotPrs: boolean;
    /**
     * Corpus window. `all` = every qualifying PR reachable from `asOfCommit`;
     * `bounded` = the `n` MOST-RECENT qualifying PRs. "Most recent" is by ANCESTRY
     * (topological) order, NOT commit-date (ADR-110 §6 ancestry-not-timestamp): the
     * caller enumerates `metas` newest-first via `git log --topo-order`, and this
     * function takes the first `n` qualifying — see `resolveSelectionRule`.
     */
    window: {
        type: 'all';
    } | {
        type: 'bounded';
        n: number;
    };
}
/** Git-derived facts about one merged (squash) PR reachable from `asOfCommit`. */
export interface PrMeta {
    pr: number;
    /** The squash-merge commit SHA (lowercase 40-hex). */
    mergeCommit: string;
    /** Author identity (name/email) used for bot detection. */
    author: string;
    /** True iff `author` ends with `[bot]` (case-insensitive). */
    isBotAuthor: boolean;
    /** If this PR is a revert, the target SHA from `This reverts commit <sha>`. */
    revertsSha?: string;
    /** Changed file paths, forward-slash normalized. */
    changedFiles: string[];
}
/** Symmetric difference between the expected (git) and actual (manifest) PR sets. */
export interface PrSetDiff {
    /** In git, absent from the manifest. */
    missing: number[];
    /** In the manifest, absent from git. */
    extra: number[];
}
/** Thrown when a merge subject carries a TRAILING `(#…)` that is not a valid PR ref. */
export declare class SelectionRuleParseError extends Error {
    readonly subject: string;
    constructor(subject: string);
}
/**
 * A PR is code-touching iff at least one changed file matches ≥1 includeGlob and
 * no excludeGlob. Exclude wins at the FILE level (not the PR level), so a PR
 * with a mix of code + doc files is still code-touching, while a docs/config/
 * generated-only PR is excluded because no file survives the classifier.
 */
export declare function isCodeTouching(changedFiles: string[], classifier: CodePathClassifier): boolean;
/**
 * Bot iff the author identity is a GitHub bot. `author` is git's `%an <%ae>` —
 * e.g. `dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>` — so
 * checking the whole string's suffix would fail (it ends with `>`). Strip the
 * trailing ` <email>` and test the display NAME's `[bot]` suffix, and also match
 * a `[bot]@` noreply email; robust to the name-only or name+email forms.
 */
export declare function isBotIdentity(author: string): boolean;
/**
 * Recognize an ALLOWLISTED review-finding bot (gemini-code-assist / coderabbitai),
 * with or without a `[bot]` suffix and with or without a trailing ` <email>`.
 *
 * DELIBERATELY SEPARATE from `isBotIdentity` (panel OQ-β1): `isBotIdentity` gates
 * CORPUS MEMBERSHIP (it excludes `[bot]`-AUTHORED PRs) and stays unchanged —
 * coupling review-comment substrate to commit-author exclusion would be the wrong
 * dependency. This predicate is a NEW review-comment classifier used ONLY for
 * `authorKind` tagging, the substantive-comment count, and chrome normalization.
 *
 * ALLOWLIST not denylist (panel OQ-β2): it fails CONSERVATIVE — a not-yet-listed
 * review tool undercounts (drops to "no substrate") rather than laundering every
 * future automation account in as a substantive reviewer. `isBotIdentity('gemini-
 * code-assist')` stays `false` (no `[bot]` suffix); `reviewBotIdentity` returns
 * `true` for it; `reviewBotIdentity('dependabot[bot]')` returns `false`.
 */
export declare function reviewBotIdentity(author: string): boolean;
/** Extract the reverted target SHA from a `This reverts commit <sha>` body line. */
export declare function parseRevertSha(commitBody: string): string | undefined;
/**
 * Extract the PR number from a squash-merge subject's TRAILING `(#N)`.
 * - No trailing `(#…)` at all → `null` (a direct-to-main non-PR commit; skip).
 * - Trailing `(#N)` with a positive integer → that number. Anchored to the END,
 *   so `…spec (#533) (#534)` → 534 and `…(closes #522) (#524)` → 524 (earlier
 *   refs are issue refs, not the PR).
 * - Trailing parenthesized ref that is NOT a positive integer (`(#abc)`, `(#)`,
 *   `(#0)`, `(#-1)`) → throws `SelectionRuleParseError` (malformed, never silent).
 */
export declare function parsePrNumber(subject: string): number | null;
/**
 * Per-PR predicate (pure, single-PrMeta): code-touching AND (not bot, when
 * excluded) AND (not itself a revert, when excluded). The reverted-TARGET
 * exclusion is NOT here — it needs cross-candidate context and lives in the
 * second pass of `resolveSelectionRule`.
 */
export declare function selectionRulePredicate(meta: PrMeta, config: SelectionRuleConfig): boolean;
/**
 * Resolve the corpus PR set from enumerated PrMetas (already filtered to
 * ancestors of `asOfCommit` by the caller). Two-pass when excluding revert
 * pairs: (1) collect reverted-target SHAs; (2) apply the per-PR predicate, then
 * drop any candidate whose `mergeCommit` is a reverted target. Fail-safe: a
 * target SHA that maps to no in-window candidate (direct-to-main / out-of-
 * ancestry) drops nothing — only the revert PR itself is excluded (by the
 * predicate). Returns sorted unique PR numbers.
 */
export declare function resolveSelectionRule(metas: PrMeta[], config: SelectionRuleConfig): number[];
/** Symmetric difference; both sides normalized to unique sets, order-invariant. */
export declare function diffPrSets(expected: number[], actual: number[]): PrSetDiff;
/** Order- and duplicate-invariant set-membership equality (both sides deduped). */
export declare function prSetsEqual(expected: number[], actual: number[]): boolean;
//# sourceMappingURL=selection-rule.d.ts.map