# 클래스 자바독과 목록 선언 (93 줄) GrpcCompletionReconciler.java:8 GrpcCompletionReconciler.java:9 /** GrpcCompletionReconciler.java:10 * Turns an unknown completion into a decision, or hands it on. GrpcCompletionReconciler.java:11 * GrpcCompletionReconciler.java:12 *

Two sources, consulted in a fixed order: the ledger first, then — only when the ledger has GrpcCompletionReconciler.java:13 * nothing and the method offers one — a business-resource probe. The order matters because they GrpcCompletionReconciler.java:14 * answer different questions. The ledger knows whether this exact operation was claimed; the GrpcCompletionReconciler.java:15 * resource only knows whether something that looks like the result exists, which is a weaker GrpcCompletionReconciler.java:16 * statement and can be satisfied by a different caller's write. GrpcCompletionReconciler.java:17 * GrpcCompletionReconciler.java:18 *

When neither is conclusive, the resolution stays UNKNOWN and the case is queued. Guessing is GrpcCompletionReconciler.java:19 * the failure mode this class exists to make impossible: a reconciler that resolves an ambiguous GrpcCompletionReconciler.java:20 * case by re-issuing has converted an unknown into a duplicate. GrpcCompletionReconciler.java:21 */ GrpcCompletionReconciler.java:22 public final class GrpcCompletionReconciler { GrpcCompletionReconciler.java:23 GrpcCompletionReconciler.java:24 private final GrpcOperationStatusQuery statusQuery; GrpcCompletionReconciler.java:25 private final List pending = new ArrayList<>(); GrpcCompletionReconciler.java:26 # pending 이 나오는 줄 전부 GrpcCompletionReconciler.java:25 private final List pending = new ArrayList<>(); GrpcCompletionReconciler.java:34 throw new IllegalArgumentException("a pending case identifies its operation"); GrpcCompletionReconciler.java:37 throw new IllegalArgumentException("a pending case says why it is pending"); GrpcCompletionReconciler.java:79 pending.add(new PendingCase(method, callerFingerprint, idempotencyKey, fromLedger.reason())); GrpcCompletionReconciler.java:85 public List pendingCases() { GrpcCompletionReconciler.java:86 return List.copyOf(pending); GrpcCompletionReconciler.java:91 pending.remove(resolved); # 동기화 표시자 — 같은 검색을 같은 리프의 짝에도 건다 표시자 이 파일 짝 파일 리프 전체 synchronized 0 5 3 volatile 0 0 0 java.util.concurrent 0 0 10 ReentrantLock 0 0 0 CopyOnWrite 0 0 0 not thread 0 0 1 single-threaded 0 0 0 같은 검색을 리프 전체에 걸어 파일별로 : GrpcContextBinder.java 2 건 GrpcCancellationCoordinator.java 5 건 GrpcOutcomeReplay.java 2 건 GrpcRetryBudget.java 1 건 GrpcCredentialRotationManager.java 1 건 GrpcSerializedStreamWriter.java 9 건 GrpcStreamAdmission.java 3 건 GrpcStreamSequence.java 1 건 리프 전체 62 파일 # pending.add 가 실행되는 조건 GrpcCompletionReconciler.java:62 GrpcCompletionResolution fromLedger = GrpcCompletionReconciler.java:63 statusQuery.resolve(method, callerFingerprint, idempotencyKey); GrpcCompletionReconciler.java:64 if (fromLedger.status() == GrpcOperationStatus.COMMITTED GrpcCompletionReconciler.java:65 || fromLedger.status() == GrpcOperationStatus.FAILED_TERMINAL) { GrpcCompletionReconciler.java:66 return fromLedger; GrpcCompletionReconciler.java:67 } GrpcCompletionReconciler.java:68 if (fromLedger.status() == GrpcOperationStatus.NOT_FOUND && businessProbe != null) { GrpcCompletionReconciler.java:69 Optional observed = businessProbe.apply(method); GrpcCompletionReconciler.java:70 if (observed.isPresent()) { GrpcCompletionReconciler.java:71 return GrpcCompletionResolution.of( GrpcCompletionReconciler.java:72 GrpcOperationStatus.COMMITTED, GrpcCompletionReconciler.java:73 observed, GrpcCompletionReconciler.java:74 "no ledger claim, but the business resource shows the effect"); GrpcCompletionReconciler.java:75 } GrpcCompletionReconciler.java:76 return fromLedger; GrpcCompletionReconciler.java:77 } GrpcCompletionReconciler.java:78 if (fromLedger.requiresReconciliation()) { GrpcCompletionReconciler.java:79 pending.add(new PendingCase(method, callerFingerprint, idempotencyKey, fromLedger.reason())); GrpcCompletionReconciler.java:80 } GrpcCompletionReconciler.java:81 return fromLedger; GrpcCompletionReconciler.java:82 } GrpcCompletionResolution.java:46 } GrpcCompletionResolution.java:47 GrpcCompletionResolution.java:48 /** Whether this must be handed to a reconciliation job rather than answered now. */ GrpcCompletionResolution.java:49 public boolean requiresReconciliation() { GrpcCompletionResolution.java:50 return status == GrpcOperationStatus.UNKNOWN || status == GrpcOperationStatus.IN_PROGRESS; GrpcCompletionResolution.java:51 } GrpcCompletionResolution.java:52 } # 같은 리프에서 같은 모양의 필드를 가진 클래스 deadline/GrpcCancellationCoordinator.java:27: private final Map operations = new LinkedHashMap<>(); validation/GrpcTransportValidator.java:104: private final List> rules = new ArrayList<>(); validation/GrpcTransportValidator.java:105: private final Set seen = new LinkedHashSet<>(); idempotency/GrpcCompletionReconciler.java:25: private final List pending = new ArrayList<>(); streaming/GrpcSerializedStreamWriter.java:31: private final Deque> queue = new ArrayDeque<>(); 그중 GrpcCancellationCoordinator 의 공개 메서드 : GrpcCancellationCoordinator.java:32 public GrpcCancellationCoordinator(GrpcCancellationToken token) { GrpcCancellationCoordinator.java:45 public synchronized void register(GrpcCancellableOperation operation) { GrpcCancellationCoordinator.java:69 public synchronized List cancel(GrpcCancellationReason cancellationReason, Instant at) { GrpcCancellationCoordinator.java:90 public synchronized void markCommitBoundaryCrossed() { GrpcCancellationCoordinator.java:100 public synchronized boolean businessEffectAborted() { GrpcCancellationCoordinator.java:105 public boolean cancelled() { GrpcCancellationCoordinator.java:110 public synchronized Optional reason() { GrpcCancellationCoordinator.java:119 public void requireMayStartSideEffect(String operationName) { # 이 타입을 참조하는 자리 — 경로 제한 없이, 절단 없이 docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md docs/adr/ADR-GRPC-003-three-axis-execution-evidence.md docs/runbooks/grpc-platform-operations.md src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcCompletionReconciler.java src/grpc/grpc-policy/src/test/java/dev/caskeleton/grpc/idempotency/GrpcCompletionReconcilerTest.java main · GrpcCompletionReconciler.java:22 public final class GrpcCompletionReconciler { main · GrpcCompletionReconciler.java:43 public GrpcCompletionReconciler(GrpcOperationStatusQuery statusQuery) { test · GrpcCompletionReconcilerTest.java:11 class GrpcCompletionReconcilerTest { test · GrpcCompletionReconcilerTest.java:96 GrpcCompletionReconciler reconciler = new GrpcCompletionReconciler(query); test · GrpcCompletionReconcilerTest.java:108 GrpcCompletionReconciler reconciler = new GrpcCompletionReconciler(query); test · GrpcCompletionReconcilerTest.java:121 GrpcCompletionReconciler reconciler = new GrpcCompletionReconciler(query); test · GrpcCompletionReconcilerTest.java:138 GrpcCompletionReconciler reconciler = new GrpcCompletionReconciler(query); 자기 파일 밖 main 참조 : 0 개 [대조] GrpcCancellationCoordinator : 2 개 (0 이 아니어야 정상) # 운영 런북이 자기를 어떻게 규정하는가 grpc-platform-operations.md:1 # Runbook: gRPC platform operations grpc-platform-operations.md:2 grpc-platform-operations.md:3 Scope: the `:grpc:*` family. All of it is build-only today — every leaf's `runtime_memberships` is grpc-platform-operations.md:4 empty — so nothing here fires in production yet. It is written now because the states it covers are grpc-platform-operations.md:5 the ones an on-call cannot work out from first principles at three in the morning, and shipping the grpc-platform-operations.md:6 behaviour before the runbook means the first person to meet one is doing that. grpc-platform-operations.md:7 grpc-platform-operations.md:8 Configuration lives under `ca-skeleton.grpc.platform.*` and is bound by `GrpcPlatformProperties`. grpc-platform-operations.md:9 The platform does not start unless `ca-skeleton.grpc.platform.enabled=true`. 그 런북이 요구하는 스위치가 코드에 있는가 : 0 개 파일 독립 문서의 같은 서술 : grpc-support-matrix.md:66 Not released. Every `:grpc:*` leaf is `runtime_memberships: []` in the module registry, so the grpc-support-matrix.md:67 platform is build-only: it compiles, its lanes run, and no deployed artifact carries it. # 그 런북이 이 클래스에 기대하는 동작 grpc-platform-operations.md:34 4. `NOT_FOUND` means the operation never started and is safe to re-issue. `FAILED_TERMINAL` means the grpc-platform-operations.md:35 same. grpc-platform-operations.md:36 5. `UNKNOWN` means the ledger could not be consulted. Nothing may be concluded. The case is queued by grpc-platform-operations.md:37 `GrpcCompletionReconciler` and retried later. grpc-platform-operations.md:38 6. If the method is not keyed, there is no ledger row. Resolve it against the business resource, or grpc-platform-operations.md:39 escalate to the service owner. This is the case the keyed profile exists to avoid. grpc-platform-operations.md:40 grpc-platform-operations.md:41 **Escalate when** the reconciler's pending list grows across passes. That means the ledger is grpc-platform-operations.md:42 unreachable rather than slow.