/** * Bootstrap confidence intervals — replaces / supplements t-test for LLM eval. * * Why bootstrap instead of t-test for LLM scores? * * 1. **No distribution assumption** — t-test assumes scores are normally * distributed. LLM scores on a 1-5 ordinal scale violate this; bootstrap * only requires that the sample is representative. * 2. **Small N robustness** — t-test's small-sample correction (df-adjusted) * breaks down with N < 10. Bootstrap is consistent at any N >= 2. * 3. **Difference-of-means is the actual question** — In A/B eval the user * asks "is variant B better than A?" — this is a 2-sample comparison and * needs CI on the *difference*, not on each variant's mean separately. * * This module exports: * - bootstrapMeanCI: CI for a single variant's mean * - bootstrapDiffCI: CI for the difference (B - A); 0 outside the CI = significant * - bootstrapWithMetric: generic interface so saturation analysis can reuse * * Reproducibility: CIs are **deterministic by default** — when no `seed` is passed, * a fixed `DEFAULT_BOOTSTRAP_SEED` is used, so the same eval run twice yields * byte-identical CIs (and a stable verdict near the significance boundary). This is * a measurement-validity requirement: an unseeded `Math.random()` would let the * `significant` flag flip between identical runs. Library callers (and specific paths * such as `eval gold compare --seed`) may pass an explicit `seed` to vary the draw; the * main `omk eval` deliberately exposes no seed knob — a fixed default also prevents * seed-shopping for significance. */ export interface BootstrapCI { /** Lower bound of the CI. */ low: number; /** Upper bound of the CI. */ high: number; /** The point estimate (mean of original sample, or whatever metric was passed). */ estimate: number; /** Number of bootstrap resamples performed. */ samples: number; } export interface BootstrapDiffCI extends BootstrapCI { /** Whether 0 is inside the CI — when false, the difference is statistically significant. */ significant: boolean; } /** * Default number of bootstrap resamples. Every eval path uses this unless * `--bootstrap-samples` overrides it. Single source of truth: the docs cite * it and `test/scripts/doc-constants-drift.test.ts` guards doc ↔ code parity. */ export declare const DEFAULT_BOOTSTRAP_SAMPLES = 1000; /** Default significance level; 0.05 → 95% CI. */ export declare const DEFAULT_BOOTSTRAP_ALPHA = 0.05; /** * Fixed default bootstrap seed → CIs are **deterministic by default** (omk default-strict: * reproducibility affects verdict validity, so it is on by default, not opt-in). The specific * value is arbitrary — only that it is **fixed** matters; it is an implementation detail, not a * user-facing constant, so unlike DEFAULT_BOOTSTRAP_SAMPLES / α it is neither cited in docs nor * guarded by `doc-constants-drift.test.ts`. Callers wanting a different draw pass an explicit `seed`. */ export declare const DEFAULT_BOOTSTRAP_SEED = 20260616; /** * Confidence-level label for display: `(1 − α)·100%`. Multiple-comparison (Bonferroni) correction * shrinks a pairwise α to α/K and widens the CI accordingly — the label must track α so a corrected * (wider) interval is never mislabeled "95%". No alpha (single comparison / classic A-B) ⇒ nominal 95%. * Shared single source for the HTML renderer and the `omk eval` CLI verdict so both read one scale. */ export declare function ciLevelLabel(alpha?: number): string; /** * Bootstrap confidence interval for the mean of a single sample. * * @param scores Sample observations (e.g., scores from N evaluation samples). * @param alpha Significance level. 0.05 = 95% CI. Default 0.05. * @param samples Number of bootstrap resamples. Default 1000. * @param seed Optional seed for deterministic CIs (tests). * @returns { low, high, estimate, samples }; all numbers rounded to 4 decimals. */ export declare function bootstrapMeanCI(scores: number[], alpha?: number, samples?: number, seed?: number): BootstrapCI; /** * Bootstrap confidence interval for the *difference* of two sample means * (treatment - control). Each bootstrap iteration resamples both groups * independently and computes mean(B) - mean(A). * * The `significant` flag is true when 0 falls outside the CI — a clean * proxy for "treatment differs from control at the alpha level". * * @param scoresA Control / baseline sample (scoresA → first group, the subtrahend in the diff). * @param scoresB Treatment sample. * @param alpha Significance level. Default 0.05. * @param samples Bootstrap resamples. Default 1000. * @param seed Optional seed. * @returns BootstrapDiffCI with low/high of (B - A) and significant flag. */ export declare function bootstrapDiffCI(scoresA: number[], scoresB: number[], alpha?: number, samples?: number, seed?: number): BootstrapDiffCI; /** * Bootstrap CI for the *difference* of two means on **paired** observations — when A and B * are two measurements of the **same unit** (e.g. control vs treatment on the same sample, * or original vs alternate judge prompt on the same response). Resamples the **pair indices * jointly** and averages each pair's `b - a`, so the within-pair correlation is preserved. * * Why paired (vs `bootstrapDiffCI`'s independent resampling): when A and B move together * across units (the usual case — the same sample scored by two variants is positively * correlated), much of each group's variance is shared and cancels in the per-pair diff. * The independent (unpaired) bootstrap ignores that, over-states the diff's variance, and * widens the CI — *conservative*, costing real power. Use paired whenever the design is * paired; use `bootstrapDiffCI` only for genuinely independent groups (or where a deliberate * conservative bias is wanted). The point estimate is identical (mean of per-pair diffs = * difference of paired means); only the CI tightens. * * `significant` is derived from the **rounded** `low`/`high` (the persisted bounds), so the * flag never contradicts what is stored / displayed: a CI that rounds to include 0 reads as * not-significant. (Computing it on the unrounded bounds would let the JSON say `low: 0, * significant: true` — a self-contradictory `CI=[0, …]` that downstream `computeVerdict` and * external consumers cannot reconcile.) Matches `bootstrapDiffCI`. * * @param pairs Aligned observations; `a` = control/baseline, `b` = treatment. diff = b - a. * @param alpha Significance level. Default 0.05. * @param samples Bootstrap resamples. Default 1000. * @param seed Optional seed (deterministic by default — see module header). */ export declare function bootstrapPairedDiffCI(pairs: Array<{ a: number; b: number; }>, alpha?: number, samples?: number, seed?: number): BootstrapDiffCI; /** * Generic bootstrap CI for an arbitrary sample-level metric. Used by * saturation analysis to get CI on metrics like stddev or * agreement, not just mean. * * @param scores Original sample. * @param metricFn Function reducing a resampled array to a scalar. */ export declare function bootstrapWithMetric(scores: number[], metricFn: (resampled: number[]) => number, alpha?: number, samples?: number, seed?: number): BootstrapCI;