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>
149 lines
8.5 KiB
Markdown
149 lines
8.5 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: a07-f004-claude
|
|
title: 문서가 선언됐다는 둘은 없고, 선언된 하나는 그 문장에 없다
|
|
topic: identity-and-identifier
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:a07-f004-claude
|
|
evidenceCapturedOn: 2026-09-02
|
|
assets:
|
|
- key: a07-f004-claude
|
|
file: ../../../final/evidence/rendered/a07-f004-claude.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/a07-f004-claude.txt
|
|
source:
|
|
- 원본 분석 절은 analysis/07-adapter-outbound-identifier.md#L131 이다. 등급은 P3 이다. 선언 블록이 두 줄이라는 사실, 도메인 코어와 외부 라이브러리가 선언돼 있지 않다는 판정, 애플리케이션 코어가 언급되지 않았다는 지적, 그리고 실제 선언이 허용 목록의 부분집합이라는 결론이 그 절에 있다.
|
|
- 그 절은 문서 인용에서 `:application-core` 를 `:application-code` 로 적었다. 원문은 `core` 다.
|
|
- 앞 문장의 공유 계약이 강제되는 허용 목록에 없다는 것은 이 기록이 새로 확인했다. 그 라이브러리가 이 리프의 클래스패스에 없다는 판정은 그 절에 있고, 여기서는 잠금 파일의 빈 구성 표기로 그것을 다시 확인했다.
|
|
---
|
|
|
|
# 문서가 선언됐다는 둘은 없고, 선언된 하나는 그 문장에 없다
|
|
|
|
리프 지침 문서의 의존성 문단은 두 문장이다. 앞 문장이 든 허용 목록 셋 중 하나는 강제되는 레지스트리에 없다. 뒤 문장이 선언돼 있다고 적은 둘은 어느 쪽도 없고, 실제 선언 하나는 그 문장에 없다.
|
|
|
|
## 관계
|
|
|
|
- **사라진 가드가 문서에만 남았고 그 부재는 이미 승인된 결정이다**
|
|
같은 문서의 다른 오류다.
|
|
- **README의 세 가지 사실 오류**
|
|
같은 리프의 문서 오류다.
|
|
- **허용 의존 목록은 상한이므로 미사용을 잡지 않는다**
|
|
실제 선언이 그 상한의 부분집합인 이유다.
|
|
|
|
## 문제
|
|
|
|
지침 문서에서 허용을 적은 절은 두 문장이다. 앞 문장은 이 리프가 의존할 수 있는 것을 셋으로 적고, 뒤 문장은 지금 빌드 파일에 실제로 선언돼 있다는 것을 적는다. 뒤 문장이 드는 둘 중 하나는 앞 목록의 항목이고 다른 하나는 그 목록에 없는 외부 라이브러리다.
|
|
|
|
## 결론
|
|
|
|
빌드 파일의 선언 블록은 두 줄이다. 애플리케이션 코어 프로젝트와 Spock 시험 라이브러리다.
|
|
|
|
뒤 문장이 든 둘은 어느 쪽도 거기 없다. 도메인 코어는 표기를 가리지 않고 세어도 빌드 파일에 0 건이다. 외부 UUID 생성 라이브러리는 이 리프의 잠금 파일에 없고, 그 잠금 파일은 컴파일과 런타임 클래스패스가 비어 있다고 적는다. 이 저장소는 모든 구성을 엄격 모드로 잠그므로, 그 라이브러리가 시험 경로에만 없는 것이 아니라 프로덕션 경로 자체에 없다. 실제로 그것을 선언하는 곳은 예제 애플리케이션 한 줄이다.
|
|
|
|
실제로 선언된 애플리케이션 코어는 그 문장에 나오지 않는다. only 라고 쓴 이상 하나를 빠뜨린 것도 위반이다.
|
|
|
|
앞 문장은 셋 중 둘이 맞고 하나가 어긋난다. 강제되는 목록은 모듈 레지스트리에 있고 거기 적힌 것은 도메인 코어와 애플리케이션 코어다. 문서가 든 셋 중 공유 계약은 그 목록에 없다.
|
|
|
|
목록의 성격은 서술이 아니라 게이트다. 루트 빌드 파일이 레지스트리에서 허용 간선 표를 파생하고, 허용 밖 간선을 만나면 빌드를 실패시키며, 모든 리프의 검사가 그 태스크에 의존한다. 공유 계약을 실제로 선언하면 빌드가 멈춘다.
|
|
|
|
빌드 자체는 정합하다. 실제 선언이 허용 목록의 부분집합이다. 어긋난 범위는 문단 하나이고, 그 안의 진술 넷이 사실과 다르다. 판정은 P3 다.
|
|
|
|
## 검증 환경
|
|
|
|
Gradle : 9.0.0
|
|
확인 방식 : 지침 문서와 빌드 파일·잠금 파일·모듈 레지스트리 대조
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
1. 리프 지침 문서의 허용 절 두 문장을 읽는다.
|
|
2. 빌드 파일의 선언 블록을 연다.
|
|
3. 도메인 코어를 표기를 가리지 않고 빌드 파일에서 센다.
|
|
4. 외부 라이브러리를 리프 잠금 파일에서 세고, 그 잠금 파일이 덮는 구성과 비어 있는 구성을 읽는다.
|
|
5. 잠금 모드 설정을 확인한다.
|
|
6. 그 라이브러리를 실제로 선언하는 곳을 찾는다.
|
|
7. 모듈 레지스트리에서 이 리프의 허용 목록을 읽고 앞 문장의 셋과 대조한다.
|
|
8. 그 목록이 어떻게 강제되는지 사슬을 따라간다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
지침 문서의 허용 절은 두 문장이다.
|
|
|
|
## 허용 절과 선언 블록
|
|
|
|
:::evidence key="a07-f004-claude" alt="리프 지침 문서의 허용 절과 빌드 파일의 선언 블록. 도메인 코어를 빌드 파일에서, 외부 UUID 생성 라이브러리를 리프 잠금 파일에서 각각 센 결과와 그 잠금 파일이 덮는 구성 목록, 비어 있는 구성, 잠금 모드 설정. 그 라이브러리를 실제로 선언하는 곳. 그리고 모듈 레지스트리의 허용 목록과 문서가 든 셋의 차이, 그 목록을 강제하는 코드 사슬을 출력한 터미널 기록." caption="문서는 도메인 코어와 uuid-creator 가 선언됐다고 적음 · 선언 블록은 애플리케이션 코어와 Spock 두 줄 · 빌드 파일의 도메인 코어 언급 0, 잠금 파일의 uuid-creator 0이고 컴파일·런타임 클래스패스는 비어 있음 · 레지스트리 허용 목록에 공유 계약 없음 · 허용 밖 간선은 빌드를 실패시킴 — 39줄 · exit 0" zoom="true"
|
|
:::
|
|
|
|
```text
|
|
- `:application-core`, `:domain-core`, `:shared-contract` (Gradle matrix). Currently
|
|
only `:domain-core` + `com.github.f4b6a3:uuid-creator` are declared in
|
|
[build.gradle](build.gradle).
|
|
```
|
|
|
|
선언 블록은 두 줄이다.
|
|
|
|
```groovy
|
|
dependencies {
|
|
implementation project(':application-core')
|
|
|
|
testImplementation 'org.spockframework:spock-core:2.4-groovy-5.0'
|
|
}
|
|
```
|
|
|
|
## 뒤 문장의 두 이름
|
|
|
|
```text
|
|
# 빌드 파일의 domain-core 언급(모든 표기) : 0
|
|
# 잠금 파일 줄 수 / uuid-creator·f4b6a3 : 154 / 0
|
|
# 프로덕션 클래스패스의 잠금 상태 : empty=compileClasspath,runtimeClasspath
|
|
336: lockAllConfigurations()
|
|
337: lockMode = LockMode.STRICT
|
|
```
|
|
|
|
도메인 코어는 어떤 표기로도 빌드 파일에 없다. 외부 라이브러리 쪽은 잠금 파일이 더 강하게 말한다 — 이 저장소는 모든 구성을 엄격 모드로 잠그고, 이 리프의 컴파일·런타임 클래스패스는 잠긴 채 비어 있다. 시험 경로에만 없는 것이 아니라 프로덕션 경로 자체에 아무것도 없다.
|
|
|
|
그 라이브러리를 선언하는 곳은 따로 있다.
|
|
|
|
```text
|
|
sample-portfolio/build.gradle:52: implementation 'com.github.f4b6a3:uuid-creator:6.1.1'
|
|
```
|
|
|
|
선언 블록의 유일한 프로젝트 의존은 그 문장에 나오지 않는다. 문장이 `only` 라고 적어 선언 전체를 배타적으로 주장했으므로, 빠뜨린 것도 어긋남이다.
|
|
|
|
## 앞 문장의 세 이름
|
|
|
|
```text
|
|
# 레지스트리 allowed_dependencies : ['domain-core', 'application-core']
|
|
# 문서가 든 셋 중 목록에 없는 것 : ['shared-contract']
|
|
```
|
|
|
|
레지스트리가 이 리프에 허용한 것은 둘이다. 셋째 항목은 여기 없다.
|
|
|
|
이 목록은 서술이 아니라 게이트다.
|
|
|
|
```groovy
|
|
1419: Map<String, Set<String>> allowedProjectDependencies = registry.modules.collectEntries { module ->
|
|
...
|
|
Set<String> forbidden = actual - allowed
|
|
if (!forbidden.isEmpty()) {
|
|
throw new GradleException(
|
|
...
|
|
577: dependsOn rootProject.tasks.named('verifyCleanArchitectureDependencies')
|
|
```
|
|
|
|
루트 빌드 파일이 레지스트리에서 허용 간선 표를 파생하고, 허용 밖 간선을 만나면 빌드를 실패시킨다. 모든 리프의 검사가 그 태스크에 의존하므로 이 리프도 지난다. 문서가 매트릭스라고 적은 셋째 항목을 실제로 선언하면 빌드가 멈춘다.
|
|
|
|
## 실제 선언과 허용 목록
|
|
|
|
실제 선언은 허용 목록 안에 있다. 간선 쪽에서 고칠 것은 없다. 어긋난 것은 문단 하나이고, 그 문단이 담은 진술 넷이 사실과 다르다.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
문서가 든 두 이름이 과거 어느 시점에 실제로 선언되어 있었는지 확인하지 않았다.
|
|
|
|
<!-- body:end -->
|