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>
6.0 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | analysis-finding-a06-f026 | 커버리지 gate 둘이 나란히 있고, 하나는 발화할 수 없다 | multitenancy-isolation | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a06-f026 | 2026-09-01 | case-analysis-finding-a06-f026.body.md |
|
|
|
커버리지 gate 둘이 나란히 있고, 하나는 발화할 수 없다
계약 묶음의 javadoc 은 실패한 계약과 한 번도 돌지 않은 계약을 구분한다고 적는다. 구현에서 실행 집합이 전체와 항상 같으므로 누락 항목이 어떤 입력으로도 생성되지 않는다. 같은 테스트킷에 옳게 구현된 형제가 있다.
관계
- 빠뜨림이 통과가 되는 게이트는 게이트가 아니다 이 사례가 그 규칙의 형태다.
- 아무것도 발견하지 못한 레인은 성공이 아니라 실패여야 한다 같은 원칙의 레인 판이다.
- 중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다 형제 구현과 비교하는 방법이다.
문제
계약 묶음의 javadoc 이 존재 이유를 적는다.
보고서는 실패한 계약과 한 번도 돌지 않은 계약을 구분한다. 절반이 건너뛰어졌기 때문에 실패 없음을 보고하는 묶음은 아무것도 인증하지 않는 초록 빌드의 전형이므로, 누락된 계약은 여기서 실패다.
결론
구현이 그 구분을 만들 수 없다.
루프가 실행 집합을 무조건 채운다. 각 계약을 실행 집합에 넣고 그다음 검사를 수행한다.
그러므로 실행 집합은 전체 집합과 언제나 같고, 누락 집합은 언제나 비며, 실행되지 않음 항목은 어떤 입력으로도 생성되지 않는다.
인증 여부를 판정하는 조건도 마찬가지로 항상 참이다.
조건부 형제가 같은 테스트킷 안에 있다. 카오스 게이트의 보고 메서드는 같은 일을 옳게 한다. 실행 집합이 명시적 기록 호출로만 채워지는 맵이고, 누락은 전체에서 기록되지 않은 것을 뺀 것이다.
그리고 그 형제의 테스트가 그것을 증명한다. 열세 시나리오 중 하나만 기록하고 나머지가 실행되지 않음으로 나타나는지 단언한다.
계약 묶음의 대응 테스트는 그렇게 하지 않는다. 모든 계약에 통과를 주고 나서 실행 집합이 전부를 담는지 단언한다. 구조상 항상 참인 것을 단언하는 것이다.
판정은 두 인증 레인의 커버리지 주장이 무효라는 것이다.
수정은 형제를 따르면 된다. 실행 메서드가 실행할 계약 집합을 인자로 받거나, 실제로 호출된 것만 실행 집합에 넣는 것이다.
검증 환경
OpenJDK : 21.0.12 확인 방식 : 구현 코드 확인과 형제 구현 비교 소스 수정 : x
재현 조건
원문은 final/evidence/raw/138 계열에 있다.
- 계약 묶음의 javadoc 을 읽는다.
- 보고 메서드의 루프에서 실행 집합이 어떻게 채워지는지 확인한다.
- 누락 집합이 어떻게 계산되는지 확인한다.
- 카오스 게이트의 같은 메서드와 비교한다.
- 두 테스트가 각각 무엇을 단언하는지 비교한다.
본문
MongoStableContractSuite의 javadoc이 존재 이유를 적는다.
The report distinguishes a failed contract from a contract that never ran. A suite that reports "no failures" because half of it was skipped is exactly the shape of green build that certifies nothing, so a missing contract is a failure here.
suite javadoc 이 적은 존재 이유
:::evidence key="analysis-finding-a06-f026" alt="분석 문서 analysis/06-adapter-outbound-persistence-mongo.md 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="analysis/06-adapter-outbound-persistence-mongo.md 발췌 — 15줄" zoom="true" :::
구현은 그 구분을 만들 수 없다
Set<MongoReplicaSetContract> executed = new LinkedHashSet<>();
for (MongoReplicaSetContract contract : MongoReplicaSetContract.all()) {
executed.add(contract); // ← 루프가 무조건 채운다
if (!contractRunner.test(contract)) { failures.add(...); }
}
Set<MongoReplicaSetContract> missing = new LinkedHashSet<>(MongoReplicaSetContract.all());
missing.removeAll(executed); // ← 항상 비어 있다
missing.forEach(contract -> failures.add(... + " (not executed)"));
executed는 all()과 언제나 같으므로 missing은 언제나 비고, (not executed) 항목은 어떤 입력으로도 생성되지 않는다. certified()의 executed.containsAll(all())(78행)도 항상 참이다. 두 인증 lane(7.0/8.0)의 커버리지 주장이 무효다. P2.
조건부 형제가 같은 testkit 안에 있다
MongoChaosGate.report()는 같은 일을 옳게 한다 — executed는 명시적 record(scenario, passed) 호출로만 채워지는 map이고, missing은 all()에서 기록되지 않은 것을 뺀 것이다. 그 test가 그것을 증명한다: aScenarioThatNeverRanIsAFailureRatherThanASilence는 13개 시나리오 중 하나만 기록하고 나머지가 (not executed)로 나타나는지 단언한다. 수정은 형제를 따르면 된다 — run(...)이 실행할 contract 집합을 인자로 받거나, runner가 실제로 호출된 것만 executed에 넣는 것.
확인하지 못한 것
계약 하나를 실제로 건너뛰게 만들어 보고서가 여전히 깨끗한지 재현하지 않았다. 코드 구조상 그 결과가 나온다.