Sub-scope 01 (notification) governance + 루트 + platform/autoconfigure — manifest and probes
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
generatedAt=2026-08-30T06:37:36+00:00

=== OWNED FILES ===
660f77ed47c90d8da6c66022c2b5d66e5a706942 CLAUDE.md 47
4fbe71c26464d9e2a3dfa420876338c9f5cdf89f README.md 78
517a950162f15d2bf8c4f55bb0323859ab4db99e build.gradle 54
b30c50f91495749ea97560903b4662d887a5e543 gradle.lockfile 167
23a2136015f44e177bdab5a477f7f498ec5d8ebb src/main/java/dev/caskeleton/adapter/outbound/notification/NotificationConfig.java 40
416c2cb2d052d3a834e03dfdd8fb996224aa9c37 src/main/java/dev/caskeleton/adapter/outbound/notification/NotificationRoutesSettings.java 28
928df24c90847cd9db99a489e3b7149656ec207a src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/AssembledProvider.java 63
c114dfcd96177d602589eff07dc731a88eec3fa2 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationPlatformAutoConfiguration.java 118
182c790f192069fc98caeb269e460b659ff5aca1 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationPlatformMode.java 24
fb23f3b75a076d06de387da7ee9f2c16f977d219 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationPlatformSettings.java 190
eba7ccdc3d555b0b7a2ad72c05797ca638d2fd95 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssembly.java 239
4ce10c47f9c74c5e426d92093b010b69ced464ed src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSecretRequirements.java 98
8870a02465769a3a8c4987c8e0e935ed0e614984 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSmtpProviderConfig.java 211
c6393dabcd7bd61abadedc6b2c9387d19554ed36 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSmtpSettings.java 60
aac01be3780329c32f76e3b857f7e8de3c3b5501 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/ProviderRuntimeAssembler.java 35
c75b240b6b23bff058b58df5ddf13fa4fed95d17 src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/ProviderType.java 87
d73384a60e4797e54127f12e5a018d58bf90350d src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/SmtpProviderRuntimeAssembler.java 164
6f630e9813d2ed3db38bc429931d1b0d74cb3bf9 src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationPlatformSettingsTest.java 166
f9fac342d9de203c1777315cc05b1cd55890a6c0 src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssemblyTest.java 267
5f8df14bba9f88525e41d483ce8444ccaf8df4fc src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationReleaseGateTest.java 63
92052e37ceccc1af9f5f72b3f9d69e94cbff3928 src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSecretRequirementsTest.java 173
1831898eba674f57a4be0f318ba667421fbcc4a8 src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/SmtpProviderRuntimeAssemblerTest.java 133

=== 8.1 활성화 게이트 전수 ===
$ grep -rn 'ConditionalOn' adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/*.java adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/*.java | sed 's|.*/notification/||'
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:19:import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:20:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:34:@ConditionalOnProperty(
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:42:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:49:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:56:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:70:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:88:   * <p>{@code @ConditionalOnBean(SecretMaterialProvider.class)} used to guard this, to express
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:102:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationPlatformAutoConfiguration.java:114:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:17:import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
platform/autoconfigure/NotificationSmtpProviderConfig.java:18:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
platform/autoconfigure/NotificationSmtpProviderConfig.java:58:@ConditionalOnProperty(name = "spring.mail.host")
platform/autoconfigure/NotificationSmtpProviderConfig.java:64:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:78:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:88:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:102:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:113:  @ConditionalOnMissingBean
platform/autoconfigure/NotificationSmtpProviderConfig.java:128:  @ConditionalOnMissingBean(name = "notificationSmtpExecutor")
platform/autoconfigure/NotificationSmtpProviderConfig.java:162:  @ConditionalOnMissingBean(name = "smtpProviderRuntimeAssembler")
exit=0

$ git ls-files 'adapter/outbound/notification/src/main/resources' 2>/dev/null | head; echo 'main resources: none tracked'
main resources: none tracked
exit=0

$ git grep -rn 'NotificationPlatformAutoConfiguration\|NotificationConfig' -- 'app-bootstrap/src/main' | head
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationRootAutoConfiguration.java:3:import dev.caskeleton.adapter.outbound.notification.NotificationConfig;
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationRootAutoConfiguration.java:6:import dev.caskeleton.adapter.outbound.notification.platform.autoconfigure.NotificationPlatformAutoConfiguration;
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationRootAutoConfiguration.java:27: * <p>{@link NotificationPlatformAutoConfiguration} is named here for a blunter reason: nothing
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationRootAutoConfiguration.java:47:  NotificationPlatformAutoConfiguration.class,
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationRootAutoConfiguration.java:51:  NotificationConfig.class,
app-bootstrap/src/main/resources/application.yml:959:    # Add a provider = new files only (NotificationConfig); select it here.
exit=0

=== 8.2 INGEST_ONLY 가 실제로 readiness 를 내리는가 ===
$ git grep -rn 'INGEST_ONLY' -- 'adapter/outbound/notification/src/main' 'app-bootstrap/src/main' | head -20
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationPlatformMode.java:23:  INGEST_ONLY
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssembly.java:35: *   <li>zero providers without {@link NotificationPlatformMode#INGEST_ONLY}, because a platform
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssembly.java:141:        routes.isEmpty() ? NotificationPlatformMode.INGEST_ONLY : NotificationPlatformMode.SERVING);
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssembly.java:183:    if (routes.isEmpty() && mode != NotificationPlatformMode.INGEST_ONLY) {
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationProviderAssembly.java:187:              + " declare ca-skeleton.notification.platform.mode=INGEST_ONLY so readiness reports"
adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSecretRequirements.java:83:   * The purposes every mode needs, including {@code INGEST_ONLY}.
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationPlatformWorkerConfig.java:128:   * Everything that starts a thread, and therefore everything {@code INGEST_ONLY} must not have.
app-bootstrap/src/main/java/dev/caskeleton/bootstrap/notification/NotificationPlatformWorkerConfig.java:132:   * method — so a deployment in {@code INGEST_ONLY}, the mode whose whole purpose is to accept and
app-bootstrap/src/main/resources/application.yml:487:      # SERVING | INGEST_ONLY. SERVING refuses to start without a working provider; INGEST_ONLY
exit=0

=== 8.3 실행 probe: dependencyPolicy 와 API 표면 ===
$ ./gradlew :adapter:outbound:notification:verifyDependencyPolicy   → BUILD SUCCESSFUL (EXIT=0)
$ ./gradlew verifyNotificationApiSurface                            → OK — 586 public types, unchanged (EXIT=0)
$ wc -l ../docs/notification/api-surface-snapshot.txt
589 ../docs/notification/api-surface-snapshot.txt
exit=0

=== 8.4 비밀 요구가 능력에 따라 좁아지는가 ===
$ grep -n 'ALWAYS\|required.add' adapter/outbound/notification/src/main/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure/NotificationSecretRequirements.java
36:    Set<SecretPurpose> required = EnumSet.copyOf(ALWAYS);
42:      required.add(SecretPurpose.CALLBACK_SIGNING);
43:      required.add(SecretPurpose.CALLBACK_FINGERPRINT_HMAC);
54:        required.add(SecretPurpose.PROVIDER_CREDENTIAL);
57:        required.add(SecretPurpose.VAPID_SIGNING);
63:        required.add(SecretPurpose.CALLBACK_SIGNING);
92:  private static final Set<SecretPurpose> ALWAYS =
exit=0

$ git grep -h '  void [a-z]' -- 'adapter/outbound/notification/src/test/java/dev/caskeleton/adapter/outbound/notification/platform/autoconfigure' | sed 's/^ *//'
void productionWebPushWithoutVapidKeyFailsStartup() {
void apnsWithoutTopicFailsStartup() {
void aCallbackCapableProviderRequiresASigningSecret() {
void aDisabledProfileIsNotValidated() {
void ambiguousFallbackCannotBeEnabled() {
void anUnboundedClaimBatchIsRefused() {
void aLeaseShorterThanThePollIntervalIsRefused() {
void defaultsAreUsableAndConservative() {
void aConfiguredProfileBecomesARuntimeAndARoute() {
void anUnknownProviderTypeFailsTheBoot() {
void aFamilyWithNoAssemblerIsRefused() {
void twoProfilesOnOneChannelNeedAPrimary() {
void aMarkedPrimaryResolvesTheAmbiguity() {
void twoPrimariesAreRefused() {
void aDisabledProfileAssemblesNothing() {
void anEnabledPlatformWithNoProviderRefusesToStart() {
void ingestOnlyIsADeclaration() {
void twoAssemblersForOneFamilyAreRefused() {
void requiredDocumentationAndAdrsExist() {
void supportMatrixDoesNotClaimGuaranteedDelivery() throws IOException {
void theEvidenceDocumentStatesTheForbiddenPromotions() throws IOException {
void theAcceptPathKeysAreAlwaysRequired() {
void anSmtpOnlyPlatformIsNotAskedForAProviderCredential() {
void aDisabledProfileDoesNotDemandItsKeys() {
void anEnabledWebPushProfileDemandsItsKeys() {
void callbacksOffDoNotDemandTheCallbackKeys() {
void callbacksOnDemandBothCallbackKeys() {
void aProfileNamingASigningRefDemandsTheSigningPurpose() {
void theAssemblerServesSmtpOnEmail() {
void aConfiguredProfileAssemblesIntoARuntime() {
void smtpClaimsNothingItCannotDo() {
void thePerProfileBoundsComeFromTheProfile() {
exit=0

