@@ -876,6 +876,129 @@ async def fake_run_agent(_prompt, _system_prompt, _config, cwd=None, trajectory=
876876
877877 mock_ensure_pr .assert_called_once ()
878878
879+ @patch ("runner.run_agent" )
880+ @patch ("pipeline.build_system_prompt" )
881+ @patch ("pipeline.discover_project_config" )
882+ @patch ("repo.setup_repo" )
883+ @patch ("pipeline.task_span" )
884+ def test_jira_card_not_moved_to_in_review_on_build_failure (
885+ self ,
886+ mock_task_span ,
887+ mock_setup_repo ,
888+ _mock_discover ,
889+ _mock_build_prompt ,
890+ mock_run_agent ,
891+ monkeypatch ,
892+ ):
893+ """review blocker #9a: ensure_pr opens a PR even on a FAILED build (so the
894+ human sees the broken diff), so the Jira In Progress → In Review transition
895+ must gate on build_passed — not merely on pr_url — or the board lies that
896+ the work is ready for review. Mirrors the Linear success-only twin."""
897+ monkeypatch .setenv ("GITHUB_TOKEN" , "ghp_test" )
898+ monkeypatch .setenv ("AWS_REGION" , "us-east-1" )
899+ mock_setup_repo .return_value = RepoSetup (
900+ repo_dir = "/workspace/repo" ,
901+ branch = "bgagent/test/branch" ,
902+ build_before = True ,
903+ )
904+
905+ async def fake_run_agent (_prompt , _system_prompt , _config , cwd = None , trajectory = None ):
906+ return AgentResult (status = "success" , turns = 2 , cost_usd = 0.01 , num_turns = 2 )
907+
908+ mock_run_agent .side_effect = fake_run_agent
909+
910+ mock_span = MagicMock ()
911+ mock_span .__enter__ = MagicMock (return_value = mock_span )
912+ mock_span .__exit__ = MagicMock (return_value = False )
913+ mock_task_span .return_value = mock_span
914+
915+ mock_transition = MagicMock ()
916+ with (
917+ patch ("pipeline.ensure_committed" , return_value = False ),
918+ # FAILED build — ensure_pr still opens the PR below.
919+ patch ("pipeline.verify_build" , return_value = VerifyOutcome (passed = False )),
920+ patch ("pipeline.verify_lint" , return_value = VerifyOutcome (passed = True )),
921+ patch ("pipeline.ensure_pr" , return_value = "https://github.com/o/r/pull/9" ),
922+ patch ("pipeline.transition_pr_opened" , mock_transition ),
923+ patch ("pipeline.get_disk_usage" , return_value = 0 ),
924+ patch ("pipeline.print_metrics" ),
925+ patch ("pipeline.task_state" ) as mock_task_state_mod ,
926+ ):
927+ mock_task_state_mod .get_task = MagicMock (return_value = {"status" : "RUNNING" })
928+ mock_task_state_mod .TaskFetchError = Exception # type: ignore[attr-defined]
929+ from pipeline import run_task
930+
931+ run_task (
932+ repo_url = "o/r" ,
933+ task_description = "x" ,
934+ github_token = "ghp_test" ,
935+ aws_region = "us-east-1" ,
936+ task_id = "t-jira-failbuild" ,
937+ channel_source = "jira" ,
938+ channel_metadata = {"jira_issue_key" : "ABC-1" },
939+ )
940+ # PR opened, but the Jira card was NOT advanced to In Review on the red build.
941+ mock_transition .assert_not_called ()
942+
943+ @patch ("runner.run_agent" )
944+ @patch ("pipeline.build_system_prompt" )
945+ @patch ("pipeline.discover_project_config" )
946+ @patch ("repo.setup_repo" )
947+ @patch ("pipeline.task_span" )
948+ def test_jira_card_moved_to_in_review_on_build_success (
949+ self ,
950+ mock_task_span ,
951+ mock_setup_repo ,
952+ _mock_discover ,
953+ _mock_build_prompt ,
954+ mock_run_agent ,
955+ monkeypatch ,
956+ ):
957+ """The positive twin: a PASSING build DOES move the Jira card to In Review."""
958+ monkeypatch .setenv ("GITHUB_TOKEN" , "ghp_test" )
959+ monkeypatch .setenv ("AWS_REGION" , "us-east-1" )
960+ mock_setup_repo .return_value = RepoSetup (
961+ repo_dir = "/workspace/repo" ,
962+ branch = "bgagent/test/branch" ,
963+ build_before = True ,
964+ )
965+
966+ async def fake_run_agent (_prompt , _system_prompt , _config , cwd = None , trajectory = None ):
967+ return AgentResult (status = "success" , turns = 2 , cost_usd = 0.01 , num_turns = 2 )
968+
969+ mock_run_agent .side_effect = fake_run_agent
970+
971+ mock_span = MagicMock ()
972+ mock_span .__enter__ = MagicMock (return_value = mock_span )
973+ mock_span .__exit__ = MagicMock (return_value = False )
974+ mock_task_span .return_value = mock_span
975+
976+ mock_transition = MagicMock ()
977+ with (
978+ patch ("pipeline.ensure_committed" , return_value = False ),
979+ patch ("pipeline.verify_build" , return_value = VerifyOutcome (passed = True )),
980+ patch ("pipeline.verify_lint" , return_value = VerifyOutcome (passed = True )),
981+ patch ("pipeline.ensure_pr" , return_value = "https://github.com/o/r/pull/9" ),
982+ patch ("pipeline.transition_pr_opened" , mock_transition ),
983+ patch ("pipeline.get_disk_usage" , return_value = 0 ),
984+ patch ("pipeline.print_metrics" ),
985+ patch ("pipeline.task_state" ) as mock_task_state_mod ,
986+ ):
987+ mock_task_state_mod .get_task = MagicMock (return_value = {"status" : "RUNNING" })
988+ mock_task_state_mod .TaskFetchError = Exception # type: ignore[attr-defined]
989+ from pipeline import run_task
990+
991+ run_task (
992+ repo_url = "o/r" ,
993+ task_description = "x" ,
994+ github_token = "ghp_test" ,
995+ aws_region = "us-east-1" ,
996+ task_id = "t-jira-okbuild" ,
997+ channel_source = "jira" ,
998+ channel_metadata = {"jira_issue_key" : "ABC-1" },
999+ )
1000+ mock_transition .assert_called_once ()
1001+
8791002 @patch ("runner.run_agent" )
8801003 @patch ("pipeline.build_system_prompt" )
8811004 @patch ("pipeline.discover_project_config" )
0 commit comments