From c002c785e340be3af6d776efce05c528cd32a075 Mon Sep 17 00:00:00 2001 From: rotatedcoded Date: Tue, 14 Jul 2026 17:41:50 +0700 Subject: [PATCH] Make elite guarded runtime more conservative --- src/ml/direct_ranker_elite_guarded_policy.py | 18 ++++++++++++++++-- tests/test_direct_ranker_guarded_hybrid.py | 11 +++++++++++ tests/test_elite_guarded_runtime_policy.py | 15 +++++++++------ tests/test_elite_guarded_runtime_wiring.py | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/ml/direct_ranker_elite_guarded_policy.py b/src/ml/direct_ranker_elite_guarded_policy.py index c46b05f..d320664 100644 --- a/src/ml/direct_ranker_elite_guarded_policy.py +++ b/src/ml/direct_ranker_elite_guarded_policy.py @@ -26,6 +26,7 @@ class EliteGuardrails: max_eval_gap_cp: float max_candidate_rank: int + min_probability: float = 0.0 allow_missing_eval_gap: bool = False allow_missing_candidate_rank: bool = False @@ -52,10 +53,23 @@ def elite_guarded_policy_label(move_policy: str | None) -> str: def guardrails_for_policy(move_policy: str | None) -> EliteGuardrails: policy = str(move_policy or "").strip() + # Step 35B: + # Elite policies are model-first only for truly engine-like personas, but + # manual/runtime use must still avoid feeling like unrestricted engine play. + # Keep the model behind conservative per-move guardrails so unsafe or + # low-confidence suggestions fall back to the Statistical selector. if policy == DIRECT_RANKER_ELITE_GUARDED_STRICT: - return EliteGuardrails(max_eval_gap_cp=100.0, max_candidate_rank=5) + return EliteGuardrails( + max_eval_gap_cp=50.0, + max_candidate_rank=3, + min_probability=0.35, + ) if policy == DIRECT_RANKER_ELITE_GUARDED: - return EliteGuardrails(max_eval_gap_cp=200.0, max_candidate_rank=5) + return EliteGuardrails( + max_eval_gap_cp=75.0, + max_candidate_rank=5, + min_probability=0.30, + ) raise ValueError(f"not an elite guarded policy: {move_policy!r}") def recommended_elite_runtime_policy( diff --git a/tests/test_direct_ranker_guarded_hybrid.py b/tests/test_direct_ranker_guarded_hybrid.py index ee12555..c4d6756 100644 --- a/tests/test_direct_ranker_guarded_hybrid.py +++ b/tests/test_direct_ranker_guarded_hybrid.py @@ -228,3 +228,14 @@ def test_guarded_hybrid_audit_counts_eligible_and_fallbacks() -> None: "engine_gap_too_high": 1, "outside_top_engine_rank": 1, } + +def test_low_probability_falls_back_when_config_requires_confidence() -> None: + result = guardrails_for_shadow_result( + shadow_result(ml_top_probability=0.12), + board=chess.Board(), + release_report=ready_report(), + config=GuardedHybridConfig(min_probability=0.30), + ) + + assert result["passed"] is False + assert result["reason"] == "probability_too_low" diff --git a/tests/test_elite_guarded_runtime_policy.py b/tests/test_elite_guarded_runtime_policy.py index 750cef7..dae247b 100644 --- a/tests/test_elite_guarded_runtime_policy.py +++ b/tests/test_elite_guarded_runtime_policy.py @@ -13,7 +13,7 @@ def test_standard_elite_policy_allows_safe_model_first_move() -> None: model_move="Nf3", fallback_move="e4", candidate_rank=3, - eval_gap_cp=90, + eval_gap_cp=60, ) assert decision.selected_move == "Nf3" assert decision.used_model_move is True @@ -35,12 +35,12 @@ def test_strict_elite_policy_uses_tighter_gap_guardrail() -> None: normal_ok, _ = should_use_model_move( move_policy=DIRECT_RANKER_ELITE_GUARDED, candidate_rank=2, - eval_gap_cp=150, + eval_gap_cp=60, ) strict_ok, strict_reason = should_use_model_move( move_policy=DIRECT_RANKER_ELITE_GUARDED_STRICT, candidate_rank=2, - eval_gap_cp=150, + eval_gap_cp=60, ) assert normal_ok is True assert strict_ok is False @@ -79,6 +79,9 @@ def test_non_elite_status_does_not_get_elite_policy() -> None: def test_guardrail_values_are_documented_by_tests() -> None: normal = guardrails_for_policy(DIRECT_RANKER_ELITE_GUARDED) strict = guardrails_for_policy(DIRECT_RANKER_ELITE_GUARDED_STRICT) - assert normal.max_eval_gap_cp == 200 - assert strict.max_eval_gap_cp == 100 - assert normal.max_candidate_rank == strict.max_candidate_rank == 5 + assert normal.max_eval_gap_cp == 75 + assert strict.max_eval_gap_cp == 50 + assert normal.max_candidate_rank == 5 + assert strict.max_candidate_rank == 3 + assert normal.min_probability == 0.30 + assert strict.min_probability == 0.35 diff --git a/tests/test_elite_guarded_runtime_wiring.py b/tests/test_elite_guarded_runtime_wiring.py index d0ea00f..fed93b2 100644 --- a/tests/test_elite_guarded_runtime_wiring.py +++ b/tests/test_elite_guarded_runtime_wiring.py @@ -236,3 +236,21 @@ def test_ui_elite_helpers_accept_original_worker_call_signature() -> None: assert report["assessment"]["status"] == "release_candidate_ready" assert config.require_release_gate_ready is False assert config.max_engine_rank == 3 + +def test_worker_elite_config_is_conservative() -> None: + strict = workers._elite_guarded_config( + BOT_MOVE_POLICY_DIRECT_RANKER_ELITE_GUARDED_STRICT, + ) + normal = workers._elite_guarded_config( + BOT_MOVE_POLICY_DIRECT_RANKER_ELITE_GUARDED, + ) + + assert strict.require_release_gate_ready is False + assert strict.max_engine_rank == 3 + assert strict.max_score_gap_cp == 50 + assert strict.min_probability == 0.35 + + assert normal.require_release_gate_ready is False + assert normal.max_engine_rank == 5 + assert normal.max_score_gap_cp == 75 + assert normal.min_probability == 0.30