From 2832a06fe38b12f5928b4a3c1c4cef08182c6969 Mon Sep 17 00:00:00 2001 From: Jiayuan Date: Sat, 4 Apr 2026 13:14:09 +0800 Subject: [PATCH] fix(comment): allow @mention of assigned agent on done/cancelled issues The assignee was unconditionally skipped in the mention path, assuming on_comment would handle it. But on_comment is suppressed for terminal statuses (done/cancelled), so an explicit @mention of the assignee had no effect. Now only skip the assignee dedup when on_comment will actually fire (non-terminal status). --- .../comment_trigger_integration_test.go | 30 +++++++++++++++++++ server/internal/handler/comment.go | 11 +++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/server/cmd/server/comment_trigger_integration_test.go b/server/cmd/server/comment_trigger_integration_test.go index b9337dad..1dfbc723 100644 --- a/server/cmd/server/comment_trigger_integration_test.go +++ b/server/cmd/server/comment_trigger_integration_test.go @@ -402,3 +402,33 @@ func TestCommentTriggerCoalescing(t *testing.T) { t.Errorf("expected 1 pending task (coalescing), got %d", n) } } + +// TestCommentTriggerMentionAssigneeDoneIssue verifies that @mentioning the +// assigned agent on a done issue still triggers execution. Previously the +// assignee was unconditionally skipped in the mention path (assuming +// on_comment handled it), but on_comment is suppressed for terminal statuses. +func TestCommentTriggerMentionAssigneeDoneIssue(t *testing.T) { + agentID := getAgentID(t) + + // Create an issue assigned to the agent, then mark it done. + issueID := createIssueAssignedToAgent(t, "Mention-assignee-done test", agentID) + clearTasks(t, issueID) // clear any tasks from assignment + resp := authRequest(t, "PUT", "/api/issues/"+issueID, map[string]any{ + "status": "done", + }) + resp.Body.Close() + + t.Cleanup(func() { + clearTasks(t, issueID) + resp := authRequest(t, "DELETE", "/api/issues/"+issueID, nil) + resp.Body.Close() + }) + + // @mention the assigned agent on the done issue — should trigger. + content := fmt.Sprintf("[@Agent](mention://agent/%s) reopen this please", agentID) + postComment(t, issueID, content, nil) + + if n := countPendingTasks(t, issueID); n != 1 { + t.Errorf("expected 1 pending task after @mention of assignee on done issue, got %d", n) + } +} diff --git a/server/internal/handler/comment.go b/server/internal/handler/comment.go index 95c00fa5..d6cb1fae 100644 --- a/server/internal/handler/comment.go +++ b/server/internal/handler/comment.go @@ -297,9 +297,14 @@ func (h *Handler) enqueueMentionedAgentTasks(ctx context.Context, issue db.Issue } agentUUID := parseUUID(m.ID) // Prevent duplicate: skip if this agent is the issue's assignee - // (already handled by the on_comment trigger above). - if issue.AssigneeType.Valid && issue.AssigneeType.String == "agent" && - issue.AssigneeID.Valid && uuidToString(issue.AssigneeID) == m.ID { + // (already handled by the on_comment trigger above) — but only + // when the issue is in a non-terminal status where on_comment + // will actually fire. For done/cancelled issues on_comment is + // suppressed, so an explicit @mention must still go through. + isAssignee := issue.AssigneeType.Valid && issue.AssigneeType.String == "agent" && + issue.AssigneeID.Valid && uuidToString(issue.AssigneeID) == m.ID + isTerminal := issue.Status == "done" || issue.Status == "cancelled" + if isAssignee && !isTerminal { continue } // Load the agent to check visibility, archive status, and trigger config.