import { type OutcomeEvent } from './solution-outcomes.js'; export type FitnessState = 'draft' | 'active' | 'champion' | 'underperform'; export interface FitnessRecord { solution: string; injected: number; accepted: number; corrected: number; errored: number; unknown: number; /** Laplace-smoothed acceptance ratio × log(1+injected). */ fitness: number; state: FitnessState; /** ms since last injection event. Infinity if never injected. */ last_injected_ago_ms: number; } export interface FitnessOptions { /** * Minimum injections required before a solution is evaluated against the * underperform threshold. Below this, state stays at `draft`. */ minEvalInjections?: number; /** * Injections required to qualify as champion (in addition to fitness cut). */ minChampionInjections?: number; /** * Champion cut: fitness must exceed this fraction of the max fitness in * the current population. Default 0.7 → top 30% by ratio of max. */ championFraction?: number; /** * Underperform cut: fitness must fall below this fraction of the median. */ underperformFraction?: number; /** Pre-loaded events (for tests). Defaults to `readAllOutcomes()`. */ events?: OutcomeEvent[]; } /** * Compute fitness scores for every solution with at least one recorded * outcome event. * * Formula: `fitness = (accept + 1) / (accept + correct + error + 1) × log(1 + injected)` * - `accept` = positive (silence = consent) * - `correct` = negative (explicit user correction within window) * - `error` = weak negative (tool failed while solution was pending) * - `unknown` = ignored (session ended mid-pending; we can't tell) * * Epsilon smoothing (+1) means a cold solution with 1 injection and 1 * accept produces `2/2 × log(2) ≈ 0.69`, not a meaningless `1.0 × 0` or * `∞`. Log confidence penalizes small-sample champions. */ export declare function computeFitness(opts?: FitnessOptions): FitnessRecord[];