/** * Holdout split + train/holdout composite breakdown. * * Deterministic (no RNG) train/holdout partitioning of a sample set, plus the * subset-composite recompute that lets `omk eval --holdout-ratio` and `omk evolve` * score a variant on a withheld slice using the *same* aggregation as the headline * composite (`buildVariantSummary`). Lives in eval-core so both the eval pipeline * and the authoring/evolve loop depend *down* into it (authoring → eval-core is the * established direction). * * A large train − holdout composite gap is the generalization / sample-set-overfitting * signal the verdict's overfitting gate reads (`src/eval-core/verdict.ts`). */ import type { Report, HoldoutBreakdown } from '../types/index.js'; /** A train / holdout partition of a sample set. */ export interface HoldoutSplit { trainIds: Set; holdoutIds: Set; } /** Below this many samples on any side, a split is too small to be meaningful — * callers fall back to full-set scoring and mark the breakdown `disabled`. */ export declare const MIN_HOLDOUT_SUBSET = 3; /** Pick `count` ids at an even stride across `ids` (deterministic, no RNG) so the * picked subset is representative of the ordering and stable across rounds/runs. */ export declare function pickByStride(ids: string[], count: number): Set; /** * Deterministically split sample ids into train / holdout by `ratio` (fraction * held out). Holdout members are picked at an even stride so the partition is * representative of the ordering, and the split is stable across rounds and runs * (no RNG). Returns null when ratio ≤ 0 or either side would drop below * MIN_HOLDOUT_SUBSET — the caller then scores on the full set. */ export declare function splitHoldout(sampleIds: string[], ratio: number): HoldoutSplit | null; /** * Mean composite over the subset of a report's results whose sample_id is in * `ids`, using the same aggregation as the full-run summary * (`buildVariantSummary`) so train / holdout scores stay comparable to the * headline composite. Returns 0 when the subset has no scorable entries. */ export declare function subsetCompositeScore(report: Report, variantKey: string, ids: Set): number; /** * How many subset results actually produced a usable composite (> 0) for a variant. * `buildVariantSummary` averages only `compositeScore > 0` entries (schema.ts), so * the mean can rest on far fewer samples than the authored split size when runs * flake / partial-error / budget-abort. The overfitting gate must trust THIS count, * not the authored `trainCount` / `holdoutCount`, or a 1-of-3 holdout gets dressed * up as a 3-sample-backed conclusion. */ export declare function subsetScorableCount(report: Report, variantKey: string, ids: Set): number; /** * Train vs holdout composite breakdown per variant for `omk eval --holdout-ratio`. * Post-hoc — never perturbs the headline aggregation or bootstrap CI. * * The split is taken over `sampleIdOrder` — the **stable authored sample order** * (the loaded `samples` file order), NOT `report.results`, whose insertion order * is the concurrent-completion order and drifts run-to-run. Binding the stride pick * to the authored order is what makes the holdout (and the verdict overfitting gate * it feeds) deterministic and reproducible. Subset scores are then read from * `report.results` by id-set membership, which is order-independent. * * When the split is too small on either side (< MIN_HOLDOUT_SUBSET) it returns * `{ disabled: true }` with an empty `perVariant`, so the verdict overfitting gate * stays inert. The testSetHash watermark (gap-spec §7.1) is attached by the caller, * shared with gapReports. */ export declare function computeHoldoutBreakdown(report: Report, variantNames: string[], ratio: number, sampleIdOrder: string[]): HoldoutBreakdown;