Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/a10-f001-readme-counts.svg
T
DongHyeonkaandClaude Opus 5 b2963105a8 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>
2026-09-04 22:51:59 +09:00

114 lines
18 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1844" viewBox="0 0 1200 1844" role="img">
<title>terminal evidence</title>
<desc>Terminal-style rendering generated from retained command output. Sensitive-looking values are redacted in the visual asset.</desc>
<rect x="1" y="1" width="1198" height="1842" rx="14" fill="#0d1117" stroke="#30363d"/>
<rect x="1" y="1" width="1198" height="44" rx="14" fill="#161b22"/>
<rect x="1" y="30" width="1198" height="14" fill="#161b22"/>
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
<circle cx="64" cy="22" r="6" fill="#28c840"/>
<text x="92" y="27" fill="#8b949e" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">terminal evidence</text>
<text x="24" y="68" fill="#c9d1d9" font-size="15" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">$ L=src/adapter/outbound/cache-redis
R=$L/src/main/java/dev/caskeleton/adapter/outbound/cache/redis
C=$R/sdk/config/Redi</tspan><tspan x="24" dy="22">sSdkAutoConfiguration.java
M=$L/src/main/java
echo &#x27;# 표가 없다고 적은 자리를 센다&#x27;
echo -n &#x27;# sdk/lettuce/connection</tspan><tspan x="24" dy="22"> : &#x27;
echo &quot;$(find $R/sdk/lettuce/connection -name &#x27;*.java&#x27; | wc -l) 파일 $(find $R/sdk/lettuce/connection -name &#x27;*.java&#x27;</tspan><tspan x="24" dy="22"> -exec cat {} + | wc -l) 줄&quot;
find $R/sdk/lettuce/connection -name &#x27;*.java&#x27; -exec wc -l {} + | sort -rn | sed -n &#x27;2,5p&#x27; |</tspan><tspan x="24" dy="22"> sed &quot;s#$R/# #&quot;
echo &#x27;# 표가 든 다섯 포트 중 구현이 있는 넷&#x27;
for p in cache idempotency ratelimit lease; do
print</tspan><tspan x="24" dy="22">f &#x27; %-12s %s 파일 %s 줄\n&#x27; &quot;$p&quot; &quot;$(find $R/$p -name &#x27;*.java&#x27; | wc -l)&quot; &quot;$(find $R/$p -name &#x27;*.java&#x27; -exec cat {} + | </tspan><tspan x="24" dy="22">wc -l)&quot;
done
echo -n &#x27; 합계 &#x27;
echo &quot;$(find $R/cache $R/idempotency $R/ratelimit $R/lease -name &#x27;*.java&#x27; | wc </tspan><tspan x="24" dy="22">-l) 파일 $(find $R/cache $R/idempotency $R/ratelimit $R/lease -name &#x27;*.java&#x27; -exec cat {} + | wc -l) 줄&quot;
echo -n &#x27;# 다</tspan><tspan x="24" dy="22">섯째 session : &#x27;
ls -d $R/session 2&gt;/dev/null || { echo -n &#x27;패키지 없음, main 전체에서 session 문자열 &#x27;; grep -rn &#x27;sessi</tspan><tspan x="24" dy="22">on&#x27; -i --include=*.java $M | wc -l; }
echo -n &#x27;# 그 행에 이름이 없어 뺀 패키지 : &#x27;
echo &quot;realtime $(find $R/realtime -</tspan><tspan x="24" dy="22">name &#x27;*.java&#x27; -exec cat {} + | wc -l) 줄, keyspace $(find $R/keyspace -name &#x27;*.java&#x27; -exec cat {} + | wc -l) 줄&quot;
echo -n</tspan><tspan x="24" dy="22"> &#x27;# 상태 기여자 : &#x27;
echo &quot;$(wc -l &lt; $R/sdk/config/RedisHealthContributor.java) + $(wc -l &lt; $R/sdk/config/RedisCorrectn</tspan><tspan x="24" dy="22">essRoles.java)&quot;
echo
echo &#x27;# 조립되지 않는다고 적힌 자동 설정의 @Bean 메서드&#x27;
grep -n &#x27;@Bean&#x27; -A2 &quot;$C&quot; | grep -E &#x27;@Bean</tspan><tspan x="24" dy="22">|@Conditional|public &#x27; | grep -v &#x27;^55:&#x27; | sed &#x27;s/^/ /&#x27;
echo
echo &#x27;# build.gradle 이 등록한 간선 셋과 앞 주석&#x27;
sed -n</tspan><tspan x="24" dy="22"> &#x27;7,15p&#x27; $L/build.gradle
echo
echo &#x27;# 그 간선들을 임포트하는 파일&#x27;
for e in application shared adapter.outbound.suppo</tspan><tspan x="24" dy="22">rt; do
printf &#x27; dev.caskeleton.%-24s %s 파일\n&#x27; &quot;$e&quot; &quot;$(grep -rl &quot;^import dev.caskeleton.$e&quot; $M | wc -l)&quot;
done
echo</tspan><tspan x="24" dy="22"> -n &#x27;# 주석의 주어인 sdk 패키지만 보면 : &#x27;
grep -rl &#x27;dev.caskeleton.application\|dev.caskeleton.shared&#x27; $R/sdk | wc -l</tspan><tspan x="24" dy="22">
echo
echo &#x27;# 스무 줄 뒤의 별개 주석&#x27;
sed -n &#x27;33,39p&#x27; $L/build.gradle</tspan></text>
<text x="24" y="464" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">cwd: /shared/codebase/clean-architecture-backend-template</tspan></text>
<text x="24" y="486" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">time: 2026-09-02T10:44:47+00:00 · exit 0</tspan></text>
<line x1="24" y1="500" x2="1176" y2="500" stroke="#30363d"/>
<text x="24" y="540" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 표가 없다고 적은 자리를 센다</tspan></text>
<text x="24" y="562" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># sdk/lettuce/connection : 9 파일 1473 줄</tspan></text>
<text x="24" y="584" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 600 sdk/lettuce/connection/RedisTopologyClientFactory.java</tspan></text>
<text x="24" y="606" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 316 sdk/lettuce/connection/RedisRuntimeOwner.java</tspan></text>
<text x="24" y="628" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 153 sdk/lettuce/connection/SentinelFailoverObserver.java</tspan></text>
<text x="24" y="650" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 142 sdk/lettuce/connection/RedisConnectionRegistry.java</tspan></text>
<text x="24" y="672" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 표가 든 다섯 포트 중 구현이 있는 넷</tspan></text>
<text x="24" y="694" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> cache 2 파일 641 줄</tspan></text>
<text x="24" y="716" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency 2 파일 835 줄</tspan></text>
<text x="24" y="738" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> ratelimit 3 파일 516 줄</tspan></text>
<text x="24" y="760" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> lease 2 파일 606 줄</tspan></text>
<text x="24" y="782" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 합계 9 파일 2598 줄</tspan></text>
<text x="24" y="804" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 다섯째 session : 패키지 없음, main 전체에서 session 문자열 8</tspan></text>
<text x="24" y="826" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 행에 이름이 없어 뺀 패키지 : realtime 591 줄, keyspace 106 줄</tspan></text>
<text x="24" y="848" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 상태 기여자 : 88 + 62</tspan></text>
<text x="24" y="870" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="892" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 조립되지 않는다고 적힌 자동 설정의 @Bean 메서드</tspan></text>
<text x="24" y="914" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 83: @Bean</tspan></text>
<text x="24" y="936" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 85- public RedisSdkSettings redisSdkSettings() {</tspan></text>
<text x="24" y="958" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 101: @Bean</tspan></text>
<text x="24" y="980" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 102- public RedisSdkSettingsValidation redisSdkSettingsValidation(</tspan></text>
<text x="24" y="1002" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 156: @Bean</tspan></text>
<text x="24" y="1024" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 157- public RedisResolvedCredentials redisResolvedCredentials(</tspan></text>
<text x="24" y="1046" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 226: @Bean</tspan></text>
<text x="24" y="1068" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 227- public RedisRuntimeClient redisRuntimeClient(</tspan></text>
<text x="24" y="1090" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 273: @Bean(destroyMethod = &quot;close&quot;)</tspan></text>
<text x="24" y="1112" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 274- public RedisRuntimeOwner redisRuntimeOwner(RedisRuntimeClient client, RedisSdkSettings settings) {</tspan></text>
<text x="24" y="1134" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 297: @Bean(RedisCorrectnessRoles.OPTIONAL_HEALTH_CONTRIBUTOR)</tspan></text>
<text x="24" y="1156" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 298- public HealthIndicator redisOptional(RedisRuntimeOwner owner, RedisSdkSettings settings) {</tspan></text>
<text x="24" y="1178" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 324: @Bean(RedisCorrectnessRoles.REQUIRED_HEALTH_CONTRIBUTOR)</tspan></text>
<text x="24" y="1200" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 325- @Conditional(RedisCorrectnessRoleBound.class)</tspan></text>
<text x="24" y="1222" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 326- public HealthIndicator redisRequired(RedisRuntimeOwner owner, RedisSdkSettings settings) {</tspan></text>
<text x="24" y="1244" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># build.gradle 이 등록한 간선 셋과 앞 주석</tspan></text>
<text x="24" y="1288" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // Registered edges the semantic port adapters need. The SDK itself imports nothing from them</tspan></text>
<text x="24" y="1310" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // today (0 imports across main source) — the semantic cache/session/idempotency/rate-limit</tspan></text>
<text x="24" y="1332" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // adapters that did were removed and are restored by Phase E of</tspan></text>
<text x="24" y="1354" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // docs/superpowers/plans/2026-08-10-redis-optionality-and-composition.md. They stay declared</tspan></text>
<text x="24" y="1376" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // because that restoration is the module&#x27;s stated responsibility, not because anything here</tspan></text>
<text x="24" y="1398" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // compiles against them.</tspan></text>
<text x="24" y="1420" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> implementation project(&#x27;:application-core&#x27;)</tspan></text>
<text x="24" y="1442" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> implementation project(&#x27;:shared-contract&#x27;)</tspan></text>
<text x="24" y="1464" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> implementation project(&#x27;:adapter:outbound:support&#x27;)</tspan></text>
<text x="24" y="1486" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1508" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 간선들을 임포트하는 파일</tspan></text>
<text x="24" y="1530" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> dev.caskeleton.application 7 파일</tspan></text>
<text x="24" y="1552" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> dev.caskeleton.shared 3 파일</tspan></text>
<text x="24" y="1574" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> dev.caskeleton.adapter.outbound.support 0 파일</tspan></text>
<text x="24" y="1596" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 주석의 주어인 sdk 패키지만 보면 : 0</tspan></text>
<text x="24" y="1618" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1640" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 스무 줄 뒤의 별개 주석</tspan></text>
<text x="24" y="1662" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // Deliberately absent:</tspan></text>
<text x="24" y="1684" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // org.springframework.data:spring-data-redis — the SDK owns its own typed API and command</tspan></text>
<text x="24" y="1706" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // policy on purpose; routing through Spring Data would reintroduce the untyped, unguarded</tspan></text>
<text x="24" y="1728" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // command surface the catalog exists to prevent. Zero imports.</tspan></text>
<text x="24" y="1750" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // io.micrometer:micrometer-core — observation leaves this leaf as RedisObservation through a</tspan></text>
<text x="24" y="1772" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // Consumer sink; binding it to a meter registry belongs to the composition root, not here.</tspan></text>
<text x="24" y="1794" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> // Zero imports.</tspan></text>
</svg>