fix: gate Open SWE in docs-plz Slack channel (#1605)

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Johannes du Plessis 2026-06-24 13:00:09 -07:00 • committed by GitHub
parent 39681102d6
commit fb467e84fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 105 additions and 0 deletions

View file

@ -108,6 +108,7 @@ from .utils.slack import (
fetch_slack_thread_messages,
format_slack_messages_for_prompt,
get_slack_channel_description,
get_slack_channel_info,
get_slack_user_info,
get_slack_user_names,
post_slack_thread_reply,
@ -172,6 +173,10 @@ DEFAULT_REPO_OWNER = os.environ.get("DEFAULT_REPO_OWNER", "langchain-ai")
DEFAULT_REPO_NAME = os.environ.get("DEFAULT_REPO_NAME", "")
SLACK_REPO_OWNER = os.environ.get("SLACK_REPO_OWNER", "") or DEFAULT_REPO_OWNER
SLACK_REPO_NAME = os.environ.get("SLACK_REPO_NAME", "") or DEFAULT_REPO_NAME
DOCS_PLZ_SLACK_CHANNEL_NAME = "docs-plz"
DOCS_PLZ_SLACK_GATE_REPLY = (
"Please don't use Open SWE here, instead ask the Fleet docs-plz agent to implement the docs"
)
LANGGRAPH_URL = os.environ.get("LANGGRAPH_URL") or os.environ.get(
"LANGGRAPH_URL_PROD", "http://localhost:2024"
@ -419,6 +424,22 @@ def _run_id_for_logging(run: Any) -> str:
return run_id if isinstance(run_id, str) and run_id else "<unknown>"
async def _is_docs_plz_slack_channel(channel_id: str) -> bool:
"""Check whether a Slack channel is the docs-plz handoff channel."""
try:
channel = await get_slack_channel_info(channel_id)
except Exception: # noqa: BLE001
logger.exception("Failed to resolve Slack channel info for docs-plz gate")
return False
if not isinstance(channel, dict):
return False
candidate_names = (channel.get("name"), channel.get("name_normalized"))
return any(
isinstance(name, str) and name.strip().lower() == DOCS_PLZ_SLACK_CHANNEL_NAME
for name in candidate_names
)
def _is_repo_allowed(repo_config: dict[str, str]) -> bool:
"""Check if the repo is in the allowlist.
@ -1588,6 +1609,15 @@ async def slack_webhook(request: Request, background_tasks: BackgroundTasks) ->
if bot_user_id and user_id == bot_user_id:
return {"status": "ignored", "reason": "Event from this bot user"}
if await _is_docs_plz_slack_channel(channel_id):
background_tasks.add_task(
post_slack_thread_reply,
channel_id,
thread_ts,
DOCS_PLZ_SLACK_GATE_REPLY,
)
return {"status": "accepted", "message": "Slack mention gated for docs-plz"}
event_data = {
"channel_id": channel_id,
"thread_ts": thread_ts,

View file

@ -521,6 +521,81 @@ def test_github_webhook_ignores_review_requested(monkeypatch) -> None:
}
def test_is_docs_plz_slack_channel_matches_name(monkeypatch) -> None:
async def fake_get_slack_channel_info(channel_id: str) -> dict[str, object]:
assert channel_id == "C_DOCS"
return {"name": "docs-plz"}
monkeypatch.setattr(webapp, "get_slack_channel_info", fake_get_slack_channel_info)
assert asyncio.run(webapp._is_docs_plz_slack_channel("C_DOCS")) is True
def test_is_docs_plz_slack_channel_matches_normalized_name(monkeypatch) -> None:
async def fake_get_slack_channel_info(channel_id: str) -> dict[str, object]:
assert channel_id == "C_DOCS"
return {"name": "Docs Plz", "name_normalized": "docs-plz"}
monkeypatch.setattr(webapp, "get_slack_channel_info", fake_get_slack_channel_info)
assert asyncio.run(webapp._is_docs_plz_slack_channel("C_DOCS")) is True
def test_slack_webhook_gates_docs_plz_channel(monkeypatch) -> None:
captured: dict[str, object] = {}
async def fake_is_docs_plz_slack_channel(channel_id: str) -> bool:
captured["checked_channel_id"] = channel_id
return True
async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool:
captured["reply"] = {"channel_id": channel_id, "thread_ts": thread_ts, "text": text}
return True
async def fail_get_slack_repo_config(
channel_id: str, thread_ts: str, slack_user_id: str | None = None
) -> dict[str, str]:
raise AssertionError("docs-plz gate should skip repo resolution")
async def fail_process_slack_mention(
event_data: dict[str, object], repo_config: dict[str, str]
) -> None:
raise AssertionError("docs-plz gate should not start the agent")
monkeypatch.setattr(webapp, "SLACK_SIGNING_SECRET", _TEST_SLACK_SECRET)
monkeypatch.setattr(webapp, "SLACK_BOT_USER_ID", "UBOT")
monkeypatch.setattr(webapp, "SLACK_BOT_USERNAME", "open-swe")
monkeypatch.setattr(slack_utils.time, "time", lambda: 1700000000)
monkeypatch.setattr(webapp, "_is_docs_plz_slack_channel", fake_is_docs_plz_slack_channel)
monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply)
monkeypatch.setattr(webapp, "get_slack_repo_config", fail_get_slack_repo_config)
monkeypatch.setattr(webapp, "process_slack_mention", fail_process_slack_mention)
client = TestClient(webapp.app)
response = _post_slack_webhook(
client,
{
"type": "event_callback",
"event": {
"type": "app_mention",
"channel": "C_DOCS",
"ts": "1700000000.000100",
"user": "U123",
"text": "<@UBOT> please update docs",
},
},
)
assert response.status_code == 200
assert response.json() == {"status": "accepted", "message": "Slack mention gated for docs-plz"}
assert captured["checked_channel_id"] == "C_DOCS"
assert captured["reply"] == {
"channel_id": "C_DOCS",
"thread_ts": "1700000000.000100",
"text": webapp.DOCS_PLZ_SLACK_GATE_REPLY,
}
def test_slack_webhook_routes_review_command_to_agent(monkeypatch) -> None:
captured: dict[str, object] = {}