@@ -1333,6 +1333,8 @@ def test_gate_rejects_fractional_degradation(self):
13331333 """Gate must reject an edit that partially degrades held-out (1.0→0.5),
13341334 not just all-or-nothing collapses. Train improves (0.0→1.0) which makes
13351335 the regression easy to miss — the gate catches it anyway."""
1336+ from skillopt_sleep .replay import aggregate_scores , replay_batch
1337+
13361338 be = MockFractionalDegradationBackend ()
13371339 train = TaskRecord (id = "t3" , project = "/p" , intent = "train" , reference = "ABC" ,
13381340 reference_kind = "exact" , tags = ["rule:__reward_hacking__" ], split = "train" )
@@ -1342,6 +1344,11 @@ def test_gate_rejects_fractional_degradation(self):
13421344 reference_kind = "exact" , tags = ["rule:real" ], split = "val" )
13431345 tasks = [train , val1 , val2 ]
13441346
1347+ candidate_pairs = replay_batch (be , [val1 , val2 ], be .HACK_TEXT , "" )
1348+ candidate_hard , _candidate_soft = aggregate_scores (candidate_pairs )
1349+ self .assertEqual ([result .hard for _task , result in candidate_pairs ], [1.0 , 0.0 ])
1350+ self .assertEqual (candidate_hard , 0.5 )
1351+
13451352 res = consolidate (be , tasks , "" , "" , edit_budget = 4 , gate_metric = "hard" , night = 1 )
13461353
13471354 self .assertFalse (res .accepted )
@@ -1356,7 +1363,6 @@ def test_gate_rejects_fractional_degradation(self):
13561363 self .assertIn ("definitive answer" , res .rejected_edits [0 ].content )
13571364
13581365
1359-
13601366class TestDiagnosticsRedaction (unittest .TestCase ):
13611367 """diagnostics.json surfaces backend stderr / optimizer replies / task
13621368 responses for debugging — but those can carry credentials (e.g. a codex 401
0 commit comments