# Prompt — cheap diversity knobs (tournament size + mutation + random immigrants), curve as the test The fitness curve collapses early (best = median by ~gen 10, then flat) — premature convergence. Add the cheap diversity levers before considering the island model. Verified against the code. Defaults must preserve current behaviour exactly. `pytest` + `tsc` after. ## Verified current state (engine_v2/gp.py) `run_gp_v2(..., population_size=150, tournament_k=3, elitism=5, p_mutate=0.7, ...)`. `_tournament_select` (line ~31) picks `k` random contenders and returns the best. Main loop (~121-204): evaluate → rank → carry `elitism` elites → fill the rest via `_tournament_select` ×2 + crossover + `mutate`. There is **no** random-immigrant mechanism today. ## Part A — add the three levers to `run_gp_v2` 1. **Tournament size** — `tournament_k` is already a param. Lowering it (3 → 2) reduces selection pressure → slower takeover → more diversity. Keep the default 3. 2. **Mutation** — `p_mutate` is already a param (0.7). Allow raising it (e.g. 0.85). Keep the default 0.7. 3. **Random immigrants (new)** — add `immigrant_fraction: float = 0.0`. When > 0, each generation reserve `round(immigrant_fraction * population_size)` slots in the new population for **fresh random programs** drawn from `ramped_population` (same rng/seed, same grammar/objective constraints as init) instead of crossover+mutation offspring. Fill them after the elites, before/among the offspring; never displace the elites. Default 0.0 ⇒ behaviour unchanged. ## Part B — one UI toggle to drive them (so you can A/B the curve) - `RunRequest` gains `diversity: bool = False`. The `_worker` maps `diversity=True` → `tournament_k=2`, `p_mutate=0.85`, `immigrant_fraction=0.10` (tune if needed); `diversity=False` → current defaults unchanged. - Frontend: a Parameters checkbox **"Maintain diversity"** (default OFF). Tooltip in plain English: "Off: the population can collapse to near-clones early (the fitness curve's best and median lines meet and go flat). On: lowers selection pressure and injects fresh random programs each generation, so the population keeps exploring — watch the best-vs-median gap stay open longer." ## Part C — the test is the curve The success criterion is visible in the Live view fitness curve: with **Maintain diversity ON**, the **best and median lines should stay separated for many more generations** (the population doesn't collapse to clones by ~gen 10), versus OFF where they meet early. Also confirm the winner's held-out AUROC doesn't meaningfully drop (diversity should preserve or improve detection, not hurt it). Re-run HPV coherence-on with the toggle off vs on and compare the curves. ## CONSTRAINTS - Defaults preserve current behaviour byte-for-byte (immigrant_fraction 0, tournament_k 3, p_mutate 0.7) so existing runs are unchanged. No airgap/API-shape break beyond the additive `diversity` field. Airgap untouched (this is search-internal; opaque IDs throughout). ## Checkpoint - `run_gp_v2` accepts `immigrant_fraction`; with it >0, fresh random programs enter each generation without displacing elites. - The "Maintain diversity" toggle (default off) flips tournament_k / p_mutate / immigrant_fraction. - With the toggle ON, the fitness curve's best-vs-median gap visibly persists longer; winner held-out is not degraded. - `pytest` green (defaults unchanged), `tsc` clean, airgap untouched.