Files
llm-wiki/harness/state/semantic-certificates/0d0f80e31b5f216f8511a295ffae4b8745ab8821dfc6e49c6a234f006b93c33f/0c1f3d22fdc729c013ddd9420d4d3822187ff38fbd04a42c970414d6d47f4e06.json
T

2 lines
178 KiB
JSON

{"audit_request":{"assertions":[{"assertion_id":"AST-001","condition":"unconditional","line_end":127,"line_start":127,"modality":"must","object":"templates/diagram-standards.md v2 (minimalist) 준수 — 정점 ≤ 10, 간선 ≤ 8, callout ≤ 1","predicate":"requires","quote":"> 6개 패턴 각각의 컴포넌트 구성도. **`templates/diagram-standards.md` v2 (minimalist) 준수** — 5초/30초 룰, 정점 ≤ 10, 간선 ≤ 8, callout ≤ 1, 80% 회색/흰색 + 강조 색 1~2 정도.","scope":"architecture-diagrams","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"시스템 아키텍처 다이어그램 (P1A~P3B)"},{"assertion_id":"AST-002","condition":"unconditional","line_end":129,"line_start":129,"modality":"must","object":"draw.io (.drawio 파일, 저장 경로 raw/diagrams/keycloak-patterns/)","predicate":"uses","quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용.","scope":"architecture-diagrams","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"시스템 아키텍처 다이어그램"},{"assertion_id":"AST-003","condition":"unconditional","line_end":129,"line_start":129,"modality":"must_not","object":"Mermaid graph TD 사용 (Mermaid 는 시퀀스/ER 다이어그램 전용)","predicate":"forbids","quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용.","scope":"architecture-diagrams","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"시스템 아키텍처 다이어그램"},{"assertion_id":"AST-004","condition":"AP2·AP3 needs-diagram 신규 작성 시","line_end":333,"line_start":333,"modality":"must","object":"rules/diagram-standards.md v2 (minimalist) 준수 + wiki-diagram-reviewer ≥95 별도 확인","predicate":"requires","quote":"작성 시 `rules/diagram-standards.md` v2 (minimalist) 준수 + `wiki-diagram-reviewer` ≥95 별도 확인(게이트는 존재만 판정, 품질 ≥95 는 사용자가 별도 실행).","scope":"architecture-diagrams","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"AP2·AP3 아키텍처 다이어그램 작성"},{"assertion_id":"AST-005","condition":"unconditional","line_end":160,"line_start":160,"modality":"observed","object":"OIDC 인증 게이트 역할 (인증 안 된 요청을 Keycloak 으로 redirect)","predicate":"owns","quote":"| Edge Proxy | OIDC 인증 게이트 — 인증 안 된 요청을 Keycloak으로 redirect | oauth2-proxy 또는 Traefik ForwardAuth |","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A Edge Proxy"},{"assertion_id":"AST-006","condition":"unconditional","line_end":160,"line_start":160,"modality":"observed","object":"oauth2-proxy 또는 Traefik ForwardAuth (둘 중 하나의 스택)","predicate":"uses","quote":"| Edge Proxy | OIDC 인증 게이트 — 인증 안 된 요청을 Keycloak으로 redirect | oauth2-proxy 또는 Traefik ForwardAuth |","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A Edge Proxy"},{"assertion_id":"AST-007","condition":"unconditional","line_end":161,"line_start":161,"modality":"observed","object":"인증 검증을 Edge Proxy 에 위임 (헤더로 사용자 식별, Spring Security 미사용)","predicate":"delegates","quote":"| Backend API | 비즈니스 로직만. 인증 검증은 proxy에 위임. 헤더로 사용자 식별 | Spring Boot 3.x (Spring Security 미사용) |","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A Backend API"},{"assertion_id":"AST-008","condition":"인증 완료 후","line_end":150,"line_start":150,"modality":"observed","object":"X-Forwarded-User 헤더 (인증 사용자명) → backend","predicate":"produces","quote":"4. `④ X-Forwarded-User` (강조 — 핵심 위탁) — proxy 가 인증 사용자명을 헤더로 backend 에 전달","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A Edge Proxy"},{"assertion_id":"AST-009","condition":"ingress 우회 경로 존재 시","line_end":153,"line_start":153,"modality":"observed","object":"X-Forwarded-User 헤더만으로 사용자 식별 → 위조 가능","predicate":"has_failure_behavior","quote":"- backend 가 `X-Forwarded-User` 헤더만으로 사용자 식별 → ingress 우회 경로 존재 시 위조 가능","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A 헤더 신뢰 모델"},{"assertion_id":"AST-010","condition":"헤더 spoofing 함정 해결 시","line_end":154,"line_start":154,"modality":"must","object":"NetworkPolicy(k8s) 또는 SG(AWS) 로 proxy→backend 만 통과 + 가능하면 mTLS 추가","predicate":"requires","quote":"- 해결: NetworkPolicy (k8s) 또는 SG (AWS) 로 proxy → backend 만 통과시키고, 가능하면 mTLS 추가","scope":"P1A (AP4 edge, no Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1A 헤더 spoofing 방어"},{"assertion_id":"AST-011","condition":"Google federation email-match auto-linking 함정 해결 시","line_end":189,"line_start":189,"modality":"must","object":"Confirm Link Existing Account 강제 + email_verified=true 필수","predicate":"requires","quote":"- 해결: First Broker Login Flow 에서 `Confirm Link Existing Account` 강제 + `email_verified=true` 필수","scope":"google-federation","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"First Broker Login Flow"},{"assertion_id":"AST-012","condition":"P1A 에 Google federation 을 추가할 때","line_end":193,"line_start":193,"modality":"observed","object":"Backend/proxy 코드 변경 0줄 (Keycloak Realm 설정만 추가)","predicate":"requires","quote":"- Backend / proxy 코드는 변경 0줄 — Keycloak Realm 설정만 추가","scope":"P1B (AP4 + Google)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P1B Google federation 추가"},{"assertion_id":"AST-013","condition":"unconditional","line_end":222,"line_start":222,"modality":"observed","object":"JWT (iss, aud, exp, signature) — Spring Security 6.x Resource Server","predicate":"validates","quote":"- Backend 가 JWT validator 코드 보유 (`iss`, `aud`, `exp`, signature) — Spring Security 6.x Resource Server","scope":"P2A (AP1 cluster-internal)","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P2A Backend"},{"assertion_id":"AST-014","condition":"unconditional","line_end":262,"line_start":262,"modality":"observed","object":"실 구현 대상으로 지정됨","predicate":"other","quote":"### 3-1-5. P3A — Single EC2 (no Google) — **실 구현 대상**","scope":"P3A","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P3A (Single EC2, no Google)"},{"assertion_id":"AST-015","condition":"KC_HOSTNAME iss mismatch 함정 해결 시","line_end":279,"line_start":279,"modality":"must","object":"KC_HOSTNAME=<public-host> + KC_HTTP_ENABLED=true 명시","predicate":"requires","quote":"- 해결: docker-compose 에 `KC_HOSTNAME=<public-host>` + `KC_HTTP_ENABLED=true` 명시","scope":"P3A","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P3A Keycloak docker-compose 설정"},{"assertion_id":"AST-016","condition":"EC2 1대 다운 시","line_end":287,"line_start":287,"modality":"observed","object":"SPoF — EC2 1대 다운 = 전체 정지; 운영급은 P2A + Keycloak HA cluster","predicate":"has_failure_behavior","quote":"- 단점: SPoF — EC2 1대 다운 = 전체 정지. 운영급은 P2A + Keycloak HA cluster.","scope":"P3A","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"P3A single-EC2 토폴로지"},{"assertion_id":"AST-017","condition":"unconditional","line_end":328,"line_start":328,"modality":"observed","object":"신 primary 축의 AP1 배포 변형 + AP4","predicate":"maps_to","quote":"> 신 primary 축의 AP2·AP3 은 기존 6 `.drawio`(P1A~P3B = AP1 배포 변형 + AP4)에 대응 아키텍처 다이어그램이 없다. **needs-diagram** — 사용자가 draw.io 로 작성 후 아래 백틱을 풀어 활성 임베드로 전환한다. (미작성 파일을 활성 임베드로 두면 9a 린터가 `BROKEN_LINK` 로 잡으므로 placeholder 는 백틱 코드로 비활성.)","scope":"hub taxonomy","source_surface":"SURF-1FB5A68428F1BD7348C2","subject":"기존 6 패턴 (P1A~P3B)"},{"assertion_id":"AST-018","condition":"2026-07-14 교정 이후 포함 범위","line_end":487,"line_start":487,"modality":"must","object":"AP1~AP4 4패턴 모두 single-EC2 로컬 스택 위 E2E(locally-verified) + signature 함정 재현→해결","predicate":"requires","quote":"- **4 인증-아키텍처 패턴(AP1~AP4) 실 구현** — 모두 single-EC2 로컬 스택 위에서 E2E(`locally-verified`) + signature 함정 재현→해결 (§1 성공기준, §8.0 branch 분해).","scope":"project scope","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"프로젝트 포함 범위"},{"assertion_id":"AST-019","condition":"unconditional","line_end":488,"line_start":488,"modality":"must","object":"cross-cutting 변형으로 1회 실 구현 (SPA/Backend 코드 0줄 변경 검증 포함)","predicate":"has_cardinality","quote":"- **Google IdP brokering** 을 cross-cutting 변형으로 1회 실 구현(어느 패턴에 얹어도 SPA/Backend 코드 0줄 변경 검증 포함).","scope":"project scope","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"Google IdP brokering"},{"assertion_id":"AST-020","condition":"제외 범위 (project-level scope)","line_end":495,"line_start":495,"modality":"must_not","object":"별도 k8s/Traefik 환경 구축 (hostname·issuer·network 차이는 문서화만)","predicate":"forbids","quote":"- **배포 토폴로지 별도 구현**: 인증 아키텍처는 single-EC2 로 실 구현하고, cluster-internal / edge 는 hostname·issuer·network 차이만 문서화(별도 k8s/Traefik 환경 구축 안 함 — §2.2 cross-cutting).","scope":"project scope — 제외 범위","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"배포 토폴로지 (cluster-internal / edge)"},{"assertion_id":"AST-021","condition":"제외 범위 (project-level scope)","line_end":496,"line_start":496,"modality":"must_not","object":"GitHub / Auth0 / Cognito federation (Google 만 허용)","predicate":"forbids","quote":"- 다른 OIDC Provider(GitHub / Auth0 / Cognito) federation. Google만.","scope":"project scope — 제외 범위","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"OIDC Provider federation"},{"assertion_id":"AST-022","condition":"제외 범위 (project-level scope)","line_end":499,"line_start":499,"modality":"must_not","object":"mTLS·FAPI·DPoP 등 고급 보안 옵션의 프로젝트 내 구현","predicate":"forbids","quote":"- mTLS, FAPI(Financial-grade API), DPoP 등 고급 보안 옵션.","scope":"project scope — 제외 범위","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"고급 보안 옵션 (mTLS / FAPI / DPoP)"},{"assertion_id":"AST-023","condition":"Deferred — keycloak SERVER-side 심화 트랙 (지금 안 함)","line_end":506,"line_start":506,"modality":"observed","object":"server-side 심화 트랙으로 deferred (Infinispan 분산 캐시, active-active 다중 노드)","predicate":"other","quote":"- **HA cluster** — Infinispan 분산 캐시, active-active Keycloak 다중 노드.","scope":"project scope — deferred","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"Keycloak HA cluster"},{"assertion_id":"AST-024","condition":"Deferred — keycloak SERVER-side 심화 트랙 (지금 안 함)","line_end":508,"line_start":508,"modality":"observed","object":"server-side 심화 트랙으로 deferred (realm/client export·import 코드화, 씨앗 존재)","predicate":"other","quote":"- **Admin REST API 자동화** — realm/client export·import 를 코드로 (`feature-keycloak-realm-client-export` 씨앗 존재).","scope":"project scope — deferred","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"Admin REST API 자동화 (feature-keycloak-realm-client-export)"},{"assertion_id":"AST-025","condition":"현 phase (4 패턴 authN E2E 완료 전)","line_end":514,"line_start":514,"modality":"observed","object":"현 phase 명시적 out-of-scope (4 패턴 E2E 후 각 패턴에 얹음, 폐기 아님; 씨앗 feature-keycloak-idp-mappers-claim-to-role 존재)","predicate":"other","quote":"- **인가(Authorization) — keycloak roles → Spring `@PreAuthorize`**: 본 4 패턴은 authN(인증) 토큰 흐름에 집중한다. RBAC 인가는 별개 관심사이며 기존 씨앗 `feature-keycloak-idp-mappers-claim-to-role` 존재 — 4 패턴 E2E 후 각 패턴에 얹음(현 phase 명시적 out-of-scope, 폐기 아님).","scope":"project scope — 인접 관심사","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"RBAC 인가 (keycloak roles → Spring @PreAuthorize)"},{"assertion_id":"AST-026","condition":"현 phase (통합 logout 흐름 기준)","line_end":515,"line_start":515,"modality":"observed","object":"deferred (씨앗 feature-keycloak-refresh-rotation-and-logout 존재; AP3 BFF session 종료·AP1 토큰 만료로 부분 커버)","predicate":"other","quote":"- **Logout / session termination (front/back-channel)**: AP3 BFF session 종료 · AP1 토큰 만료로 부분 커버. 통합 logout 흐름은 기존 씨앗 `feature-keycloak-refresh-rotation-and-logout` 존재 — deferred.","scope":"project scope — 인접 관심사","source_surface":"SURF-C73BCB1F8B2541AA5D7D","subject":"통합 logout / session termination 흐름"},{"assertion_id":"AST-027","condition":"Decision status: active","line_end":553,"line_start":553,"modality":"must","object":"AP1~AP4 인증 통합 아키텍처 + cross-cutting 변형","predicate":"other","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |","scope":"hub taxonomy — DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"canonical 분류축"},{"assertion_id":"AST-028","condition":"Decision status: active","line_end":554,"line_start":554,"modality":"must","object":"public client + Authorization Code + PKCE","predicate":"uses","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |","scope":"AP1 — DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"AP1"},{"assertion_id":"AST-029","condition":"Decision status: active","line_end":555,"line_start":555,"modality":"must","object":"token 은 backend session 에 두고 browser 에는 cookie 만 둠","predicate":"requires","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |","scope":"AP3 — DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"AP3 (BFF)"},{"assertion_id":"AST-030","condition":"Decision status: active","line_end":556,"line_start":556,"modality":"must","object":"access token 만 browser 에 전달 (token 획득은 backend 가 수행)","predicate":"returns","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |","scope":"AP2 — DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"AP2 backend"},{"assertion_id":"AST-031","condition":"Decision status: active","line_end":557,"line_start":557,"modality":"must","object":"oauth2-proxy ForwardAuth","predicate":"uses","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |","scope":"AP4 — DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"AP4"},{"assertion_id":"AST-032","condition":"Decision status: active","line_end":558,"line_start":558,"modality":"must","object":"Keycloak IdP brokering + hardened First Broker Login","predicate":"uses","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |","scope":"google-federation — DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"Google federation"},{"assertion_id":"AST-033","condition":"Decision status: documented-only","line_end":559,"line_start":559,"modality":"must","object":"E2E success + signature security failure 재현·해결 evidence","predicate":"has_threshold","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |","scope":"acceptance — DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"done-bar (완료 기준)"},{"assertion_id":"AST-034","condition":"Decision status: documented-only","line_end":560,"line_start":560,"modality":"must","object":"단일 realm keycloak-patterns + 인증 패턴별 client 분리","predicate":"has_cardinality","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |","scope":"realm-client — DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"Keycloak realm/client 구성"},{"assertion_id":"AST-035","condition":"Decision status: documented-only","line_end":561,"line_start":561,"modality":"must_not","object":"commit·realm export 평문 (env var 주입만 허용)","predicate":"forbids","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |","scope":"secret-boundary — DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"confidential client secret"},{"assertion_id":"AST-036","condition":"Decision status: documented-only","line_end":562,"line_start":562,"modality":"must","object":"single-EC2 docker-compose","predicate":"uses","quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |","scope":"deployment — DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1","source_surface":"SURF-E8D20E10C18A20571CB3","subject":"AP1~AP4 E2E 구현 topology"},{"assertion_id":"AST-037","condition":"로그인 시작 시","line_end":352,"line_start":352,"modality":"observed","object":"PKCE code_verifier + code_challenge=SHA256(verifier)","predicate":"produces","quote":" SPA->>SPA: PKCE code_verifier 생성, code_challenge=SHA256(verifier)","scope":"AP1/P3A sequence","source_surface":"SURF-A16A70A9C563B8192465","subject":"AP1/P3A SPA (vanilla JS)"},{"assertion_id":"AST-038","condition":"자격 증명 유효 + code/code_verifier 교환 성공 시","line_end":359,"line_start":358,"modality":"observed","object":"access_token + id_token + refresh_token → SPA (browser)","predicate":"returns","quote":" SPA->>KC: POST /token (code + code_verifier)\n KC-->>SPA: 200 OK {access_token, id_token, refresh_token}","scope":"AP1/P3A sequence","source_surface":"SURF-A16A70A9C563B8192465","subject":"Keycloak /token endpoint"},{"assertion_id":"AST-039","condition":"Bearer access_token 수신 시","line_end":361,"line_start":361,"modality":"observed","object":"JWT (iss, aud, exp, signature with JWKS)","predicate":"validates","quote":" API->>API: JWT 검증 (iss, aud, exp, signature with JWKS)","scope":"AP1/P3A sequence","source_surface":"SURF-A16A70A9C563B8192465","subject":"Spring Boot Resource Server (API)"},{"assertion_id":"AST-040","condition":"aud claim mismatch 시","line_end":366,"line_start":365,"modality":"observed","object":"aud claim mismatch → 401 Unauthorized {error: invalid_token}","predicate":"has_failure_behavior","quote":" else aud claim mismatch\n API-->>SPA: 401 Unauthorized {error: invalid_token}","scope":"AP1/P3A sequence","source_surface":"SURF-A16A70A9C563B8192465","subject":"Spring Boot Resource Server (API)"},{"assertion_id":"AST-041","condition":"unconditional","line_end":375,"line_start":375,"modality":"observed","object":"AP1 (SPA-direct + Resource Server) 의 single-EC2 배포","predicate":"maps_to","quote":"> 위 P3A 시퀀스 = **AP1**(SPA-direct + Resource Server)의 single-EC2 배포. 아래는 나머지 3 패턴의 핵심 시퀀스(happy + error path).","scope":"hub taxonomy","source_surface":"SURF-A16A70A9C563B8192465","subject":"P3A 시퀀스"},{"assertion_id":"AST-042","condition":"unconditional","line_end":338,"line_start":338,"modality":"must","object":"templates/project-template.md §4 표준 준수 (autonumber, actor/participant 구분, alt/opt/loop, Note over)","predicate":"requires","quote":"> 토큰 교환 흐름은 패턴마다 다름. happy path + 주요 error path 함께. `templates/project-template.md` §4 표준 준수 — `autonumber`, `actor` vs `participant` 구분, alt/opt/loop 블록, `Note over` 비자명한 동작.","scope":"sequence-diagrams","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"핵심 시퀀스 다이어그램 (Mermaid)"},{"assertion_id":"AST-043","condition":"로그인 성공 시","line_end":395,"line_start":395,"modality":"must_not","object":"refresh_token 의 브라우저 전달 (백엔드만 보유)","predicate":"forbids","quote":" Note over BE: refresh_token 은 백엔드만 보유 (브라우저 미전달)","scope":"AP2","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP2 Backend (confidential client)"},{"assertion_id":"AST-044","condition":"로그인 성공 시","line_end":396,"line_start":396,"modality":"observed","object":"access_token 만 브라우저에 전달","predicate":"returns","quote":" BE-->>B: access_token 만 전달","scope":"AP2","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP2 Backend (confidential client)"},{"assertion_id":"AST-045","condition":"access_token 만료 시","line_end":399,"line_start":399,"modality":"observed","object":"access_token 만료 시 재발급은 백엔드 경유 (백엔드 보유 refresh_token 으로 refresh_grant)","predicate":"has_failure_behavior","quote":" else access_token 만료 (재발급은 백엔드 경유)","scope":"AP2","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP2 access_token 재발급"},{"assertion_id":"AST-046","condition":"로그인 성공 시","line_end":428,"line_start":427,"modality":"observed","object":"access/refresh token 저장 위치 = BFF session; 브라우저는 httpOnly SESSION cookie 만 (토큰 없음)","predicate":"other","quote":" KC-->>BFF: access/refresh token (BFF session 에 저장)\n BFF-->>B: Set-Cookie: SESSION (httpOnly) — 브라우저에 토큰 없음","scope":"AP3","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP3 BFF"},{"assertion_id":"AST-047","condition":"외부 사이트의 cookie 실린 상태변경 요청 위조 시","line_end":435,"line_start":435,"modality":"observed","object":"CSRF (cookie 자동첨부 악용) → 403 차단 (SameSite=Lax + CSRF token 검증 실패)","predicate":"has_failure_behavior","quote":" BFF-->>B: 403 (SameSite=Lax + CSRF token 검증 실패로 차단)","scope":"AP3","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP3 BFF CSRF 방어"},{"assertion_id":"AST-048","condition":"인증 성공 시","line_end":455,"line_start":455,"modality":"observed","object":"X-Forwarded-User: <sub> 헤더 → Backend API","predicate":"produces","quote":" Proxy->>API: GET /app + X-Forwarded-User: <sub>","scope":"AP4","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP4 oauth2-proxy"},{"assertion_id":"AST-049","condition":"인증 성공 경로","line_end":456,"line_start":456,"modality":"observed","object":"X-Forwarded-User 헤더 (헤더만으로 사용자 식별)","predicate":"consumes","quote":" API-->>Proxy: 200 OK (헤더만으로 사용자 식별)","scope":"AP4","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP4 Backend API"},{"assertion_id":"AST-050","condition":"ingress 우회 경로로 헤더 직접 주입 + network 격리 실패 시","line_end":460,"line_start":460,"modality":"observed","object":"network 격리 실패 시 X-Forwarded-User 위조 성공 (200)","predicate":"has_failure_behavior","quote":" API-->>User: 200 (❌ network 격리 실패 시 위조 성공)","scope":"AP4","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP4 헤더 신뢰 모델"},{"assertion_id":"AST-051","condition":"헤더 위조 우회 방어 시","line_end":461,"line_start":461,"modality":"must","object":"NetworkPolicy/SG 로 proxy→API 만 통과 (+ mTLS)","predicate":"requires","quote":" Note over API: 방어 = NetworkPolicy/SG 로 proxy→API 만 통과 (+ mTLS)","scope":"AP4","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"AP4 헤더 spoofing 방어"},{"assertion_id":"AST-052","condition":"unconditional","line_end":467,"line_start":467,"modality":"observed","object":"AP1~AP4 어디에도 코드 0줄로 얹히는 cross-cutting 변형; 상세 다이어그램은 sub-branch 로 위임","predicate":"other","quote":"(Google brokering 은 AP1~AP4 어디에도 코드 0줄로 얹히는 cross-cutting. 상세 다이어그램은 각 sub-branch — [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]], [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] — 에서.)","scope":"google-federation","source_surface":"SURF-BC8F97AEBA340930FBF1","subject":"Google IdP brokering"},{"assertion_id":"AST-053","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1; Status: planned","line_end":569,"line_start":569,"modality":"must","object":"완료 조건: Keycloak·PostgreSQL·nginx·Spring 4 containers healthy + admin console 접속","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-001 (feature-keycloak-docker-compose-stack)"},{"assertion_id":"AST-054","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1; Status: planned","line_end":570,"line_start":570,"modality":"must","object":"완료 조건: realm import + client 4개 등록 후 unauthenticated protected endpoint 401 + export 재현","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-002 (feature-keycloak-realm-client-export)"},{"assertion_id":"AST-055","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":571,"line_start":571,"modality":"must","object":"완료 조건: vanilla JS PKCE login·token 수령·protected API 200 재현","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-003 (feature-keycloak-vanilla-js-spa-pkce)"},{"assertion_id":"AST-056","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":572,"line_start":572,"modality":"must","object":"완료 조건: foreign audience token 수용 실패 재현 + validator 적용 후 401 검증","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-004 (feature-keycloak-spring-rs-audience-validator)"},{"assertion_id":"AST-057","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":573,"line_start":573,"modality":"must","object":"완료 조건: hostname 미설정 iss mismatch 401 + 설정 후 복구 log 존재","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-005 (feature-keycloak-iss-claim-hostname-mismatch)"},{"assertion_id":"AST-058","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":575,"line_start":575,"modality":"must","object":"완료 조건: refresh rotation + logout 후 session·token 무효화 검증","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-007 (feature-keycloak-refresh-rotation-and-logout)"},{"assertion_id":"AST-059","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":577,"line_start":577,"modality":"must","object":"완료 조건: browser access token 으로 API 200 + refresh token 은 network response 에 부재","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-009 (feature-keycloak-token-mediating-access-handoff)"},{"assertion_id":"AST-060","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1; Status: planned","line_end":578,"line_start":578,"modality":"must","object":"완료 조건: browser token 0개 + SESSION cookie 만으로 BFF proxy API 200 재현","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-010 (feature-keycloak-bff-oauth2login-session)"},{"assertion_id":"AST-061","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":579,"line_start":579,"modality":"must","object":"완료 조건: CSRF 재현 + SameSite·CSRF token 적용 후 403 검증","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-011 (feature-keycloak-bff-csrf-samesite-defense)"},{"assertion_id":"AST-062","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":580,"line_start":580,"modality":"must","object":"완료 조건: unauthenticated redirect + login 후 X-Forwarded-User backend 200 재현","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-012 (feature-keycloak-oauth2-proxy-oidc-flow)"},{"assertion_id":"AST-063","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":582,"line_start":582,"modality":"must","object":"완료 조건: forwarded-user spoofing 우회 재현 + network isolation 후 차단","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-014 (feature-keycloak-header-spoofing-defense)"},{"assertion_id":"AST-064","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1; Status: planned","line_end":583,"line_start":583,"modality":"must","object":"완료 조건: Google login·Keycloak user mapping 200 + application diff 0줄 검증","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-015 (feature-keycloak-idp-brokering-google-client)"},{"assertion_id":"AST-065","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","line_end":584,"line_start":584,"modality":"must","object":"완료 조건: unsafe auto-linking 재현 + Confirm Link Existing Account 로 차단","predicate":"has_threshold","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-016 (feature-keycloak-first-broker-login-flow)"},{"assertion_id":"AST-066","condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1; Status: in-progress","line_end":588,"line_start":588,"modality":"observed","object":"project governance hub 로서 AP1~AP4 taxonomy + child progress index 유지","predicate":"owns","quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-020` | `feature-keycloak-patterns` | project governance hub가 AP1~AP4 taxonomy와 child progress index를 유지한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1` | - | `in-progress` |","scope":"work-items","source_surface":"SURF-814F7A1C82815A20329F","subject":"WI-KEYCLOAK-PATTERNS-OVERVIEW-020 (feature-keycloak-patterns)"}],"candidate_manifest_sha256":"0a2131bcf5c9e5f1564a3bd5310628e48fee4586c047fcde046453ca7e6826b4","candidates":[{"assertion_a":"AST-002","assertion_b":"AST-003","candidate_id":"SEM-6600FD028F0536F1BBB8","grouping_key":{"condition":"unconditional","predicate":"","scope":"architecture-diagrams","subject":"시스템 아키텍처 다이어그램"},"rule_ids":["C6"]},{"assertion_a":"AST-002","assertion_b":"AST-017","candidate_id":"SEM-C12BB2CC8D81AE5F3B02","grouping_key":{"condition":"unconditional","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-003","assertion_b":"AST-017","candidate_id":"SEM-06927D2D95B5FE51256B","grouping_key":{"condition":"unconditional","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-024","assertion_b":"AST-054","candidate_id":"SEM-8ABCFA66BE51F5A4C1F2","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-026","assertion_b":"AST-058","candidate_id":"SEM-4274BE454C18F7CF0FBA","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-028","candidate_id":"SEM-C2D1F27AEED73049D9A8","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-029","candidate_id":"SEM-30B9E7D33FFA545C5948","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-030","candidate_id":"SEM-027E145D0852D3AF76F4","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-031","candidate_id":"SEM-08E69273935E8AE69926","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-032","candidate_id":"SEM-BD2655A34D19CB611762","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-033","candidate_id":"SEM-B59DDCB9A3CD31194DC6","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-034","candidate_id":"SEM-4A86B1E53282C1154B6B","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-035","candidate_id":"SEM-7861C489454350BF9256","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-027","assertion_b":"AST-036","candidate_id":"SEM-ECC537D88EC78CC8006D","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-029","candidate_id":"SEM-5AB1CBA83B8686FA2EAF","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-030","candidate_id":"SEM-91E11738FE0B355DD386","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-031","candidate_id":"SEM-F9C94B79786214E0BF9C","grouping_key":{"condition":"Decision status: active","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-032","candidate_id":"SEM-ECA70429077754077B4E","grouping_key":{"condition":"Decision status: active","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-033","candidate_id":"SEM-C91E07B44587E2620FE5","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-034","candidate_id":"SEM-13465D63C6333F625013","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-035","candidate_id":"SEM-6F908BD8B41521D1F7E9","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-028","assertion_b":"AST-036","candidate_id":"SEM-3550689E45780277A457","grouping_key":{"condition":"","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-030","candidate_id":"SEM-B1FDC5609351CC74BA47","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-031","candidate_id":"SEM-81F6FFE567F60B5AED6D","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-032","candidate_id":"SEM-CF54087EAC7257F48FB2","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-033","candidate_id":"SEM-2E6C6EF0501ACD0C2095","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-034","candidate_id":"SEM-7F9C1E85357DEAE05DF2","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-035","candidate_id":"SEM-9FC0118F245BE12ABD88","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-029","assertion_b":"AST-036","candidate_id":"SEM-E60E5D6E410302BA2F26","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-031","candidate_id":"SEM-FCBF0C6F8F1AA7C6AB9D","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-032","candidate_id":"SEM-F1FCB756FA2089313BF8","grouping_key":{"condition":"Decision status: active","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-033","candidate_id":"SEM-993925DFABEBC1395E2F","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-034","candidate_id":"SEM-981D8D5CF0F7E36D6B94","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-035","candidate_id":"SEM-ABCB12385E9D10811A03","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-030","assertion_b":"AST-036","candidate_id":"SEM-4BB6C874472588195E36","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-031","assertion_b":"AST-032","candidate_id":"SEM-C6A1E4B02BF1CA6403C1","grouping_key":{"condition":"Decision status: active","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-031","assertion_b":"AST-033","candidate_id":"SEM-3E4EBE8D3AD827746DF5","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-031","assertion_b":"AST-034","candidate_id":"SEM-451985592A2BC4B57BD0","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-031","assertion_b":"AST-035","candidate_id":"SEM-E74F9830C6779943937B","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-031","assertion_b":"AST-036","candidate_id":"SEM-DD318B0FB4D586A0C450","grouping_key":{"condition":"","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-032","assertion_b":"AST-033","candidate_id":"SEM-5FC8AF16156D9BE1FB85","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-032","assertion_b":"AST-034","candidate_id":"SEM-130A2C9DFD24353EDD01","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-032","assertion_b":"AST-035","candidate_id":"SEM-36B8FE738419BE993603","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-032","assertion_b":"AST-036","candidate_id":"SEM-DE4F32D95712451048BA","grouping_key":{"condition":"","predicate":"uses","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-033","assertion_b":"AST-034","candidate_id":"SEM-36FDB4CD1D00F4DCCAC3","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-033","assertion_b":"AST-035","candidate_id":"SEM-79F57273F9AEEC27A24C","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-033","assertion_b":"AST-036","candidate_id":"SEM-EC72C2BFDA3665CBCDC7","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-034","assertion_b":"AST-035","candidate_id":"SEM-C968D39A323154B91833","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-034","assertion_b":"AST-036","candidate_id":"SEM-072AB7C5B0E12E4C48E0","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-035","assertion_b":"AST-036","candidate_id":"SEM-58F14987B4F7F4DB6DC0","grouping_key":{"condition":"Decision status: documented-only","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-054","candidate_id":"SEM-9A893A31916C2B2372D5","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-053","assertion_b":"AST-055","candidate_id":"SEM-EFE3436782A81073958E","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-056","candidate_id":"SEM-83A16EC9CC302E3AEE60","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-057","candidate_id":"SEM-CCFA86027600F0CA3ACB","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-058","candidate_id":"SEM-2867A5DA24EFF29BD4A2","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-059","candidate_id":"SEM-8CA99FD2405B0FAC904B","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-060","candidate_id":"SEM-4D6CB80772357AADB155","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-061","candidate_id":"SEM-3E2BBDFBAE0E8ECCB804","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-062","candidate_id":"SEM-689F050305FFE9668E87","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-063","candidate_id":"SEM-02DAA4C91901862C1915","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-064","candidate_id":"SEM-773FF6B1D6C3C496B251","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-053","assertion_b":"AST-065","candidate_id":"SEM-8E709BBE4A6DAB00C118","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-055","candidate_id":"SEM-E20F56B315A0CCB78A9D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-054","assertion_b":"AST-056","candidate_id":"SEM-E9A6478A2A962231099A","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-057","candidate_id":"SEM-61470FBC755E24748122","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-058","candidate_id":"SEM-B152E1BFB403DFCFF87B","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-059","candidate_id":"SEM-9C85F9137EED871311BF","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-060","candidate_id":"SEM-CE2BF080EF836863B869","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-054","assertion_b":"AST-061","candidate_id":"SEM-6290836942C54715A57F","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-062","candidate_id":"SEM-FEB70E89535512652BBA","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-054","assertion_b":"AST-063","candidate_id":"SEM-E7BAA4817B076140D39E","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-064","candidate_id":"SEM-F6852FFC2DDC7BB69F6C","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-054","assertion_b":"AST-065","candidate_id":"SEM-6D4DA0A6C4D8D76074F2","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-055","assertion_b":"AST-056","candidate_id":"SEM-7CA8B3746E85BF0EF07F","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-057","candidate_id":"SEM-409EFE680E6625E0E647","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-058","candidate_id":"SEM-5F9FD8E1BAF705A3B0BC","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-059","candidate_id":"SEM-30719A07C7F61FB9505D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-060","candidate_id":"SEM-20C099EFCBC1AE9345E7","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-061","candidate_id":"SEM-F4976BBB8305D0F59B66","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-062","candidate_id":"SEM-4320955DAF02BAAD18A6","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-063","candidate_id":"SEM-268A7E9F16AAFD4A73C4","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-064","candidate_id":"SEM-AAB34C7CF23F73943A68","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-055","assertion_b":"AST-065","candidate_id":"SEM-E671193B4ACFDCC9CC0B","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-057","candidate_id":"SEM-0A20EA5886A73EA87865","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-058","candidate_id":"SEM-BF8E51815ADA1752FA10","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-059","candidate_id":"SEM-A6D74E3AAC98ECC7694A","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-060","candidate_id":"SEM-B377CB40FC3DE47BE38C","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-056","assertion_b":"AST-061","candidate_id":"SEM-3E4AE54159BF7C205150","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-062","candidate_id":"SEM-A2F6B82A2F4EE8DDD20D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-063","candidate_id":"SEM-ADF5040514D03E1C9257","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-064","candidate_id":"SEM-C80A7A24F309E2B0108D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-056","assertion_b":"AST-065","candidate_id":"SEM-F681E01B7F583AE1AF82","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-058","candidate_id":"SEM-39D42D30D5D274C99F98","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-059","candidate_id":"SEM-E01FD267AFB0D09E0738","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-060","candidate_id":"SEM-D0E622FBABD29A23C4A0","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-057","assertion_b":"AST-061","candidate_id":"SEM-6D8C2A7A87A67AEE2F16","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-062","candidate_id":"SEM-76E0C311AAB39F4CBE54","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-063","candidate_id":"SEM-AB59B5A9BDAFC5F92A25","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-064","candidate_id":"SEM-AA1BD7C986770975F340","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-057","assertion_b":"AST-065","candidate_id":"SEM-302DA97B5C7602EE50CC","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-058","assertion_b":"AST-059","candidate_id":"SEM-78654167EF9F7C98D04D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-058","assertion_b":"AST-060","candidate_id":"SEM-25AADE87D307CBFE88F7","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-058","assertion_b":"AST-061","candidate_id":"SEM-C4BD58078F748311B193","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-058","assertion_b":"AST-062","candidate_id":"SEM-B782C6339ACA9F1D51AB","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-058","assertion_b":"AST-063","candidate_id":"SEM-07789639D1F2362C0158","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-058","assertion_b":"AST-064","candidate_id":"SEM-AA32F2C3F7FC3F02C337","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-058","assertion_b":"AST-065","candidate_id":"SEM-52D49C58AE4A24CD66AC","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-059","assertion_b":"AST-060","candidate_id":"SEM-41AA033047A7985867B8","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-059","assertion_b":"AST-061","candidate_id":"SEM-11193138E582CDBEC453","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-059","assertion_b":"AST-062","candidate_id":"SEM-5E4F86088F70B54EBD9D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-059","assertion_b":"AST-063","candidate_id":"SEM-B00A2AC8177B81A0897D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-059","assertion_b":"AST-064","candidate_id":"SEM-B97FE5E1C7C10A2B6D2C","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-059","assertion_b":"AST-065","candidate_id":"SEM-6E30F13DCF572161F45D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-060","assertion_b":"AST-061","candidate_id":"SEM-6BEB4483F009D001B250","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-060","assertion_b":"AST-062","candidate_id":"SEM-FF29D5081AB7AC6E8498","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-060","assertion_b":"AST-063","candidate_id":"SEM-685937E388472B5CC281","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-060","assertion_b":"AST-064","candidate_id":"SEM-5AD16B1AE34A39B112A0","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-060","assertion_b":"AST-065","candidate_id":"SEM-9E1CB005F3C8240E6192","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-061","assertion_b":"AST-062","candidate_id":"SEM-40B714E5085052FBDF54","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-061","assertion_b":"AST-063","candidate_id":"SEM-B6560DA725D2F73B0919","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-061","assertion_b":"AST-064","candidate_id":"SEM-6C8CD9B870C32C134D3D","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-061","assertion_b":"AST-065","candidate_id":"SEM-02188E5BD26FA55AAFEF","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-062","assertion_b":"AST-063","candidate_id":"SEM-02B9C013D556A7742630","grouping_key":{"condition":"Applies DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1; Status: planned","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-062","assertion_b":"AST-064","candidate_id":"SEM-709F0ADDDE14D22AB176","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-062","assertion_b":"AST-065","candidate_id":"SEM-506DDEE44F16207746C4","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-063","assertion_b":"AST-064","candidate_id":"SEM-BC9802C0E8AF1C1FD3C8","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6"]},{"assertion_a":"AST-063","assertion_b":"AST-065","candidate_id":"SEM-DDFE44EB00FA97B10C57","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]},{"assertion_a":"AST-064","assertion_b":"AST-065","candidate_id":"SEM-D05CE0F928A505D989B6","grouping_key":{"condition":"","predicate":"has_threshold","scope":"work-items","subject":""},"rule_ids":["C6","C7"]}],"coverage":{"assertions":66,"candidate_pairs":128,"eligible_surfaces":9,"processed_surfaces":9},"document_sha256":"0c1f3d22fdc729c013ddd9420d4d3822187ff38fbd04a42c970414d6d47f4e06","explicit_blocking":[],"mode":"hub","ontology_sha256":"76d41a29233c830e1940ebb3244263e2b9bfb8dd6a01f2a1d1c51ce5a1b10468","output_schema":"semantic-audit-result/v1","schema_version":"semantic-verdict-request/v1","subject":"raw/project-notes/keycloak-patterns-overview.md"},"audit_request_sha256":"fdfc5e2554cfe392375dab82dc59e093b22bc4995864a77781547c0de43a51db","audit_result":{"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"aad7521cee6370b7a"},"mode":"hub","request_sha256":"fdfc5e2554cfe392375dab82dc59e093b22bc4995864a77781547c0de43a51db","schema_version":"semantic-audit-result/v1","subject":"raw/project-notes/keycloak-patterns-overview.md","verdicts":[{"candidate_id":"SEM-6600FD028F0536F1BBB8","evidence_a":{"line_end":129,"line_start":129,"quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용."},"evidence_b":{"line_end":129,"line_start":129,"quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용."},"proof_manifest":null,"rationale":"draw.io 사용 강제와 Mermaid graph TD 금지는 같은 문장에서 나온 동일 규칙의 양면으로, 서로 호환되는 제약을 보완한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C12BB2CC8D81AE5F3B02","evidence_a":{"line_end":129,"line_start":129,"quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용."},"evidence_b":{"line_end":328,"line_start":328,"quote":"> 신 primary 축의 AP2·AP3 은 기존 6 `.drawio`(P1A~P3B = AP1 배포 변형 + AP4)에 대응 아키텍처 다이어그램이 없다. **needs-diagram** — 사용자가 draw.io 로 작성 후 아래 백틱을 풀어 활성 임베드로 전환한다. (미작성 파일을 활성 임베드로 두면 9a 린터가 `BROKEN_LINK` 로 잡으므로 placeholder 는 백틱 코드로 비활성.)"},"proof_manifest":null,"rationale":"아키텍처 다이어그램 도구 규칙과 P1A~P3B→AP 축 대응 주장은 서로 간섭 없는 독립 주장이다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-06927D2D95B5FE51256B","evidence_a":{"line_end":129,"line_start":129,"quote":"> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용."},"evidence_b":{"line_end":328,"line_start":328,"quote":"> 신 primary 축의 AP2·AP3 은 기존 6 `.drawio`(P1A~P3B = AP1 배포 변형 + AP4)에 대응 아키텍처 다이어그램이 없다. **needs-diagram** — 사용자가 draw.io 로 작성 후 아래 백틱을 풀어 활성 임베드로 전환한다. (미작성 파일을 활성 임베드로 두면 9a 린터가 `BROKEN_LINK` 로 잡으므로 placeholder 는 백틱 코드로 비활성.)"},"proof_manifest":null,"rationale":"Mermaid 금지 규칙과 hub taxonomy 대응 주장은 상호 충돌 지점이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-8ABCFA66BE51F5A4C1F2","evidence_a":{"line_end":508,"line_start":508,"quote":"- **Admin REST API 자동화** — realm/client export·import 를 코드로 (`feature-keycloak-realm-client-export` 씨앗 존재)."},"evidence_b":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"proof_manifest":null,"rationale":"deferred 조건은 server-side 심화 트랙의 'Admin REST API 자동화(export·import 코드화)'이고 WI-002의 planned bar는 baseline realm import/client 등록/export 재현이므로, 조건(deferred 트랙 vs 현 phase work-item)이 차이를 설명하며 branch 스케줄 SSOT는 WI registry로 명시돼 있다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-4274BE454C18F7CF0FBA","evidence_a":{"line_end":515,"line_start":515,"quote":"- **Logout / session termination (front/back-channel)**: AP3 BFF session 종료 · AP1 토큰 만료로 부분 커버. 통합 logout 흐름은 기존 씨앗 `feature-keycloak-refresh-rotation-and-logout` 존재 — deferred."},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"deferred 대상은 '통합 front/back-channel logout 흐름'이고 WI-007의 planned bar는 refresh rotation+기본 logout session·token 무효화 검증으로, 조건(통합 흐름 기준 deferred vs 현 phase 부분 커버·WI registry SSOT)이 차이를 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-C2D1F27AEED73049D9A8","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"proof_manifest":null,"rationale":"AP1 PKCE 결정은 AP1~AP4 분류축의 한 요소를 상세화하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-30B9E7D33FFA545C5948","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"proof_manifest":null,"rationale":"AP3 BFF 세션 결정은 분류축의 AP3 요소를 상세화하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-027E145D0852D3AF76F4","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"proof_manifest":null,"rationale":"AP2 token-mediating 결정은 분류축의 AP2 요소를 상세화하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-08E69273935E8AE69926","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"proof_manifest":null,"rationale":"AP4 forwardauth 결정은 분류축의 AP4 요소를 상세화하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-BD2655A34D19CB611762","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"proof_manifest":null,"rationale":"Google brokering 결정은 분류축이 정의한 cross-cutting 변형을 상세화한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B59DDCB9A3CD31194DC6","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"분류축과 done-bar는 서로 다른 관심사(taxonomy vs acceptance)의 무충돌 결정이다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-4A86B1E53282C1154B6B","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"인증 패턴별 client 분리는 AP1~AP4 분류축을 전제로 이를 지원하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-7861C489454350BF9256","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"분류축과 secret 경계 규칙은 독립 관심사로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-ECC537D88EC78CC8006D","evidence_a":{"line_end":553,"line_start":553,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 배포 결정은 분류축이 cross-cutting으로 분류한 배포 변형을 구체화한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-5AB1CBA83B8686FA2EAF","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"proof_manifest":null,"rationale":"토큰 위치 차이(브라우저 public client vs backend session)는 AP1 vs AP3라는 패턴 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-91E11738FE0B355DD386","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"proof_manifest":null,"rationale":"토큰 획득 주체 차이(브라우저 직접 PKCE vs backend 획득 후 access token handoff)는 AP1 vs AP2 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-F9C94B79786214E0BF9C","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"proof_manifest":null,"rationale":"인증 통합 메커니즘 차이(app 내 PKCE public client vs edge oauth2-proxy)는 AP1 vs AP4 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-ECA70429077754077B4E","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"proof_manifest":null,"rationale":"Google brokering은 AP1 위에 코드 0줄로 얹히는 호환 cross-cutting 변형이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C91E07B44587E2620FE5","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"done-bar는 AP1 구현 branch(WI-003/004/005)에 적용되는 호환 수용 기준을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-13465D63C6333F625013","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"패턴별 client 분리(spa-public)는 AP1 public client 결정을 지원하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-6F908BD8B41521D1F7E9","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"AP1은 public client라 confidential client secret 규칙과 적용 대상이 겹치지 않아 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-3550689E45780277A457","evidence_a":{"line_end":554,"line_start":554,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 topology는 AP1 E2E가 수행될 호환 배포 단계를 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B1FDC5609351CC74BA47","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"proof_manifest":null,"rationale":"브라우저 토큰 노출 차이(cookie만 vs access token 전달)는 AP3 vs AP2 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-81F6FFE567F60B5AED6D","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"proof_manifest":null,"rationale":"BFF session cookie 방식 vs edge proxy 위임 방식의 차이는 AP3 vs AP4 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-CF54087EAC7257F48FB2","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"proof_manifest":null,"rationale":"Google brokering은 AP3에 얹히는 호환 cross-cutting 변형이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-2E6C6EF0501ACD0C2095","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"done-bar는 AP3 구현에 적용되는 호환 수용 기준이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-7F9C1E85357DEAE05DF2","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"bff-confidential client 분리는 AP3 결정을 지원하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-9FC0118F245BE12ABD88","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"AP3의 confidential client secret에 env var 주입·평문 금지 제약이 호환되게 적용된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E60E5D6E410302BA2F26","evidence_a":{"line_end":555,"line_start":555,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 topology는 AP3 E2E의 호환 배포 단계를 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-FCBF0C6F8F1AA7C6AB9D","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"proof_manifest":null,"rationale":"backend 토큰 중개 vs edge 프록시 헤더 위임 차이는 AP2 vs AP4 scope 차이가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-F1FCB756FA2089313BF8","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"proof_manifest":null,"rationale":"Google brokering은 AP2에 얹히는 호환 cross-cutting 변형이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-993925DFABEBC1395E2F","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"done-bar는 AP2 구현에 적용되는 호환 수용 기준이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-981D8D5CF0F7E36D6B94","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"token-mediating-confidential client 분리는 AP2 결정을 지원하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-ABCB12385E9D10811A03","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"AP2 confidential client secret에 secret 경계 규칙이 호환되게 적용된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-4BB6C874472588195E36","evidence_a":{"line_end":556,"line_start":556,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 topology는 AP2 E2E의 호환 배포 단계를 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C6A1E4B02BF1CA6403C1","evidence_a":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"evidence_b":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"proof_manifest":null,"rationale":"Google brokering은 AP4 forwardauth 위에 얹히는 호환 cross-cutting 변형이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-3E4EBE8D3AD827746DF5","evidence_a":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"done-bar는 AP4 구현에 적용되는 호환 수용 기준이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-451985592A2BC4B57BD0","evidence_a":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"edge-proxy client 분리는 AP4 결정을 지원하는 호환 세부다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E74F9830C6779943937B","evidence_a":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"AP4 프록시 결정과 confidential secret 경계 규칙은 명시된 상호작용 없이 양립한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-DD318B0FB4D586A0C450","evidence_a":{"line_end":557,"line_start":557,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 topology는 AP4 E2E의 호환 배포 단계를 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-5FC8AF16156D9BE1FB85","evidence_a":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"evidence_b":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"proof_manifest":null,"rationale":"done-bar는 federation 검증 항목(WI-016 등)에도 적용되는 호환 수용 기준이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-130A2C9DFD24353EDD01","evidence_a":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"단일 realm/패턴별 client 분리와 IdP brokering은 충돌 없이 양립한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-36B8FE738419BE993603","evidence_a":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"brokering 결정과 client secret 경계 규칙은 독립 관심사로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-DE4F32D95712451048BA","evidence_a":{"line_end":558,"line_start":558,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"federation 변형과 single-EC2 topology는 무충돌 독립 결정이다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-36FDB4CD1D00F4DCCAC3","evidence_a":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"evidence_b":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"proof_manifest":null,"rationale":"수용 기준과 realm/client cardinality는 독립 속성으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-79F57273F9AEEC27A24C","evidence_a":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"done-bar와 secret 경계 규칙은 독립 관심사로 양립한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-EC72C2BFDA3665CBCDC7","evidence_a":{"line_end":559,"line_start":559,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"single-EC2 topology가 done-bar의 E2E 검증이 수행될 호환 단계를 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C968D39A323154B91833","evidence_a":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"evidence_b":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"proof_manifest":null,"rationale":"secret 규칙이 realm/client 구성의 export(평문 금지)에 호환 제약을 추가한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-072AB7C5B0E12E4C48E0","evidence_a":{"line_end":560,"line_start":560,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"realm/client 구성과 배포 topology는 독립 속성으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-58F14987B4F7F4DB6DC0","evidence_a":{"line_end":561,"line_start":561,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |"},"evidence_b":{"line_end":562,"line_start":562,"quote":"| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |"},"proof_manifest":null,"rationale":"secret 경계와 배포 topology는 독립 속성으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-9A893A31916C2B2372D5","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"proof_manifest":null,"rationale":"WI-001 스택 기동은 WI-002 realm 구성의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-EFE3436782A81073958E","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"서로 다른 branch의 독립 완료 조건으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-83A16EC9CC302E3AEE60","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-CCFA86027600F0CA3ACB","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-2867A5DA24EFF29BD4A2","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-8CA99FD2405B0FAC904B","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-4D6CB80772357AADB155","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-3E2BBDFBAE0E8ECCB804","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-689F050305FFE9668E87","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-02DAA4C91901862C1915","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-773FF6B1D6C3C496B251","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-8E709BBE4A6DAB00C118","evidence_a":{"line_end":569,"line_start":569,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-E20F56B315A0CCB78A9D","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"WI-002 realm 구성은 WI-003 PKCE 로그인의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E9A6478A2A962231099A","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-61470FBC755E24748122","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B152E1BFB403DFCFF87B","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-9C85F9137EED871311BF","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-CE2BF080EF836863B869","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"WI-002는 WI-010 BFF 세션 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-6290836942C54715A57F","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-FEB70E89535512652BBA","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"WI-002는 WI-012 oauth2-proxy 흐름의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E7BAA4817B076140D39E","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-F6852FFC2DDC7BB69F6C","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-6D4DA0A6C4D8D76074F2","evidence_a":{"line_end":570,"line_start":570,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-7CA8B3746E85BF0EF07F","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"WI-003은 WI-004 audience validator 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-409EFE680E6625E0E647","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"WI-003은 WI-005 iss mismatch 재현의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-5F9FD8E1BAF705A3B0BC","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-30719A07C7F61FB9505D","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"AP1(WI-003)과 AP2(WI-009) 각각의 branch 완료 조건으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-20C099EFCBC1AE9345E7","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"브라우저 토큰 수령(AP1 WI-003) vs 브라우저 token 0개(AP3 WI-010)의 차이는 패턴 scope(AP1 vs AP3)가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-F4976BBB8305D0F59B66","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-4320955DAF02BAAD18A6","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-268A7E9F16AAFD4A73C4","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-AAB34C7CF23F73943A68","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"WI-003은 WI-015 Google brokering 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E671193B4ACFDCC9CC0B","evidence_a":{"line_end":571,"line_start":571,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-0A20EA5886A73EA87865","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"같은 AP1 계열의 서로 다른 함정(aud vs iss) 검증으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-BF8E51815ADA1752FA10","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"WI-004는 WI-007 rotation/logout 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-A6D74E3AAC98ECC7694A","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B377CB40FC3DE47BE38C","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-3E4AE54159BF7C205150","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-A2F6B82A2F4EE8DDD20D","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-ADF5040514D03E1C9257","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-C80A7A24F309E2B0108D","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-F681E01B7F583AE1AF82","evidence_a":{"line_end":572,"line_start":572,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-39D42D30D5D274C99F98","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-E01FD267AFB0D09E0738","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-D0E622FBABD29A23C4A0","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-6D8C2A7A87A67AEE2F16","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-76E0C311AAB39F4CBE54","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-AB59B5A9BDAFC5F92A25","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-AA1BD7C986770975F340","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-302DA97B5C7602EE50CC","evidence_a":{"line_end":573,"line_start":573,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-78654167EF9F7C98D04D","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-25AADE87D307CBFE88F7","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-C4BD58078F748311B193","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B782C6339ACA9F1D51AB","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-07789639D1F2362C0158","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-AA32F2C3F7FC3F02C337","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-52D49C58AE4A24CD66AC","evidence_a":{"line_end":575,"line_start":575,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-41AA033047A7985867B8","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"브라우저 access token 보유(AP2 WI-009) vs 브라우저 token 0개(AP3 WI-010)의 차이는 패턴 scope(AP2 vs AP3)가 설명한다.","verdict":"CONTEXTUAL_VARIANT"},{"candidate_id":"SEM-11193138E582CDBEC453","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-5E4F86088F70B54EBD9D","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B00A2AC8177B81A0897D","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B97FE5E1C7C10A2B6D2C","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-6E30F13DCF572161F45D","evidence_a":{"line_end":577,"line_start":577,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-6BEB4483F009D001B250","evidence_a":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"proof_manifest":null,"rationale":"WI-010 BFF 세션은 WI-011 CSRF 방어 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-FF29D5081AB7AC6E8498","evidence_a":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"AP3(WI-010)와 AP4(WI-012) 각각의 branch 완료 조건으로 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-685937E388472B5CC281","evidence_a":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-5AD16B1AE34A39B112A0","evidence_a":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-9E1CB005F3C8240E6192","evidence_a":{"line_end":578,"line_start":578,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-40B714E5085052FBDF54","evidence_a":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"evidence_b":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B6560DA725D2F73B0919","evidence_a":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-6C8CD9B870C32C134D3D","evidence_a":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-02188E5BD26FA55AAFEF","evidence_a":{"line_end":579,"line_start":579,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-02B9C013D556A7742630","evidence_a":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"proof_manifest":null,"rationale":"WI-012 proxy 흐름은 WI-014 spoofing 방어 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-709F0ADDDE14D22AB176","evidence_a":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-506DDEE44F16207746C4","evidence_a":{"line_end":580,"line_start":580,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-BC9802C0E8AF1C1FD3C8","evidence_a":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"evidence_b":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-DDFE44EB00FA97B10C57","evidence_a":{"line_end":582,"line_start":582,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"독립 branch 완료 조건 간 충돌이 없다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-D05CE0F928A505D989B6","evidence_a":{"line_end":583,"line_start":583,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |"},"evidence_b":{"line_end":584,"line_start":584,"quote":"| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |"},"proof_manifest":null,"rationale":"WI-015 brokering client는 WI-016 First Broker Login 차단 검증의 명시된 선행 단계로 호환된다.","verdict":"COMPLEMENTARY"}]},"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"aad7521cee6370b7a"},"auditor_contract_sha256":"1cbc67c27e5183a272687f635e3682a26d56785a1cf144da562eb7edd8f6cbce","coverage":{"candidate_pairs":128,"dropped_pairs":0,"eligible_surfaces":9,"processed_pairs":128,"processed_surfaces":9},"document_id":"0d0f80e31b5f216f8511a295ffae4b8745ab8821dfc6e49c6a234f006b93c33f","document_sha256":"0c1f3d22fdc729c013ddd9420d4d3822187ff38fbd04a42c970414d6d47f4e06","findings":{"blocking":0,"readiness_blocking":0,"verified":0},"mode":"hub","ontology_sha256":"5d601b96f0ca4d75eea89e9086c38e3acf2c4f0c7833846ef58c6a5719603126","policy_sha256":"0465598e9c1c4f2c3400ba51bf4719aa4890d60d56dc83304fb2224e660562b7","proof_manifest_sha256":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","schema_version":"semantic-certificate/v1","semantic_audit_sha256":"14c157336e8be9508da9609eae8b1f0586a054fde11b34fe50e46090b6c47b0c","subject":"raw/project-notes/keycloak-patterns-overview.md","typed_contract_graph_sha256":"481fc3335a494c454ab13ad1d6a6ddccdec99aa8e083f6720ff8886bfa19cc89","verdict":"PASS"}