"""Offline checks for the generation-5 stage configuration and gates.""" import unittest import generation5 def flag_value(flags: list[str], name: str) -> str: index = flags.index(name) return flags[index + 1] class Generation5ConfigTests(unittest.TestCase): def test_default_evaluation_seeds_are_multiple_and_unique(self) -> None: self.assertEqual(len(generation5.DEFAULT_EVALUATION_SEEDS), 3) self.assertEqual(len(set(generation5.DEFAULT_EVALUATION_SEEDS)), 3) def test_stage_sequence_and_lineage(self) -> None: self.assertEqual([stage["number"] for stage in generation5.STAGES], [4, 5, 6]) state = generation5.fresh_state() checkpoint = generation5.resume_checkpoint( state, stage_index=0, attempt=0, foundation=generation5.FOUNDATION_CHECKPOINT ) self.assertEqual(checkpoint, generation5.FOUNDATION_CHECKPOINT) def test_start_state_probabilities_leave_random_remainder(self) -> None: for stage in generation5.STAGES: flags = stage["flags"] total = sum( float(flag_value(flags, name)) if name in flags else 0.0 for name in ( "--kickoff-chance", "--near-goal-chance", "--air-drill-chance", "--air-intercept-chance", "--wall-play-chance", "--rebound-chance", ) ) with self.subTest(stage=stage["name"]): self.assertLessEqual(total, 1.0) def test_only_league_stage_requests_pool(self) -> None: self.assertEqual( [stage["name"] for stage in generation5.STAGES if stage.get("league_pool")], ["league"], ) self.assertEqual(flag_value(generation5.STAGES[2]["flags"], "--opponent-mode"), "league") def test_league_stage_enables_wall_and_rebound_states_after_intercepts(self) -> None: self.assertEqual(flag_value(generation5.STAGES[2]["flags"], "--wall-play-chance"), "0.10") self.assertEqual(flag_value(generation5.STAGES[2]["flags"], "--rebound-chance"), "0.10") self.assertNotIn("--wall-play-chance", generation5.STAGES[0]["flags"]) self.assertNotIn("--rebound-chance", generation5.STAGES[1]["flags"]) def test_telemetry_floors_fail_closed_on_missing_metric(self) -> None: ok, failures = generation5.telemetry_passes( generation5.STAGES[0], {"rollout/upright_fraction": 1.0} ) self.assertFalse(ok) self.assertIn("rollout/forward_motion_fraction missing", failures) def test_match_gate_only_blocks_clear_regression(self) -> None: self.assertTrue( generation5.match_passes({"wins_a": 40, "wins_b": 54, "episodes": 100}) ) self.assertFalse( generation5.match_passes({"wins_a": 40, "wins_b": 55, "episodes": 100}) ) def test_evaluation_goal_rate_counts_either_scorer(self) -> None: self.assertEqual( generation5.evaluation_goal_rate( {"wins_a": 45, "wins_b": 35, "draws": 20, "episodes": 100} ), 0.8, ) def test_physical_side_imbalance_exposes_broken_player_slot(self) -> None: self.assertEqual( generation5.physical_side_imbalance( { "physical_team_wins": {"team_0": 90, "team_1": 5}, "episodes": 100, } ), 0.85, ) self.assertEqual( generation5.physical_side_imbalance( { "physical_team_wins": {"team_0": 42, "team_1": 38}, "episodes": 100, } ), 0.04, ) if __name__ == "__main__": unittest.main()