docs(keycloak-session-store): import the session-storage lab as a new project

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>
This commit is contained in:
DongHyeonka
2026-09-04 22:51:59 +09:00
co-authored by Claude Opus 5
parent 43bccd08a8
commit b2963105a8
5017 changed files with 372751 additions and 4943 deletions
@@ -0,0 +1,128 @@
# Tech-Log candidate recall audit — cycle 2
> 대상: `/shared/document-detail/clean-architecture-backend-template` 의 canonical module SSOT **61편**
> 목적: 61개 SSOT 에서 발견된 material semantic unit 이 `candidate-ledger.json` 에서 전부 설명되는지에 대한 coverage proof.
> 판정 기준: verifier 가 인벤토리하는 앵커(`analysis/**/*.md` 의 `P1|P2|P3` 헤딩과 미해결 질문 절의 번호 항목)마다
> `EMITTED | MERGED | REJECTED | BLOCKED` 중 하나가 존재해야 한다. `unaccounted` 는 0 이어야 한다.
## 전체 수치
| 항목 | 값 |
|---|---:|
| canonical module SSOT | 61 / 61 |
| verifier inventory 앵커 | 425 |
| 그중 nested leaf SSOT | 226 |
| §17 explicit finding (61 SSOT) | 392 |
| candidate ledger total | 605 |
| EMITTED | 555 |
| MERGED | 49 |
| REJECTED | 0 |
| BLOCKED | 1 |
| **unaccounted** | **0** |
## 모듈별
| module | explicit findings | CASE | OPEN QUESTION | DECISION | CONCEPT | REFERENCE | MERGED | REJECTED | BLOCKED | unaccounted |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| `adapter-inbound-graphql` | 12 | 9 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 |
| `adapter-inbound-grpc` | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `adapter-inbound-web` | 19 | 16 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 |
| `adapter-inbound-websocket` | 3 | 2 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| `adapter-outbound-cache-redis` | 11 | 7 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 |
| `adapter-outbound-fileserver` | 6 | 4 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 |
| `adapter-outbound-httpclient` | 7 | 6 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 |
| `adapter-outbound-identifier` | 6 | 5 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| `adapter-outbound-messaging` | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `adapter-outbound-notification` | 11 | 9 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 |
| `adapter-outbound-objectstorage` | 6 | 3 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 |
| `adapter-outbound-persistence-jpa` | 34 | 26 | 1 | 0 | 0 | 0 | 7 | 0 | 0 | 0 |
| `adapter-outbound-persistence-mongo` | 28 | 24 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 |
| `adapter-outbound-support` | 6 | 3 | 1 | 0 | 0 | 0 | 2 | 0 | 0 | 0 |
| `app-bootstrap` | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `application-core` | 4 | 1 | 2 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| `domain-core` | 2 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| `grpc-admin` | 4 | 5 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| `grpc-advanced-bootstrap` | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-advanced-compat` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-advanced-diagnostics` | 3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-advanced-edition` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-advanced-resilience` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-advanced-streaming` | 3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-client` | 4 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-codegen` | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-core-api` | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-discovery` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-observability` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-operation-ledger-jpa` | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-policy` | 8 | 10 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| `grpc-proto-contract` | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-server` | 4 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-spring-boot-starter` | 4 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `grpc-testkit` | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `messaging-admin-api` | 9 | 9 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| `messaging-admin-runtime` | 11 | 12 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-claim-check` | 5 | 2 | 1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-cloudevents` | 5 | 1 | 1 | 1 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-core-api` | 6 | 4 | 2 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
| `messaging-inbox-jdbc-postgresql` | 6 | 4 | 1 | 0 | 1 | 4 | 0 | 0 | 0 | 0 |
| `messaging-kafka` | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `messaging-kafka-share-experimental` | 6 | 1 | 1 | 0 | 0 | 4 | 0 | 0 | 0 | 0 |
| `messaging-nats-experimental` | 4 | 6 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 |
| `messaging-observability` | 7 | 5 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| `messaging-outbox-jdbc-postgresql` | 8 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `messaging-policy` | 7 | 3 | 1 | 0 | 0 | 3 | 0 | 0 | 0 | 0 |
| `messaging-pulsar-experimental` | 3 | 4 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-rabbit` | 5 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `messaging-reliability-api` | 8 | 4 | 1 | 0 | 1 | 4 | 0 | 0 | 0 | 0 |
| `messaging-runtime-core` | 7 | 3 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 0 |
| `messaging-schema-api` | 3 | 1 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-schema-avro` | 5 | 2 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 |
| `messaging-schema-json` | 3 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| `messaging-schema-protobuf` | 4 | 1 | 1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 |
| `messaging-security` | 8 | 5 | 1 | 0 | 0 | 5 | 0 | 0 | 0 | 0 |
| `messaging-spring-boot-starter` | 6 | 8 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| `messaging-spring-cloud-stream-bridge` | 6 | 0 | 1 | 0 | 0 | 5 | 0 | 0 | 0 | 0 |
| `messaging-testkit` | 8 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `messaging-transport-spi` | 4 | 2 | 0 | 0 | 1 | 4 | 0 | 0 | 0 | 0 |
| `shared-contract` | 5 | 0 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| **합계** | **392** | **313** | **22** | **1** | **6** | **60** | **35** | **0** | **0** | **0** |
## candidate 0건 모듈
없음 — 61개 모듈 전부 최소 1건.
## 분류 근거
kind 는 임의로 정하지 않았다. 우선순위는 다음과 같다.
1. **SSOT 자신의 kind hint.** 다수의 리프 SSOT 가 finding 말미에 `- **다음 단계.** … CASE 후보 / REFERENCE 후보 /
OPEN QUESTION 후보` 를 적어 둔다. 그 힌트가 있으면 그것을 따랐다(88건). verifier 도 같은 힌트를
`allowedKinds` 로 강제하므로, 힌트와 어긋난 kind 는 실패로 잡힌다.
2. **힌트가 없으면 finding 의 성격.** 구체적 사건·재현 가능한 결함은 CASE, 재사용 가능한 판단 기준은 REFERENCE,
현재 근거로 닫을 수 없는 질문은 OPEN QUESTION.
3. **P1/P2/P3 는 kind 를 정하지 않는다.** 우선순위는 candidate 의 `priority` 로만 남는다.
## MERGED 판정 기준
`MERGED` 는 causal unit · semantic unit · verification unit 이 **모두** 같을 때만 썼다. "주제가 비슷하다" 는
근거가 아니다. 예로 `messaging-kafka` 의 트랜잭션 검증기 미배선 · 일시정지 파티션 재개 누락 · 오염된 재시도 헤더의
무한 pause · 트랜잭션 레거시 API 잔존은 전부 독립 노드로 냈다. 같은 리프의 신뢰성 주제라는 것은 병합 근거가 아니다.
## §17 밖 recall
문제 finding 외에 다음 절도 검토 대상이었다 — 모듈 정체와 경계, 계약·불변식, 상태 모델, 성공/실패 메커니즘,
transaction/concurrency/lifecycle, negative-space 결과, "확인된 설계(문제 아님)", 소스 주석에 남은 결함 이력.
여기서 나온 CONCEPT/REFERENCE/DECISION 은 TOPIC 17~22 에 있다(CONCEPT 5 · CASE 17 · REFERENCE 11).
정상 설계에서 뽑은 CONCEPT 의 예 — 능력 선언의 세 출처, 원자 타입 위의 검사 후 실행과 비교 후 교체,
bounded/unbounded 오버로드를 나란히 둔 포트, 8단계 종료 순서 계약, 승인·검증·실행의 분리.
kind 별 quota 는 만들지 않았다. 어떤 모듈에 특정 kind 가 0 인 것은 정상이며, 그 경우 위 표의 해당 칸이 0 으로
남고 그 모듈의 finding 이 전부 다른 kind 로 설명된다는 사실이 같은 행에서 확인된다.
## integration/family 문서
`analysis/19-messaging-platform.md` 의 material finding 6건은 leaf candidate 로 환원되지 않는 cross-leaf 사실이라
별도로 disposition 했다 — EMITTED 5(TOPIC 32 `cross-leaf-integration-facts`), MERGED 1(문서 계약 테스트의
커버리지 경계는 기존 CASE 와 같은 사건·같은 검증 단위).