chore: 이전 세션이 남긴 변경을 커밋한다

이번 파이프라인 작업과 무관하게 작업 트리에 남아 있던 것을 그대로 올린다.
사용자가 「전부 커밋」으로 정했고, 이번 작업과 섞이지 않게 커밋만 나눴다.

대부분은 clean-architecture-backend-template 의 그림 정본 재배치다 —
final/assets/diagrams/<이름>/ 에 있던 것이 CLAUDE.md 가 적은 배치인
final/assets/<이름>/ 로 옮겨졌고 .techviz/<이름>/ 이 함께 들어왔다.
삽입 줄의 대부분(3.15M)이 그 .techviz context.json 이다.

그 밖에 ca-tmpl·document-haness 의 정리, .claude/agents/ 열한 개,
writing-practitioner-guides 스킬, .playwright-mcp 세션 산출물,
scripts/check-ssot-facts.py 와 그 시험이 들어 있다.

이 커밋의 내용은 내가 만든 것이 아니라 이전 세션이 남긴 것이고 검증하지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-17 11:02:02 +09:00
co-authored by Claude Opus 5
parent 2109f726fe
commit ab59130196
1524 changed files with 3160026 additions and 8369 deletions
@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="366" viewBox="0 0 880 366" role="img">
<title>요청 예산에서 파생되는 계층</title>
<desc>배선된 요청 데드라인 상자에서 나가는 화살표가 없다. 네 파생 계층은 메서드로만 존재하고 프로덕션 호출자가 0 이라, 파생 전이 자체가 일어나지 않는다.</desc>
<rect x="0" y="0" width="880" height="366" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">요청 예산에서 파생되는 계층</text>
<rect x="28" y="181.0" width="262" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="159.0" y="215.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">요청 데드라인 — 배선됨</text>
<rect x="458" y="76" width="394" height="268" rx="14" fill="none" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="474" y="100" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">파생 없음 — 프로덕션 호출자 0</text>
<rect x="474" y="114" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="144.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">리졸버 예산</text>
<rect x="474" y="172" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="202.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">DataLoader 배치 타임아웃</text>
<rect x="474" y="230" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="260.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">다운스트림 데드라인</text>
<rect x="474" y="288" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="318.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">구독 데드라인</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,24 @@
# 요청 예산에서 파생되는 계층
## Alternative text
요청 데드라인에서 네 파생 계층으로 가는 화살표가 모두 점선이다.
## Long description
SSOT §10 은 이 리프가 무엇을 하는 코드인지를 센다. 배선된 요청 데드라인에서 나가는 화살표가 없고 리졸버 예산·DataLoader 배치 타임아웃·다운스트림 데드라인·구독 데드라인은 메서드로만 존재한다.
## Elements and evidence
- **요청 데드라인** (service): No additional description. Evidence: L18990L19005.
- **리졸버 예산** (service): No additional description. Evidence: L18990L19005.
- **DataLoader 배치 타임아웃** (service): No additional description. Evidence: L18990L19005.
- **다운스트림 데드라인** (service): No additional description. Evidence: L18990L19005.
- **구독 데드라인** (service): No additional description. Evidence: L18990L19007.
## Relationships
- **요청 데드라인 → 리졸버 예산:** 파생 없음. Evidence: L18990L19005.
- **요청 데드라인 → DataLoader 배치 타임아웃:** 파생 없음. Evidence: L18990L19005.
- **요청 데드라인 → 다운스트림 데드라인:** 파생 없음. Evidence: L18990L19005.
- **요청 데드라인 → 구독 데드라인:** 파생 없음. Evidence: L18990L19007.
@@ -0,0 +1,22 @@
# 요청 예산에서 파생되는 계층
# Question: 요청 데드라인은 아래 계층으로 내려가는가?
direction: right
n0: "요청 데드라인" {
shape: rectangle
}
n1: "리졸버 예산" {
shape: rectangle
}
n2: "DataLoader 배치 타임아웃" {
shape: rectangle
}
n3: "다운스트림 데드라인" {
shape: rectangle
}
n4: "구독 데드라인" {
shape: rectangle
}
n0 -> n1: "파생 없음"
n0 -> n2: "파생 없음"
n0 -> n3: "파생 없음"
n0 -> n4: "파생 없음"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-graphql-c04",
"spec_version": "1.1",
"spec_sha256": "4867c622e3c753f2aeeb689498152cc7f97cff9485ffc73deddef70784b3cb85",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 18990,
"line": 18990
}
},
"outputs": [
"adapter-inbound-graphql-c04.svg",
"adapter-inbound-graphql-c04.d2",
"adapter-inbound-graphql-c04.alt.md"
],
"lint_issue_count": 3,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="577" viewBox="0 0 680 577" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">요청 예산에서 파생되는 계층</title>
<desc id="diagram-description">SSOT §10 은 이 리프가 무엇을 하는 코드인지를 센다. 배선된 요청 데드라인에서 나가는 화살표가 없고 리졸버 예산·DataLoader 배치 타임아웃·다운스트림 데드라인·구독 데드라인은 메서드로만 존재한다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-graphql-c04&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:18990,&quot;line&quot;:18990}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="577" />
<polyline class="edge kind-request style-dashed emphasis-primary" points="220.0,303.3 302.5,303.3 302.5,364.0 385.0,364.0" data-evidence="18990-19005" />
<rect class="edge-label-bg" x="300.8" y="319.7" width="51.5" height="22" rx="3" />
<text class="edge-label" x="326.5" y="334.7">파생 없음</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,288.7 300.0,288.7 300.0,228.0 380.0,228.0" data-evidence="18990-19005" />
<rect class="edge-label-bg" x="298.2" y="244.3" width="51.5" height="22" rx="3" />
<text class="edge-label" x="324.0" y="259.3">파생 없음</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,274.0 302.5,274.0 302.5,92.0 385.0,92.0" data-evidence="18990-19005" />
<rect class="edge-label-bg" x="300.8" y="169.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="326.5" y="184.0">파생 없음</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,318.0 302.5,318.0 302.5,500.0 385.0,500.0" data-evidence="18990-19007" />
<rect class="edge-label-bg" x="300.8" y="395.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="326.5" y="410.0">파생 없음</text>
<g id="node-budget">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="18990-19005" x="70.0" y="260.5" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="145.0" y="287.5">요청 데드라인</text>
<line class="node-detail-divider" x1="84.0" y1="308.5" x2="206.0" y2="308.5" />
<text class="node-detail" x="86.0" y="325.5">배선됨</text>
</g>
<g id="node-down">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="18990-19005" x="385.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="460.0" y="90.0">다운스트림 데드라인</text>
</g>
<g id="node-loader">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="18990-19005" x="380.0" y="196.0" width="160.0" height="64.0" rx="7" />
<text class="node-label" x="460.0" y="226.0">DataLoader 배치 타임아웃</text>
</g>
<g id="node-resolver">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="18990-19005" x="385.0" y="332.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="460.0" y="362.0">리졸버 예산</text>
</g>
<g id="node-sub">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="18990-19007" x="385.0" y="468.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="460.0" y="498.0">구독 데드라인</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -1,15 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="230" viewBox="0 0 880 230" role="img">
<title>응답을 실제로 만드는 쪽</title>
<desc>상태 코드와 미디어 타입 규칙을 담은 http 패키지가 조립되지 않고, 실제 응답은 프레임워크가 만든다. 노출이 아니라 통제권의 문제다.</desc>
<rect x="0" y="0" width="880" height="230" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">응답을 실제로 만드는 쪽</text>
<rect x="28" y="66" width="560" height="134" rx="14" fill="none" stroke="#374151" stroke-width="1.6"/>
<text x="44" y="88" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">Spring GraphQL</text>
<rect x="44.0" y="100" width="255.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="171.5" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">상태 코드 규칙</text>
<rect x="317.0" y="100" width="255.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="444.5" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">미디어 타입 협상</text>
<rect x="624" y="100" width="228" height="54" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="738.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">http 패키지 — 미배선</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1,22 @@
# 응답을 실제로 만드는 쪽
## Alternative text
GraphQL 요청에서 Spring GraphQL 로 화살표가 가고 거기서 응답이 나온다. http 패키지로 가는 화살표는 점선이다.
## Long description
SSOT §20.1b 는 HTTP 전송 계약 계층이 미배선이고 실제 전송은 프레임워크가 정한다고 적는다. 노출이 아니라 통제권의 문제다.
## Elements and evidence
- **GraphQL 요청** (service): No additional description. Evidence: L19344L19350.
- **Spring GraphQL** (service): No additional description. Evidence: L19344L19355.
- **응답** (service): No additional description. Evidence: L19344L19355.
- **http 패키지** (service): No additional description. Evidence: L19344L19360.
## Relationships
- **GraphQL 요청 → Spring GraphQL:** 처리. Evidence: L19344L19350.
- **Spring GraphQL → 응답:** 응답 생성. Evidence: L19344L19355.
- **Spring GraphQL → http 패키지:** 미도달. Evidence: L19344L19360.
@@ -0,0 +1,18 @@
# 응답을 실제로 만드는 쪽
# Question: GraphQL 응답의 상태 코드와 미디어 타입은 누가 정하는가?
direction: right
n0: "GraphQL 요청" {
shape: rectangle
}
n1: "Spring GraphQL" {
shape: rectangle
}
n2: "응답" {
shape: rectangle
}
n3: "http 패키지" {
shape: rectangle
}
n0 -> n1: "처리"
n1 -> n2: "응답 생성"
n1 -> n3: "미도달"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-graphql-c09",
"spec_version": "1.1",
"spec_sha256": "fe8f8b7c0590e25d9463a3bc9610b7d037aa093401356acb3f60a01e8af26988",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 19344,
"line": 19344
}
},
"outputs": [
"adapter-inbound-graphql-c09.svg",
"adapter-inbound-graphql-c09.d2",
"adapter-inbound-graphql-c09.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="885" height="346" viewBox="0 0 885 346" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">응답을 실제로 만드는 쪽</title>
<desc id="diagram-description">SSOT §20.1b 는 HTTP 전송 계약 계층이 미배선이고 실제 전송은 프레임워크가 정한다고 적는다. 노출이 아니라 통제권의 문제다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-graphql-c09&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:19344,&quot;line&quot;:19344}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="885" height="346" />
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,180.5 300.0,180.5 300.0,180.5 380.0,180.5" data-evidence="19344-19350" />
<rect class="edge-label-bg" x="278.0" y="138.5" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="153.5">처리</text>
<polyline class="edge kind-request style-solid emphasis-primary" points="530.0,189.5 610.0,189.5 610.0,269.0 690.0,269.0" data-evidence="19344-19355" />
<rect class="edge-label-bg" x="608.2" y="215.2" width="51.5" height="22" rx="3" />
<text class="edge-label" x="634.0" y="230.2">응답 생성</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="530.0,171.5 610.0,171.5 610.0,112.5 690.0,112.5" data-evidence="19344-19360" />
<rect class="edge-label-bg" x="612.0" y="128.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="634.0" y="143.0">미도달</text>
<g id="node-req">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="19344-19350" x="70.0" y="148.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="178.5">GraphQL 요청</text>
</g>
<g id="node-spring">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="19344-19355" x="380.0" y="148.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="178.5">Spring GraphQL</text>
</g>
<g id="node-http">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="19344-19360" x="690.0" y="60.0" width="150.0" height="105.0" rx="7" />
<text class="node-label" x="765.0" y="87.0">http 패키지</text>
<line class="node-detail-divider" x1="704.0" y1="108.0" x2="826.0" y2="108.0" />
<text class="node-detail" x="706.0" y="125.0">상태 코드 규칙</text>
<text class="node-detail" x="706.0" y="141.0">미디어 타입 협상</text>
<text class="node-detail" x="706.0" y="157.0">미배선</text>
</g>
<g id="node-resp">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="19344-19355" x="690.0" y="237.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="267.0">응답</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

@@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="394" viewBox="0 0 880 394" role="img">
<title>패키지별 도달 경로</title>
<desc>자동설정과 배치 로더 등록기에서 나가는 경로는 dataloader 에만 닿는다. fetch·pagination·mutation 으로는 화살표가 없다 — 어떤 배선 경로에도 놓여 있지 않기 때문이다.</desc>
<rect x="0" y="0" width="880" height="394" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">패키지별 도달 경로</text>
<rect x="28" y="74.0" width="262" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="159.0" y="108.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">자동설정 · 배치 로더 등록기</text>
<rect x="474" y="76" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="108.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">dataloader</text>
<line x1="296" y1="103.0" x2="468" y2="103.0" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="95.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">registrar 경유</text>
<rect x="458" y="162" width="394" height="210" rx="14" fill="none" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="474" y="186" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">배선 경로 없음 — 화살표 없음</text>
<rect x="474" y="200" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="230.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">fetch</text>
<rect x="474" y="258" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="288.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">pagination</text>
<rect x="474" y="316" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="346.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">mutation</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,22 @@
# 패키지별 도달 경로
## Alternative text
자동설정에서 registrar 를 거쳐 dataloader 로 화살표가 가고 fetch 와 pagination 과 mutation 으로는 점선이 간다.
## Long description
SSOT 는 자동설정과 배치 로더 등록기에서 나가는 배선 경로가 dataloader 에만 닿는다고 적는다.
## Elements and evidence
- **자동설정 · 배치 로더 등록기** (service): No additional description. Evidence: L19824L20000.
- **dataloader** (service): No additional description. Evidence: L19824L20000.
- **fetch** (service): No additional description. Evidence: L19824L20024.
- **pagination · mutation** (service): No additional description. Evidence: L19824L20024.
## Relationships
- **자동설정 · 배치 로더 등록기 → dataloader:** registrar 경유. Evidence: L19824L20000.
- **자동설정 · 배치 로더 등록기 → fetch:** 배선 없음. Evidence: L19824L20024.
- **자동설정 · 배치 로더 등록기 → pagination · mutation:** 배선 없음. Evidence: L19824L20024.
@@ -0,0 +1,18 @@
# 패키지별 도달 경로
# Question: 자동설정에서 어느 패키지까지 배선이 닿는가?
direction: right
n0: "자동설정 · 배치 로더 등록기" {
shape: rectangle
}
n1: "dataloader" {
shape: rectangle
}
n2: "fetch" {
shape: rectangle
}
n3: "pagination · mutation" {
shape: rectangle
}
n0 -> n1: "registrar 경유"
n0 -> n2: "배선 없음"
n0 -> n3: "배선 없음"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-graphql-c10",
"spec_version": "1.1",
"spec_sha256": "868f272be1b8fa4611d281d0a1f6e1b712f1d218bf31e05733284a230bd47b3f",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 19824,
"line": 19824
}
},
"outputs": [
"adapter-inbound-graphql-c10.svg",
"adapter-inbound-graphql-c10.d2",
"adapter-inbound-graphql-c10.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="441" viewBox="0 0 680 441" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">패키지별 도달 경로</title>
<desc id="diagram-description">SSOT 는 자동설정과 배치 로더 등록기에서 나가는 배선 경로가 dataloader 에만 닿는다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-graphql-c10&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:19824,&quot;line&quot;:19824}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="441" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,210.0 307.8,210.0 307.8,92.0 395.5,92.0" data-evidence="19824-20000" />
<rect class="edge-label-bg" x="282.6" y="137.0" width="98.4" height="22" rx="3" />
<text class="edge-label" x="331.8" y="152.0">registrar 경유</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,228.0 307.8,228.0 307.8,228.0 395.5,228.0" data-evidence="19824-20024" />
<rect class="edge-label-bg" x="282.0" y="186.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="307.8" y="201.0">배선 없음</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,246.0 300.0,246.0 300.0,364.0 380.0,364.0" data-evidence="19824-20024" />
<rect class="edge-label-bg" x="298.2" y="291.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="324.0" y="306.0">배선 없음</text>
<g id="node-auto">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="19824-20000" x="70.0" y="196.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="226.0">자동설정 · 배치 로더 등록기</text>
</g>
<g id="node-dl">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="19824-20000" x="395.5" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="470.5" y="90.0">dataloader</text>
</g>
<g id="node-fetch">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="19824-20024" x="395.5" y="196.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="470.5" y="226.0">fetch</text>
</g>
<g id="node-page">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="19824-20024" x="380.0" y="332.0" width="181.0" height="64.0" rx="7" />
<text class="node-label" x="470.5" y="362.0">pagination · mutation</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -1,15 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="366" viewBox="0 0 880 366" role="img">
<title>인터셉터 순서</title>
<desc>인증과 예외 처리의 순서가 곧 계약이다. 예외 처리가 인증보다 앞서면 인증 실패가 업무 오류로 보고된다.</desc>
<rect x="0" y="0" width="880" height="366" fill="#ffffff"/>
<text x="28" y="42" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">인터셉터 순서</text>
<defs><marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="h" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<rect x="28" y="74" width="824" height="64" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="111" text-anchor="middle" font-family="system-ui,sans-serif" font-size="15" fill="#111827">인증</text>
<rect x="28" y="164" width="824" height="64" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="201" text-anchor="middle" font-family="system-ui,sans-serif" font-size="15" fill="#111827">업무 처리</text>
<rect x="28" y="254" width="824" height="64" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="291" text-anchor="middle" font-family="system-ui,sans-serif" font-size="15" fill="#111827">예외 변환</text>
<line x1="440" y1="140" x2="440" y2="162" stroke="#374151" stroke-width="1.6" marker-end="url(#a)"/>
<line x1="440" y1="230" x2="440" y2="252" stroke="#374151" stroke-width="1.6" marker-end="url(#a)"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,20 @@
# 인터셉터 순서
## Alternative text
요청이 인증과 업무 처리를 지나 예외 변환으로 이어지는 순서.
## Long description
SSOT 는 인증과 예외 처리의 순서가 곧 계약이고 예외 처리가 인증보다 앞서면 인증 실패가 업무 오류로 보고된다고 적는다.
## Elements and evidence
- **인증** (service): No additional description. Evidence: L494L512.
- **업무 처리** (service): No additional description. Evidence: L494L512.
- **예외 변환** (service): No additional description. Evidence: L494L517.
## Relationships
- **인증 → 업무 처리:** 통과. Evidence: L494L512.
- **업무 처리 → 예외 변환:** 오류 전달. Evidence: L494L517.
@@ -0,0 +1,14 @@
# 인터셉터 순서
# Question: 인증과 예외 변환은 어느 순서인가?
direction: right
n0: "인증" {
shape: rectangle
}
n1: "업무 처리" {
shape: rectangle
}
n2: "예외 변환" {
shape: rectangle
}
n0 -> n1: "통과"
n1 -> n2: "오류 전달"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-grpc-c01",
"spec_version": "1.1",
"spec_sha256": "4183ef3251a81fc8a80c8f56e9fc8e22c5e8ae48e152af2ad9c6886742030c89",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 504,
"line": 504
}
},
"outputs": [
"adapter-inbound-grpc-c01.svg",
"adapter-inbound-grpc-c01.d2",
"adapter-inbound-grpc-c01.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="885" height="300" viewBox="0 0 885 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">인터셉터 순서</title>
<desc id="diagram-description">SSOT 는 인증과 예외 처리의 순서가 곧 계약이고 예외 처리가 인증보다 앞서면 인증 실패가 업무 오류로 보고된다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-grpc-c01&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:504,&quot;line&quot;:504}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="885" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="494-512" />
<rect class="edge-label-bg" x="278.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="494-517" />
<rect class="edge-label-bg" x="584.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">오류 전달</text>
<g id="node-auth">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="494-512" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">인증</text>
</g>
<g id="node-biz">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="494-512" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">업무 처리</text>
</g>
<g id="node-err">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="494-517" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">예외 변환</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="248" viewBox="0 0 880 248" role="img">
<title>타입이 고르는 자동설정</title>
<desc>두 자동설정은 모두 기본 켜짐이지만 활성화를 가르는 것은 프로퍼티가 아니라 애플리케이션 타입이다. 두 아티팩트가 클래스패스에 함께 있어도 한쪽만 활성화된다.</desc>
<rect x="0" y="0" width="880" height="248" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">타입이 고르는 자동설정</text>
<rect x="28" y="109" width="262" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="159.0" y="143.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">웹 애플리케이션 타입</text>
<rect x="474" y="76" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="108.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">MVC 자동설정</text>
<line x1="296" y1="138" x2="468" y2="103" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="70" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">SERVLET</text>
<rect x="474" y="142" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="174.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">WebFlux 자동설정</text>
<line x1="296" y1="138" x2="468" y2="169" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="162" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">REACTIVE</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,20 @@
# 타입이 고르는 자동설정
## Alternative text
웹 애플리케이션 타입이 SERVLET 이면 MVC 자동설정으로 REACTIVE 면 WebFlux 자동설정으로 간다.
## Long description
SSOT 는 두 자동설정이 모두 기본 켜짐이고 활성화를 가르는 것이 프로퍼티가 아니라 애플리케이션 타입이라고 적는다.
## Elements and evidence
- **웹 애플리케이션 타입** (service): No additional description. Evidence: L17783L17815.
- **MVC 자동설정** (service): No additional description. Evidence: L17783L17815.
- **WebFlux 자동설정** (service): No additional description. Evidence: L17783L17819.
## Relationships
- **웹 애플리케이션 타입 → MVC 자동설정:** SERVLET. Evidence: L17783L17815.
- **웹 애플리케이션 타입 → WebFlux 자동설정:** REACTIVE. Evidence: L17783L17819.
@@ -0,0 +1,14 @@
# 타입이 고르는 자동설정
# Question: 두 자동설정 중 무엇이 켜지는가?
direction: right
n0: "웹 애플리케이션 타입" {
shape: rectangle
}
n1: "MVC 자동설정" {
shape: rectangle
}
n2: "WebFlux 자동설정" {
shape: rectangle
}
n0 -> n1: "SERVLET"
n0 -> n2: "REACTIVE"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-web-c01",
"spec_version": "1.1",
"spec_sha256": "19ab77244a2e9bb7a5f7dc6489594ff9d1031b60279070aa997b87f393df5337",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 17783,
"line": 17783
}
},
"outputs": [
"adapter-inbound-web-c01.svg",
"adapter-inbound-web-c01.d2",
"adapter-inbound-web-c01.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="305" viewBox="0 0 680 305" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">타입이 고르는 자동설정</title>
<desc id="diagram-description">SSOT 는 두 자동설정이 모두 기본 켜짐이고 활성화를 가르는 것이 프로퍼티가 아니라 애플리케이션 타입이라고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-web-c01&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:17783,&quot;line&quot;:17783}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="305" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,169.0 300.0,169.0 300.0,228.0 380.0,228.0" data-evidence="17783-17815" />
<rect class="edge-label-bg" x="291.6" y="184.5" width="64.9" height="22" rx="3" />
<text class="edge-label" x="324.0" y="199.5">SERVLET</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,151.0 300.0,151.0 300.0,92.0 380.0,92.0" data-evidence="17783-17819" />
<rect class="edge-label-bg" x="288.2" y="107.5" width="71.6" height="22" rx="3" />
<text class="edge-label" x="324.0" y="122.5">REACTIVE</text>
<g id="node-type">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="17783-17815" x="70.0" y="128.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="158.0">웹 애플리케이션 타입</text>
</g>
<g id="node-flux">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="17783-17819" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">WebFlux 자동설정</text>
</g>
<g id="node-mvc">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="17783-17815" x="380.0" y="196.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="226.0">MVC 자동설정</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="230" viewBox="0 0 880 230" role="img">
<title>신원 모델이 닿는 범위</title>
<desc>security 패키지의 타입들은 서로를 부르지만 패키지 바깥에서 들어오는 프로덕션 호출자가 없다. 교차 테넌트 가드도 그 안에만 있다.</desc>
<rect x="0" y="0" width="880" height="230" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">신원 모델이 닿는 범위</text>
<rect x="28" y="66" width="560" height="134" rx="14" fill="none" stroke="#374151" stroke-width="1.6"/>
<text x="44" y="88" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">security 패키지</text>
<rect x="44.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="126.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">WebSecurityContextBridge</text>
<rect x="226.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="308.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">AuthenticationView</text>
<rect x="408.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="490.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">rejectTenantInput</text>
<rect x="624" y="100" width="228" height="54" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="738.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">바깥 프로덕션 호출자 없음</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,22 @@
# 신원 모델이 닿는 범위
## Alternative text
바깥 프로덕션 호출자에서 security 패키지로 가는 화살표는 점선이고, 패키지 안에서는 브리지와 뷰와 가드가 이어진다.
## Long description
SSOT §11.1 은 신원 모델의 프로덕션 참조 수를 센다. 타입들은 서로를 부르지만 패키지 바깥에서 들어오는 프로덕션 호출자가 없고 교차 테넌트 가드도 그 안에만 있다.
## Elements and evidence
- **바깥 프로덕션 호출자** (service): No additional description. Evidence: L16831L16850.
- **WebSecurityContextBridge** (service): No additional description. Evidence: L16831L16850.
- **AuthenticationView** (service): No additional description. Evidence: L16831L16850.
- **rejectTenantInput** (service): No additional description. Evidence: L16831L16850.
## Relationships
- **바깥 프로덕션 호출자 → WebSecurityContextBridge:** 없음. Evidence: L16831L16850.
- **WebSecurityContextBridge → AuthenticationView:** 신원 노출. Evidence: L16831L16850.
- **AuthenticationView → rejectTenantInput:** 교차 테넌트 검사. Evidence: L16831L16850.
@@ -0,0 +1,18 @@
# 신원 모델이 닿는 범위
# Question: security 패키지의 타입을 누가 부르는가?
direction: right
n0: "바깥 프로덕션 호출자" {
shape: rectangle
}
n1: "WebSecurityContextBridge" {
shape: rectangle
}
n2: "AuthenticationView" {
shape: rectangle
}
n3: "rejectTenantInput" {
shape: rectangle
}
n0 -> n1: "없음"
n1 -> n2: "신원 노출"
n2 -> n3: "교차 테넌트 검사"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-web-c03",
"spec_version": "1.1",
"spec_sha256": "23734fb4dfa87579eb163c92f7dbca1908c9ec608ecd05a48f88677ca5657b17",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 16831,
"line": 16831
}
},
"outputs": [
"adapter-inbound-web-c03.svg",
"adapter-inbound-web-c03.d2",
"adapter-inbound-web-c03.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1260" height="300" viewBox="0 0 1260 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">신원 모델이 닿는 범위</title>
<desc id="diagram-description">SSOT §11.1 은 신원 모델의 프로덕션 참조 수를 센다. 타입들은 서로를 부르지만 패키지 바깥에서 들어오는 프로덕션 호출자가 없고 교차 테넌트 가드도 그 안에만 있다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-web-c03&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:16831,&quot;line&quot;:16831}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1260" height="300" />
<polyline class="edge kind-request style-dashed emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="16831-16850" />
<rect class="edge-label-bg" x="278.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">없음</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="582.0,92.0 662.0,92.0 662.0,92.0 742.0,92.0" data-evidence="16831-16850" />
<rect class="edge-label-bg" x="636.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="662.0" y="65.0">신원 노출</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="902.0,92.0 982.0,92.0 982.0,92.0 1062.0,92.0" data-evidence="16831-16850" />
<rect class="edge-label-bg" x="942.9" y="50.0" width="78.3" height="22" rx="3" />
<text class="edge-label" x="982.0" y="65.0">교차 테넌트 검사</text>
<g id="node-outside">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="16831-16850" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">바깥 프로덕션 호출자</text>
</g>
<g id="node-bridge">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="16831-16850" x="380.0" y="60.0" width="202.0" height="64.0" rx="7" />
<text class="node-label" x="481.0" y="90.0">WebSecurityContextBridge</text>
</g>
<g id="node-view">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="16831-16850" x="742.0" y="60.0" width="160.0" height="64.0" rx="7" />
<text class="node-label" x="822.0" y="90.0">AuthenticationView</text>
</g>
<g id="node-guard">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="16831-16850" x="1062.0" y="60.0" width="153.0" height="64.0" rx="7" />
<text class="node-label" x="1138.5" y="90.0">rejectTenantInput</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>매처가 등록되는 순서</title>
<desc>Spring Security 는 먼저 일치한 매처가 이긴다. 공개 경로가 먼저 등록되므로 제한 경로 규칙보다 앞선다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">매처가 등록되는 순서</text>
<rect x="28.0" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="140.7" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">publicPaths permitAll</text>
<rect x="327.3" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">RestrictedPathRule</text>
<rect x="626.7" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="739.3" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">anyRequest authenticated</text>
<line x1="259.3" y1="133" x2="321.3" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="290.3" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">먼저 등록</text>
<line x1="558.7" y1="133" x2="620.7" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="589.7" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">다음 등록</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,22 @@
# 매처가 등록되는 순서
## Alternative text
요청이 publicPaths permitAll 을 먼저 만나고 다음에 RestrictedPathRule 과 anyRequest authenticated 를 만나는 순서.
## Long description
SSOT 는 Spring Security 에서 먼저 일치한 매처가 이기고 공개 경로가 먼저 등록되므로 제한 경로 규칙보다 앞선다고 적는다.
## Elements and evidence
- **요청** (service): No additional description. Evidence: L16987L16994.
- **publicPaths permitAll** (service): No additional description. Evidence: L16987L16994.
- **RestrictedPathRule** (service): No additional description. Evidence: L16987L16994.
- **anyRequest authenticated** (service): No additional description. Evidence: L16987L16996.
## Relationships
- **요청 → publicPaths permitAll:** 먼저 등록. Evidence: L16987L16994.
- **publicPaths permitAll → RestrictedPathRule:** 다음 등록. Evidence: L16987L16994.
- **RestrictedPathRule → anyRequest authenticated:** 마지막. Evidence: L16987L16996.
@@ -0,0 +1,18 @@
# 매처가 등록되는 순서
# Question: 보안 매처는 어떤 순서로 걸리는가?
direction: right
n0: "요청" {
shape: rectangle
}
n1: "publicPaths permitAll" {
shape: rectangle
}
n2: "RestrictedPathRule" {
shape: rectangle
}
n3: "anyRequest authenticated" {
shape: rectangle
}
n0 -> n1: "먼저 등록"
n1 -> n2: "다음 등록"
n2 -> n3: "마지막"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-web-c04",
"spec_version": "1.1",
"spec_sha256": "1b8a6c4a647f9918cb3c2a3d10effec8a79a0cefcaa38e05fe068b23c001cf06",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 16987,
"line": 16987
}
},
"outputs": [
"adapter-inbound-web-c04.svg",
"adapter-inbound-web-c04.d2",
"adapter-inbound-web-c04.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1236" height="300" viewBox="0 0 1236 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">매처가 등록되는 순서</title>
<desc id="diagram-description">SSOT 는 Spring Security 에서 먼저 일치한 매처가 이기고 공개 경로가 먼저 등록되므로 제한 경로 규칙보다 앞선다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-web-c04&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:16987,&quot;line&quot;:16987}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1236" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,97.0 300.0,97.0 300.0,97.0 380.0,97.0" data-evidence="16987-16994" />
<rect class="edge-label-bg" x="274.2" y="55.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="300.0" y="70.0">먼저 등록</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="561.0,97.0 641.0,97.0 641.0,97.0 721.0,97.0" data-evidence="16987-16994" />
<rect class="edge-label-bg" x="615.2" y="55.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="641.0" y="70.0">다음 등록</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="881.0,97.0 961.0,97.0 961.0,97.0 1041.0,97.0" data-evidence="16987-16996" />
<rect class="edge-label-bg" x="939.0" y="55.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="961.0" y="70.0">마지막</text>
<g id="node-req">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="16987-16994" x="70.0" y="65.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="95.0">요청</text>
</g>
<g id="node-public">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="16987-16994" x="380.0" y="65.0" width="181.0" height="64.0" rx="7" />
<text class="node-label" x="470.5" y="95.0">publicPaths permitAll</text>
</g>
<g id="node-rule">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="16987-16994" x="721.0" y="65.0" width="160.0" height="64.0" rx="7" />
<text class="node-label" x="801.0" y="95.0">RestrictedPathRule</text>
</g>
<g id="node-any">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="16987-16996" x="1041.0" y="60.0" width="150.0" height="74.0" rx="7" />
<text class="node-label" x="1116.0" y="87.0">anyRequest</text>
<text class="node-label" x="1116.0" y="105.0">authenticated</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="344" viewBox="0 0 880 344" role="img">
<title>설정 접두사와 소비자</title>
<desc>두 접두사에서 그것을 읽는 Configuration 으로 화살표가 간다. 세 번째 접두사에는 화살표가 없다 — 그것을 읽어 조립하는 Configuration 이 저장소에 없기 때문이다.</desc>
<rect x="0" y="0" width="880" height="344" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">설정 접두사와 소비자</text>
<rect x="28" y="107.0" width="262" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="159.0" y="141.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">설정 접두사</text>
<rect x="474" y="76" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="108.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">WebSocketConfig</text>
<line x1="296" y1="136.0" x2="468" y2="103.0" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="95.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">ca-skeleton.websocket</text>
<rect x="474" y="142" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="174.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">SockJs · Stomp</text>
<line x1="296" y1="136.0" x2="468" y2="169.0" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="161.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">app.websocket-platform.advanced</text>
<rect x="458" y="228" width="394" height="94" rx="14" fill="none" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="474" y="252" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">읽는 Configuration 없음</text>
<rect x="474" y="266" width="362" height="50" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="655.0" y="296.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">backend.websocket</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,22 @@
# 설정 접두사와 소비자
## Alternative text
WebSocketConfig 가 두 설정 접두사를 읽고 세 번째 접두사는 읽는 곳이 없다.
## Long description
SSOT 는 두 접두사에서 그것을 읽는 Configuration 으로 화살표가 가고 세 번째 접두사에는 읽어 조립하는 곳이 없다고 적는다.
## Elements and evidence
- **WebSocketConfig** (service): No additional description. Evidence: L20052L20074.
- **ca-skeleton.websocket** (service): No additional description. Evidence: L20052L20074.
- **app.websocket-platform.advanced** (service): No additional description. Evidence: L20052L20074.
- **backend.websocket** (service): No additional description. Evidence: L20052L20078.
## Relationships
- **WebSocketConfig → ca-skeleton.websocket:** 읽음. Evidence: L20052L20074.
- **WebSocketConfig → app.websocket-platform.advanced:** 읽음. Evidence: L20052L20074.
- **WebSocketConfig → backend.websocket:** 읽지 않음. Evidence: L20052L20078.
@@ -0,0 +1,18 @@
# 설정 접두사와 소비자
# Question: 설정 접두사를 읽는 Configuration 이 있는가?
direction: right
n0: "WebSocketConfig" {
shape: rectangle
}
n1: "ca-skeleton.websocket" {
shape: rectangle
}
n2: "app.websocket-platform.advanced" {
shape: rectangle
}
n3: "backend.websocket" {
shape: rectangle
}
n0 -> n1: "읽음"
n0 -> n2: "읽음"
n0 -> n3: "읽지 않음"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-inbound-websocket-c01",
"spec_version": "1.1",
"spec_sha256": "f441562736fd3f0964bdbcb4dc31f2372f72a41b860c2cad1a8257545b995ba4",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 20052,
"line": 20052
}
},
"outputs": [
"adapter-inbound-websocket-c01.svg",
"adapter-inbound-websocket-c01.d2",
"adapter-inbound-websocket-c01.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "orchestrator-workers",
"reference_ids": [
"mission-workers"
],
"diagram_only": true
}
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="820" height="375" viewBox="0 0 820 375" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">설정 접두사와 소비자</title>
<desc id="diagram-description">SSOT 는 두 접두사에서 그것을 읽는 Configuration 으로 화살표가 가고 세 번째 접두사에는 읽어 조립하는 곳이 없다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-inbound-websocket-c01&quot;,&quot;profile&quot;:&quot;orchestrator-workers&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:20052,&quot;line&quot;:20052}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="820" height="375" />
<polyline class="edge kind-request style-solid emphasis-primary" points="297.0,109.0 297.0,157.0 157.5,157.0 157.5,205.0" data-evidence="20052-20074" />
<rect class="edge-label-bg" x="205.2" y="115.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="227.2" y="130.0">읽음</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="315.0,109.0 315.0,157.0 397.5,157.0 397.5,205.0" data-evidence="20052-20074" />
<rect class="edge-label-bg" x="334.2" y="115.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="356.2" y="130.0">읽음</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="333.0,109.0 333.0,157.0 665.0,157.0 665.0,205.0" data-evidence="20052-20078" />
<rect class="edge-label-bg" x="473.2" y="115.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="499.0" y="130.0">읽지 않음</text>
<g id="node-cfg">
<rect class="node-shape kind-service emphasis-primary role-orchestrator" data-evidence="20052-20074" x="55.0" y="35.0" width="520.0" height="74.0" rx="5" /><line class="controller-divider" x1="55.0" y1="60.2" x2="575.0" y2="60.2" /><line class="controller-divider" x1="55.0" y1="84.6" x2="575.0" y2="84.6" /><rect class="controller-led" x="68.0" y="43.6" width="8" height="8" rx="1" /><rect class="controller-led" x="68.0" y="68.0" width="8" height="8" rx="1" /><rect class="controller-led" x="68.0" y="92.4" width="8" height="8" rx="1" />
<text class="node-role" x="315.0" y="54.0">«orchestrator»</text>
<text class="node-label" x="315.0" y="81.0">WebSocketConfig</text>
<line class="node-detail-divider" x1="69.0" y1="102.0" x2="561.0" y2="102.0" />
<text class="node-detail" x="71.0" y="119.0">SockJs · Stomp</text>
</g>
<g id="node-skel">
<rect class="node-shape kind-service emphasis-normal role-worker" data-evidence="20052-20074" x="55.0" y="205.0" width="205.0" height="110.0" rx="7" />
<text class="node-role" x="157.5" y="224.0">«worker»</text>
<text class="node-label" x="157.5" y="258.0">ca-skeleton.websocket</text>
</g>
<g id="node-adv">
<rect class="node-shape kind-service emphasis-normal role-worker" data-evidence="20052-20074" x="295.0" y="205.0" width="205.0" height="110.0" rx="7" />
<text class="node-role" x="397.5" y="224.0">«worker»</text>
<text class="node-label" x="397.5" y="258.0">app.websocket-platform.advanced</text>
</g>
<g id="node-backend">
<rect class="node-shape kind-service emphasis-normal role-monitor" data-evidence="20052-20078" x="555.0" y="205.0" width="220.0" height="125.0" rx="7" />
<text class="node-label" x="665.0" y="257.5">backend.websocket</text>
<line class="node-detail-divider" x1="569.0" y1="278.5" x2="761.0" y2="278.5" />
<text class="node-detail" x="571.0" y="295.5">읽는 Configuration 없음</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>조립이 고정된 순서</title>
<desc>설정을 묶고 교차 필드 규칙을 돌린 뒤에야 클라이언트와 연결이 만들어진다. 검증이 bean factory 메서드 안에 있어서 검증되지 않은 설정을 쓰는 bean 이 생기지 않는다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">조립이 고정된 순서</text>
<rect x="28.0" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="140.7" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">설정 바인딩</text>
<rect x="327.3" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">교차 필드 검증</text>
<rect x="626.7" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="739.3" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">클라이언트 · 연결 생성</text>
<line x1="259.3" y1="133" x2="321.3" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="290.3" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">bound settings</text>
<line x1="558.7" y1="133" x2="620.7" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="589.7" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">검증 통과</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,20 @@
# 조립이 고정된 순서
## Alternative text
설정 바인딩에서 교차 필드 검증으로 그리고 클라이언트 생성으로 이어지는 순서.
## Long description
SSOT 는 검증이 bean factory 메서드 안에 있어 검증을 지나지 않고는 클라이언트가 만들어질 수 없다고 적는다.
## Elements and evidence
- **설정 바인딩** (service): No additional description. Evidence: L10437L10442.
- **교차 필드 검증** (service): No additional description. Evidence: L10437L10442.
- **클라이언트 · 연결 생성** (service): No additional description. Evidence: L10437L10444.
## Relationships
- **설정 바인딩 → 교차 필드 검증:** bound settings. Evidence: L10437L10442.
- **교차 필드 검증 → 클라이언트 · 연결 생성:** 검증 통과. Evidence: L10437L10444.
@@ -0,0 +1,14 @@
# 조립이 고정된 순서
# Question: 클라이언트는 무엇을 지난 뒤에 만들어지는가?
direction: right
n0: "설정 바인딩" {
shape: rectangle
}
n1: "교차 필드 검증" {
shape: rectangle
}
n2: "클라이언트 · 연결 생성" {
shape: rectangle
}
n0 -> n1: "bound settings"
n1 -> n2: "검증 통과"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c01",
"spec_version": "1.1",
"spec_sha256": "1aa67860649fcec548f8aa6d92d5b523df75da5849bde5f1539d0ce1746b131d",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 10437,
"line": 10437
}
},
"outputs": [
"adapter-outbound-cache-redis-c01.svg",
"adapter-outbound-cache-redis-c01.d2",
"adapter-outbound-cache-redis-c01.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="885" height="300" viewBox="0 0 885 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">조립이 고정된 순서</title>
<desc id="diagram-description">SSOT 는 검증이 bean factory 메서드 안에 있어 검증을 지나지 않고는 클라이언트가 만들어질 수 없다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c01&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:10437,&quot;line&quot;:10437}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="885" height="300" />
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="10437-10442" />
<rect class="edge-label-bg" x="244.1" y="50.0" width="111.8" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">bound settings</text>
<polyline class="edge kind-request style-solid emphasis-primary" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="10437-10444" />
<rect class="edge-label-bg" x="584.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">검증 통과</text>
<g id="node-bind">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="10437-10442" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">설정 바인딩</text>
</g>
<g id="node-check">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="10437-10442" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">교차 필드 검증</text>
</g>
<g id="node-client">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="10437-10444" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">클라이언트 · 연결 생성</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -1,15 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="276" viewBox="0 0 880 276" role="img">
<title>부재를 지키는 세 자리</title>
<desc>없는 명령이라는 선언이 API 문서에만 있지 않다. 구현 계층과 명령 정책 파일이 같은 부재를 각각 다시 막는다.</desc>
<rect x="0" y="0" width="880" height="276" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">부재를 지키는 세 자리</text>
<rect x="28" y="70" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="99.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">sdk/api 선언</text>
<rect x="28" y="130" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="159.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">Lettuce 구현 계층</text>
<rect x="28" y="190" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="219.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">redis-command-policy.yml</text>
<line x1="796" y1="118" x2="796" y2="202" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="796" y="62" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">강제 방향</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,20 @@
# 부재를 지키는 세 자리
## Alternative text
sdk/api 선언에서 Lettuce 구현 계층으로 그리고 redis-command-policy.yml 로 강제가 이어진다.
## Long description
SSOT 는 없는 명령이라는 선언이 API 문서에만 있지 않고 구현 계층과 명령 정책 파일이 같은 부재를 각각 다시 막는다고 적는다.
## Elements and evidence
- **sdk/api 선언** (service): No additional description. Evidence: L12887L12894.
- **Lettuce 구현 계층** (service): No additional description. Evidence: L12887L12894.
- **redis-command-policy.yml** (service): No additional description. Evidence: L12887L12896.
## Relationships
- **sdk/api 선언 → Lettuce 구현 계층:** 강제 방향. Evidence: L12887L12894.
- **Lettuce 구현 계층 → redis-command-policy.yml:** 강제 방향. Evidence: L12887L12896.
@@ -0,0 +1,14 @@
# 부재를 지키는 세 자리
# Question: 없는 명령은 어디서 막히는가?
direction: right
n0: "sdk/api 선언" {
shape: rectangle
}
n1: "Lettuce 구현 계층" {
shape: rectangle
}
n2: "redis-command-policy.yml" {
shape: rectangle
}
n0 -> n1: "강제 방향"
n1 -> n2: "강제 방향"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c03",
"spec_version": "1.1",
"spec_sha256": "745e132f5f01c7ab838beb161c9da2297a42915a3156820b8b653682f7e0588f",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 12887,
"line": 12887
}
},
"outputs": [
"adapter-outbound-cache-redis-c03.svg",
"adapter-outbound-cache-redis-c03.d2",
"adapter-outbound-cache-redis-c03.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="937" height="300" viewBox="0 0 937 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">부재를 지키는 세 자리</title>
<desc id="diagram-description">SSOT 는 없는 명령이라는 선언이 API 문서에만 있지 않고 구현 계층과 명령 정책 파일이 같은 부재를 각각 다시 막는다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c03&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:12887,&quot;line&quot;:12887}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="937" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="12887-12894" />
<rect class="edge-label-bg" x="274.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">강제 방향</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="12887-12896" />
<rect class="edge-label-bg" x="584.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">강제 방향</text>
<g id="node-sdk">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="12887-12894" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">sdk/api 선언</text>
</g>
<g id="node-lettuce">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="12887-12894" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">Lettuce 구현 계층</text>
</g>
<g id="node-policy">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="12887-12896" x="690.0" y="60.0" width="202.0" height="64.0" rx="7" />
<text class="node-label" x="791.0" y="90.0">redis-command-policy.yml</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -1,15 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="276" viewBox="0 0 880 276" role="img">
<title>검사기에 대한 메타 검사</title>
<desc>두 진입점의 대칭을 반사 검사가 강제하고, 그 검사기가 고장 나 항상 통과하는 상태를 잡는 메타 테스트가 위에 하나 더 있다.</desc>
<rect x="0" y="0" width="880" height="276" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">검사기에 대한 메타 검사</text>
<rect x="28" y="70" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="99.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">두 진입점 인터페이스</text>
<rect x="28" y="130" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="159.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">ApiParityTest</text>
<rect x="28" y="190" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="219.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">검사기에 대한 메타 테스트</text>
<line x1="796" y1="118" x2="796" y2="202" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="796" y="62" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">검사 방향</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,20 @@
# 검사기에 대한 메타 검사
## Alternative text
두 진입점 인터페이스를 ApiParityTest 가 검사하고 그 검사기를 메타 테스트가 다시 검사한다.
## Long description
SSOT 는 두 진입점의 대칭을 반사 검사가 강제하고 그 검사기가 고장 나 항상 통과하는 상태를 잡는 메타 테스트가 위에 하나 더 있다고 적는다.
## Elements and evidence
- **두 진입점 인터페이스** (service): No additional description. Evidence: L12903L12912.
- **ApiParityTest** (service): No additional description. Evidence: L12903L12912.
- **검사기에 대한 메타 테스트** (service): No additional description. Evidence: L12903L12914.
## Relationships
- **두 진입점 인터페이스 → ApiParityTest:** 검사 방향. Evidence: L12903L12912.
- **ApiParityTest → 검사기에 대한 메타 테스트:** 검사 방향. Evidence: L12903L12914.
@@ -0,0 +1,14 @@
# 검사기에 대한 메타 검사
# Question: 검사기가 고장 나면 무엇이 잡는가?
direction: right
n0: "두 진입점 인터페이스" {
shape: rectangle
}
n1: "ApiParityTest" {
shape: rectangle
}
n2: "검사기에 대한 메타 테스트" {
shape: rectangle
}
n0 -> n1: "검사 방향"
n1 -> n2: "검사 방향"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c04",
"spec_version": "1.1",
"spec_sha256": "705fb1096f3e32789e8877f5605bb765ad1ffe743c7b9fdc19e2ca088f82f76f",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 12903,
"line": 12903
}
},
"outputs": [
"adapter-outbound-cache-redis-c04.svg",
"adapter-outbound-cache-redis-c04.d2",
"adapter-outbound-cache-redis-c04.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="885" height="300" viewBox="0 0 885 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">검사기에 대한 메타 검사</title>
<desc id="diagram-description">SSOT 는 두 진입점의 대칭을 반사 검사가 강제하고 그 검사기가 고장 나 항상 통과하는 상태를 잡는 메타 테스트가 위에 하나 더 있다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c04&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:12903,&quot;line&quot;:12903}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="885" height="300" />
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="12903-12912" />
<rect class="edge-label-bg" x="274.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">검사 방향</text>
<polyline class="edge kind-request style-solid emphasis-primary" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="12903-12914" />
<rect class="edge-label-bg" x="584.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">검사 방향</text>
<g id="node-api">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="12903-12912" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">두 진입점 인터페이스</text>
</g>
<g id="node-parity">
<rect class="node-shape kind-service emphasis-primary role-service" data-evidence="12903-12912" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">ApiParityTest</text>
</g>
<g id="node-meta">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="12903-12914" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">검사기에 대한 메타 테스트</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="248" viewBox="0 0 880 248" role="img">
<title>두 모델이 공유하는 빌더</title>
<desc>동기 표면과 반응형 표면이 같은 request builder 를 만들어 쓴다. 그래서 permit·예산·인코딩·명령 선택의 변경이 한쪽에만 적용될 수 없다.</desc>
<rect x="0" y="0" width="880" height="248" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">두 모델이 공유하는 빌더</text>
<rect x="28" y="109" width="262" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="159.0" y="143.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">ValueOperationRequests</text>
<rect x="474" y="76" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="108.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">동기 표면</text>
<line x1="296" y1="138" x2="468" y2="103" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="70" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">같은 요청 객체</text>
<rect x="474" y="142" width="378" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="663.0" y="174.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">반응형 표면</text>
<line x1="296" y1="138" x2="468" y2="169" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="384" y="162" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">같은 요청 객체</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,20 @@
# 두 모델이 공유하는 빌더
## Alternative text
동기 표면과 반응형 표면이 모두 ValueOperationRequests 로 같은 요청 객체를 만든다.
## Long description
SSOT 는 동기 표면과 반응형 표면이 같은 request builder 를 만들어 쓰므로 permit·예산·인코딩·명령 선택의 변경이 양쪽에 함께 적용된다고 적는다.
## Elements and evidence
- **동기 표면** (service): No additional description. Evidence: L13108L13113.
- **반응형 표면** (service): No additional description. Evidence: L13108L13115.
- **ValueOperationRequests** (service): No additional description. Evidence: L13108L13113.
## Relationships
- **동기 표면 → ValueOperationRequests:** 요청 생성. Evidence: L13108L13113.
- **반응형 표면 → ValueOperationRequests:** 요청 생성. Evidence: L13108L13115.
@@ -0,0 +1,14 @@
# 두 모델이 공유하는 빌더
# Question: 동기와 반응형 표면은 무엇을 공유하는가?
direction: right
n0: "동기 표면" {
shape: rectangle
}
n1: "반응형 표면" {
shape: rectangle
}
n2: "ValueOperationRequests" {
shape: rectangle
}
n0 -> n2: "요청 생성"
n1 -> n2: "요청 생성"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c08",
"spec_version": "1.1",
"spec_sha256": "b6a7f818e89fc2cd5c68ae887f463f53ac84786f4f7e9731ca1801cd05750620",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 13108,
"line": 13108
}
},
"outputs": [
"adapter-outbound-cache-redis-c08.svg",
"adapter-outbound-cache-redis-c08.d2",
"adapter-outbound-cache-redis-c08.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="305" viewBox="0 0 680 305" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">두 모델이 공유하는 빌더</title>
<desc id="diagram-description">SSOT 는 동기 표면과 반응형 표면이 같은 request builder 를 만들어 쓰므로 permit·예산·인코딩·명령 선택의 변경이 양쪽에 함께 적용된다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c08&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:13108,&quot;line&quot;:13108}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="305" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,151.0 380.0,151.0" data-evidence="13108-13113" />
<rect class="edge-label-bg" x="298.2" y="107.5" width="51.5" height="22" rx="3" />
<text class="edge-label" x="324.0" y="122.5">요청 생성</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="220.0,228.0 300.0,228.0 300.0,169.0 380.0,169.0" data-evidence="13108-13115" />
<rect class="edge-label-bg" x="298.2" y="184.5" width="51.5" height="22" rx="3" />
<text class="edge-label" x="324.0" y="199.5">요청 생성</text>
<g id="node-sync">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="13108-13113" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">동기 표면</text>
</g>
<g id="node-reactive">
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="13108-13115" x="70.0" y="196.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="226.0">반응형 표면</text>
</g>
<g id="node-builder">
<rect class="node-shape kind-service emphasis-primary role-sink" data-evidence="13108-13113" x="380.0" y="124.5" width="188.0" height="71.0" rx="7" />
<text class="node-label" x="474.0" y="151.5">ValueOperationRequests</text>
<line class="node-detail-divider" x1="394.0" y1="172.5" x2="554.0" y2="172.5" />
<text class="node-detail" x="396.0" y="189.5">같은 요청 객체</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>고정된 검증 순서</title>
<desc>뒤 단계일수록 비싸므로 명백히 부적격한 명령은 인코딩도 전송도 하기 전에 거절된다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">고정된 검증 순서</text>
<rect x="28.0" y="104" width="105.6" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="80.8" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">capability</text>
<rect x="207.6" y="104" width="105.6" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="260.4" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">risk · permit</text>
<rect x="387.2" y="104" width="105.6" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440.0" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">namespace</text>
<rect x="566.8" y="104" width="105.6" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="619.6" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">slot</text>
<rect x="746.4" y="104" width="105.6" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="799.2" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">request budget</text>
<line x1="139.6" y1="133" x2="201.6" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="170.6" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">버전 대조</text>
<line x1="319.2" y1="133" x2="381.2" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="350.2" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">위험 등급</text>
<line x1="498.8" y1="133" x2="560.8" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="529.8" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">키 소유</text>
<line x1="678.4" y1="133" x2="740.4" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="709.4" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">단일 슬롯</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,24 @@
# 고정된 검증 순서
## Alternative text
명령이 capability 와 risk·permit 과 namespace 와 slot 과 request budget 을 차례로 지나는 순서.
## Long description
SSOT 는 명령 하나가 나가기 전에 지나는 단계를 적는다 — 카탈로그 분류, capability 와 최소 버전 확인, permit provenance 검증, 네임스페이스 검사, Cluster 동일 슬롯 검사, 요청 예산이다.
## Elements and evidence
- **capability** (service): No additional description. Evidence: L46523L46700.
- **risk · permit** (service): No additional description. Evidence: L46523L46700.
- **namespace** (service): No additional description. Evidence: L46523L46700.
- **slot** (service): No additional description. Evidence: L46523L46796.
- **request budget** (service): No additional description. Evidence: L46523L46796.
## Relationships
- **capability → risk · permit:** 통과. Evidence: L46523L46700.
- **risk · permit → namespace:** 통과. Evidence: L46523L46700.
- **namespace → slot:** 통과. Evidence: L46523L46796.
- **slot → request budget:** 통과. Evidence: L46523L46796.
@@ -0,0 +1,22 @@
# 고정된 검증 순서
# Question: 명령은 어떤 순서로 검증되는가?
direction: right
n0: "capability" {
shape: rectangle
}
n1: "risk · permit" {
shape: rectangle
}
n2: "namespace" {
shape: rectangle
}
n3: "slot" {
shape: rectangle
}
n4: "request budget" {
shape: rectangle
}
n0 -> n1: "통과"
n1 -> n2: "통과"
n2 -> n3: "통과"
n3 -> n4: "통과"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c10",
"spec_version": "1.1",
"spec_sha256": "dde4610264a7c89a81dbd94f29ce2c0ce78a4d14d953977aaae928a7097acbaa",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 46523,
"line": 46523
}
},
"outputs": [
"adapter-outbound-cache-redis-c10.svg",
"adapter-outbound-cache-redis-c10.d2",
"adapter-outbound-cache-redis-c10.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1505" height="300" viewBox="0 0 1505 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">고정된 검증 순서</title>
<desc id="diagram-description">SSOT 는 명령 하나가 나가기 전에 지나는 단계를 적는다 — 카탈로그 분류, capability 와 최소 버전 확인, permit provenance 검증, 네임스페이스 검사, Cluster 동일 슬롯 검사, 요청 예산이다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c10&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:46523,&quot;line&quot;:46523}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1505" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,95.5 300.0,95.5 300.0,95.5 380.0,95.5" data-evidence="46523-46700" />
<rect class="edge-label-bg" x="278.0" y="53.5" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="68.5">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,95.5 610.0,95.5 610.0,95.5 690.0,95.5" data-evidence="46523-46700" />
<rect class="edge-label-bg" x="588.0" y="53.5" width="44.0" height="22" rx="3" />
<text class="edge-label" x="610.0" y="68.5">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="840.0,95.5 920.0,95.5 920.0,95.5 1000.0,95.5" data-evidence="46523-46796" />
<rect class="edge-label-bg" x="898.0" y="53.5" width="44.0" height="22" rx="3" />
<text class="edge-label" x="920.0" y="68.5">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="1150.0,95.5 1230.0,95.5 1230.0,95.5 1310.0,95.5" data-evidence="46523-46796" />
<rect class="edge-label-bg" x="1208.0" y="53.5" width="44.0" height="22" rx="3" />
<text class="edge-label" x="1230.0" y="68.5">통과</text>
<g id="node-cap">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="46523-46700" x="70.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="145.0" y="87.0">capability</text>
<line class="node-detail-divider" x1="84.0" y1="108.0" x2="206.0" y2="108.0" />
<text class="node-detail" x="86.0" y="125.0">버전 대조</text>
</g>
<g id="node-risk">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="46523-46700" x="380.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="455.0" y="87.0">risk · permit</text>
<line class="node-detail-divider" x1="394.0" y1="108.0" x2="516.0" y2="108.0" />
<text class="node-detail" x="396.0" y="125.0">위험 등급</text>
</g>
<g id="node-ns">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="46523-46700" x="690.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="765.0" y="87.0">namespace</text>
<line class="node-detail-divider" x1="704.0" y1="108.0" x2="826.0" y2="108.0" />
<text class="node-detail" x="706.0" y="125.0">키 소유</text>
</g>
<g id="node-slot">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="46523-46796" x="1000.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="1075.0" y="87.0">slot</text>
<line class="node-detail-divider" x1="1014.0" y1="108.0" x2="1136.0" y2="108.0" />
<text class="node-detail" x="1016.0" y="125.0">단일 슬롯</text>
</g>
<g id="node-budget">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="46523-46796" x="1310.0" y="63.5" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1385.0" y="93.5">request budget</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>원시 명령이 지나야 하는 두 문</title>
<desc>조직이 정한 카탈로그 분류와 배포가 등록한 승인 둘 다를 통과해야 한다. 어느 쪽도 요청 시점에 결정되지 않는다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">원시 명령이 지나야 하는 두 문</text>
<rect x="28.0" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="140.7" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">정책 카탈로그 RAW_ONLY</text>
<rect x="327.3" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">ApprovedRawCommand 등록</text>
<rect x="626.7" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="739.3" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">RedisRawGateway</text>
<line x1="259.3" y1="133" x2="321.3" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="290.3" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">조직의 결정</text>
<line x1="558.7" y1="133" x2="620.7" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="589.7" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">배포의 결정</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,21 @@
# 원시 명령이 지나야 하는 두 문
## Alternative text
원시 명령이 정책 카탈로그와 승인 등록을 차례로 지나 게이트웨이에 닿는 순서.
## Long description
SSOT 는 원시 명령이 조직이 정한 카탈로그 분류(RAW_ONLY)와 배포가 등록한 승인(ApprovedRawCommand) 둘 다를 통과해야 한다고 적는다. 어느 쪽도 요청 시점에 결정되지 않는다.
## Elements and evidence
- **원시 명령** (service): No additional description. Evidence: L13603L13740.
- **정책 카탈로그 RAW_ONLY** (service): No additional description. Evidence: L13603L13740.
- **RedisRawGateway** (service): No additional description. Evidence: L13603L13755.
## Relationships
- **원시 명령 → 정책 카탈로그 RAW_ONLY:** 분류 확인. Evidence: L13603L13740.
- **정책 카탈로그 RAW_ONLY → RedisRawGateway:** RAW_ONLY 통과. Evidence: L13603L13740.
- **RedisRawGateway → 원시 명령:** 승인 등록 확인. Evidence: L13603L13755.
@@ -0,0 +1,15 @@
# 원시 명령이 지나야 하는 두 문
# Question: 원시 명령은 무엇을 통과해야 하는가?
direction: right
n0: "원시 명령" {
shape: rectangle
}
n1: "정책 카탈로그 RAW_ONLY" {
shape: rectangle
}
n2: "RedisRawGateway" {
shape: rectangle
}
n0 -> n1: "분류 확인"
n1 -> n2: "RAW_ONLY 통과"
n2 -> n0: "승인 등록 확인"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c12",
"spec_version": "1.1",
"spec_sha256": "335e133bc731f2875fa1ac4250d6db1265978087feb84105e8b6cbff634efba1",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 13603,
"line": 13603
}
},
"outputs": [
"adapter-outbound-cache-redis-c12.svg",
"adapter-outbound-cache-redis-c12.d2",
"adapter-outbound-cache-redis-c12.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "sequence",
"reference_ids": [
"payment-approval-sequence"
],
"diagram_only": true
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="691" height="356" viewBox="0 0 691 356" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">원시 명령이 지나야 하는 두 문</title>
<desc id="diagram-description">SSOT 는 원시 명령이 조직이 정한 카탈로그 분류(RAW_ONLY)와 배포가 등록한 승인(ApprovedRawCommand) 둘 다를 통과해야 한다고 적는다. 어느 쪽도 요청 시점에 결정되지 않는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c12&quot;,&quot;profile&quot;:&quot;sequence&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:13603,&quot;line&quot;:13603}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="691" height="356" />
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="13603-13740" x="45.0" y="35.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="120.0" y="65.0">원시 명령</text>
<line class="lifeline" x1="120.0" y1="99.0" x2="120.0" y2="326.0" />
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="13603-13740" x="255.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="330.0" y="62.0">정책 카탈로그 RAW_ONLY</text>
<line class="node-detail-divider" x1="269.0" y1="83.0" x2="391.0" y2="83.0" />
<text class="node-detail" x="271.0" y="100.0">조직의 결정</text>
<line class="lifeline" x1="330.0" y1="106.0" x2="330.0" y2="326.0" />
<rect class="node-shape kind-service emphasis-primary role-participant" data-evidence="13603-13755" x="465.0" y="35.0" width="181.0" height="78.0" rx="7" />
<text class="node-label" x="555.5" y="62.0">RedisRawGateway</text>
<line class="node-detail-divider" x1="479.0" y1="83.0" x2="632.0" y2="83.0" />
<text class="node-detail" x="481.0" y="100.0">ApprovedRawCommand 등록</text>
<text class="node-detail" x="481.0" y="116.0">배포의 결정</text>
<line class="lifeline" x1="555.5" y1="113.0" x2="555.5" y2="326.0" />
<polyline class="edge kind-request style-solid emphasis-normal" points="120.0,140.0 330.0,140.0" data-evidence="13603-13740" />
<rect class="edge-label-bg" x="189.2" y="114.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="225.0" y="129.0">1. 분류 확인</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="330.0,202.0 555.5,202.0" data-evidence="13603-13740" />
<rect class="edge-label-bg" x="386.9" y="176.0" width="111.8" height="22" rx="3" />
<text class="edge-label" x="442.8" y="191.0">2. RAW_ONLY 통과</text>
<polyline class="edge kind-response style-solid emphasis-primary semantic-dashed" points="555.5,264.0 120.0,264.0" data-evidence="13603-13755" />
<rect class="edge-label-bg" x="291.9" y="238.0" width="91.7" height="22" rx="3" />
<text class="edge-label" x="337.8" y="253.0">3. 승인 등록 확인</text>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>등록과 실행의 분리</title>
<desc>등록되지 않은 스크립트는 digest 가 없어서 서버에 닿을 방법이 없다. 그래서 검토된 것만 실행된다는 성질이 관례가 아니라 구조가 된다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">등록과 실행의 분리</text>
<rect x="28.0" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="140.7" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">배포 시 스크립트 등록</text>
<rect x="327.3" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">digest 대조</text>
<rect x="626.7" y="104" width="225.3" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="739.3" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">EVALSHA 실행</text>
<line x1="259.3" y1="133" x2="321.3" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="290.3" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">등록본 저장</text>
<line x1="558.7" y1="133" x2="620.7" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="589.7" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">본문 일치</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,20 @@
# 등록과 실행의 분리
## Alternative text
배포 시 스크립트를 등록하고 digest 를 대조한 뒤 EVALSHA 로 실행하는 순서.
## Long description
SSOT 는 등록되지 않은 스크립트가 digest 가 없어 서버에 닿을 방법이 없다고 적는다.
## Elements and evidence
- **배포 시 스크립트 등록** (service): No additional description. Evidence: L13241L13251.
- **digest 대조** (service): No additional description. Evidence: L13241L13251.
- **EVALSHA 실행** (service): No additional description. Evidence: L13241L13254.
## Relationships
- **배포 시 스크립트 등록 → digest 대조:** 등록 결과. Evidence: L13241L13251.
- **digest 대조 → EVALSHA 실행:** 일치 확인. Evidence: L13241L13254.
@@ -0,0 +1,14 @@
# 등록과 실행의 분리
# Question: 스크립트는 어떻게 실행되는가?
direction: right
n0: "배포 시 스크립트 등록" {
shape: rectangle
}
n1: "digest 대조" {
shape: rectangle
}
n2: "EVALSHA 실행" {
shape: rectangle
}
n0 -> n1: "등록 결과"
n1 -> n2: "일치 확인"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-cache-redis-c13",
"spec_version": "1.1",
"spec_sha256": "7f3e74963819811adb1d3dbe7f27d5f8d9deb43fdc35632fbf657b8827583bed",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 13245,
"line": 13245
}
},
"outputs": [
"adapter-outbound-cache-redis-c13.svg",
"adapter-outbound-cache-redis-c13.d2",
"adapter-outbound-cache-redis-c13.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "sequence",
"reference_ids": [
"payment-approval-sequence"
],
"diagram_only": true
}
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="300" viewBox="0 0 680 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">등록과 실행의 분리</title>
<desc id="diagram-description">SSOT 는 등록되지 않은 스크립트가 digest 가 없어 서버에 닿을 방법이 없다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-cache-redis-c13&quot;,&quot;profile&quot;:&quot;sequence&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:13245,&quot;line&quot;:13245}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="300" />
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="13241-13251" x="45.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="120.0" y="62.0">배포 시 스크립트 등록</text>
<line class="node-detail-divider" x1="59.0" y1="83.0" x2="181.0" y2="83.0" />
<text class="node-detail" x="61.0" y="100.0">등록본 저장</text>
<line class="lifeline" x1="120.0" y1="106.0" x2="120.0" y2="270.0" />
<rect class="node-shape kind-service emphasis-primary role-participant" data-evidence="13241-13251" x="255.0" y="35.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="330.0" y="62.0">digest 대조</text>
<line class="node-detail-divider" x1="269.0" y1="83.0" x2="391.0" y2="83.0" />
<text class="node-detail" x="271.0" y="100.0">본문 일치</text>
<line class="lifeline" x1="330.0" y1="106.0" x2="330.0" y2="270.0" />
<rect class="node-shape kind-service emphasis-normal role-participant" data-evidence="13241-13254" x="465.0" y="35.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="540.0" y="65.0">EVALSHA 실행</text>
<line class="lifeline" x1="540.0" y1="99.0" x2="540.0" y2="270.0" />
<polyline class="edge kind-request style-solid emphasis-normal" points="120.0,140.0 330.0,140.0" data-evidence="13241-13251" />
<rect class="edge-label-bg" x="189.2" y="114.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="225.0" y="129.0">1. 등록 결과</text>
<polyline class="edge kind-request style-solid emphasis-primary" points="330.0,202.0 540.0,202.0" data-evidence="13241-13254" />
<rect class="edge-label-bg" x="399.2" y="176.0" width="71.6" height="22" rx="3" />
<text class="edge-label" x="435.0" y="191.0">2. 일치 확인</text>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="244" viewBox="0 0 880 244" role="img">
<title>두 종착 상태</title>
<desc>비terminal 상태에서는 정상 종료로 가거나 격리로 이탈할 수 있고, 두 종착 상태에서 나가는 전이는 없다.</desc>
<rect x="0" y="0" width="880" height="244" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">두 종착 상태</text>
<rect x="28.0" y="120" width="210.7" height="56" rx="26" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="133.3" y="153.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">비terminal 상태</text>
<rect x="334.7" y="120" width="210.7" height="56" rx="26" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="440" y="153.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">PUBLISHED</text>
<rect x="641.3" y="120" width="210.7" height="56" rx="26" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="746.7" y="153.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">QUARANTINED</text>
<line x1="244.7" y1="148" x2="328.7" y2="148" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="286.7" y="138" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">정상 종료</text>
<path d="M133.3,178 C133.3,232 746.7,232 746.7,178" fill="none" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#ar)"/>
<text x="440.0" y="228" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">격리로 이탈</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,20 @@
# 두 종착 상태
## Alternative text
비terminal 상태에서 PUBLISHED 와 QUARANTINED 로 화살표가 가고 그 둘에서 나가는 화살표는 없다.
## Long description
SSOT 는 비terminal 상태에서 정상 종료로 가거나 격리로 이탈할 수 있고 두 종착 상태에서 나가는 전이가 없다고 적는다.
## Elements and evidence
- **비terminal 상태** (service): No additional description. Evidence: L11220L11235.
- **PUBLISHED** (service): No additional description. Evidence: L11220L11235.
- **QUARANTINED** (service): No additional description. Evidence: L11220L11239.
## Relationships
- **비terminal 상태 → PUBLISHED:** 정상 종료. Evidence: L11220L11235.
- **비terminal 상태 → QUARANTINED:** 격리로 이탈. Evidence: L11220L11239.
@@ -0,0 +1,14 @@
# 두 종착 상태
# Question: 파일 상태는 어디서 멈추는가?
direction: right
n0: "비terminal 상태" {
shape: rectangle
}
n1: "PUBLISHED" {
shape: rectangle
}
n2: "QUARANTINED" {
shape: rectangle
}
n0 -> n1: "정상 종료"
n0 -> n2: "격리로 이탈"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-fileserver-c03",
"spec_version": "1.1",
"spec_sha256": "9f5115f1e5c467bbb478f1227866597e23ca92f60685a4512f0deaa7177700ff",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 11220,
"line": 11220
}
},
"outputs": [
"adapter-outbound-fileserver-c03.svg",
"adapter-outbound-fileserver-c03.d2",
"adapter-outbound-fileserver-c03.alt.md"
],
"lint_issue_count": 0,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="319" viewBox="0 0 680 319" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">두 종착 상태</title>
<desc id="diagram-description">SSOT 는 비terminal 상태에서 정상 종료로 가거나 격리로 이탈할 수 있고 두 종착 상태에서 나가는 전이가 없다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-fileserver-c03&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:11220,&quot;line&quot;:11220}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="680" height="319" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,158.0 300.0,158.0 300.0,95.5 380.0,95.5" data-evidence="11220-11235" />
<rect class="edge-label-bg" x="298.2" y="112.8" width="51.5" height="22" rx="3" />
<text class="edge-label" x="324.0" y="127.8">정상 종료</text>
<polyline class="edge kind-request style-dashed emphasis-normal" points="220.0,176.0 300.0,176.0 300.0,238.5 380.0,238.5" data-evidence="11220-11239" />
<rect class="edge-label-bg" x="294.9" y="193.2" width="58.2" height="22" rx="3" />
<text class="edge-label" x="324.0" y="208.2">격리로 이탈</text>
<g id="node-mid">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="11220-11235" x="70.0" y="135.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="165.0">비terminal 상태</text>
</g>
<g id="node-pub">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11220-11235" x="380.0" y="60.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="455.0" y="87.0">PUBLISHED</text>
<line class="node-detail-divider" x1="394.0" y1="108.0" x2="516.0" y2="108.0" />
<text class="node-detail" x="396.0" y="125.0">종착 상태</text>
</g>
<g id="node-quar">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11220-11239" x="380.0" y="203.0" width="150.0" height="71.0" rx="7" />
<text class="node-label" x="455.0" y="230.0">QUARANTINED</text>
<line class="node-detail-divider" x1="394.0" y1="251.0" x2="516.0" y2="251.0" />
<text class="node-detail" x="396.0" y="268.0">종착 상태</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="230" viewBox="0 0 880 230" role="img">
<title>선언된 것과 선언되지 않은 것</title>
<desc>이 계층은 자기가 막는 것과 막지 못하는 것을 함께 적는다. 잔여 경로 연산은 문서에 선언되고 identity 검사로 감싸인다.</desc>
<rect x="0" y="0" width="880" height="230" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">선언된 것과 선언되지 않은 것</text>
<rect x="28" y="66" width="560" height="134" rx="14" fill="none" stroke="#374151" stroke-width="1.6"/>
<text x="44" y="88" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">LocalPersistentPayloadOperations</text>
<rect x="44.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="126.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">fileKey 재확인</text>
<rect x="226.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="308.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">소유자 · 권한 재확인</text>
<rect x="408.0" y="100" width="164.0" height="54" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="490.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">FileStore 재확인</text>
<rect x="624" y="100" width="228" height="54" rx="10" fill="url(#hx)" stroke="#374151" stroke-width="1.6" stroke-dasharray="6 4"/>
<text x="738.0" y="132.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">서술자 상대 대응물 없음</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,24 @@
# 선언된 것과 선언되지 않은 것
## Alternative text
잔여 경로 연산에서 세 가지 재확인으로 화살표가 가고 서술자 상대 대응물로는 점선이 간다.
## Long description
SSOT §48 은 payload 계층이 자기가 막는 것과 막지 못하는 것을 함께 적는다고 기록한다. 잔여 경로 연산은 문서에 선언되고 identity 검사로 감싸인다.
## Elements and evidence
- **LocalPersistentPayloadOperations** (service): No additional description. Evidence: L11508L11515.
- **fileKey 재확인** (service): No additional description. Evidence: L11508L11515.
- **소유자 · 권한 재확인** (service): No additional description. Evidence: L11508L11515.
- **FileStore 재확인** (service): No additional description. Evidence: L11508L11515.
- **서술자 상대 대응물** (service): No additional description. Evidence: L11508L11517.
## Relationships
- **LocalPersistentPayloadOperations → fileKey 재확인:** 검사. Evidence: L11508L11515.
- **LocalPersistentPayloadOperations → 소유자 · 권한 재확인:** 검사. Evidence: L11508L11515.
- **LocalPersistentPayloadOperations → FileStore 재확인:** 검사. Evidence: L11508L11515.
- **LocalPersistentPayloadOperations → 서술자 상대 대응물:** 없음. Evidence: L11508L11517.
@@ -0,0 +1,22 @@
# 선언된 것과 선언되지 않은 것
# Question: payload 계층은 잔여 위험을 어떻게 다루는가?
direction: right
n0: "LocalPersistentPayloadOperations" {
shape: rectangle
}
n1: "fileKey 재확인" {
shape: rectangle
}
n2: "소유자 · 권한 재확인" {
shape: rectangle
}
n3: "FileStore 재확인" {
shape: rectangle
}
n4: "서술자 상대 대응물" {
shape: rectangle
}
n0 -> n1: "검사"
n0 -> n2: "검사"
n0 -> n3: "검사"
n0 -> n4: "없음"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-fileserver-c08",
"spec_version": "1.1",
"spec_sha256": "5033047cb66ec9572692ab1519550d0ccc6e5564212983b8b710f99968ab2fda",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 11508,
"line": 11508
}
},
"outputs": [
"adapter-outbound-fileserver-c08.svg",
"adapter-outbound-fileserver-c08.d2",
"adapter-outbound-fileserver-c08.alt.md"
],
"lint_issue_count": 2,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="683" height="577" viewBox="0 0 683 577" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">선언된 것과 선언되지 않은 것</title>
<desc id="diagram-description">SSOT §48 은 payload 계층이 자기가 막는 것과 막지 못하는 것을 함께 적는다고 기록한다. 잔여 경로 연산은 문서에 선언되고 identity 검사로 감싸인다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-fileserver-c08&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:11508,&quot;line&quot;:11508}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="683" height="577" />
<polyline class="edge kind-request style-solid emphasis-normal" points="328.0,289.4 408.0,289.4 408.0,228.0 488.0,228.0" data-evidence="11508-11515" />
<rect class="edge-label-bg" x="410.0" y="244.7" width="44.0" height="22" rx="3" />
<text class="edge-label" x="432.0" y="259.7">검사</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="328.0,302.6 408.0,302.6 408.0,364.0 488.0,364.0" data-evidence="11508-11515" />
<rect class="edge-label-bg" x="410.0" y="319.3" width="44.0" height="22" rx="3" />
<text class="edge-label" x="432.0" y="334.3">검사</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="328.0,315.8 408.0,315.8 408.0,500.0 488.0,500.0" data-evidence="11508-11515" />
<rect class="edge-label-bg" x="410.0" y="393.9" width="44.0" height="22" rx="3" />
<text class="edge-label" x="432.0" y="408.9">검사</text>
<polyline class="edge kind-request style-dashed emphasis-primary" points="328.0,276.2 408.0,276.2 408.0,92.0 488.0,92.0" data-evidence="11508-11517" />
<rect class="edge-label-bg" x="410.0" y="170.1" width="44.0" height="22" rx="3" />
<text class="edge-label" x="432.0" y="185.1">없음</text>
<g id="node-ops">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="11508-11515" x="70.0" y="264.0" width="258.0" height="64.0" rx="7" />
<text class="node-label" x="199.0" y="294.0">LocalPersistentPayloadOperations</text>
</g>
<g id="node-desc">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11508-11517" x="488.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="563.0" y="90.0">서술자 상대 대응물</text>
</g>
<g id="node-key">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11508-11515" x="488.0" y="196.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="563.0" y="226.0">fileKey 재확인</text>
</g>
<g id="node-owner">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11508-11515" x="488.0" y="332.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="563.0" y="362.0">소유자 · 권한 재확인</text>
</g>
<g id="node-store">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="11508-11515" x="488.0" y="468.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="563.0" y="498.0">FileStore 재확인</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

@@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>재시도 결정의 순서</title>
<desc>싼 절대 차단이 먼저 오고 그다음 모호성, 그다음 상태별 규칙이 온다. 뒤 규칙이 앞 규칙이 금지한 것을 다시 허용할 수 없다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">재시도 결정의 순서</text>
<rect x="28.0" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="103.2" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">절대 차단 여섯</text>
<rect x="252.5" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="327.8" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">영구 실패 범주</text>
<rect x="477.0" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="552.2" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">증거</text>
<rect x="701.5" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="776.8" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">상태 · 실패별 규칙</text>
<line x1="184.5" y1="133" x2="246.5" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="215.5" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">통과</text>
<line x1="409.0" y1="133" x2="471.0" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="440.0" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">통과</text>
<line x1="633.5" y1="133" x2="695.5" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="664.5" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">통과</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,22 @@
# 재시도 결정의 순서
## Alternative text
절대 차단 여섯을 지나 영구 실패 범주와 증거를 거쳐 상태별 규칙에 닿는 순서.
## Long description
SSOT 는 싼 절대 차단이 먼저 오고 그다음 모호성, 그다음 상태별 규칙이 온다고 적는다. 뒤 규칙이 앞 규칙이 금지한 것을 다시 허용할 수 없다.
## Elements and evidence
- **절대 차단 여섯** (service): No additional description. Evidence: L14084L14098.
- **영구 실패 범주** (service): No additional description. Evidence: L14084L14098.
- **증거** (service): No additional description. Evidence: L14084L14098.
- **상태 · 실패별 규칙** (service): No additional description. Evidence: L14084L14101.
## Relationships
- **절대 차단 여섯 → 영구 실패 범주:** 통과. Evidence: L14084L14098.
- **영구 실패 범주 → 증거:** 통과. Evidence: L14084L14098.
- **증거 → 상태 · 실패별 규칙:** 통과. Evidence: L14084L14101.
@@ -0,0 +1,18 @@
# 재시도 결정의 순서
# Question: 재시도 판정은 어떤 순서로 걸리는가?
direction: right
n0: "절대 차단 여섯" {
shape: rectangle
}
n1: "영구 실패 범주" {
shape: rectangle
}
n2: "증거" {
shape: rectangle
}
n3: "상태 · 실패별 규칙" {
shape: rectangle
}
n0 -> n1: "통과"
n1 -> n2: "통과"
n2 -> n3: "통과"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-httpclient-c04",
"spec_version": "1.1",
"spec_sha256": "bc57bd7e7ef28c4eb05dae88725ad1d5697d3796b4a54d77e98e7f178d7fd064",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 14084,
"line": 14084
}
},
"outputs": [
"adapter-outbound-httpclient-c04.svg",
"adapter-outbound-httpclient-c04.d2",
"adapter-outbound-httpclient-c04.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1195" height="300" viewBox="0 0 1195 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">재시도 결정의 순서</title>
<desc id="diagram-description">SSOT 는 싼 절대 차단이 먼저 오고 그다음 모호성, 그다음 상태별 규칙이 온다고 적는다. 뒤 규칙이 앞 규칙이 금지한 것을 다시 허용할 수 없다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-httpclient-c04&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:14084,&quot;line&quot;:14084}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1195" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="14084-14098" />
<rect class="edge-label-bg" x="278.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="14084-14098" />
<rect class="edge-label-bg" x="588.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">통과</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="840.0,92.0 920.0,92.0 920.0,92.0 1000.0,92.0" data-evidence="14084-14101" />
<rect class="edge-label-bg" x="898.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="920.0" y="65.0">통과</text>
<g id="node-block">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="14084-14098" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">절대 차단 여섯</text>
</g>
<g id="node-perm">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="14084-14098" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">영구 실패 범주</text>
</g>
<g id="node-evid">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="14084-14098" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">증거</text>
</g>
<g id="node-rule">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="14084-14101" x="1000.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1075.0" y="90.0">상태 · 실패별 규칙</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="196" viewBox="0 0 880 196" role="img">
<title>시도마다 지나는 가드 순서</title>
<desc>열린 회로가 요금 토큰이나 벌크헤드 permit 을 쓰기 전에 먼저 거절한다. 해제는 역순이다.</desc>
<rect x="0" y="0" width="880" height="196" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">시도마다 지나는 가드 순서</text>
<rect x="28.0" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="103.2" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">Circuit Breaker</text>
<rect x="252.5" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="327.8" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">Rate Limiter</text>
<rect x="477.0" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="552.2" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">Bulkhead</text>
<rect x="701.5" y="104" width="150.5" height="58" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="776.8" y="138.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">HTTP 호출</text>
<line x1="184.5" y1="133" x2="246.5" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="215.5" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">허가</text>
<line x1="409.0" y1="133" x2="471.0" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="440.0" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">토큰</text>
<line x1="633.5" y1="133" x2="695.5" y2="133" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="664.5" y="92" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">permit</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,22 @@
# 시도마다 지나는 가드 순서
## Alternative text
시도가 Circuit Breaker 와 Rate Limiter 와 Bulkhead 를 차례로 지나 HTTP 호출에 닿는 순서.
## Long description
SSOT 는 열린 회로가 요금 토큰이나 벌크헤드 permit 을 쓰기 전에 먼저 거절하고 해제는 역순이라고 적는다.
## Elements and evidence
- **Circuit Breaker** (service): No additional description. Evidence: L14102L14112.
- **Rate Limiter** (service): No additional description. Evidence: L14102L14112.
- **Bulkhead** (service): No additional description. Evidence: L14102L14112.
- **HTTP 호출** (service): No additional description. Evidence: L14102L14114.
## Relationships
- **Circuit Breaker → Rate Limiter:** 허가. Evidence: L14102L14112.
- **Rate Limiter → Bulkhead:** 토큰. Evidence: L14102L14112.
- **Bulkhead → HTTP 호출:** permit. Evidence: L14102L14114.
@@ -0,0 +1,18 @@
# 시도마다 지나는 가드 순서
# Question: 가드는 어떤 순서로 걸리고 풀리는가?
direction: right
n0: "Circuit Breaker" {
shape: rectangle
}
n1: "Rate Limiter" {
shape: rectangle
}
n2: "Bulkhead" {
shape: rectangle
}
n3: "HTTP 호출" {
shape: rectangle
}
n0 -> n1: "허가"
n1 -> n2: "토큰"
n2 -> n3: "permit"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-httpclient-c05",
"spec_version": "1.1",
"spec_sha256": "4db72801007900e5e6f28646decd4da8ad429b21d12e9cb04ecb9035eb005c6a",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 14102,
"line": 14102
}
},
"outputs": [
"adapter-outbound-httpclient-c05.svg",
"adapter-outbound-httpclient-c05.d2",
"adapter-outbound-httpclient-c05.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1195" height="300" viewBox="0 0 1195 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">시도마다 지나는 가드 순서</title>
<desc id="diagram-description">SSOT 는 열린 회로가 요금 토큰이나 벌크헤드 permit 을 쓰기 전에 먼저 거절하고 해제는 역순이라고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-httpclient-c05&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:14102,&quot;line&quot;:14102}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1195" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="14102-14112" />
<rect class="edge-label-bg" x="278.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">허가</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="14102-14112" />
<rect class="edge-label-bg" x="588.0" y="50.0" width="44.0" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">토큰</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="840.0,92.0 920.0,92.0 920.0,92.0 1000.0,92.0" data-evidence="14102-14114" />
<rect class="edge-label-bg" x="890.9" y="50.0" width="58.2" height="22" rx="3" />
<text class="edge-label" x="920.0" y="65.0">permit</text>
<g id="node-cb">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="14102-14112" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">Circuit Breaker</text>
</g>
<g id="node-rl">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="14102-14112" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">Rate Limiter</text>
</g>
<g id="node-bh">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="14102-14112" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">Bulkhead</text>
</g>
<g id="node-http">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="14102-14114" x="1000.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1075.0" y="90.0">HTTP 호출</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -1,17 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="336" viewBox="0 0 880 336" role="img">
<title>응답 크기 예산이 강제되는 자리</title>
<desc>와이어 바이트 예산, 읽는 도중의 상한 강제, 디코드 바이트 예산, 오류 본문 제한 읽기가 읽는 순서대로 쌓여 있고 오른쪽에 읽는 방향 화살표가 있다.</desc>
<rect x="0" y="0" width="880" height="336" fill="#ffffff"/>
<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#374151"/></marker><pattern id="hx" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="7" stroke="#9ca3af" stroke-width="2"/></pattern></defs>
<text x="28" y="40" font-family="system-ui,sans-serif" font-size="19" font-weight="600" fill="#111827">응답 크기 예산이 강제되는 자리</text>
<rect x="28" y="70" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="99.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">와이어 바이트 예산</text>
<rect x="28" y="130" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="159.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">읽는 도중 상한 강제</text>
<rect x="28" y="190" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="219.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">디코드 바이트 예산</text>
<rect x="28" y="250" width="700" height="48" rx="10" fill="#eef2ff" stroke="#374151" stroke-width="1.6"/>
<text x="378.0" y="279.0" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#111827">오류 본문 제한 읽기</text>
<line x1="796" y1="118" x2="796" y2="262" stroke="#374151" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="796" y="62" text-anchor="middle" font-family="system-ui,sans-serif" font-size="13" fill="#6b7280">읽는 순서</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,22 @@
# 응답 크기 예산이 강제되는 자리
## Alternative text
응답을 읽는 순서대로 와이어 바이트 예산과 읽는 도중 상한 강제와 디코드 바이트 예산과 오류 본문 제한 읽기가 이어진다.
## Long description
SSOT 는 응답 크기 예산이 강제되는 자리가 읽는 순서대로 쌓여 있다고 적는다.
## Elements and evidence
- **와이어 바이트 예산** (service): No additional description. Evidence: L1L1500.
- **읽는 도중 상한 강제** (service): No additional description. Evidence: L1L1500.
- **디코드 바이트 예산** (service): No additional description. Evidence: L1L1838.
- **오류 본문 제한 읽기** (service): No additional description. Evidence: L1L1838.
## Relationships
- **와이어 바이트 예산 → 읽는 도중 상한 강제:** 읽는 순서. Evidence: L1L1500.
- **읽는 도중 상한 강제 → 디코드 바이트 예산:** 읽는 순서. Evidence: L1L1838.
- **디코드 바이트 예산 → 오류 본문 제한 읽기:** 읽는 순서. Evidence: L1L1838.
@@ -0,0 +1,18 @@
# 응답 크기 예산이 강제되는 자리
# Question: 응답 크기는 어디서 몇 번 걸리는가?
direction: right
n0: "와이어 바이트 예산" {
shape: rectangle
}
n1: "읽는 도중 상한 강제" {
shape: rectangle
}
n2: "디코드 바이트 예산" {
shape: rectangle
}
n3: "오류 본문 제한 읽기" {
shape: rectangle
}
n0 -> n1: "읽는 순서"
n1 -> n2: "읽는 순서"
n2 -> n3: "읽는 순서"
@@ -0,0 +1,28 @@
{
"harness_version": "0.2.0",
"spec_id": "adapter-outbound-httpclient-c06",
"spec_version": "1.1",
"spec_sha256": "1949547860128c85d1af8036d5f49ac34bbffdbce3a7c9c6aa530effd8906048",
"source_context": {
"document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md",
"document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d",
"anchor": {
"kind": "line",
"value": 1,
"line": 1
}
},
"outputs": [
"adapter-outbound-httpclient-c06.svg",
"adapter-outbound-httpclient-c06.d2",
"adapter-outbound-httpclient-c06.alt.md"
],
"lint_issue_count": 1,
"assumption_count": 0,
"assumptions_allowed": false,
"composition_profile": "component-flow",
"reference_ids": [
"payment-event-flow"
],
"diagram_only": true
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1195" height="300" viewBox="0 0 1195 300" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">응답 크기 예산이 강제되는 자리</title>
<desc id="diagram-description">SSOT 는 응답 크기 예산이 강제되는 자리가 읽는 순서대로 쌓여 있다고 적는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;adapter-outbound-httpclient-c06&quot;,&quot;profile&quot;:&quot;component-flow&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md&quot;,&quot;document_sha256&quot;:&quot;8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;line&quot;,&quot;value&quot;:1,&quot;line&quot;:1}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;:true}</metadata>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<style>
:root { color-scheme: light; }
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
.canvas { fill: #ffffff; }
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
.group-label-bg { fill: #ffffff; }
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
.edge.style-dotted { stroke-dasharray: 2 5; }
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.edge.emphasis-muted { stroke: #9ca3af; }
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
.edge-label-bg { fill: #ffffff; }
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
.node-shape.assumption { stroke-dasharray: 4 4; }
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
.controller-led { fill: #4b5563; }
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
.node-detail { font-size: 11px; fill: #374151; }
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
</style>
</defs>
<rect class="canvas" width="1195" height="300" />
<polyline class="edge kind-request style-solid emphasis-primary" points="220.0,92.0 300.0,92.0 300.0,92.0 380.0,92.0" data-evidence="1-1500" />
<rect class="edge-label-bg" x="274.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="300.0" y="65.0">읽는 순서</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="530.0,92.0 610.0,92.0 610.0,92.0 690.0,92.0" data-evidence="1-1838" />
<rect class="edge-label-bg" x="584.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="610.0" y="65.0">읽는 순서</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="840.0,92.0 920.0,92.0 920.0,92.0 1000.0,92.0" data-evidence="1-1838" />
<rect class="edge-label-bg" x="894.2" y="50.0" width="51.5" height="22" rx="3" />
<text class="edge-label" x="920.0" y="65.0">읽는 순서</text>
<g id="node-wire">
<rect class="node-shape kind-service emphasis-primary role-source" data-evidence="1-1500" x="70.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="145.0" y="90.0">와이어 바이트 예산</text>
</g>
<g id="node-during">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1-1500" x="380.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="455.0" y="90.0">읽는 도중 상한 강제</text>
</g>
<g id="node-decode">
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="1-1838" x="690.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="765.0" y="90.0">디코드 바이트 예산</text>
</g>
<g id="node-err">
<rect class="node-shape kind-service emphasis-normal role-sink" data-evidence="1-1838" x="1000.0" y="60.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="1075.0" y="90.0">오류 본문 제한 읽기</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

Some files were not shown because too many files have changed in this diff Show More