Skip to content
Merged
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
5 changes: 5 additions & 0 deletions src/forge/orchestrator/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,11 @@ async def _handle_resume_event(
and (current_node in _REVIEW_GATES or targets_implementation_pr)
and current_state.get("is_paused", True)
):
sender_login = payload.get("sender", {}).get("login", "")
if sender_login and sender_login == await self._get_forge_github_login():
logger.debug("Ignoring Forge's own pull request review")
return current_state

review = payload.get("review", {})
review_state = review.get("state", "").lower()
review_body = review.get("body", "") or ""
Expand Down
45 changes: 45 additions & 0 deletions tests/unit/orchestrator/test_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,51 @@ async def test_forge_github_login_is_cached_per_worker(self):
mock_github.get_authenticated_user.assert_awaited_once()
mock_github.close.assert_awaited_once()

@pytest.mark.asyncio
async def test_forge_authored_pr_review_does_not_resume_review_workflow(self):
"""Thread replies create review events that Forge must not consume itself."""
worker = OrchestratorWorker(consumer_name="test-worker")
state = {
"ticket_key": "TEST-236",
"current_node": "human_review_gate",
"current_repo": "owner/repo",
"current_pr_number": 42,
"is_paused": True,
"context": {},
}
message = QueueMessage(
message_id="msg-forge-review",
event_id="evt-forge-review",
source=EventSource.GITHUB,
event_type="pull_request_review:submitted",
ticket_key="TEST-236",
payload={
"review": {
"id": 99,
"state": "commented",
"body": "",
"user": {"login": "forge-bot", "type": "Bot"},
},
"pull_request": {"number": 42},
"repository": {"full_name": "owner/repo"},
"sender": {"login": "forge-bot", "type": "Bot"},
},
)

with (
patch.object(
worker,
"_get_forge_github_login",
new=AsyncMock(return_value="forge-bot"),
) as get_forge_login,
patch("forge.orchestrator.worker.GitHubClient") as github_client,
):
result = await worker._handle_resume_event(message, state)

assert result is state
get_forge_login.assert_awaited_once()
github_client.assert_not_called()

@pytest.mark.asyncio
async def test_inline_reply_resumes_only_its_contested_thread(self):
worker = OrchestratorWorker(consumer_name="test-worker")
Expand Down
Loading