/** * 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 `. */ 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 ` ` 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 ` `. * * 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 ` 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