Build step 4 of 8 — Phase EJ (umbrella #281)
Build doc: documentation/plans/evolve-judging-plan.md §7 Step EJ.4
What to build
EJ.3's honest cost is regression catch at p=0.35 dropping 76.5% → 42.8%, and the "caught later" backstop is false (the next generation compares against the same weakened bar) — so the one-sided rule needs a drift backstop that cannot cycle. The EL gauntlet already plays the promoted version vs each frozen anchor under --fitness-mode both and is explicitly log-only ("a later step can drive gating off it", scripts/evolve.py L3825-3832). This is that later step — consume the existing gauntlet, add no games.
Behind --panel-floor: when the gauntlet ran and any GauntletResult.per_baseline win rate is 0.0 (sweep loss vs any anchor), trigger the existing rollback machinery (revert-first ordering per L4030-4063) with reason prefix panel-floor: and flip promoted imps to regression-rollback.
Fail-open by construction: gauntlet crash, empty registry, or --fitness-mode parent → floor inert (the existing defense-in-depth try/except at L3837-3923 must keep protecting the committed promotion — a monitoring layer must never block an already-committed promotion on its own failure). Add a startup log line when panel-floor is on but the registry is empty.
Document in SKILL.md that production soaks should not enable --regression-rule one-sided without --panel-floor + registered baselines.
Existing context
- Gauntlet log-only block: scripts/evolve.py L3825-3832 (comment: "The gauntlet does NOT change the promotion gate in this step — it LOGS + records ... so a later step can drive gating off it"), wrapped defense-in-depth L3837-3923.
- Rollback consumption: scripts/evolve.py L4030-4063 —
if regression_result.rolled_back: flips promoted imps to _REGRESSION_ROLLBACK, revert-first ordering (git revert on a clean tree restores the pointer via reverse diff), pointer-restore fallback.
run_baseline_gauntlet (src/orchestrator/evolve.py L847-976) returns GauntletResult.per_baseline win rates + mean_win_rate; skips a baseline whose version IS the candidate; empty registry → mean_win_rate=0.0.
- Joint null false-positive with the posterior bar ≈ 26% at K=3 anchors (each anchor sweep-loss ≈ 3.1% under fair coin) — but frozen anchors cannot cycle, so sustained drift cannot pass them (AlphaStar max-min rationale).
data/baselines.json is ABSENT today — EJ.7 registers anchors; unit tests use fakes. Do NOT create it early.
Files to modify/create
scripts/evolve.py (--panel-floor flag + floor consumption; panel-floor: rollback rows; startup log when on-but-empty)
.claude/skills/improve-bot-evolve/SKILL.md (pairing warning: one-sided ⇒ requires panel-floor + baselines)
- tests
Done when
Unit tests with fake gauntlet results cover: sweep loss vs one anchor → rollback via the same revert path; all anchors ≥ 1 win → no effect; gauntlet crash → promotion stands (fail-open); flag OFF → byte-identical. Integration test drives the loop with fitness-mode both + fake baselines and asserts floor rollback lands a panel-floor: reason row. uv run pytest / mypy src bots --strict / ruff check . clean; test count ≥ baseline.
Flags (recommended)
--reviewers code
Depends on
#284 (EJ.3 — reuses the one-sided rollback machinery / is the mandatory pairing for the relaxed regression bar)
Parallel-safe with
none — strictly sequential (edits scripts/evolve.py shared with sibling EJ steps; also has a hard Depends on: EJ.3; plan §4 mandates serial execution)
Produces
--panel-floor flag + floor consumption in scripts/evolve.py; panel-floor: rollback rows; SKILL.md pairing warning; tests
Operator workflow notes (autonomous + UI-bundle + parallel)
- Runs end-to-end via
/build-step; the Done when is the gate.
- No
--ui — backend + SKILL.md doc.
- Serial + depends on EJ.3.
Synced from documentation/plans/evolve-judging-plan.md by /repo-sync at c6099d9
Build step 4 of 8 — Phase EJ (umbrella #281)
Build doc: documentation/plans/evolve-judging-plan.md §7 Step EJ.4
What to build
EJ.3's honest cost is regression catch at p=0.35 dropping 76.5% → 42.8%, and the "caught later" backstop is false (the next generation compares against the same weakened bar) — so the one-sided rule needs a drift backstop that cannot cycle. The EL gauntlet already plays the promoted version vs each frozen anchor under
--fitness-mode bothand is explicitly log-only ("a later step can drive gating off it", scripts/evolve.py L3825-3832). This is that later step — consume the existing gauntlet, add no games.Behind
--panel-floor: when the gauntlet ran and anyGauntletResult.per_baselinewin rate is0.0(sweep loss vs any anchor), trigger the existing rollback machinery (revert-first ordering per L4030-4063) with reason prefixpanel-floor:and flip promoted imps toregression-rollback.Fail-open by construction: gauntlet crash, empty registry, or
--fitness-mode parent→ floor inert (the existing defense-in-depth try/except at L3837-3923 must keep protecting the committed promotion — a monitoring layer must never block an already-committed promotion on its own failure). Add a startup log line when panel-floor is on but the registry is empty.Document in SKILL.md that production soaks should not enable
--regression-rule one-sidedwithout--panel-floor+ registered baselines.Existing context
if regression_result.rolled_back:flips promoted imps to_REGRESSION_ROLLBACK, revert-first ordering (git revert on a clean tree restores the pointer via reverse diff), pointer-restore fallback.run_baseline_gauntlet(src/orchestrator/evolve.py L847-976) returnsGauntletResult.per_baselinewin rates +mean_win_rate; skips a baseline whose version IS the candidate; empty registry →mean_win_rate=0.0.data/baselines.jsonis ABSENT today — EJ.7 registers anchors; unit tests use fakes. Do NOT create it early.Files to modify/create
scripts/evolve.py(--panel-floorflag + floor consumption;panel-floor:rollback rows; startup log when on-but-empty).claude/skills/improve-bot-evolve/SKILL.md(pairing warning: one-sided ⇒ requires panel-floor + baselines)Done when
Unit tests with fake gauntlet results cover: sweep loss vs one anchor → rollback via the same revert path; all anchors ≥ 1 win → no effect; gauntlet crash → promotion stands (fail-open); flag OFF → byte-identical. Integration test drives the loop with fitness-mode both + fake baselines and asserts floor rollback lands a
panel-floor:reason row.uv run pytest/mypy src bots --strict/ruff check .clean; test count ≥ baseline.Flags (recommended)
--reviewers codeDepends on
#284 (EJ.3 — reuses the one-sided rollback machinery / is the mandatory pairing for the relaxed regression bar)
Parallel-safe with
none — strictly sequential (edits
scripts/evolve.pyshared with sibling EJ steps; also has a hardDepends on: EJ.3; plan §4 mandates serial execution)Produces
--panel-floorflag + floor consumption inscripts/evolve.py;panel-floor:rollback rows; SKILL.md pairing warning; testsOperator workflow notes (autonomous + UI-bundle + parallel)
/build-step; theDone whenis the gate.--ui— backend + SKILL.md doc.Synced from documentation/plans/evolve-judging-plan.md by /repo-sync at c6099d9