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>
6.3 KiB
Notification Delivery Platform — module mapping
Source design:
notification-superpowers-package/docs/superpowers/specs/2026-08-10-notification-platform-design.mdSource plan:
notification-superpowers-package/docs/superpowers/plans/2026-08-10-notification-platform-implementation-plan.md
Why a mapping exists
The plan was written against a hypothetical repository (modules/notification/**, root package
io.backend.skeleton.notification, 31 Gradle projects). This repository is a fail-closed
19-leaf Clean Architecture template: src/settings.gradle rejects any registry that does not
contain exactly the 19 modules in src/config/architecture/modules.json, and
verifyCleanArchitectureDependencies rejects any project edge outside allowed_dependencies.
Creating 31 new Gradle projects would violate HARD-STOP #5 of AGENTS.md. The package README
anticipates this and instructs the implementer to map dependency catalog and package/file paths onto
the host repository's rules while preserving the public contracts and reliability semantics.
Every logical module of the plan is therefore implemented as a package inside the registered leaf that owns its responsibility. No public contract, evidence rule, or reliability semantic is dropped.
Logical module → registered leaf
| Plan module | Registered leaf | Package |
|---|---|---|
notification-core-api |
application-core |
dev.caskeleton.application.notification.platform.api |
notification-content-api |
application-core |
…platform.api.content |
notification-contact-api |
application-core |
…platform.contact |
notification-template-api |
application-core |
…platform.template |
notification-policy |
application-core |
…platform.policy |
notification-provider-spi |
application-core |
…platform.provider |
notification-callback-api |
application-core |
…platform.callback |
notification-email-api |
application-core |
…platform.email |
notification-sms-api |
application-core |
…platform.sms |
notification-push-api |
application-core |
…platform.push |
notification-webpush (API half) |
application-core |
…platform.webpush |
notification-inbox-api |
application-core |
…platform.inbox |
notification-admin-api |
application-core |
…platform.admin |
notification-security (ports + redaction) |
application-core |
…platform.security |
notification-observability (ports) |
application-core |
…platform.observation |
notification-dispatch-runtime |
adapter:outbound:notification |
dev.caskeleton.adapter.outbound.notification.platform.dispatch |
notification-security (AES-GCM/HMAC impl) |
adapter:outbound:notification |
…platform.security |
notification-template-thymeleaf (reference renderer) |
adapter:outbound:notification |
…platform.template |
notification-email-smtp |
adapter:outbound:notification |
…platform.provider.smtp |
notification-email-ses |
adapter:outbound:notification |
…platform.provider.ses |
notification-sms-twilio |
adapter:outbound:notification |
…platform.provider.twilio |
notification-push-fcm |
adapter:outbound:notification |
…platform.provider.fcm |
notification-push-apns |
adapter:outbound:notification |
…platform.provider.apns |
notification-webpush (transport + crypto) |
adapter:outbound:notification |
…platform.provider.webpush |
notification-webhook-extension |
adapter:outbound:notification |
…platform.provider.webhook |
notification-observability (Micrometer impl) |
adapter:outbound:notification |
…platform.observation |
notification-admin-runtime |
adapter:outbound:notification |
…platform.admin |
notification-reactor |
adapter:outbound:notification |
…platform.reactor |
notification-spring-boot-starter |
adapter:outbound:notification (+ app-bootstrap wiring) |
…platform.autoconfigure |
notification-persistence-jpa |
adapter:outbound:persistence-jpa |
dev.caskeleton.adapter.outbound.persistence.notification.platform |
notification-inbox-jpa |
adapter:outbound:persistence-jpa |
…persistence.notification.platform.inbox |
notification-callback-mvc |
adapter:inbound:web |
dev.caskeleton.adapter.inbound.web.notification.platform.callback |
notification-callback-webflux |
adapter:inbound:web |
…callback.reactive |
notification-testkit |
test source sets of the owning leaves | …platform.testkit |
Dependency-direction consequences
The plan's module DAG (*-api → provider-spi/policy → runtime/adapters → starter) is preserved
by the leaf DAG that the registry already enforces:
application-core (all *-api, provider SPI, policy, callback contracts)
↑ ↑ ↑
adapter:outbound:notification adapter:outbound:persistence-jpa adapter:inbound:web
↑ ↑ ↑
app-bootstrap
Two plan edges cannot be expressed as project edges in this repository, and are replaced by ports:
notification-email-ses,notification-sms-twilio,notification-push-fcm,notification-push-apns,notification-webpush,notification-webhook-extension→httpclient platform.adapter-outbound-notificationis not allowed to depend onadapter-outbound-httpclient. The provider adapters therefore calldev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpGateway, an adapter-local port with a JDKjava.net.http.HttpClientdefault implementation.app-bootstrapsees both leaves and is the supported place to substitute an implementation backed by the HTTP Client Platform (TLS/timeout/circuit-breaker/SSRF/dynamic-target policy reuse).notification-inbox-jpa→optional messaging outbox integration.adapter-outbound-persistence-jpamay not depend onadapter-outbound-messaging; the inbox publishes through the existing persistence outbox tables plus theNotificationInboxSignalPortapplication port, andapp-bootstrapbinds the relay.
Commit policy
AGENTS.md pins commit policy to human-only. Step 5 (git add / git commit) of every plan task
is therefore intentionally not executed by the agent; the working tree carries the change and the
human owner commits.