Event streams roll into digest batches keyed by recipient and channel, with deduplication, cutoff schedules, partial-failure isolation, and idempotent send tokens so duplicate workers cannot deliver the same digest twice or drop unreadable aggregates silently.
Package status: AI-assisted reference package. Static package validation has passed; implementation scenarios remain not run and human domain review is required before stable adoption.
Decision
High-volume notification events append to per-recipient digest buffers rather than sending immediately when the recipient profile selects digest mode. A scheduler closes buffers at profile-defined cutoffs, renders one digest payload, assigns an idempotent send token, and dispatches through the channel adapter. Send_pending retries follow a bounded loop until success or terminal failed state. Quiet-hours policy may defer digest dispatch until the recipient open window. Overflow beyond max_buffer_events spills to a weekly buffer rather than dropping silently.
Scope
- Buffering, deduplication keys, cutoff scheduling, rendering, send idempotency, retry loop, quiet-hours deferral, and delivery status for digest channels.
- Email daily/weekly digests and in-app digest summaries in the adoption profile.
- Empty-buffer suppression so recipients receive no message when nothing qualified during the period.
Outside this block
- Real-time transactional notifications sent outside digest mode.
- Template HTML design and marketing unsubscribe compliance flows beyond idempotent send recording.
- Quiet-hours schedule definition (see quiet-hours block); this block consumes defer outcomes when quiet_hours_respect is enabled.
Contract
- Each digest send attempt carries a send token unique per recipient, channel, and cutoff period; a second dispatch with the same token does not create a second visible message.
- Every provider dispatch counts as one send attempt. State send_pending retries with the matching backoff while attempts remain; after
max_send_attemptsfailed provider calls, it transitions to failed with no off-by-one extra dispatch. - When quiet_hours_respect is true, dispatch during quiet hours uses quiet_hours_defer_action hold-until-open-then-send rather than immediate provider send.
- Overflow policy spill-to-weekly moves excess events from the daily buffer into weekly-buffer with an auditable spill record; events are not dropped silently.
- Events deduplicate on profile-defined keys within the active buffer window.
- Empty buffers after deduplication produce no outbound send and record state skipped_empty.
- Digest state transitions follow digest-state-machine.yaml; illegal transitions return problem-details errors to operators.
Implementation guidance
- Persist buffers durably before acknowledging source events when the source requires at-least-once ingestion.
- Separate render from send so render output hash is stored before provider dispatch.
- Integrate quiet-hours evaluation before provider dispatch when quiet_hours_respect is enabled.
Failure handling and safeguards
- Provider timeout leaves state send_pending with retry schedule rather than marking sent.
- Terminal failed digests remain operator-visible without creating duplicate recipient messages on later retries unless a new cutoff period begins.
Verification and operations
- Tests prove send_pending retry loop, terminal failed path, quiet-hours defer, spill-to-weekly, and send-token idempotency.
- Metrics track buffer age, send_pending attempts, failed terminal rate, and spill volume.
Adoption assumptions
- Names and numeric values in example.yaml and digest-state-machine.yaml are an adoption profile, not universal defaults.
- The adopting team defines event schemas, digest templates, and cutoff calendars.
References
- No external normative source is required for this batching and delivery-state contract. RFC 5322 governs Internet message format, not digest scheduling or retry semantics.