open-swe/agent/middleware/settle_review_check.py
Johannes du Plessis 8fa98398fa
fix: settle incomplete review check as neutral, not failure (#1501)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-11 13:03:34 -07:00

86 lines
3.4 KiB
Python

"""After-agent middleware that closes a still-open review check run.
``publish_review`` normally completes the ``Open SWE Review`` check run and
clears ``review_check_run_id`` from reviewer thread metadata. If the run ends
without ever publishing (crash, model-call limit, sandbox failure), the check
would hang "in progress" on the PR forever. This hook closes it as neutral —
the review not completing is reviewer infrastructure failing, not the PR.
"""
from __future__ import annotations
import logging
from typing import Any
from langchain.agents.middleware import AgentState, after_agent
from langgraph.config import get_config
from langgraph.runtime import Runtime
from ..reviewer_findings import get_thread_metadata
from ..reviewer_publish import settle_review_check_run
from ..utils.github_token import get_github_token
logger = logging.getLogger(__name__)
@after_agent
async def settle_review_check_on_exit(
state: AgentState,
runtime: Runtime,
) -> dict[str, Any] | None:
"""Fail the tracked review check run if the run ended without publishing."""
config = get_config()
configurable = config.get("configurable", {})
if not isinstance(configurable, dict):
return None
thread_id = configurable.get("thread_id")
repo_config = configurable.get("repo")
if not isinstance(thread_id, str) or not thread_id or not isinstance(repo_config, dict):
return None
owner = repo_config.get("owner")
repo = repo_config.get("name")
if not isinstance(owner, str) or not owner or not isinstance(repo, str) or not repo:
return None
try:
metadata = await get_thread_metadata(thread_id)
if not isinstance(metadata.get("review_check_run_id"), int):
return None
token = get_github_token()
if not token:
logger.warning("No GitHub token to settle stale review check on thread %s", thread_id)
return None
# A pending result means publish_review DID finish but its completion
# PATCH failed transiently — retry with the real conclusion instead of
# misreporting a published review as failed.
pending = metadata.get("review_check_pending_result")
if isinstance(pending, dict) and pending.get("conclusion") in {
"success",
"neutral",
"failure",
}:
conclusion = pending["conclusion"]
title = str(pending.get("title") or "Review completed")
summary = str(pending.get("summary") or "")
else:
# Neutral, not failure: an incomplete review is a reviewer-infra
# problem, and a red X on the PR misreads as a code problem.
conclusion = "neutral"
title = "Review did not complete"
summary = (
"The Open SWE review run ended without publishing a review. "
"Re-trigger the review by pushing a commit or re-requesting it."
)
await settle_review_check_run(
thread_id=thread_id,
owner=owner,
repo=repo,
token=token,
conclusion=conclusion,
title=title,
summary=summary,
)
logger.info("Settled stale review check run for thread %s", thread_id)
except Exception:
logger.exception("Failed to settle stale review check run for thread %s", thread_id)
return None