import type { RunnerSelfSeat } from "../core/runner/contracts.js";
import type { TaskSpec } from "../core/types.js";
import type { ExecutionEnv } from "../internal/harness-types.js";
import type { VerificationResult } from "./verify.js";
import type { Checkpoint } from "../core/checkpoint-store.js";
import { type OracleIsolationVerdict } from "../core/oracle-isolation.js";
/**
 * SAFE-tier oracle self-repair loop (design/76 D1 命门, design/78 Slice-1). Given an implementation spec
 * and an **injected, trusted oracle**, this runs the worker, grades it with the oracle, and — on failure —
 * loops a fix turn (and a bounded clean-restart) until the oracle passes or the loop gives up. The terminal
 * is then projected through the {@link terminalForTier} table, which **caps every PASS at `candidate_only`**:
 * this layer NEVER auto-accepts. Auto-accept (`fixed`) requires the OFF Gate-1 out-of-process
 * `oracleIsolation` boundary that this slice deliberately does not build — SAFE-tier escalates to a human
 * (`needs_human_oracle`) or surfaces a candidate, but never clears its own work.
 *
 * Like {@link verifyCompleted}, this is a **thin composition** over `runner.runTask` (verify.ts's DEPLOYMENT POSTURE section)
 * — it adds no Runner-core surface, touches no vendored code, and is called by a leader/profile (the sibling
 * of {@link runWithVerification}). The oracle internals (which gate/judge/property-harness to compose) live
 * in the PROFILE-injected {@link RepairOracle} closure; core only fixes the {@link OracleResult} shape and the
 * read-only / identity contract (§1 裁决①). N-parallel candidate fan-out + a robustness-first selection ranker
 * are PROFILE concerns (§3 裁决②) — core stays single-candidate.
 */
/**
 * The provenance/strength tier of the oracle that produced a verdict. The control plane assigns this — it is
 * NEVER derived from agent-visible state (not a {@link TaskSpec} field), mirroring design/44 §7 Q4. Drives the
 * {@link terminalForTier} projection.
 * - `trusted_hidden` — a hidden, spec-derived held-out oracle (strongest; still candidate_only here).
 * - `trusted_visible` — a visible trusted oracle (must be paired with an anchor + no property-harness regression + L3).
 * - `property_harness_weak` — derived property invariants only (a weak signal).
 * - `l3_judge_advisory` — an L3 read-only judge that advises but does not clear.
 * - `none` — no oracle available → only a human can adjudicate.
 */
export type OracleTier = "trusted_hidden" | "trusted_visible" | "property_harness_weak" | "l3_judge_advisory" | "none";
/**
 * The terminal state the repair loop resolves to. SAFE-tier (this slice) can reach every value EXCEPT `fixed`:
 * `fixed` is reserved for auto-accept, which needs the OFF Gate-1 out-of-process oracleIsolation boundary and
 * is therefore unreachable here (terminal-by-tier §2 caps PASS at `candidate_only`).
 * - `fixed` — auto-accepted (NOT reachable SAFE-tier; reserved for a future Gate-1 slice).
 * - `candidate_only` — the oracle passed; surface the candidate for human acceptance, do not auto-accept.
 * - `needs_human_oracle` — no usable oracle (`tier: "none"`) → only a human can adjudicate.
 * - `gave_up` — attempts exhausted without a passing oracle (a first-class abstain, never "satisfy the test").
 * - `conflict` — the spec and the oracle disagree (escalate; never rewrite the work to satisfy the test).
 *   Slice-1 core does NOT synthesize this (the test-tampering diff-monitor is §6-deferred to the profile —
 *   core honestly `gave_up`s rather than under-detect a conflict); it is in the vocabulary for the profile.
 * - `oracle.unprotected` — fail-closed: the grader env was not isolated from the worker env (identity check).
 *
 * `needs_human_oracle` is disjoint from `needs_review` (a future dry-run gate) and `irreversible_ask` (shipped).
 */
export type RepairTerminal = "fixed" | "candidate_only" | "needs_human_oracle" | "gave_up" | "conflict" | "oracle.unprotected";
/**
 * One oracle verdict. Produced by the injected {@link RepairOracle} closure. `flaky` records that the oracle
 * was non-deterministic across `retries` (a flaky verdict is NEVER projected to `fixed` — §5 / §6 flakyK).
 * `trace` is the raw failure output fed back into the next fix turn; it is treated as untrusted worker-adjacent
 * data (delimited into context, sanitized into the objective) but its INNER text is NEVER rewritten (§5.5 #8a)
 * — rewriting it would destroy the real-trace feedback lever.
 */
export interface OracleResult {
    tier: OracleTier;
    passed: boolean;
    /** Raw failure trace (on a failed verdict). Untrusted; fed back verbatim into the fix turn (never inner-rewritten). */
    trace?: string;
    /** The oracle's verdict was non-deterministic across `retries` re-isolations. A flaky verdict never → `fixed`. */
    flaky: boolean;
    /** How many re-isolation retries the oracle ran to settle the verdict. */
    retries: number;
    /**
     * HRD-AGO-6 — what producing this verdict COST (micro-USD, the oracle's own spend: judge/gate model calls,
     * re-isolation retries, …). Optional: a mechanical oracle (an exec gate, a property harness) spends nothing
     * and omits it. When supplied it counts against {@link RepairLoopConfig.costCeilingMicroUsd} exactly like the
     * worker's spend — an LLM-judge oracle re-run per attempt is real money the ceiling used to be blind to —
     * and is surfaced cumulatively as {@link RepairResult.oracleCostMicroUsd}. It is NOT folded into
     * `TaskResult.stats` (those are the WORKER's, and the oracle runs outside `runner.runTask`).
     */
    costMicroUsd?: number;
}
/**
 * The PROFILE-injected oracle (§1 裁决①). The profile composes the actual grading inside this closure —
 * `runExecGate(graderEnv, steps)` (core/exec-gate.ts, env is the FIRST param = L2 provenance bound to the
 * grader) → `verifyCompleted` (agents/verify.ts, the read-only L3 judge) → `checkInvariants`
 * (core/property-harness.ts, explore-only) — and maps the composite to an {@link OracleResult}. Core never
 * composes these internals (so the oracle type can't grow a long discriminated union); it only fixes the
 * `OracleResult` shape and the read-only/identity contract. `graderEnv` is the isolated grader env; `evidence`
 * is the diff/results to judge (recompute it from the post-resume working tree on a resume — threat BUG5, the
 * same freshness duty verify.ts states on its own `evidence` config field).
 */
export type RepairOracle = (graderEnv: ExecutionEnv, evidence: string | undefined) => Promise<OracleResult>;
/**
 * The durable, JSON-safe repair state carried across an orthogonal durable suspend (§4). Serialized ONLY when
 * an unrelated resource/HITL suspend interleaves the loop (happy-path is in-memory) and re-seeded on resume so
 * `attemptCount` advances MONOTONICALLY (never reset). Contains NO functions/Dates (epoch ms if a timestamp is
 * ever needed) so `structuredClone`/`JSON.stringify` round-trip it on the checkpoint. `baselinePassTests` is
 * DELIBERATELY ABSENT (§4 MAJOR-C): the anchor is grader-computed out-of-process so a worker can't shrink it.
 */
export interface RepairBundle {
    /** The latest failure trace (untrusted; fed back into the fix turn). */
    failureTrace: string;
    /** Reflexion-style diagnoses accumulated across attempts (≤3, Reflexion cap — §1). */
    diagnostics: string[];
    /** Hypotheses tried and rejected (so a re-seed doesn't re-explore them). */
    rejectedHypotheses: string[];
    /** The in-loop attempt counter — re-seeded MONOTONICALLY on resume, never reset/max/downscaled (§4 / §5). */
    attemptCount: number;
    /** The tier of the last oracle verdict (for the resumed loop's projection). */
    oracleTier: OracleTier;
    /** F1 (ruling 2026-08-02) — cumulative loop spend (micro-USD: worker + nested + oracle) at the last
     *  attempt boundary. Re-seeded on resume so a resumed loop CONTINUES the money account instead of
     *  restarting it at 0 (a resume must not wash `costCeilingMicroUsd`). Optional: bundles serialized
     *  before this field existed read as 0 — the pre-F1 behavior, monotonic direction only. Updated at
     *  attempt boundaries HERE; a mid-attempt park is topped up by the RUNNER (RB-481): the checkpoint mint
     *  folds the in-flight leg's spend into the serialized copy on a HUMAN-DECISION park (whose resume reports
     *  its own leg only), and deliberately does not on a resource-slice park (whose resume returns that money
     *  through its cumulative stats). Either way the parked account is complete, never washed. */
    spentMicroUsd?: number;
    /** F1 — cumulative ACTIVE loop wall-time (ms) at the last attempt boundary. ACTIVE means time the loop
     *  was actually running: the suspended interval (a human deciding a HITL gate for days) is excluded by
     *  construction, so `totalTimeoutMs` bounds loop work, not human latency. Same optionality/monotonic
     *  contract as {@link RepairBundle.spentMicroUsd}. */
    activeElapsedMs?: number;
}
export interface RepairLoopConfig {
    /** The PROFILE-injected, trusted oracle (§1 裁决①). Core calls it; it never composes the oracle internals. */
    oracle: RepairOracle;
    /**
     * The ISOLATED grader env the oracle grades in. MUST be a distinct object from {@link workerEnv}: if they
     * are the SAME reference the loop fails closed to `oracle.unprotected` BEFORE running anything (§5.1 —
     * necessary-not-sufficient; the real out-of-process boundary is the OFF Gate-1).
     */
    graderEnv: ExecutionEnv;
    /**
     * The worker's execution env (the env `runner.runTask` runs the impl in), passed by the trusted caller so
     * the §5.1 identity check can run: `graderEnv === workerEnv` → fail-closed `oracle.unprotected`. `TaskSpec`
     * deliberately has NO `executionEnv` (the worker env is wired on the Runner deps / `executionEnvFactory`, not
     * reachable from `implSpec`), so the caller must supply the reference here for the check to be meaningful.
     * Omit it only when the worker env genuinely can't collide with the grader (e.g. distinct factories) — the
     * check is then skipped (no reference to compare) and isolation is the caller's deployment contract.
     */
    workerEnv?: ExecutionEnv;
    /**
     * design/77 §1 Gate-1 (oracleIsolation): the paths the oracle/spec lives behind that the worker must NOT be
     * able to corrupt (e.g. a hidden held-out test dir). When supplied **together with** {@link workerEnv}, the
     * loop runs the FULL structural {@link assertOracleIsolation} (identity + structural-class + bash write-probe)
     * instead of the bare reference-identity check, and surfaces {@link RepairResult.isolationClass}. Omit it (the
     * default) to keep the existing necessary-not-sufficient reference-identity check (the merged Slice-1 posture).
     *
     * The real isolated grader env is SERVICE-provided (the trusted control plane mints it; the engine has no seat for it);
     * `graderEnv` here is its `.env`, and the control-plane `provenance` brand is reattached internally for the
     * assertion. The assertion only matters for the (mandate-OFF) auto-accept path: `isolationClass` caps a
     * non-`out_of_process` grader at `candidate_only`. SAFE-tier never auto-accepts regardless.
     */
    immutableOraclePaths?: string[];
    /**
     * In-loop attempt ceiling (the loop runs at most this many GENERATE/oracle attempts, PLUS the documented
     * one-shot CLEAN_RESTART the state machine grants once at exhaustion). Profile validates this to 2-3; core
     * only enforces it as a ceiling, it does not fence the value — except that a NON-FINITE one falls back
     * to 1 and is reported on {@link RepairLoopConfig.onWarn} (CLS-A-3/A-7 class: a NaN ceiling makes every
     * `>=` gate false, i.e. no ceiling at all). This counter is DISTINCT from
     * `suspendCount`(maxSuspends) and `sliceCount`(maxSlices) — it never borrows those budgets (§5 / §7).
     *
     * HRD-AGO-4: enforced BEFORE each dispatch as well as after, so a loop entered with {@link resumeBundle}
     * whose `attemptCount` already reached the ceiling dispatches NOTHING and returns `gave_up`
     * (`errorCode: "repair.attempts_exhausted"`) instead of spending one more worker run (plus a restart).
     */
    maxAttempts: number;
    /**
     * On a resume after an orthogonal durable suspend, the restored {@link RepairBundle} (from
     * `PrepareResume.seed.repairBundle`). `attemptCount` is re-seeded from it MONOTONICALLY (§4) — never reset.
     */
    resumeBundle?: RepairBundle;
    /**
     * Stop the loop once cumulative cost reaches this (verify.ts's `costCeilingMicroUsd` backstop). HRD-AGO-6 — "cumulative" is
     * every leg the loop pays for: each attempt's own + nested `runTask` cost PLUS each verdict's
     * {@link OracleResult.costMicroUsd} (an LLM-judge oracle is charged per attempt too).
     */
    costCeilingMicroUsd?: number;
    /** Overall ACTIVE wall-clock ceiling for the whole loop (verify.ts's `totalTimeoutMs` backstop). F1: both ceilings
     *  are CROSS-RESUME — the carried {@link RepairBundle.spentMicroUsd}/{@link RepairBundle.activeElapsedMs}
     *  re-seed the accounts, so a durable resume continues the budget rather than restarting it. */
    totalTimeoutMs?: number;
    /** Per-attempt callback (observability). */
    onAttempt?: (info: {
        attempt: number;
        repairTerminal?: RepairTerminal;
        oracle: OracleResult;
    }) => void;
    /**
     * Operational-warning sink (observability). MINOR-2: the §5.1 isolation identity check is SKIPPED when
     * `workerEnv` is undefined (no reference to compare). An ACCIDENTAL omission — `graderEnv` supplied but
     * `workerEnv` forgotten — would then disable the check silently. When wired, that case is surfaced here
     * once at loop start so the gap is observable; the posture stays necessary-not-sufficient (no hard-fail).
     */
    onWarn?: (warning: Error) => void;
}
export interface RepairResult extends VerificationResult {
    /** The projected REPAIR terminal (§2) — the loop's own word (fixed / candidate_only / gave_up / …), distinct
     *  from the inherited run terminal `terminal` (a {@link import("../core/types.js").TerminalCause}, WHY the
     *  worker's last leg ended). SAFE-tier never returns `fixed`. */
    repairTerminal: RepairTerminal;
    /** The final repair state (in-memory unless an orthogonal suspend serialized it — §4). */
    bundle: RepairBundle;
    /**
     * design/77 §1 Gate-1: the structural isolation class of the grader env, when the full
     * {@link assertOracleIsolation} ran (i.e. {@link RepairLoopConfig.immutableOraclePaths} + `workerEnv` were
     * supplied). `"out_of_process"` is the ONLY class the (mandate-OFF) auto-accept path may consider;
     * `"in_process_probe_only"` CAPS the run at `candidate_only`. Undefined when only the bare reference-identity
     * check ran. SAFE-tier never auto-accepts regardless of this value.
     */
    isolationClass?: OracleIsolationVerdict["isolationClass"];
    /**
     * HRD-AGO-6 — cumulative ORACLE spend (micro-USD) across every verdict this loop took, summed from
     * {@link OracleResult.costMicroUsd}. Reported SEPARATELY from `stats` (the worker legs) because the oracle
     * runs outside `runner.runTask`: `stats.costMicroUsd + stats.nested.costMicroUsd + oracleCostMicroUsd` is the
     * loop's true total. `0` when no verdict reported a cost (a mechanical oracle).
     */
    oracleCostMicroUsd: number;
}
/**
 * design/77 §1 Gate-1 invariant — auto-accept (`fixed`) is UNREACHABLE here. The terminal-by-tier projection
 * (§2) already caps every PASS at `candidate_only`, so `isolationClass` only ever matters for the (OFF)
 * auto-accept path. This guard makes the invariant load-bearing rather than caller discipline: a non-isolated
 * (`in_process_probe_only`) or unprotected verdict can NEVER carry a `fixed` terminal — at most
 * `candidate_only` / `oracle.unprotected`. It is a pure assertion over the projected terminal; it never
 * upgrades anything (auto-accept stays OFF — only a future Gate-1 slice running on an `out_of_process` grader
 * may even consider it).
 */
export declare function isolationPermitsAutoAccept(verdict: OracleIsolationVerdict): boolean;
/**
 * CONSUMER seam (design/78 Slice-1, MAJOR-3 wiring — the re-entry side of the round-trip). Read the durable
 * {@link RepairBundle} a resumed checkpoint carries, so a leader/profile resuming a repair-interleaved
 * suspend can re-seed a FRESH {@link runRepairLoop} call with it.
 *
 * ## The contract (core vs profile)
 * - **CORE owns the persistence + restore.** When an orthogonal durable suspend (resource/HITL) interleaves a
 *   `runRepairLoop` attempt, the Runner serializes the live bundle onto the minted checkpoint
 *   (`prepareTask.serializeCheckpointState` sources `internals.repairBundle`), and on resume re-seeds it back
 *   into `prepareTask` from `cp.state.repairBundle`. So `cp.state.repairBundle` is round-trip-true after a
 *   `runner.resume(token, …)`: it survives the suspend with `attemptCount` intact (never reset — §4 / MAJOR-A).
 * - **PROFILE owns the re-entry loop.** The leader that resumes the worker (`runner.resume`) is a PROFILE
 *   concern — core does NOT build the "resume → grade → re-seed `runRepairLoop`" loop. The profile reads the
 *   restored bundle (via this accessor) off the SAME checkpoint it resumed, and on its NEXT repair attempt
 *   passes it as {@link RepairLoopConfig.resumeBundle}. `runRepairLoop` then re-seeds `attemptCount`
 *   MONOTONICALLY from it (`config.resumeBundle` → the loop's `bundle`), so the repair ratchet is preserved
 *   across the durable suspend rather than starting the attempt budget over.
 *
 * Returns `undefined` when the checkpoint carries no repair state (a non-repair task, or a repair attempt that
 * suspended on its very first GENERATE before any oracle verdict — the re-seed treats `undefined` as a clean
 * start). The returned object is the checkpoint's own (already an independent `structuredClone` from the
 * store); pass it straight into `resumeBundle` — `runRepairLoop` copies its arrays defensively on re-seed.
 */
export declare function repairBundleFromCheckpoint(cp: Checkpoint): RepairBundle | undefined;
/**
 * The terminal-by-tier projection (§2) — a PURE function, the core invariant. **SAFE-tier caps every PASS at
 * `candidate_only`**; `fixed` (auto-accept) is unreachable here because it needs the OFF Gate-1 out-of-process
 * boundary. Enforced HERE (in the projection table), not as caller discipline, so no consumer can promote a
 * PASS to `fixed`.
 *
 * - any tier, `passed: true` → `candidate_only` (trusted_hidden/trusted_visible/property_harness_weak alike).
 * - `l3_judge_advisory` → advisory only; even a "pass" can't clear → `candidate_only` (it never self-clears).
 * - `none` → `needs_human_oracle` (no usable oracle → only a human adjudicates).
 * - not-passed with a usable tier → not a terminal yet (the loop continues / gives up); represented as
 *   `undefined` so the caller's state machine drives the `gave_up`/`conflict` path explicitly.
 */
export declare function terminalForTier(oracle: OracleResult): RepairTerminal | undefined;
/**
 * Run the SAFE-tier oracle self-repair loop over `implSpec` (design/78 §1 state machine — a DETERMINISTIC
 * switch, no LLM in the control flow):
 *
 * `ASSERT_ISOLATION` (identity check → fail-closed `oracle.unprotected`) → `GENERATE` (`runner.runTask`) →
 * `ORACLE` (the injected closure) → { passed → terminal-by-tier projection } | { failed & attempt<max → REPAIR
 * (a fix turn) → loop } | { failed & attempt==max → CLEAN_RESTART once, else `gave_up` } | { conflict → terminal }.
 *
 * `ASSERT_ISOLATION` is the design/77 §1 Gate-1 seam: when {@link RepairLoopConfig.immutableOraclePaths} +
 * `workerEnv` are supplied it runs the FULL {@link assertOracleIsolation} (identity + structural-class + bash
 * write-probe) and surfaces {@link RepairResult.isolationClass}; otherwise it runs the bare reference-identity
 * check (the merged Slice-1 posture). Either way a failed assertion fails closed to `oracle.unprotected` before
 * any work runs, and the result NEVER reaches `fixed` — `isolationClass` only gates the (mandate-OFF) auto-accept
 * path that this SAFE-tier loop does not take.
 *
 * SAFE-tier: the projection caps every PASS at `candidate_only` — this NEVER auto-accepts. `runRepairLoop` is a
 * SIBLING of {@link verifyCompleted}: it composes over `runner.runTask` and never touches the Runner core.
 */
export declare function runRepairLoop(runner: RunnerSelfSeat, implSpec: TaskSpec, config: RepairLoopConfig): Promise<RepairResult>;
