/**
 * ADR-111 Stage-1 Extract — the live CLI `ReviewThreadSource` adapter (slice 5a,
 * mmnto-ai/totem#2201).
 *
 * Core defines the `ReviewThreadSource` port and stays network-free + LLM-free +
 * deterministic; THIS file is the IO-at-the-edge implementation that wraps the
 * GitHub GraphQL `reviewThreads` API behind that port. It fetches a PR's merge
 * commit + its review threads (each carrying `isResolved` / `isOutdated`), maps
 * them to the provider-neutral `ReviewThreadContent`, and SURFACES the per-thread
 * resolution flags to core. It does NOT filter resolved/outdated threads — the
 * contract-owner ruling is "surface, don't filter": core decides eligibility and
 * drop-ledgers every rejection (§8 "every rejection ledgered"). A server-side or
 * client-side `isResolved:false` pre-filter is FORBIDDEN here.
 *
 * Error contract (the discriminated `FetchResult`, §6): a per-PR failure is NEVER
 * thrown — the orchestrator iterates the whole train slice and must not abort on
 * one bad PR. Network / not-found → `{kind:'unreachable'}`; a malformed or
 * unmappable payload → `{kind:'unparseable'}`. The mining run continues; the loud
 * drop is core's job.
 *
 * Reuses the established `gh` CLI exec pattern (Tenet-21 — no bespoke GraphQL
 * client). The `exec` seam is injectable so the query-spy + mapping tests run
 * fully offline (no network, CI-locked).
 */
import { z } from 'zod';
import type { AuthorKind, FetchResult, ReviewThread, ReviewThreadComment, ReviewThreadSource } from '@mmnto/totem';
declare const GqlReviewThreadSchema: z.ZodObject<{
    isResolved: z.ZodBoolean;
    isOutdated: z.ZodBoolean;
    path: z.ZodString;
    comments: z.ZodObject<{
        pageInfo: z.ZodObject<{
            hasNextPage: z.ZodBoolean;
        }, "strip", z.ZodTypeAny, {
            hasNextPage: boolean;
        }, {
            hasNextPage: boolean;
        }>;
        nodes: z.ZodArray<z.ZodObject<{
            author: z.ZodNullable<z.ZodObject<{
                login: z.ZodString;
            }, "strip", z.ZodTypeAny, {
                login: string;
            }, {
                login: string;
            }>>;
            body: z.ZodString;
        }, "strip", z.ZodTypeAny, {
            body: string;
            author: {
                login: string;
            } | null;
        }, {
            body: string;
            author: {
                login: string;
            } | null;
        }>, "many">;
    }, "strip", z.ZodTypeAny, {
        pageInfo: {
            hasNextPage: boolean;
        };
        nodes: {
            body: string;
            author: {
                login: string;
            } | null;
        }[];
    }, {
        pageInfo: {
            hasNextPage: boolean;
        };
        nodes: {
            body: string;
            author: {
                login: string;
            } | null;
        }[];
    }>;
}, "strip", z.ZodTypeAny, {
    path: string;
    comments: {
        pageInfo: {
            hasNextPage: boolean;
        };
        nodes: {
            body: string;
            author: {
                login: string;
            } | null;
        }[];
    };
    isResolved: boolean;
    isOutdated: boolean;
}, {
    path: string;
    comments: {
        pageInfo: {
            hasNextPage: boolean;
        };
        nodes: {
            body: string;
            author: {
                login: string;
            } | null;
        }[];
    };
    isResolved: boolean;
    isOutdated: boolean;
}>;
/**
 * Build the `reviewThreads` GraphQL query. It REQUESTS `isResolved` and
 * `isOutdated` per thread (so core HAS the signal to decide on) — it deliberately
 * does NOT add an `isResolved: false` server-side filter (the "surface, don't
 * filter" ruling; the query-spy test asserts both halves). Exported for the
 * query-spy test.
 */
export declare function buildReviewThreadsQuery(owner: string, name: string, pr: number): string;
/**
 * The injectable command-exec seam. Matches the relevant part of `safeExec`'s
 * signature; the tests pass a fake that intercepts the outgoing GraphQL query
 * (no network). When not injected, the adapter lazy-loads `safeExec` (below).
 */
export type GhExec = (command: string, args: string[]) => string;
/**
 * The two CORE-homed comment classifiers `mapThreads` stamps onto each comment
 * (slice β). Injected — the LOGIC + version live in core (`classifyAuthorKind`,
 * `normalizeReviewChrome`; panel OQ-β1/β3), the adapter is only the mapping-
 * boundary call site, so the inputKey can never drift from the provenance. The
 * real `fetch` resolves these by lazy-loading core (the barrel is loaded anyway
 * for `safeExec`); tests pass the real core fns directly.
 */
export interface CommentEnrichers {
    classifyAuthorKind: (author: string) => AuthorKind;
    normalizeReviewChrome: (body: string) => string;
}
/**
 * Slice-β comment enrichment (the SHARED single home): stamp `authorKind` (via core
 * `classifyAuthorKind`) + `normalizedBody` (the de-chromed text the extractor
 * consumes — `normalizeReviewChrome(body)` for a recognized review bot, else the raw
 * body verbatim; chrome is stripped ONLY for bot comments so human prose is never
 * altered). BOTH the live `mapThreads` AND the replay-time `frozenSourceFrom` loader
 * (`spine-cert-run-corpus`) call THIS, so the de-chromed body they feed the
 * extractor — hence the `extractorInputKey` — can never diverge between record and
 * replay (a divergence would be indistinguishable from model drift).
 */
export declare function enrichComment(enrich: CommentEnrichers, author: string, body: string): ReviewThreadComment;
/**
 * Map a validated GraphQL response to the surviving `ReviewThread[]` with their
 * resolution flags surfaced and each comment slice-β-ENRICHED (see `enrichComment`).
 * Pure given `enrich` — exported for the mapping test. A null author (deleted/ghost)
 * coerces to '' (`reviewBotIdentity('')` is false → `authorKind: 'human'`, and
 * `isBotIdentity('')` is false, so core's count keeps it; the body still gates
 * inclusion).
 */
export declare function mapThreads(nodes: z.infer<typeof GqlReviewThreadSchema>[], enrich: CommentEnrichers): ReviewThread[];
export interface ReviewThreadSourceAdapterOptions {
    /** The repo to query, as `owner` + `name`. */
    owner: string;
    name: string;
    /** Working directory for the `gh` invocation (real adapter). */
    cwd?: string;
    /** Injectable exec seam (tests). Defaults to a `gh`-backed `safeExec`. */
    exec?: GhExec;
    /**
     * Injectable slice-β comment enrichers (tests). Defaults to a lazy-load of core's
     * `classifyAuthorKind` + `normalizeReviewChrome` (the barrel is loaded anyway for
     * `safeExec`). Tests pass the real core fns so the mapping is exercised end-to-end.
     */
    enrich?: CommentEnrichers;
}
/**
 * The live `ReviewThreadSource`. Constructible + exported now (slice 5a); wiring
 * it into the spine orchestrator's `run` command is slice 5c.
 */
export declare class ReviewThreadSourceAdapter implements ReviewThreadSource {
    private readonly owner;
    private readonly name;
    private readonly cwd;
    /** Injected exec seam (tests); when absent the default is lazy-loaded once via `execPromise`. */
    private readonly injectedExec;
    /**
     * Memoized lazy-load of the default `gh`-backed exec (real runs). Memoizes the
     * PROMISE, not the resolved value, so concurrent `fetch()` calls on one adapter
     * instance share a single `loadDefaultExec` instead of racing a `!this.exec`
     * guard (CR #2207 — was benign since dynamic import is idempotent, now explicit).
     */
    private execPromise;
    /** Injected slice-β enrichers (tests); when absent the default lazy-loads core once via `enrichPromise`. */
    private readonly injectedEnrich;
    /** Memoized lazy-load of core's `classifyAuthorKind` + `normalizeReviewChrome` (real runs) — see `execPromise`. */
    private enrichPromise;
    constructor(opts: ReviewThreadSourceAdapterOptions);
    /** Resolve the exec seam: the injected one (tests), else the memoized lazy-loaded default. */
    private resolveExec;
    /** Resolve the slice-β enrichers: the injected ones (tests), else a memoized lazy-load of core. */
    private resolveEnrich;
    fetch(pr: number): Promise<FetchResult>;
}
export {};
//# sourceMappingURL=spine-review-thread-source.d.ts.map