--- kind: CASE slug: a-release-gate-with-no-evidence-producer title: 릴리스 게이트가 읽는 증거를 아무도 생산하지 않는다 topic: what-a-gate-does-not-prove project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:a-release-gate-with-no-evidence-producer evidenceCapturedOn: 2026-09-02 assets: - key: a-release-gate-with-no-evidence-producer file: ../../../final/evidence/rendered/a-release-gate-with-no-evidence-producer.svg evidence: - ../../../final/evidence/raw/a-release-gate-with-no-evidence-producer.txt - ../../../final/evidence/raw/tl-grpc-release-gate-no-producer.txt source: - 분석 문서는 gRPC 플랫폼 편 §3.2 다. 그 절이 지원 매트릭스의 현재 시제 문장과 게이트의 설계 근거를 인용하고, 생성 지점 넷이 전부 테스트라는 것과 messaging 이 그것을 세 층으로 닫았다는 대비를 적는다. - 같은 절이 근거로 든 검색은 리프의 build.gradle 안에서 태스크 등록 문자열을 세는 것이라 0 이 나온다. 이 저장소는 등록을 컨벤션 플러그인으로 옮겨 두었으므로 그 수는 등록된 태스크 수가 아니다. 위 터미널 출력의 레인 목록이 그것을 보여 준다. --- # 릴리스 게이트가 읽는 증거를 아무도 생산하지 않는다 gRPC 안정 릴리스 게이트는 증거 객체를 읽어 판정한다. 그 객체를 만드는 곳은 게이트 자신의 단위 테스트뿐이다. 그 테스트는 매 PR 의 check 를 타고 돌지만, 거기서 게이트가 읽는 것은 테스트가 손으로 채운 값이다. ## 관계 - **아무도 돌리지 않는 레인의 게이트는 마지막으로 돌린 사람이 본 것을 보고한다** 이 사례에서 끌어낸 규칙이다. - **빠뜨림이 통과가 되는 게이트는 게이트가 아니다** 증거를 만드는 쪽이 없을 때 게이트가 통과로 끝나면 안 된다는 규칙이다. - **시작 검증기 13개 규칙이 유일한 조립 지점에서 호출되지 않는다** 같은 블록에서 나왔고, 둘 다 검증 코드는 있는데 그것을 부르는 자리가 없다. ## 문제 게이트는 증거 객체를 입력으로 받아 안정 등급 승격을 판정한다. 판정 로직은 존재하고 테스트되어 있다. 증거 객체의 다섯 성분은 전부 호출자가 넘기는 값이다. 등급과 능력이 둘이고, 문서 셋의 존재 여부가 나머지다. 어느 것도 파일 시스템이나 테스트 리포트를 보지 않는다. ## 결론 증거 객체를 생성하는 곳은 게이트 자신의 테스트 네 군데뿐이다. 등급 넷에 대응하는 증거 레인 넷은 존재한다. 리프의 build.gradle 이 아니라 컨벤션 플러그인이 등록하고, 지원 매트릭스가 등급마다 그 이름을 적는다. 없는 것은 둘이다. 레인이 낸 결과를 증거 객체로 바꾸는 코드가 없고, 그 레인 넷을 check 나 워크플로에 붙이는 배선이 없다. messaging 쪽은 그 둘을 세 층으로 닫았다. 레인이 증거 파일을 쓰고, Gradle 태스크가 실행 산출물과 커밋본을 대조하고, 워크플로가 실제 브로커를 띄워 그 태스크를 돌린다. gRPC 블록이 어떤 런타임 컴포지션에도 속하지 않는다는 것은 지원 매트릭스가 스스로 적고 있으며 결함이 아니다. 남는 것은 게이트의 입력이다. ## 검증 환경 OpenJDK : 21.0.12 Gradle : 9.0.0 확인 방식 : 정적 도달성 확인과 태스크 그래프 확인 소스 수정 : x ## 재현 조건 1. 증거 레코드의 성분을 읽고, 그중 파일 시스템이나 리포트를 보는 것이 있는지 확인한다. 2. 그 객체를 생성하는 코드를 저장소 전체에서 찾고 main 과 test 를 구분한다. 3. 게이트의 테스트에 태그가 붙어 있는지 확인하고, check 그래프에 그 테스트가 들어 있는지 확인한다. 4. grpc-testkit 에 등록된 증거 레인을 나열하고, 그중 몇 개가 check 그래프에 있는지 센다. 5. CI 워크플로 중 grpc 를 담은 것의 수를 세고, root check 를 도는 워크플로를 확인한다. 6. 대비를 위해 messaging 쪽의 증거 파일과 대조 태스크와 워크플로를 확인한다. ## 본문 `docs/compatibility/grpc-support-matrix.md` 는 안정 릴리스 게이트를 현재 시제로 적는다. 인증된 레인에 결과가 없거나 실패하면 릴리스를 막는다는 것이다. 게이트는 그 판정을 증거 객체 하나에서 읽는다. ## 다섯 성분은 전부 호출자가 넘긴다 :::evidence key="a-release-gate-with-no-evidence-producer" alt="코드베이스에서 게이트가 읽는 증거 레코드의 다섯 성분과 그 객체를 만드는 네 지점, 그 테스트에 태그가 없어 check 그래프 안에 있다는 것과 그 check 를 도는 워크플로 줄, 등급 넷에 대응해 컨벤션이 등록한 증거 레인 넷과 그중 check 그래프에 들어 있는 수와 grpc 를 담은 워크플로 수, 그리고 messaging 쪽에서 레인이 쓰는 증거 파일과 그것을 대조하는 태스크와 그 태스크를 돌리는 워크플로 단계를 뽑은 출력 39줄. 게이트의 테스트는 check 안에 있고 증거 레인 넷은 밖에 있다는 것이 그 출력에 보인다." caption="증거의 다섯 성분 · 생성 지점 넷 전부 테스트 · 그 테스트는 check 안 · 레인 넷은 check 밖 · messaging 의 세 층 — 39줄" zoom="true" ::: 증거 레코드는 실행한 등급 집합과 인증된 능력 집합, 그리고 런북과 결정 기록과 지원 매트릭스의 존재 여부 셋을 담는다. 셋은 불리언이다. 파일이 있는지 보고 채우는 코드가 아니라, 넘겨받은 값을 그대로 담는 자리다. 앞의 두 집합도 호출자가 만든다. ## 그 객체를 만드는 곳은 게이트의 테스트뿐이다 생성 지점이 넷이고 전부 같은 파일, 게이트 자신의 단위 테스트다. 그 테스트에는 태그가 붙어 있지 않다. 그래서 기본 test 태스크에 들어가고, check 그래프 안에 있고, 매 PR 마다 도는 워크플로가 그 check 를 돌린다. 판정 로직은 그러니까 실제로 돈다. 그때 게이트가 읽는 다섯 성분이 테스트가 손으로 채운 값일 뿐이다. ## 레인은 등록돼 있는데 아무 게이트에도 붙어 있지 않다 증거 등급이 넷이고, 그 넷에 대응하는 레인이 넷 등록되어 있다. 인프로세스 계약, 실제 Netty, 결함 주입, 성능이다. 등록은 리프의 build.gradle 이 아니라 컨벤션 플러그인이 한다. 지원 매트릭스가 등급마다 이 이름들을 적어 둔다. 그 레인 넷 중 check 그래프에 들어 있는 것은 0 이다. 28개 워크플로 중 grpc 를 이름이나 내용에 담은 것도 0 이다. 레인은 손으로 부르면 돈다. 부르는 곳이 없을 뿐이다. ## 없는 것은 레인과 증거 사이의 코드다 레인이 낸 결과를 증거 객체의 다섯 성분으로 바꾸는 코드가 없다. 그래서 게이트가 읽는 값은 레인이 실제로 무엇을 했는지와 연결되어 있지 않다. 블록이 출하되기 시작해도 이 연결은 저절로 생기지 않는다. ## 같은 저장소에 그 사슬을 이어 놓은 예가 있다 messaging 쪽은 세 층이다. 레인이 실행 결과를 증거 파일로 쓴다. Gradle 태스크가 그 산출물과 커밋된 매니페스트를 대조하고, 커밋본이 레인이 내지 않은 시나리오를 주장하면 실패한다. 워크플로가 실제 브로커를 띄워 그 태스크를 돌리고 결과를 산출물로 올린다. 그 태스크에는 캐시를 끄는 한 줄과 이유가 붙어 있다. 이전 실행의 결과로 답할 수 있는 게이트는 그 실행에 대한 증거라는 것이다. 워크플로 단계에도 한 줄이 있다. 커밋 해시를 레인이 읽어 모든 증거 줄에 적는다는 것이고, 인증이 하나의 소스 트리에 대한 주장이기 때문이라는 것이다. ## 게이트의 설계가 차단 사유를 만드는 방식 게이트는 빠진 결과와 빠진 등급과 스키마 판정과 문서 존재를 합쳐 차단 사유 목록을 만든다. 문서를 후속 과제가 아니라 차단 사유로 둔 근거도 적혀 있다. 동작을 먼저 출하하고 런북을 나중에 쓰면, 그것을 처음 만나는 사람이 새벽 세 시에 스스로 알아내야 한다는 것이다. ## 출하되지 않는다는 사실은 결함이 아니다 같은 지원 매트릭스가 그 상태를 적는다. grpc 리프는 전부 런타임 소속이 비어 있어서 이 플랫폼은 빌드 전용이다. 컴파일되고 레인이 돌지만 배포되는 산출물이 담지 않는다. 같은 문서가 릴리스까지 남은 게이트 입력 두 가지도 적는다. 성능 기준선과 스키마 코드 생성이다. ## 확인하지 못한 것 gRPC 블록이 어떤 배포에도 들어가지 않아서 런타임에서 볼 방법이 없다. 레인 넷을 실제로 돌려 결과를 보지는 않았다.