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 5af1af95..d5d3415d 100644 --- a/server/internal/handler/comment.go +++ b/server/internal/handler/comment.go @@ -389,9 +389,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.