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>
93 lines
15 KiB
XML
93 lines
15 KiB
XML
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1580" viewBox="0 0 1200 1580" 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="1578" 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">$ echo '# 릴리스 레지스트리가 이 게이트에 붙인 생산자 태스크'
|
|
grep -n -A3 '"collection-fetch-pagination"' src/config/jpa</tspan><tspan x="24" dy="22">/release-registry.json
|
|
echo -n '# blocking 이 참인 게이트 / 거짓인 게이트: '
|
|
printf '%s / %s\n' "$(grep -c '"blocking"</tspan><tspan x="24" dy="22">: true' src/config/jpa/release-registry.json)" \
|
|
"$(grep -c '"blocking": false' src/config/jpa/releas</tspan><tspan x="24" dy="22">e-registry.json)"
|
|
M=src/adapter/outbound/persistence-jpa/src/testkit/java/dev/caskeleton/adapter/outbound/persistence/te</tspan><tspan x="24" dy="22">stkit/release/JpaReleaseManifest.java
|
|
echo -n '# 매니페스트 파서가 blocking 을 읽는 줄: '
|
|
grep -c 'blocking' "$M"
|
|
echo</tspan><tspan x="24" dy="22"> -n '# 게이트 검증기가 blocking 을 읽는 줄: '
|
|
sed -n '1046,1090p' src/build.gradle | grep -c 'blocking'
|
|
|
|
echo
|
|
echo '# </tspan><tspan x="24" dy="22">그 태스크와 게이트 테스트의 태그'
|
|
grep -n -A3 "registerJpaPlatformLane(" src/adapter/outbound/persistence-jpa/build.grad</tspan><tspan x="24" dy="22">le | grep -E "jpaPlatform|'jpa-"
|
|
grep -n 'includeTags tag' src/adapter/outbound/persistence-jpa/build.gradle
|
|
grep -n '@T</tspan><tspan x="24" dy="22">ag' src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/</tspan><tspan x="24" dy="22">platform/HibernateCollectionFetchPaginationContractTest.java
|
|
echo -n '# jpa-queryplan 태그를 단 클래스: '
|
|
grep -rl '@T</tspan><tspan x="24" dy="22">ag("jpa-queryplan")' --include=*.java --exclude-dir=build src | sed 's|.*/||'
|
|
|
|
echo
|
|
echo '# 그 불일치를 지나가게 하는 검</tspan><tspan x="24" dy="22">증기가 무엇을 보는가'
|
|
sed -n '1058,1078p' src/build.gradle | grep 'violations <<' | sed 's/^ */ /'
|
|
echo '# 레지스트리</tspan><tspan x="24" dy="22">를 읽는 단위 테스트의 게이트 단언'
|
|
grep -n -A3 'for (String gate : JpaReleaseGate.required())' \
|
|
src/adapter/outbound/</tspan><tspan x="24" dy="22">persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/testkit/release/../JpaReleaseManifestTest.java</tspan><tspan x="24" dy="22"> 2>/dev/null \
|
|
|| grep -n -A3 'for (String gate : JpaReleaseGate.required())' \
|
|
src/adapter/outbound/persistence-jpa</tspan><tspan x="24" dy="22">/src/test/java/dev/caskeleton/adapter/outbound/persistence/testkit/JpaReleaseManifestTest.java
|
|
|
|
echo
|
|
echo '# 그래도 그 </tspan><tspan x="24" dy="22">테스트는 다른 레인으로 돈다'
|
|
grep -rn 'jpaPlatformContractTest' .github/workflows/*.yml | sed 's|.*/||' | cut -c1-90
|
|
gre</tspan><tspan x="24" dy="22">p -n 'dependsOn jpaPlatformContractTest' src/adapter/outbound/persistence-jpa/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-02T04:06:45+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">56: "name": "collection-fetch-pagination",</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">57- "task": ":adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest",</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">58- "blocking": true</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">59- }</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"># blocking 이 참인 게이트 / 거짓인 게이트: 6 / 0</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"># 매니페스트 파서가 blocking 을 읽는 줄: 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"># 게이트 검증기가 blocking 을 읽는 줄: 0</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"></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"># 그 태스크와 게이트 테스트의 태그</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">250:def jpaPlatformContractTest = registerJpaPlatformLane(</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">251- 'jpaPlatformContractTest',</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">252- 'jpa-contract',</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">254:def jpaPlatformMigrationTest = registerJpaPlatformLane(</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">255- 'jpaPlatformMigrationTest',</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">256- 'jpa-migration',</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">258:def jpaPlatformFailureTest = registerJpaPlatformLane(</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">259- 'jpaPlatformFailureTest',</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">260- 'jpa-failure',</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">262:def jpaPlatformQueryPlanTest = registerJpaPlatformLane(</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">263- 'jpaPlatformQueryPlanTest',</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">264- 'jpa-queryplan',</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">266:def jpaPlatformSecurityTest = registerJpaPlatformLane(</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">267- 'jpaPlatformSecurityTest',</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">268- 'jpa-security',</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">238: includeTags tag</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">26:@Tag("jpa-contract")</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"># jpa-queryplan 태그를 단 클래스: PostgreSqlQueryPlanContractTest.java</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"></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"># 그 불일치를 지나가게 하는 검증기가 무엇을 보는가</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"> violations << "${name}: gate task must be an absolute Gradle path, was '${path}'"</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"> violations << "${name}: no project at '${projectPath}' for gate task '${path}'"</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"> violations << "${name}: no task '${taskName}' in '${projectPath}'"</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"> violations << "${name}: '${path}' is not a Test task, so it produces no JUnit evidence"</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"># 레지스트리를 읽는 단위 테스트의 게이트 단언</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">45: for (String gate : JpaReleaseGate.required()) {</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">46- assertThat(manifest.taskFor(gate))</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">47- .as("gate %s must name the task that produces its evidence", gate)</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">48- .startsWith(":");</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"></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"># 그래도 그 테스트는 다른 레인으로 돈다</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">jpa-nightly.yml:49: :adapter:outbound:persistence-jpa:jpaPlatformContractTest</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">jpa-pr.yml:86: :adapter:outbound:persistence-jpa:jpaPlatformContractTest</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">gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest --console=plain</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">gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest :adapter:outbound:persis</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">304: dependsOn jpaPlatformContractTest</tspan></text>
|
|
</svg>
|