From ba135f18f38fa4006e38250f6f95fb532093de75 Mon Sep 17 00:00:00 2001 From: Coy Geek <65363919+coygeek@users.noreply.github.com> Date: Sat, 20 Jun 2026 21:30:32 -0700 Subject: [PATCH] fix: match required check terminal conclusions Treat GitHub skipped and neutral required-check conclusions as passing terminal states, and classify startup failures as failed so queue evaluation matches GitHub. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- src/agent_merge_queue/cli.py | 6 ++++-- tests/test_cli.py | 13 +++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/agent_merge_queue/cli.py b/src/agent_merge_queue/cli.py index 9395b92..8c0b83c 100755 --- a/src/agent_merge_queue/cli.py +++ b/src/agent_merge_queue/cli.py @@ -97,8 +97,10 @@ "ERROR", "FAILURE", "STALE", + "STARTUP_FAILURE", "TIMED_OUT", } +PASSED_CHECK_STATES = {"NEUTRAL", "SKIPPED", "SUCCESS"} MERGEABILITY_RETRIES = 6 REVIEW_THREADS_QUERY = """ query($owner: String!, $name: String!, $number: Int!) { @@ -166,7 +168,7 @@ def check_states(checks: Iterable[dict[str, Any]]) -> dict[str, str]: state = normalize_check_state(check) # A newly queued run may not have a timestamp yet. Fail closed instead # of letting an older success hide that pending rerun. - order = "\uffff" if not timestamp and state != "SUCCESS" else timestamp + order = "\uffff" if not timestamp and state not in PASSED_CHECK_STATES else timestamp candidate = (order, index, state) if name not in grouped or candidate[:2] > grouped[name][:2]: grouped[name] = candidate @@ -176,7 +178,7 @@ def check_states(checks: Iterable[dict[str, Any]]) -> dict[str, str]: state = value[2] if state in FAILED_CHECK_STATES: result[name] = "failed" - elif state == "SUCCESS": + elif state in PASSED_CHECK_STATES: result[name] = "passed" else: result[name] = "pending" diff --git a/tests/test_cli.py b/tests/test_cli.py index b876f50..b86f942 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1574,14 +1574,23 @@ def test_integration_snapshot_uses_exact_commit_check_fallback(self) -> None: self.assertEqual(value.checks["CI"], "passed") client.commit_check_runs.assert_called_once_with(head_sha) - def test_required_checks_do_not_accept_skipped(self) -> None: + def test_required_checks_accept_passing_terminal_conclusions(self) -> None: states = check_states( [ {"name": "CI", "conclusion": "SUCCESS"}, {"name": "Review", "conclusion": "SKIPPED"}, + {"name": "Lint", "conclusion": "NEUTRAL"}, ] ) - self.assertEqual(states, {"CI": "passed", "Review": "pending"}) + self.assertEqual( + states, + {"CI": "passed", "Review": "passed", "Lint": "passed"}, + ) + + def test_required_checks_treat_startup_failure_as_failed(self) -> None: + states = check_states([{"name": "CI", "conclusion": "STARTUP_FAILURE"}]) + + self.assertEqual(states, {"CI": "failed"}) def test_latest_check_rerun_wins(self) -> None: states = check_states(