Files
DongHyeonkaandClaude Opus 5 ef947e5bb0 refactor(build,ci): 현재 상태 검증을 걷어내고 불변조건만 남기는 검증 표면 축소
외부 리뷰("현재 상태를 유지하기 위한 검증이 너무 많고, 그 검증 자체를
다시 검증하는 구조까지 생겼다")를 설계 문서로 정리하고 코드로 반영한다.
설계·판단 근거는 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>
2026-09-16 20:33:19 +09:00

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