# 타입 안전 gRPC Advanced Capability Expansion Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Stable Unary·Server Streaming 플랫폼을 변경하지 않고 Edition 2024/2026, Client/Bidirectional Streaming, Manual Flow Control, Hedging, Custom Resolver·Load Balancer, xDS, gRPC-Web, Servlet, Spring Integration, Reactor·Kotlin과 Channel Diagnostics를 독립적으로 검증·승격할 수 있는 Advanced capability 계층을 구축한다. **Architecture:** 모든 Advanced 기능은 `modules/grpc-advanced`에 격리하고 명시적 feature flag를 요구한다. Stable method policy, deadline, execution evidence, security, status, metadata, observability와 streaming guardrail을 그대로 소비하며 raw gRPC escape hatch를 제공하지 않는다. 각 capability는 자체 Compatibility·Security·Fault·Performance·Soak gate를 통과한 뒤에만 Advanced Stable로 승격된다. **Tech Stack:** Java 21, Spring Boot 4.1 BOM, Boot-managed gRPC Java/Protobuf, Protobuf Editions, grpc-xds, grpc-web proxy/Envoy, Servlet HTTP/2, Spring Integration 7.1, Project Reactor, Kotlin coroutines/Flow, Channelz/CSDS, JUnit 5, AssertJ, Testcontainers, Toxiproxy. ## Global Constraints - Stable Task 1–53과 Stable Release Gate가 먼저 완료돼야 한다. - Advanced module root는 `modules/grpc-advanced`이다. - Root package는 `io.backend.skeleton.grpc.advanced`이다. - 모든 capability는 `backend.grpc.advanced.*` 아래의 명시적 feature flag를 요구한다. - Stable starter는 Advanced module에 compile/runtime dependency를 갖지 않는다. - Edition 2024는 opt-in이고 Edition 2026은 Watch/Experimental이다. - Client Streaming과 Bidirectional Streaming은 session·sequence·dedup·resume 계약 없이 활성화하지 않는다. - Manual Flow Control이 raw StreamObserver를 application에 직접 반환해서는 안 된다. - Hedging은 READ_ONLY Unary만 허용한다. - Custom Resolver와 Load Balancer는 Stable channel security·retry owner·observability를 우회하지 않는다. - xDS는 proxyless GR3 Experimental로 유지한다. - gRPC-Web은 Unary와 Server Streaming만 지원한다. - Client Streaming·Bidirectional Streaming의 gRPC-Web 지원을 선언하지 않는다. - Servlet transport는 Netty certification을 대체하지 않는다. - Spring Integration bridge는 Generated Stub·Service 타입 안전 계약을 대체하지 않는다. - Reactor와 Kotlin adapter는 Stable Core 타입과 execution evidence를 유지한다. - Channelz·CSDS는 관리자 전용이며 credential·metadata·payload를 노출하지 않는다. - 각 capability는 독립 승격이 가능해야 한다. - 모든 task는 red-green TDD와 독립 commit으로 끝난다. --- ## Execution Baseline ```text Stable Task 1–53 → Advanced Task 1–18 ``` ## Advanced Module Map ```text modules/grpc-advanced/ ├── grpc-advanced-bootstrap ├── grpc-edition-2024 ├── grpc-edition-2026-experimental ├── grpc-client-streaming ├── grpc-bidi-streaming ├── grpc-manual-flow-control ├── grpc-hedging ├── grpc-custom-resolver ├── grpc-custom-load-balancer ├── grpc-xds ├── grpc-web ├── grpc-servlet-compat ├── grpc-integration-bridge ├── grpc-reactor ├── grpc-kotlin └── grpc-channel-diagnostics ``` ## Capability Classification | Capability | Initial grade | Stable default | |---|---|---:| | Edition 2024 | Advanced opt-in | No | | Edition 2026 | Watch/Experimental | No | | Client Streaming | Advanced | No | | Bidirectional Streaming | Advanced | No | | Manual Flow Control | Advanced | No | | Hedging | Experimental | No | | Custom Resolver | Advanced | No | | Custom Load Balancer | Experimental | No | | xDS | Experimental | No | | gRPC-Web | Advanced compatibility | No | | Servlet HTTP/2 | Compatibility | No | | Spring Integration bridge | Optional | No | | Reactor adapter | Optional Advanced | No | | Kotlin adapter | Optional Advanced | No | | Channelz/CSDS diagnostics | Admin Advanced | No | ## Delivery Phases | Phase | Tasks | Result | |---|---:|---| | Boundary·Edition | 1–3 | Feature isolation and schema evaluation lanes | | Streaming | 4–7 | Client/Bidi session, checkpoint and manual flow control | | Resilience·Discovery | 8–11 | Hedging, custom resolver/LB and xDS | | Compatibility Bridges | 12–16 | gRPC-Web, Servlet, Integration, Reactor, Kotlin | | Diagnostics·Promotion | 17–18 | Admin diagnostics, infrastructure testkit and promotion gate | --- ### Task 1: Advanced Module Boundary와 Feature Flag **Files:** - Modify: `settings.gradle.kts` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedCapability.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedFeatureFlags.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedModuleGuard.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedCapabilityDisabledException.java` - Create: `modules/grpc-advanced/build.gradle.kts` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/build.gradle.kts` - Test: `modules/grpc-advanced/grpc-advanced-bootstrap/src/test/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedModuleGuardTest.java` **Interfaces:** - Consumes: Stable Task 53 release evidence와 Spring Boot environment. - Produces: Advanced·Experimental capability가 Stable starter에 자동 유입되지 않도록 하는 dependency·feature flag 경계. **Implementation requirements:** - 모든 capability는 `backend.grpc.advanced.*` 아래의 명시적 flag를 요구한다. - Stable starter는 Advanced module에 compile/runtime dependency를 갖지 않는다. - capability grade는 `ADVANCED_STABLE`, `EXPERIMENTAL`, `WATCH`, `DISABLED`로 구분한다. - production에서 Experimental capability는 별도 승인 profile 없이는 시작되지 않는다. - Advanced module은 Stable public types를 소비하지만 Stable guardrail을 우회하지 않는다. - [ ] **Step 1: Write the failing test** ```java class GrpcAdvancedModuleGuardTest { @org.junit.jupiter.api.Test void disabledCapabilityCannotStart() { var guard = new GrpcAdvancedModuleGuard( GrpcAdvancedFeatureFlags.disabled()); org.assertj.core.api.Assertions.assertThatThrownBy( () -> guard.requireEnabled( GrpcAdvancedCapability.XDS)) .isInstanceOf( GrpcAdvancedCapabilityDisabledException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-advanced-bootstrap:test --tests 'io.backend.skeleton.grpc.advanced.bootstrap.GrpcAdvancedModuleGuardTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public enum GrpcAdvancedCapability { EDITION_2024, EDITION_2026, CLIENT_STREAMING, BIDI_STREAMING, MANUAL_FLOW_CONTROL, HEDGING, CUSTOM_RESOLVER, CUSTOM_LOAD_BALANCER, XDS, GRPC_WEB, SERVLET_COMPAT, SPRING_INTEGRATION, REACTOR, KOTLIN, CHANNEL_DIAGNOSTICS } public record GrpcAdvancedFeatureFlags( java.util.Set enabled) { public static GrpcAdvancedFeatureFlags disabled() { return new GrpcAdvancedFeatureFlags(java.util.Set.of()); } public boolean isEnabled(GrpcAdvancedCapability capability) { return enabled.contains(capability); } } public final class GrpcAdvancedModuleGuard { private final GrpcAdvancedFeatureFlags flags; public GrpcAdvancedModuleGuard( GrpcAdvancedFeatureFlags flags) { this.flags = flags; } public void requireEnabled( GrpcAdvancedCapability capability) { if (!flags.isEnabled(capability)) { throw new GrpcAdvancedCapabilityDisabledException( capability.name()); } } } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-advanced-bootstrap:test --tests 'io.backend.skeleton.grpc.advanced.bootstrap.GrpcAdvancedModuleGuardTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedCapability.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedFeatureFlags.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedModuleGuard.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedCapabilityDisabledException.java' 'modules/grpc-advanced/build.gradle.kts' 'modules/grpc-advanced/grpc-advanced-bootstrap/build.gradle.kts' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/test/java/io/backend/skeleton/grpc/advanced/bootstrap/GrpcAdvancedModuleGuardTest.java' 'settings.gradle.kts' git commit -m "build: isolate grpc advanced modules" ``` ### Task 2: Protobuf Edition 2024 Opt-in Lane **Files:** - Create: `modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024Policy.java` - Create: `modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEditionCompatibilityReport.java` - Create: `modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024Gate.java` - Create: `modules/grpc-advanced/grpc-edition-2024/src/main/proto/edition2024/compatibility.proto` - Create: `modules/grpc-advanced/grpc-edition-2024/buf.yaml` - Test: `modules/grpc-advanced/grpc-edition-2024/src/test/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024GateTest.java` **Interfaces:** - Consumes: Stable proto3+optional descriptor, consumer language/toolchain fixtures와 Buf gate. - Produces: Edition 2024 schema를 Stable 기본값과 분리하여 검증·비교하는 Advanced lane. **Implementation requirements:** - Edition 2024는 module-level opt-in으로만 사용한다. - Java뿐 아니라 지원 consumer language/toolchain compile evidence를 요구한다. - proto3+optional과 wire/source/JSON behavior를 비교한다. - public service를 Edition 2024로 이동하려면 승격 ADR과 consumer migration이 필요하다. - Edition 2024 failure가 Stable proto3 release를 차단하지 않되 promotion을 차단한다. - [ ] **Step 1: Write the failing test** ```java class GrpcEdition2024GateTest { @org.junit.jupiter.api.Test void missingConsumerEvidenceBlocksPromotion() { var gate = new GrpcEdition2024Gate(); org.assertj.core.api.Assertions.assertThat( gate.promotable(new GrpcEditionCompatibilityReport( true, false, true))).isFalse(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-edition-2024:test --tests 'io.backend.skeleton.grpc.advanced.edition.GrpcEdition2024GateTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcEditionCompatibilityReport( boolean javaCompatible, boolean consumerLanguagesCompatible, boolean jsonCompatible) {} public final class GrpcEdition2024Gate { public boolean promotable( GrpcEditionCompatibilityReport report) { return report.javaCompatible() && report.consumerLanguagesCompatible() && report.jsonCompatible(); } } public record GrpcEdition2024Policy( boolean explicitOptIn, boolean stableDefault) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-edition-2024:test --tests 'io.backend.skeleton.grpc.advanced.edition.GrpcEdition2024GateTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024Policy.java' 'modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEditionCompatibilityReport.java' 'modules/grpc-advanced/grpc-edition-2024/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024Gate.java' 'modules/grpc-advanced/grpc-edition-2024/src/main/proto/edition2024/compatibility.proto' 'modules/grpc-advanced/grpc-edition-2024/buf.yaml' 'modules/grpc-advanced/grpc-edition-2024/src/test/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2024GateTest.java' git commit -m "test: add protobuf edition 2024 lane" ``` ### Task 3: Protobuf Edition 2026 Watch Lane **Files:** - Create: `modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026Status.java` - Create: `modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026WatchReport.java` - Create: `modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026Guard.java` - Test: `modules/grpc-advanced/grpc-edition-2026-experimental/src/test/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026GuardTest.java` **Interfaces:** - Consumes: Task 2 Edition 2024 report와 current Protobuf toolchain metadata. - Produces: released-edition 상태와 toolchain support가 확정되기 전 Stable 사용을 차단하는 Watch lane. **Implementation requirements:** - Edition 2026은 `WATCH` 또는 `EXPERIMENTAL` 상태만 가진다. - official release status, protoc support, Buf support, Java/runtime support를 분리 기록한다. - Stable public contract source로 사용하지 않는다. - CI failure는 watch report를 생성하지만 Stable build와 분리한다. - 승격은 별도 ADR와 complete cross-language evidence를 요구한다. - [ ] **Step 1: Write the failing test** ```java class GrpcEdition2026GuardTest { @org.junit.jupiter.api.Test void edition2026CannotBeStable() { var guard = new GrpcEdition2026Guard(); org.assertj.core.api.Assertions.assertThatThrownBy( () -> guard.requireStable( GrpcEdition2026Status.WATCH)) .isInstanceOf(IllegalStateException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-edition-2026-experimental:test --tests 'io.backend.skeleton.grpc.advanced.edition.GrpcEdition2026GuardTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public enum GrpcEdition2026Status { WATCH, EXPERIMENTAL } public final class GrpcEdition2026Guard { public void requireStable( GrpcEdition2026Status status) { throw new IllegalStateException( "Edition 2026 is not a Stable contract"); } } public record GrpcEdition2026WatchReport( boolean officiallyReleased, boolean protocSupported, boolean bufSupported, boolean javaSupported) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-edition-2026-experimental:test --tests 'io.backend.skeleton.grpc.advanced.edition.GrpcEdition2026GuardTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026Status.java' 'modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026WatchReport.java' 'modules/grpc-advanced/grpc-edition-2026-experimental/src/main/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026Guard.java' 'modules/grpc-advanced/grpc-edition-2026-experimental/src/test/java/io/backend/skeleton/grpc/advanced/edition/GrpcEdition2026GuardTest.java' git commit -m "test: track protobuf edition 2026" ``` ### Task 4: Client Streaming Session·Sequence Contract **Files:** - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamSessionId.java` - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamMessage.java` - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamPolicy.java` - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamState.java` - Test: `modules/grpc-advanced/grpc-client-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamPolicyTest.java` **Interfaces:** - Consumes: Stable method policy, deadline, metadata, execution evidence와 generated client-streaming RPC. - Produces: session ID, client sequence, half-close, server result를 명시하는 client-streaming contract. **Implementation requirements:** - 각 stream message는 session generation과 monotonic client sequence를 가진다. - half-close 이후 새 message를 거부한다. - 전체 stream을 transparent retry하지 않는다. - server final response 전에 일부 message가 적용됐을 수 있음을 evidence로 보존한다. - stream max duration, idle timeout, message rate와 in-flight limit을 요구한다. - [ ] **Step 1: Write the failing test** ```java class GrpcClientStreamPolicyTest { @org.junit.jupiter.api.Test void duplicateOrDecreasingSequenceIsRejected() { var policy = new GrpcClientStreamPolicy(); policy.accept(1); org.assertj.core.api.Assertions.assertThatThrownBy( () -> policy.accept(1)) .isInstanceOf(IllegalArgumentException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-client-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcClientStreamPolicyTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcClientStreamSessionId(String value) {} public record GrpcClientStreamMessage( GrpcClientStreamSessionId sessionId, long sequence, T payload) {} public final class GrpcClientStreamPolicy { private long last; public void accept(long sequence) { if (sequence <= last) { throw new IllegalArgumentException( "client stream sequence must increase"); } last = sequence; } } public enum GrpcClientStreamState { OPEN, HALF_CLOSED, COMPLETED, CANCELLED, UNKNOWN } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-client-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcClientStreamPolicyTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamSessionId.java' 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamMessage.java' 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamPolicy.java' 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamState.java' 'modules/grpc-advanced/grpc-client-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamPolicyTest.java' git commit -m "feat: define grpc client streaming sessions" ``` ### Task 5: Client Streaming Dedup·Checkpoint·Resume **Files:** - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamCheckpoint.java` - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicator.java` - Create: `modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamResumeDecision.java` - Test: `modules/grpc-advanced/grpc-client-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicatorTest.java` **Interfaces:** - Consumes: Task 4 client stream session and application checkpoint storage. - Produces: 재연결 시 client message 중복을 제거하고 last applied sequence에서 resume하는 contract. **Implementation requirements:** - dedup identity는 stream session + sequence다. - checkpoint는 application side effect와 가능한 한 같은 transaction에 저장한다. - checkpoint 이전 duplicate는 replay 결과를 반환하거나 무시한다. - history·session이 만료되면 새 stream/full resync를 요구한다. - transport ACK와 application applied checkpoint를 구분한다. - [ ] **Step 1: Write the failing test** ```java class GrpcClientMessageDeduplicatorTest { @org.junit.jupiter.api.Test void alreadyAppliedSequenceIsDuplicate() { var deduplicator = new GrpcClientMessageDeduplicator(10); org.assertj.core.api.Assertions.assertThat( deduplicator.accept(9)).isFalse(); org.assertj.core.api.Assertions.assertThat( deduplicator.accept(11)).isTrue(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-client-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcClientMessageDeduplicatorTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcClientStreamCheckpoint( String sessionId, long lastAppliedSequence, String businessRevision) {} public final class GrpcClientMessageDeduplicator { private long lastApplied; public GrpcClientMessageDeduplicator(long lastApplied) { this.lastApplied = lastApplied; } public boolean accept(long sequence) { if (sequence <= lastApplied) { return false; } lastApplied = sequence; return true; } } public enum GrpcClientStreamResumeDecision { RESUME, START_NEW, FULL_RESYNC_REQUIRED } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-client-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcClientMessageDeduplicatorTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamCheckpoint.java' 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicator.java' 'modules/grpc-advanced/grpc-client-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientStreamResumeDecision.java' 'modules/grpc-advanced/grpc-client-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicatorTest.java' git commit -m "feat: add grpc client stream resume" ``` ### Task 6: Bidirectional Streaming Dual Sequence·Lifecycle **Files:** - Create: `modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSession.java` - Create: `modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiDirectionState.java` - Create: `modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSequenceTracker.java` - Create: `modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiResumeState.java` - Test: `modules/grpc-advanced/grpc-bidi-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSequenceTrackerTest.java` **Interfaces:** - Consumes: Tasks 4–5 client stream contract와 Stable server stream envelope. - Produces: client→server와 server→client sequence·flow-control·resume를 독립적으로 관리하는 bidi session. **Implementation requirements:** - 양 방향 sequence를 하나의 counter로 합치지 않는다. - 각 방향의 half-close와 cancellation을 독립적으로 기록한다. - 양 방향 bounded queue와 single writer를 사용한다. - resume token은 client applied/server applied sequence를 모두 보존한다. - session generation mismatch는 full session restart를 요구한다. - [ ] **Step 1: Write the failing test** ```java class GrpcBidiSequenceTrackerTest { @org.junit.jupiter.api.Test void directionsAdvanceIndependently() { var tracker = new GrpcBidiSequenceTracker(); tracker.acceptClient(1); tracker.acceptServer(1); tracker.acceptServer(2); org.assertj.core.api.Assertions.assertThat( tracker.clientSequence()).isEqualTo(1); org.assertj.core.api.Assertions.assertThat( tracker.serverSequence()).isEqualTo(2); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-bidi-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcBidiSequenceTrackerTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public final class GrpcBidiSequenceTracker { private long client; private long server; public void acceptClient(long sequence) { if (sequence <= client) { throw new IllegalArgumentException("client sequence"); } client = sequence; } public void acceptServer(long sequence) { if (sequence <= server) { throw new IllegalArgumentException("server sequence"); } server = sequence; } public long clientSequence() { return client; } public long serverSequence() { return server; } } public record GrpcBidiResumeState( long lastClientApplied, long lastServerApplied, long generation) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-bidi-streaming:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcBidiSequenceTrackerTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSession.java' 'modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiDirectionState.java' 'modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSequenceTracker.java' 'modules/grpc-advanced/grpc-bidi-streaming/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiResumeState.java' 'modules/grpc-advanced/grpc-bidi-streaming/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcBidiSequenceTrackerTest.java' git commit -m "feat: define grpc bidi stream lifecycle" ``` ### Task 7: Manual Flow Control 승인 API **Files:** - Create: `modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcManualFlowControlPolicy.java` - Create: `modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandController.java` - Create: `modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandDecision.java` - Test: `modules/grpc-advanced/grpc-manual-flow-control/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandControllerTest.java` **Interfaces:** - Consumes: Stable bounded flow-control policy와 gRPC readiness/request APIs. - Produces: 승인된 streaming method가 수신 demand와 outbound readiness를 직접 제어하는 GR2 API. **Implementation requirements:** - manual inbound request 수와 outstanding demand를 제한한다. - application code에 raw observer를 직접 반환하지 않는다. - read와 write 양쪽이 서로 기다리는 deadlock을 탐지하는 watchdog을 둔다. - demand와 queue high-watermark를 metric으로 기록한다. - manual mode가 없는 method에는 자동 flow control을 유지한다. - [ ] **Step 1: Write the failing test** ```java class GrpcDemandControllerTest { @org.junit.jupiter.api.Test void demandCannotExceedConfiguredWindow() { var controller = new GrpcDemandController(4); org.assertj.core.api.Assertions.assertThatThrownBy( () -> controller.request(5)) .isInstanceOf(IllegalArgumentException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-manual-flow-control:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcDemandControllerTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public final class GrpcDemandController { private final int maxOutstanding; private int outstanding; public GrpcDemandController(int maxOutstanding) { this.maxOutstanding = maxOutstanding; } public void request(int count) { if (count <= 0 || outstanding + count > maxOutstanding) { throw new IllegalArgumentException( "manual flow-control window exceeded"); } outstanding += count; } } public record GrpcManualFlowControlPolicy( int maxOutstanding, java.time.Duration deadlockWatchdog) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-manual-flow-control:test --tests 'io.backend.skeleton.grpc.advanced.streaming.GrpcDemandControllerTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcManualFlowControlPolicy.java' 'modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandController.java' 'modules/grpc-advanced/grpc-manual-flow-control/src/main/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandDecision.java' 'modules/grpc-advanced/grpc-manual-flow-control/src/test/java/io/backend/skeleton/grpc/advanced/streaming/GrpcDemandControllerTest.java' git commit -m "feat: add approved grpc manual flow control" ``` ### Task 8: Read-only Unary Hedging **Files:** - Create: `modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingPolicy.java` - Create: `modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingEligibility.java` - Create: `modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingBudget.java` - Create: `modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingResult.java` - Test: `modules/grpc-advanced/grpc-hedging/src/test/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingEligibilityTest.java` **Interfaces:** - Consumes: Stable method policy, deadline, retry owner와 Service Config. - Produces: READ_ONLY Unary에만 제한된 duplicate attempt와 first-success 정책. **Implementation requirements:** - READ_ONLY + UNARY만 hedging 대상이다. - mutation, streaming, idempotency-key write에는 hedging을 금지한다. - maximum attempts는 초기 2로 제한한다. - hedging delay와 total deadline·attempt budget을 함께 검증한다. - duplicate backend load와 cancelled loser result를 metric으로 기록한다. - [ ] **Step 1: Write the failing test** ```java class GrpcHedgingEligibilityTest { @org.junit.jupiter.api.Test void mutationCannotHedge() { var eligibility = new GrpcHedgingEligibility(); org.assertj.core.api.Assertions.assertThat( eligibility.allowed( RpcIdempotencyProfile.NON_IDEMPOTENT, RpcType.UNARY)).isFalse(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-hedging:test --tests 'io.backend.skeleton.grpc.advanced.resilience.GrpcHedgingEligibilityTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcHedgingPolicy( int maxAttempts, java.time.Duration hedgingDelay) { public GrpcHedgingPolicy { if (maxAttempts < 2 || maxAttempts > 2) { throw new IllegalArgumentException( "initial hedging supports exactly two attempts"); } } } public final class GrpcHedgingEligibility { public boolean allowed( RpcIdempotencyProfile profile, RpcType type) { return profile == RpcIdempotencyProfile.READ_ONLY && type == RpcType.UNARY; } } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-hedging:test --tests 'io.backend.skeleton.grpc.advanced.resilience.GrpcHedgingEligibilityTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingPolicy.java' 'modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingEligibility.java' 'modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingBudget.java' 'modules/grpc-advanced/grpc-hedging/src/main/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingResult.java' 'modules/grpc-advanced/grpc-hedging/src/test/java/io/backend/skeleton/grpc/advanced/resilience/GrpcHedgingEligibilityTest.java' git commit -m "feat: add read only grpc hedging" ``` ### Task 9: Custom Name Resolver SPI **Files:** - Create: `modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcEndpointSnapshot.java` - Create: `modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverUpdate.java` - Create: `modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcCustomResolver.java` - Create: `modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverSafetyPolicy.java` - Test: `modules/grpc-advanced/grpc-custom-resolver/src/test/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverSafetyPolicyTest.java` **Interfaces:** - Consumes: Stable Named Channel Profile and custom discovery event source. - Produces: backend address와 Service Config update를 versioned snapshot으로 전달하는 승인된 resolver SPI. **Implementation requirements:** - resolver update는 monotonic revision과 endpoint set을 가진다. - 빈 endpoint update, stale revision, invalid authority를 거부한다. - resolver는 credential·business metadata를 제공하지 않는다. - Service Config를 함께 제공할 경우 retry owner·LB policy 검증을 통과해야 한다. - resolver close 후 update를 수신하지 않는다. - [ ] **Step 1: Write the failing test** ```java class GrpcResolverSafetyPolicyTest { @org.junit.jupiter.api.Test void staleRevisionIsRejected() { var policy = new GrpcResolverSafetyPolicy(); policy.accept(new GrpcEndpointSnapshot(2, java.util.Set.of("10.0.0.2:9090"))); org.assertj.core.api.Assertions.assertThatThrownBy( () -> policy.accept(new GrpcEndpointSnapshot( 1, java.util.Set.of("10.0.0.1:9090")))) .isInstanceOf(IllegalArgumentException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-custom-resolver:test --tests 'io.backend.skeleton.grpc.advanced.discovery.GrpcResolverSafetyPolicyTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcEndpointSnapshot( long revision, java.util.Set endpoints) {} public final class GrpcResolverSafetyPolicy { private long revision = -1; public void accept(GrpcEndpointSnapshot snapshot) { if (snapshot.endpoints().isEmpty() || snapshot.revision() <= revision) { throw new IllegalArgumentException( "invalid resolver update"); } revision = snapshot.revision(); } } public interface GrpcCustomResolver extends AutoCloseable { void start( java.util.function.Consumer listener); } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-custom-resolver:test --tests 'io.backend.skeleton.grpc.advanced.discovery.GrpcResolverSafetyPolicyTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcEndpointSnapshot.java' 'modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverUpdate.java' 'modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcCustomResolver.java' 'modules/grpc-advanced/grpc-custom-resolver/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverSafetyPolicy.java' 'modules/grpc-advanced/grpc-custom-resolver/src/test/java/io/backend/skeleton/grpc/advanced/discovery/GrpcResolverSafetyPolicyTest.java' git commit -m "feat: add grpc custom resolver spi" ``` ### Task 10: Custom Load Balancer SPI **Files:** - Create: `modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcEndpointCandidate.java` - Create: `modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerPicker.java` - Create: `modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerDecision.java` - Create: `modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerSafetyPolicy.java` - Test: `modules/grpc-advanced/grpc-custom-load-balancer/src/test/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerSafetyPolicyTest.java` **Interfaces:** - Consumes: Task 9 custom resolver snapshot and Stable channel profile. - Produces: bounded endpoint candidate에서 picker가 결정을 내리고 unsafe policy를 차단하는 GR3 SPI. **Implementation requirements:** - picker는 resolver가 제공한 endpoint만 선택할 수 있다. - endpoint health, connectivity, weight와 ejection state를 bounded metadata로 사용한다. - business request body·tenant ID를 routing input으로 사용하지 않는다. - custom picker failure는 deterministic fallback 또는 call failure로 드러난다. - load-aware/weighted policy는 performance·fairness·failover evidence를 요구한다. - [ ] **Step 1: Write the failing test** ```java class GrpcLoadBalancerSafetyPolicyTest { @org.junit.jupiter.api.Test void pickerCannotChooseUnknownEndpoint() { var policy = new GrpcLoadBalancerSafetyPolicy( java.util.Set.of("a:9090")); org.assertj.core.api.Assertions.assertThatThrownBy( () -> policy.validate("b:9090")) .isInstanceOf(IllegalArgumentException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-custom-load-balancer:test --tests 'io.backend.skeleton.grpc.advanced.discovery.GrpcLoadBalancerSafetyPolicyTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcEndpointCandidate( String authority, int weight, boolean ready) {} public final class GrpcLoadBalancerSafetyPolicy { private final java.util.Set endpoints; public GrpcLoadBalancerSafetyPolicy( java.util.Set endpoints) { this.endpoints = java.util.Set.copyOf(endpoints); } public void validate(String selected) { if (!endpoints.contains(selected)) { throw new IllegalArgumentException( "picker selected unknown endpoint"); } } } public interface GrpcLoadBalancerPicker { GrpcLoadBalancerDecision pick( java.util.List candidates); } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-custom-load-balancer:test --tests 'io.backend.skeleton.grpc.advanced.discovery.GrpcLoadBalancerSafetyPolicyTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcEndpointCandidate.java' 'modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerPicker.java' 'modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerDecision.java' 'modules/grpc-advanced/grpc-custom-load-balancer/src/main/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerSafetyPolicy.java' 'modules/grpc-advanced/grpc-custom-load-balancer/src/test/java/io/backend/skeleton/grpc/advanced/discovery/GrpcLoadBalancerSafetyPolicyTest.java' git commit -m "feat: add grpc custom load balancer spi" ``` ### Task 11: xDS Proxyless Experimental Profile **Files:** - Create: `modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsProfile.java` - Create: `modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsResourceSnapshot.java` - Create: `modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsStartupGuard.java` - Create: `modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsFailurePolicy.java` - Create: `modules/grpc-advanced/grpc-xds/src/test/resources/xds/bootstrap.json` - Test: `modules/grpc-advanced/grpc-xds/src/test/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsStartupGuardTest.java` **Interfaces:** - Consumes: Stable channel/security policy, xDS bootstrap and control-plane resources. - Produces: xds target, resource discovery, traffic policy, mTLS와 fallback을 별도 Experimental profile로 검증. **Implementation requirements:** - `xds:///` target만 explicit xDS profile에서 허용한다. - bootstrap·control-plane credential·resource namespace를 검증한다. - application YAML과 xDS에 retry/LB owner를 중복 정의하지 않는다. - resource not found, stale resource, control-plane outage와 last-known-good policy를 명시한다. - xDS 기능을 Stable DNS/LB support로 광고하지 않는다. - [ ] **Step 1: Write the failing test** ```java class GrpcXdsStartupGuardTest { @org.junit.jupiter.api.Test void xdsRequiresExplicitFeatureFlagAndBootstrap() { var guard = new GrpcXdsStartupGuard(false); org.assertj.core.api.Assertions.assertThatThrownBy( () -> guard.validate( new GrpcXdsProfile( "xds:///document-service", ""))) .isInstanceOf(IllegalStateException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-xds:test --tests 'io.backend.skeleton.grpc.advanced.xds.GrpcXdsStartupGuardTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcXdsProfile( String target, String bootstrapResource) {} public final class GrpcXdsStartupGuard { private final boolean enabled; public GrpcXdsStartupGuard(boolean enabled) { this.enabled = enabled; } public void validate(GrpcXdsProfile profile) { if (!enabled || !profile.target().startsWith("xds:///") || profile.bootstrapResource().isBlank()) { throw new IllegalStateException( "valid xDS experimental profile is required"); } } } public record GrpcXdsResourceSnapshot( String version, java.util.Set clusters, java.time.Instant receivedAt) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-xds:test --tests 'io.backend.skeleton.grpc.advanced.xds.GrpcXdsStartupGuardTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsProfile.java' 'modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsResourceSnapshot.java' 'modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsStartupGuard.java' 'modules/grpc-advanced/grpc-xds/src/main/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsFailurePolicy.java' 'modules/grpc-advanced/grpc-xds/src/test/resources/xds/bootstrap.json' 'modules/grpc-advanced/grpc-xds/src/test/java/io/backend/skeleton/grpc/advanced/xds/GrpcXdsStartupGuardTest.java' git commit -m "feat: add experimental grpc xds profile" ``` ### Task 12: gRPC-Web Unary·Server Streaming Bridge **Files:** - Create: `modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebRpcSupport.java` - Create: `modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebProfile.java` - Create: `modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebProxyContract.java` - Create: `modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebCompatibilityGate.java` - Create: `modules/grpc-advanced/grpc-web/src/test/resources/envoy/envoy.yaml` - Test: `modules/grpc-advanced/grpc-web/src/test/java/io/backend/skeleton/grpc/advanced/web/GrpcWebCompatibilityGateTest.java` **Interfaces:** - Consumes: Stable proto contract, Envoy/proxy profile and browser gRPC-Web client contract. - Produces: 브라우저에서 Unary와 grpcwebtext Server Streaming만 지원하고 Client/Bidi를 명시적으로 차단하는 bridge. **Implementation requirements:** - 지원 RPC는 Unary와 Server Streaming이다. - Client Streaming과 Bidirectional Streaming을 지원한다고 선언하지 않는다. - Envoy 또는 승인된 proxy의 CORS, TLS, metadata/header mapping을 검증한다. - browser credential·cookie·bearer profile과 CSRF/CORS 정책을 분리한다. - native gRPC와 gRPC-Web compatibility suite를 같은 schema에 실행한다. - [ ] **Step 1: Write the failing test** ```java class GrpcWebCompatibilityGateTest { @org.junit.jupiter.api.Test void bidiStreamingIsUnsupported() { var gate = GrpcWebCompatibilityGate.standard(); org.assertj.core.api.Assertions.assertThat( gate.supports(RpcType.BIDI_STREAMING)).isFalse(); org.assertj.core.api.Assertions.assertThat( gate.supports(RpcType.UNARY)).isTrue(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-web:test --tests 'io.backend.skeleton.grpc.advanced.web.GrpcWebCompatibilityGateTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public enum GrpcWebRpcSupport { UNARY, SERVER_STREAMING } public final class GrpcWebCompatibilityGate { public static GrpcWebCompatibilityGate standard() { return new GrpcWebCompatibilityGate(); } public boolean supports(RpcType type) { return type == RpcType.UNARY || type == RpcType.SERVER_STREAMING; } } public record GrpcWebProfile( String proxy, boolean tlsRequired, java.util.Set allowedOrigins) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-web:test --tests 'io.backend.skeleton.grpc.advanced.web.GrpcWebCompatibilityGateTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebRpcSupport.java' 'modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebProfile.java' 'modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebProxyContract.java' 'modules/grpc-advanced/grpc-web/src/main/java/io/backend/skeleton/grpc/advanced/web/GrpcWebCompatibilityGate.java' 'modules/grpc-advanced/grpc-web/src/test/resources/envoy/envoy.yaml' 'modules/grpc-advanced/grpc-web/src/test/java/io/backend/skeleton/grpc/advanced/web/GrpcWebCompatibilityGateTest.java' git commit -m "feat: add grpc web compatibility bridge" ``` ### Task 13: Servlet HTTP/2 Compatibility Profile **Files:** - Create: `modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletCompatibilityProfile.java` - Create: `modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletCapabilityMatrix.java` - Create: `modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletStartupValidator.java` - Test: `modules/grpc-advanced/grpc-servlet-compat/src/test/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletStartupValidatorTest.java` **Interfaces:** - Consumes: Stable server profile and Spring Servlet container HTTP/2 integration. - Produces: 동일 web server/port를 사용하는 Servlet transport의 지원·비지원 기능을 명시하는 compatibility module. **Implementation requirements:** - Servlet transport는 Stable Netty certification을 대체하지 않는다. - container가 network layer를 소유하므로 native Netty-only 설정을 요청하면 실패한다. - HTTP/2, TLS, message/metadata limits, health, reflection, drain을 실제 container에서 검증한다. - keepalive·connection age·flow-control capability 차이를 matrix에 기록한다. - Servlet profile은 명시적 feature flag와 compatibility release gate를 요구한다. - [ ] **Step 1: Write the failing test** ```java class GrpcServletStartupValidatorTest { @org.junit.jupiter.api.Test void nativeOnlyFlowControlSettingIsRejected() { var validator = new GrpcServletStartupValidator(); org.assertj.core.api.Assertions.assertThatThrownBy( () -> validator.validate( new GrpcServletCompatibilityProfile(true))) .isInstanceOf(IllegalArgumentException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-servlet-compat:test --tests 'io.backend.skeleton.grpc.advanced.servlet.GrpcServletStartupValidatorTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcServletCompatibilityProfile( boolean requestsNativeNettyFlowControl) {} public final class GrpcServletStartupValidator { public void validate( GrpcServletCompatibilityProfile profile) { if (profile.requestsNativeNettyFlowControl()) { throw new IllegalArgumentException( "Servlet transport cannot promise Netty-only policy"); } } } public record GrpcServletCapabilityMatrix( boolean http2, boolean tls, boolean health, boolean reflection, boolean nettyFlowControl) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-servlet-compat:test --tests 'io.backend.skeleton.grpc.advanced.servlet.GrpcServletStartupValidatorTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletCompatibilityProfile.java' 'modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletCapabilityMatrix.java' 'modules/grpc-advanced/grpc-servlet-compat/src/main/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletStartupValidator.java' 'modules/grpc-advanced/grpc-servlet-compat/src/test/java/io/backend/skeleton/grpc/advanced/servlet/GrpcServletStartupValidatorTest.java' git commit -m "feat: add grpc servlet compatibility profile" ``` ### Task 14: Spring Integration gRPC Bridge **Files:** - Create: `modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationInboundGateway.java` - Create: `modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationOutboundGateway.java` - Create: `modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationBridgePolicy.java` - Test: `modules/grpc-advanced/grpc-integration-bridge/src/test/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationBridgePolicyTest.java` **Interfaces:** - Consumes: Stable Generated service/stub, Spring Integration Message flow and method policy. - Produces: Spring `Message` 기반 Integration Flow를 typed RPC 계약과 분리해 연결하는 선택 bridge. **Implementation requirements:** - bridge가 일반 Generated Stub/Service API를 대체한다고 설명하지 않는다. - message header를 gRPC metadata로 무제한 복사하지 않는다. - deadline, security, status, error, observability Stable policy를 그대로 적용한다. - payload type은 registered converter를 통해 Proto message와 변환한다. - broker-style ACK·durability semantics를 bridge에 추가하지 않는다. - [ ] **Step 1: Write the failing test** ```java class GrpcIntegrationBridgePolicyTest { @org.junit.jupiter.api.Test void arbitraryHeadersAreNotForwarded() { var policy = GrpcIntegrationBridgePolicy.standard(); org.assertj.core.api.Assertions.assertThat( policy.forwardedHeaders()) .containsExactlyInAnyOrder( "correlation-id", "traceparent"); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-integration-bridge:test --tests 'io.backend.skeleton.grpc.advanced.integration.GrpcIntegrationBridgePolicyTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcIntegrationBridgePolicy( java.util.Set forwardedHeaders) { public static GrpcIntegrationBridgePolicy standard() { return new GrpcIntegrationBridgePolicy( java.util.Set.of( "correlation-id", "traceparent")); } } public interface GrpcIntegrationInboundGateway {} public interface GrpcIntegrationOutboundGateway {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-integration-bridge:test --tests 'io.backend.skeleton.grpc.advanced.integration.GrpcIntegrationBridgePolicyTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationInboundGateway.java' 'modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationOutboundGateway.java' 'modules/grpc-advanced/grpc-integration-bridge/src/main/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationBridgePolicy.java' 'modules/grpc-advanced/grpc-integration-bridge/src/test/java/io/backend/skeleton/grpc/advanced/integration/GrpcIntegrationBridgePolicyTest.java' git commit -m "feat: add spring integration grpc bridge" ``` ### Task 15: Reactor gRPC Adapter **Files:** - Create: `modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/ReactiveGrpcClient.java` - Create: `modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/ReactiveGrpcServerAdapter.java` - Create: `modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorContextBridge.java` - Create: `modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorCancellationBridge.java` - Test: `modules/grpc-advanced/grpc-reactor/src/test/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorContextBridgeTest.java` **Interfaces:** - Consumes: Stable typed stub/service adapter, Reactor Context and cancellation contracts. - Produces: Unary·Server Streaming을 Mono/Flux로 노출하면서 context·deadline·cancel·backpressure를 유지하는 adapter. **Implementation requirements:** - Core public contract를 Reactor에 종속시키지 않는다. - Mono/Flux cancellation을 gRPC call cancellation으로 전파한다. - gRPC Context와 Reactor Context의 Actor·Tenant·Trace·Deadline을 bridge한다. - blocking JPA/SDK 호출을 event loop에서 실행하지 않는다. - server stream Flux가 Stable bounded flow-control contract를 우회하지 않는다. - [ ] **Step 1: Write the failing test** ```java class GrpcReactorContextBridgeTest { @org.junit.jupiter.api.Test void missingActorContextFailsClosed() { var bridge = new GrpcReactorContextBridge(); org.assertj.core.api.Assertions.assertThatThrownBy( () -> bridge.requireContext(java.util.Map.of())) .isInstanceOf(IllegalStateException.class); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-reactor:test --tests 'io.backend.skeleton.grpc.advanced.reactor.GrpcReactorContextBridgeTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public final class GrpcReactorContextBridge { public GrpcRequestContext requireContext( java.util.Map context) { var value = context.get("grpcRequestContext"); if (!(value instanceof GrpcRequestContext requestContext)) { throw new IllegalStateException( "gRPC request context is required"); } return requestContext; } } public interface ReactiveGrpcClient {} public interface ReactiveGrpcServerAdapter {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-reactor:test --tests 'io.backend.skeleton.grpc.advanced.reactor.GrpcReactorContextBridgeTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/ReactiveGrpcClient.java' 'modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/ReactiveGrpcServerAdapter.java' 'modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorContextBridge.java' 'modules/grpc-advanced/grpc-reactor/src/main/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorCancellationBridge.java' 'modules/grpc-advanced/grpc-reactor/src/test/java/io/backend/skeleton/grpc/advanced/reactor/GrpcReactorContextBridgeTest.java' git commit -m "feat: add reactor grpc adapter" ``` ### Task 16: Kotlin Coroutine·Flow Adapter **Files:** - Create: `modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinProfile.java` - Create: `modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcCoroutineContextBridge.java` - Create: `modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinCompatibilityGate.java` - Create: `modules/grpc-advanced/grpc-kotlin/src/main/kotlin/io/backend/skeleton/grpc/advanced/kotlin/GrpcCoroutineAdapter.kt` - Test: `modules/grpc-advanced/grpc-kotlin/src/test/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinCompatibilityGateTest.java` **Interfaces:** - Consumes: Stable schema artifact, Kotlin generated code and coroutine/Flow runtime. - Produces: Kotlin coroutine unary와 Flow streaming을 Stable method policy·evidence와 연결하는 optional adapter. **Implementation requirements:** - Java generated contract와 Kotlin generated contract의 schema source를 하나로 유지한다. - Coroutine cancellation을 gRPC cancellation으로 전파한다. - Flow backpressure가 Stable stream buffer limit을 우회하지 않는다. - Kotlin adapter가 Java Core의 evidence·status·deadline types를 보존한다. - Kotlin toolchain compatibility를 별도 release lane에서 검증한다. - [ ] **Step 1: Write the failing test** ```java class GrpcKotlinCompatibilityGateTest { @org.junit.jupiter.api.Test void schemaDigestMustMatchJavaLane() { var gate = new GrpcKotlinCompatibilityGate(); org.assertj.core.api.Assertions.assertThat( gate.compatible("sha256:a", "sha256:b")) .isFalse(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-kotlin:test --tests 'io.backend.skeleton.grpc.advanced.kotlin.GrpcKotlinCompatibilityGateTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public final class GrpcKotlinCompatibilityGate { public boolean compatible( String javaSchemaDigest, String kotlinSchemaDigest) { return javaSchemaDigest.equals(kotlinSchemaDigest); } } public record GrpcKotlinProfile( String kotlinVersion, String grpcKotlinVersion, boolean coroutineCancellationEnabled) {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-kotlin:test --tests 'io.backend.skeleton.grpc.advanced.kotlin.GrpcKotlinCompatibilityGateTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinProfile.java' 'modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcCoroutineContextBridge.java' 'modules/grpc-advanced/grpc-kotlin/src/main/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinCompatibilityGate.java' 'modules/grpc-advanced/grpc-kotlin/src/main/kotlin/io/backend/skeleton/grpc/advanced/kotlin/GrpcCoroutineAdapter.kt' 'modules/grpc-advanced/grpc-kotlin/src/test/java/io/backend/skeleton/grpc/advanced/kotlin/GrpcKotlinCompatibilityGateTest.java' git commit -m "feat: add kotlin grpc adapter" ``` ### Task 17: Channelz·CSDS 진단과 Advanced Testkit **Files:** - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsSnapshot.java` - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsPolicy.java` - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcAdvancedInfrastructureTestkit.java` - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcDiagnosticsRedactor.java` - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/test/resources/xds/control-plane-snapshot.json` - Create: `modules/grpc-advanced/grpc-channel-diagnostics/src/test/resources/grpc-web/envoy.yaml` - Test: `modules/grpc-advanced/grpc-channel-diagnostics/src/test/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsPolicyTest.java` **Interfaces:** - Consumes: Channel runtime, xDS profile, server state and admin authorization. - Produces: Channelz/CSDS 기반 channel·subchannel·socket·xDS 상태를 관리자에게 제한적으로 제공하는 진단 계층. **Implementation requirements:** - diagnostics endpoint는 admin network·role을 요구한다. - socket address·authority는 정책에 따라 마스킹한다. - token, certificate private material, metadata와 payload를 노출하지 않는다. - xDS CSDS는 xDS feature가 활성화된 경우에만 등록한다. - testkit은 gRPC-Web proxy, Servlet container, xDS control-plane failure를 검증한다. - [ ] **Step 1: Write the failing test** ```java class GrpcChannelDiagnosticsPolicyTest { @org.junit.jupiter.api.Test void anonymousAccessIsDenied() { var policy = GrpcChannelDiagnosticsPolicy.adminOnly(); org.assertj.core.api.Assertions.assertThat( policy.allowed(java.util.Set.of())).isFalse(); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-channel-diagnostics:test --tests 'io.backend.skeleton.grpc.advanced.diagnostics.GrpcChannelDiagnosticsPolicyTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public record GrpcChannelDiagnosticsPolicy( java.util.Set requiredRoles) { public static GrpcChannelDiagnosticsPolicy adminOnly() { return new GrpcChannelDiagnosticsPolicy( java.util.Set.of("GRPC_ADMIN")); } public boolean allowed(java.util.Set roles) { return roles.containsAll(requiredRoles); } } public record GrpcChannelDiagnosticsSnapshot( String channelProfile, String connectivityState, int subchannels, int sockets, java.time.Instant capturedAt) {} public interface GrpcAdvancedInfrastructureTestkit {} ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-channel-diagnostics:test --tests 'io.backend.skeleton.grpc.advanced.diagnostics.GrpcChannelDiagnosticsPolicyTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsSnapshot.java' 'modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsPolicy.java' 'modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcAdvancedInfrastructureTestkit.java' 'modules/grpc-advanced/grpc-channel-diagnostics/src/main/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcDiagnosticsRedactor.java' 'modules/grpc-advanced/grpc-channel-diagnostics/src/test/resources/xds/control-plane-snapshot.json' 'modules/grpc-advanced/grpc-channel-diagnostics/src/test/resources/grpc-web/envoy.yaml' 'modules/grpc-advanced/grpc-channel-diagnostics/src/test/java/io/backend/skeleton/grpc/advanced/diagnostics/GrpcChannelDiagnosticsPolicyTest.java' git commit -m "feat: add grpc channel diagnostics and testkit" ``` ### Task 18: Advanced Capability Promotion Gate **Files:** - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionEvidence.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionDecision.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionGate.java` - Create: `modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedSupportMatrix.java` - Create: `docs/compatibility/grpc-advanced-support-matrix.md` - Create: `docs/runbooks/grpc-advanced-capabilities.md` - Create: `docs/adr/ADR-065-grpc-advanced-capability-promotion.md` - Test: `modules/grpc-advanced/grpc-advanced-bootstrap/src/test/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionGateTest.java` **Interfaces:** - Consumes: Tasks 1–17의 capability-specific compatibility, security, fault, performance, soak evidence. - Produces: 각 Advanced/Experimental capability를 독립적으로 승격·차단하고 Stable starter 유입을 관리하는 release gate. **Implementation requirements:** - capability별 required evidence를 분리한다. - Edition, streaming, xDS, gRPC-Web, Servlet, language adapter가 서로의 승격을 묶지 않는다. - Experimental에서 Advanced Stable로 승격하려면 ADR, runbook, actual environment test와 soak evidence가 필요하다. - Stable default로 승격하려면 dependency·security·operational cost 재검토가 필요하다. - 미승격 capability는 feature flag와 별도 module로 유지한다. - [ ] **Step 1: Write the failing test** ```java class GrpcAdvancedPromotionGateTest { @org.junit.jupiter.api.Test void xdsWithoutControlPlaneSoakIsBlocked() { var evidence = new GrpcAdvancedPromotionEvidence( GrpcAdvancedCapability.XDS, java.util.Set.of( "compatibility", "security", "fault", "adr")); var gate = new GrpcAdvancedPromotionGate(); org.assertj.core.api.Assertions.assertThat( gate.decide(evidence)) .isEqualTo(GrpcAdvancedPromotionDecision.BLOCKED); } } ``` - [ ] **Step 2: Run the focused test and verify the failure** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-advanced-bootstrap:test --tests 'io.backend.skeleton.grpc.advanced.release.GrpcAdvancedPromotionGateTest' ``` Expected: FAIL because the Advanced production contract does not exist or the capability bypasses the Stable guardrail. - [ ] **Step 3: Implement the smallest complete production contract** ```java public enum GrpcAdvancedPromotionDecision { ADVANCED_STABLE, EXPERIMENTAL, BLOCKED } public record GrpcAdvancedPromotionEvidence( GrpcAdvancedCapability capability, java.util.Set passed) {} public final class GrpcAdvancedPromotionGate { public GrpcAdvancedPromotionDecision decide( GrpcAdvancedPromotionEvidence evidence) { var required = java.util.Set.of( "compatibility", "security", "fault", "performance", "soak", "adr", "runbook"); return evidence.passed().containsAll(required) ? GrpcAdvancedPromotionDecision.ADVANCED_STABLE : GrpcAdvancedPromotionDecision.BLOCKED; } } ``` Implement every listed production file with the exact names and invariants above. Preserve the Stable method policy, security, deadline, evidence, error and observability contracts; an Advanced capability is not a raw escape hatch. - [ ] **Step 4: Run the focused test and Advanced aggregate suite** Run: ```bash ./gradlew :modules:grpc-advanced:grpc-advanced-bootstrap:test --tests 'io.backend.skeleton.grpc.advanced.release.GrpcAdvancedPromotionGateTest' ./gradlew grpcAdvancedTest ``` Expected: PASS for the focused test and Advanced aggregate suite. - [ ] **Step 5: Commit the independently reviewable change** ```bash git add 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionEvidence.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionDecision.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionGate.java' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/main/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedSupportMatrix.java' 'docs/compatibility/grpc-advanced-support-matrix.md' 'docs/runbooks/grpc-advanced-capabilities.md' 'docs/adr/ADR-065-grpc-advanced-capability-promotion.md' 'modules/grpc-advanced/grpc-advanced-bootstrap/src/test/java/io/backend/skeleton/grpc/advanced/release/GrpcAdvancedPromotionGateTest.java' git commit -m "docs: complete grpc advanced promotion gate" ```