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
6.0 KiB
JSON
15 lines
6.0 KiB
JSON
{
|
|
"assetKey": "a20-f003-claude",
|
|
"kind": "terminal",
|
|
"command": "#!/bin/bash\nset -o pipefail\ncd /shared/codebase/clean-architecture-backend-template/src\nR=grpc/grpc-testkit/build/test-results\nnotime() { sed -E 's/ in [0-9]+m? ?[0-9.]*s$//'; }\nxml() { python3 - \"$1\" <<'PY'\nimport sys, glob, xml.etree.ElementTree as ET\nd = sys.argv[1]\nnames = set()\nfor p in sorted(glob.glob(f\"{d}/TEST-*.xml\")):\n r = ET.parse(p).getroot()\n print(f\" {r.get('name').split('.')[-1]:36s} tests={r.get('tests')} failures={r.get('failures')} errors={r.get('errors')} skipped={r.get('skipped')}\")\n for c in r.iter('testcase'): names.add(c.get('classname') + '#' + c.get('name'))\nopen('/tmp/names_' + d.split('/')[-1], 'w').write('\\n'.join(sorted(names)))\nprint(f\" 시험 {len(names)} 개\")\nPY\n}\n\necho \"# gradle 판\"\n./gradlew --offline --no-daemon --version 2>/dev/null | grep -E '^Gradle|^JVM' | sed 's#^# #'\n\necho\necho \"# grpc-testkit 이 등록하는 레인 넷과 각각의 태그\"\ngrep -n \"lane('\\|tag = \" grpc/grpc-testkit/build.gradle | sed -E 's#^([0-9]+): *# grpc-testkit/build.gradle:\\1 #'\n\necho\necho \"# 기본 test 에 실제로 걸리는 제외 태그는 두 곳에서 온다\"\ngrep -n 'excludeTags' build.gradle grpc/grpc-testkit/build.gradle | sed 's#^# #'\nawk 'NR>=37 && NR<=44 {printf \" grpc-testkit/build.gradle:%d %s\\n\", NR, $0}' grpc/grpc-testkit/build.gradle\necho \" grpc-testkit 안의 quarantine 태그 : $(grep -rc 'quarantine' grpc/grpc-testkit/src/test --include=*.java 2>/dev/null | awk -F: '{s+=$2} END{print s+0}') 건\"\n\necho\necho \"# 레인 태그는 클래스 단위인가\"\ngrep -rn -A1 '@Tag' grpc/grpc-testkit/src/test/java --include=*.java \\\n | grep -E '@Tag|class ' | sed 's#.*/java/dev/caskeleton/grpc/##;s#^# #'\n\necho\necho \"# check 의 태스크 그래프 — 시험을 돌리지 않고 확인한다\"\ntimeout 900 ./gradlew --offline --no-daemon :grpc:grpc-testkit:check --dry-run > /tmp/dry.out 2>&1; D=$?\necho \" \\$ ./gradlew :grpc:grpc-testkit:check --dry-run GRADLE_EXIT=$D\"\ngrep ':grpc:grpc-testkit:' /tmp/dry.out | sed 's#^# #'\necho \" 그 그래프 안의 레인 태스크 : $(grep -cE ':grpc:grpc-testkit:(grpcInProcess|grpcNetty|grpcFault|grpcPerformance)' /tmp/dry.out) 건\"\n\necho\necho \"# 낡은 결과를 지우고 세 레인을 이름으로 돌린다\"\nrm -rf $R/grpcInProcessContractTest $R/grpcNettyContractTest $R/grpcFaultTest $R/test\necho \" 실행 전 남아 있는 결과 XML : $(find $R -maxdepth 2 -name 'TEST-*.xml' 2>/dev/null | grep -E '/(grpcInProcessContractTest|grpcNettyContractTest|grpcFaultTest|test)/' | wc -l) 건\"\ntimeout 1800 ./gradlew --offline --no-daemon --rerun-tasks \\\n :grpc:grpc-testkit:grpcInProcessContractTest \\\n :grpc:grpc-testkit:grpcNettyContractTest \\\n :grpc:grpc-testkit:grpcFaultTest > /tmp/g1.out 2>&1; G1=$?\necho \" \\$ ./gradlew :grpc:grpc-testkit:{grpcInProcessContractTest,grpcNettyContractTest,grpcFaultTest}\"\ngrep -E 'BUILD (SUCCESSFUL|FAILED)' /tmp/g1.out | notime | sed 's#^# #'\necho \" GRADLE_EXIT=$G1\"\nif [ $G1 -ne 0 ]; then\n echo \" 레인 실행이 실패했으므로 집계하지 않는다\"\n sed -n '/^FAILURE:/,$p' /tmp/g1.out | head -12 | sed 's#^# #'\n exit $G1\nfi\nfor l in grpcInProcessContractTest grpcNettyContractTest grpcFaultTest; do\n echo \" [$l]\"; xml \"$R/$l\"\ndone\n\necho\necho \"# 같은 leaf 의 기본 test 를 돌린다\"\ntimeout 1800 ./gradlew --offline --no-daemon --rerun-tasks :grpc:grpc-testkit:test > /tmp/g2.out 2>&1; G2=$?\necho \" \\$ ./gradlew :grpc:grpc-testkit:test\"\ngrep -E 'BUILD (SUCCESSFUL|FAILED)' /tmp/g2.out | notime | sed 's#^# #'\necho \" GRADLE_EXIT=$G2\"\nif [ $G2 -ne 0 ]; then\n echo \" 기본 test 실행이 실패했으므로 집계하지 않는다\"\n sed -n '/^FAILURE:/,$p' /tmp/g2.out | head -12 | sed 's#^# #'\n exit $G2\nfi\nxml \"$R/test\"\n\necho\necho \"# 레인이 돌린 시험이 기본 test 가 돌린 시험의 부분집합인가\"\npython3 - <<'PY'\nlanes = set()\nfor l in (\"grpcInProcessContractTest\", \"grpcNettyContractTest\", \"grpcFaultTest\"):\n lanes |= set(open('/tmp/names_' + l).read().split('\\n'))\nbase = set(open('/tmp/names_test').read().split('\\n'))\nprint(f\" 레인 {len(lanes)} 개 · 기본 test {len(base)} 개\")\nprint(f\" 레인이 돌린 시험이 기본 test 안에 전부 있는가 : {lanes <= base}\")\nprint(f\" 기본 test 에만 있는 시험 : {len(base - lanes)} 개\")\nPY\necho \" 성능 레인 클래스가 기본 test 결과에 있는가 : $(ls $R/test/TEST-*GrpcPerformanceLaneTest.xml 2>/dev/null | wc -l) 건\"\n\necho\necho \"# 레인 태스크만 거는 보증\"\nG=build-logic/src/main/groovy/ca.strict-test-lane.gradle\ngrep -n 'failOnNoDiscoveredTests\\|upToDateWhen\\|executedTests\\|executedSelectors\\|excludes tests \\*after\\*\\|refuses zero\\|throw new GradleException' $G \\\n | cut -c1-150 | sed -E 's#^([0-9]+): *# ca.strict-test-lane.gradle:\\1 #'\n\necho\necho \"# 레인 태스크를 자동으로 부르는 경로\"\necho \" grpc-testkit/build.gradle 이 check 를 언급하는가 : $(grep -c 'check' grpc/grpc-testkit/build.gradle) 건\"\necho \" 워크플로 $(ls ../.github/workflows/*.yml | wc -l) 개 중 grpc 를 언급하는 것 : $(grep -rli 'grpc' ../.github/workflows/ 2>/dev/null | wc -l) 건\"\nawk 'NR>=48 && NR<=61 {printf \" ci-quality-gates.yml:%d %s\\n\", NR, $0}' ../.github/workflows/ci-quality-gates.yml\necho \" 그 마지막 스텝이 부르는 태스크가 무엇을 요구하는가 :\"\ngrep -n 'grpcTransportQualificationTest' build.gradle | sed -E 's#^([0-9]+): *# src/build.gradle:\\1 #'\n",
|
|
"cwd": "/shared/codebase/clean-architecture-backend-template/src",
|
|
"exitCode": 0,
|
|
"executedAt": "2026-09-04T02:08:19+00:00",
|
|
"sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916",
|
|
"raw": "evidence/raw/a20-f003-claude.txt",
|
|
"svg": "evidence/rendered/a20-f003-claude.svg",
|
|
"rawSha256": "54287f044cb33765e11563e0b1740c1020f0b277a92c8aa774256061f0173da6",
|
|
"lines": 134,
|
|
"redaction": "비밀값 없음"
|
|
}
|