The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
9.0 KiB
Markdown
137 lines
9.0 KiB
Markdown
---
|
|
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 쪽의 증거 파일과 대조 태스크와 워크플로를 확인한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
`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 블록이 어떤 배포에도 들어가지 않아서 런타임에서 볼 방법이 없다. 레인 넷을 실제로 돌려 결과를 보지는 않았다.
|
|
|
|
<!-- body:end -->
|