@@ -533,6 +533,73 @@ def test_refinement_falls_back_on_malformed_response(
533533 assert result == groups
534534 mock_call_llm .assert_called_once ()
535535
536+ @patch ("pr_split.planner.client._call_llm" )
537+ def test_refinement_that_drops_hunks_is_rejected (
538+ self , mock_call_llm : MagicMock , monkeypatch : pytest .MonkeyPatch
539+ ) -> None :
540+ monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
541+ monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
542+ parsed = parse_diff (_TWO_FILE_DIFF )
543+ settings = Settings (
544+ partition_strategy = PartitionStrategy .GRAPH ,
545+ min_loc = 5 ,
546+ max_loc = 10 ,
547+ max_refinement_iterations = 2 ,
548+ )
549+ # Only a.py survives: b.py's hunk is gone from the plan.
550+ mock_call_llm .return_value = RawToolOutput (
551+ groups = [
552+ {
553+ "id" : "pr-1" ,
554+ "title" : "feat: add a" ,
555+ "description" : "Only a" ,
556+ "depends_on" : [],
557+ "assignments" : [
558+ {"file_path" : "a.py" , "assignment_type" : "whole_file" , "hunk_indices" : [0 ]}
559+ ],
560+ "estimated_loc" : 3 ,
561+ }
562+ ]
563+ )
564+
565+ groups = _undersized_groups ()
566+ with patch ("pr_split.planner.client.logger" ) as mock_logger :
567+ result = _refine_plan_with_llm (groups , parsed , settings , system = "system" )
568+
569+ assert result is groups
570+ assert [g .id for g in result ] == ["pr-1" , "pr-2" ]
571+ mock_call_llm .assert_called_once ()
572+ warning = mock_logger .warning .call_args [0 ][0 ]
573+ assert "produced an invalid plan" in warning
574+ assert "b.py[0] not assigned to any group" in warning
575+
576+ @patch ("pr_split.planner.client._call_llm" )
577+ def test_refinement_that_does_not_improve_is_rejected (
578+ self , mock_call_llm : MagicMock , monkeypatch : pytest .MonkeyPatch
579+ ) -> None :
580+ monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
581+ monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
582+ parsed = parse_diff (_TWO_FILE_DIFF )
583+ settings = Settings (
584+ partition_strategy = PartitionStrategy .GRAPH ,
585+ min_loc = 5 ,
586+ max_loc = 10 ,
587+ max_refinement_iterations = 3 ,
588+ )
589+ # Same two undersized groups handed straight back.
590+ mock_call_llm .return_value = RawToolOutput (
591+ groups = _groups_to_raw_dicts (_undersized_groups ()) # type: ignore[typeddict-item]
592+ )
593+
594+ groups = _undersized_groups ()
595+ with patch ("pr_split.planner.client.logger" ) as mock_logger :
596+ result = _refine_plan_with_llm (groups , parsed , settings , system = "system" )
597+
598+ assert result is groups
599+ mock_call_llm .assert_called_once ()
600+ warning = mock_logger .warning .call_args [0 ][0 ]
601+ assert "did not reduce violations (2 -> 2)" in warning
602+
536603 def test_no_refinement_when_min_loc_is_none (self , monkeypatch : pytest .MonkeyPatch ) -> None :
537604 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
538605 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
0 commit comments