/**
 * MergeRunner — the act-path engine for the GreenPrAutoMerger (Step 4 of
 * green-pr-automerge-enforcement §3.1/R5). Owns everything that touches a real
 * process:
 *
 *  - probeContract(): runs `safe-merge --capabilities`, pins the script's
 *    content hash + absolute path for the attempt (round-3 mid-run-swap defense),
 *    and re-verifies that hash IMMEDIATELY before exec (round-5: the spawn runs
 *    the verified bytes).
 *  - run(): writes a DURABLE two-phase in-flight record BEFORE the spawn (intent
 *    first, pid/pgid patched in after), spawns safe-merge in its OWN process
 *    group, hard-kills the group at mergeTimeoutMs + grace, parses the classified
 *    `safe-merge-result:` line, and confirms a `merged` outcome INDEPENDENTLY
 *    (B10). The record is cleared on classification.
 *  - reapOrphan(): at boot/warm-up, reaps a surviving orphan from a crash —
 *    verifies the recorded pid/pgid identity via a unique attempt token before
 *    signalling (pid-reuse safe), handles the dead-leader/live-group corner
 *    (orphan-reap-incomplete), and re-verifies PR state via gh. A pid-less
 *    in-flight record is treated as attempt-of-unknown-outcome (re-verify,
 *    never assume not-spawned).
 *
 * The guarantee survives the server's own death: the watcher's merges trigger
 * releases → auto-update restarts, so a restart mid-attempt is a NORMAL
 * condition. The durable record + own-process-group + shutdown group-kill +
 * warm-up reap close the double-attempt / wedged-orphan classes.
 *
 * All process + fs + gh I/O is behind injected seams so the runner is testable
 * without spawning anything.
 *
 * RULE 3.1 RATIONALE
 *   Criticality: high — this code drives an autonomous merge to main.
 *   Frequency:   per-merge-attempt (at most one per ~10-minute tick).
 *   Stability:   stable — parses safe-merge's OWN structured contract line
 *                (`safe-merge-result: {...}`), not human gh output; and
 *                `gh ... --json` structured responses, never regex over prose.
 *   Fallback:    every parse failure is caught and resolves to a NON-merged
 *                outcome (fail-toward-skip); a `merged` outcome is never trusted
 *                without an INDEPENDENT `gh pr view` confirmation (B10).
 *   Verdict:     deterministic; the act-time authority is safe-merge, which
 *                re-verifies, so a misread here can only refuse, never over-merge.
 */
import type { MergeAttempt, MergeRunResult, MergeRunner } from './GreenPrAutoMerger.js';
/** A spawned child's observable result (seam over child_process). */
export interface SpawnOutcome {
    stdout: string;
    stderr: string;
    status: number | null;
    signal: string | null;
    /** Whether the deadline timer fired and the group was killed. */
    deadlineKilled: boolean;
    pid: number | null;
}
export interface SpawnArgs {
    command: string;
    args: string[];
    env: NodeJS.ProcessEnv;
    deadlineMs: number;
    /** Called once the child has a pid, so the runner can patch the durable record. */
    onPid?: (pid: number) => void;
}
export interface MergeRunnerConfig {
    stateDir: string;
    repo: string;
    /** Absolute path to scripts/safe-merge.mjs. */
    safeMergePath: string;
    mergeTimeoutMs: number;
    mergeKillGraceMs: number;
    expectedContractVersion: number;
    /**
     * Merge strategy (mergerunner-auto-arm-handoff). `auto` (default) arms GitHub
     * native auto-merge via `safe-merge … --auto` and returns in seconds; `admin`
     * is the legacy synchronous poll+merge via `--admin`. Absent → 'auto'.
     */
    mergeStrategy?: 'auto' | 'admin';
    /**
     * The `--auto` spawn deadline (default ~60s). Arming is a single API call, so
     * the 25-min `mergeTimeoutMs` is vestigial on the auto path — a hung arm
     * should trip in minutes, not ~26 min. The admin path keeps `mergeTimeoutMs`.
     */
    armTimeoutMs?: number;
    /** node executable (default process.execPath). */
    nodePath?: string;
}
export interface MergeRunnerDeps {
    /** Spawn a child in its OWN process group; resolve when it exits or is killed. */
    spawn?: (a: SpawnArgs) => Promise<SpawnOutcome>;
    /** Independent merge confirmation (gh pr view --json state,mergedAt). */
    confirmMerged: (pr: number, repo: string) => Promise<boolean>;
    /** Re-verify a PR's live state (for orphan reap). 'MERGED'|'CLOSED'|'OPEN'|'UNKNOWN'. */
    prState: (pr: number, repo: string) => Promise<string>;
    /** Is a process group still alive? (process.kill(pgid, 0) wrapper.) */
    isAlive?: (pid: number) => boolean;
    /** Kill a process group (SIGKILL the negative pid). */
    killGroup?: (pgid: number) => void;
    now?: () => number;
    logger?: (msg: string) => void;
}
/** The durable in-flight record (state/green-pr-automerge-inflight.json). */
export interface InFlightRecord {
    pr: number;
    headRefOid: string;
    repo: string;
    attemptToken: string;
    startedAt: number;
    /** Patched in AFTER the spawn (phase 2). Null until then. */
    pid: number | null;
    pgid: number | null;
}
export declare class DefaultMergeRunner implements MergeRunner {
    private readonly cfg;
    private readonly deps;
    private readonly now;
    /** Pinned per-attempt: the probed script's hash + path. */
    private pinnedHash;
    constructor(cfg: MergeRunnerConfig, deps: MergeRunnerDeps);
    /**
     * The resolved merge strategy the runner will spawn with (mergerunner-auto-arm-
     * handoff M2). Read-only — exposed so the wiring-integrity test can prove the
     * config survived `config → GreenPrAutoMergerConfig → buildGreenPrDeps →
     * MergeRunnerConfig` and the runner is no longer hardcoded to `--admin`.
     */
    get resolvedStrategy(): 'auto' | 'admin';
    /** The resolved `--auto` spawn deadline (M2 wiring-integrity surface). */
    get resolvedArmTimeoutMs(): number;
    probeContract(): Promise<{
        ok: boolean;
        version?: number;
    }>;
    private hashScript;
    run(attempt: MergeAttempt): Promise<MergeRunResult>;
    reapOrphan(): Promise<{
        reaped: boolean;
        outcome?: string;
    }>;
    private reverifyAndClear;
    /** Best-effort identity check: does the live pid's argv carry our token? */
    private verifyAttemptToken;
    private inFlightPath;
    private writeInFlight;
    private readInFlight;
    private clearInFlight;
    private spawn;
    private log;
}
/** Parse safe-merge's classified `safe-merge-result: {...}` line from stdout. */
export declare function parseResultLine(stdout: string): string | null;
//# sourceMappingURL=MergeRunner.d.ts.map