feat: grpc 기능 deep 구현

This commit is contained in:
DongHyeonka
2026-08-31 07:28:39 +09:00
parent a24ece9cf7
commit 21234e38cd
400 changed files with 40217 additions and 4 deletions
@@ -0,0 +1,117 @@
# 타입 안전 gRPC 실행 플랫폼 — 실행 계획과 실행 결과
설계 SSOT: [docs/superpowers/specs/2026-08-30-grpc-platform-adaptation-design.md](../specs/2026-08-30-grpc-platform-adaptation-design.md)
원본:
- `docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md` (Stable, Task 153)
- `docs/2026-08-13-grpc-advanced-capabilities-expansion-plan.md` (Advanced, Task 118)
이 문서는 실행 전 계획이자 실행 결과 기록이다. 각 phase는 leaf 단위로 닫혔고, 닫힘 조건은
`./gradlew <gradle-path>:check` 통과다 — 즉 test + spotless + checkstyle + SpotBugs(HIGH) +
Error Prone/`-Werror` + 저장소 전역 게이트 전이 실행이다.
## Phase 0 — 레지스트리와 스캐폴딩
- [x] `src/config/architecture/modules.json`에 18개 leaf 등록 (`grpc:*` 12, `grpc-advanced:*` 6)
- [x] leaf별 `build.gradle` 18개. io.grpc를 쓰는 leaf는 `grpc-bom`을 module scope로 import
- [x] `src/build.gradle`: `:grpc:` / `:grpc-advanced:` 를 plain JUnit+AssertJ 테스트 분기에 추가
(`messaging:*`와 같은 이유 — core-api가 Spring도 io.grpc도 이름 부르지 않는다는 주장을
검증 가능하게 만든다)
- [x] `./gradlew --write-locks ...resolveAndLockAll` 로 lockfile 18개 생성
- [x] `verifyCleanArchitectureDependencies` 통과
## Phase 1 — Foundation (`grpc-core-api`, Stable Task 17 + ledger port)
- [x] Task 1 `GrpcStableModuleCatalog` / `GrpcStableBuildInvariant`
- [x] Task 2 `GrpcMethodName` / `GrpcServiceName` / `GrpcChannelProfileName` / `RpcType` / `GrpcStatusCode`
- [x] Task 3 `RpcIdempotencyProfile` / `WaitForReadyPolicy` / `GrpcMethodPolicy` / `GrpcMethodPolicyCatalog`
- [x] Task 4 `GrpcTransportEvidence` / `GrpcBusinessEvidence` / `GrpcStreamEvidence` / `GrpcExecutionEvidence`
- [x] Task 5 `GrpcFailureCategory` / `GrpcCompletionOutcome` / `GrpcFailureContext` / `GrpcPlatformException`
- [x] Task 6 `GrpcDeadlineProfile` / `GrpcDeadlineBudget` / `GrpcCancellationToken` / `GrpcDeadlineExceededException`
- [x] Task 7 `GrpcRequestContext` / `GrpcMetadataKey` / `GrpcMetadataBudget` / `GrpcClientIdentity`
- [x] 추가: `dev.caskeleton.grpc.ledger` port (`GrpcOperationLedger` 외 3) — 정책 계층이 DB에 의존하지
않고 durable idempotency를 요구할 수 있게 하기 위해 core-api에 둔다
## Phase 2 — Contract governance (`grpc-proto-contract`, `grpc-codegen`, Task 811)
- [x] Task 8 `.proto` 2개 + `buf.yaml` + `GrpcProtoStyleManifest` / `GrpcProtoRuleViolation` / `GrpcProtoContractValidator`
- [x] Task 9 `GrpcBufPolicy` / `GrpcBreakingCategory` / `GrpcSchemaBaseline`
- [x] Task 10 `GrpcCodegenManifest` / `GrpcGeneratedPackagePolicy` / `GrpcCodegenOutput`
- [x] Task 11 `GrpcDescriptorArtifact` / `GrpcConsumerFixture` / `GrpcSchemaArtifactPublisher`
- 편차: protoc·Buf CLI 미실행. 근거는 ADR-GRPC-002
## Phase 3 — Policy (`grpc-policy`, Task 12·16·17·20·2831·33·34·3743)
- [x] Task 12 validation, Task 16 context propagation, Task 17 status/rich error, Task 20 TLS/credential rotation
- [x] Task 28 deadline calculator, Task 29 cancellation coordinator
- [x] Task 30 service config/retry owner, Task 31 retry eligibility/budget/coordinator, Task 42 wait-for-ready
- [x] Task 33 idempotency interceptor, Task 34 completion reconciliation
- [x] Task 3741 stream envelope·writer·flow control·resume token·lifetime
- [x] Task 43 message size / compression / payload boundary
## Phase 4 — Server boundary (`grpc-server`, Task 1315·1819)
- [x] Task 13 boundary rules + raw API import rule, Task 14 typed service adapter SPI
- [x] Task 15 interceptor 순서 계약, Task 18 Netty profile/executor/admission, Task 19 shaded parity
## Phase 5 — Client / discovery / admin / observability / ledger
- [x] `grpc-client` Task 2427
- [x] `grpc-discovery` Task 3536
- [x] `grpc-admin` Task 2123·45
- [x] `grpc-observability` Task 44
- [x] `grpc-operation-ledger-jpa` Task 32 (entity + repository + migration + port impl)
## Phase 6 — Composition과 인증 (`grpc-spring-boot-starter`, `grpc-testkit`)
- [x] Task 52 properties / auto-configuration / startup validator
- [x] Task 46 in-process fixture, Task 47 실제 Netty + TLS/mTLS fixture
- [x] Task 48 fault point / scenario / evidence classifier
- [x] Task 4950 unary·streaming contract suite, Task 51 performance budget/gate
- [x] Task 53 compatibility matrix / release evidence / release gate
- [x] strict test lane 4개 등록 및 실행: `grpcInProcessContractTest`, `grpcNettyContractTest`,
`grpcFaultTest`, `grpcPerformanceTest`
## Phase 7 — Advanced (`grpc-advanced:*`, Advanced Task 118)
- [x] A1·A18 `grpc-advanced-bootstrap`
- [x] A2·A3 `grpc-advanced-edition`
- [x] A4A7 `grpc-advanced-streaming`
- [x] A8A11 `grpc-advanced-resilience`
- [x] A12A16 `grpc-advanced-compat`
- [x] A17 `grpc-advanced-diagnostics`
## Phase 8 — 문서와 게이트
- [x] `src/grpc/CLAUDE.md`, `src/grpc-advanced/CLAUDE.md`
- [x] ADR-GRPC-001..005, ADR-GRPC-ADV-001
- [x] `docs/runbooks/grpc-platform-operations.md`, `docs/runbooks/grpc-advanced-capabilities.md`
- [x] `docs/compatibility/grpc-support-matrix.md`, `docs/compatibility/grpc-advanced-support-matrix.md`
- [x] `verifyRunbookReferences`, `verifyDocumentedLeafCount` 통과
## Phase 9 — 2026-08-31 계획 대조 감사
사용자가 "빠짐없이 반영한게 맞나"를 물어 계획의 `Files:` 절 전체를 기계 대조했다. 결과와 조치는
설계 문서 §6이 SSOT다. 요약:
- [x] 대조 스크립트 실행 → 초기 결과 present=316 / missing=38
- [x] 실제 누락 6건 보완: `GrpcKubernetesProfileValidator`, `ADR-GRPC-006`(discovery/Kubernetes),
xDS `bootstrap.json`, `control-plane-snapshot.json`, `DocumentClientFixture`,
`buf.gen.yaml`+`buf.lock`
- [x] 추가한 픽스처는 전부 실제 검사에 물렸다 — 장식이 되지 않도록:
`GrpcXdsStartupGuard.bootstrapMismatches`(namespace 불일치·비TLS control plane),
redactor가 실제 모양의 CSDS 데이터로 검증, `GrpcConsumerFixture.fromJavaSource`가 fixture
소스에서 요구사항을 역산
- [x] 테스트 클래스 17건을 계획이 명시한 이름으로 분리
- [x] 재대조 → present=341 / missing=13, 잔여 13건은 전부 §6.1–6.4의 기록된 편차
(ADR 파일명 6, codegen convention plugin 3, TLS 인증서 3, Kotlin `.kt` 1)
## 남은 작업 (이 계획 범위 밖, 별도 결정 필요)
1. **런타임 투입.** 신규 leaf 전부 `runtime_memberships: []`다. `app-bootstrap`에 배선하려면 registry
membership 변경 + `verifyRuntimeModuleMembership` 통과가 선행이며, 그것은 별도 결정이다.
2. **`adapter:inbound:grpc` 브리지.** registry의 `allowed_dependencies``grpc-core-api`·`grpc-server`
추가하고 typed service adapter를 배선하는 작업. 플랫폼이 green이 된 지금이 시작점이다.
3. **protoc / Buf CLI 활성화.** ADR-GRPC-002가 조건과 비용을 기록한다.
4. **performance baseline 기록.** 현재 lane은 shape만 검증한다. 알려진 runner에서의 baseline이
regression gate를 만든다.
@@ -0,0 +1,208 @@
# 타입 안전 gRPC 실행 플랫폼 — 이 저장소 규약으로의 어댑팅 설계
두 계획 문서를 이 저장소(`ca-skeleton`)의 실제 구조·정책·빌드 게이트에 맞춰 실행 가능한 형태로
옮기는 설계다.
- 원본 A: `docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md` (Stable, Task 153)
- 원본 B: `docs/2026-08-13-grpc-advanced-capabilities-expansion-plan.md` (Advanced, Task 118)
원본은 `modules/grpc/**` · Gradle Kotlin DSL · 패키지 `io.backend.skeleton.grpc` · Spring Boot 4.1을
전제한다. 이 저장소는 `src/**` · Groovy DSL · `dev.caskeleton` · Spring Boot 4.0.8 · fail-closed
module registry를 쓴다. 원본 Global Constraint의 마지막 항목이 이 어댑팅을 명시적으로 허용한다:
"실제 저장소 구조가 예상 경로와 다르면 파일 경로만 매핑하고 공개 계약·불변 조건·테스트 의미는
변경하지 않는다."
## 1. 패밀리 위치 — 왜 `adapter:inbound:grpc` 확장이 아닌가
원본이 기술하는 것은 인바운드 어댑터 하나가 아니라 **자기 API·SPI·adapter·조립 경계를 가진 벤더드
RPC 플랫폼**이다. 이 저장소에는 그 형태의 선례가 이미 있다 — `messaging:*`. root `CLAUDE.md`
직접 그렇게 규정한다: "vendored messaging platform: a product with its own API, SPI, adapters and
composition boundary, not a layer of this application".
따라서 gRPC 플랫폼도 같은 자리에 둔다.
```text
src/grpc/ → :grpc:* Stable 플랫폼 (12 leaf)
src/grpc-advanced/ → :grpc-advanced:* Advanced/Experimental (6 leaf)
src/adapter/inbound/grpc 기존 인바운드 전송 어댑터 (그대로 유지)
```
`grpc-advanced`를 별도 디렉터리·별도 Gradle prefix로 分離하는 이유는 원본 Stable Task 1의 불변
조건 "Stable starter가 `modules/grpc-advanced`를 참조하면 build를 실패시킨다"를 registry의
`allowed_dependencies`만으로 기계 검증할 수 있게 만들기 위해서다. `verifyCleanArchitectureDependencies`
그 게이트다.
패키지 루트는 `dev.caskeleton.grpc` / `dev.caskeleton.grpc.advanced`다.
## 2. Leaf 매핑 — 원본 31개 모듈 → 이 저장소 18개 leaf
원본 모듈 경계 중 **이 저장소가 이미 다른 메커니즘으로 표현하는 것**만 합친다. 능력(capability)은
하나도 버리지 않는다.
### Stable — `src/grpc/`
| leaf | 원본 모듈 | 담는 Task | 의존 |
| --- | --- | --- | --- |
| `grpc-core-api` | grpc-core-api | 27 | (없음, Java stdlib) |
| `grpc-proto-contract` | grpc-proto-contract | 8 | core-api |
| `grpc-codegen` | grpc-codegen | 911 | core-api, proto-contract |
| `grpc-policy` | grpc-policy | 12, 16, 17, 20, 2831, 33, 34, 3743 | core-api |
| `grpc-server` | grpc-server | 1315, 18, 19 | core-api, policy |
| `grpc-client` | grpc-client | 2427 | core-api, policy |
| `grpc-discovery` | grpc-discovery | 35, 36 | core-api, client |
| `grpc-admin` | grpc-admin | 2123, 45 | core-api, server |
| `grpc-observability` | grpc-observability | 44 | core-api |
| `grpc-operation-ledger-jpa` | grpc-operation-ledger-jpa | 32 | core-api |
| `grpc-spring-boot-starter` | grpc-spring-boot-starter | 52 | 위 전부 |
| `grpc-testkit` | testkit-core + -inprocess + -netty + -fault | 4651, 53 | 위 전부 |
**testkit 4개를 1개로 합친 근거.** 원본이 testkit을 넷으로 쪼갠 목적은 "in-process 증거를 실제
네트워크 증거로 오인하지 않게 한다"이다. 이 저장소는 그 목적을 모듈 경계가 아니라 **strict test
lane** 컨벤션(`ca.strict-test-lane`)으로 이미 표현한다 — lane은 태그/소스셋/명시 테스트 중 하나로만
선택하고, 아무것도 실행하지 않으면 실패하며, up-to-date 결과를 제공하지 않는다. 그래서 네 모듈은
`grpc-testkit` 한 leaf 안의 네 lane이 된다:
`grpcInProcessContractTest`, `grpcNettyContractTest`, `grpcFaultTest`, `grpcPerformanceTest`.
원본의 "in-process는 HTTP/2·TLS 증거가 아니다"는 lane 분리와 `GrpcEvidenceGrade`로 강제한다.
### Advanced — `src/grpc-advanced/`
| leaf | 원본 모듈 | 담는 Task |
| --- | --- | --- |
| `grpc-advanced-bootstrap` | grpc-advanced-bootstrap | A1, A18 |
| `grpc-advanced-edition` | grpc-edition-2024 + grpc-edition-2026-experimental | A2, A3 |
| `grpc-advanced-streaming` | grpc-client-streaming + grpc-bidi-streaming + grpc-manual-flow-control | A4A7 |
| `grpc-advanced-resilience` | grpc-hedging + grpc-custom-resolver + grpc-custom-load-balancer + grpc-xds | A8A11 |
| `grpc-advanced-compat` | grpc-web + grpc-servlet-compat + grpc-integration-bridge + grpc-reactor + grpc-kotlin | A12A16 |
| `grpc-advanced-diagnostics` | grpc-channel-diagnostics | A17 |
Advanced 모듈은 capability마다 leaf를 나누는 대신 **capability grade와 feature flag**
(`GrpcAdvancedCapability` / `GrpcAdvancedFeatureFlags`)로 분리한다. A18의 요구사항
"Edition, streaming, xDS, gRPC-Web, Servlet, language adapter가 서로의 승격을 묶지 않는다"는
leaf 경계가 아니라 capability별 독립 promotion evidence로 표현되므로, 합쳐도 그 불변 조건은 유지된다.
## 3. 원본과 달라지는 지점 (deviation)과 근거
| # | 원본 | 이 저장소 | 근거 |
| --- | --- | --- | --- |
| D1 | Spring Boot 4.1 BOM | Spring Boot 4.0.8 BOM | 저장소 실제 baseline(`src/build.gradle:13`). BOM이 SSOT라는 계약 자체는 유지 |
| D2 | Boot-managed Spring gRPC starter | self-managed `io.grpc` (`ext.grpcVersion`) | 기존 기록된 결정(`adapter/inbound/grpc/README.md` "왜 self-managed Netty 인가"). starter 커플링 회피 |
| D3 | Gradle Kotlin DSL, `settings.gradle.kts` | Groovy DSL + `config/architecture/modules.json` | registry가 leaf 목록의 SSOT이고 settings는 그것을 읽기만 한다 |
| D4 | `io.backend.skeleton.grpc` | `dev.caskeleton.grpc` | 저장소 기본 패키지 |
| D5 | Buf CLI (`bufLint`/`bufBreaking` 등) | 저장소 소유 규칙 엔진 + Gradle verify task | Buf CLI 바이너리가 이 환경에 없다. lint/format/breaking **규칙**을 Java로 구현해 동일 판정을 내리고, CLI는 동일 규칙을 재확인하는 선택 경로로 남긴다 |
| D6 | protoc/grpc-java codegen을 빌드에서 실행 | `.proto` 소스 + codegen **정책·descriptor 계약**만 실행, protoc 실행은 명시적 확장점 | 아래 별도 절 |
| D7 | `grpc-kotlin``.kt` 소스 | Java 쪽 coroutine/Flow **경계 계약**만 | 저장소에 Kotlin 플러그인·소스셋이 없다. A16의 5개 요구사항 중 4개(스키마 단일 소스, cancellation 전파 계약, backpressure 우회 금지, evidence 타입 보존)는 Java 계약으로 표현 가능하고, Kotlin 툴체인 lane은 `GrpcKotlinCompatibilityGate`가 미충족으로 fail-closed 판정한다 |
| D8 | 새 leaf가 곧 런타임 | 신규 leaf 전부 `runtime_memberships: []` (build-only) | `messaging:*`가 처음 착지한 방식과 동일. 런타임 투입은 registry membership 변경 + `verifyRuntimeModuleMembership` 통과가 선행 조건이며, 그것은 별도 결정이다 |
### D6 — protoc 실행을 지금 켜지 않는 이유
이 저장소의 모든 leaf는 예외 없이 spotless(google-java-format) · checkstyle · spotbugs(HIGH) ·
errorprone · `-Werror`를 통과해야 한다(`src/build.gradle``configure(subprojects)` 블록). protoc가
만든 소스는 그 어느 것도 통과하지 못하므로, 실제 codegen을 켜려면 해당 소스셋에서 다섯 게이트를
모두 끄는 carve-out이 필요하다. 저장소에 선례는 있다(`jmh` 소스셋). 하지만 그 carve-out은 Task
10 하나를 위해 품질 게이트를 여는 결정이고, 그 결정은 이 작업 범위 밖의 승인 사항이다.
그래서 Task 8–11의 **불변 조건**은 전부 실행 가능한 형태로 구현한다:
proto style 규칙 검증, 삭제 필드 `reserved` 이력 대조, Buf breaking category(`FILE`) 판정,
generated package와 hand-written package 겹침 금지, descriptor+schema hash 릴리스 아티팩트,
consumer fixture 실패 시 릴리스 차단. 빠지는 것은 protoc 프로세스 호출 하나이고,
`GrpcCodegenManifest`가 그 지점을 단일 owner로 고정한 채 비워둔다.
## 4. 유지되는 원본 불변 조건 (변경 없음)
- 실행 증거 3축(Transport/Business/Stream) 분리, `RESPONSE_HEADERS_SEEN``COMMIT_CONFIRMED` 자동 승격 금지
- `DEADLINE_EXCEEDED` mutation = `COMPLETION_UNKNOWN` 후보, `UNAVAILABLE`만으로 상태 변경 RPC 재호출 금지
- `NON_IDEMPOTENT`에 explicit retry·hedging 금지, explicit retry owner는 하나
- Stable Unary는 positive deadline 필수
- Server Streaming: bounded queue + single serialized writer, partial delivery 후 whole-call retry 금지
- Stable RPC 유형은 Unary·Server Streaming, Client/Bidi는 Advanced
- production reflection 기본 비활성, dev·stage·prod TLS 필수, trust-all 금지
- Stable resolver = Static·DNS, Stable LB = pick_first·round_robin
- metric tag에 raw metadata/payload/actor/tenant/object/stream/idempotency ID 금지
- Netty가 Stable certification transport, in-process는 network/TLS 증거가 아님
## 5. 애플리케이션이 이 패밀리에 도달하는 경로
`messaging:*`의 MSG-015(bridge 부재)를 반복하지 않는다. 계약상의 경로는
```text
application-owned port → adapter:inbound:grpc (typed service adapter) → :grpc:grpc-server SPI
```
이고, Stable Task 13/14가 그 경계를 소유한다(`GrpcApplicationBoundaryRules`,
`GrpcRawApiImportRule`, `GrpcServiceAdapter`). 플랫폼이 green이 된 뒤 마지막 단계에서
`adapter-inbound-grpc``allowed_dependencies``grpc-core-api`·`grpc-server`를 추가하고
브리지를 배선한다. 그 전까지 플랫폼은 self-contained build-only다.
## 6. 원본 파일 목록 대비 대조 (2026-08-31 감사)
두 계획이 `Files:` 절에 명시한 산출물 전체를 기계적으로 대조했다. 감사 스크립트는 각 Task의 파일
basename이 저장소에 존재하는지 확인한다(build 산출물 제외, 단 annotation processor가 생성하는
`spring-configuration-metadata.json`은 build 출력에서 확인).
```text
STABLE (Task 153) : present=250 missing=11
ADVANCED (Task 118) : present= 91 missing= 2
TOTAL : present=341 missing=13
```
잔여 13건은 전부 아래 편차로 설명된다. **행동이 빠진 것은 없다.**
### 6.1 ADR 파일명 (6건) — 저장소 명명 규약
계획은 `ADR-060` ~ `ADR-065` 연번을 쓴다. 이 저장소의 `docs/adr/`는 접두사 규약을 쓴다
(`ADR-WS-001`, `ADR-MONGO-004`, `ADR-WEB-ADV-003`). 내용은 1:1이다.
| 계획 | 이 저장소 |
| --- | --- |
| ADR-060 platform-boundary | `ADR-GRPC-001-platform-family-and-registry-shape.md` |
| ADR-061 execution-evidence | `ADR-GRPC-003-three-axis-execution-evidence.md` |
| ADR-062 retry-idempotency | `ADR-GRPC-004-retry-ownership-and-durable-idempotency.md` |
| ADR-063 streaming-resume | `ADR-GRPC-005-server-streaming-single-writer-and-resume.md` |
| ADR-064 discovery-kubernetes | `ADR-GRPC-006-stable-discovery-and-kubernetes-routing.md` |
| ADR-065 advanced-promotion | `ADR-GRPC-ADV-001-capability-promotion-is-per-capability.md` |
계획에 없던 `ADR-GRPC-002-schema-governance-without-protoc.md`가 추가로 있다 — D6 결정을 기록한다.
### 6.2 codegen convention plugin 3건 — D6
`io.backend.grpc-buf-conventions.gradle.kts`, `io.backend.grpc-codegen-conventions.gradle.kts`,
`.properties`. protoc를 실행하지 않으므로 실행할 convention plugin이 없다. 이들이 고정했을 결정은
`GrpcCodegenManifest`·`GrpcCodegenOutput`·`GrpcGeneratedPackagePolicy`가 Java로 강제하고,
`buf.yaml`·`buf.gen.yaml`·`buf.lock`이 CLI가 있는 환경에서 같은 판정을 내리도록 커밋되어 있다.
### 6.3 TLS 인증서 3건 — 런타임 생성으로 대체
`ca.crt`, `server.crt`, `client.crt`. 커밋된 인증서는 만료되고, 커밋된 개인키는 개인키다.
`GrpcTlsTestMaterial`이 JDK `keytool`로 fixture마다 PKCS12를 생성하고 `close()`가 지운다. Netty의
`SelfSignedCertificate`는 JDK 21에서 `sun.security.x509` 미export로 실패하므로 쓰지 않았다.
### 6.4 Kotlin 소스 1건 — D7
`GrpcCoroutineAdapter.kt`. 저장소에 Kotlin 툴체인이 없다. 계약 요구 4건은 Java로 검증하고
(`GrpcKotlinProfile`, `GrpcCoroutineContextBridge`), compile lane은
`GrpcKotlinCompatibilityGate.supportableHere()``false`로 fail-closed다.
### 6.5 2026-08-31 감사에서 실제로 메꾼 것 (6건)
감사가 아니었으면 남았을 것들이다.
| 항목 | 조치 |
| --- | --- |
| `GrpcKubernetesProfileValidator` | 이름 있는 타입으로 분리. VIP+장기스트림, drain grace < reconnect budget 두 규칙 추가 |
| ADR-064 상당 (discovery/Kubernetes) | `ADR-GRPC-006` 작성 |
| xDS `bootstrap.json` | 테스트 리소스로 추가 + `GrpcXdsStartupGuard.bootstrapMismatches`가 실제로 대조 (namespace 불일치·비TLS control plane 검출) |
| `control-plane-snapshot.json` | 테스트 리소스로 추가 + redactor가 실제 모양의 데이터로 검증됨 |
| `DocumentClientFixture.java` + fixture `build.gradle.kts` | 추가. `GrpcConsumerFixture.fromJavaSource`가 fixture 소스에서 service·method·package 요구사항을 **역산**하므로 손으로 적은 목록이 아니다 |
| `buf.gen.yaml`, `buf.lock` | 추가 + proto contract 테스트가 내용을 검증 |
### 6.6 테스트 클래스 17건 — 감사 후 계획대로 분리
감사 시점에 leaf별로 통합돼 있던 테스트를 계획이 명시한 이름으로 분리했다. 분리 전에도 모든
타입이 실제로 검증되고 있었으나(통합 클래스가 해당 타입을 참조), 계획과의 추적성을 위해 나눴다:
`GrpcNettyVariantSelectorTest`, `GrpcReflectionPolicyTest`, `GrpcClientMetadataPolicyTest`,
`GrpcKubernetesProfileTest`, `GrpcEdition2026GuardTest`, `GrpcClientStreamPolicyTest`,
`GrpcClientMessageDeduplicatorTest`, `GrpcBidiSequenceTrackerTest`, `GrpcDemandControllerTest`,
`GrpcHedgingEligibilityTest`, `GrpcResolverSafetyPolicyTest`, `GrpcLoadBalancerSafetyPolicyTest`,
`GrpcXdsStartupGuardTest`, `GrpcWebCompatibilityGateTest`, `GrpcServletStartupValidatorTest`,
`GrpcIntegrationBridgePolicyTest`, `GrpcReactorContextBridgeTest`, `GrpcKotlinCompatibilityGateTest`.