Files
clean-architecture-backend-…/docs/adr/ADR-GRPC-ADV-001-capability-promotion-is-per-capability.md

2.9 KiB

ADR-GRPC-ADV-001: Each advanced capability has its own flag, its own grade and its own promotion

  • Status: accepted
  • Date: 2026-08-30
  • Scope: :grpc-advanced:*

Context

The advanced plan covers sixteen capabilities that differ by orders of magnitude in what they bring with them. gRPC-Web adds a proxy. Reactor adds a dependency. xDS adds a control plane, its outage modes, its own security boundary and its own version skew. Hedging duplicates production traffic.

Bundling them under one flag makes enabling the cheapest of those the same decision as enabling the most consequential.

Decision

One flag per capability, under ca-skeleton.grpc.advanced.<capability>.enabled, all off by default.

Four grades. ADVANCED_STABLE starts on its flag; EXPERIMENTAL additionally needs a separate production approval, because the flag says somebody wanted the feature and the approval says somebody accepted that its failure modes are not fully characterised; WATCH cannot start at all; DISABLED is withdrawn.

GrpcAdvancedModuleGuard distinguishes the three refusals — flag unset, grade unstartable, production unapproved — because the remedy differs in each case.

Promotion evidence is per capability. GrpcAdvancedPromotionEvidence is one record per capability, so no promotion can drag another along; GrpcAdvancedPromotionGate.capabilitiesDraggedAlong returns an empty list, and that is a tested property rather than a claim. Two thresholds: seven days of soak plus complete evidence for ADVANCED_STABLE, thirty for a Stable default, because the second means every deployment gets the capability's dependencies and its failure modes.

Infrastructure is named per capability. GrpcAdvancedInfrastructureTestkit records that gRPC-Web needs a proxy, Servlet needs a container, xDS needs a stoppable control plane and Kotlin needs a toolchain. A suite that runs without its infrastructure passes and establishes nothing, which is worse than not having one.

Consequences

The Kotlin adapter fails closed here, and says why. This repository has no Kotlin toolchain, so GrpcKotlinCompatibilityGate.supportableHere() returns false. The four contract requirements — one schema source, coroutine cancellation propagation, Flow backpressure inside the Stable bounds, platform evidence types preserved — are checkable and are checked; only the compile lane is missing.

Edition 2026 cannot be used however its watch report reads. GrpcEdition2026Guard is not conditional on the report, because letting a status record also authorise use means a schema moves onto an edition the moment somebody marks four fields SUPPORTED, with no promotion decision, no consumer migration and no ADR.

xDS is not part of the Stable support statement. It works, behind its flag and its approval; GrpcXdsStartupGuard.advertisableAsStableSupport() returns false so a support matrix cannot widen quietly.