Skip to main content
← Back to list
01Issue
FeatureOpenSwamp Club
AssigneesNone

Email delivery for mention notifications

Opened by stack72 · 4/20/2026

Summary

Follow-up to #127. The first cut of the mentions system delivers notifications only to the in-app inbox. This issue tracks the second channel: emailing the mentioned operative when they have a verified email and haven't opted out.

Prerequisites

  • #127 merged and shipped. That issue establishes the Notification aggregate, the Mongo collection, and the write-path hooks that produce notification rows. This issue piggy-backs on those rows — it does not re-implement mention parsing or fan-out.

Desired behavior

When a Notification row is created for a recipient whose operative profile has emailMentions === true AND emailVerified === true, an email is sent via the existing Resend integration with a link back to the issue/comment. Self-mentions are already filtered out upstream by #127.

Scope suggestions

  • Add an emailMentions: boolean preference to OperativeProfileData (default true). Expose in the profile editor with a labelled toggle. Decide whether to add a numbered migration under migrations/ or just default-on-read in the Operative entity when the stored doc lacks the field.
  • Add a NotificationEmailQueue (new Mongo collection) with the same shape as event_queue: status, nextAttemptAt, attempts, exponential backoff on failure.
  • Write-path: when processMentions creates a Notification row, enqueue an email job IFF the recipient has emailMentions === true AND a verified email.
  • Background watcher: mirror startEventQueueWatcher in structure. Wire into startBackgroundServices() in main.ts (NOT import.meta.main — see CLAUDE.md). Drain the queue, send via Resend, mark delivered or retry.
  • Email template: content/emails/mention.md with {{actor_username}}, {{issue_title}}, {{issue_number}}, {{excerpt}}, {{cta_url}}, {{cta_label}}, {{settings_url}}. HTML-escape all user-controlled fields via the existing escapeHtml in lib/email.ts.
  • Pin the public-facing origin for cta_url / settings_url. BetterAuth's BETTER_AUTH_URL may or may not be the right source — Railway prod, keeb.dev, and docker-compose all differ. Fail loudly at send time if the env var is unset rather than sending localhost:8000 links to users.

Out of scope

  • Mention notifications themselves (owned by #127)
  • Digest emails (batch multiple mentions into one send)
  • Email for non-mention notification kinds (future expansion)

Privacy

If the referenced issue is reclassified to security between enqueue and send, the worker must re-check canAccessIssue before sending. Notifications for issues the recipient can no longer access must be dropped, not delivered.

Why this matters

Many operatives only check their email, not the in-app inbox. Without the email channel, the mention system only closes the loop for people who happen to visit swamp.club between the mention and when they're in a hurry to respond.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

4/20/2026, 1:25:01 PM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.