Applies rule 32's migration exception: each header still says what the
schema was, what changes and what it prevents, but task, milestone,
decision and rule ids are replaced by the practice they stood for, the
operator quotes by the decision, single-install counts are dropped, and
pointers to a deleted package and an external design doc are gone.
golang-migrate keeps no checksum, so editing applied migrations'
comments is safe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing is emailed per event. New music (request_completed) goes out at
most once a day, at the summary hour in each user's own timezone, grouped
by artist. Everything else is batched: one email a window after the first
un-emailed item, holding whatever accumulated.
- Migration 0074: notification_email_settings (summary hour, batch window,
admin-configurable) and user_notification_email_state (batch start, last
sent, failures and retry_after per user and group). Existing rows are
stamped emailed so the upgrade sends no backlog.
- The Notifier stamps emailed_at at write time when the recipient's email
channel is off, so turning email on later doesn't send old items.
- Read rows are never selected. A row is stamped only after the mailer
accepts, in one transaction with the state, against the read's clock, so
a coalesced row updated mid-send stays pending.
- A failed send backs off 5m doubling to 6h; SMTP not configured just waits.
- Links come from the public address; without one the email has none.
- The mailer now RFC 2047-encodes subjects and strips line breaks from them.
- Admin → Integrations gains a Notification emails card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>