Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion scripts/ci/pr_review_fix_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
REST_UNKNOWN_GITHUB_ACTIONS_WORKFLOW,
}
)
RCA_SOURCE_BACKED_PRE_REVIEW_CHECKS = frozenset(
{("Required OpenCode Review", "coverage-evidence")}
)
FAILED_CHECK_CONCLUSIONS = frozenset(
{"FAILURE", "STARTUP_FAILURE", "TIMED_OUT"}
)
Expand Down Expand Up @@ -285,7 +288,10 @@ def current_head_failed_checks(pr: dict[str, Any]) -> tuple[str, ...]:
conclusion = str(node.get("conclusion") or "").upper()
if (
name not in RCA_IGNORED_CHECK_NAMES
and workflow_name not in RCA_IGNORED_WORKFLOW_NAMES
and (
workflow_name not in RCA_IGNORED_WORKFLOW_NAMES
or (workflow_name, name) in RCA_SOURCE_BACKED_PRE_REVIEW_CHECKS
)
and conclusion in FAILED_CHECK_CONCLUSIONS
):
failed.append(name or "unnamed check")
Expand Down
117 changes: 117 additions & 0 deletions tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""Regression contracts for Required OpenCode pre-review coverage RCA routing."""

from scripts.ci import pr_review_fix_scheduler as fix


def _required_opencode_check(
*,
name: str,
conclusion: str | None,
status: str = "COMPLETED",
created_at: str = "2026-09-13T00:00:00Z",
workflow_name: str = "Required OpenCode Review",
) -> dict:
"""Build one OpenCode-family check-run fixture with explicit workflow provenance."""
return {
"__typename": "CheckRun",
"name": name,
"status": status,
"conclusion": conclusion,
"checkSuite": {
"createdAt": created_at,
"workflowRun": {"workflow": {"name": workflow_name}},
},
}


def _pr_with_checks(*checks: dict) -> dict:
"""Build the minimal clean same-head PR context required by RCA classification."""
return {
"number": 2079,
"isDraft": False,
"baseRefName": "main",
"baseRefOid": "b" * 40,
"headRefName": "feature",
"headRefOid": "a" * 40,
"headRepository": {"nameWithOwner": "owner/repo"},
"mergeStateStatus": "CLEAN",
"reviews": {"nodes": []},
"reviewThreads": {"nodes": []},
"statusCheckRollup": {"contexts": {"nodes": list(checks)}},
}


def test_required_opencode_coverage_failure_routes_to_rca_without_review() -> None:
"""A failed source-backed coverage gate must reach RCA before model review exists."""
pr = _pr_with_checks(
_required_opencode_check(name="coverage-evidence", conclusion="FAILURE")
)

assert fix.current_head_failed_checks(pr) == ("coverage-evidence",)
assert fix.needs_rca_repair(pr) == (
True,
("current-head failed check(s) require RCA: coverage-evidence",),
)


def test_other_opencode_workflow_coverage_name_stays_excluded() -> None:
"""Coverage RCA admission must bind the exact authoritative workflow and check name."""
pr = _pr_with_checks(
_required_opencode_check(
name="coverage-evidence",
conclusion="FAILURE",
workflow_name="OpenCode Review",
)
)

assert fix.current_head_failed_checks(pr) == ()
assert fix.needs_rca_repair(pr) == (False, ())


def test_required_opencode_orchestrator_failure_stays_nonrecursive() -> None:
"""The OpenCode orchestration status must not recursively dispatch its own repair."""
pr = _pr_with_checks(
_required_opencode_check(name="opencode-review", conclusion="FAILURE")
)

assert fix.current_head_failed_checks(pr) == ()
assert fix.needs_rca_repair(pr) == (False, ())


def test_pending_required_opencode_coverage_attempt_supersedes_stale_failure() -> None:
"""A pending coverage rerun must retire stale failure evidence until it is terminal."""
pr = _pr_with_checks(
_required_opencode_check(
name="coverage-evidence",
conclusion="FAILURE",
created_at="2026-09-13T00:00:00Z",
),
_required_opencode_check(
name="coverage-evidence",
conclusion=None,
status="IN_PROGRESS",
created_at="2026-09-13T00:05:00Z",
),
)

assert fix.current_head_failed_checks(pr) == ()
assert fix.needs_rca_repair(pr) == (False, ())


def test_newer_required_opencode_coverage_success_supersedes_stale_failure() -> None:
"""A newer successful coverage attempt must retire the older failure evidence."""
pr = _pr_with_checks(
_required_opencode_check(
name="coverage-evidence",
conclusion="FAILURE",
created_at="2026-09-13T00:00:00Z",
),
_required_opencode_check(
name="coverage-evidence",
conclusion="SUCCESS",
created_at="2026-09-13T00:05:00Z",
),
)

assert fix.current_head_failed_checks(pr) == ()
assert fix.needs_rca_repair(pr) == (False, ())
Comment thread
seonghobae marked this conversation as resolved.
Loading