Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions requirements-pip-audit-ci-hashes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ packaging==26.2 \
# via
# pip-audit
# pip-requirements-parser
pip==26.1.2 \
--hash=sha256:382ff9f685ee3bc25864f820aa50505825f10f5458ffff07e30a6d96e5715cab \
--hash=sha256:f49cd134c61cf2fd75e0ce2676db03e4054504a5a4986d00f8299ae632dc4605
pip==26.2.1 \
--hash=sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e \
--hash=sha256:f6ad667e89a1fe78046c8f13232b247200f5258d7828f3f7883d660878e0813f
# via pip-api
pip-api==0.0.34 \
--hash=sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb \
Expand Down
3 changes: 2 additions & 1 deletion scripts/ci/organization_commercial_readiness_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class GitHubClient:
"""Use the GitHub CLI as an authenticated, bounded REST transport."""

def __init__(self, token: str, *, timeout_seconds: int = 60) -> None:
"""Initialize the client with one non-empty GitHub credential."""
if not token:
raise GitHubError("GH_TOKEN is required for organization coordination")
self._token = token
Expand Down Expand Up @@ -853,4 +854,4 @@ def main(


if __name__ == "__main__": # pragma: no cover - exercised through main()
raise SystemExit(main())
raise SystemExit(main())
71 changes: 69 additions & 2 deletions scripts/ci/pr_review_merge_scheduler.py
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@
"deterministic fallback approval",
"did not emit a usable current-head control block",
)
COVERAGE_REVIEW_MARKERS = (
"coverage evidence did not pass",
"coverage-evidence",
"required test/docstring evidence",
)
LAST_PUSH_APPROVAL_RESTAMP_MESSAGE = "chore: refresh head for last-push approval"


Expand Down Expand Up @@ -1277,6 +1282,33 @@ def has_current_head_changes_requested(pr: dict[str, Any]) -> bool:
return current_head_review_state(pr, "CHANGES_REQUESTED")


def current_head_coverage_change_request(pr: dict[str, Any]) -> bool:
"""Return whether the latest current-head request is only a coverage gate."""
for review in reversed((pr.get("reviews") or {}).get("nodes") or []):
if not is_opencode_review(review) or not review_matches_current_head(review, pr):
continue
if (review.get("state") or "").upper() != "CHANGES_REQUESTED":
return False
body = (review.get("body") or "").lower()
return all(marker in body for marker in COVERAGE_REVIEW_MARKERS)
return False
Comment thread
seonghobae marked this conversation as resolved.


def coverage_evidence_state(pr: dict[str, Any]) -> str:
"""Return missing, running, complete, or failed for the latest coverage gate."""
for node in reversed(context_nodes(pr)):
name = (node.get("name") or node.get("context") or "").lower()
if name != "coverage-evidence":
continue
status = (node.get("status") or node.get("state") or "").upper()
if status in RUNNING_CHECK_STATES:
return "running"
if node.get("__typename") == "CheckRun":
return "complete" if (node.get("conclusion") or "").upper() == "SUCCESS" else "failed"
return "complete" if status == "SUCCESS" else "failed"
return "missing"


def stale_opencode_change_request_ids(pr: dict[str, Any]) -> list[int]:
"""Return dismissible automated change requests tied to previous heads."""
review_ids: list[int] = []
Expand Down Expand Up @@ -1459,8 +1491,17 @@ def dismiss_stale_opencode_change_requests(repo: str, pr: dict[str, Any], *, dry
return len(review_ids)


def failed_status_checks(pr: dict[str, Any]) -> list[str]:
"""Return failing check or status context names from the PR rollup."""
def failed_status_checks(
pr: dict[str, Any],
*,
ignore_opencode: bool = False,
) -> list[str]:
"""Return failing check or status context names from the PR rollup.

``ignore_opencode`` is reserved for the authenticated coverage-only retry
path: the previous OpenCode run is expected to be failing there because it
published the current-head coverage change request being retried.
"""
failed: list[str] = []
latest_check_runs: dict[
tuple[str, str],
Expand Down Expand Up @@ -1501,6 +1542,8 @@ def failed_status_checks(pr: dict[str, Any]) -> list[str]:
for _, _, node in sorted(latest_check_runs.values(), key=lambda item: item[1]):
conclusion = (node.get("conclusion") or "").upper()
if conclusion in FAILED_CHECK_CONCLUSIONS:
if ignore_opencode and is_opencode_context(node):
continue
if is_strix_context(node) and "strix" in successful_status_contexts:
continue
if is_opencode_context(node) and "opencode-review" in successful_status_contexts:
Expand All @@ -1509,6 +1552,8 @@ def failed_status_checks(pr: dict[str, Any]) -> list[str]:
for node in status_contexts:
state = (node.get("state") or "").upper()
if state in {"FAILURE", "ERROR"}:
if ignore_opencode and is_opencode_context(node):
continue
Comment thread
seonghobae marked this conversation as resolved.
failed.append(node.get("context") or "status-context")
return failed

Expand Down Expand Up @@ -2492,6 +2537,28 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio
return request_branch_update(
"current-head OpenCode review requested changes; branch is outdated before re-review"
)
coverage_ready = (
trigger_reviews
and review_dispatch_allowed
and current_head_coverage_change_request(pr)
Comment thread
seonghobae marked this conversation as resolved.
and coverage_evidence_state(pr) == "complete"
and strix_evidence_state(pr) == "complete"
and not failed_status_checks(pr, ignore_opencode=True)
)
Comment thread
seonghobae marked this conversation as resolved.
if coverage_ready:
wait_reason = repository_dispatch_wait_reason(repo, workflow)
if wait_reason:
return decide("wait", wait_reason)
dispatch_result = dispatch_opencode_review(repo, workflow, pr, dry_run=dry_run)
if dispatch_result == "already_running":
return decide(
"wait",
"current-head coverage evidence is complete, but a same-head OpenCode workflow run is already active",
)
return decide(
"review_dispatch",
"current-head OpenCode coverage blocker is cleared; same-head OpenCode re-dispatched",
)
Comment thread
seonghobae marked this conversation as resolved.
if pr.get("autoMergeRequest"):
return finish(
disable_auto_merge_decision(
Comment thread
seonghobae marked this conversation as resolved.
Expand Down
166 changes: 166 additions & 0 deletions tests/test_pr_review_merge_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,82 @@ def test_review_state_and_failed_checks():
assert sched.has_current_head_approval(superseded)
assert not sched.has_current_head_changes_requested(superseded)

coverage_request = make_pr(
reviews={
"nodes": [
{
**opencode_review("CHANGES_REQUESTED", "head"),
"body": (
"OpenCode cannot approve yet because required coverage evidence did not pass. "
"The coverage-evidence gate reported that required test/docstring evidence was not proven."
),
}
]
},
statusCheckRollup={
"contexts": {
"nodes": [
strix_check(),
{
"__typename": "CheckRun",
"name": "coverage-evidence",
"status": "COMPLETED",
"conclusion": "SUCCESS",
},
]
}
},
)
assert sched.current_head_coverage_change_request(coverage_request)
assert sched.coverage_evidence_state(coverage_request) == "complete"
assert sched.coverage_evidence_state(
make_pr(
statusCheckRollup={
"contexts": {"nodes": [{"name": "coverage-evidence", "state": "PENDING"}]}
}
)
) == "running"
assert sched.coverage_evidence_state(
make_pr(statusCheckRollup={"contexts": {"nodes": [strix_check()]}})
) == "missing"
assert sched.coverage_evidence_state(
make_pr(
statusCheckRollup={
"contexts": {"nodes": [{"name": "coverage-evidence", "state": "SUCCESS"}]}
}
)
) == "complete"
assert sched.coverage_evidence_state(
make_pr(
statusCheckRollup={
"contexts": {"nodes": [{"name": "coverage-evidence", "state": "FAILURE"}]}
}
)
) == "failed"
assert sched.coverage_evidence_state(make_pr()) == "missing"
human_coverage_request = make_pr(
reviews={
"nodes": [
{
**opencode_review("CHANGES_REQUESTED", "head", login="human"),
"body": "coverage evidence did not pass; coverage-evidence; required test/docstring evidence",
}
]
}
)
assert not sched.current_head_coverage_change_request(human_coverage_request)
assert not sched.current_head_coverage_change_request(
make_pr(reviews={"nodes": [opencode_review("APPROVED", "head")]})
)
ordinary_request = make_pr(
reviews={
"nodes": [
{**opencode_review("CHANGES_REQUESTED", "head"), "body": "Fix the estimator."}
]
}
)
assert not sched.current_head_coverage_change_request(ordinary_request)

stale_gate_reviews = make_pr(
reviews={
"nodes": [
Expand Down Expand Up @@ -3134,6 +3210,96 @@ def test_inspect_pr_blocks_and_waits_for_policy_states(monkeypatch):
"current-head OpenCode review requested changes"
)
assert update_calls == []
coverage_request = make_pr(
reviews={
"nodes": [
{
**opencode_review("CHANGES_REQUESTED", "head"),
"body": (
"OpenCode cannot approve yet because required coverage evidence did not pass. "
"The coverage-evidence gate reported that required test/docstring evidence was not proven."
),
}
]
},
statusCheckRollup={
"contexts": {
"nodes": [
strix_check(),
{
"__typename": "CheckRun",
"name": "coverage-evidence",
"status": "COMPLETED",
"conclusion": "SUCCESS",
},
{
"__typename": "CheckRun",
"name": "opencode-review",
"status": "COMPLETED",
"conclusion": "FAILURE",
"checkSuite": {
"workflowRun": {"workflow": {"name": "OpenCode Review"}}
},
},
]
}
},
)
dispatched = []
monkeypatch.setattr(
sched,
"dispatch_opencode_review",
lambda repo, workflow, pr, dry_run: dispatched.append(
(repo, workflow, pr["headRefOid"], dry_run)
)
or "dispatched",
)
monkeypatch.setattr(
sched,
"repository_dispatch_wait_reason",
lambda repo, workflow: "review dispatch waits",
)
coverage_wait = inspect(coverage_request)
assert coverage_wait.action == "wait"
assert coverage_wait.reason == "review dispatch waits"
monkeypatch.setattr(sched, "repository_dispatch_wait_reason", lambda repo, workflow: None)
monkeypatch.setattr(
sched,
"dispatch_opencode_review",
lambda repo, workflow, pr, dry_run: "already_running",
)
coverage_active = inspect(coverage_request)
assert coverage_active.action == "wait"
assert coverage_active.reason == (
"current-head coverage evidence is complete, but a same-head OpenCode workflow run is already active"
)
monkeypatch.setattr(
sched,
"dispatch_opencode_review",
lambda repo, workflow, pr, dry_run: dispatched.append(
(repo, workflow, pr["headRefOid"], dry_run)
)
or "dispatched",
)
coverage_decision = inspect(coverage_request)
assert coverage_decision.action == "review_dispatch"
assert coverage_decision.reason == (
"current-head OpenCode coverage blocker is cleared; same-head OpenCode re-dispatched"
)
assert dispatched == [("owner/repo", "OpenCode Review", "head", True)]

coverage_request["statusCheckRollup"]["contexts"]["nodes"].append(
{
"__typename": "CheckRun",
"name": "Security Scan",
"status": "COMPLETED",
"conclusion": "FAILURE",
}
)
unrelated_failure = inspect(coverage_request)
assert unrelated_failure.action == "block"
assert unrelated_failure.reason == "current-head OpenCode review requested changes"

action_required_pr = make_pr(
statusCheckRollup={
"contexts": {
Expand Down
Loading