Maps the 31-module plan onto the registry's 19 leaves as packages; the two edges the registry forbids (provider->httpclient, inbox->messaging) are replaced by application-owned ports. See docs/notification/module-mapping.md. Acceptance is not delivery: ProviderSubmissionResult refuses to carry a delivery outcome, and AMBIGUOUS is a first-class terminal state that blocks automatic retry and fallback until reconciliation resolves it. Providers: SES (SigV4 + SNS callback), Twilio (X-Twilio-Signature + reconciliation), FCM (FID-primary batch), APNs, Web Push (RFC 8030/8291/8292), SMTP and webhook. Contact points are AES-256-GCM encrypted with a separate HMAC lookup fingerprint; nothing raw reaches a log, metric tag or exception. Dispatch commits the attempt row, calls the provider with no transaction open, then records the outcome; the durable queue uses FOR UPDATE SKIP LOCKED. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
26 lines
1.1 KiB
Markdown
26 lines
1.1 KiB
Markdown
# NOTIF-ADR-002 — append-only event ledger with channel projectors
|
|
|
|
## Status
|
|
|
|
Accepted.
|
|
|
|
## Context
|
|
|
|
A single linear delivery status has to be updated in place, which forces a rule for deciding whether
|
|
a new event outranks the stored one. The natural rule — compare ordinals — is wrong for real provider
|
|
traffic. Twilio does not guarantee callback ordering, so `sent` arrives after `delivered`. Email
|
|
generates complaints after deliveries. Both cases lose information under an ordinal rule.
|
|
|
|
## Decision
|
|
|
|
Provider events are appended to an immutable ledger before any projection runs. Channel-specific
|
|
projectors merge events into `SubmissionOutcome`, `DeliveryOutcome`, `EvidenceLevel`,
|
|
`EngagementFacts` and `SuppressionFacts` using explicit transition tables. Projection is idempotent
|
|
and can be replayed from the ledger.
|
|
|
|
## Consequences
|
|
|
|
Duplicate, out-of-order and late events are normal inputs rather than defects. A projector bug is
|
|
recoverable, because the events it mis-projected are still stored. Projector versions can be migrated
|
|
by replay. The cost is a second write per event and a projection that can lag its ledger.
|