feat(messaging): digest batching for notifications (ADR-0030 P3b-2)#1582
Merged
Conversation
Recipients can batch a topic into a daily/weekly digest instead of receiving every notification immediately. Builds on P3b-1's deferral seam: - PreferenceResolver consumes the `digest` field; digestDeferral() defers a batched recipient to the next window (local midnight / Monday 00:00) and tags the target with a stable window. Digest takes precedence over quiet-hours; critical + mandatory topics bypass it. - sys_notification_delivery gains digest_key (recipient|channel|window). Batched rows partition by that key so a window co-locates; normal claim() skips them and the new claimDigest() drains a window whole (memory + sql outbox). - The dispatcher's digest pass collapses each (recipient, channel, window) group into one renderDigest() message under the per-partition lock, then acks every row in the group with that single outcome. Additive — non-digest delivery is unchanged. +13 tests (127 total). tz-from- sys_user, configurable send-hour, and MJML digest emails are deferred follow-ups. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes ADR-0030 P3b-2 — the digest middleware the platform declared in P2 (
sys_notification_preference.digest) but never consumed.What
Recipients can batch a topic into a
daily/weeklydigest instead of getting every notification immediately. Builds directly on P3b-1's deferral seam.digestfield.digestDeferral()defers a batched recipient to the next window (local midnight for daily, Monday 00:00 for weekly) and tags the target with a stablewindowlabel. Digest takes precedence over quiet-hours;criticaland mandatory topics bypass it entirely.sys_notification_deliverygainsdigest_key(recipient|channel|window) + a grouping index. Batched rows partition by that key so a window's rows co-locate in one partition; the normal outboxclaim()skips them and a newclaimDigest()drains a window whole (implemented in both memory + sql outbox).digest_key, and collapses each(recipient, channel, window)group into onerenderDigest()message, then acks every row in the group with that single outcome (failure re-defers the whole group).Partition-by-
digest_key+ the existing lock means exactly one node assembles each digest — no double-send across a cluster.Scope
Additive: non-digest notifications are completely unchanged. Deferred follow-ups (noted in the ADR): timezone fallback to a
sys_userfield (windows currently usequiet_hours.tz→ UTC), a configurable daily send-hour, and MJML digest emails.Tests
+13 (
127total in service-messaging):digestDeferraldaily/weekly + tz; resolver stamps window + precedence + critical bypass;renderDigestcollapse; outboxclaim/claimDigestseparation + window deferral; end-to-end dispatcher collapse (3 rows → 1 message, all acked) + group re-defer on failure.🤖 Generated with Claude Code