외부 리뷰("현재 상태를 유지하기 위한 검증이 너무 많고, 그 검증 자체를
다시 검증하는 구조까지 생겼다")를 설계 문서로 정리하고 코드로 반영한다.
설계·판단 근거는 docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md.
삭제
- .github/ci-gate-matrix.yml(1,025줄) + verify-gate-matrix.sh(568줄):
Gradle task graph와 workflow graph에 이미 있는 정보의 3중 복제
- verify-gradle-wrapper.sh(799줄): workflow 바이트 해시 잠금.
wrapper 검증은 gradle/actions/wrapper-validation(full SHA 핀)에 위임
- DeveloperExperienceContractTest 등의 CI YAML mutation 테스트:
애플리케이션 test suite가 GitHub Actions YAML 파서를 검증하던 계층 역전
- 문서 drift 파서: verifyReadmeCommands, verifyRunbookReferences,
verifyDocumentedLeafCount, verifyTestSourceSetRegistry
- 빈 레지스트리를 지키던 커스텀 YAML 파서: verifyTrivyignore,
verifyQuarantineSunset, flaky-quarantine.yaml
- verifyConfigurationPropertiesProcessor, verifyOneTypePerFile:
각각 ca.spring-config convention과 Checkstyle OneTopLevelClass가 대체
- 정상 입력으로도 성공할 수 없던 messaging always-fail task
- ModuleRegistry의 JSON 필드 집합 정확 일치, sample-portfolio negative guard
이동
- java/quality/spring 공통 설정을 configure(subprojects) 블록에서
ca.java-conventions / ca.quality-conventions / ca.java-library /
ca.spring-library convention plugin으로
- 아키텍처 검증을 ca.architecture로, JPA·messaging qualification을
gradle/qualification/ 아래로, verifyEnvKeys를 :app-bootstrap 소유로
완화
- Git revision은 releaseCheck·아카이브 생성에서만 요구. 일반 빌드는 SNAPSHOT
- SpotBugs/FindSecBugs는 로컬 check에서 빼고 qualityCheck 레인으로
task 계층
- leaf check는 그 leaf만. architectureCheck / qualityCheck /
configContractCheck / integrationCheck / ci / releaseCheck로 이름 분리
CI
- _reusable-gradle.yml 신규. checkout + wrapper validation + JDK/캐시 공통화
- fileserver-release.yml -> fileserver-certification.yml (CD가 아니라 certification)
- GitHub Actions = CI + artifact, Argo CD = CD 경계를 docs/ci-cd/boundary.md로 고정
순증감 +3,274 / -7,483.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
180 lines
9.2 KiB
YAML
180 lines
9.2 KiB
YAML
# HTTP Client platform — Java field path to environment variable template.
|
|
#
|
|
# The SSOT is HttpClientPlatformSettings. HttpClientEnvironmentKeys derives this list from the
|
|
# record tree at runtime, HttpClientPlatformEnvManifestTest fails when the two disagree in either
|
|
# direction, and the platform refuses to start on an APP_HTTPCLIENT_ variable that is not here. So a
|
|
# field added with no entry, an entry whose field was renamed, and a misspelled variable in a
|
|
# deployment are all failures rather than silence.
|
|
#
|
|
# `N` and `M` are list indices, not literals: `N` for the outermost list, `M` for a list inside it.
|
|
# `app.httpclient.clients[N].base-url` is set as APP_HTTPCLIENT_CLIENTS_0_BASE_URL for the first
|
|
# client, and `clients[N].allowed-hosts[M]` as APP_HTTPCLIENT_CLIENTS_0_ALLOWED_HOSTS_0.
|
|
#
|
|
# Only APP_HTTPCLIENT_ENABLED is registered in docs/registries/env-keys.yaml and shipped in
|
|
# src/.env: it is the only key with a deployment-independent value, and it is the only one the
|
|
# three-way :app-bootstrap:verifyEnvKeys gate can express. Everything below is per deployment and is set directly
|
|
# in the environment — templating an indexed client in application.yml would materialise a nameless
|
|
# client in every deployment, which the settings' aggregate validation refuses.
|
|
#
|
|
# This file lives beside the HTTP Client documentation rather than in docs/registries, which is a
|
|
# fail-closed catalog of exactly eight contract registries with a fixed row schema
|
|
# (owner_branch/compatibility_impact/required_test per row). A field-to-variable mapping does not
|
|
# have that shape, and admitting it would have meant loosening a gate rather than satisfying one.
|
|
#
|
|
# Secrets are referenced, never carried: authentication.secret-reference, tls.*-material-reference
|
|
# and proxy.credential-provider name material that a secret backend resolves. Putting the material
|
|
# itself in one of these variables defeats the indirection they exist for.
|
|
fields:
|
|
- field: enabled
|
|
env: APP_HTTPCLIENT_ENABLED
|
|
- field: clients[N].name
|
|
env: APP_HTTPCLIENT_CLIENTS_N_NAME
|
|
- field: clients[N].mode
|
|
env: APP_HTTPCLIENT_CLIENTS_N_MODE
|
|
- field: clients[N].base-url
|
|
env: APP_HTTPCLIENT_CLIENTS_N_BASE_URL
|
|
- field: clients[N].allowed-hosts[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_ALLOWED_HOSTS_M
|
|
- field: clients[N].allowed-ports[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_ALLOWED_PORTS_M
|
|
- field: clients[N].api
|
|
env: APP_HTTPCLIENT_CLIENTS_N_API
|
|
- field: clients[N].transport
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TRANSPORT
|
|
- field: clients[N].protocols[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROTOCOLS_M
|
|
- field: clients[N].pool.max-total-connections
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_MAX_TOTAL_CONNECTIONS
|
|
- field: clients[N].pool.max-connections-per-route
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_MAX_CONNECTIONS_PER_ROUTE
|
|
- field: clients[N].pool.max-pending-acquires
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_MAX_PENDING_ACQUIRES
|
|
- field: clients[N].pool.pending-acquire-timeout
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_PENDING_ACQUIRE_TIMEOUT
|
|
- field: clients[N].pool.max-idle-time
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_MAX_IDLE_TIME
|
|
- field: clients[N].pool.max-life-time
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_MAX_LIFE_TIME
|
|
- field: clients[N].pool.validate-after-inactivity
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_VALIDATE_AFTER_INACTIVITY
|
|
- field: clients[N].pool.eviction-interval
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_EVICTION_INTERVAL
|
|
- field: clients[N].pool.shutdown-timeout
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_SHUTDOWN_TIMEOUT
|
|
- field: clients[N].pool.requires-route-pool
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_REQUIRES_ROUTE_POOL
|
|
- field: clients[N].pool.requires-bounded-pending-queue
|
|
env: APP_HTTPCLIENT_CLIENTS_N_POOL_REQUIRES_BOUNDED_PENDING_QUEUE
|
|
- field: clients[N].timeout.dns
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_DNS
|
|
- field: clients[N].timeout.connect
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_CONNECT
|
|
- field: clients[N].timeout.tls-handshake
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_TLS_HANDSHAKE
|
|
- field: clients[N].timeout.proxy-connect
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_PROXY_CONNECT
|
|
- field: clients[N].timeout.request-write-idle
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_REQUEST_WRITE_IDLE
|
|
- field: clients[N].timeout.response-header
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_RESPONSE_HEADER
|
|
- field: clients[N].timeout.read-idle
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_READ_IDLE
|
|
- field: clients[N].timeout.total-call
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_TOTAL_CALL
|
|
- field: clients[N].timeout.streaming-idle
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TIMEOUT_STREAMING_IDLE
|
|
- field: clients[N].redirect.enabled
|
|
env: APP_HTTPCLIENT_CLIENTS_N_REDIRECT_ENABLED
|
|
- field: clients[N].redirect.max-hops
|
|
env: APP_HTTPCLIENT_CLIENTS_N_REDIRECT_MAX_HOPS
|
|
- field: clients[N].redirect.allow-cross-origin
|
|
env: APP_HTTPCLIENT_CLIENTS_N_REDIRECT_ALLOW_CROSS_ORIGIN
|
|
- field: clients[N].request.max-body-bytes
|
|
env: APP_HTTPCLIENT_CLIENTS_N_REQUEST_MAX_BODY_BYTES
|
|
- field: clients[N].request.compression
|
|
env: APP_HTTPCLIENT_CLIENTS_N_REQUEST_COMPRESSION
|
|
- field: clients[N].response.max-wire-bytes
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RESPONSE_MAX_WIRE_BYTES
|
|
- field: clients[N].response.max-decoded-bytes
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RESPONSE_MAX_DECODED_BYTES
|
|
- field: clients[N].response.allowed-content-types[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RESPONSE_ALLOWED_CONTENT_TYPES_M
|
|
- field: clients[N].authentication.type
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_TYPE
|
|
- field: clients[N].authentication.registration-id
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_REGISTRATION_ID
|
|
- field: clients[N].authentication.scopes[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_SCOPES_M
|
|
- field: clients[N].authentication.audience
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_AUDIENCE
|
|
- field: clients[N].authentication.header-name
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_HEADER_NAME
|
|
- field: clients[N].authentication.secret-reference
|
|
env: APP_HTTPCLIENT_CLIENTS_N_AUTHENTICATION_SECRET_REFERENCE
|
|
- field: clients[N].retry.policy
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_POLICY
|
|
- field: clients[N].retry.max-attempts
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_MAX_ATTEMPTS
|
|
- field: clients[N].retry.base-backoff
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_BASE_BACKOFF
|
|
- field: clients[N].retry.max-backoff
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_MAX_BACKOFF
|
|
- field: clients[N].retry.jitter
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_JITTER
|
|
- field: clients[N].retry.retry-after
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_RETRY_AFTER
|
|
- field: clients[N].retry.budget
|
|
env: APP_HTTPCLIENT_CLIENTS_N_RETRY_BUDGET
|
|
- field: clients[N].observability.operation-name-required
|
|
env: APP_HTTPCLIENT_CLIENTS_N_OBSERVABILITY_OPERATION_NAME_REQUIRED
|
|
- field: clients[N].observability.full-url-recording
|
|
env: APP_HTTPCLIENT_CLIENTS_N_OBSERVABILITY_FULL_URL_RECORDING
|
|
- field: clients[N].observability.body-logging
|
|
env: APP_HTTPCLIENT_CLIENTS_N_OBSERVABILITY_BODY_LOGGING
|
|
- field: clients[N].tls.profile-id
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_PROFILE_ID
|
|
- field: clients[N].tls.protocols[M]
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_PROTOCOLS_M
|
|
- field: clients[N].tls.hostname-verification
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_HOSTNAME_VERIFICATION
|
|
- field: clients[N].tls.trust-all
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_TRUST_ALL
|
|
- field: clients[N].tls.allow-plain-http
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_ALLOW_PLAIN_HTTP
|
|
- field: clients[N].tls.trust-material-reference
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_TRUST_MATERIAL_REFERENCE
|
|
- field: clients[N].tls.key-material-reference
|
|
env: APP_HTTPCLIENT_CLIENTS_N_TLS_KEY_MATERIAL_REFERENCE
|
|
- field: clients[N].proxy.enabled
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_ENABLED
|
|
- field: clients[N].proxy.host
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_HOST
|
|
- field: clients[N].proxy.port
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_PORT
|
|
- field: clients[N].proxy.type
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_TYPE
|
|
- field: clients[N].proxy.credential-provider
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_CREDENTIAL_PROVIDER
|
|
- field: clients[N].proxy.connect-timeout
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_CONNECT_TIMEOUT
|
|
- field: clients[N].proxy.import-ambient-no-proxy
|
|
env: APP_HTTPCLIENT_CLIENTS_N_PROXY_IMPORT_AMBIENT_NO_PROXY
|
|
- field: clients[N].experimental-acknowledgement
|
|
env: APP_HTTPCLIENT_CLIENTS_N_EXPERIMENTAL_ACKNOWLEDGEMENT
|
|
- field: dynamic-targets[N].name
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_NAME
|
|
- field: dynamic-targets[N].allowed-schemes[M]
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_ALLOWED_SCHEMES_M
|
|
- field: dynamic-targets[N].allowed-ports[M]
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_ALLOWED_PORTS_M
|
|
- field: dynamic-targets[N].allowed-host-suffixes[M]
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_ALLOWED_HOST_SUFFIXES_M
|
|
- field: dynamic-targets[N].allowed-hosts[M]
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_ALLOWED_HOSTS_M
|
|
- field: dynamic-targets[N].max-redirect-hops
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_MAX_REDIRECT_HOPS
|
|
- field: dynamic-targets[N].trace-propagation
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_TRACE_PROPAGATION
|
|
- field: dynamic-targets[N].blocked-cidrs[M]
|
|
env: APP_HTTPCLIENT_DYNAMIC_TARGETS_N_BLOCKED_CIDRS_M
|