/**
 * Anytime-valid sequential promotion gate — an e-process (betting
 * test-martingale, see `eProcess` in `statistics.ts`) over paired
 * per-scenario deltas, so a campaign stops the MOMENT evidence decides
 * instead of burning a fixed-n budget. Decisions remain valid at any
 * data-dependent stopping time (Ville's inequality), which is exactly what
 * fixed-n machinery cannot offer: peeking at a bootstrap CI after every
 * observation and stopping on the first significant peek inflates type-I
 * error far beyond alpha.
 *
 * REPLACES, never layers on, a fixed-n gate. Running `heldoutSignificance`
 * or `paretoSignificanceGate` repeatedly on a growing sample and stopping
 * early is optional stopping no matter how it is dressed up; this gate is
 * the valid way to stop early. Use one or the other per evidence stream.
 *
 * Pre-registration binding: anytime validity holds only for the
 * PRE-REGISTERED statistic. When a `SignedManifest` is bound, the gate takes
 * alpha from `manifest.alpha`, the observation budget from
 * `manifest.preRegisteredN`, orients deltas by `manifest.direction`, and
 * shifts the null boundary by `manifest.minEffect` — re-deciding the same
 * stream under different parameters after seeing data would reopen optional
 * stopping under a fancier name. The manifest's content hash is verified at
 * construction (sync, same `sha256-content` scheme as `signManifest`).
 *
 * Non-iid caveat (stated honestly): the supermartingale guarantee needs each
 * delta's conditional mean under H0 to stay ≤ the null boundary given the
 * past — exchangeable scenario deltas suffice. Scenario streams ordered by
 * difficulty or by scenario family violate this; `decide(ctx)` therefore
 * shuffles the paired deltas with a SEEDED permutation by default (the
 * permutation is data-independent, so bet predictability is preserved).
 * Stratified betting (per-stratum λ) is future work, not implemented here.
 */
import { type SignedManifest } from '../../pre-registration';
import { type EProcessState } from '../../statistics';
import type { Gate, GenerationRecord, Scenario } from '../types';
export type SequentialDecision = 'promote' | 'continue' | 'undecided-at-maxN';
export interface SequentialObservation {
    decision: SequentialDecision;
    /** Current e-value (the betting wealth) against H0. */
    eValue: number;
    /** Paired deltas consumed so far. */
    n: number;
    /** Names the decision basis. For 'undecided-at-maxN' it states explicitly
     *  that exhausting the budget is NOT evidence of no effect. */
    reason: string;
}
export interface SequentialPairedGateOptions {
    /** Type-I budget. With `preRegistration` bound this MUST match
     *  `manifest.alpha` (conflict throws). Default 0.05. */
    alpha?: number;
    /** Minimum paired deltas before a promote may fire. The stopping rule is
     *  "first n ≥ minN with e-value ≥ 1/alpha" — still a valid stopping time.
     *  Default 5. */
    minN?: number;
    /** Pre-registered observation budget. Required unless `preRegistration`
     *  supplies it via `preRegisteredN` (conflict throws). */
    maxN?: number;
    /** Bet truncation forwarded to `eProcess`. Default 0.5. */
    maxBet?: number;
    /** Bound on |delta| in the judge's native scale; deltas are mapped to
     *  x = (d/scale + 1)/2 ∈ [0,1]. A delta outside ±scale throws (use
     *  `detectScale` to pick 1 vs 100 BEFORE streaming). Default 1. */
    scale?: number;
    /** Seed for the data-independent shuffle of paired deltas in `decide(ctx)`
     *  (exchangeability guard). Default 1337. */
    shuffleSeed?: number;
    /** Bind the pre-registered hypothesis. Verified (content hash) at
     *  construction; alpha/maxN/direction/minEffect come FROM the manifest. */
    preRegistration?: SignedManifest;
    /** Override the gate name in reports. */
    name?: string;
}
export interface SequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario> extends Gate<TArtifact, TScenario> {
    /** Streaming entry point: feed one paired per-scenario delta
     *  (candidate − baseline, native scale). Each gate instance carries ONE
     *  observe-stream; `decide(ctx)` runs on its own fresh stream and never
     *  consumes or advances this one. 'promote' is sticky; observing past the
     *  pre-registered maxN throws (extending a finished stream after seeing
     *  the result reopens optional stopping — start a NEW pre-registered
     *  test). */
    observe(delta: number): SequentialObservation;
    /** Read-only snapshot of the observe-stream. */
    state(): EProcessState & {
        decision: SequentialDecision;
    };
}
/**
 * Anytime-valid sequential paired gate. Conforms to the existing `Gate`
 * contract (`decide(ctx)` consumes candidate vs baseline judge scores via
 * `pairHoldout` — same pairing granularity as the fixed-n gates: full cellId,
 * never scenarioId) and adds a streaming `observe(delta)` entry for campaigns
 * that score cells incrementally and want to stop mid-stream.
 *
 * Decision mapping onto the substrate's five-valued `GateDecision`:
 *   - 'promote'            → 'ship'
 *   - 'continue'           → 'need_more_work' (stream ended before maxN with
 *                            the e-value undecided — more reps could decide)
 *   - 'undecided-at-maxN'  → 'hold', with the reason stating it is NOT
 *                            evidence of no effect (never a silent default)
 */
export declare function sequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(options: SequentialPairedGateOptions): SequentialPairedGate<TArtifact, TScenario>;
export interface SequentialDecideOptions {
    /** Type-I budget for the early-stop evidence. Default 0.05. */
    alpha?: number;
    /** Minimum paired deltas before a stop may fire. Default 5. */
    minN?: number;
    /** Bet truncation forwarded to `eProcess`. Default 0.5. */
    maxBet?: number;
    /** Bound on |per-scenario composite delta|. Default 1. */
    scale?: number;
}
export interface SequentialDecideFn {
    (args: {
        history: GenerationRecord[];
    }): {
        stop: boolean;
        reason?: string;
    };
    /** Read-only snapshot of the accumulated e-process (observability + tests). */
    state(): EProcessState;
}
/**
 * `SurfaceProposer.decide` adapter — stops the optimization loop the moment
 * the e-process decides the loop has produced a real improvement, instead of
 * always running `maxGenerations`.
 *
 * Stream: for each generation g ≥ 1, the per-scenario composite deltas of
 * generation g's top candidate vs the generation-0 top candidate (the
 * incumbent the loop set out to beat), paired by scenarioId. H0: no proposed
 * surface improves any scenario's expected composite over the incumbent —
 * under it every delta has conditional mean ≤ 0 and the e-process is valid.
 * Once wealth ≥ 1/alpha the loop stops and hands the winner to the promotion
 * gate (which re-scores on HELD-OUT data — this adapter only spends the
 * exploration budget, it never promotes).
 *
 * Honesty caveats: (1) the incumbent's scores are measured once and shared
 * across all generations' deltas, so type-I control is exact only insofar as
 * those scores approximate the incumbent's true per-scenario means (more reps
 * → tighter); (2) an UNDECIDED process never stops the loop — absence of a
 * crossing is NOT evidence of no effect, so the loop simply runs its normal
 * course. Calling the adapter repeatedly with a growing history consumes each
 * generation exactly once (re-feeding an already-seen record would double-count
 * evidence).
 */
export declare function sequentialDecide(options?: SequentialDecideOptions): SequentialDecideFn;
//# sourceMappingURL=sequential.d.ts.map