Skip to content

fix(notifications): skip squad assignees in subscriber + inbox listeners#4323

Draft
bold84 wants to merge 1 commit into
multica-ai:mainfrom
bold84:fix/squad-assignee-subscriber-constraint
Draft

fix(notifications): skip squad assignees in subscriber + inbox listeners#4323
bold84 wants to merge 1 commit into
multica-ai:mainfrom
bold84:fix/squad-assignee-subscriber-constraint

Conversation

@bold84

@bold84 bold84 commented Jun 18, 2026

Copy link
Copy Markdown

Problem

When an issue is created or reassigned with assignee_type='squad', the subscriber and notification listeners call AddIssueSubscriber and notifyDirect with user_type='squad' / recipient_type='squad'. Both issue_subscriber.user_type and inbox_item.recipient_type are CHECK-constrained to ('member','agent'), so every squad assignment logs two SQLSTATE 23514 errors in production:

ERR failed to add issue subscriber ... user_type=squad ... reason=assignee
    error="... issue_subscriber_user_type_check (SQLSTATE 23514)"
ERR direct notification creation failed ... type=issue_assigned
    error="... inbox_item_recipient_type_check (SQLSTATE 23514)"

The squad leader is already dispatched via EnqueueTaskForSquadLeader (service/autopilot.go), so a subscriber row and an inbox item are both impossible and redundant — the squad has no inbox to read.

Context

This is the remaining server-side gap between two existing fixes:

  • MUL-2165 taught the CLI resolver to opt out of squad resolution for project.lead_type and the subscriber handler (isWorkspaceEntity), but the server-side event-bus listeners were not touched. The issue:created / issue:updated listeners kept forwarding *AssigneeType straight into AddIssueSubscriber.
  • MUL-2538 added an author_type == "system" early-return in subscriber_listeners.go and notification_listeners.go for the child-done system comment, with an explicit comment noting "issue_subscriber.user_type is constrained to (member,agent); without the gate the listener would log a constraint violation." The squad-assignee case — the same shape of bug — was left open.

This PR closes that gap using the same boundary-level gate pattern the team has applied both times.

Changes

  • subscriber_listeners.go: gate the assignee branch in the issue:created and issue:updated listeners on *AssigneeType != "squad".
  • notification_listeners.go: same gate on the two notifyDirect call sites that produce issue_assigned.
  • Tests: createTestSquad helper + 3 regression tests (TestSubscriberIssueCreated_SquadAssigneeNotSubscribed, TestSubscriberIssueUpdated_SquadAssigneeNotSubscribed, TestNotification_SquadAssigneeSkipsInbox) mirroring the MUL-2538 system-comment test pair.

Verification

  • go build ./cmd/server/ and go vet ./cmd/server/ clean.
  • go test -race -run 'TestSubscriber|TestNotification' ./cmd/server/ — all 30 tests pass (3 new + existing subscriber/notification suite).
  • Verified against the unfixed production code: the new tests reproduce the exact production error lines (SQLSTATE 23514 on both tables); with the fix applied, those errors are gone.

A note on the regression-test shape

The new tests are invariant guards — they pass both with and without the source fix because the DB rejects the bad insert either way (the listener swallows the error and continues). This is the same tradeoff the MUL-2538 tests accepted (TestSubscriberSystemCommentDoesNotSubscribe, TestNotification_SystemCommentSkipsInboxAndMentions). The value of these tests is:

  1. They pin the contract (squad assignee produces no subscriber row / no inbox item).
  2. They will fail loudly if anyone later widens the CHECK constraints to include squad without revisiting these listeners — preventing a silent regression where squad rows start getting inserted with the old listener logic.

I kept the style consistent with the existing MUL-2538 tests rather than introducing slog-output capture, which would be inconsistent with the package's testing conventions. Happy to switch to stricter output-capture tests if the team prefers.

When an issue is created or reassigned with assignee_type='squad', the
subscriber and notification listeners were calling AddIssueSubscriber and
notifyDirect with user_type='squad' / recipient_type='squad'. Both tables
are CHECK-constrained to ('member','agent'), so every squad assignment
logged two SQLSTATE 23514 errors:

  ERR failed to add issue subscriber ... user_type=squad ... issue_subscriber_user_type_check
  ERR direct notification creation failed ... inbox_item_recipient_type_check

The squad leader is already dispatched via EnqueueTaskForSquadLeader
(service/autopilot.go), so a subscriber row and an inbox item are both
impossible and redundant.

Mirrors the author_type=='system' gate added in MUL-2538
(TestSubscriberSystemCommentDoesNotSubscribe /
TestNotification_SystemCommentSkipsInboxAndMentions) — same shape of bug,
same boundary-level fix. The CLI-side resolution guard for the sibling
'project.lead_type' CHECK landed in MUL-2165; this closes the remaining
server-side gap between MUL-2165 and MUL-2538.

Changes:
- subscriber_listeners.go: gate the assignee branch in issue:created and
  issue:updated on *AssigneeType != "squad".
- notification_listeners.go: same gate on the two notifyDirect call sites
  for issue_assigned.
- tests: createTestSquad helper + three regression tests pinning the
  invariant. The behavioral assertions hold either way (the inserts fail
  on the CHECK), but the tests document the contract and will fail loudly
  if anyone widens the CHECK constraints without revisiting these
  listeners — same tradeoff as the MUL-2538 tests.
@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

@bold84 is attempting to deploy a commit to the IndexLabs Team on Vercel.

A member of the Team first needs to authorize it.

@bold84 bold84 marked this pull request as draft June 18, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant