fix: remove manual review trigger surfaces (#1396)

Open SWE Review now runs from automated PR triggers, so drop the old Slack/GitHub review keyword entrypoints and keep PR comments on the regular agent path.
This commit is contained in:
Johannes du Plessis 2026-06-03 12:33:22 -07:00 • committed by GitHub
parent f4b27c0fee
commit 8913926dba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 22 additions and 596 deletions

View file

@ -25,7 +25,6 @@ __all__ = [
"fetch_pr_comments_since_last_tag",
"format_github_comment_body_for_prompt",
"get_thread_id_from_branch",
"parse_github_review_command",
"post_github_comment",
"react_to_github_comment",
"sanitize_github_comment_body",
@ -33,8 +32,6 @@ __all__ = [
]
OPEN_SWE_TAGS = ("@openswe", "@open-swe", "@openswe-dev")
_OPEN_SWE_MENTION_RE = re.compile(r"(?i)@(?:openswe-dev|open-swe|openswe)\b")
_REVIEW_COMMAND_RE = re.compile(r"(?i)\Areview(?:\s+(https?://\S+))?\s*\Z")
UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "<dangerous-external-untrusted-users-comment>"
UNTRUSTED_GITHUB_COMMENT_CLOSE_TAG = "</dangerous-external-untrusted-users-comment>"
_SANITIZED_UNTRUSTED_GITHUB_COMMENT_OPEN_TAG = "[blocked-untrusted-comment-tag-open]"
@ -76,23 +73,6 @@ def get_thread_id_from_branch(branch_name: str) -> str | None:
return match.group(0) if match else None
def parse_github_review_command(body: str) -> tuple[bool, str | None]:
"""Detect ``@open-swe review [URL]`` in a GitHub comment body.
Returns ``(is_review_command, optional_pr_url)``. When ``is_review_command``
is True and the URL is None, the command applies to the PR being commented
on. The URL — if present — is returned as-is so the caller can validate it
with ``parse_github_pr_url``.
"""
if not body:
return False, None
stripped = _OPEN_SWE_MENTION_RE.sub("", body).strip()
match = _REVIEW_COMMAND_RE.match(stripped)
if not match:
return False, None
return True, match.group(1) or None
def sanitize_github_comment_body(body: str) -> str:
"""Strip reserved trust wrapper tags from raw GitHub comment bodies."""
sanitized = body.replace(

View file

@ -23,10 +23,6 @@ logger = logging.getLogger(__name__)
SLACK_API_BASE_URL = "https://slack.com/api"
SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", "")
GITHUB_PR_URL_RE = re.compile(r"https?://(?:www\.)?github\.com/[^\s<>|]+/[^\s<>|]+/pull/\d+")
URL_RE = re.compile(r"https?://[^\s<>|]+")
DEFAULT_ASSISTANT_STATUS = "is thinking…"
# Curated rotating loading strings shown by Slack while the indicator is active.
@ -174,36 +170,6 @@ def parse_github_pr_url(url: str) -> GitHubPrRef | None:
)
def parse_slack_review_command(text: str) -> GitHubPrRef | None:
stripped = text.strip()
command_match = re.fullmatch(r"(?is)review\s+(.+)", stripped)
if not command_match:
return None
rest = command_match.group(1).strip()
url_match = GITHUB_PR_URL_RE.search(rest)
if not url_match:
return None
trailing_text = rest[url_match.end() :].strip()
if trailing_text and trailing_text != ">" and not trailing_text.startswith("|"):
return None
return parse_github_pr_url(url_match.group(0))
def looks_like_slack_pr_review_command(text: str) -> bool:
stripped = text.strip()
if not re.match(r"(?is)^review\b", stripped):
return False
for match in URL_RE.finditer(stripped):
parsed = urlparse(match.group(0).strip("<>"))
host = (parsed.hostname or "").lower()
if parsed.scheme in {"http", "https"} and host in {"github.com", "www.github.com"}:
return True
return False
def select_slack_context_messages(
messages: list[dict[str, Any]],
current_message_ts: str,

View file

@ -55,7 +55,6 @@ from .utils.auth import (
persist_encrypted_github_token,
resolve_github_token_from_email,
)
from .utils.authorship import OPEN_SWE_BOT_NAME
from .utils.comments import get_recent_comments
from .utils.github_app import (
get_github_app_installation_token,
@ -70,7 +69,6 @@ from .utils.github_comments import (
fetch_pr_comments_since_last_tag,
format_github_comment_body_for_prompt,
get_thread_id_from_branch,
parse_github_review_command,
react_to_github_comment,
sanitize_github_comment_body,
verify_github_signature,
@ -88,7 +86,6 @@ from .utils.slack import (
format_slack_messages_for_prompt,
get_slack_user_info,
get_slack_user_names,
parse_github_pr_url,
post_slack_thread_reply,
post_slack_trace_reply,
resolve_slack_links_in_context,
@ -1163,33 +1160,6 @@ async def process_slack_mention(event_data: dict[str, Any], repo_config: dict[st
)
async def process_slack_pr_review_request(
pr_ref: GitHubPrRef, channel_id: str, thread_ts: str
) -> None:
await set_slack_assistant_status(channel_id, thread_ts)
result = await trigger_pr_review_from_ref(
pr_ref,
source="slack",
slack_channel_id=channel_id,
slack_thread_ts=thread_ts,
)
if result.get("success"):
thread_id = result.get("thread_id")
if isinstance(thread_id, str) and thread_id:
await post_slack_trace_reply(
channel_id, thread_ts, thread_id, include_dashboard_link=False
)
await set_slack_assistant_status(channel_id, thread_ts)
return
await post_slack_thread_reply(
channel_id,
thread_ts,
f"Could not start review for <{pr_ref.url}|{pr_ref.owner}/{pr_ref.repo}#{pr_ref.number}>: "
f"{result.get('error', 'unknown error')}.",
)
def verify_linear_signature(body: bytes, signature: str, secret: str) -> bool:
"""Verify the Linear webhook signature.
@ -1494,7 +1464,6 @@ _SUPPORTED_GH_EVENTS = frozenset(
_SUPPORTED_GH_ISSUE_ACTIONS = frozenset(["edited", "opened", "reopened"])
_SUPPORTED_GH_PULL_REQUEST_ACTIONS = frozenset(
[
"review_requested",
"opened",
"ready_for_review",
"converted_to_draft",
@ -1619,12 +1588,6 @@ async def _trigger_or_queue_run(
logger.info("LangGraph run created for thread %s from GitHub PR comment", thread_id)
def _is_open_swe_reviewer_request(payload: dict[str, Any]) -> bool:
reviewer = payload.get("requested_reviewer") or {}
login = reviewer.get("login", "") if isinstance(reviewer, dict) else ""
return login.lower() == OPEN_SWE_BOT_NAME.lower()
def build_github_pr_review_prompt(
repo_config: dict[str, str],
pr_number: int,
@ -2005,11 +1968,6 @@ async def _dispatch_first_review_from_pr_payload(payload: dict[str, Any], *, sou
logger.info("Reviewer run created for thread %s (source=%s)", thread_id, source)
async def process_github_pr_review_request(payload: dict[str, Any]) -> None:
"""Trigger the reviewer agent when the Open SWE bot is requested on a PR."""
await _dispatch_first_review_from_pr_payload(payload, source="github")
async def process_github_pr_ready(payload: dict[str, Any]) -> None:
"""Auto-review a PR that has just been opened or marked ready-for-review.
@ -2033,75 +1991,6 @@ async def process_github_pr_ready(payload: dict[str, Any]) -> None:
await _dispatch_first_review_from_pr_payload(payload, source="github")
async def process_github_pr_review_command(
payload: dict[str, Any],
event_type: str,
pr_url_override: str | None,
) -> None:
"""Trigger the reviewer when a PR comment contains ``@open-swe review``.
``pr_url_override`` is the optional URL token that followed ``review``. If
set, the review targets that PR; otherwise the comment's own PR is used.
"""
repo = payload.get("repository", {})
repo_config = {
"owner": repo.get("owner", {}).get("login", ""),
"name": repo.get("name", ""),
}
pr_data = payload.get("pull_request") or payload.get("issue", {})
sender = payload.get("sender", {})
github_login = sender.get("login", "")
github_user_id = sender.get("id")
pr_ref: GitHubPrRef | None = None
if pr_url_override:
pr_ref = parse_github_pr_url(pr_url_override)
if pr_ref is None:
logger.info("Ignoring @open-swe review with unparseable URL %s", pr_url_override)
return
else:
pr_number = pr_data.get("number")
if not pr_number:
logger.warning("@open-swe review command missing pr_number, skipping")
return
pr_ref = GitHubPrRef(
owner=repo_config["owner"],
repo=repo_config["name"],
number=pr_number,
url=pr_data.get("html_url", "") or pr_data.get("url", ""),
)
comment = payload.get("comment") or payload.get("review", {})
comment_id = comment.get("id")
node_id = comment.get("node_id") if event_type == "pull_request_review" else None
if comment_id:
app_token = await get_github_app_installation_token()
if app_token:
await react_to_github_comment(
repo_config,
comment_id,
event_type=event_type,
token=app_token,
pull_number=pr_data.get("number"),
node_id=node_id,
)
result = await trigger_pr_review_from_ref(
pr_ref,
source="github",
github_login=github_login,
github_user_id=github_user_id,
)
if not result.get("success"):
logger.warning(
"Failed to trigger reviewer from @open-swe review on %s/%s#%s: %s",
pr_ref.owner,
pr_ref.repo,
pr_ref.number,
result.get("error"),
)
async def _fetch_open_pr_for_branch(
repo_config: dict[str, str], head_ref: str, *, token: str
) -> dict[str, Any] | None:
@ -2541,20 +2430,9 @@ async def process_github_pr_comment(payload: dict[str, Any], event_type: str) ->
else:
logger.warning("Failed to persist branch_name metadata for thread %s", thread_id)
comment = payload.get("comment") or payload.get("review", {})
is_review_request, _pr_url_override = parse_github_review_command(comment.get("body") or "")
email = await email_for_login(github_login) or ""
if email:
github_token = await _get_or_resolve_thread_github_token(thread_id, email)
elif is_review_request:
github_token, expires_at = await get_github_app_installation_token_with_expiry()
if github_token:
try:
await persist_encrypted_github_token(thread_id, github_token, expires_at=expires_at)
except Exception:
logger.warning(
"Could not persist bot token for PR review request thread %s", thread_id
)
else:
logger.warning("No email mapping for GitHub user '%s', skipping", github_login)
return
@ -3011,24 +2889,11 @@ async def github_webhook(request: Request, background_tasks: BackgroundTasks) ->
logger.info("Accepted GitHub PR %s webhook, scheduling auto-review task", action)
background_tasks.add_task(process_github_pr_ready, payload)
return {"status": "accepted", "message": f"Processing PR {action} for auto-review"}
if not _is_open_swe_reviewer_request(payload):
logger.info("Ignoring PR review request for a different reviewer")
return {"status": "ignored", "reason": "Review request is not for open-swe bot"}
if not await _is_repo_enabled_for_review(webhook_repo_config):
logger.warning(
"Rejecting GitHub reviewer webhook: repo '%s/%s' not enabled for review",
webhook_repo_config.get("owner"),
webhook_repo_config.get("name"),
)
return {"status": "ignored", "reason": "Repository not enabled for review"}
gate_rejection = await _enforce_public_repo_org_gate(payload, "pull_request")
if gate_rejection is not None:
return gate_rejection
logger.info("Accepted GitHub PR review request webhook, scheduling reviewer task")
background_tasks.add_task(process_github_pr_review_request, payload)
return {"status": "accepted", "message": "Processing GitHub PR review request"}
logger.info("Ignoring unsupported GitHub pull_request action: %s", action)
return {
"status": "ignored",
"reason": f"Unsupported GitHub pull_request action: {action}",
}
if event_type == "push":
if not await _is_repo_enabled_for_review(webhook_repo_config):

View file

@ -11,7 +11,6 @@ from fastapi.testclient import TestClient
from agent import webapp
from agent.tools import request_pr_review as request_pr_review_tool
from agent.utils import github_comments
from agent.utils import slack as slack_utils
from agent.utils.slack import GitHubPrRef
@ -501,23 +500,8 @@ def test_github_webhook_ignores_unsupported_comment_action(monkeypatch) -> None:
}
def test_github_webhook_blocks_reviewer_repo_not_enabled_in_dashboard(monkeypatch) -> None:
called = False
async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None:
nonlocal called
called = True
monkeypatch.setattr(
webapp, "process_github_pr_review_request", fake_process_github_pr_review_request
)
def test_github_webhook_ignores_review_requested(monkeypatch) -> None:
monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET)
async def fake_is_review_repo_enabled(_owner: str, _name: str) -> bool:
return False
monkeypatch.setattr(webapp, "is_review_repo_enabled", fake_is_review_repo_enabled)
client = TestClient(webapp.app)
response = _post_github_webhook(
client,
@ -537,77 +521,10 @@ def test_github_webhook_blocks_reviewer_repo_not_enabled_in_dashboard(monkeypatc
)
assert response.status_code == 200
assert response.json() == {"status": "ignored", "reason": "Repository not enabled for review"}
assert called is False
def test_github_webhook_accepts_open_swe_review_requested(monkeypatch) -> None:
called: dict[str, object] = {}
async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None:
called["payload"] = payload
monkeypatch.setattr(
webapp, "process_github_pr_review_request", fake_process_github_pr_review_request
)
monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET)
client = TestClient(webapp.app)
response = _post_github_webhook(
client,
"pull_request",
{
"action": "review_requested",
"requested_reviewer": {"login": "open-swe[bot]"},
"pull_request": {
"number": 1244,
"html_url": "https://github.com/langchain-ai/open-swe/pull/1244",
"base": {"sha": "base-sha"},
"head": {"sha": "head-sha", "ref": "feature-branch"},
},
"repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"},
"sender": {"login": "octocat"},
},
)
assert response.status_code == 200
assert response.json()["status"] == "accepted"
assert called["payload"]["requested_reviewer"]["login"] == "open-swe[bot]"
def test_github_webhook_ignores_review_requested_for_other_reviewer(monkeypatch) -> None:
called = False
async def fake_process_github_pr_review_request(payload: dict[str, object]) -> None:
nonlocal called
called = True
monkeypatch.setattr(
webapp, "process_github_pr_review_request", fake_process_github_pr_review_request
)
monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET)
client = TestClient(webapp.app)
response = _post_github_webhook(
client,
"pull_request",
{
"action": "review_requested",
"requested_reviewer": {"login": "someone-else"},
"pull_request": {
"number": 1244,
"html_url": "https://github.com/langchain-ai/open-swe/pull/1244",
"base": {"sha": "base-sha"},
"head": {"sha": "head-sha", "ref": "feature-branch"},
},
"repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"},
"sender": {"login": "octocat"},
},
)
assert response.status_code == 200
assert response.json()["status"] == "ignored"
assert called is False
assert response.json() == {
"status": "ignored",
"reason": "Unsupported GitHub pull_request action: review_requested",
}
def test_slack_webhook_routes_review_command_to_agent(monkeypatch) -> None:
@ -816,76 +733,9 @@ def test_slack_webhook_threaded_followup_uses_parent_thread_ts(monkeypatch) -> N
assert event_data["event_ts"] == "1700000000.000200"
def test_process_slack_pr_review_request_posts_trace_reply(monkeypatch) -> None:
def test_process_github_pr_ready_creates_reviewer_run(monkeypatch) -> None:
captured: dict[str, object] = {}
async def fake_trigger_pr_review_from_ref(
pr_ref: GitHubPrRef,
*,
source: str,
github_login: str = "",
github_user_id: int | None = None,
slack_channel_id: str = "",
slack_thread_ts: str = "",
) -> dict[str, object]:
captured["pr_ref"] = pr_ref
captured["source"] = source
captured["slack_channel_id"] = slack_channel_id
captured["slack_thread_ts"] = slack_thread_ts
return {"success": True, "thread_id": "reviewer-thread-id", "pr_url": pr_ref.url}
async def fake_post_slack_trace_reply(
channel_id: str, thread_ts: str, thread_id: str, *, include_dashboard_link: bool = True
) -> None:
captured["trace_reply"] = {
"channel_id": channel_id,
"thread_ts": thread_ts,
"thread_id": thread_id,
"include_dashboard_link": include_dashboard_link,
}
async def fake_set_slack_assistant_status(channel_id: str, thread_ts: str) -> bool:
captured.setdefault("status_calls", []).append((channel_id, thread_ts))
return True
monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger_pr_review_from_ref)
monkeypatch.setattr(webapp, "post_slack_trace_reply", fake_post_slack_trace_reply)
monkeypatch.setattr(webapp, "set_slack_assistant_status", fake_set_slack_assistant_status)
asyncio.run(
webapp.process_slack_pr_review_request(
GitHubPrRef(
owner="langchain-ai",
repo="open-swe",
number=1244,
url="https://github.com/langchain-ai/open-swe/pull/1244",
),
"C123",
"1700000000.000100",
)
)
assert captured["source"] == "slack"
assert captured["slack_channel_id"] == "C123"
assert captured["slack_thread_ts"] == "1700000000.000100"
assert captured["trace_reply"] == {
"channel_id": "C123",
"thread_ts": "1700000000.000100",
"thread_id": "reviewer-thread-id",
"include_dashboard_link": False,
}
assert captured["status_calls"] == [
("C123", "1700000000.000100"),
("C123", "1700000000.000100"),
]
def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> None:
captured: dict[str, object] = {}
async def fake_get_github_app_installation_token() -> str | None:
return "app-token"
async def fake_get_github_app_installation_token_with_expiry() -> tuple[str | None, str | None]:
return "app-token", None
@ -919,9 +769,6 @@ def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> N
captured["set_metadata_thread_id"] = thread_id
captured["set_metadata_kwargs"] = kwargs
monkeypatch.setattr(
webapp, "get_github_app_installation_token", fake_get_github_app_installation_token
)
monkeypatch.setattr(
webapp,
"get_github_app_installation_token_with_expiry",
@ -935,10 +782,9 @@ def test_process_github_pr_review_request_creates_reviewer_run(monkeypatch) -> N
monkeypatch.setattr(webapp, "get_client", lambda url: _FakeLangGraphClient())
asyncio.run(
webapp.process_github_pr_review_request(
webapp.process_github_pr_ready(
{
"action": "review_requested",
"requested_reviewer": {"login": "open-swe[bot]"},
"action": "opened",
"pull_request": {
"number": 1244,
"html_url": "https://github.com/langchain-ai/open-swe/pull/1244",
@ -1158,7 +1004,7 @@ def test_request_pr_review_tool_uses_shared_trigger(monkeypatch) -> None:
assert result["success"] is True
def test_process_github_pr_comment_review_request_without_email_uses_app_token(
def test_process_github_pr_comment_without_email_skips(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
@ -1174,15 +1020,6 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token(
None,
)
async def fake_get_app_token_with_expiry() -> tuple[str, str]:
return "app-token", "2026-01-01T00:00:00Z"
async def fake_persist_token(
thread_id: str, token: str, *, expires_at: str | None = None
) -> str:
captured["persisted"] = {"thread_id": thread_id, "token": token, "expires_at": expires_at}
return "encrypted"
async def fake_react(*args, **kwargs) -> bool:
captured["reaction_token"] = kwargs["token"]
return True
@ -1196,10 +1033,6 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token(
monkeypatch.setattr(webapp, "extract_pr_context", fake_extract_pr_context)
monkeypatch.setattr(webapp, "email_for_login", lambda login: asyncio.sleep(0, result=None))
monkeypatch.setattr(
webapp, "get_github_app_installation_token_with_expiry", fake_get_app_token_with_expiry
)
monkeypatch.setattr(webapp, "persist_encrypted_github_token", fake_persist_token)
monkeypatch.setattr(webapp, "react_to_github_comment", fake_react)
monkeypatch.setattr(webapp, "fetch_pr_comments_since_last_tag", fake_fetch_comments)
monkeypatch.setattr(webapp, "_trigger_or_queue_run", fake_trigger_or_queue_run)
@ -1214,14 +1047,7 @@ def test_process_github_pr_comment_review_request_without_email_uses_app_token(
)
)
assert captured["reaction_token"] == "app-token"
assert captured["fetch_token"] == "app-token"
assert captured["persisted"] == {
"thread_id": "00000000-0000-0000-0000-000000000001",
"token": "app-token",
"expires_at": "2026-01-01T00:00:00Z",
}
assert captured["triggered"]
assert captured == {}
def test_process_github_issue_uses_resolved_user_token_for_reaction(monkeypatch) -> None:
@ -1394,50 +1220,6 @@ def test_process_github_issue_existing_thread_uses_followup_prompt(monkeypatch)
assert "## Repository" not in captured["prompt"]
def test_parse_github_review_command_standalone() -> None:
is_review, url = github_comments.parse_github_review_command("@open-swe review")
assert is_review is True
assert url is None
def test_parse_github_review_command_with_url() -> None:
is_review, url = github_comments.parse_github_review_command(
"@open-swe review https://github.com/langchain-ai/open-swe/pull/1244"
)
assert is_review is True
assert url == "https://github.com/langchain-ai/open-swe/pull/1244"
def test_parse_github_review_command_case_insensitive_and_aliases() -> None:
assert github_comments.parse_github_review_command("@OpenSWE Review") == (True, None)
assert github_comments.parse_github_review_command("@openswe review") == (True, None)
assert github_comments.parse_github_review_command("@openswe-dev review") == (True, None)
def test_parse_github_review_command_freeform_does_not_match() -> None:
assert github_comments.parse_github_review_command("@open-swe review my code") == (False, None)
assert github_comments.parse_github_review_command("@open-swe please review") == (False, None)
assert github_comments.parse_github_review_command("@open-swe fix this") == (False, None)
assert github_comments.parse_github_review_command("") == (False, None)
def test_parse_github_review_command_does_not_swallow_trailing_text() -> None:
# Trailing text after `review` (on a new line, with punctuation, or extra
# words) must NOT be parsed as a URL — otherwise the comment would be
# silently dropped instead of falling through to the regular handler.
assert github_comments.parse_github_review_command("@open-swe review\nthanks!") == (
False,
None,
)
assert github_comments.parse_github_review_command("@open-swe review please") == (False, None)
assert github_comments.parse_github_review_command("@open-swe review now") == (False, None)
# Non-http schemes are not URLs we can route to a PR.
assert github_comments.parse_github_review_command("@open-swe review ftp://x/y/pull/1") == (
False,
None,
)
def test_github_webhook_routes_pr_comment_review_to_agent(monkeypatch) -> None:
captured: dict[str, object] = {}
@ -1445,13 +1227,7 @@ def test_github_webhook_routes_pr_comment_review_to_agent(monkeypatch) -> None:
captured["payload"] = payload
captured["event_type"] = event_type
async def fake_process_review_command(
payload: dict[str, object], event_type: str, pr_url_override: str | None
) -> None:
raise AssertionError("review commands should route through the main agent")
monkeypatch.setattr(webapp, "process_github_pr_comment", fake_process_pr_comment)
monkeypatch.setattr(webapp, "process_github_pr_review_command", fake_process_review_command)
monkeypatch.setattr(webapp, "GITHUB_WEBHOOK_SECRET", _TEST_WEBHOOK_SECRET)
monkeypatch.setattr(webapp, "ALLOWED_GITHUB_ORGS", frozenset({"langchain-ai"}))
@ -1508,105 +1284,3 @@ def test_github_webhook_routes_pr_review_request_comment_to_agent(monkeypatch) -
assert response.status_code == 200
assert response.json() == {"status": "accepted", "message": "Processing issue_comment event"}
assert captured["event_type"] == "issue_comment"
def test_process_github_pr_review_command_uses_payload_pr(monkeypatch) -> None:
captured: dict[str, object] = {}
async def fake_get_app_token() -> str:
return "app-token"
async def fake_react(*args, **kwargs) -> None:
captured["reacted"] = True
async def fake_trigger(
pr_ref: GitHubPrRef,
*,
source: str,
github_login: str = "",
github_user_id: int | None = None,
slack_channel_id: str = "",
slack_thread_ts: str = "",
) -> dict[str, object]:
captured["pr_ref"] = pr_ref
captured["source"] = source
captured["github_login"] = github_login
captured["github_user_id"] = github_user_id
return {"success": True, "thread_id": "tid", "pr_url": pr_ref.url}
monkeypatch.setattr(webapp, "get_github_app_installation_token", fake_get_app_token)
monkeypatch.setattr(webapp, "react_to_github_comment", fake_react)
monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger)
asyncio.run(
webapp.process_github_pr_review_command(
{
"issue": {
"number": 1244,
"html_url": "https://github.com/langchain-ai/open-swe/pull/1244",
"pull_request": {"url": "..."},
},
"comment": {"id": 9, "body": "@open-swe review"},
"repository": {"owner": {"login": "langchain-ai"}, "name": "open-swe"},
"sender": {"login": "octocat", "id": 123},
},
"issue_comment",
None,
)
)
pr_ref = captured["pr_ref"]
assert isinstance(pr_ref, GitHubPrRef)
assert pr_ref.owner == "langchain-ai"
assert pr_ref.repo == "open-swe"
assert pr_ref.number == 1244
assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244"
assert captured["source"] == "github"
assert captured["github_login"] == "octocat"
assert captured["github_user_id"] == 123
assert captured.get("reacted") is True
def test_process_github_pr_review_command_uses_url_override(monkeypatch) -> None:
captured: dict[str, object] = {}
async def fake_get_app_token() -> str:
return "app-token"
async def fake_react(*args, **kwargs) -> None:
return None
async def fake_trigger(
pr_ref: GitHubPrRef,
*,
source: str,
github_login: str = "",
github_user_id: int | None = None,
slack_channel_id: str = "",
slack_thread_ts: str = "",
) -> dict[str, object]:
captured["pr_ref"] = pr_ref
return {"success": True, "thread_id": "tid", "pr_url": pr_ref.url}
monkeypatch.setattr(webapp, "get_github_app_installation_token", fake_get_app_token)
monkeypatch.setattr(webapp, "react_to_github_comment", fake_react)
monkeypatch.setattr(webapp, "trigger_pr_review_from_ref", fake_trigger)
asyncio.run(
webapp.process_github_pr_review_command(
{
"issue": {"number": 99, "pull_request": {"url": "..."}},
"comment": {"id": 1, "body": "@open-swe review https://github.com/x/y/pull/7"},
"repository": {"owner": {"login": "x"}, "name": "y"},
"sender": {"login": "octocat", "id": 1},
},
"issue_comment",
"https://github.com/x/y/pull/7",
)
)
pr_ref = captured["pr_ref"]
assert isinstance(pr_ref, GitHubPrRef)
assert pr_ref.number == 7
assert pr_ref.owner == "x"
assert pr_ref.repo == "y"

View file

@ -54,15 +54,9 @@ def test_gate_blocks_non_member_on_public_pr_comment(monkeypatch) -> None:
_common_setup(monkeypatch)
seen = _install_membership_stub(monkeypatch, members={"insider"})
async def fake_process_github_pr_review_command(*_args, **_kwargs) -> None:
raise AssertionError("should not be called")
async def fake_process_github_pr_comment(*_args, **_kwargs) -> None:
raise AssertionError("should not be called")
monkeypatch.setattr(
webapp, "process_github_pr_review_command", fake_process_github_pr_review_command
)
monkeypatch.setattr(webapp, "process_github_pr_comment", fake_process_github_pr_comment)
client = TestClient(webapp.app)
@ -254,16 +248,9 @@ def test_gate_blocks_non_member_on_public_issue(monkeypatch) -> None:
assert "not a member" in body["reason"]
def test_gate_blocks_non_member_on_public_review_requested(monkeypatch) -> None:
def test_review_requested_is_unsupported_before_public_repo_gate(monkeypatch) -> None:
_common_setup(monkeypatch)
_install_membership_stub(monkeypatch, members={"insider"})
async def fake_process_github_pr_review_request(*_args, **_kwargs) -> None:
raise AssertionError("should not be called")
monkeypatch.setattr(
webapp, "process_github_pr_review_request", fake_process_github_pr_review_request
)
seen = _install_membership_stub(monkeypatch, members={"insider"})
client = TestClient(webapp.app)
response = _post_github_webhook(
@ -288,9 +275,11 @@ def test_gate_blocks_non_member_on_public_review_requested(monkeypatch) -> None:
)
assert response.status_code == 200
body = response.json()
assert body["status"] == "ignored"
assert "not a member" in body["reason"]
assert response.json() == {
"status": "ignored",
"reason": "Unsupported GitHub pull_request action: review_requested",
}
assert seen["calls"] == []
def test_gate_allows_internal_bot_sender(monkeypatch) -> None:

View file

@ -8,9 +8,7 @@ from agent.utils.slack import (
TRACE_REPLY_TIPS,
convert_mentions_to_slack_format,
format_slack_messages_for_prompt,
looks_like_slack_pr_review_command,
parse_github_pr_url,
parse_slack_review_command,
post_slack_trace_reply,
replace_bot_mention_with_username,
select_slack_context_messages,
@ -155,52 +153,6 @@ def test_parse_github_pr_url_slack_formatted_link() -> None:
assert pr_ref.number == 1244
def test_parse_slack_review_command_requires_exact_review_command() -> None:
pr_ref = parse_slack_review_command("review https://github.com/langchain-ai/open-swe/pull/1244")
assert pr_ref is not None
assert pr_ref.owner == "langchain-ai"
assert pr_ref.repo == "open-swe"
assert pr_ref.number == 1244
assert (
parse_slack_review_command(
"please review https://github.com/langchain-ai/open-swe/pull/1244"
)
is None
)
assert (
parse_slack_review_command("review https://github.com/langchain-ai/open-swe/issues/1244")
is None
)
def test_parse_slack_review_command_supports_slack_link() -> None:
pr_ref = parse_slack_review_command(
"review <https://github.com/langchain-ai/open-swe/pull/1244|PR>"
)
assert pr_ref is not None
assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244"
def test_parse_slack_review_command_supports_slack_wrapped_raw_link() -> None:
pr_ref = parse_slack_review_command(
"review <https://github.com/langchain-ai/open-swe/pull/1244>"
)
assert pr_ref is not None
assert pr_ref.url == "https://github.com/langchain-ai/open-swe/pull/1244"
def test_looks_like_slack_pr_review_command_validates_github_host() -> None:
assert looks_like_slack_pr_review_command(
"review https://github.com/langchain-ai/open-swe/issues/1244"
)
assert not looks_like_slack_pr_review_command(
"review https://example.com/redirect?next=https://github.com/langchain-ai/open-swe/pull/1244"
)
def test_format_slack_messages_for_prompt_uses_name_and_id() -> None:
formatted = format_slack_messages_for_prompt(
[{"ts": "1.0", "text": "hello", "user": "U123"}],