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>
15 lines
4.9 KiB
JSON
15 lines
4.9 KiB
JSON
{
|
|
"assetKey": "a20-f010-grpcoutcomereplay",
|
|
"kind": "terminal",
|
|
"command": "#!/bin/bash\nset -euo pipefail\ncd /shared/codebase/clean-architecture-backend-template\nR=src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcOutcomeReplay.java\nD=src/grpc-advanced/grpc-advanced-streaming/src/main/java/dev/caskeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicator.java\ntest -f \"$R\"; test -f \"$D\"\nCPP=$(cat /tmp/cp-grpc-policy.txt)\n\n# 검색이 헛돌면 0 을 제조하지 않고 죽는다. rc 0=매치, 1=매치없음, 그 밖=오류.\nrefs() {\n local name=$1 self=$2 out rc\n out=$(git grep -l \"$name\" -- 'src/**/*.java'); rc=$?\n [ $rc -le 1 ] || { echo \"SEARCH FAILED rc=$rc for $name\" >&2; exit 2; }\n printf '%s\\n' \"$out\" | { grep '/main/' || true; } | { grep -v \"/$self\" || true; } | grep -c . || true\n}\n\necho \"# 저장소 전체 ($(wc -l < $R) 줄)\"\nawk '{printf \" GrpcOutcomeReplay.java:%d %s\\n\", NR, $0}' $R\n\necho\necho \"# 그 맵에 가해지는 연산 전부\"\ngrep -n 'storedOutcomes' $R | sed -E 's#^([0-9]+): *# GrpcOutcomeReplay.java:\\1 #'\necho \" 클래스와 필드의 접근 한정 :\"\ngrep -nE 'public final class|private final ConcurrentMap' $R | sed -E 's#^([0-9]+): *# GrpcOutcomeReplay.java:\\1 #'\nN='\\.remove|\\.clear|removeIf|computeIfPresent|\\.merge|entrySet|keySet|evict|expire|TTL|Duration|Instant|maximumSize|Caffeine|LinkedHashMap|WeakReference|SoftReference|Cleaner'\necho \" 줄이는 연산 이름 열여덟을 통틀어 : $({ grep -cE \"$N\" $R || true; }) 건\"\necho \" 같은 검색을 대조 파일에 걸면 : $({ grep -cE \"$N\" $D || true; }) 건 (검색이 헛돌지 않음을 보인다)\"\n\necho\necho \"# 크기 상한이 실제로 거부하는가\"\ncd /tmp/probe5\njavac -nowarn -encoding UTF-8 -cp \"$CPP\" -d . OutcomeReplayProbe.java\njava -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -cp \".:$CPP\" OutcomeReplayProbe\ncd /shared/codebase/clean-architecture-backend-template\n\necho\necho \"# 이 타입이 나오는 파일 전부 — 경로 제한 없이, 절단 없이\"\ngit grep -l 'GrpcOutcomeReplay' | sed 's/^/ /'\necho \" 자기 파일 밖 main 참조 : $(refs GrpcOutcomeReplay GrpcOutcomeReplay.java) 개\"\necho \" [대조] 같은 패키지의 GrpcIdempotencyDecision : $(refs GrpcIdempotencyDecision GrpcIdempotencyDecision.java) 개 (0 이 아니어야 정상)\"\necho \" git 이 추적하지 않는 변경 : $(git status --porcelain | grep -c . || true) 건\"\necho \" .java 밖에서 이 이름을 쓰는 파일 : $({ git grep -l 'GrpcOutcomeReplay' -- ':!*.java' || true; } | grep -c . || true) 개\"\n\necho\necho \"# 같은 패키지의 인터셉터는 재생을 어떻게 판정하는가\"\nI=src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcIdempotencyInterceptor.java\nawk 'NR>=95 && NR<=110 {printf \" GrpcIdempotencyInterceptor.java:%d %s\\n\", NR, $0}' $I\necho \" 그 파일이 GrpcOutcomeReplay 를 부르는가 : $({ grep -c 'GrpcOutcomeReplay' $I || true; }) 건\"\n\necho\necho \"# 자바독이 이 타입을 무엇이라고 적는가\"\nawk 'NR>=7 && NR<=16 {printf \" GrpcOutcomeReplay.java:%d %s\\n\", NR, $0}' $R\n\necho\necho \"# 대조 클래스에 같은 잣대를 댄다 ($(wc -l < $D) 줄)\"\necho \" 이 파일 : $R\"\necho \" 대조 파일 : $D\"\nawk 'NR>=11 && NR<=14 {printf \" GrpcClientMessageDeduplicator.java:%d %s\\n\", NR, $0}' $D\nawk 'NR>=16 && NR<=23 {printf \" GrpcClientMessageDeduplicator.java:%d %s\\n\", NR, $0}' $D\necho \" 적용된 메시지마다 무엇을 넣는가 :\"\nawk 'NR>=70 && NR<=77 {printf \" GrpcClientMessageDeduplicator.java:%d %s\\n\", NR, $0}' $D\necho \" 정리 경로와 그 호출자 :\"\ngit grep -n 'endSession' -- 'src/**/*.java' \\\n | sed -E 's#^src/[^:]*/src/(main|test)/java/dev/caskeleton/[^:]*/([^:/]+):([0-9]+):# \\1 · \\2:\\3 #'\necho \" 그 대조 클래스의 자기 파일 밖 main 참조 : $(refs GrpcClientMessageDeduplicator GrpcClientMessageDeduplicator.java) 개\"\n\necho\necho \"# 두 클래스가 같은 family 인가\"\necho \" modules.json 의 family 키 : $({ grep -c 'family' src/config/architecture/modules.json || true; }) 건\"\nawk 'NR>=1 && NR<=3 {printf \" src/grpc/CLAUDE.md:%d %s\\n\", NR, $0}' src/grpc/CLAUDE.md\nawk 'NR>=3 && NR<=9 {printf \" src/grpc-advanced/CLAUDE.md:%d %s\\n\", NR, $0}' src/grpc-advanced/CLAUDE.md\ngrep -nE 'grpc:\\*.*→|Stable leaf' src/grpc-advanced/CLAUDE.md \\\n | sed -E 's#^([0-9]+): *# src/grpc-advanced/CLAUDE.md:\\1 #'\n",
|
|
"cwd": "/shared/codebase/clean-architecture-backend-template",
|
|
"exitCode": 0,
|
|
"executedAt": "2026-09-04T03:20:56+00:00",
|
|
"sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916",
|
|
"raw": "evidence/raw/a20-f010-grpcoutcomereplay.txt",
|
|
"svg": "evidence/rendered/a20-f010-grpcoutcomereplay.svg",
|
|
"rawSha256": "52cd9945d3ab8ade10258912cd363ab0900d3439f939546eb4db6cd958465118",
|
|
"lines": 166,
|
|
"redaction": "비밀값 없음"
|
|
}
|