Sub-scope 01 (httpclient) governance + profile/** — manifest and probes revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 generatedAt=2026-08-30T05:21:40+00:00 === OWNED FILES === eaeb311707b44e88fdc5914251774db544b64186 CLAUDE.md 79 21139d2ec3ee8edf0ce7c2e71a571da6198b62bc README.md 77 47e011d4cf6a04f4db4ef153653a4049f4d5a3f2 build.gradle 258 5f186c01a45409f963e17bb199a6b14ef9e0a0a9 gradle.lockfile 249 a8f810ca166f416197ff199b4475f8d68fe7c8e1 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/AuthenticationSettings.java 42 9d4e34483521fd54a7caf07a448758d8131fb142 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/AuthenticationType.java 21 b74ee2604e3a001be5e9b480f5db9f9e2b81e08e src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientApiType.java 7 d35151bdf7a319646af8709fdde7b2719479c257 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientMode.java 9 a24610e1639c6956ae02b86465e8d58a80785d70 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientObservabilitySettings.java 13 a18f25310f7faf710ea6485bd45336bfded15bff src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfile.java 75 6d1ea925b16d7da65dc109d64d3447f4b80f17fa src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidator.java 291 4b97c29f7a8b288f43753840e1a1b9ac7a2a92d1 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileViolation.java 24 0c7110af7e768916828f0fb4687bcb11325be6fe src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntime.java 102 f2511df6cdfeeade638e54085713c7a94ad56bcd src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeFactory.java 13 6dcdc1770ea903e16a42179f76c82b1e1a861494 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeLease.java 33 09bb0b65b62637b83944ba7f30dd6b6f95efc333 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeRegistry.java 187 8c64ba3c861e2215170b7bbcff0c9c830bf8f3b5 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeState.java 11 0e98fb9fae7439d6952456cf76fe838aa48475c8 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/HttpProtocol.java 9 f40341f55e85c665db394d0342c8b3940c55234d src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/JitterStrategy.java 8 c869e6d94ce77e53a2c37d9d2c495271276cdbce src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/PoolSettings.java 39 8e1d299d96b52dc43482e580394326100eb34f90 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ProtocolIntent.java 61 5c7380a6490b379f4411db1e5879c654511d489a src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ProxySettings.java 39 cbdba40b3977eb78e5603330fee53c85e8fb8fe1 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ProxyType.java 7 dde509058da2af01c7b76f4c9a8811e6a378c821 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RedirectSettings.java 20 f756c6f4845e39ab55fd320fdaa58a0599e85707 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RequestLimits.java 11 400a95a2af281c0cb4ab65ab7aae28c0437053a7 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ResponseLimits.java 38 bb3fb5481dbd80256d42aeacdba5984c3fb13803 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RetryAfterPolicy.java 11 2f6905695d191e43bf049753ca16f87f12524e4d src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RetrySettings.java 57 da3ddc8be6df41f420a2f32e5f9a50c2fd9b8121 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RuntimeEnvironment.java 16 283544ae6d50a9339e6e94d6908d68987e20bae8 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/RuntimeGeneration.java 15 b9502e98f2c2817becaef6929b496c8a9f45a095 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/TimeoutSettings.java 46 e44285231db61d50346283e9114ca66969973fbc src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/TlsSettings.java 51 93a3febddb367dc9ee180dae11f7f9493e607af9 src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/TransportType.java 15 8a6180116432266d97928b847d626395121e8f51 src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidatorTest.java 106 6a992e4043922dcdb839479ab794e015d4c161b4 src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeRegistryTest.java 64 main: 29 test: 2 governance: 4 === 8.1 reachability: validator와 registry가 실제로 배선되는가 === $ git grep -n 'ClientProfileValidator' -- . | grep -v 'profile/ClientProfileValidator.java' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/HttpProtocol.java:7: /** Experimental. Rejected on Stable profiles by {@link ClientProfileValidator}. */ adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/TlsSettings.java:15: * {@code ClientProfileValidator} and {@code TlsPolicyValidator} both fail closed on them. adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/TransportType.java:13: /** Local test only. Rejected in production by {@link ClientProfileValidator}. */ adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidatorTest.java:11:class ClientProfileValidatorTest { adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidatorTest.java:13: private final ClientProfileValidator validator = new ClientProfileValidator(); app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientStartupValidator.java:9:import dev.caskeleton.adapter.outbound.httpclient.profile.ClientProfileValidator; app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientStartupValidator.java:37: private final ClientProfileValidator profileValidator = new ClientProfileValidator(); exit=0 $ git grep -ln 'ClientRuntimeRegistry' -- 'adapter/outbound/httpclient/src/main' 'app-bootstrap/src/main' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/dynamic/DefaultDynamicTargetGateway.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeRegistry.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/restclient/BlockingStreamingGateway.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/restclient/DefaultGenericHttpGateway.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/security/TlsRuntimeRotationCoordinator.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/service/DefaultHttpServiceRegistry.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/service/DefaultReactiveHttpServiceRegistry.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/webclient/DefaultReactiveHttpGateway.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/webclient/DefaultReactiveSseGateway.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/webclient/ReactiveStreamingGateway.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/DynamicTargetAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientActuatorEndpoint.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientManagementAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientProfileAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpServiceClientAutoConfiguration.java exit=0 $ git ls-files 'adapter/outbound/httpclient/src/main/resources' 2>/dev/null; echo 'resources: none tracked' resources: none tracked exit=0 === 8.1b app-bootstrap가 이 leaf를 어떻게 조립하는가 === $ git grep -ln 'dev.caskeleton.adapter.outbound.httpclient' -- 'app-bootstrap/src/main' | head -20 app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/DynamicTargetAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientActuatorEndpoint.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientAuthenticationAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientManagementAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientObservationAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientProfileAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientProfileFactory.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientResilienceAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientSecurityAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientStartupValidator.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientTransportAutoConfiguration.java app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpServiceClientAutoConfiguration.java exit=0 === 8.2 위반 코드 집합과 test 대조 === $ grep -oE 'violation\("[A-Z0-9_]+"' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidator.java | sed 's/violation("//;s/"//' | sort -u ALLOWED_HOST_MISMATCH ALLOWED_PORT_MISMATCH API_KEY_HEADER_NAME_REQUIRED BASE_URL_QUERY_FORBIDDEN BASE_URL_USERINFO_FORBIDDEN BODY_LOGGING_FORBIDDEN DNS_TIMEOUT_UNSUPPORTED DYNAMIC_BASE_URL_REQUIRED DYNAMIC_DEFAULT_CREDENTIAL_FORBIDDEN DYNAMIC_TARGET_PROXY_UNSUPPORTED DYNAMIC_TARGET_TRANSPORT_UNSUPPORTED FULL_URL_RECORDING_FORBIDDEN HOSTNAME_VERIFICATION_REQUIRED HTTP2_REQUIRED_TRANSPORT_UNSUPPORTED HTTP3_STABLE_FORBIDDEN INVALID_TIMEOUT_BUDGET JDK_FINE_GRAINED_POOL_UNSUPPORTED MISSING_PRODUCTION_SETTING OAUTH2_REGISTRATION_REQUIRED PLAINTEXT_FALLBACK_FORBIDDEN PLAINTEXT_PRODUCTION_TARGET POOL_ROUTE_EXCEEDS_TOTAL PRODUCTION_SIMPLE_FACTORY_FORBIDDEN PROXY_CREDENTIAL_UNSUPPORTED REACTIVE_REDIRECT_UNSUPPORTED REDIRECT_CROSS_ORIGIN_CREDENTIAL_POLICY_REQUIRED REDIRECT_POLICY_INVALID RESPONSE_HARD_MAXIMUM_EXCEEDED RETRY_BACKOFF_REQUIRED RETRY_POLICY_CONTRADICTS_ATTEMPTS TLS_PROTOCOL_FORBIDDEN TLS_PROTOCOL_SET_REQUIRED TRUSTED_BASE_URL_REQUIRED TRUST_ALL_FORBIDDEN exit=0 $ grep -oE 'violation\("[A-Z0-9_]+"' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidator.java | sed 's/violation("//;s/"//' | sort -u | wc -l 34 exit=0 $ git grep -ohE '"[A-Z0-9_]{6,}"' -- 'adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile' | tr -d '"' | sort -u BASE_URL_QUERY_FORBIDDEN BASE_URL_USERINFO_FORBIDDEN DYNAMIC_DEFAULT_CREDENTIAL_FORBIDDEN HOSTNAME_VERIFICATION_REQUIRED HTTP3_STABLE_FORBIDDEN INVALID_TIMEOUT_BUDGET JDK_FINE_GRAINED_POOL_UNSUPPORTED PLAINTEXT_FALLBACK_FORBIDDEN PLAINTEXT_PRODUCTION_TARGET PRODUCTION_SIMPLE_FACTORY_FORBIDDEN TRUST_ALL_FORBIDDEN exit=0 === 8.3 close() 순서: 실패 시 scheduler 종료에 도달하는가 === $ sed -n '119,160p' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientRuntimeRegistry.java @Override public void close() { List all = new ArrayList<>(); runtimes.values().forEach(holder -> all.add(holder.get())); // Retired-but-still-draining generations are closed too; they used to survive registry // shutdown entirely. all.addAll(retired); // Every runtime is closed even when one refuses. forEach stopped at the first exception, so a // single misbehaving pool left every remaining connection, thread and socket open — shutdown // leaked more the worse the failure was. RuntimeException firstFailure = null; for (ClientRuntime runtime : all) { try { runtime.forceClose(); } catch (RuntimeException failure) { if (firstFailure == null) { firstFailure = failure; } else { firstFailure.addSuppressed(failure); } } } retired.clear(); runtimes.clear(); if (firstFailure != null) { throw firstFailure; } ScheduledExecutorService scheduler = drainScheduler.getAndSet(null); if (scheduler != null) { // Await termination: a registry that returns while its drain thread is still alive would // leak a thread per rotation cycle, which the resource-bound suite exists to catch. scheduler.shutdownNow(); try { if (!scheduler.awaitTermination(SHUTDOWN_AWAIT_MILLIS, TimeUnit.MILLISECONDS)) { throw new IllegalStateException("http client drain scheduler did not terminate"); } } catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); } } } exit=0 === 8.4 test 목록과 이름 === $ git ls-files 'adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile' | xargs -n1 basename ClientProfileValidatorTest.java ClientRuntimeRegistryTest.java exit=0 $ git grep -h ' void [a-z]' -- 'adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/profile' | sed 's/^ *//' void rejectsPlainHttpInProduction() { void rejectsDynamicCredentialInheritance() { void rejectsTotalTimeoutShorterThanConnectBudget() { void rejectsUnsafeTlsConfiguration() { void rejectsSimpleFactoryAndUnacknowledgedHttp3AndJdkRoutePool() { void rejectsBaseUrlUserInfoAndQuery() { void violationOrderIsDeterministic() { void acceptsAFullyDeclaredProductionProfile() { void newCallsUseNewGenerationWhileOldCallDrains() { void drainingRuntimeRefusesNewAttempts() { void closingTheRegistryReleasesEveryGenerationAndLeavesNoThread() { exit=0 === 8.3b POOL_ROUTE_EXCEEDS_TOTAL 도달 가능성 === $ grep -n 'maxConnectionsPerRoute > maxTotalConnections' -A 2 adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/PoolSettings.java 35: if (maxConnectionsPerRoute > maxTotalConnections) { 36- throw new IllegalArgumentException("per-route pool must not exceed the total pool"); 37- } exit=0 $ grep -n 'POOL_ROUTE_EXCEEDS_TOTAL' -B 4 adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidator.java 191- } 192- if (profile.pool().maxConnectionsPerRoute() > profile.pool().maxTotalConnections()) { 193- // A per-route ceiling above the total is incoherent, and on Reactor — where the per-route 194- // knob is the only one that exists — it silently becomes the effective limit. 195: out.add(violation("POOL_ROUTE_EXCEEDS_TOTAL", profile, "pool.max-connections-per-route")); exit=0 $ git grep -rn 'new PoolSettings(' -- 'adapter/outbound/httpclient/src/main' 'app-bootstrap/src/main' | head app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientProfileFactory.java:83: return new PoolSettings( exit=0 === 8.4b 위반 코드 34종 중 test가 이름으로 잡는 것 === $ for c in $(grep -oE 'violation\("[A-Z0-9_]+"' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/profile/ClientProfileValidator.java | sed 's/violation("//;s/"//' | sort -u); do n=$(git grep -l "$c" -- '*/src/test/*' '*/src/testkit/*' | wc -l); printf '%-45s test=%s\n' "$c" "$n"; done ALLOWED_HOST_MISMATCH test=0 ALLOWED_PORT_MISMATCH test=0 API_KEY_HEADER_NAME_REQUIRED test=0 BASE_URL_QUERY_FORBIDDEN test=1 BASE_URL_USERINFO_FORBIDDEN test=1 BODY_LOGGING_FORBIDDEN test=0 DNS_TIMEOUT_UNSUPPORTED test=0 DYNAMIC_BASE_URL_REQUIRED test=0 DYNAMIC_DEFAULT_CREDENTIAL_FORBIDDEN test=2 DYNAMIC_TARGET_PROXY_UNSUPPORTED test=0 DYNAMIC_TARGET_TRANSPORT_UNSUPPORTED test=0 FULL_URL_RECORDING_FORBIDDEN test=0 HOSTNAME_VERIFICATION_REQUIRED test=2 HTTP2_REQUIRED_TRANSPORT_UNSUPPORTED test=0 HTTP3_STABLE_FORBIDDEN test=2 INVALID_TIMEOUT_BUDGET test=1 JDK_FINE_GRAINED_POOL_UNSUPPORTED test=1 MISSING_PRODUCTION_SETTING test=0 OAUTH2_REGISTRATION_REQUIRED test=0 PLAINTEXT_FALLBACK_FORBIDDEN test=2 PLAINTEXT_PRODUCTION_TARGET test=2 POOL_ROUTE_EXCEEDS_TOTAL test=0 PRODUCTION_SIMPLE_FACTORY_FORBIDDEN test=2 PROXY_CREDENTIAL_UNSUPPORTED test=0 REACTIVE_REDIRECT_UNSUPPORTED test=0 REDIRECT_CROSS_ORIGIN_CREDENTIAL_POLICY_REQUIRED test=0 REDIRECT_POLICY_INVALID test=0 RESPONSE_HARD_MAXIMUM_EXCEEDED test=0 RETRY_BACKOFF_REQUIRED test=0 RETRY_POLICY_CONTRADICTS_ATTEMPTS test=0 TLS_PROTOCOL_FORBIDDEN test=1 TLS_PROTOCOL_SET_REQUIRED test=0 TRUSTED_BASE_URL_REQUIRED test=0 TRUST_ALL_FORBIDDEN test=3 exit=0