refactor: 문서 개선 중
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
# keycloak 상세 리뷰 — 2026-09-18
|
||||
|
||||
## 판정
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
자동 품질 게이트는 대부분 통과하지만, OAuth/OIDC 의미를 일반화하는 과정에서 기술적 범위 오류가 남아 있다. 특히 AP2/AP3 PKCE 범위, Keycloak refresh-token 재사용 설정, public/confidential client와 token endpoint caller의 관계는 수정이 필요하다.
|
||||
|
||||
현재 source repository:
|
||||
|
||||
`/home/donghyeon/workspace/keycloak-pattern`
|
||||
|
||||
는 이 머신에 존재하지 않는다. 따라서 live source reconciliation은 **UNVERIFIABLE**이다. 현재 판정은 커밋된 SSOT/Record/evidence와 외부 표준·제품 문서 사이의 의미 검토 결과이며, 실제 source code와의 최신 일치 여부를 PASS로 간주하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 현재 통과한 자동 검증
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| Tech Log Tree | PASS — topics 1, nodes 24, written 24, unwritten 0 |
|
||||
| Project Layout | PASS — error 0, warn 10 |
|
||||
| Record Audit | PASS — 문제 없음 |
|
||||
| Figure Text | PASS — SVG 10, 문장 오류 0 |
|
||||
| Figure Provenance | PASS — error 0 |
|
||||
| Figure Overlap | PASS — overlap 0 |
|
||||
| Required Content | PASS — records 24, error 0 |
|
||||
| SSOT Facts | PASS — error 0 |
|
||||
| Natural Prose | PASS — 24/24 hard fail 0 |
|
||||
| Voice | PASS — 24/24 OK |
|
||||
| Command Pedagogy | PASS — records 24, findings 0 |
|
||||
| Standalone pipeline baseline | PASS — exit 0 |
|
||||
| Full unittest regression baseline | PASS — 391 tests, skipped 14 |
|
||||
| git diff --check baseline | PASS |
|
||||
| Live source reconciliation | **UNVERIFIABLE** — source repository 없음 |
|
||||
|
||||
주의: 위 PASS는 자동 검사기가 검사하는 계약에 대한 결과다. 기술 의미의 과도한 일반화나 문장 주어 누락까지 자동으로 잡는 것은 아니다.
|
||||
|
||||
---
|
||||
|
||||
# Findings
|
||||
|
||||
## K01 — HIGH — AP3 설명 직후 AP2 미검증 문장이 붙어 PKCE 의미가 충돌한다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-authorization-code-and-pkce.md`
|
||||
|
||||
현재 120~122행의 흐름:
|
||||
|
||||
- 120행: AP3의 `bff-confidential`은 `client_secret_basic` + PKCE S256을 함께 사용한다고 설명한다.
|
||||
- 122행: 주어 없이 “클라이언트 설정에 S256 강제 속성이 없고 테스트도 challenge를 검사하지 않는다”고 이어진다.
|
||||
|
||||
이 마지막 문장은 SSOT 기준으로 **AP2**를 가리킨다. 실제 SSOT는 AP2에 대해 다음처럼 범위를 제한한다.
|
||||
|
||||
- AP2 client 설정에는 S256을 강제하는 속성이 없음
|
||||
- AP2 E2E도 authorization request challenge를 검사하지 않음
|
||||
- 따라서 AP2는 Authorization Code confidential client까지 확인했고 PKCE S256은 확인하지 않음
|
||||
|
||||
Record에서는 `AP2`라는 주어가 빠져 바로 앞 AP3 설명을 뒤집는 문장처럼 읽힌다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
예:
|
||||
|
||||
> 반면 AP2의 `token-mediating-confidential`은 client 설정에서 S256을 강제하지 않고 AP2 E2E도 authorization request의 challenge를 검사하지 않는다. 따라서 AP2는 Authorization Code를 사용하는 confidential client라는 사실까지 확인했으며, PKCE S256이 고정되었다고 기록하지 않는다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- AP3 PKCE S256 = 확인된 사실
|
||||
- AP2 PKCE S256 = 미확인
|
||||
- 두 범위가 같은 Concept 안에서 주어가 분명하게 분리되어야 한다.
|
||||
|
||||
---
|
||||
|
||||
## K02 — HIGH — Keycloak refresh-token “재사용 허용”을 시간 창처럼 설명한다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-refresh-rotation-replica.md`
|
||||
|
||||
현재 99~103행:
|
||||
|
||||
> 재사용을 짧게 허용하면 ...
|
||||
>
|
||||
> 허용한 시간 안에는 훔친 refresh token이 ...
|
||||
|
||||
문제는 Keycloak의 `refreshTokenMaxReuse`가 **시간(duration) 설정이 아니라 정수형 reuse count**라는 점이다. Keycloak의 현재 API 모델도 `refreshTokenMaxReuse: Integer`로 노출한다. `Revoke Refresh Token`은 사용한 refresh token을 회전시키는 동작이며, timeout과 max reuse를 같은 “허용 시간”으로 설명하면 안 된다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
시간 표현을 제거하고 reuse count로 쓴다.
|
||||
|
||||
예:
|
||||
|
||||
> 재사용 허용 횟수를 늘리면 동일 refresh token의 추가 사용을 일정 횟수 받아들이도록 구성할 수 있다. 그만큼 탈취된 refresh token의 replay를 허용할 여지도 커진다. 현재 실험은 rotation + max reuse 0을 전제로 하므로 이 선택지는 제외한다.
|
||||
|
||||
정확한 Keycloak 26.7.0 동작을 source config와 runtime으로 다시 확인할 수 있을 때에는 실제 realm 값과 동시 refresh 결과를 별도 evidence로 남긴다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- `refreshTokenMaxReuse`를 시간 창처럼 설명하는 문장 0건
|
||||
- rotation / token lifespan / max reuse count를 서로 다른 설정으로 구분
|
||||
- replica race 결과는 계속 **미검증**으로 유지
|
||||
|
||||
---
|
||||
|
||||
## K03 — HIGH — “client type이 token endpoint caller를 결정한다”는 인과가 잘못됐다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-authorization-code-endpoints.md`
|
||||
|
||||
현재 40, 56행의 핵심:
|
||||
|
||||
> 보내는 쪽은 클라이언트 종류에 따라 서버이거나 브라우저
|
||||
>
|
||||
> Token Endpoint를 서버 안에서만 부르게 하려면 클라이언트 종류부터 confidential client로 정해야 한다.
|
||||
|
||||
이 프로젝트의 네 구현에서는 실제로:
|
||||
|
||||
- AP1 public SPA → browser가 token endpoint 호출
|
||||
- AP2/AP3/AP4 confidential component → server-side component가 code 교환
|
||||
|
||||
이라는 배치가 맞다.
|
||||
|
||||
하지만 이것은 **이 프로젝트의 아키텍처 배치**이지 OAuth의 client type 자체가 network caller를 결정한다는 뜻이 아니다.
|
||||
|
||||
OAuth의 public/confidential 구분은 authorization server에 대해 안전하게 client authentication을 수행하고 client credential의 기밀성을 유지할 수 있는지에 대한 분류다. 실제 token exchange를 browser에서 할지 server에서 할지는 callback/code-exchange ownership과 배포 구조가 정한다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
일반 규칙과 프로젝트 사실을 분리한다.
|
||||
|
||||
예:
|
||||
|
||||
> 이 프로젝트에서는 AP1 public SPA가 브라우저에서 token endpoint를 호출하고, AP2~AP4의 confidential component가 server-side에서 code를 교환한다. 다만 public/confidential client 구분 자체가 token endpoint의 호출 위치를 강제하는 것은 아니다. 서버 전용 교환은 callback과 code exchange를 server component가 소유하고, client credential이 browser로 노출되지 않도록 설계함으로써 만든다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- “client type → caller 위치” 직접 인과 제거
|
||||
- “현재 네 패턴에서의 배치”와 “OAuth 일반 규칙”을 별도 문장으로 구분
|
||||
|
||||
---
|
||||
|
||||
## K04 — MEDIUM — Public/Confidential 정의를 client secret 하나로 축소했다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-public-confidential-client.md`
|
||||
|
||||
현재 23, 41, 47, 51행 주변에서 반복적으로:
|
||||
|
||||
> 클라이언트 종류는 클라이언트 시크릿을 안전하게 보관할 수 있는지로 정한다.
|
||||
|
||||
이 설명은 **현재 프로젝트가 shared client secret을 사용한다는 범위**에서는 이해하기 쉽지만, 일반 OAuth Reference의 정의로는 너무 좁다.
|
||||
|
||||
RFC 6749의 기준은 client credential의 confidentiality 및 authorization server와의 secure client authentication 능력이다. confidential client 인증 수단은 shared secret만이 아니라 private key, mTLS 같은 방식도 가능하다. RFC 9700도 asymmetric client authentication을 권고한다.
|
||||
|
||||
또 38~39행의:
|
||||
|
||||
> PKCE를 쓸지, 클라이언트 시크릿으로 클라이언트를 인증할지 정할 수 있다
|
||||
|
||||
는 표현은 둘 중 하나를 고르는 것처럼 읽힐 수 있다. 같은 문서 뒤쪽에서 confidential client도 PKCE를 함께 쓸 수 있다고 올바르게 설명하고 있으므로 앞부분도 그 의미와 맞춰야 한다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
일반 정의:
|
||||
|
||||
> OAuth client type은 authorization server에 대해 client credential을 안전하게 보호하고 신뢰할 수 있는 client authentication을 수행할 수 있는지로 구분한다.
|
||||
|
||||
프로젝트 scope:
|
||||
|
||||
> 이 프로젝트의 confidential client들은 `client_secret_basic`을 사용하므로, 여기서는 server-side client secret 보관 여부가 그 차이를 가장 직접적으로 보여 준다.
|
||||
|
||||
PKCE:
|
||||
|
||||
> PKCE와 client authentication은 대체 관계가 아니다. public client에는 PKCE가 필수적인 보호 수단이고, confidential client에도 함께 적용할 수 있다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- 일반 정의에서 “client secret만”이 client type의 유일한 기준처럼 읽히지 않음
|
||||
- 현재 프로젝트의 `client_secret_basic` 사용은 별도 scope로 명시
|
||||
- PKCE vs client authentication을 양자택일처럼 읽히게 하는 문장 제거
|
||||
|
||||
---
|
||||
|
||||
## K05 — MEDIUM — “남는 선택지는 하나”가 문서 범위를 넘어 일반화된다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-pattern-selection.md`
|
||||
|
||||
현재 94행:
|
||||
|
||||
> 브라우저에 토큰을 둘 수 없고 서버가 API를 조합해야 하면 남는 선택지는 하나다.
|
||||
|
||||
이 문서가 비교하는 **AP1~AP4 네 패턴 안에서만** 보면 의도는 AP3 BFF다. 그러나 Reference 문장 자체는 OAuth/OIDC 전체 설계 공간에서 유일한 답인 것처럼 읽힌다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
> 이 문서에서 비교하는 네 패턴만 놓고 보면, 브라우저에 OAuth token을 둘 수 없고 server-side API composition이 필요할 때 AP3 BFF가 해당 조건을 만족한다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- 선택의 유일성이 “이 문서의 네 패턴” 범위로 제한됨
|
||||
- 외부 구조 전체에 대한 일반론으로 확대되지 않음
|
||||
|
||||
---
|
||||
|
||||
## K06 — MEDIUM — CSRF token을 “사용자 의도 증명”으로 설명한다
|
||||
|
||||
대표 파일:
|
||||
|
||||
- `concept/concept-cookie-auth-csrf.md`
|
||||
- `concept/concept-browser-credential-storage.md`
|
||||
- `reference/reference-bff-auth-design.md`
|
||||
- `reference/reference-token-vs-session.md`
|
||||
- `question/question-edge-authorization-scope.md`
|
||||
- SSOT `final/document.md`에도 같은 표현이 있음
|
||||
|
||||
현재 반복 표현:
|
||||
|
||||
> 상태를 바꾸는 요청이 사용자가 의도한 것인지 확인한다.
|
||||
|
||||
CSRF token이 증명하는 것은 인간 사용자의 실제 의도 자체가 아니다. 예상한 anti-CSRF token을 가진 클라이언트 문맥에서 요청이 만들어졌는지를 검사해 cross-site forged request를 구분하는 방어선에 가깝다.
|
||||
|
||||
현재 문서 자체도 뒤쪽에서 **same-origin XSS가 XSRF-TOKEN을 읽고 사용자의 세션으로 요청할 수 있다**고 정확하게 설명한다. 따라서 “사용자 의도 확인” 표현은 같은 문서의 XSS 설명과도 의미적으로 충돌한다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
다음처럼 바꾼다.
|
||||
|
||||
> 브라우저가 session cookie를 자동 첨부하므로, state-changing request에는 공격 사이트가 cookie만 이용해 만든 cross-site forged request와 애플리케이션이 anti-CSRF token을 가지고 만든 요청을 구분하는 CSRF 검증이 필요하다.
|
||||
|
||||
또는 더 짧게:
|
||||
|
||||
> CSRF token은 cookie가 자동으로 붙는 요청에 별도의 검증 값을 요구해 cross-site forged request를 차단한다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- “사용자의 실제 의도 자체를 증명한다”는 의미 제거
|
||||
- SameSite / CSRF token / same-origin XSS 역할이 서로 구분됨
|
||||
- SSOT와 파생 Record를 함께 수정하여 semantic consistency 유지
|
||||
|
||||
---
|
||||
|
||||
# SVG / Figure 검토
|
||||
|
||||
현재 10개 SVG는 자동 검증상 다음을 통과한다.
|
||||
|
||||
- Figure Text PASS
|
||||
- Figure Provenance PASS
|
||||
- Figure Overlap PASS
|
||||
|
||||
직접 VizSpec의 title, actor, edge label도 확인했다. 현재 내용과 그림의 **주제 선택 자체는 적절하다**.
|
||||
|
||||
특히 다음 연결은 독자 설명 흐름에 유효하다.
|
||||
|
||||
- AP1: browser memory ↔ Keycloak ↔ Resource Server
|
||||
- AP2: mediator custody와 browser API caller 분리
|
||||
- AP3: browser session → BFF → authorized client → Bearer
|
||||
- AP3 CSRF: masked body vs raw cookie/header
|
||||
- AP4: Nginx auth_request → oauth2-proxy → trusted upstream header
|
||||
- login/API phase split: credential owner와 API caller가 다른 축이라는 설명
|
||||
|
||||
다만 Project Layout에는 warn 10건이 남아 있다.
|
||||
|
||||
- 7건: **SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다**
|
||||
- 3건: techviz 도구 부재로 **전체 SSOT hash를 대조하지 못함**, context snapshot은 일치
|
||||
|
||||
따라서 SVG를 현재 당장 “틀린 그림”으로 판정하지는 않는다. 하지만 K01~K06 수정으로 주변 문맥이 다시 바뀌므로 수정 후에는 10개 figure context/provenance를 다시 검증해야 한다. 특히 PKCE 범위를 건드리는 경우 `login-api-phase-split`과 AP2 관련 그림의 label이 AP2 PKCE를 암시하지 않는지 다시 확인한다.
|
||||
|
||||
---
|
||||
|
||||
# Evidence / Provenance 검토
|
||||
|
||||
좋은 점:
|
||||
|
||||
1. 최신 실행 성적표와 “커밋된 테스트가 확인하도록 정의한 acceptance contract”를 구분한다.
|
||||
2. AP3의 `browserTokenCount: 0` self-report만으로 tokenless browser를 증명하지 않고 browser network/Web Storage를 별도 evidence로 둔다.
|
||||
3. AP4에서 exact `/api/edge` 401과 general location redirect의 범위를 구분한다.
|
||||
4. mock OIDC provider 검증과 실제 Google account/public HTTPS callback 검증을 분리한다.
|
||||
5. AP2 PKCE를 SSOT에서는 미확인으로 유지한다.
|
||||
6. source repository 부재 시 live reconciliation을 꾸며낼 근거가 없다.
|
||||
|
||||
남은 제약:
|
||||
|
||||
- source repository가 없으므로 현재 Record의 source-derived 세부 사실을 live code와 재대조할 수 없다.
|
||||
- historical `verifiedOn`과 source revision은 현재 runtime 실행을 뜻하지 않는다.
|
||||
- figure context warning 10건은 hard failure는 아니지만 수정 완료 후 다시 닫아야 한다.
|
||||
|
||||
---
|
||||
|
||||
# SSOT ↔ Record semantic consistency
|
||||
|
||||
현재 가장 명확한 mismatch는 K01이다.
|
||||
|
||||
SSOT:
|
||||
|
||||
> AP2 client 설정에는 S256을 강제하는 속성이 없고 AP2 E2E도 challenge를 검사하지 않는다.
|
||||
|
||||
Record:
|
||||
|
||||
> AP3 PKCE S256 설명 직후 주어 없이 “S256을 강제하지 않고 검사하지 않았다”고 적음.
|
||||
|
||||
따라서 Record가 SSOT의 scope를 잃었다.
|
||||
|
||||
K02, K03, K04, K05는 SSOT에서 직접 강하게 주장한 내용이라기보다 Reference/Question으로 확장하는 과정에서 범위가 넓어진 문제다.
|
||||
|
||||
K06은 반대로 SSOT와 여러 Record가 같은 부정확한 표현을 공유하므로 **SSOT부터 정정한 뒤 파생 Record를 같이 고쳐야 한다.**
|
||||
|
||||
---
|
||||
|
||||
# 수정 우선순위
|
||||
|
||||
## 1차 — 반드시 수정
|
||||
|
||||
1. K01 AP2/AP3 PKCE scope
|
||||
2. K02 refresh max reuse를 time window로 표현한 부분
|
||||
3. K03 client type과 token endpoint caller의 잘못된 인과
|
||||
|
||||
## 2차 — 같은 수정 묶음에서 처리
|
||||
|
||||
4. K04 public/confidential 일반 정의 scope
|
||||
5. K05 “남는 선택지는 하나” scope
|
||||
6. K06 CSRF “사용자 의도” 표현
|
||||
|
||||
## 3차 — 수정 후 재검증
|
||||
|
||||
1. stale phrase 검색
|
||||
2. SSOT ↔ Record semantic consistency
|
||||
3. Natural prose / Voice
|
||||
4. Tree / Layout
|
||||
5. Figure Text / Figure Provenance / Figure Overlap
|
||||
6. Required Content / SSOT Facts
|
||||
7. Command Pedagogy
|
||||
8. `git diff --check`
|
||||
9. standalone `verify-pipeline.py`
|
||||
10. full unittest regression
|
||||
|
||||
pipeline과 unittest는 병렬 실행하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 외부 기술 근거
|
||||
|
||||
검토 시 다음 공식 문서/표준을 기준으로 의미를 대조했다.
|
||||
|
||||
- RFC 6749 §2.1 Client Types
|
||||
https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
||||
- RFC 9700 OAuth 2.0 Security Best Current Practice
|
||||
https://www.rfc-editor.org/rfc/rfc9700.html
|
||||
- Keycloak Server Administration Guide 26.7.x — Refresh token rotation
|
||||
https://www.keycloak.org/docs/latest/server_admin/
|
||||
- Keycloak `RealmRepresentation.refreshTokenMaxReuse` API
|
||||
https://www.keycloak.org/docs-api/latest/javadocs/org/keycloak/representations/idm/RealmRepresentation.html
|
||||
- Spring Security `OAuth2AuthorizedClientService` API
|
||||
https://docs.spring.io/spring-security/reference/api/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientService.html
|
||||
- oauth2-proxy session options
|
||||
https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview/
|
||||
|
||||
RFC 6749는 confidential client를 shared secret 자체가 아니라 client credentials의 기밀성 유지 또는 다른 안전한 client authentication 능력으로 정의한다. RFC 9700은 public client에 PKCE를 요구하고 confidential client에도 PKCE를 권고하며, asymmetric client authentication도 권고한다. Keycloak의 current API는 `refreshTokenMaxReuse`를 Integer로 표현한다.
|
||||
|
||||
---
|
||||
|
||||
# 최종 판정
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
자동 검사기는 통과했지만 K01~K03은 기술 의미를 바꾸는 문제이므로 완료로 볼 수 없다. K04~K06도 Reference/Concept의 일반화 범위를 바로잡아야 한다.
|
||||
|
||||
수정 후에는 대상 문서만 다시 읽는 것으로 끝내지 않고, SSOT/Record/figure context와 전체 pipeline + unittest regression까지 다시 확인해야 한다.
|
||||
@@ -0,0 +1,738 @@
|
||||
# Keycloak 수정 반영 재검토 리뷰
|
||||
|
||||
- 검토일: 2026-09-19
|
||||
- 대상 프로젝트: `docs/keycloak`
|
||||
- 검토 기준: 7단계 TechLog 하네스
|
||||
- 최종 판정: **아직 더 봐야댐**
|
||||
|
||||
---
|
||||
|
||||
## 1. 결론
|
||||
|
||||
직전 리뷰에서 지적한 **문서 내용과 SVG 문제는 이번 수정에서 모두 정상적으로 반영됐다.**
|
||||
|
||||
특히 다음 항목은 현재 기준으로 닫혔다.
|
||||
|
||||
- Bearer JWT validation chain SVG 누락
|
||||
- IdP Brokering boundary/flow SVG 누락
|
||||
- `reference-pattern-selection.md`의 작성 지시형 문장
|
||||
- Public / Confidential Client 정의 반복 및 shared-secret 중심 과도한 일반화
|
||||
- IdP Federation Reference의 과도하게 긴 문장
|
||||
- BFF state-store Question의 `여기까지` 같은 문서 진행 메타 표현
|
||||
- Multi-instance Question의 지나치게 기계적인 선택지 리듬
|
||||
- Public / Confidential Client의 일반 정의와 프로젝트 구현 사실의 범위 혼동
|
||||
- Token Endpoint 섹션 제목의 과도한 일반화
|
||||
- Authorization Endpoint의 referrer 설명에서 Referrer-Policy 조건 누락
|
||||
|
||||
자동 검사도 모두 통과했다.
|
||||
|
||||
다만 현재 수정 작업에 대응하는 **새 run ledger가 없다.**
|
||||
|
||||
이 프로젝트의 리뷰 기준은 결과물만 보는 것이 아니라, 문서를 다음 7단계 절차로 작성·수정했는지까지 확인하는 것이다.
|
||||
|
||||
1. S1 — 코드베이스 → SSOT
|
||||
2. S2 — SSOT → 분해 계약
|
||||
3. S3 — 글감 → 기록
|
||||
4. S4 — 기록 → 그림
|
||||
5. S5 — AI 티 제거
|
||||
6. S6 — 일한 사람의 목소리
|
||||
7. S7 — Studio 저장
|
||||
|
||||
이번 수정은 결과물 기준으로는 정상이나, **2026-09-19 수정 작업을 위 절차로 수행했다는 현재 시점의 실행 원장이 남지 않았다.**
|
||||
|
||||
따라서 최종 상태는 다음과 같다.
|
||||
|
||||
> **문서 내용 수정: 문제 없음**
|
||||
> **7단계 절차 증빙: 미완료**
|
||||
|
||||
즉, 현재 `keycloak = 아직 더 봐야댐`이다.
|
||||
|
||||
---
|
||||
|
||||
# 2. 이번 재검토 범위
|
||||
|
||||
이번 검토는 단순 기술 정확성 리뷰가 아니라 다음 순서를 기준으로 다시 확인했다.
|
||||
|
||||
## 2.1 S3 — 사실과 범위
|
||||
|
||||
- 일반 OAuth/OIDC 개념과 이 프로젝트 구현 사실이 섞이지 않았는지
|
||||
- 확인한 것과 확인하지 못한 것을 분리했는지
|
||||
- source repository가 없는 상황에서 live source 검증을 했다고 쓰지 않았는지
|
||||
- Public / Confidential Client 정의가 shared secret 하나로 축소되지 않았는지
|
||||
- Token Endpoint, Referrer-Policy 같은 세부 기술 설명이 과도하게 일반화되지 않았는지
|
||||
|
||||
## 2.2 S4 — SVG
|
||||
|
||||
- 그림이 필요한 Concept에 그림이 실제로 추가됐는지
|
||||
- 그림이 표로 대체 가능한 내용이 아닌지
|
||||
- 관계, 순서, 경계를 보여 주는 그림인지
|
||||
- TechViz `context.json`, `prompt.md`, `spec.json`이 있는지
|
||||
- 생성된 SVG에 provenance가 있는지
|
||||
- Figure Text / Overlap / Provenance 검사를 통과하는지
|
||||
|
||||
## 2.3 S5 — AI 티 제거
|
||||
|
||||
- 문서 작성 지시문처럼 읽히는 표현이 남아 있지 않은지
|
||||
- 같은 정의를 반복하지 않는지
|
||||
- 기계적인 문단 대칭과 리듬이 줄었는지
|
||||
- 지나치게 긴 문장을 의미 단위로 나눴는지
|
||||
- 스타일 점수를 맞추기 위해 OAuth 식별자까지 억지로 번역하지 않았는지
|
||||
|
||||
## 2.4 S6 — 일한 사람의 목소리
|
||||
|
||||
- 실제 관찰, 코드 추적, 미검증 범위가 남아 있는지
|
||||
- 근거 없는 1인칭 경험을 새로 만들지 않았는지
|
||||
- S5 수정 과정에서 기존 판단 맥락을 평평하게 만들지 않았는지
|
||||
|
||||
## 2.5 Command Pedagogy
|
||||
|
||||
Keycloak 프로젝트에는 현재 shell/CLI block이 없다.
|
||||
|
||||
따라서 이 프로젝트에서는 command pedagogy를 억지로 적용하거나 명령어를 새로 추가하는 것이 아니라 **정상 SKIP / N/A**가 맞다.
|
||||
|
||||
---
|
||||
|
||||
# 3. 직전 리뷰 항목 폐쇄 확인
|
||||
|
||||
## F-S4-01 — Bearer JWT validation chain SVG
|
||||
|
||||
### 이전 문제
|
||||
|
||||
`concept-bearer-jwt-validation-chain.md`는 다음 변환 사슬을 텍스트로만 설명하고 있었다.
|
||||
|
||||
```text
|
||||
Bearer JWT
|
||||
→ NimbusJwtDecoder
|
||||
→ issuer/time validation
|
||||
→ audience validation
|
||||
→ validated Jwt
|
||||
→ role converter
|
||||
→ authenticated principal
|
||||
```
|
||||
|
||||
이 기록의 핵심은 변환 순서였기 때문에 Concept에서 SVG가 필요한 항목이었다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
추가된 TechViz:
|
||||
|
||||
```text
|
||||
docs/keycloak/final/.techviz/bearer-jwt-validation-chain/
|
||||
├── context.json
|
||||
├── prompt.md
|
||||
└── spec.json
|
||||
```
|
||||
|
||||
추가된 자산:
|
||||
|
||||
```text
|
||||
docs/keycloak/final/assets/bearer-jwt-validation-chain/
|
||||
└── bearer-jwt-validation-chain.svg
|
||||
```
|
||||
|
||||
Spec 확인 결과:
|
||||
|
||||
- profile: `component-flow`
|
||||
- diagram-only: true
|
||||
- source gap: 없음
|
||||
- assumption: 없음
|
||||
|
||||
노드 구성도 기록의 실제 검증 체인을 그대로 따른다.
|
||||
|
||||
```text
|
||||
Bearer JWT
|
||||
→ NimbusJwtDecoder
|
||||
→ Issuer · Time validators
|
||||
→ AudienceValidator
|
||||
→ Validated Jwt
|
||||
→ Realm role converter
|
||||
→ Authenticated principal
|
||||
```
|
||||
|
||||
Concept 본문에도 SVG가 실제로 연결되어 있다.
|
||||
|
||||
### 판단
|
||||
|
||||
S4 누락 문제는 해결됐다.
|
||||
|
||||
---
|
||||
|
||||
## F-S4-02 — IdP Brokering boundary/flow SVG
|
||||
|
||||
### 이전 문제
|
||||
|
||||
`concept-idp-brokering.md`의 핵심은 외부 IdP가 다섯 번째 애플리케이션 인증 패턴이 아니라,
|
||||
|
||||
```text
|
||||
upstream IdP
|
||||
→ Keycloak broker
|
||||
→ Keycloak-issued code/token
|
||||
→ AP1/AP2/AP3/AP4 application boundary
|
||||
```
|
||||
|
||||
라는 **두 OAuth 경계의 연결**을 보여 주는 것이었다.
|
||||
|
||||
기존에는 이를 text fence로만 설명했다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
추가된 TechViz:
|
||||
|
||||
```text
|
||||
docs/keycloak/final/.techviz/idp-broker-upstream-downstream-boundary/
|
||||
├── context.json
|
||||
├── prompt.md
|
||||
└── spec.json
|
||||
```
|
||||
|
||||
Spec 확인 결과:
|
||||
|
||||
- profile: `two-zone-pipeline`
|
||||
- diagram-only: true
|
||||
- source gap: 없음
|
||||
- assumption: 없음
|
||||
|
||||
노드:
|
||||
|
||||
```text
|
||||
Google IdP
|
||||
→ Keycloak broker
|
||||
→ Keycloak authorization code
|
||||
→ AP1 · AP2 · AP3 · AP4
|
||||
```
|
||||
|
||||
이 구조는 문서의 핵심 주장인 다음 두 가지를 잘 분리한다.
|
||||
|
||||
1. upstream authentication은 외부 IdP와 Keycloak 사이에서 끝난다.
|
||||
2. 애플리케이션이 상대하는 issuer와 downstream 인증 경계는 계속 Keycloak이다.
|
||||
|
||||
### 판단
|
||||
|
||||
S4 누락 문제는 해결됐다.
|
||||
|
||||
---
|
||||
|
||||
# 4. SVG 전체 검증
|
||||
|
||||
현재 Keycloak 프로젝트의 TechViz / SVG는 각각 12개다.
|
||||
|
||||
검사 결과:
|
||||
|
||||
```text
|
||||
FIGURE TEXT: PASS
|
||||
- 그림 12장
|
||||
- 문장 0건
|
||||
|
||||
FIGURE PROVENANCE: PASS
|
||||
- 정본과 짝지은 그림 12
|
||||
|
||||
FIGURE OVERLAP: PASS
|
||||
- 그림 12
|
||||
- 본 그림 12
|
||||
- 겹친 그림 0
|
||||
```
|
||||
|
||||
따라서 새 SVG 두 장은 단순 파일 추가가 아니라 현재 하네스의 Figure gate도 통과했다.
|
||||
|
||||
기존 10개 SVG를 다시 그릴 이유도 확인되지 않았다.
|
||||
|
||||
---
|
||||
|
||||
# 5. S5 — AI 티 제거 재검토
|
||||
|
||||
## F-S5-01 — pattern-selection 작성 지시문
|
||||
|
||||
### 이전 문제
|
||||
|
||||
다음과 같은 문장이 있었다.
|
||||
|
||||
```text
|
||||
성공 응답과 실패 응답까지 봐야 한다.
|
||||
어떤 보안 요구사항과 운영 조건 때문에 그 구조를 골랐는지 함께 적는다.
|
||||
운영 항목까지 같이 적는다.
|
||||
```
|
||||
|
||||
이 표현은 패턴 선택 기준 자체가 아니라 “문서를 어떻게 작성해야 하는가”를 설명하는 문장에 가까웠다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재는 실제 비교 기준을 직접 서술한다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
비교 입력에는 엔드포인트와 메서드, 중간에 생기는 자격 증명,
|
||||
성공·실패 응답이 포함되고 로그인 구간과 로그인 뒤 API 호출 구간을 각각 나눠 본다.
|
||||
```
|
||||
|
||||
또 선택 기록에 어떤 운영 조건이 들어가야 하는지도 “작성 지시”가 아니라 선택 기준의 구성 요소로 바뀌었다.
|
||||
|
||||
---
|
||||
|
||||
## F-S5-02 — Public / Confidential Client 반복 정의
|
||||
|
||||
### 이전 문제
|
||||
|
||||
같은 정의를 여러 위치에서 반복하면서 Reference 전체가 정의의 재진술처럼 읽혔다.
|
||||
|
||||
또 general OAuth definition과 이 프로젝트의 `client_secret_basic` 구현 사실이 섞여 있었다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재 첫 정의는 다음처럼 정리됐다.
|
||||
|
||||
```text
|
||||
OAuth 클라이언트 종류는 인증 서버에 대해 장기 자격 증명의 기밀성을 유지하고
|
||||
신뢰할 수 있는 클라이언트 인증을 수행할 수 있는지로 구분한다.
|
||||
```
|
||||
|
||||
그 뒤에는 같은 정의를 다시 반복하지 않고 다음 실제 프로젝트 차이로 넘어간다.
|
||||
|
||||
- AP1 SPA → public client
|
||||
- AP2~AP4 → confidential client
|
||||
- 이 프로젝트의 confidential client 인증 방식 → `client_secret_basic`
|
||||
- Confidential Client 인증 수단 자체는 shared secret에 한정되지 않음
|
||||
- client type과 browser token custody는 별도 축
|
||||
|
||||
이 구성이 훨씬 낫다.
|
||||
|
||||
---
|
||||
|
||||
## F-S5-03 — IdP Federation 장문
|
||||
|
||||
### 이전 문제
|
||||
|
||||
한 문장 안에 현재 경계, 예외 조건, 직접 연동 시 변화, 미검증 범위가 함께 들어가 읽기 부담이 컸다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재는 다음 축으로 나뉘어 있다.
|
||||
|
||||
- broker 경계
|
||||
- external identity key
|
||||
- email collision
|
||||
- mock provider 검증 범위
|
||||
- broker를 쓰지 않는 direct OIDC 예외
|
||||
|
||||
내용은 그대로 유지하면서 문장 구조만 단순해졌다.
|
||||
|
||||
---
|
||||
|
||||
## F-S5-04 — BFF State Store의 `여기까지`
|
||||
|
||||
### 이전 문제
|
||||
|
||||
```text
|
||||
여기까지는 한 대에서 실행한 학습 환경에서 코드와 테스트로 확인한 것이다.
|
||||
```
|
||||
|
||||
이 표현은 사실 범위를 말하는 데 필요한 내용이지만, “여기까지”는 문서 진행을 설명하는 메타 표현이었다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재 문장:
|
||||
|
||||
```text
|
||||
현재 확인 범위는 한 대에서 실행한 학습 환경의 코드와 테스트다.
|
||||
```
|
||||
|
||||
범위만 직접 남겼다.
|
||||
|
||||
---
|
||||
|
||||
## F-S5-05 — Multi-instance 선택지의 기계적 대칭
|
||||
|
||||
### 이전 문제
|
||||
|
||||
각 선택지가 모두:
|
||||
|
||||
```text
|
||||
정의
|
||||
→ 장점
|
||||
→ 다만
|
||||
→ 추가 설계 필요
|
||||
```
|
||||
|
||||
패턴으로 반복돼 AI가 템플릿으로 만든 선택지처럼 읽혔다.
|
||||
|
||||
### 현재 상태
|
||||
|
||||
**닫힘**
|
||||
|
||||
지금은 각 선택지가 서로 다른 판단 축을 제목과 본문에서 직접 드러낸다.
|
||||
|
||||
```text
|
||||
공유 저장소
|
||||
→ 상태 일관성 / 저장소 가용성
|
||||
|
||||
Session affinity
|
||||
→ 라우팅 고정 / node loss는 해결하지 못함
|
||||
|
||||
브라우저 토큰
|
||||
→ 서버 공유 상태 제거 / credential ownership이 browser로 이동
|
||||
|
||||
Client-side cookie
|
||||
→ 저장소 문제가 아니라 trust boundary 변화
|
||||
```
|
||||
|
||||
질문의 성격상 선택지 병렬 구조 자체는 유지해야 하므로, 현재 수준이면 충분하다.
|
||||
|
||||
---
|
||||
|
||||
# 6. S3 기술 범위 수정 확인
|
||||
|
||||
## Public / Confidential Client 정의
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재 SSOT와 Case에서도 다음 방향으로 정리됐다.
|
||||
|
||||
```text
|
||||
브라우저 실행 환경에서 장기 client credential의 기밀성을 유지하고
|
||||
신뢰할 수 있는 client authentication을 수행하기 어렵기 때문에 public client
|
||||
```
|
||||
|
||||
AP2는:
|
||||
|
||||
```text
|
||||
server에서 client credential을 보호하고
|
||||
client authentication을 수행할 수 있는 confidential client
|
||||
```
|
||||
|
||||
로 표현한다.
|
||||
|
||||
즉:
|
||||
|
||||
```text
|
||||
public/confidential
|
||||
≠ 브라우저가 토큰을 받느냐
|
||||
```
|
||||
|
||||
라는 프로젝트의 핵심 구분도 유지된다.
|
||||
|
||||
---
|
||||
|
||||
## Token Endpoint 제목
|
||||
|
||||
**닫힘**
|
||||
|
||||
이전의:
|
||||
|
||||
```text
|
||||
Token Endpoint에서 비로소 클라이언트를 인증한다
|
||||
```
|
||||
|
||||
같은 일반화 대신 현재는:
|
||||
|
||||
```text
|
||||
confidential client는 Token Endpoint에서 자신을 인증한다
|
||||
```
|
||||
|
||||
로 범위를 좁혔다.
|
||||
|
||||
본문도 AP1 public SPA와 AP2~AP4 confidential client를 분리해서 설명한다.
|
||||
|
||||
---
|
||||
|
||||
## Referrer-Policy
|
||||
|
||||
**닫힘**
|
||||
|
||||
현재는 Authorization Endpoint URL 노출 범위를 다음처럼 적는다.
|
||||
|
||||
```text
|
||||
다른 문서나 origin으로 이동할 때 referrer에 어느 범위까지 전달되는지는
|
||||
브라우저의 Referrer-Policy와 이동 대상의 관계에 따라 달라진다.
|
||||
```
|
||||
|
||||
따라서 “항상 query 전체가 referrer에 남는다”는 식의 과도한 일반화는 제거됐다.
|
||||
|
||||
---
|
||||
|
||||
# 7. S5 / S6 자동 검사
|
||||
|
||||
현재 24개 기록 전체 검사 결과:
|
||||
|
||||
```text
|
||||
Natural Prose
|
||||
- FAIL 0
|
||||
|
||||
Voice
|
||||
- FAIL 0
|
||||
```
|
||||
|
||||
Style profile에는 일부 advisory outlier가 남아 있다.
|
||||
|
||||
예:
|
||||
|
||||
- OAuth 식별자와 영문 기술명 때문에 Hangul ratio가 낮은 문서
|
||||
- 120자 초과 문장 비율이 약간 높은 문서
|
||||
- 이유 연결 표현 빈도가 reference 범위보다 조금 낮은 문서
|
||||
|
||||
하지만 style profile은 하네스에서도 advisory다.
|
||||
|
||||
현재 outlier를 없애기 위해 다음과 같은 행동을 하면 오히려 문서 품질이 떨어진다.
|
||||
|
||||
- `Authorized Client`, `Token Endpoint`, `client_secret_basic` 같은 보호된 기술어를 억지 번역
|
||||
- 문장 길이 수치만 맞추려고 의미 단위 파괴
|
||||
- 이유 연결어미 수치를 맞추려고 불필요한 “때문에”, “따라서” 추가
|
||||
|
||||
따라서 현재 자동 style metric만을 이유로 추가 수정을 요구하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 8. Command Pedagogy
|
||||
|
||||
현재 Keycloak Studio 기록에는 shell/CLI block이 없다.
|
||||
|
||||
검사 결과:
|
||||
|
||||
```text
|
||||
shell blocks = 0
|
||||
findings = 0
|
||||
```
|
||||
|
||||
따라서 이 프로젝트의 Command Pedagogy는 **정상 SKIP / N/A**다.
|
||||
|
||||
HTTP 예시, JSON 예시, text flow를 shell 명령어처럼 취급해서는 안 된다.
|
||||
|
||||
또 “명령어 품질도 리뷰 기준이니 shell command를 추가하자”는 식으로 대응하면 안 된다.
|
||||
|
||||
---
|
||||
|
||||
# 9. 자동 검증 결과
|
||||
|
||||
Keycloak 단독:
|
||||
|
||||
```text
|
||||
verify-tech-log-tree.py keycloak
|
||||
PASS
|
||||
records=24
|
||||
topics=1
|
||||
nodes=24
|
||||
written=24
|
||||
unwritten=0
|
||||
|
||||
verify-project-layout.py keycloak
|
||||
PASS
|
||||
error=0
|
||||
|
||||
audit-records.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-text.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-provenance.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-overlap.py keycloak
|
||||
PASS
|
||||
|
||||
check-required-content.py keycloak
|
||||
PASS
|
||||
|
||||
check-ssot-facts.py keycloak
|
||||
PASS
|
||||
|
||||
git diff --check -- docs/keycloak runs/keycloak
|
||||
PASS
|
||||
```
|
||||
|
||||
전체 하네스 회귀:
|
||||
|
||||
```text
|
||||
verify-pipeline.py
|
||||
PASS
|
||||
exit 0
|
||||
```
|
||||
|
||||
전체 테스트:
|
||||
|
||||
```text
|
||||
Ran 391 tests
|
||||
|
||||
OK (skipped=14)
|
||||
```
|
||||
|
||||
따라서 이번 수정으로 하네스 자체나 다른 프로젝트가 깨진 것은 아니다.
|
||||
|
||||
---
|
||||
|
||||
# 10. 남은 문제 — 이번 수정의 run ledger
|
||||
|
||||
현재 Keycloak에는:
|
||||
|
||||
```text
|
||||
records = 24
|
||||
run.json = 13
|
||||
run이 덮은 unique record = 12
|
||||
```
|
||||
|
||||
기존 12개의 historical record에 run ledger가 없는 것은 이번에 새로 발견된 결함으로 취급하지 않는다.
|
||||
|
||||
하네스 규칙상 과거 실행을 하지 않았는데 과거 원장을 만들어 내면 안 된다.
|
||||
|
||||
> **historical run ledger를 소급 작성하지 않는다.**
|
||||
|
||||
문제는 이번 2026-09-19 수정이다.
|
||||
|
||||
이번 재검토 시점에 변경된 Keycloak Studio Record는 17개인데, `runs/keycloak`의 최신 실행 원장은:
|
||||
|
||||
```text
|
||||
2026-09-16-2130-fact-redis-preference
|
||||
```
|
||||
|
||||
다.
|
||||
|
||||
즉 **이번 수정 작업을 위한 current run이 하나도 없다.**
|
||||
|
||||
이 상태에서는:
|
||||
|
||||
- S3를 누가 수행했는지
|
||||
- S4에서 왜 SVG를 추가했거나 SKIP했는지
|
||||
- S5를 다시 거쳤는지
|
||||
- S6를 다시 거쳤는지
|
||||
- command lane을 왜 SKIP했는지
|
||||
- fact review를 수행했는지
|
||||
- S7을 왜 SKIP했는지
|
||||
|
||||
를 절차적으로 증명할 수 없다.
|
||||
|
||||
자동 결과가 PASS라고 해도, 사용자가 요구한 “7단계 절차를 기반으로 문서를 작성했는가”에는 아직 답이 부족하다.
|
||||
|
||||
---
|
||||
|
||||
# 11. 수정 실행 시 지켜야 할 원칙
|
||||
|
||||
## 하지 말 것
|
||||
|
||||
### 1. 과거 12개 무원장 기록을 소급해서 만들지 않는다
|
||||
|
||||
이번 수정과 관계없는 historical record까지 fake ledger로 채우면 안 된다.
|
||||
|
||||
### 2. 기존 run.json을 고쳐서 이번 실행처럼 만들지 않는다
|
||||
|
||||
과거 영수증을 수정하는 것도 금지한다.
|
||||
|
||||
### 3. 문서를 다시 대규모 rewrite하지 않는다
|
||||
|
||||
현재 문서 내용 finding은 닫혔다.
|
||||
|
||||
다시 손댈 이유가 없다.
|
||||
|
||||
### 4. SVG를 다시 그리지 않는다
|
||||
|
||||
현재 12개 모두 Figure gate를 통과한다.
|
||||
|
||||
### 5. shell command를 추가하지 않는다
|
||||
|
||||
Keycloak에는 현재 command pedagogy lane이 적용될 shell/CLI가 없다.
|
||||
|
||||
---
|
||||
|
||||
# 12. 필요한 실제 조치
|
||||
|
||||
이번 수정 대상에 대해 **새 current remediation run**을 만든다.
|
||||
|
||||
수정된 Record마다 현재 하네스 규칙을 적용한다.
|
||||
|
||||
권장 흐름:
|
||||
|
||||
```text
|
||||
run 시작
|
||||
↓
|
||||
S1
|
||||
기존 SSOT로 충분하면 SKIPPED + 구체적인 이유
|
||||
↓
|
||||
S2
|
||||
기존 PROMOTE / CONFIRMED tree node라면 SKIPPED + 이유
|
||||
↓
|
||||
S3
|
||||
현재 수정 결과 확인
|
||||
↓
|
||||
Command initial analysis
|
||||
shell/CLI 없음 → SKIPPED + 이유
|
||||
↓
|
||||
S4
|
||||
Bearer JWT / IdP Brokering은 DONE
|
||||
나머지는 figure 필요 여부 판단 후 SKIPPED + 이유
|
||||
↓
|
||||
S5
|
||||
현재 prose 확인
|
||||
↓
|
||||
S6
|
||||
현재 voice 확인
|
||||
↓
|
||||
Command final analysis
|
||||
shell/CLI 없음 → SKIPPED
|
||||
↓
|
||||
Fact review
|
||||
↓
|
||||
S7
|
||||
Studio import 요청이 없으므로 SKIPPED + 이유
|
||||
↓
|
||||
verify-pipeline-run.py
|
||||
```
|
||||
|
||||
중요한 점은 **이미 한 작업을 과거 시점에 했다고 꾸미는 것이 아니라, 현재 수정 결과를 대상으로 remediation run을 실제로 수행하는 것**이다.
|
||||
|
||||
---
|
||||
|
||||
# 13. 완료 조건
|
||||
|
||||
다음 조건을 모두 만족하면 `keycloak = 완료`로 바꿀 수 있다.
|
||||
|
||||
- [x] Bearer JWT validation chain SVG 추가
|
||||
- [x] IdP Brokering SVG 추가
|
||||
- [x] 두 SVG 모두 TechViz context/spec/provenance 존재
|
||||
- [x] Figure Text PASS
|
||||
- [x] Figure Overlap PASS
|
||||
- [x] Figure Provenance PASS
|
||||
- [x] pattern-selection 작성 지시형 문장 제거
|
||||
- [x] Public / Confidential Client 반복 정의 정리
|
||||
- [x] IdP Federation 장문 정리
|
||||
- [x] BFF State Store 메타 진행 표현 제거
|
||||
- [x] Multi-instance 선택지 기계적 리듬 정리
|
||||
- [x] Public / Confidential 정의 scope 수정
|
||||
- [x] Token Endpoint 제목 scope 수정
|
||||
- [x] Referrer-Policy 조건 반영
|
||||
- [x] Natural Prose PASS
|
||||
- [x] Voice PASS
|
||||
- [x] command pedagogy 정상 SKIP
|
||||
- [ ] 이번 수정 대상에 대한 current run ledger 생성
|
||||
- [ ] 새 run ledger 각각 `verify-pipeline-run.py` PASS
|
||||
- [x] Tree / Layout / Required Content / Figure gates PASS
|
||||
- [x] standalone pipeline PASS
|
||||
- [x] full unittest PASS
|
||||
- [ ] source repo가 계속 없으면 최종 보고에 live source reconciliation = UNVERIFIABLE 명시
|
||||
|
||||
---
|
||||
|
||||
# 14. 최종 판정
|
||||
|
||||
현재 Keycloak 문서의 **내용 품질은 직전 리뷰 기준을 충족했다.**
|
||||
|
||||
새 SVG도 적절하고, AI 티 제거 수정도 제대로 됐으며, 기술 범위 수정도 반영됐다.
|
||||
|
||||
남은 것은 문서 자체를 다시 고치는 일이 아니다.
|
||||
|
||||
**이번 수정 작업이 7단계 문서 하네스를 실제로 통과했다는 current run ledger를 남기는 일이다.**
|
||||
|
||||
따라서 현재 최종 상태:
|
||||
|
||||
> **keycloak = 아직 더 봐야댐**
|
||||
|
||||
다음 수정에서는 문서 본문을 또 건드리지 말고, 이번 remediation에 대한 실행 원장과 검증 결과만 정확하게 닫는 것을 우선한다.
|
||||
@@ -0,0 +1,543 @@
|
||||
# keycloak 재검토 리뷰 — 2026-09-19
|
||||
|
||||
## 판정
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
직전 상세 리뷰의 K01~K06은 상당 부분 반영되었다. 그러나 K03과 K04는 의미상 완전히 닫히지 않았고, 같은 기술 정확성 기준으로 전체 문맥을 다시 읽는 과정에서 Authorization Endpoint의 Referrer 설명이 지나치게 일반화된 부분도 추가로 확인했다.
|
||||
|
||||
자동 검증은 모두 통과했다. 이번 판정이 남아 있는 이유는 빌드/파이프라인 실패가 아니라 **기술 의미와 문서 내부 정의의 일관성** 때문이다.
|
||||
|
||||
현재 source repository:
|
||||
|
||||
`/home/donghyeon/workspace/keycloak-pattern`
|
||||
|
||||
는 현재 머신에 존재하지 않는다. 따라서 live source reconciliation은 계속 **UNVERIFIABLE**이다. 이를 PASS로 간주하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 1. 이번 재검토 기준
|
||||
|
||||
이번 검토는 직전 리뷰의 K01~K06 문자열만 없어졌는지 확인하는 방식으로 하지 않았다.
|
||||
|
||||
동일한 기준을 세 층으로 유지했다.
|
||||
|
||||
## 1.1 정본 정확성
|
||||
|
||||
`final/document.md` 자체가 OAuth/OIDC, Keycloak, Spring Security의 의미를 기술적으로 정확하게 설명하는지 본다.
|
||||
|
||||
확인 기준:
|
||||
|
||||
- 일반 표준 규칙과 이 프로젝트의 구현 사실을 구분하는가
|
||||
- 사실, 추론, 미확인을 섞지 않는가
|
||||
- 현재 구현에서 확인하지 않은 것을 확인한 것처럼 쓰지 않는가
|
||||
- 특정 구현의 우연한 배치를 OAuth/OIDC 일반 규칙으로 확대하지 않는가
|
||||
|
||||
## 1.2 SSOT → Record 의미 보존
|
||||
|
||||
Concept / Reference / Case / Question이 SSOT를 파생하면서 다음을 바꾸지 않는지 본다.
|
||||
|
||||
- fact
|
||||
- scope
|
||||
- certainty
|
||||
- causality
|
||||
- condition
|
||||
|
||||
즉 문장이 자연스러워졌는지가 아니라 **정본이 말한 범위와 확실성이 그대로 유지됐는지**를 본다.
|
||||
|
||||
## 1.3 문서 시스템 무결성
|
||||
|
||||
의미가 맞더라도 문서 시스템에서 깨진 것이 없는지 본다.
|
||||
|
||||
- Natural prose
|
||||
- Voice
|
||||
- Tech Log Tree
|
||||
- Project Layout
|
||||
- Figure Text
|
||||
- Figure Provenance
|
||||
- Figure Overlap
|
||||
- Required Content
|
||||
- SSOT Facts
|
||||
- Command Pedagogy
|
||||
- git diff --check
|
||||
- standalone pipeline
|
||||
- full unittest regression
|
||||
|
||||
그림은 자동 PASS만 보지 않고 SSOT가 바뀐 뒤 context/spec이 여전히 같은 주장을 그리는지도 직접 본다.
|
||||
|
||||
### 맥락 유지 원칙
|
||||
|
||||
이번 재검토에서 **새 평가 축을 임의로 추가하지 않았다.**
|
||||
|
||||
새로 발견된 항목이 있더라도 그것은 기존 기준인:
|
||||
|
||||
1. 기술 정확성
|
||||
2. 사실/추론/미확인 구분
|
||||
3. SSOT ↔ Record semantic consistency
|
||||
4. 설명 범위의 과도한 일반화 방지
|
||||
|
||||
중 하나로 설명될 때만 finding으로 잡았다.
|
||||
|
||||
---
|
||||
|
||||
# 2. 직전 K01~K06 반영 상태
|
||||
|
||||
| ID | 직전 finding | 현재 상태 | 판정 |
|
||||
|---|---|---|---|
|
||||
| K01 | AP2/AP3 PKCE scope 충돌 | AP2라는 주어가 명시되고 AP2 PKCE S256 미확인이 분리됨 | **CLOSED** |
|
||||
| K02 | refreshTokenMaxReuse를 시간 창처럼 설명 | reuse count와 lifespan을 분리하고 runtime 경쟁은 미검증 유지 | **CLOSED** |
|
||||
| K03 | client type이 token endpoint caller를 결정한다는 인과 | 본문은 수정됐으나 섹션 제목이 여전히 과도한 일반화 | **PARTIAL** |
|
||||
| K04 | Public/Confidential 정의를 client secret 하나로 축소 | Reference 본문은 개선됐으나 서두·SSOT·Case에 축약 정의 잔존 | **PARTIAL** |
|
||||
| K05 | “남는 선택지는 하나” 일반화 | AP1~AP4 네 패턴 범위로 제한됨 | **CLOSED** |
|
||||
| K06 | CSRF token = 사용자 의도 증명 | cross-site forged request를 구분하는 anti-CSRF 검증으로 수정됨 | **CLOSED** |
|
||||
|
||||
따라서 K01~K06 전체가 닫힌 것은 아니다.
|
||||
|
||||
---
|
||||
|
||||
# 3. 남은 Findings
|
||||
|
||||
## R01 — HIGH — K04가 문서 전체에서는 아직 닫히지 않았다
|
||||
|
||||
직전 리뷰에서 요구한 핵심은:
|
||||
|
||||
> Public/Confidential client를 shared client secret 하나로 정의하지 말고,
|
||||
> client credential의 기밀성 및 신뢰할 수 있는 client authentication 능력으로 설명한다.
|
||||
|
||||
Reference 본문은 이 방향으로 잘 수정됐다.
|
||||
|
||||
현재 올바르게 수정된 부분:
|
||||
|
||||
`reference-public-confidential-client.md:41`
|
||||
|
||||
> OAuth client type은 authorization server에 대해 client credential의 기밀성을 유지하고 신뢰할 수 있는 client authentication을 수행할 수 있는지로 구분한다.
|
||||
|
||||
`reference-public-confidential-client.md:53`
|
||||
|
||||
> Shared secret은 한 방식이고 private key나 mTLS 같은 다른 client authentication 방식도 가능하다.
|
||||
|
||||
문제는 이보다 앞의 문장과 다른 파생 문서가 여전히 예전 정의를 그대로 사용한다는 점이다.
|
||||
|
||||
### 잔여 1 — Reference 첫 정의
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-public-confidential-client.md`
|
||||
|
||||
23행:
|
||||
|
||||
> OAuth 클라이언트의 종류는 클라이언트 시크릿을 안전하게 보관할 수 있는지로 정한다.
|
||||
|
||||
41행에서 더 정확한 정의를 다시 제시하므로 **한 문서 안에서 정의가 두 개**가 된다.
|
||||
|
||||
### 잔여 2 — SSOT
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/final/document.md`
|
||||
|
||||
183행:
|
||||
|
||||
> Public client는 브라우저처럼 client secret을 안전하게 숨길 수 없는 애플리케이션입니다.
|
||||
|
||||
210행:
|
||||
|
||||
> Confidential client는 client secret을 server에서 보관할 수 있는 애플리케이션입니다.
|
||||
|
||||
이 문장들은 “이 프로젝트가 shared secret을 사용한다”는 범위를 붙이면 설명용 단축 표현으로 사용할 수 있다.
|
||||
|
||||
하지만 현재 문장 구조는 OAuth client type의 일반 정의처럼 읽힌다.
|
||||
|
||||
### 잔여 3 — Case
|
||||
|
||||
파일:
|
||||
|
||||
`case-browser-credential-boundary.md`
|
||||
|
||||
101행:
|
||||
|
||||
> public client는 브라우저처럼 client secret을 안전하게 숨길 수 없는 애플리케이션이다.
|
||||
|
||||
파일:
|
||||
|
||||
`case-ap2-split-custody.md`
|
||||
|
||||
116행:
|
||||
|
||||
> confidential client는 client secret을 서버에 두고 자기를 인증할 수 있는 애플리케이션이다.
|
||||
|
||||
Case가 프로젝트 구현을 설명하는 문서인 만큼 shared secret 자체를 언급하는 것은 문제가 아니다. 다만 일반 정의처럼 시작하지 말고 프로젝트 scope를 먼저 걸어야 한다.
|
||||
|
||||
### 왜 중요한가
|
||||
|
||||
RFC 6749의 public/confidential 구분은 단순히 shared secret을 숨길 수 있는가만으로 정의되지 않는다. 핵심은 client credentials의 confidentiality를 유지할 수 있는지와 authorization server에 대해 secure client authentication을 수행할 수 있는지다.
|
||||
|
||||
따라서 Reference 본문만 수정하고 SSOT/Case의 정의형 문장을 남기면 semantic consistency가 깨진다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
Reference 첫 문장:
|
||||
|
||||
> OAuth client type은 authorization server에 대해 client credential의 기밀성을 유지하고 신뢰할 수 있는 client authentication을 수행할 수 있는지로 구분한다.
|
||||
|
||||
SSOT AP1:
|
||||
|
||||
> 이 프로젝트의 AP1 SPA는 사용자 기기에서 실행되어 장기 client credential의 기밀성을 유지하기 어려우므로 public client로 구성했습니다.
|
||||
|
||||
SSOT AP2:
|
||||
|
||||
> 이 프로젝트의 AP2 mediator는 server-side에서 shared client secret을 보호하고 `client_secret_basic`으로 인증하므로 confidential client로 구성했습니다.
|
||||
|
||||
Case도 같은 방식으로 **“일반 정의”가 아니라 “이 프로젝트에서 왜 그렇게 등록했는지”**로 바꾼다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
다음 패턴의 일반 정의형 문장이 0건이어야 한다.
|
||||
|
||||
- “public client는 client secret을 숨길 수 없는 애플리케이션이다”
|
||||
- “confidential client는 client secret을 서버에 보관하는 애플리케이션이다”
|
||||
- “client type은 client secret 보관 가능 여부로 정한다”
|
||||
|
||||
프로젝트에서 실제로 `client_secret_basic`을 쓴 사실은 삭제하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## R02 — MEDIUM — K03 본문은 고쳤지만 섹션 제목이 이전 의미를 남긴다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-authorization-code-endpoints.md`
|
||||
|
||||
현재 제목:
|
||||
|
||||
> ### 2. Token Endpoint에서 비로소 클라이언트를 인증한다
|
||||
|
||||
그 아래 본문은 이미 다음처럼 정확하게 바뀌었다.
|
||||
|
||||
> client authentication도 이 요청에서 수행할 수 있다.
|
||||
|
||||
그리고:
|
||||
|
||||
> 이 프로젝트의 confidential client들은 `client_secret_basic`을 사용하므로 server-side component가 client secret으로 자신을 인증한다.
|
||||
|
||||
즉 본문은:
|
||||
|
||||
- public client가 항상 client authentication을 하는 것은 아님
|
||||
- confidential client는 이 프로젝트에서 token endpoint에서 인증함
|
||||
|
||||
으로 고쳐졌다.
|
||||
|
||||
그러나 제목은 여전히 **Authorization Code Flow의 모든 client가 token endpoint에서 “클라이언트를 인증한다”**는 의미로 읽힌다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
예:
|
||||
|
||||
> ### 2. Client authentication이 필요한 경우 Token Endpoint에서 수행한다
|
||||
|
||||
또는 프로젝트 중심으로:
|
||||
|
||||
> ### 2. 이 프로젝트의 confidential client는 Token Endpoint에서 자신을 인증한다
|
||||
|
||||
첫 번째가 Reference 문서 성격에는 더 적절하다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
제목과 본문의 범위가 같아야 한다.
|
||||
|
||||
---
|
||||
|
||||
## R03 — MEDIUM — Authorization Endpoint URL이 “referrer에 남는다”는 표현이 과도하게 단정적이다
|
||||
|
||||
파일:
|
||||
|
||||
`docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-authorization-code-endpoints.md`
|
||||
|
||||
39행:
|
||||
|
||||
> URL이 히스토리와 서버 로그, referrer에 남고
|
||||
|
||||
46행:
|
||||
|
||||
> 링크를 타고 온 경우에는 referrer에도 남는다.
|
||||
|
||||
주소창, 브라우저 history, Authorization Server access log에 authorization request URL이 남을 수 있다는 설명은 타당하다.
|
||||
|
||||
문제는 **Referrer는 Referrer-Policy에 따라 전달 범위가 달라진다**는 점이다.
|
||||
|
||||
현대 브라우저의 기본 정책인 `strict-origin-when-cross-origin`에서는 일반적인 cross-origin 이동에서 전체 path/query가 아니라 origin만 보내며, downgrade에서는 referrer를 보내지 않는다. 같은 origin이거나 더 허용적인 정책에서는 path/query가 전달될 수 있다.
|
||||
|
||||
따라서 “authorization request URL이 referrer에 남는다”를 일반 규칙처럼 쓰면 scope가 넓다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
39행:
|
||||
|
||||
> Authorization Endpoint 요청 URL은 주소창과 브라우저 history, Authorization Server access log에 남을 수 있다. Referer 전달 범위는 브라우저의 Referrer-Policy와 요청 관계에 따라 달라진다.
|
||||
|
||||
46행:
|
||||
|
||||
> 같은 origin이거나 더 허용적인 Referrer-Policy에서는 path/query가 Referer에 포함될 수 있으므로, authorization request URL에 비밀값을 싣지 않는다.
|
||||
|
||||
이 문서의 핵심 결론인 “authorization endpoint query에 client secret을 넣지 않는다”는 유지한다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- referrer에 full URL/query가 항상 남는다는 인상 제거
|
||||
- Referrer-Policy에 따라 범위가 달라진다는 조건 명시
|
||||
- 주소창/history/access log와 Referer를 같은 확실성으로 묶지 않음
|
||||
|
||||
---
|
||||
|
||||
## R04 — LOW — Figure 자동 검증은 PASS지만 context 검토 상태가 완전히 닫히지 않았다
|
||||
|
||||
현재 자동 결과:
|
||||
|
||||
`PROJECT LAYOUT: PASS — error 0 · warn 10`
|
||||
|
||||
keycloak warning:
|
||||
|
||||
- 8건: techviz 도구 부재로 전체 SSOT hash 대조 불가, context snapshot은 일치
|
||||
- 2건: SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다고 기록됨
|
||||
- `final/.techviz/ap1-direct-architecture`
|
||||
- `final/.techviz/login-api-phase-split`
|
||||
|
||||
이번 재검토에서 위 두 spec/context를 직접 읽었다.
|
||||
|
||||
### 직접 판독 결과
|
||||
|
||||
`ap1-direct-architecture`
|
||||
|
||||
- AP1 public SPA
|
||||
- PKCE S256
|
||||
- browser JS memory token custody
|
||||
- Resource Server issuer/time/audience 검증
|
||||
|
||||
으로 현재 SSOT와 의미가 맞는다.
|
||||
|
||||
`login-api-phase-split`
|
||||
|
||||
- AP2 mediator가 token을 받음
|
||||
- AP2 browser가 실제 API caller
|
||||
- AP3는 BFF가 login/API 모두 담당
|
||||
- AP4는 oauth2-proxy와 Nginx 책임 분리
|
||||
- PKCE verifier 목록은 AP1/AP3/AP4이며 AP2를 억지로 포함하지 않음
|
||||
|
||||
으로 K01 수정과도 충돌하지 않는다.
|
||||
|
||||
따라서 **그림 내용 자체의 오류는 발견하지 않았다.**
|
||||
|
||||
다만 completion contract를 엄격하게 적용한다면 techviz가 남긴 stale-review 상태는 적절한 techviz review/regeneration 경로로 닫는 편이 맞다.
|
||||
|
||||
### 주의
|
||||
|
||||
- hash를 손으로 맞추지 않는다.
|
||||
- SVG를 임의 수작업으로 고치지 않는다.
|
||||
- techviz 정본 → spec/context → SVG 경로를 유지한다.
|
||||
|
||||
---
|
||||
|
||||
# 4. 직전 Findings 중 제대로 닫힌 항목
|
||||
|
||||
## K01 — CLOSED
|
||||
|
||||
AP3 설명 뒤에 AP2라는 주어 없이 붙어 있던 PKCE 미검증 문장이 다음처럼 수정됐다.
|
||||
|
||||
> 반면 AP2의 `token-mediating-confidential`은 ...
|
||||
|
||||
이제 다음 범위가 분명하다.
|
||||
|
||||
- AP3 = PKCE S256 확인
|
||||
- AP2 = Authorization Code confidential client 확인, PKCE S256 미확인
|
||||
|
||||
SSOT와 Record의 certainty가 일치한다.
|
||||
|
||||
---
|
||||
|
||||
## K02 — CLOSED
|
||||
|
||||
`refreshTokenMaxReuse`가 시간 창이 아니라 reuse count라는 점이 명시됐다.
|
||||
|
||||
또:
|
||||
|
||||
- rotation
|
||||
- max reuse count
|
||||
- refresh token lifespan
|
||||
|
||||
을 서로 다른 설정으로 분리했다.
|
||||
|
||||
replica 동시 refresh의 실제 결과도 여전히 미검증으로 유지했다.
|
||||
|
||||
---
|
||||
|
||||
## K05 — CLOSED
|
||||
|
||||
기존:
|
||||
|
||||
> 남는 선택지는 하나다.
|
||||
|
||||
에서:
|
||||
|
||||
> 이 문서에서 비교하는 AP1~AP4 네 패턴만 놓고 보면 ...
|
||||
|
||||
으로 범위가 제한됐다.
|
||||
|
||||
OAuth/OIDC 전체 설계 공간의 유일한 답처럼 보이던 문제가 사라졌다.
|
||||
|
||||
---
|
||||
|
||||
## K06 — CLOSED
|
||||
|
||||
SSOT와 관련 Record에서:
|
||||
|
||||
> 사용자가 의도한 요청인지 확인
|
||||
|
||||
이라는 표현을 제거하고:
|
||||
|
||||
> cookie가 자동 첨부되는 state-changing request에 별도 anti-CSRF 검증 값을 요구해 cross-site forged request를 구분
|
||||
|
||||
하는 의미로 정리됐다.
|
||||
|
||||
SameSite / CSRF token / same-origin XSS의 역할도 기존 문맥과 충돌하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 5. 자동 검증 결과
|
||||
|
||||
이번 재검토에서 현재 변경 상태 그대로 다시 실행했다.
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| Tech Log Tree | PASS — records 24, topics 1, nodes 24, written 24, unwritten 0 |
|
||||
| Project Layout | PASS — error 0, warn 10 |
|
||||
| Record Audit | PASS — 문제 0 |
|
||||
| Figure Text | PASS — SVG 10, sentence error 0 |
|
||||
| Figure Provenance | PASS — error 0 |
|
||||
| Figure Overlap | PASS — overlap 0 |
|
||||
| Required Content | PASS — records 24, error 0 |
|
||||
| SSOT Facts | PASS — error 0 |
|
||||
| Natural Prose | PASS — 24/24 |
|
||||
| Voice | PASS — 24/24 |
|
||||
| Command Pedagogy | PASS — records 24, findings 0 |
|
||||
| git diff --check | PASS |
|
||||
| standalone verify-pipeline.py | PASS — exit 0 |
|
||||
| full unittest regression | PASS — 391 tests, skipped 14 |
|
||||
| live source reconciliation | **UNVERIFIABLE** |
|
||||
|
||||
pipeline과 unittest는 병렬로 돌리지 않았다.
|
||||
|
||||
---
|
||||
|
||||
# 6. 왜 자동 PASS인데 아직 완료가 아닌가
|
||||
|
||||
현재 자동 검사는 구조적으로 깨진 문서, tree, figure provenance, prose hard error, pipeline regression을 잘 잡는다.
|
||||
|
||||
하지만 다음은 자동 검사기가 보장하지 않는다.
|
||||
|
||||
- 한 문서의 첫 정의와 본문 정의가 의미상 서로 다른지
|
||||
- 제목이 본문보다 범위를 넓히는지
|
||||
- 프로젝트 구현 사실을 OAuth 일반 규칙으로 오해하게 만드는지
|
||||
- Referrer-Policy 같은 조건을 생략해 문장이 과도하게 단정적인지
|
||||
|
||||
실제로 `check-ssot-facts.py` 자체도:
|
||||
|
||||
> 적힌 것이 맞는지만 본다. SSOT가 빠뜨린 finding은 찾지 못한다.
|
||||
|
||||
는 한계를 출력한다.
|
||||
|
||||
따라서 자동 PASS만으로 완료 판정을 내리지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 7. 수정 우선순위
|
||||
|
||||
## 1순위 — K04 완전 종료
|
||||
|
||||
다음 문서의 public/confidential 정의형 문장을 같은 scope로 맞춘다.
|
||||
|
||||
1. `final/document.md`
|
||||
2. `reference-public-confidential-client.md`
|
||||
3. `case-browser-credential-boundary.md`
|
||||
4. `case-ap2-split-custody.md`
|
||||
|
||||
일반 정의와 프로젝트의 `client_secret_basic` 구현 사실을 분리한다.
|
||||
|
||||
## 2순위 — K03 제목 정리
|
||||
|
||||
`reference-authorization-code-endpoints.md`
|
||||
|
||||
> Token Endpoint에서 비로소 클라이언트를 인증한다
|
||||
|
||||
를 conditional/confidential scope가 드러나는 제목으로 바꾼다.
|
||||
|
||||
## 3순위 — Referrer scope
|
||||
|
||||
같은 Reference의 두 문장을 Referrer-Policy 조건을 포함하도록 수정한다.
|
||||
|
||||
## 4순위 — figure review 상태
|
||||
|
||||
내용은 직접 판독상 문제 없지만, 가능하면 techviz 정식 review 경로로 stale context 상태를 닫는다.
|
||||
|
||||
---
|
||||
|
||||
# 8. 수정 후 재검증 순서
|
||||
|
||||
1. stale client-type definition 검색
|
||||
2. token endpoint heading/context 직접 판독
|
||||
3. referrer 일반화 검색
|
||||
4. SSOT ↔ Record semantic consistency
|
||||
5. Natural prose
|
||||
6. Voice
|
||||
7. Tree
|
||||
8. Layout
|
||||
9. Figure Text / Provenance / Overlap
|
||||
10. Required Content / SSOT Facts
|
||||
11. Command Pedagogy
|
||||
12. `git diff --check`
|
||||
13. standalone `verify-pipeline.py`
|
||||
14. full unittest regression
|
||||
|
||||
standalone pipeline과 unittest는 순차 실행한다.
|
||||
|
||||
---
|
||||
|
||||
# 9. 기술 근거
|
||||
|
||||
- RFC 6749 §2.1 — Client Types
|
||||
- public/confidential은 client credential confidentiality 및 secure client authentication 능력 기준
|
||||
- RFC 6749 Token Endpoint
|
||||
- confidential client 또는 client credentials가 발급된 client가 token endpoint에서 인증
|
||||
- 모든 public client가 token endpoint에서 client authentication을 하는 것은 아님
|
||||
- RFC 9700 — OAuth 2.0 Security Best Current Practice
|
||||
- public client PKCE 요구
|
||||
- confidential client에도 PKCE 권고
|
||||
- asymmetric client authentication 권고
|
||||
- Keycloak RealmRepresentation
|
||||
- `refreshTokenMaxReuse`는 Integer
|
||||
- lifespan 계열 필드와 별도 속성
|
||||
- MDN Referrer-Policy
|
||||
- 기본 `strict-origin-when-cross-origin`
|
||||
- same-origin과 cross-origin에서 Referer 전송 범위가 다름
|
||||
|
||||
---
|
||||
|
||||
# 최종 판정
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
직전 리뷰를 무시하거나 새로운 방향으로 리뷰를 바꾼 것이 아니다.
|
||||
|
||||
오히려 직전 K01~K06을 같은 기준으로 다시 추적했을 때:
|
||||
|
||||
- K01 CLOSED
|
||||
- K02 CLOSED
|
||||
- K03 PARTIAL
|
||||
- K04 PARTIAL
|
||||
- K05 CLOSED
|
||||
- K06 CLOSED
|
||||
|
||||
라는 상태다.
|
||||
|
||||
남은 핵심은 **public/confidential client 정의의 scope를 SSOT와 Record 전체에서 통일하는 것**, **token endpoint client authentication 제목을 본문 범위와 맞추는 것**, **Referrer-Policy 조건을 반영하는 것**이다.
|
||||
|
||||
이 세 축이 닫히고 동일한 전체 회귀가 다시 통과하면 그때 완료 여부를 다시 판정한다.
|
||||
@@ -0,0 +1,971 @@
|
||||
# keycloak 7단계 하네스 기준 재검토 — 2026-09-19
|
||||
|
||||
## 최종 판정
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
이번 리뷰의 정본 기준은 기술 정확성 자체가 아니라 다음 하네스 계약이다.
|
||||
|
||||
`.agents/skills/running-tech-log-pipeline/SKILL.md`
|
||||
|
||||
문서 작성은 다음 7단계를 기준으로 판단한다.
|
||||
|
||||
| 단계 | 계약 |
|
||||
|---|---|
|
||||
| S1 | 코드베이스 → SSOT |
|
||||
| S2 | SSOT → 분해 계약 |
|
||||
| S3 | 글감 → 기록 |
|
||||
| S4 | 기록 → 그림 |
|
||||
| S5 | AI 티 제거 |
|
||||
| S6 | 일한 사람의 목소리 |
|
||||
| S7 | Studio 저장 |
|
||||
|
||||
이번 리뷰에서 특히 확인하는 사용자 기준은 다음 세 가지다.
|
||||
|
||||
1. **AI 티를 제거하고 사람이 쓴 기술 문서처럼 읽히는가**
|
||||
2. **그림이 필요한 곳에 적절한 SVG를 쓰고, 필요 없는 곳에 억지 그림을 넣지 않았는가**
|
||||
3. **사람이 따라 치는 명령이 있다면 이해하기 쉬운 형태인가**
|
||||
|
||||
기술 사실 오류는 이 세 기준과 별개로 버리지 않는다. S3의 evidence/fact review 범위에서 보조 finding으로 유지한다.
|
||||
|
||||
---
|
||||
|
||||
# 1. 7단계 절차 준수 상태
|
||||
|
||||
## 1.1 런 원장으로 확인되는 기록
|
||||
|
||||
현재 keycloak 기록은 24개다.
|
||||
|
||||
현재 런 원장:
|
||||
|
||||
- run.json 13개
|
||||
- 고유 기록 기준 12개
|
||||
- `question-bff-state-store.md`만 두 런이 존재
|
||||
|
||||
따라서 **24개 중 12개는 7단계 런 원장으로 절차를 확인할 수 있고, 나머지 12개는 현재 결과물로만 역검증할 수 있다.**
|
||||
|
||||
이것은 과거 기록에 원장을 새로 만들어 채우라는 뜻이 아니다.
|
||||
|
||||
하네스 계약 자체가:
|
||||
|
||||
> 지나간 일에 원장을 소급해 만드는 것은 영수증 위조다.
|
||||
|
||||
라고 명시한다.
|
||||
|
||||
따라서 **기존 12개 무원장 기록에 가짜 원장을 만들면 안 된다.**
|
||||
|
||||
현재 존재하는 keycloak run.json 13개는 각각:
|
||||
|
||||
`python3 scripts/verify-pipeline-run.py <run.json>`
|
||||
|
||||
으로 다시 확인했고 **전부 exit 0**이다.
|
||||
|
||||
원장이 있는 기록의 공통 흐름은 다음과 같다.
|
||||
|
||||
- S1 SKIPPED — 기존 SSOT 사용
|
||||
- S2 SKIPPED — 기존 PROMOTE/CONFIRMED tree node 사용
|
||||
- S3 DONE
|
||||
- S4 SKIPPED — 이번 수정에서 새 그림 소재 없음
|
||||
- S5 DONE
|
||||
- S6 DONE
|
||||
- S7 SKIPPED — Studio 반입 요청 없음
|
||||
|
||||
skip reason도 전부 기록돼 있어 절차적으로는 유효하다.
|
||||
|
||||
### 판정
|
||||
|
||||
**현재 존재하는 run ledger는 정상이다.**
|
||||
|
||||
다만 전체 24개 기록에 대해:
|
||||
|
||||
> “모든 문서가 실제로 7단계 파이프라인을 거쳤다”
|
||||
|
||||
고 말할 수는 없다.
|
||||
|
||||
**12개는 procedural evidence 없음**으로 남긴다.
|
||||
|
||||
---
|
||||
|
||||
# 2. S1 — 코드베이스 → SSOT
|
||||
|
||||
## 현재 상태
|
||||
|
||||
`docs/keycloak/final/document.md` 존재.
|
||||
|
||||
Project Layout:
|
||||
|
||||
- error 0
|
||||
|
||||
Tech Log Tree에서 source repository는:
|
||||
|
||||
`/home/donghyeon/workspace/keycloak-pattern`
|
||||
|
||||
을 가리킨다.
|
||||
|
||||
현재 머신에는 이 source repository가 없으므로 live reconciliation은:
|
||||
|
||||
**UNVERIFIABLE**
|
||||
|
||||
이다.
|
||||
|
||||
## 판정
|
||||
|
||||
S1 산출물 구조는 정상이다.
|
||||
|
||||
다만 현재 리뷰에서 source repo를 다시 대조할 수 없으므로:
|
||||
|
||||
- 기존 SSOT가 현재 코드와 정확히 같은가
|
||||
- 최근 코드 변경이 SSOT에 빠졌는가
|
||||
|
||||
는 확인할 수 없다.
|
||||
|
||||
이를 PASS로 올리지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 3. S2 — SSOT → 분해 계약
|
||||
|
||||
현재:
|
||||
|
||||
- records = 24
|
||||
- topics = 1
|
||||
- nodes = 24
|
||||
- written = 24
|
||||
- unwritten = 0
|
||||
- PROMOTE = 24
|
||||
|
||||
`verify-tech-log-tree.py keycloak`:
|
||||
|
||||
**PASS**
|
||||
|
||||
현재 24개 기록은 tree에 모두 연결되어 있다.
|
||||
|
||||
## 판정
|
||||
|
||||
**S2는 현재 산출물 기준 정상이다.**
|
||||
|
||||
---
|
||||
|
||||
# 4. S3 — 글감 → 기록
|
||||
|
||||
Required Content:
|
||||
|
||||
**PASS — 24 records · error 0**
|
||||
|
||||
Record Audit:
|
||||
|
||||
**문제 0**
|
||||
|
||||
종류 구성:
|
||||
|
||||
- Case 4
|
||||
- Concept 6
|
||||
- Decision 2
|
||||
- Question 4
|
||||
- Reference 8
|
||||
- Setup 0
|
||||
|
||||
Reference / Question / Decision에 SVG를 억지로 넣지 않은 것도 하네스 계약과 맞다.
|
||||
|
||||
## S3 보조 finding — 기술 사실
|
||||
|
||||
직전 기술 리뷰에서 남은 항목은 S3/fact-review 관점에서 유지한다.
|
||||
|
||||
### F-S3-01 — Public/Confidential client 정의 scope 통일 필요
|
||||
|
||||
다음 위치에 shared client secret만으로 client type을 정의하는 문장이 남아 있다.
|
||||
|
||||
- `final/document.md`
|
||||
- `reference-public-confidential-client.md`
|
||||
- `case-browser-credential-boundary.md`
|
||||
- `case-ap2-split-custody.md`
|
||||
|
||||
일반 OAuth client type 정의와 이 프로젝트의 `client_secret_basic` 구현 사실을 분리해야 한다.
|
||||
|
||||
### F-S3-02 — Token Endpoint 제목 scope
|
||||
|
||||
`reference-authorization-code-endpoints.md`
|
||||
|
||||
현재:
|
||||
|
||||
> Token Endpoint에서 비로소 클라이언트를 인증한다
|
||||
|
||||
본문은 이미 conditional/confidential scope로 수정됐으므로 제목도 같은 범위로 맞춘다.
|
||||
|
||||
### F-S3-03 — Referrer-Policy 조건
|
||||
|
||||
Authorization Endpoint URL이 referrer에 남는다는 문장은 Referrer-Policy 조건을 붙여야 한다.
|
||||
|
||||
이 세 건은 **이번 하네스 리뷰의 주축은 아니지만 S3 fact review에서 그대로 고쳐야 할 항목**이다.
|
||||
|
||||
---
|
||||
|
||||
# 5. S4 — 기록 → 그림
|
||||
|
||||
이 단계가 이번 재검토에서 가장 중요한 차이를 만들었다.
|
||||
|
||||
하네스의 그림 판단 기준은 세 관문이다.
|
||||
|
||||
1. Case / Concept / Setup인가
|
||||
2. 표로 해결되는 내용이 아닌가
|
||||
3. 그림이 없으면 독자가 보지 못하는 구조·순서·경계가 있는가
|
||||
|
||||
그리고 Concept은:
|
||||
|
||||
> 남의 것이 어떤 순서·구조로 동작하나
|
||||
|
||||
를 `sequence`, `component-flow` 등으로 표현한다.
|
||||
|
||||
---
|
||||
|
||||
## 5.1 현재 존재하는 SVG
|
||||
|
||||
TechViz spec은 10개다.
|
||||
|
||||
| SVG | profile | 판정 |
|
||||
|---|---|---|
|
||||
| ap1-browser-bearer-flow | sequence | 적절 |
|
||||
| ap1-direct-architecture | component-flow | 적절 |
|
||||
| ap2-mediator-architecture | component-flow | 적절 |
|
||||
| ap2-mediator-handoff-flow | sequence | 적절 |
|
||||
| ap3-bff-architecture | component-flow | 적절 |
|
||||
| ap3-bff-session-flow | sequence | 적절 |
|
||||
| ap3-csrf-boundary | component-flow | 적절 |
|
||||
| ap4-edge-forward-auth-flow | sequence | 적절 |
|
||||
| ap4-edge-trust-architecture | component-flow | 적절 |
|
||||
| login-api-phase-split | component-flow | 적절 |
|
||||
|
||||
자동 검사:
|
||||
|
||||
- Figure Text PASS
|
||||
- Figure Provenance PASS
|
||||
- Figure Overlap PASS
|
||||
- 현재 SVG 10개 모두 techviz 정본 존재
|
||||
|
||||
### 기존 그림 자체에 대한 판정
|
||||
|
||||
**현재 연결된 SVG 10개에서 “이 기록에 왜 이 그림이 있는지 모르겠다”는 그림은 발견하지 않았다.**
|
||||
|
||||
AP1~AP4 Case의 architecture + sequence 조합도 각기:
|
||||
|
||||
- custody / boundary
|
||||
- 실제 요청 순서
|
||||
|
||||
를 나눠 보여 주므로 중복이라 보기 어렵다.
|
||||
|
||||
CSRF 그림과 Forward-Auth 그림의 Concept 재사용도 같은 관계를 설명하므로 허용 가능하다.
|
||||
|
||||
---
|
||||
|
||||
# 6. S4 누락 Finding
|
||||
|
||||
## F-S4-01 — HIGH — Bearer JWT validation chain은 SVG가 있어야 한다
|
||||
|
||||
파일:
|
||||
|
||||
`concept/concept-bearer-jwt-validation-chain.md`
|
||||
|
||||
현재 본문은 다음 변환을 text fence로만 표현한다.
|
||||
|
||||
```text
|
||||
raw Bearer JWT
|
||||
→ NimbusJwtDecoder(JWK signature)
|
||||
→ default issuer + timestamp validators
|
||||
→ AudienceValidator("keycloak-pattern-api")
|
||||
→ validated Jwt
|
||||
→ KeycloakRealmRoleConverter
|
||||
→ authenticated principal + ROLE_* authorities
|
||||
```
|
||||
|
||||
이 기록의 제목 자체가:
|
||||
|
||||
> Bearer JWT가 인증된 principal이 되기까지
|
||||
|
||||
다.
|
||||
|
||||
즉 독자 질문이 명확하게 **“입력이 어떤 단계의 검증과 변환을 지나 principal이 되는가”**다.
|
||||
|
||||
이는 하네스의 Concept 그림 기준인:
|
||||
|
||||
- sequence
|
||||
- component-flow
|
||||
- 변환 사슬
|
||||
|
||||
에 정확히 해당한다.
|
||||
|
||||
### 왜 text fence만으로 부족한가
|
||||
|
||||
현재 text fence는 문장보다 낫지만:
|
||||
|
||||
- Spring이 맡는 구간
|
||||
- 직접 구성한 validator/converter 구간
|
||||
- 검증과 변환의 경계
|
||||
|
||||
를 공간적으로 구분하지 못한다.
|
||||
|
||||
### 권장 그림
|
||||
|
||||
예:
|
||||
|
||||
`bearer-jwt-validation-chain`
|
||||
|
||||
profile:
|
||||
|
||||
`component-flow`
|
||||
|
||||
핵심 노드:
|
||||
|
||||
`Bearer input → JwtDecoder → issuer/time validation → audience validation → validated Jwt → role converter → principal`
|
||||
|
||||
Spring framework-owned 단계와 project-owned 단계가 근거상 가능하면 group으로 나눈다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S4 누락.**
|
||||
|
||||
---
|
||||
|
||||
## F-S4-02 — HIGH — IdP Brokering은 SVG가 있어야 한다
|
||||
|
||||
파일:
|
||||
|
||||
`concept/concept-idp-brokering.md`
|
||||
|
||||
현재 text fence:
|
||||
|
||||
```text
|
||||
Google identity assertion
|
||||
→ Keycloak broker validation
|
||||
→ provider alias + upstream sub로 account identity 결정
|
||||
→ Keycloak local user/session
|
||||
→ Keycloak authorization code
|
||||
→ AP1·AP2·AP3·AP4 중 선택한 downstream 경계
|
||||
```
|
||||
|
||||
그리고 본문은:
|
||||
|
||||
> 두 개의 OAuth 왕복이 이어진다
|
||||
|
||||
를 핵심으로 삼는다.
|
||||
|
||||
이 Concept에서 독자가 봐야 하는 것은:
|
||||
|
||||
- upstream IdP ↔ Keycloak broker
|
||||
- Keycloak broker ↔ application
|
||||
- issuer가 어디에서 끊기는지
|
||||
- 외부 IdP가 다섯 번째 application pattern이 아닌 이유
|
||||
|
||||
다.
|
||||
|
||||
이건 표보다 **경계와 순서**가 중요하다.
|
||||
|
||||
### 권장 그림
|
||||
|
||||
예:
|
||||
|
||||
`idp-broker-upstream-downstream-boundary`
|
||||
|
||||
profile:
|
||||
|
||||
`two-zone-pipeline` 또는 `sequence`
|
||||
|
||||
핵심 경계:
|
||||
|
||||
`Google/upstream → Keycloak broker → AP1~AP4 application boundary`
|
||||
|
||||
### 판정
|
||||
|
||||
**S4 누락.**
|
||||
|
||||
---
|
||||
|
||||
## 6.3 SVG가 없는 것이 맞는 Concept
|
||||
|
||||
파일:
|
||||
|
||||
`concept-browser-credential-storage.md`
|
||||
|
||||
이 기록의 핵심 비교는 이미:
|
||||
|
||||
| 위치 | 새로고침 뒤 | JavaScript가 읽나 | 요청에 자동으로 붙나 |
|
||||
|
||||
표로 표현돼 있다.
|
||||
|
||||
관계선을 제거해도 의미가 남는 비교이므로 하네스 기준상 **표가 맞다.**
|
||||
|
||||
따라서 이 기록에 SVG가 없는 것은 문제 아니다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S4 SKIP 적절.**
|
||||
|
||||
---
|
||||
|
||||
# 7. S5 — AI 티 제거
|
||||
|
||||
기계 검사:
|
||||
|
||||
- Natural Prose: 24/24 hard error 0
|
||||
|
||||
하지만 하네스는 이것만으로 S5 완료라고 하지 않는다.
|
||||
|
||||
`style_profile.mjs`도 함께 보고, 마지막에는 사람이 읽어야 한다.
|
||||
|
||||
현재 24개 중 14개가 style profile의 참고 범위를 하나 이상 벗어난다.
|
||||
|
||||
이 수치 자체는 FAIL이 아니다.
|
||||
|
||||
하네스가 명시적으로:
|
||||
|
||||
> 수치를 맞추려고 문장을 넣지 않는다.
|
||||
|
||||
고 하기 때문이다.
|
||||
|
||||
따라서 아래는 **수치 + 실제 문장 패턴이 함께 문제인 문서만** 추렸다.
|
||||
|
||||
---
|
||||
|
||||
# 8. S5 재작업 Finding
|
||||
|
||||
## F-S5-01 — HIGH — reference-pattern-selection에 작성 지시문이 남아 있다
|
||||
|
||||
파일:
|
||||
|
||||
`reference/reference-pattern-selection.md`
|
||||
|
||||
하네스 review checklist는 다음을 금지한다.
|
||||
|
||||
- `봐야 한다`
|
||||
- `먼저 본다`
|
||||
- `함께 적는다`
|
||||
- 작성 방법을 독자에게 지시하는 문장
|
||||
|
||||
현재 실제 문장:
|
||||
|
||||
52행:
|
||||
|
||||
> 성공 응답과 실패 응답까지 봐야 한다.
|
||||
|
||||
68행:
|
||||
|
||||
> 어떤 보안 요구사항과 운영 조건 때문에 그 구조를 골랐는지 함께 적는다.
|
||||
|
||||
74행:
|
||||
|
||||
> ... 어떻게 복구할지를 따로 확인해야 한다.
|
||||
|
||||
76행:
|
||||
|
||||
> ... 운영 항목까지 같이 적는다.
|
||||
|
||||
이 Reference는 “선택 기준”을 설명해야 하는데 중간중간 **문서를 어떻게 작성할지 지시하는 문체**가 섞인다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
작성 지시를 기준 자체로 바꾼다.
|
||||
|
||||
예:
|
||||
|
||||
기존:
|
||||
|
||||
> 성공 응답과 실패 응답까지 봐야 한다.
|
||||
|
||||
방향:
|
||||
|
||||
> 비교 입력에는 실제 endpoint, method, 중간 credential, 성공·실패 응답이 포함된다.
|
||||
|
||||
기존:
|
||||
|
||||
> 운영 항목까지 같이 적는다.
|
||||
|
||||
방향:
|
||||
|
||||
> 선택 기준에는 재시작, replica 이동, 저장소 장애, secret rotation 같은 운영 조건도 포함된다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S5 재작업 필요.**
|
||||
|
||||
---
|
||||
|
||||
## F-S5-02 — HIGH — reference-public-confidential-client가 반복 정의 + 영문 밀도가 높다
|
||||
|
||||
style profile:
|
||||
|
||||
- 평균 문장 길이 77.6
|
||||
- 120자 초과 문장 비율 0.125
|
||||
- 문장당 bare English word 3.77
|
||||
- 한글 비율 0.50
|
||||
|
||||
4개 지표가 동시에 범위를 벗어난 유일한 기록이다.
|
||||
|
||||
문제는 숫자가 아니라 실제 본문에서도 같은 정의가 반복된다는 점이다.
|
||||
|
||||
23행:
|
||||
|
||||
> OAuth 클라이언트의 종류는 클라이언트 시크릿을 안전하게 보관할 수 있는지로 정한다.
|
||||
|
||||
41행:
|
||||
|
||||
> OAuth client type은 authorization server에 대해 client credential의 기밀성을 유지하고 ...
|
||||
|
||||
47행:
|
||||
|
||||
> 클라이언트 종류는 client credential을 안전하게 보호하고 ...
|
||||
|
||||
85행:
|
||||
|
||||
> 클라이언트 종류는 authorization server에 대한 client credential 보호와 ...
|
||||
|
||||
같은 정의를 다른 영문 조합으로 여러 번 반복한다.
|
||||
|
||||
이건 S5가 제거해야 하는:
|
||||
|
||||
- 반복 문형
|
||||
- 결론 재진술
|
||||
- 과도한 technical English 밀도
|
||||
|
||||
에 해당한다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
정의는 앞에서 한 번 정확하게 고정한다.
|
||||
|
||||
그 뒤에는:
|
||||
|
||||
- AP1에서 왜 public인지
|
||||
- AP2~AP4에서 왜 confidential인지
|
||||
- client type과 token custody가 왜 다른 축인지
|
||||
|
||||
실제 프로젝트 예시로 바로 들어간다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S5 재작업 필요.**
|
||||
|
||||
---
|
||||
|
||||
## F-S5-03 — MEDIUM — reference-idp-federation-boundary의 문장이 과도하게 길다
|
||||
|
||||
style profile:
|
||||
|
||||
- 평균 79.5자
|
||||
- 120자 초과 12%
|
||||
|
||||
특히:
|
||||
|
||||
34행
|
||||
46행
|
||||
66행
|
||||
77~78행
|
||||
|
||||
은 조건과 예외가 한 문장에 너무 많이 들어간다.
|
||||
|
||||
기술 내용은 비교적 명확하지만 사람이 읽을 때 한 문장 안에서:
|
||||
|
||||
`상황 → 조건 → 예외 → 판단`
|
||||
|
||||
을 여러 번 되짚게 된다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
사실을 줄이지 않고:
|
||||
|
||||
1. 현재 경계
|
||||
2. 경계가 깨지는 조건
|
||||
3. 아직 확인하지 않은 것
|
||||
|
||||
으로 문장을 나눈다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S5 한 번 더 필요.**
|
||||
|
||||
---
|
||||
|
||||
## F-S5-04 — MEDIUM — question-bff-state-store에 메타 안내 문장이 남아 있다
|
||||
|
||||
49행:
|
||||
|
||||
> 여기까지는 한 대에서 실행한 학습 환경에서 코드와 테스트로 확인한 것이다.
|
||||
|
||||
“확인 범위” 자체는 반드시 필요한 정보다.
|
||||
|
||||
문제는 `여기까지`라는 **문서 진행 안내 표현**이다.
|
||||
|
||||
하네스 checklist가 직접 금지하는 패턴이다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
> 현재 확인 범위는 단일 인스턴스 학습 환경이다.
|
||||
|
||||
처럼 사실을 바로 쓴다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S5 수정 필요.**
|
||||
|
||||
---
|
||||
|
||||
## F-S5-05 — MEDIUM — question-multi-instance-session의 선택지 리듬이 너무 균일하다
|
||||
|
||||
style profile:
|
||||
|
||||
- 독자 안내 표현 / 100문장 = 18.5
|
||||
|
||||
현재 선택지 1~4가 거의 같은 형태로 반복된다.
|
||||
|
||||
- 방식 정의
|
||||
- 장점
|
||||
- “다만/대신”
|
||||
- 추가 설계 항목
|
||||
|
||||
Question 종류 자체가 선택지를 병렬로 보여 주므로 어느 정도 대칭은 정상이다.
|
||||
|
||||
하지만 현재는 문장 길이와 전환어까지 고르게 반복돼 **생성형 문서의 템플릿 리듬**이 남는다.
|
||||
|
||||
### 수정 방향
|
||||
|
||||
모든 선택지를 같은 길이로 맞추지 않는다.
|
||||
|
||||
각 선택지에서 실제로 다른 판단 재료만 남긴다.
|
||||
|
||||
예:
|
||||
|
||||
- shared store → consistency / availability
|
||||
- sticky session → node loss
|
||||
- browser token → policy conflict
|
||||
- client-side cookie → trust boundary
|
||||
|
||||
로 중심이 다르므로 같은 문단 구조를 강제하지 않는다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S5 수동 재검토 필요.**
|
||||
|
||||
---
|
||||
|
||||
# 9. S5 참고 검토 대상
|
||||
|
||||
다음은 style_profile이 벗어났지만 **수치만으로 수정하면 안 되는 기록**이다.
|
||||
|
||||
- case-ap2-split-custody
|
||||
- case-browser-credential-boundary
|
||||
- concept-authorization-code-and-pkce
|
||||
- decision-bff-owns-token
|
||||
- question-edge-authorization-scope
|
||||
- question-refresh-rotation-replica
|
||||
- reference-authorization-code-endpoints
|
||||
- reference-bff-auth-design
|
||||
- reference-pattern-selection
|
||||
- reference-token-vs-session
|
||||
|
||||
기술 식별자와 OAuth 용어가 많아서 한글 비율이 낮은 문서는 단순 번역 대상으로 보면 안 된다.
|
||||
|
||||
S5는 **보호 구간을 유지한 상태에서 실제 반복/번역투가 있는 문장만** 고친다.
|
||||
|
||||
---
|
||||
|
||||
# 10. S6 — 일한 사람의 목소리
|
||||
|
||||
기계 검사:
|
||||
|
||||
**Voice 24/24 PASS**
|
||||
|
||||
하지만 `check_voice`는:
|
||||
|
||||
> 지어낸 목소리를 잡지만, 목소리가 모자란지는 재지 않는다.
|
||||
|
||||
현재 Case 쪽은 비교적 잘 되어 있다.
|
||||
|
||||
예:
|
||||
|
||||
`case-ap3-bff-session-csrf.md`
|
||||
|
||||
> 이 구성을 실행했을 때 브라우저 쪽 JavaScript가 받는 응답에는 OAuth 토큰이 없었다. 처음에는 토큰을 다루는 일도 함께 사라진 것처럼 보였다. BFF 코드를 따라가 보니 ...
|
||||
|
||||
이 서술은 SSOT에도 같은 관찰이 남아 있고, 실제로 본 것과 코드를 따라 확인한 것이 구분돼 있다.
|
||||
|
||||
이런 형태는 S6 의도와 맞다.
|
||||
|
||||
반대로 Reference와 Question은 사람 목소리를 억지로 넣지 않은 것도 맞다.
|
||||
|
||||
하네스가:
|
||||
|
||||
> 흔적이 없으면 멈춘다.
|
||||
|
||||
고 하기 때문이다.
|
||||
|
||||
### 판정
|
||||
|
||||
**S6 전체 재작성까지는 필요 없다.**
|
||||
|
||||
다만 S5에서 수정하는 문서들은 S5 뒤 반드시 S6을 다시 통과시킨다.
|
||||
|
||||
S5가 기존 사람의 선택·비교 문장을 깎아 버리지 않았는지 확인해야 한다.
|
||||
|
||||
---
|
||||
|
||||
# 11. 명령어 / Command Pedagogy
|
||||
|
||||
이번 프로젝트에는:
|
||||
|
||||
- Setup record = 0
|
||||
- shell/CLI block = 0
|
||||
- command-pedagogy finding = 0
|
||||
|
||||
따라서 사용자 기준:
|
||||
|
||||
> 이해하기 쉬운 명령어를 사용하고 있는가
|
||||
|
||||
는 이번 keycloak 프로젝트에서는 **적용 대상 없음(N/A)** 이다.
|
||||
|
||||
이는 문제를 놓친 것이 아니다.
|
||||
|
||||
하네스 계약도:
|
||||
|
||||
> shell/CLI block이 없으면 command planner/editor/reviewer를 모두 SKIPPED
|
||||
|
||||
하도록 한다.
|
||||
|
||||
현재 기록에는 HTTP 요청, JSON, text flow, JavaScript 예시는 있지만 **운영자가 직접 따라 치는 shell command는 없다.**
|
||||
|
||||
### 중요한 점
|
||||
|
||||
명령어 리뷰 기준을 만족시키려고 이 프로젝트에 shell 명령을 억지로 추가하면 안 된다.
|
||||
|
||||
Setup이 생기는 시점에:
|
||||
|
||||
`writing-practitioner-guides`
|
||||
|
||||
를 적용한다.
|
||||
|
||||
### 판정
|
||||
|
||||
**Command pedagogy = N/A / 정상 skip**
|
||||
|
||||
---
|
||||
|
||||
# 12. S7 — Studio 저장
|
||||
|
||||
24개 기록 모두 `studio:` URL이 존재한다.
|
||||
|
||||
현재:
|
||||
|
||||
- 일부는 `게시 중`
|
||||
- 일부는 `게시 전`
|
||||
|
||||
기존 7단계 재검토 run에서는 S7을:
|
||||
|
||||
> 사용자가 Studio 반입을 요청하지 않았다
|
||||
|
||||
는 이유로 SKIPPED했다.
|
||||
|
||||
이는 하네스 계약상 허용된다.
|
||||
|
||||
### 판정
|
||||
|
||||
현재 리뷰 작업에서 S7 자체는 결함으로 보지 않는다.
|
||||
|
||||
다만 이번 리뷰를 반영해 파일을 다시 수정한다면, 사용자가 Studio 반입을 요청하지 않은 한 저장소 파일까지만 고치고 **임의로 Studio version을 올리지 않는다.**
|
||||
|
||||
---
|
||||
|
||||
# 13. 기존 SVG가 실제로 적절한가
|
||||
|
||||
현재 10개 spec을 다시 확인했다.
|
||||
|
||||
profile 분포:
|
||||
|
||||
- component-flow
|
||||
- sequence
|
||||
|
||||
중심이다.
|
||||
|
||||
이는 현재 문서의 주제와 맞다.
|
||||
|
||||
AP1~AP4의 핵심은:
|
||||
|
||||
- credential owner
|
||||
- token custody
|
||||
- API caller
|
||||
- trust boundary
|
||||
- request sequence
|
||||
|
||||
이므로 generic card나 comparison 그림보다 component-flow / sequence가 맞다.
|
||||
|
||||
현재:
|
||||
|
||||
- Figure Text PASS
|
||||
- Figure Provenance PASS
|
||||
- Figure Overlap PASS
|
||||
|
||||
이며, `comparison` profile을 억지로 쓴 그림도 없다.
|
||||
|
||||
### 판정
|
||||
|
||||
**기존 10개 SVG 선택은 적절하다.**
|
||||
|
||||
문제는 S4 누락 2건이다.
|
||||
|
||||
---
|
||||
|
||||
# 14. 현재 자동 검증 결과
|
||||
|
||||
| 검사 | 결과 |
|
||||
|---|---|
|
||||
| Tech Log Tree | PASS — 24/24 |
|
||||
| Project Layout | PASS — error 0 |
|
||||
| Record Audit | PASS |
|
||||
| Required Content | PASS |
|
||||
| Figure Text | PASS |
|
||||
| Figure Provenance | PASS |
|
||||
| Figure Overlap | PASS |
|
||||
| Natural Prose hard gate | PASS — 24/24 |
|
||||
| Voice hard gate | PASS — 24/24 |
|
||||
| Command Pedagogy | PASS — shell blocks 0, findings 0 |
|
||||
| Existing keycloak run ledgers | 13/13 verify exit 0 |
|
||||
| Run coverage | 24 records 중 12 unique records만 ledger 있음 |
|
||||
| Source reconciliation | UNVERIFIABLE |
|
||||
|
||||
---
|
||||
|
||||
# 15. 이번 리뷰의 실제 수정 대상
|
||||
|
||||
## 반드시 수정
|
||||
|
||||
### A. S4
|
||||
|
||||
1. `concept-bearer-jwt-validation-chain.md`
|
||||
- validation chain SVG 추가
|
||||
|
||||
2. `concept-idp-brokering.md`
|
||||
- upstream / broker / application boundary SVG 추가
|
||||
|
||||
### B. S5
|
||||
|
||||
3. `reference-pattern-selection.md`
|
||||
- “봐야 한다 / 함께 적는다” 등 작성 지시문 제거
|
||||
|
||||
4. `reference-public-confidential-client.md`
|
||||
- 반복 정의 축소
|
||||
- 영문 용어 밀도 완화
|
||||
- 기술 정의는 한 번 정확하게
|
||||
|
||||
5. `reference-idp-federation-boundary.md`
|
||||
- 장문 분리
|
||||
|
||||
6. `question-bff-state-store.md`
|
||||
- “여기까지” 같은 문서 진행 안내 제거
|
||||
|
||||
7. `question-multi-instance-session.md`
|
||||
- 선택지의 기계적인 동일 리듬 완화
|
||||
|
||||
### C. S3/fact review
|
||||
|
||||
8. Public/Confidential client 정의 scope 통일
|
||||
9. Token Endpoint client-auth 제목 scope 정리
|
||||
10. Referrer-Policy 조건 반영
|
||||
|
||||
---
|
||||
|
||||
# 16. 수정할 필요 없는 것
|
||||
|
||||
다음은 고치려고 건드리지 않는다.
|
||||
|
||||
## command
|
||||
|
||||
shell/CLI가 없으므로 명령어를 새로 넣지 않는다.
|
||||
|
||||
## browser credential storage SVG
|
||||
|
||||
`concept-browser-credential-storage.md`는 비교표가 핵심이라 SVG를 추가하지 않는다.
|
||||
|
||||
## 기존 10개 SVG
|
||||
|
||||
현재 그림을 “새 하네스를 적용한다”는 이유로 전부 다시 만들지 않는다.
|
||||
|
||||
의미와 profile이 맞고 provenance도 정상이므로 재사용한다.
|
||||
|
||||
## 과거 run ledger
|
||||
|
||||
원장이 없는 12개 기록에 과거 원장을 소급 생성하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 17. 리뷰 반영 작업을 할 때의 올바른 절차
|
||||
|
||||
이번 리뷰를 실제로 반영할 때는 **과거 원장을 수정하지 않고 현재 수정용 새 run을 연다.**
|
||||
|
||||
수정 대상 기록마다:
|
||||
|
||||
1. 새 run.json 생성
|
||||
2. S1 — 기존 SSOT면 SKIPPED 사유
|
||||
3. S2 — 기존 PROMOTE/CONFIRMED면 SKIPPED 사유
|
||||
4. S3 — 기록 수정
|
||||
5. command initial analysis
|
||||
6. S4 — 필요한 두 Concept은 SVG 생성, 나머지는 적절한 SKIP
|
||||
7. S5 — prose rewrite
|
||||
8. S6 — voice pass
|
||||
9. final command analysis
|
||||
10. fact review
|
||||
11. S7 — 사용자 요청이 없으면 SKIPPED
|
||||
12. verify-pipeline-run
|
||||
13. project gates
|
||||
14. standalone pipeline
|
||||
15. full unittest
|
||||
|
||||
특히 **S4 → S5 → S6 순서를 바꾸지 않는다.**
|
||||
|
||||
그림을 붙인 뒤 생기는 설명 문단도 S5/S6을 거쳐야 하기 때문이다.
|
||||
|
||||
---
|
||||
|
||||
# 18. 완료 조건
|
||||
|
||||
다음이 모두 충족돼야 `keycloak = 완료`로 판정한다.
|
||||
|
||||
- [ ] Bearer JWT validation chain SVG 존재
|
||||
- [ ] IdP brokering boundary/flow SVG 존재
|
||||
- [ ] 새 SVG가 techviz spec/context/provenance를 갖춤
|
||||
- [ ] 새 SVG가 Figure Text/Overlap/Provenance PASS
|
||||
- [ ] reference-pattern-selection 작성 지시문 제거
|
||||
- [ ] public/confidential reference 반복 문형 정리
|
||||
- [ ] idp federation 장문 정리
|
||||
- [ ] bff-state-store 메타 안내 표현 제거
|
||||
- [ ] multi-instance 선택지 기계적 리듬 수동 재검토
|
||||
- [ ] S5 후 S6 재실행
|
||||
- [ ] 기술 사실 3건 정리
|
||||
- [ ] command lane은 shell/CLI 없음으로 정상 SKIP
|
||||
- [ ] 수정한 기록마다 현재 시점의 새 run ledger 생성
|
||||
- [ ] historical missing ledger는 소급 생성하지 않음
|
||||
- [ ] 모든 새 run verify exit 0
|
||||
- [ ] Tree/Layout/Required Content/Figure gates PASS
|
||||
- [ ] standalone pipeline PASS
|
||||
- [ ] unittest regression PASS
|
||||
- [ ] source repo 부재 시 reconciliation은 UNVERIFIABLE로 그대로 표기
|
||||
|
||||
---
|
||||
|
||||
# 최종 결론
|
||||
|
||||
이번 프로젝트는 **문서 구조 자체가 잘못된 상태는 아니다.**
|
||||
|
||||
현재 판단은 다음과 같다.
|
||||
|
||||
- **S1:** 구조 정상 / live source 대조는 UNVERIFIABLE
|
||||
- **S2:** 정상
|
||||
- **S3:** 구조 정상 / fact-review 잔여 3건
|
||||
- **S4:** 기존 SVG 10개 적절 / **필요한 SVG 2개 누락**
|
||||
- **S5:** hard gate 통과 / **사람이 읽으면 재작업 필요한 문서 5개**
|
||||
- **S6:** 전반적으로 정상 / S5 수정 뒤 다시 통과 필요
|
||||
- **S7:** 현재 범위에서 정상 skip 가능
|
||||
- **Command pedagogy:** **적용 대상 없음**
|
||||
|
||||
따라서:
|
||||
|
||||
**keycloak = 아직 더 봐야댐**
|
||||
|
||||
이다.
|
||||
|
||||
이번 판정의 핵심 이유는 기술 오류 자체보다 **7단계 하네스의 S4와 S5가 현재 결과물에 완전히 반영되지 않았기 때문**이다.
|
||||
@@ -0,0 +1,428 @@
|
||||
# Keycloak 최종 검증 리뷰 — current remediation 재검토
|
||||
|
||||
- 검토일: 2026-09-20
|
||||
- 대상: `docs/keycloak`, `runs/keycloak`
|
||||
- 기준: 현재 7단계 TechLog 하네스 계약
|
||||
- 최종 판정: **아직 더 봐야댐**
|
||||
|
||||
---
|
||||
|
||||
## 1. 결론
|
||||
|
||||
문서 내용 자체는 이전 리뷰의 지적사항이 정상적으로 반영됐다.
|
||||
|
||||
다음 항목은 모두 다시 확인했고 문제를 찾지 못했다.
|
||||
|
||||
- 기록 24개 Tree / Layout 기본 구조
|
||||
- Required Content
|
||||
- SSOT fact 형식 검사
|
||||
- Natural Prose
|
||||
- Voice
|
||||
- Command Pedagogy
|
||||
- Figure Text
|
||||
- Figure Overlap
|
||||
- Figure Provenance
|
||||
- 전체 pipeline
|
||||
- 전체 unittest
|
||||
|
||||
새로 만든 17개의 current remediation run도 `verify-pipeline-run.py` 자체는 **17/17 PASS**한다.
|
||||
|
||||
하지만 현재 하네스의 실제 계약과 원장 내용을 다시 대조하면 **아직 완료로 볼 수 없는 절차 문제가 남아 있다.**
|
||||
|
||||
핵심은 두 가지다.
|
||||
|
||||
1. current remediation에서 별도 subagent를 사용하지 않았다는 사실이 원장에 직접 남아 있다.
|
||||
2. S3/S5/S6의 evidence gate가 계약상 필요한 `--repo` 없이 실행됐다.
|
||||
|
||||
추가로 SVG 2개의 SSOT context/manifest도 stale 상태다.
|
||||
|
||||
따라서 Keycloak은 아직 다음 프로젝트로 넘기지 않는다.
|
||||
|
||||
> **keycloak = 아직 더 봐야댐**
|
||||
|
||||
---
|
||||
|
||||
# 2. 재검증 결과
|
||||
|
||||
## 2.1 Current remediation run
|
||||
|
||||
2026-09-19 current remediation run:
|
||||
|
||||
```text
|
||||
17 runs
|
||||
17/17 verify-pipeline-run.py PASS
|
||||
schemaVersion = 4
|
||||
```
|
||||
|
||||
수정된 17개 Record와 17개 current run의 record mapping도 일치한다.
|
||||
|
||||
## 2.2 Keycloak 프로젝트 검사
|
||||
|
||||
```text
|
||||
verify-tech-log-tree.py keycloak
|
||||
PASS
|
||||
records=24
|
||||
topics=1
|
||||
nodes=24
|
||||
written=24
|
||||
unwritten=0
|
||||
|
||||
verify-project-layout.py keycloak
|
||||
PASS
|
||||
error=0
|
||||
|
||||
audit-records.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-text.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-overlap.py keycloak
|
||||
PASS
|
||||
|
||||
check-figure-provenance.py keycloak
|
||||
PASS
|
||||
|
||||
check-required-content.py keycloak
|
||||
PASS
|
||||
|
||||
check-ssot-facts.py keycloak
|
||||
PASS
|
||||
|
||||
git diff --check -- docs/keycloak runs/keycloak reviews
|
||||
PASS
|
||||
```
|
||||
|
||||
## 2.3 S5 / S6 / Command Pedagogy
|
||||
|
||||
24개 Record 전체를 다시 검사했다.
|
||||
|
||||
```text
|
||||
Natural Prose: PASS 24/24
|
||||
Voice: PASS 24/24
|
||||
Command: PASS 24/24
|
||||
```
|
||||
|
||||
Keycloak 기록에는 shell/CLI block이 없으므로 command lane은 정상적으로 N/A다.
|
||||
|
||||
## 2.4 전체 회귀
|
||||
|
||||
```text
|
||||
verify-pipeline.py
|
||||
exit 0
|
||||
```
|
||||
|
||||
전체 테스트:
|
||||
|
||||
```text
|
||||
Ran 391 tests
|
||||
OK (skipped=14)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 3. F-01 — current remediation이 실제 subagent 분리 계약을 충족하지 않음
|
||||
|
||||
**등급: 완료 차단**
|
||||
|
||||
현재 하네스는 단순히 역할 이름을 원장에 적는 것이 아니라 각 단계마다 정해진 subagent를 별도로 실행하는 구조다.
|
||||
|
||||
`running-tech-log-pipeline/SKILL.md`와 `stage-contracts.md`는 다음을 요구한다.
|
||||
|
||||
```text
|
||||
S3 -> record-writer
|
||||
S4 -> diagram-maker
|
||||
S5 -> prose-rewriter
|
||||
S6 -> voice-writer
|
||||
technical evidence review -> fact-reviewer
|
||||
```
|
||||
|
||||
그리고 이 역할을 일반 세션이 대신 수행하는 것이 아니라 `Agent(subagent_type="...")` 형태로 분리하는 것이 이 하네스의 목적이다.
|
||||
|
||||
그런데 새 current remediation 원장의 `technicalEvidence.notes`에는 17개 모두 다음 사실이 직접 기록돼 있다.
|
||||
|
||||
```text
|
||||
별도 Agent tool은 노출되지 않아 current remediation 세션이
|
||||
fact-reviewer 계약을 직접 수행했다.
|
||||
```
|
||||
|
||||
즉 원장의:
|
||||
|
||||
```json
|
||||
"runBy": "fact-reviewer"
|
||||
```
|
||||
|
||||
와 실제 실행 설명이 일치하지 않는다.
|
||||
|
||||
`verify-pipeline-run.py`는 `runBy` 문자열이 계약상의 에이전트 이름인지와 해당 agent 정의 파일이 존재하는지를 검사한다.
|
||||
|
||||
하지만 저장소 밖에서 실제로 `Agent(subagent_type="fact-reviewer")`가 실행됐는지까지 증명하지는 않는다.
|
||||
|
||||
따라서 **기계 검사는 PASS지만 현재 원장 자체의 notes가 subagent isolation을 수행하지 않았다고 밝히고 있다.**
|
||||
|
||||
### 수정 원칙
|
||||
|
||||
기존 2026-09-19 remediation run을 고쳐서 마치 subagent가 실행된 것처럼 만들면 안 된다.
|
||||
|
||||
현재 run은 현재 시점의 실행 영수증으로 그대로 둔다.
|
||||
|
||||
별도 Agent/subagent 실행이 가능한 환경에서 **새 current remediation run을 다시 수행**해야 한다.
|
||||
|
||||
최소한 S3, S5, S6, fact-review는 계약에 지정된 독립 agent가 실제로 수행해야 한다.
|
||||
|
||||
---
|
||||
|
||||
# 4. F-02 — S3/S5/S6 evidence gate에 `--repo`가 빠짐
|
||||
|
||||
**등급: 완료 차단**
|
||||
|
||||
현재 stage contract의 S3 gate는 명확히 다음 명령을 요구한다.
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||||
```
|
||||
|
||||
S5와 S6도 문장 수정 이후 **S3 gate를 다시 실행**하는 계약이다.
|
||||
|
||||
하지만 17개 current remediation run을 전부 확인하면 S3/S5/S6가 다음 형태로 기록되어 있다.
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak
|
||||
```
|
||||
|
||||
즉 `--repo`가 빠져 있다.
|
||||
|
||||
결과:
|
||||
|
||||
```text
|
||||
missing --repo:
|
||||
17/17 runs
|
||||
S3, S5, S6 모두
|
||||
```
|
||||
|
||||
이 차이는 단순 옵션 차이가 아니다.
|
||||
|
||||
현재 올바른 계약 명령을 직접 실행하면:
|
||||
|
||||
```text
|
||||
node .../check_evidence.mjs keycloak --repo
|
||||
|
||||
[keycloak] 증빙 대조 (저장소 포함)
|
||||
? 1 저장소 경로가 이 기계에 없다
|
||||
/home/donghyeon/workspace/keycloak-pattern
|
||||
|
||||
불일치 0건 · 대조 불가 1건
|
||||
|
||||
exit=3
|
||||
```
|
||||
|
||||
즉 현재 원장의 evidence gate `exit=0`은 **source repository까지 대조해서 PASS한 결과가 아니다.**
|
||||
|
||||
하네스 SKILL도 exit code를 다음처럼 구분한다.
|
||||
|
||||
- 0: repo 대조 완료
|
||||
- 1: 실제 불일치
|
||||
- 2: 검사 대상 성립 안 함
|
||||
- 3: source repository 부재 → UNVERIFIABLE
|
||||
|
||||
현재 source repo가 없는 상태에서 `--repo`를 빼고 exit 0을 기록하면 이 구분이 사라진다.
|
||||
|
||||
### 수정 원칙
|
||||
|
||||
가장 올바른 해결은 source repository를 현재 머신에서 실제로 접근 가능하게 만드는 것이다.
|
||||
|
||||
현재 정본 경로:
|
||||
|
||||
```text
|
||||
/home/donghyeon/workspace/keycloak-pattern
|
||||
```
|
||||
|
||||
은 존재하지 않는다.
|
||||
|
||||
`/shared/document-haness/.run/keycloak-four-patterns`도 확인했지만 이는 Record 작업 산출물이고 source repository 대체물이 아니다.
|
||||
|
||||
source repository를 준비한 뒤 새 remediation run에서 S3/S5/S6의 `check_evidence.mjs keycloak --repo`를 실제로 통과시켜야 한다.
|
||||
|
||||
기존 2026-09-19 run의 gate 명령이나 exit code를 사후 수정하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 5. F-03 — SVG 2개의 source context manifest가 stale
|
||||
|
||||
**등급: 수정 필요**
|
||||
|
||||
현재 project layout은 error 0으로 PASS하지만 다음 경고가 남아 있다.
|
||||
|
||||
```text
|
||||
SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다
|
||||
|
||||
- ap1-direct-architecture
|
||||
- login-api-phase-split
|
||||
```
|
||||
|
||||
현재 두 TechViz `context.json` / `spec.json`은 새 SSOT hash를 가리킨다.
|
||||
|
||||
```text
|
||||
current document/context sha256
|
||||
ea10df24b892e2c57123a37a4b4f0d821e4f394353e6746f50df6a48342353e9
|
||||
```
|
||||
|
||||
하지만 두 asset manifest는 이전 source context hash를 계속 갖고 있다.
|
||||
|
||||
```text
|
||||
manifest source_context.document_sha256
|
||||
15e7c79412ac05ed39d6f3d0c14de8dda92abe7b402139ac2a90492dfe5e162c
|
||||
```
|
||||
|
||||
### 의미
|
||||
|
||||
Spec의 evidence line/hash는 새 SSOT에 맞게 갱신됐지만 최종 render/review 영수증인 manifest가 새 context 기준으로 닫히지 않았다.
|
||||
|
||||
현재 diff를 보면 도식의 node/edge 구조를 바꿔야 할 정도의 의미 변경은 확인되지 않았다.
|
||||
|
||||
- AP1: public client 설명의 scope가 더 정확하게 바뀜
|
||||
- login/API split: 도식 핵심 흐름 자체는 유지됨
|
||||
|
||||
따라서 새 그림을 설계할 필요는 없다.
|
||||
|
||||
### 필요한 조치
|
||||
|
||||
TechViz 정본을 기준으로 두 그림만 다시 render/review한다.
|
||||
|
||||
대상:
|
||||
|
||||
```text
|
||||
ap1-direct-architecture
|
||||
login-api-phase-split
|
||||
```
|
||||
|
||||
그 뒤:
|
||||
|
||||
```text
|
||||
techviz lint
|
||||
check-figure-text.py
|
||||
check-figure-overlap.py
|
||||
preview-figure.py
|
||||
check-figure-provenance.py
|
||||
verify-project-layout.py keycloak
|
||||
```
|
||||
|
||||
를 다시 돌린다.
|
||||
|
||||
목표는 `SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다` 경고 2건을 없애는 것이다.
|
||||
|
||||
SVG를 손으로 편집하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
# 6. 완료 차단이 아닌 항목
|
||||
|
||||
## 6.1 Historical record run coverage
|
||||
|
||||
전체 pipeline은 현재 Keycloak 24개 중 22개가 어떤 run ledger에 덮여 있다고 보고한다.
|
||||
|
||||
원장이 없는 historical record:
|
||||
|
||||
```text
|
||||
concept-forward-auth-and-auth-request.md
|
||||
decision-federation-not-a-pattern.md
|
||||
```
|
||||
|
||||
이 두 기록을 위해 과거 run ledger를 소급 생성하지 않는다.
|
||||
|
||||
현재 하네스 원칙상 historical ledger 부재 자체를 이번 remediation의 실패로 바꾸지 않는다.
|
||||
|
||||
## 6.2 Source repository absence
|
||||
|
||||
현재 source repository가 없으므로 live source reconciliation은:
|
||||
|
||||
```text
|
||||
UNVERIFIABLE
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
이를 PASS라고 쓰면 안 된다.
|
||||
|
||||
다만 문서 본문 자체의 기존 수정 사항은 SSOT / local evidence 기준으로 재검토했고 새로운 내용 오류는 찾지 못했다.
|
||||
|
||||
---
|
||||
|
||||
# 7. 현재 완료 조건
|
||||
|
||||
## 내용 / 문체 / SVG 구조
|
||||
|
||||
- [x] 기존 리뷰의 문서 내용 finding 반영
|
||||
- [x] Bearer JWT validation chain SVG
|
||||
- [x] IdP Brokering SVG
|
||||
- [x] Natural Prose 24/24 PASS
|
||||
- [x] Voice 24/24 PASS
|
||||
- [x] Command 24/24 PASS
|
||||
- [x] Figure Text PASS
|
||||
- [x] Figure Overlap PASS
|
||||
- [x] Figure Provenance PASS
|
||||
- [x] Required Content PASS
|
||||
- [x] Tree PASS
|
||||
- [x] 전체 pipeline exit 0
|
||||
- [x] unittest 391 PASS / 14 skipped
|
||||
|
||||
## 남은 완료 차단
|
||||
|
||||
- [ ] source repository를 실제 대조 가능한 위치에 준비
|
||||
- [ ] 새 current remediation run에서 S3 evidence gate를 `--repo`로 실행
|
||||
- [ ] S5 evidence gate를 `--repo`로 재실행
|
||||
- [ ] S6 evidence gate를 `--repo`로 재실행
|
||||
- [ ] S3/S5/S6를 실제 지정 subagent로 수행
|
||||
- [ ] fact-reviewer를 실제 독립 subagent로 수행
|
||||
- [ ] 새 run ledger 각각 `verify-pipeline-run.py` PASS
|
||||
- [ ] `ap1-direct-architecture` manifest/context 재검토
|
||||
- [ ] `login-api-phase-split` manifest/context 재검토
|
||||
- [ ] `verify-project-layout.py keycloak`에서 위 stale-context 경고 2건 제거
|
||||
- [ ] 마지막 전체 pipeline / unittest 재실행
|
||||
|
||||
---
|
||||
|
||||
# 8. 다음 작업 지시
|
||||
|
||||
현재 Keycloak 본문을 다시 대규모 수정하지 않는다.
|
||||
|
||||
다음 실행의 범위는 **절차 증빙과 stale SVG 2개만**이다.
|
||||
|
||||
순서:
|
||||
|
||||
```text
|
||||
1. keycloak-pattern source repo를 현재 머신에 준비
|
||||
2. current remediation run 새로 시작
|
||||
3. 지정 subagent로 S3 수행
|
||||
4. check_evidence.mjs keycloak --repo 확인
|
||||
5. 필요한 S4에서 stale 그림 2개만 rerender/review
|
||||
6. 지정 subagent로 S5
|
||||
7. --repo evidence gate 재실행
|
||||
8. 지정 subagent로 S6
|
||||
9. --repo evidence gate 재실행
|
||||
10. 독립 fact-reviewer
|
||||
11. S7은 Studio 요청이 없으므로 SKIP
|
||||
12. verify-pipeline-run.py
|
||||
13. keycloak project gates
|
||||
14. 전체 verify-pipeline.py
|
||||
15. 전체 unittest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 9. 최종 판정
|
||||
|
||||
현재 결과물의 **내용 품질 자체에는 추가 리뷰 finding이 없다.**
|
||||
|
||||
하지만 7단계 하네스 준수를 완료 조건으로 보는 현재 프로젝트 기준에서는 다음 두 가지가 실제 완료 차단이다.
|
||||
|
||||
1. subagent isolation을 실제로 수행하지 않은 current remediation run
|
||||
2. `--repo`가 빠진 S3/S5/S6 evidence gate
|
||||
|
||||
그리고 SVG 2개의 stale manifest도 정리해야 한다.
|
||||
|
||||
따라서 현재 상태는:
|
||||
|
||||
> **keycloak = 아직 더 봐야댐**
|
||||
|
||||
이 세 범위가 닫힌 뒤에만 Keycloak을 **완료**로 바꾸고 다음 프로젝트로 넘어간다.
|
||||
@@ -0,0 +1,102 @@
|
||||
# Keycloak 2026-09-20 역할 계약 기반 remediation
|
||||
|
||||
- 기준 리뷰: reviews/2026-09-20-keycloak-final-verification-review.md
|
||||
- 대상: docs/keycloak
|
||||
- 수행 방식: 별도 subagent 프로세스를 실행했다고 기록하지 않는다. 현재 세션이 각 .claude/agents/<role>.md와 연결된 SKILL.md/reference를 읽고 역할별 책임을 분리해 수행했다.
|
||||
- 변경 Record: 17개
|
||||
- 현재 판정: **아직 더 봐야됨**
|
||||
|
||||
## 이번 remediation에서 실제 수행한 역할
|
||||
|
||||
| 역할 | 계약 | 결과 |
|
||||
|---|---|---|
|
||||
| record-writer | .claude/agents/record-writer.md + writing-tech-log-records | 17개 Record의 kind/source/body/evidence 계약 재검토. body/prose/local evidence hard failure 0. --repo는 source checkout 부재로 exit 3 |
|
||||
| prose-rewriter | .claude/agents/prose-rewriter.md + rewriting-technical-prose-naturally | 17개 모두 check_prose exit 0. style profile 12건 nonzero는 advisory로 유지. 의미·불확실성 보존 위반 추가 발견 없음 |
|
||||
| voice-writer | .claude/agents/voice-writer.md + writing-as-the-person-who-did-it | 17개 모두 check_voice exit 0. 상류 자료에 없는 경험/감정 문장을 새로 넣지 않음 |
|
||||
| fact-reviewer | .claude/agents/fact-reviewer.md | local SSOT/evidence 역대조는 수행. exact source revision reconciliation은 완료하지 못해 VERDICT: FAIL |
|
||||
| diagram-maker | .claude/agents/diagram-maker.md + technical-visualizer | stale context 2개를 canonical spec/context로 재렌더. Figure gates PASS, layout warn 0 |
|
||||
|
||||
## F-01 — 역할별 계약 수행
|
||||
|
||||
사용자가 허용한 실행 방식에 맞춰 **역할을 실제로 읽고 역할별 책임을 분리해서 수행**했다.
|
||||
|
||||
중요한 제한:
|
||||
- Agent(subagent_type=...)를 실행했다고 주장하지 않는다.
|
||||
- 기존 2026-09-19 run.json을 실제 subagent 실행처럼 소급 수정하지 않는다.
|
||||
- 이번 역할별 검토는 이 폴더의 별도 artifact로 남긴다.
|
||||
- fact-reviewer가 source reconciliation을 완료하지 못했으므로 새 green remediation run을 만들지 않는다.
|
||||
|
||||
따라서 F-01의 “역할 계약을 실제 작업에 적용하지 않았다”는 문제는 이번 remediation에서 해소했다. **프로세스 격리 자체를 수행했다는 의미는 아니다.**
|
||||
|
||||
## F-02 — source repository reconciliation
|
||||
|
||||
현재 Tree가 요구하는 source repository:
|
||||
|
||||
~~~text
|
||||
/home/donghyeon/workspace/keycloak-pattern
|
||||
~~~
|
||||
|
||||
고정 revision:
|
||||
|
||||
~~~text
|
||||
AP1 64175266df05545f8f181fc91c1f0364bc47fce9
|
||||
AP2 d019846f8725bdb0badde33043b020dc252e32ff
|
||||
AP3 934c5da5d6edc2429dfb558b773656e46f21d677
|
||||
AP4 f4aea65dc6255eae07b20ebbe21e02fb6115e563
|
||||
~~~
|
||||
|
||||
실제 실행:
|
||||
|
||||
~~~text
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak --repo
|
||||
|
||||
exit = 3
|
||||
불일치 = 0
|
||||
대조 불가 = 1
|
||||
원인 = sourceRepository.path가 이 기계에 없음
|
||||
~~~
|
||||
|
||||
/shared, /home/donghyeon, Library의 과거 Keycloak source snapshot까지 확인했다. Library의 repomix-output.xml에는 같은 Keycloak 프로젝트의 실제 source와 develop-keycloak-pattern1..4 구조가 있고 refreshTokenMaxReuse=0도 존재하지만, 위 네 commit SHA는 포함하지 않는다. 따라서 해당 snapshot을 exact revision checkout처럼 취급하지 않는다.
|
||||
|
||||
**F-02는 아직 열려 있다.**
|
||||
|
||||
## F-03 — stale TechViz context
|
||||
|
||||
재렌더 대상:
|
||||
|
||||
~~~text
|
||||
ap1-direct-architecture
|
||||
login-api-phase-split
|
||||
~~~
|
||||
|
||||
canonical spec.json + context.json으로 다시 render했고 두 manifest의 source document hash가 현재 SSOT hash로 갱신됐다.
|
||||
|
||||
~~~text
|
||||
ea10df24b892e2c57123a37a4b4f0d821e4f394353e6746f50df6a48342353e9
|
||||
~~~
|
||||
|
||||
검증:
|
||||
|
||||
~~~text
|
||||
FIGURE TEXT: PASS
|
||||
FIGURE OVERLAP: PASS
|
||||
FIGURE PROVENANCE: PASS
|
||||
PROJECT LAYOUT: PASS — error 0 · warn 0
|
||||
~~~
|
||||
|
||||
F-03은 **완료**.
|
||||
|
||||
## 현재 남은 완료 조건
|
||||
|
||||
- [x] 역할별 contract/SKILL/reference를 읽고 직접 책임 분리 수행
|
||||
- [x] 17개 Record body/prose/voice/command hard failure 0
|
||||
- [x] style advisory를 수치 맞추기 위해 rewrite하지 않음
|
||||
- [x] stale TechViz 2개 재렌더
|
||||
- [x] Figure/Layout gates PASS
|
||||
- [x] 과거 run ledger 소급 수정 안 함
|
||||
- [ ] exact source repository 또는 revision-proven import source 확보
|
||||
- [ ] check_evidence.mjs keycloak --repo exit 0
|
||||
- [ ] 그 상태에서 fact-reviewer 최종 PASS
|
||||
- [ ] 그 상태에서만 새 remediation run 작성/검증
|
||||
|
||||
따라서 현재 Keycloak 최종 상태는 **아직 더 봐야됨**이다.
|
||||
@@ -0,0 +1,61 @@
|
||||
# record-writer 역할 재검토
|
||||
|
||||
## 읽은 계약
|
||||
|
||||
- .claude/agents/record-writer.md
|
||||
- .agents/skills/writing-tech-log-records/SKILL.md
|
||||
- references/record-kinds.md
|
||||
- references/from-ssot-to-records.md
|
||||
- references/tech-log-tree-contract.md
|
||||
- references/writing-each-kind.md
|
||||
- references/body-syntax.md
|
||||
- references/code-tables-diagrams.md
|
||||
- references/explaining.md
|
||||
- references/ai-tells.md
|
||||
- references/choosing-a-diagram.md
|
||||
- references/review-checklist.md
|
||||
- references/studio-draft-review.md
|
||||
|
||||
## 역할 기준
|
||||
|
||||
record-writer는 한 Record의 kind/field/source를 계약에 맞춰 쓰고, SSOT에 없는 사실을 만들어 넣지 않는다. 인용과 source는 SSOT에 역대조하고 S3의 evidence gate까지 확인해야 한다.
|
||||
|
||||
## 이번에 수행한 일
|
||||
|
||||
현재 수정된 17개 Record를 대상으로 다음을 다시 확인했다.
|
||||
|
||||
- Tree에 해당 파일이 존재하는지
|
||||
- frontmatter source가 final/document.md를 가리키는지
|
||||
- Record kind별 구조가 맞는지
|
||||
- studio-body.py 변환이 되는지
|
||||
- Studio body parser가 실제 frontend parser로 PASS하는지
|
||||
- prose hard error가 없는지
|
||||
- local SSOT/evidence 대조가 통과하는지
|
||||
- command/CLI가 없는 기록에 command 예시를 억지로 추가하지 않았는지
|
||||
|
||||
## 자동 결과
|
||||
|
||||
~~~text
|
||||
17/17 studio-body exit 0
|
||||
17/17 check_body exit 0
|
||||
17/17 check_prose exit 0
|
||||
17/17 command-pedagogy exit 0
|
||||
|
||||
check_evidence keycloak
|
||||
exit 0
|
||||
|
||||
check_evidence keycloak --repo
|
||||
exit 3
|
||||
mismatch 0
|
||||
unverifiable 1
|
||||
~~~
|
||||
|
||||
## 역할 판정
|
||||
|
||||
Record 구조와 현재 SSOT에 대한 local consistency에서는 추가 rewrite가 필요한 오류를 찾지 못했다.
|
||||
|
||||
다만 record-writer 계약은 source repository까지 대조하는 evidence gate를 요구한다. 원본 checkout이 없으므로 **S3 전체를 source-reconciled DONE으로 판정하지 않는다.**
|
||||
|
||||
특히 question-refresh-rotation-replica.md의 refreshTokenMaxReuse 의미는 현재 final/document.md에 직접 서술돼 있지 않다. 과거 Library source snapshot에는 refreshTokenMaxReuse=0과 “refresh token max reuse must be 0” 검증 코드가 있으나 exact AP1 revision provenance가 없다. 이 정보는 보조 자료일 뿐 --repo 대체물이 아니다.
|
||||
|
||||
**결론: 문서 구조/작성 품질은 완료, source-backed S3 증빙은 아직 더 봐야됨.**
|
||||
@@ -0,0 +1,45 @@
|
||||
# prose-rewriter 역할 재검토
|
||||
|
||||
## 읽은 계약
|
||||
|
||||
- .claude/agents/prose-rewriter.md
|
||||
- .agents/skills/rewriting-technical-prose-naturally/SKILL.md
|
||||
- references/article-shape.md
|
||||
- references/document-skeleton.md
|
||||
- references/editorial-rules.md
|
||||
- references/protected-content.md
|
||||
- references/regression-examples.md
|
||||
|
||||
## 역할 기준
|
||||
|
||||
이번 단계는 사실을 새로 만드는 단계가 아니다. 수치·버전·식별자·코드·명령·URL·불확실성·검증 범위를 보호한 채 번역투, 반복 문형, 진행 메타 문장 같은 표현만 정리한다. style_profile은 측정값이지 통과시키기 위한 목표가 아니다.
|
||||
|
||||
## 17개 기록 결과
|
||||
|
||||
- check_prose: 17/17 exit 0
|
||||
- check_voice: 17/17 exit 0
|
||||
- check_body: 17/17 exit 0
|
||||
- command pedagogy: 17/17 exit 0
|
||||
- style_profile: 12/17 nonzero advisory, 5/17 zero
|
||||
|
||||
style_profile nonzero는 hard failure로 취급하지 않았다. 다음과 같은 수치 맞추기 수정은 하지 않았다.
|
||||
|
||||
- OAuth/OIDC 공식 용어를 억지로 한글화
|
||||
- 문장 길이 비율을 맞추기 위한 의미 없는 분리/병합
|
||||
- 이유 연결어미 비율을 올리기 위한 접속어 삽입
|
||||
- 짧은 문장 비율을 맞추기 위한 내용 없는 문장 추가
|
||||
|
||||
## 변경 diff를 다시 읽은 결과
|
||||
|
||||
이번 변경의 문체 목적은 실제로 다음 범위 안에 있다.
|
||||
|
||||
- 작성 지시형 문장을 실제 비교 기준으로 전환
|
||||
- “여기까지” 같은 문서 진행 메타 표현 제거
|
||||
- public/confidential 정의 중복을 줄이고 프로젝트 사실과 일반 정의를 분리
|
||||
- IdP federation 장문을 의미 단위로 분리
|
||||
- multi-instance 선택지를 서로 다른 판단 축으로 재구성
|
||||
- CSRF 표현에서 “사용자 의도 판별” 같은 과도한 표현을 구체적 request/token 검증 의미로 좁힘
|
||||
|
||||
보호해야 할 기술적 범위를 style 수치 때문에 다시 바꿀 이유는 찾지 못했다.
|
||||
|
||||
**결론: prose-rewriter 역할 기준 완료.**
|
||||
@@ -0,0 +1,31 @@
|
||||
# voice-writer 역할 재검토
|
||||
|
||||
## 읽은 계약
|
||||
|
||||
- .claude/agents/voice-writer.md
|
||||
- .agents/skills/writing-as-the-person-who-did-it/SKILL.md
|
||||
- references/voice-moves.md
|
||||
- prose 보호 규칙과 Record kind 규칙
|
||||
|
||||
## 역할 기준
|
||||
|
||||
목소리는 “친근한 말투”를 만드는 것이 아니다. 상류 자료에서 실제 선택, 제약, 어긋남, 확인하지 못한 범위를 찾고 그것만 남긴다. 자료에 흔적이 없으면 **흔적 없음**이 결과이며 경험 서사를 만들지 않는다.
|
||||
|
||||
## 이번 재검토
|
||||
|
||||
17개 Record 모두 check_voice.mjs exit 0을 다시 확인했다.
|
||||
|
||||
변경 diff에서 새로 추가된 표현을 중심으로 다음을 봤다.
|
||||
|
||||
- “처음에는”, “해보니”, “놀랍게도”, “고민 끝에” 같은 근거 없는 경험 서사가 추가됐는가
|
||||
- “우리가/저희가 선택했다” 같은 1인칭 결정을 상류 자료 없이 만들었는가
|
||||
- 기존의 “확인하지 못했다” 범위를 감정적 결론으로 바꿨는가
|
||||
- Question/Reference를 억지로 Case 서사처럼 바꿨는가
|
||||
|
||||
추가 위반을 찾지 못했다.
|
||||
|
||||
Case의 기존 1인칭/선택 서사는 SSOT 자체에 AP1/AP2 선택 이유와 제약의 흔적이 있다. 이번 수정은 그 경험을 새로 만든 것이 아니라 public/confidential 범위를 좁힌 것이다.
|
||||
|
||||
Reference/Question 수정은 대부분 정의·검증 범위·선택지 축을 정리한 것이고, 자료에 없는 개인 경험을 새로 넣지 않았다. 따라서 이들에는 별도 “목소리 문장”을 추가하지 않았다.
|
||||
|
||||
**결론: voice-writer 역할 기준 완료.**
|
||||
@@ -0,0 +1,84 @@
|
||||
# fact-reviewer 역할 재검토
|
||||
|
||||
VERDICT: FAIL
|
||||
|
||||
이 FAIL은 “현재 문서에서 틀린 사실을 발견했다”는 뜻이 아니다. fact-reviewer 계약이 요구하는 **최종 source/evidence 역대조를 exact revision까지 완료하지 못했다**는 뜻이다.
|
||||
|
||||
## 읽은 계약
|
||||
|
||||
- .claude/agents/fact-reviewer.md
|
||||
- Record의 source가 가리키는 docs/keycloak/final/document.md
|
||||
- 현재 수정된 17개 Record diff
|
||||
- local evidence checker 결과
|
||||
- source repository contract (tech-log-tree.json)
|
||||
|
||||
## 현재 직접 대조한 핵심 주장
|
||||
|
||||
다음은 현재 SSOT에서 직접 근거를 확인했다.
|
||||
|
||||
1. AP1은 browser 환경에서 장기 client credential 기밀성과 trusted client authentication을 유지하기 어려운 public client다.
|
||||
2. AP2는 server-side confidential client이며 이 프로젝트에서 client_secret_basic을 사용한다.
|
||||
3. public/confidential client type과 browser token custody/API caller는 별도 축이다.
|
||||
4. AP2는 Authorization Code confidential client까지 확인했고 현재 구현을 PKCE S256 예시라고 확정하지 않는다.
|
||||
5. AP3의 SameSite와 CSRF는 서로 다른 방어선이며 raw XSRF-TOKEN/X-XSRF-TOKEN 검증 경로가 있다.
|
||||
6. Google federation은 upstream IdP 경계이고 downstream issuer는 Keycloak이다.
|
||||
7. 외부 identity key는 provider + upstream sub이며 email collision은 자동 병합하지 않는다.
|
||||
8. mock provider 검증은 실제 Google account/public HTTPS callback/consent 검증을 의미하지 않는다.
|
||||
9. Bearer JWT 검증은 signature/issuer·time/audience/role conversion을 분리한다.
|
||||
10. multi-instance/session 문제는 현재 단일 인스턴스 검증 범위를 넘는 open question이다.
|
||||
|
||||
## 대조하지 못한 것
|
||||
|
||||
### 1. exact source revisions
|
||||
|
||||
Tree가 고정한 네 revision을 가진 checkout이 현재 머신에 없다.
|
||||
|
||||
~~~text
|
||||
AP1 64175266df05545f8f181fc91c1f0364bc47fce9
|
||||
AP2 d019846f8725bdb0badde33043b020dc252e32ff
|
||||
AP3 934c5da5d6edc2429dfb558b773656e46f21d677
|
||||
AP4 f4aea65dc6255eae07b20ebbe21e02fb6115e563
|
||||
~~~
|
||||
|
||||
따라서:
|
||||
|
||||
~~~text
|
||||
check_evidence.mjs keycloak --repo
|
||||
exit 3
|
||||
mismatch 0
|
||||
unverifiable 1
|
||||
~~~
|
||||
|
||||
### 2. refreshTokenMaxReuse 설명의 exact revision provenance
|
||||
|
||||
현재 Record는 refreshTokenMaxReuse를 동일 refresh token의 **최대 재사용 횟수**로 설명하고 lifespan과 분리한다.
|
||||
|
||||
현재 final/document.md에는 이 identifier/설명이 직접 없다.
|
||||
|
||||
과거 Library의 실제 Keycloak source snapshot에서는 다음은 확인했다.
|
||||
|
||||
~~~text
|
||||
revokeRefreshToken == true
|
||||
refreshTokenMaxReuse == 0
|
||||
validator message = "refresh token max reuse must be 0"
|
||||
~~~
|
||||
|
||||
하지만 그 snapshot에는 Tree의 exact AP1 revision SHA가 없다. 따라서 source content의 보조 확인은 가능하지만 **revision-proven reconciliation은 아니다.**
|
||||
|
||||
### 3. Referrer-Policy 일반 규칙
|
||||
|
||||
현재 Record는 referrer에 전달되는 범위가 Referrer-Policy와 same-origin/cross-origin 관계에 따라 달라진다고 좁혔다. 이전의 “query가 항상 referrer에 남는다”보다 범위를 제한하는 수정이지만, 현재 SSOT에는 Referrer-Policy 자체가 직접 기록돼 있지 않다.
|
||||
|
||||
이 항목 역시 현재 local SSOT만으로는 source-derived claim으로 완결되지 않는다.
|
||||
|
||||
## 대조했는데 맞은 것
|
||||
|
||||
- 위 핵심 주장 10개 그룹: current SSOT와 일치하거나 현재 SSOT가 명시한 미검증 범위를 유지함.
|
||||
- local evidence checker: mismatch 0.
|
||||
|
||||
## 대조하지 못한 것
|
||||
|
||||
- live/exact revision source reconciliation: 1 source repository class.
|
||||
- 그 영향으로 exact source provenance를 요구하는 변경 주장 일부는 최종 확정하지 않음.
|
||||
|
||||
**결론: 문서 내용이 틀렸다고 판정한 것은 아니며, source repository가 복구되기 전에는 fact-reviewer 최종 PASS를 발급하지 않는다.**
|
||||
@@ -0,0 +1,56 @@
|
||||
# diagram-maker 역할 재검토
|
||||
|
||||
## 읽은 계약
|
||||
|
||||
- .claude/agents/diagram-maker.md
|
||||
- .agents/skills/technical-visualizer/SKILL.md
|
||||
- references/composition-profiles.md
|
||||
- references/visual-principles.md
|
||||
- references/format-selection.md
|
||||
- references/diagram-types.md
|
||||
- writing-tech-log-records/references/choosing-a-diagram.md
|
||||
|
||||
## F-03 대상
|
||||
|
||||
~~~text
|
||||
ap1-direct-architecture
|
||||
login-api-phase-split
|
||||
~~~
|
||||
|
||||
두 그림 모두 SVG를 손으로 수정하지 않고 canonical spec.json + context.json에서 다시 렌더했다.
|
||||
|
||||
## 실행
|
||||
|
||||
~~~text
|
||||
techviz doctor
|
||||
techviz lint <spec> --context <context> --json
|
||||
techviz render <spec> --context <context> -o <asset-dir>
|
||||
preview-figure.py --file <svg>
|
||||
check-figure-text.py keycloak
|
||||
check-figure-overlap.py keycloak
|
||||
check-figure-provenance.py keycloak
|
||||
verify-project-layout.py keycloak
|
||||
~~~
|
||||
|
||||
## 결과
|
||||
|
||||
두 manifest의 source document hash:
|
||||
|
||||
~~~text
|
||||
ea10df24b892e2c57123a37a4b4f0d821e4f394353e6746f50df6a48342353e9
|
||||
~~~
|
||||
|
||||
검증:
|
||||
|
||||
~~~text
|
||||
FIGURE TEXT: PASS — 그림 12장 · 문장 0건
|
||||
FIGURE OVERLAP: PASS — 그림 12장 · overlap 0
|
||||
FIGURE PROVENANCE: PASS — error 0
|
||||
PROJECT LAYOUT: PASS — error 0 · warn 0
|
||||
~~~
|
||||
|
||||
PNG preview 파일 생성도 성공했다.
|
||||
|
||||
주의: 현재 ChatGPT local image viewer와 Coka remote /tmp가 서로 다른 filesystem이라, 생성한 PNG를 이 세션의 vision surface에서 직접 열어 픽셀 단위 육안 판정까지 했다고 주장하지 않는다. 대신 renderer의 canonical output, overlap/text/provenance/layout gate를 재실행했고 stale-context warning 2건은 사라졌다.
|
||||
|
||||
**결론: F-03 완료.**
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"records": [
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/case/case-ap2-split-custody.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/case/case-browser-credential-boundary.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-authorization-code-and-pkce.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-bearer-jwt-validation-chain.md", "studioBody": 0, "body": 0, "prose": 0, "style": 0, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-browser-credential-storage.md", "studioBody": 0, "body": 0, "prose": 0, "style": 0, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-cookie-auth-csrf.md", "studioBody": 0, "body": 0, "prose": 0, "style": 0, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/concept/concept-idp-brokering.md", "studioBody": 0, "body": 0, "prose": 0, "style": 0, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-bff-state-store.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-edge-authorization-scope.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-refresh-rotation-replica.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-authorization-code-endpoints.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-bff-auth-design.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-idp-federation-boundary.md", "studioBody": 0, "body": 0, "prose": 0, "style": 0, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-pattern-selection.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-public-confidential-client.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0},
|
||||
{"file": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-token-vs-session.md", "studioBody": 0, "body": 0, "prose": 0, "style": 1, "voice": 0, "command": 0}
|
||||
],
|
||||
"evidenceLocal": 0,
|
||||
"evidenceRepo": 3
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
# keycloak-session-store 최종 검수 리뷰
|
||||
|
||||
- 검토일: 2026-09-20
|
||||
- 대상: `reviews/2026-09-20-keycloak-session-store-post-remediation-verification.md` 반영 결과
|
||||
- 판정: **아직 더 봐야댐**
|
||||
|
||||
## 1. 결론
|
||||
|
||||
이전 후속 리뷰에서 완료 차단으로 잡았던 핵심 항목은 **대부분 제대로 반영됐다.**
|
||||
|
||||
이번 검수에서 확인한 완료 항목:
|
||||
|
||||
1. `--repo` evidence gate 우회 제거
|
||||
2. source repository 부재를 `UNVERIFIABLE`로 명시적으로 모델링
|
||||
3. run verifier가 잘못된 evidence gate를 실제 FAIL시키도록 보강
|
||||
4. 33개 command-bearing Record / 992 shell block의 현재 project-level audit 작성
|
||||
5. Tree / 문체 / command parser 개선 유지
|
||||
6. 전체 project gate PASS
|
||||
7. 전체 pipeline exit 0
|
||||
8. 전체 unittest **406 PASS / 14 skipped**
|
||||
|
||||
따라서 **검증 계약과 command audit 문제는 이번에는 제대로 닫혔다.**
|
||||
|
||||
남은 것은 한 축뿐이다.
|
||||
|
||||
> **TechViz 7개의 주변 context snapshot warning이 아직 남아 있다.**
|
||||
|
||||
그림이 직접 근거로 삼는 `current_section`은 31/31 최신 SSOT와 일치하므로 그림의 기술 내용이 틀렸다고 보지는 않는다. 하지만 이전 리뷰에서 최종 cleanup 대상으로 지정한 warning이 실제 verifier에 그대로 남아 있으므로 아직 `완료`로 넘기지는 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 2. evidence gate — 반영 완료
|
||||
|
||||
새 schemaVersion 5 verification run 6개를 확인했다.
|
||||
|
||||
각 run의 S3/S5/S6 evidence gate는 실제로:
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak-session-store --repo
|
||||
```
|
||||
|
||||
를 실행하고 있다.
|
||||
|
||||
현재 머신에는 source repository가 없으므로 결과는:
|
||||
|
||||
```text
|
||||
exit: 3
|
||||
status: UNVERIFIABLE
|
||||
semanticId: evidence-repo
|
||||
reason: source repository unavailable on current machine
|
||||
acceptedByProjectReview: true
|
||||
```
|
||||
|
||||
로 보존된다.
|
||||
|
||||
대상 6 run 모두:
|
||||
|
||||
```text
|
||||
verify-pipeline-run.py
|
||||
→ PASS
|
||||
→ live source UNVERIFIABLE=['S3', 'S5', 'S6']
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
즉 이전처럼 `--repo`를 빼고 exit 0을 만든 것이 아니다.
|
||||
|
||||
### technicalEvidence도 일치
|
||||
|
||||
각 run의 `qualityReviews.technicalEvidence`에도:
|
||||
|
||||
```text
|
||||
liveSourceReconciliation: UNVERIFIABLE
|
||||
liveSourceReason: source repository unavailable on current machine
|
||||
acceptedByProjectReview: true
|
||||
```
|
||||
|
||||
가 기록되어 stage gate 상태와 일치한다.
|
||||
|
||||
이 항목은 **완료**다.
|
||||
|
||||
---
|
||||
|
||||
## 3. run verifier blind spot — 반영 완료
|
||||
|
||||
verifier가 필드만 읽고 형식적으로 PASS하는지 확인하기 위해 실제 v5 run을 /tmp 사본으로 변조해 음성 테스트를 했다.
|
||||
|
||||
### case 1 — `--repo` 삭제
|
||||
|
||||
변조:
|
||||
|
||||
```text
|
||||
check_evidence ... keycloak-session-store
|
||||
exit 0
|
||||
status PASS
|
||||
```
|
||||
|
||||
결과:
|
||||
|
||||
```text
|
||||
PIPELINE RUN: FAIL
|
||||
live source evidence gate에서 --repo가 빠졌다
|
||||
```
|
||||
|
||||
### case 2 — UNVERIFIABLE reason 삭제
|
||||
|
||||
결과:
|
||||
|
||||
```text
|
||||
PIPELINE RUN: FAIL
|
||||
UNVERIFIABLE evidence gate에 이유가 없다
|
||||
```
|
||||
|
||||
### case 3 — semanticId 삭제
|
||||
|
||||
결과:
|
||||
|
||||
```text
|
||||
PIPELINE RUN: FAIL
|
||||
필수 evidence semantic gate가 정확히 하나가 아니다
|
||||
```
|
||||
|
||||
따라서 이전 verifier blind spot은 닫혔다.
|
||||
|
||||
이 항목은 **완료**다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 33 Record / 992 command audit — 반영 완료
|
||||
|
||||
프로젝트 내부에 다음 current audit이 추가됐다.
|
||||
|
||||
```text
|
||||
docs/keycloak-session-store/reviews/
|
||||
2026-09-20-keycloak-session-store-command-audit.json
|
||||
2026-09-20-keycloak-session-store-command-audit.md
|
||||
```
|
||||
|
||||
현재 JSON audit과 실제 파일을 다시 대조했다.
|
||||
|
||||
```text
|
||||
records 33
|
||||
shellBlocks 992
|
||||
findings 53
|
||||
major 0
|
||||
text-like 15
|
||||
SHA mismatch 0
|
||||
detail mismatch 0
|
||||
```
|
||||
|
||||
33개 Record 전부 현재 파일 SHA256과 맞는다.
|
||||
|
||||
finding 상세도 현재 analyzer를 다시 실행한 결과와 정확히 일치한다.
|
||||
|
||||
남은 finding:
|
||||
|
||||
```text
|
||||
hidden-stderr 48
|
||||
compound-remote-shell 5
|
||||
```
|
||||
|
||||
은 모두 minor이고 audit에서 문맥 검토 후 허용 상태로 남겼다.
|
||||
|
||||
projectVerdict도:
|
||||
|
||||
```text
|
||||
PASS
|
||||
```
|
||||
|
||||
다.
|
||||
|
||||
옛 run을 소급 수정하지 않고 현재 audit을 별도 artifact로 남긴 방식도 이전 리뷰의 요구와 맞는다.
|
||||
|
||||
이 항목은 **완료**다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 문체 / Tree / Record — 정상
|
||||
|
||||
현재:
|
||||
|
||||
```text
|
||||
records=61
|
||||
written=61
|
||||
unwritten=0
|
||||
|
||||
prose 61/61 PASS
|
||||
voice 61/61 PASS
|
||||
```
|
||||
|
||||
Tree의 이전 미처분 절도:
|
||||
|
||||
```text
|
||||
KEEP_IN_SSOT
|
||||
CONFIRMED
|
||||
```
|
||||
|
||||
상태를 유지한다.
|
||||
|
||||
Required Content, SSOT Facts, Audit도 모두 PASS다.
|
||||
|
||||
추가 문체 수정 finding은 이번 검수에서 발견하지 못했다.
|
||||
|
||||
---
|
||||
|
||||
## 6. Figure gate — 내용은 정상
|
||||
|
||||
현재:
|
||||
|
||||
```text
|
||||
Figure Text PASS 31/31
|
||||
Figure Overlap PASS 31/31
|
||||
Figure Provenance PASS 31/31
|
||||
```
|
||||
|
||||
그리고 각 TechViz가 직접 근거로 삼는:
|
||||
|
||||
```text
|
||||
current_section exact match = 31 / 31
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
따라서 **그림의 직접 기술 근거는 현재 SSOT와 맞는다.**
|
||||
|
||||
---
|
||||
|
||||
## 7. 유일한 잔여 finding — stale surrounding context 7개
|
||||
|
||||
`verify-project-layout.py keycloak-session-store`에는 아직 다음 경고가 남는다.
|
||||
|
||||
```text
|
||||
SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다: 7
|
||||
```
|
||||
|
||||
정확한 대상과 mismatch:
|
||||
|
||||
### 1. a1-transport-vs-discovery
|
||||
|
||||
```text
|
||||
previous_section STALE — A층 — Keycloak 자체가 깨질 때
|
||||
current_section MATCH — A-1 · JGroups 전송(TCP 7800) 차단
|
||||
next_section MATCH — A-2 · A-3 — DB 가 멈출 때와 죽을 때
|
||||
```
|
||||
|
||||
### 2. d2-upgrade-direction
|
||||
|
||||
```text
|
||||
previous_section STALE — D층 — 운영
|
||||
current_section MATCH — D-1 · D-2 — 백업과 업그레이드
|
||||
next_section MATCH — D-3 · 비밀
|
||||
```
|
||||
|
||||
### 3. lab-topology
|
||||
|
||||
```text
|
||||
previous_section STALE — 문제를 어렵게 만든 제약
|
||||
current_section MATCH — 실험대
|
||||
next_section MATCH — 게스트와 호스트의 sudo 가 다르다
|
||||
```
|
||||
|
||||
### 4. measurement-control
|
||||
|
||||
```text
|
||||
previous_section STALE — 결정이 지켜지는지 확인하는 방법
|
||||
current_section MATCH — 측정이 거짓말할 때
|
||||
next_section MATCH — 재현 가능성을 어떻게 보장했나
|
||||
```
|
||||
|
||||
### 5. observation-points
|
||||
|
||||
```text
|
||||
previous_section STALE — 검토한 선택지와 막힌 지점
|
||||
current_section MATCH — 관측을 어디에 둘 것인가
|
||||
next_section MATCH — 스크립트를 쓰지 않는다
|
||||
```
|
||||
|
||||
### 6. open-questions-answered
|
||||
|
||||
```text
|
||||
previous_section STALE — 얻은 것, 잃은 것, 적용하지 않을 때
|
||||
current_section MATCH — 열린 질문 네 개에 대한 답
|
||||
next_section MATCH — 이 기록이 적용되지 않는 조건
|
||||
```
|
||||
|
||||
### 7. wrong-predictions
|
||||
|
||||
```text
|
||||
previous_section MATCH — 얻은 것, 잃은 것, 적용하지 않을 때
|
||||
current_section MATCH — 결국 지키려던 것은 무엇이었나
|
||||
next_section STALE — 자료
|
||||
```
|
||||
|
||||
### 판단
|
||||
|
||||
이 7개는 **그림의 직접 source section mismatch가 아니다.**
|
||||
|
||||
여섯 개는 parent/previous section이 이후 child section 변경까지 포함하면서 stale이 되었고, 하나는 뒤의 `자료` section이 변경된 경우다.
|
||||
|
||||
즉 semantic node/edge를 다시 설계해야 한다는 evidence는 현재 없다.
|
||||
|
||||
하지만 현재 canonical verifier가 stale context로 잡고 있고 이전 리뷰에서 이 warning을 마지막에 닫으라고 명시했으므로 **최종 cleanup이 아직 남아 있다.**
|
||||
|
||||
---
|
||||
|
||||
## 8. 필요한 마지막 작업
|
||||
|
||||
SVG를 손으로 수정하지 않는다.
|
||||
|
||||
7개에 대해서만 canonical TechViz 흐름으로:
|
||||
|
||||
1. 현재 `final/document.md`에서 context 재생성
|
||||
2. 기존 spec의 semantic node/edge가 여전히 맞는지 확인
|
||||
3. 의미 변경이 없으면 context/evidence hash만 최신화
|
||||
4. 필요한 canonical render 실행
|
||||
5. Figure Text / Overlap / Provenance
|
||||
6. preview 눈 확인
|
||||
7. `verify-project-layout.py keycloak-session-store`
|
||||
|
||||
를 다시 실행한다.
|
||||
|
||||
목표:
|
||||
|
||||
```text
|
||||
SSOT 문맥이 바뀐 뒤 그림을 다시 보지 않았다 = 0
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
parent/neighbor section 전체를 snapshot으로 잡는 현재 context 모델 때문에 재생성 후에도 같은 경고가 반복된다면, 그때는 억지로 context를 고치지 말고 `verify-project-layout.py`의 context 비교 모델을 수정하고 regression test를 추가한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. 현재 전체 검증
|
||||
|
||||
프로젝트:
|
||||
|
||||
```text
|
||||
verify-tech-log-tree PASS
|
||||
verify-project-layout PASS (stale-context warn 7 포함)
|
||||
audit-records PASS
|
||||
check-figure-text PASS 31/31
|
||||
check-figure-overlap PASS 31/31
|
||||
check-figure-provenance PASS 31/31
|
||||
check-required-content PASS 61
|
||||
check-ssot-facts PASS
|
||||
git diff --check PASS
|
||||
|
||||
prose PASS 61/61
|
||||
voice PASS 61/61
|
||||
```
|
||||
|
||||
v5 evidence runs:
|
||||
|
||||
```text
|
||||
6 / 6 PASS
|
||||
UNVERIFIABLE 상태 정상 보존
|
||||
```
|
||||
|
||||
전체 pipeline:
|
||||
|
||||
```text
|
||||
verify-pipeline.py
|
||||
exit 0
|
||||
```
|
||||
|
||||
전체 tests:
|
||||
|
||||
```text
|
||||
Ran 406 tests
|
||||
OK
|
||||
skipped=14
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 최종 판정
|
||||
|
||||
이전 리뷰의 핵심 문제였던:
|
||||
|
||||
- evidence `--repo` 우회
|
||||
- UNVERIFIABLE 모델 부재
|
||||
- run verifier blind spot
|
||||
- 33 Record command audit 부재
|
||||
|
||||
는 이번에는 **제대로 수정됐다.**
|
||||
|
||||
문서 내용, command audit, 검증 계약도 현재 정상이다.
|
||||
|
||||
그러나 이전 리뷰에서 마지막 cleanup으로 요구한 **TechViz stale surrounding-context warning 7개가 아직 남아 있다.**
|
||||
|
||||
따라서 현재 판정은:
|
||||
|
||||
> **keycloak-session-store = 아직 더 봐야댐**
|
||||
|
||||
7개 context warning만 닫고 다시 project gate → 전체 pipeline → unittest를 순차 실행하면 된다.
|
||||
@@ -0,0 +1,602 @@
|
||||
# keycloak-session-store 후속 검증 리뷰
|
||||
|
||||
- 검토일: 2026-09-20
|
||||
- 대상: 이전 리뷰 `reviews/2026-09-20-keycloak-session-store-seven-stage-review.md` 반영 결과
|
||||
- 판정: **아직 더 봐야댐**
|
||||
|
||||
---
|
||||
|
||||
## 1. 결론
|
||||
|
||||
이전 리뷰의 **문서 내용 자체는 대부분 제대로 반영됐다.**
|
||||
|
||||
특히 다음은 확실히 닫혔다.
|
||||
|
||||
- command analyzer가 `bash label="..."`, `sh label="..."`을 읽도록 수정됨
|
||||
- TechLog Record의 shell block **992개 / 33 Record**를 analyzer가 실제로 인식함
|
||||
- label의 `[lab host]`, `[워크스테이션]`, `[탐침 파드]` 같은 실행 위치를 execution context로 인식함
|
||||
- 관련 회귀 테스트가 추가됨
|
||||
- Tree의 미처분 SSOT 절이 `KEEP_IN_SSOT / CONFIRMED`로 처분됨
|
||||
- `이 기준이 선 근거.`가 `이 기준의 근거다.`로 수정됨
|
||||
- prose/voice는 현재도 **61/61 PASS**
|
||||
- TechViz가 직접 근거로 삼는 `current_section`은 **31/31 최신 SSOT와 일치**
|
||||
- Figure Text / Overlap / Provenance 모두 PASS
|
||||
- 전체 pipeline은 exit 0
|
||||
- 전체 unittest는 **397 PASS / 14 skipped**
|
||||
|
||||
따라서 이전처럼 “문서 내용이 많이 남아 있다”는 상태는 아니다.
|
||||
|
||||
하지만 **완료를 증명하는 현재 remediation run의 관문 기록이 하네스 계약과 맞지 않는다.**
|
||||
이 문제가 남아 있어서 현재 상태를 `완료`로 판정하면 안 된다.
|
||||
|
||||
---
|
||||
|
||||
# 2. 이전 리뷰 항목별 재검증
|
||||
|
||||
## 2.1 Command analyzer — 반영됨
|
||||
|
||||
이전 문제:
|
||||
|
||||
> analyzer가 `bash label=...`, `sh label=...`을 읽지 못해 shell block 99.2%를 놓쳤다.
|
||||
|
||||
현재 `scripts/command_pedagogy.py`는 fence의 첫 info token을 언어로 읽는다.
|
||||
|
||||
현재 전수 결과:
|
||||
|
||||
```text
|
||||
Record 61
|
||||
command-bearing Record 33
|
||||
shell block 992
|
||||
```
|
||||
|
||||
즉 이전에 빠졌던 labeled fence까지 모두 잡힌다.
|
||||
|
||||
현재 테스트에도 다음 계약이 들어갔다.
|
||||
|
||||
- labeled bash fence 분석
|
||||
- labeled sh fence 분석
|
||||
- 첫 token 전체 일치 검사
|
||||
- label execution context 인식
|
||||
- context가 아닌 label은 remote execution context를 숨기지 못함
|
||||
|
||||
이 항목은 **반영 완료**로 본다.
|
||||
|
||||
---
|
||||
|
||||
## 2.2 Command corpus 재분석 — 내용상 큰 결함은 새로 나오지 않음
|
||||
|
||||
기존 corpus 정책대로 `reference` mode로 61 Record 전체를 다시 분석했다.
|
||||
|
||||
```text
|
||||
command-bearing Record = 33
|
||||
shell blocks = 992
|
||||
findings = 53
|
||||
major findings = 0
|
||||
command-like text = 15
|
||||
```
|
||||
|
||||
남은 53개 deterministic signal:
|
||||
|
||||
```text
|
||||
hidden-stderr 48
|
||||
compound-remote-shell 5
|
||||
```
|
||||
|
||||
이것들은 전역 금지 항목이 아니다.
|
||||
|
||||
실제 문맥을 다시 확인하면 대표적으로:
|
||||
|
||||
- OpenSSL의 handshake noise를 없앤 뒤 x509 값을 보는 경우
|
||||
- JWT payload를 base64 decode하면서 decode stderr를 숨기는 경우
|
||||
- build/restore 출력을 `/tmp/*.log`로 모은 뒤 그 파일을 읽는 경우
|
||||
- `nginx -t && nginx -s reload`처럼 **검증 성공 뒤에만 변경**하는 안전 조건
|
||||
- conntrack 실험처럼 stderr 억제가 어떤 오판을 만드는지 본문이 오히려 설명하는 경우
|
||||
|
||||
가 대부분이다.
|
||||
|
||||
따라서 현재 전수 재검사에서 **새로운 high-confidence command defect는 발견하지 못했다.**
|
||||
|
||||
다만 아래 §4의 **검토 증거 범위 문제**는 별개다.
|
||||
|
||||
---
|
||||
|
||||
## 2.3 Tree 미처분 절 — 반영됨
|
||||
|
||||
이전 미처분 절:
|
||||
|
||||
```text
|
||||
2026-09-17 재현 — 어디까지 밟았고 무엇이 막았나
|
||||
```
|
||||
|
||||
현재 tree에는 새 candidate가 생겼다.
|
||||
|
||||
```text
|
||||
id: SSOT-2026-09-17-reproduction-progress-and-blocker
|
||||
disposition: KEEP_IN_SSOT
|
||||
dispositionReview: CONFIRMED
|
||||
```
|
||||
|
||||
이유도 적절하다.
|
||||
|
||||
- 시점 의존적인 진행 추적 정보
|
||||
- 각 Setup이 실제 절차와 미완료 단계를 이미 담음
|
||||
- 별도 Record로 올리면 중복
|
||||
|
||||
현재 Tree 결과:
|
||||
|
||||
```text
|
||||
records=61
|
||||
written=61
|
||||
unwritten=0
|
||||
KEEP_IN_SSOT=23
|
||||
warn=1
|
||||
```
|
||||
|
||||
남은 warn 1은 source repository 부재뿐이다.
|
||||
|
||||
이 항목은 **반영 완료**다.
|
||||
|
||||
---
|
||||
|
||||
## 2.4 깨진 문장 — 반영됨
|
||||
|
||||
이전:
|
||||
|
||||
```text
|
||||
이 기준이 선 근거.
|
||||
```
|
||||
|
||||
현재:
|
||||
|
||||
```text
|
||||
이 기준의 근거다.
|
||||
```
|
||||
|
||||
또 Relation의 반복 문형도 일부 선별 정리했다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
이 절차가 만드는 두 시각의 차이에 대한 결론은 그 기록에 있다.
|
||||
```
|
||||
|
||||
등으로 바뀌었다.
|
||||
|
||||
현재:
|
||||
|
||||
```text
|
||||
check_prose 61/61 PASS
|
||||
check_voice 61/61 PASS
|
||||
```
|
||||
|
||||
이 항목은 **반영 완료**다.
|
||||
|
||||
---
|
||||
|
||||
# 3. SVG — 본문 근거는 닫혔지만 layout warning 7개는 남음
|
||||
|
||||
이전에는 31개 중 28개가 현재 SSOT section과 달랐다.
|
||||
|
||||
현재 직접 비교 결과:
|
||||
|
||||
```text
|
||||
current_section exact match = 31 / 31
|
||||
```
|
||||
|
||||
즉 **그림이 직접 근거로 삼는 현재 절은 전부 최신 SSOT와 맞는다.**
|
||||
|
||||
Figure gate도:
|
||||
|
||||
```text
|
||||
Figure Text PASS 31/31
|
||||
Figure Overlap PASS 31/31
|
||||
Figure Provenance PASS 31/31
|
||||
```
|
||||
|
||||
이다.
|
||||
|
||||
따라서 이전의 “28개 그림이 옛 본문을 보고 있다”는 핵심 문제는 닫혔다.
|
||||
|
||||
다만 `verify-project-layout.py`에는 아직 **7개 context warning**이 남는다.
|
||||
|
||||
```text
|
||||
a1-transport-vs-discovery
|
||||
d2-upgrade-direction
|
||||
lab-topology
|
||||
measurement-control
|
||||
observation-points
|
||||
open-questions-answered
|
||||
wrong-predictions
|
||||
```
|
||||
|
||||
세부적으로 보면 앞의 여섯 개는:
|
||||
|
||||
- `current_section` = MATCH
|
||||
- `next_section` = MATCH
|
||||
- 상위/이전 parent section snapshot만 STALE
|
||||
|
||||
인 형태다.
|
||||
|
||||
`wrong-predictions`는:
|
||||
|
||||
- previous = MATCH
|
||||
- current = MATCH
|
||||
- next `자료` section = STALE
|
||||
|
||||
이다.
|
||||
|
||||
즉 **그림 자체의 직접 근거가 낡았다는 의미는 아니다.**
|
||||
여러 child section을 포함하는 parent/neighbor context가 뒤의 SSOT 수정 때문에 다시 달라진 잔여 warning이다.
|
||||
|
||||
그래도 현재 verifier가 이를 stale context로 보고 있으므로 최종 cleanup은 필요하다.
|
||||
|
||||
권장:
|
||||
|
||||
1. 모든 SSOT 수정이 끝난 뒤 이 7개 context를 마지막에 다시 prepare
|
||||
2. spec의 semantic node/edge가 그대로인지 확인
|
||||
3. semantic 변화가 없으면 context/evidence hash만 최신화
|
||||
4. render → lint → text → overlap → provenance → preview
|
||||
5. 다시 `verify-project-layout.py keycloak-session-store`
|
||||
|
||||
또는 parent section 전체를 previous context로 잡는 구조가 계속 연쇄 stale을 만든다면 verifier/context 모델을 별도 개선한다.
|
||||
|
||||
**SVG 내용 자체는 현재 정상으로 보지만, layout warning 7개는 아직 정리되지 않았다.**
|
||||
|
||||
---
|
||||
|
||||
# 4. 완료 차단 — 새 remediation run이 evidence gate에서 `--repo`를 빼고 PASS를 만들었다
|
||||
|
||||
이것이 현재 가장 중요한 finding이다.
|
||||
|
||||
현재 authoritative stage contract의 S3 gate:
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||||
```
|
||||
|
||||
S5와 S6도 S3 gate를 다시 실행하므로 동일하게 `--repo`가 필요하다.
|
||||
|
||||
또 현재 pipeline skill은 명확히 적는다.
|
||||
|
||||
```text
|
||||
exit 0 = 저장소 대조 완료
|
||||
exit 1 = 실제 불일치
|
||||
exit 2 = 검사 대상 불성립
|
||||
exit 3 = source repo 부재로 UNVERIFIABLE
|
||||
|
||||
새 publication run에서는 exit 3을 PASS로 기록하면 안 된다.
|
||||
```
|
||||
|
||||
그런데 새로 만든 remediation run 6개를 확인하면 S3/S5/S6가 전부 다음처럼 기록돼 있다.
|
||||
|
||||
```bash
|
||||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak-session-store
|
||||
```
|
||||
|
||||
즉 **`--repo`가 없다.**
|
||||
|
||||
대상 run:
|
||||
|
||||
```text
|
||||
2026-09-20-1725-remediation-01-setup-reproduce-d4-certificate-renewal
|
||||
2026-09-20-1725-remediation-02-setup-reproduce-d4a-deploy-hook
|
||||
2026-09-20-1725-remediation-03-setup-reproduce-a7a-volatile-cause
|
||||
2026-09-20-1725-remediation-04-setup-reproduce-b7a-orphan-session
|
||||
2026-09-20-1725-remediation-05-reference-look-at-the-lookup-key-before-moving-the-store
|
||||
2026-09-20-1725-remediation-06-setup-reproduce-b6-key-rotation
|
||||
```
|
||||
|
||||
6 run × S3/S5/S6 = **18개 evidence gate 전부 `--repo` 누락**이다.
|
||||
|
||||
실제로 현재 머신에서 실행하면:
|
||||
|
||||
### 현재 run이 기록한 명령
|
||||
|
||||
```text
|
||||
check_evidence.mjs keycloak-session-store
|
||||
→ exit 0
|
||||
→ 문제 없음
|
||||
```
|
||||
|
||||
### 계약에 적힌 명령
|
||||
|
||||
```text
|
||||
check_evidence.mjs keycloak-session-store --repo
|
||||
→ exit 3
|
||||
→ 저장소 경로가 이 기계에 없다
|
||||
→ 불일치 0 / 대조 불가 1
|
||||
```
|
||||
|
||||
즉 현재 run은 **원본 대조를 수행하지 않은 명령을 실행하고 PASS라고 기록했다.**
|
||||
|
||||
사용자와 합의한 기준상 source repository 부재 자체는 완료 차단으로 보지 않아도 된다.
|
||||
|
||||
하지만 그것은:
|
||||
|
||||
> 원본 대조를 하지 않고 PASS로 바꿔도 된다
|
||||
|
||||
는 뜻이 아니다.
|
||||
|
||||
정확한 상태는:
|
||||
|
||||
> **live source reconciliation = UNVERIFIABLE**
|
||||
|
||||
이다.
|
||||
|
||||
## 필요한 수정
|
||||
|
||||
기존 17:25 remediation run은 이미 만들어진 영수증이므로 고쳐 쓰지 않는다.
|
||||
|
||||
대신 둘 중 하나로 해결한다.
|
||||
|
||||
### A. source repo가 실제로 있으면
|
||||
|
||||
정본 경로를 찾아 tree의 sourceRepository를 실제 경로에 맞춘 뒤:
|
||||
|
||||
```text
|
||||
check_evidence --repo
|
||||
→ exit 0
|
||||
```
|
||||
|
||||
으로 새 verification/remediation run을 만든다.
|
||||
|
||||
### B. source repo가 이 환경에는 없고 이를 사용자 예외로 허용한다면
|
||||
|
||||
**`--repo`를 삭제하지 말고** 결과를 그대로 남길 수 있어야 한다.
|
||||
|
||||
즉 run schema/verifier에 명시적인 상태를 둔다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
status: UNVERIFIABLE
|
||||
exit: 3
|
||||
reason: source repository unavailable on current machine
|
||||
acceptedByProjectReview: true
|
||||
```
|
||||
|
||||
이름은 달라도 된다.
|
||||
|
||||
핵심은:
|
||||
|
||||
- 실제 명령은 `--repo` 포함
|
||||
- 실제 exit 3 보존
|
||||
- PASS로 위장하지 않음
|
||||
- 이 프로젝트에서는 사용자가 허용한 환경 제약이므로 전체 완료를 막지 않는다는 정책을 별도 표현
|
||||
|
||||
이다.
|
||||
|
||||
현재처럼 flag 자체를 빼는 방식은 안 된다.
|
||||
|
||||
---
|
||||
|
||||
# 5. verifier blind spot — 잘못된 gate인데 run verifier가 PASS한다
|
||||
|
||||
위 6개 run에 대해:
|
||||
|
||||
```text
|
||||
python3 scripts/verify-pipeline-run.py <run.json>
|
||||
```
|
||||
|
||||
은 전부 PASS한다.
|
||||
|
||||
즉 verifier는 현재:
|
||||
|
||||
- evidence gate가 하나 존재하는지
|
||||
- recorded exit가 0인지
|
||||
|
||||
등은 보지만,
|
||||
|
||||
> **S3/S5/S6가 계약에 적힌 `check_evidence.mjs <project> --repo`를 실제로 실행했는가**
|
||||
|
||||
까지는 강제하지 않는다.
|
||||
|
||||
그래서 계약보다 약한 명령을 기록해도 통과했다.
|
||||
|
||||
이 부분도 같이 막아야 한다.
|
||||
|
||||
권장:
|
||||
|
||||
- stage gate를 raw command 문자열만으로 보지 말고 semantic gate id를 둔다.
|
||||
- 최소한 S3/S5/S6의 evidence gate는 `--repo` 존재를 검증한다.
|
||||
- 사용자가 허용한 source-missing 예외를 도입한다면 exit 3 + UNVERIFIABLE 상태를 verifier가 이해하게 한다.
|
||||
|
||||
**검사기를 통과시키려고 관문을 약하게 만드는 경로를 차단해야 한다.**
|
||||
|
||||
---
|
||||
|
||||
# 6. 33 Record / 992 command 재검토의 “증거 범위”는 아직 약함
|
||||
|
||||
이전 리뷰는:
|
||||
|
||||
```text
|
||||
33개 command-bearing Record
|
||||
992 shell block
|
||||
```
|
||||
|
||||
을 parser 수정 뒤 다시 분석하라고 했다.
|
||||
|
||||
현재 내용 자체는 내가 이번 검토에서 다시 전수 실행했고:
|
||||
|
||||
```text
|
||||
reference mode:
|
||||
records 33
|
||||
blocks 992
|
||||
findings 53
|
||||
major 0
|
||||
```
|
||||
|
||||
을 확인했다.
|
||||
|
||||
그래서 **현재 command 내용에서 새 major defect가 남았다고 보지는 않는다.**
|
||||
|
||||
다만 repository에 새 command review artifact가 남아 있는 것은 이번 remediation에서 다룬 일부 Record뿐이다.
|
||||
|
||||
새 schemaVersion 4 remediation 중 command block이 있는 Record:
|
||||
|
||||
```text
|
||||
D-4
|
||||
D-4a
|
||||
A-7a
|
||||
B-7a
|
||||
B-6
|
||||
```
|
||||
|
||||
즉 current reviewer artifact가 직접 남은 것은 **5 / 33 command-bearing Record**다.
|
||||
|
||||
나머지는 옛 schemaVersion 1/2 run만 있고, 그것은 parser가 labeled fence를 못 보던 시기의 영수증이다.
|
||||
|
||||
이전 리뷰는 historical ledger를 소급 조작하지 말라고 했으므로 옛 run을 고치면 안 된다.
|
||||
|
||||
대신 **프로젝트 단위 current command audit artifact**를 하나 남기는 편이 맞다.
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
reviews/2026-09-20-keycloak-session-store-command-audit.json
|
||||
reviews/2026-09-20-keycloak-session-store-command-audit.md
|
||||
```
|
||||
|
||||
여기에 33 Record 각각에 대해:
|
||||
|
||||
- source SHA256
|
||||
- shellBlocks
|
||||
- mode
|
||||
- findings
|
||||
- majorFindings
|
||||
- minor finding disposition
|
||||
- reviewer verdict
|
||||
|
||||
을 남긴다.
|
||||
|
||||
Record를 실제로 수정한 경우에만 새 remediation run을 만든다.
|
||||
|
||||
이렇게 하면 historical receipt를 위조하지 않으면서도 **“parser 수정 뒤 992개를 다시 봤다”**는 현재 증거가 남는다.
|
||||
|
||||
---
|
||||
|
||||
# 7. 현재 자동 검증 결과
|
||||
|
||||
## 프로젝트 gate
|
||||
|
||||
```text
|
||||
verify-tech-log-tree PASS
|
||||
verify-project-layout PASS (warn 존재)
|
||||
audit-records PASS
|
||||
check-figure-text PASS 31/31
|
||||
check-figure-overlap PASS 31/31
|
||||
check-figure-provenance PASS 31/31
|
||||
check-required-content PASS 61
|
||||
check-ssot-facts PASS
|
||||
git diff --check PASS
|
||||
```
|
||||
|
||||
## 문체
|
||||
|
||||
```text
|
||||
prose 61/61 PASS
|
||||
voice 61/61 PASS
|
||||
```
|
||||
|
||||
## 전체 pipeline
|
||||
|
||||
```text
|
||||
verify-pipeline.py
|
||||
exit 0
|
||||
```
|
||||
|
||||
parser가 수정되면서 전체 corpus도 이제 Record의 labeled shell fence를 실제로 세고 있다.
|
||||
|
||||
keycloak-session-store 전체(final SSOT + Record) 기준:
|
||||
|
||||
```text
|
||||
shell blocks = 1920
|
||||
findings = 150
|
||||
```
|
||||
|
||||
이 수치가 이전보다 늘어난 것은 defect가 늘어난 것이 아니라 **전에 못 보던 labeled fence를 이제 보기 시작했기 때문**이다.
|
||||
|
||||
## 전체 unittest
|
||||
|
||||
```text
|
||||
Ran 397 tests
|
||||
OK
|
||||
skipped=14
|
||||
```
|
||||
|
||||
새 labeled-fence 관련 테스트도 통과한다.
|
||||
|
||||
---
|
||||
|
||||
# 8. 남은 작업 순서
|
||||
|
||||
## 1. evidence gate 우회부터 고친다
|
||||
|
||||
가장 먼저 한다.
|
||||
|
||||
- 기존 17:25 run 수정 금지
|
||||
- `--repo`를 제거해 PASS시키는 방식 금지
|
||||
- source missing을 명시적 `UNVERIFIABLE`로 보존할 수 있게 계약/verifier를 정리
|
||||
- 또는 실제 source repo를 찾을 수 있다면 정본 경로로 대조
|
||||
|
||||
## 2. run verifier가 필수 evidence gate를 검증하게 한다
|
||||
|
||||
최소 S3/S5/S6에서:
|
||||
|
||||
```text
|
||||
check_evidence.mjs <project> --repo
|
||||
```
|
||||
|
||||
를 요구한다.
|
||||
|
||||
환경 예외가 있으면 명시적 상태로만 허용한다.
|
||||
|
||||
## 3. 33 Record command audit를 현재 artifact로 남긴다
|
||||
|
||||
- 992 block
|
||||
- reference-mode 53 minor / 0 major
|
||||
- minor의 문맥상 허용 여부
|
||||
- 15 command-like text의 분류
|
||||
|
||||
를 프로젝트 단위 audit으로 남긴다.
|
||||
|
||||
실제 수정한 Record만 새 remediation run을 만든다.
|
||||
|
||||
## 4. SVG 잔여 context warning 7개를 마지막에 닫는다
|
||||
|
||||
current section은 이미 31/31 맞다.
|
||||
|
||||
남은 7개 previous/next snapshot만 모든 SSOT 수정 후 마지막에 regenerate/review한다.
|
||||
|
||||
## 5. 최종 검증
|
||||
|
||||
순서대로:
|
||||
|
||||
```text
|
||||
project gates
|
||||
→ verify-pipeline.py
|
||||
→ unittest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 9. 최종 판정
|
||||
|
||||
이전 리뷰의 **문서 내용 수정은 대부분 제대로 반영됐다.**
|
||||
|
||||
특히:
|
||||
|
||||
- parser
|
||||
- Tree
|
||||
- 문체
|
||||
- 그림의 직접 SSOT 근거
|
||||
|
||||
는 제대로 개선됐다.
|
||||
|
||||
하지만 현재 remediation run 6개가 계약상 필수인 `--repo` evidence reconciliation을 생략한 채 PASS로 기록되어 있고, verifier도 그 우회를 잡지 못한다.
|
||||
|
||||
따라서 지금 상태는:
|
||||
|
||||
> **keycloak-session-store = 아직 더 봐야댐**
|
||||
|
||||
문서 내용보다 **검증 계약과 영수증의 진실성**을 한 번 더 고쳐야 한다.
|
||||
@@ -0,0 +1,58 @@
|
||||
# keycloak-session-store 7단계 하네스 재검토
|
||||
|
||||
- 검토일: 2026-09-20
|
||||
- 대상: 61 Record / 31 TechViz SVG / 63 historical run ledger
|
||||
- 판정: **아직 더 봐야댐**
|
||||
|
||||
## 결론
|
||||
|
||||
대규모 문체 재작성은 필요 없다. `check_prose`와 `check_voice`는 61/61 PASS이고, Figure Text/Overlap/Provenance도 31/31 PASS다. historical run도 63/63 PASS다.
|
||||
|
||||
완료 차단은 다음 네 가지다.
|
||||
|
||||
1. command-pedagogy analyzer가 `bash label=...`, `sh label=...` fence를 읽지 못한다.
|
||||
2. TechViz 31개 중 28개의 current SSOT context가 달라졌다.
|
||||
3. `2026-09-17 재현 — 어디까지 밟았고 무엇이 막았나` 절이 tree에서 미처분 상태다.
|
||||
4. `reference-look-at-the-lookup-key-before-moving-the-store.md`에 `이 기준이 선 근거.`라는 깨진 문장이 남아 있다.
|
||||
|
||||
## Command pedagogy
|
||||
|
||||
Record 61개를 info string의 첫 토큰 기준으로 다시 세면 shell block은 992개이고 33개 Record에 분포한다. 이 가운데 plain fence는 8개, label metadata가 붙은 fence는 984개다. 즉 현재 analyzer가 놓치는 형식이 984/992 = 99.2%다.
|
||||
|
||||
first-token 방식으로 기존 deterministic finding 로직을 적용했을 때 signal은 346개였다. minor 341, major 5이며 compressed-pipeline 129, command-substitution 122, hidden-stderr 48, execution-context-implicit 40, compound-remote-shell 5 등이었다.
|
||||
|
||||
이 346개를 모두 고치면 안 된다. label에 이미 `[lab host]`, `[kc-lab-1]`, `[test-server]`, `[dev]`, `[워크스테이션]`, `[탐침 파드]` 실행 위치가 있어서 analyzer의 execution-context signal 중 상당수는 false positive가 될 수 있다. 먼저 parser가 info string 첫 토큰을 language로 해석하고 label을 execution context로 읽게 고친 뒤 33개 Record/992 block을 다시 리뷰한다.
|
||||
|
||||
D-4a의 `printf > /tmp/reload-nginx.sh` 형태는 실제 그날 친 historical command라고 문서가 밝히므로 삭제하지 않는다. 따라 하는 절차는 편집기 + 파일 listing으로 두고 historical block은 reference 성격으로 분리한다.
|
||||
|
||||
## SVG
|
||||
|
||||
31개 SVG의 구조 검사는 모두 PASS지만 현재 SSOT section snapshot과 일치하는 것은 `cpu-io-passthrough-paths`, `ghost-row-cleanup-order`, `guest-as-host-process` 3개뿐이다.
|
||||
|
||||
나머지 28개는 실제 `current_section`도 달라졌다. Keycloak에서 봤던 parent-heading false positive가 아니다. 기존 spec이 여전히 맞는지 현재 SSOT로 다시 확인하고, 맞으면 context/evidence/render만 갱신한다. 의미가 바뀐 그림만 spec을 수정한다. SVG는 손으로 편집하지 않는다.
|
||||
|
||||
## Tree / prose
|
||||
|
||||
Tree의 `NEEDS_DECISION 2`, `NEEDS_EVIDENCE 12`는 모두 CONFIRMED 이유가 있어 문제 자체가 아니다. 문제는 범위 안의 `2026-09-17 재현 — 어디까지 밟았고 무엇이 막았나` 절이 어떤 candidate에도 연결되지 않은 것이다. KEEP_IN_SSOT / MERGE_INTO / PROMOTE 중 실제 역할에 맞는 disposition을 남긴다.
|
||||
|
||||
수동 S5 finding은 `이 기준이 선 근거.` 한 곳이다. 자연스럽게 `이 기준의 근거다.` 정도로 고친다. Relation 섹션의 반복 문형은 겹치는 곳만 선별 정리하고 전면 재작성하지 않는다.
|
||||
|
||||
## 원장
|
||||
|
||||
historical `run.json` 63개는 모두 당시 schema 계약 기준 PASS다. 현재 command quality lane은 더 새 계약이므로 과거 원장을 소급 수정하지 않는다. 실제 수정한 Record만 current remediation run을 만든다.
|
||||
|
||||
서브에이전트 사용 불가와 `/home/donghyeon/workspace/keycloak-pattern` 부재는 사용자와 합의한 환경 제약으로 완료 차단에서 제외한다. source reconciliation은 UNVERIFIABLE로 명시한다.
|
||||
|
||||
## 수정 순서
|
||||
|
||||
1. command analyzer parser + label context + regression test
|
||||
2. 33개 Record / 992 shell block 재분석
|
||||
3. high-confidence command finding만 수정
|
||||
4. Tree 미처분 section 1개 분류
|
||||
5. 깨진 문장 1곳 수정
|
||||
6. TechViz 28개 current SSOT 기준 재검토
|
||||
7. 실제 수정분만 remediation ledger
|
||||
8. project gates
|
||||
9. 전체 pipeline과 unittest를 순차 실행
|
||||
|
||||
> **keycloak-session-store = 아직 더 봐야댐**
|
||||
@@ -0,0 +1,99 @@
|
||||
# virtualization 7단계 하네스 리뷰
|
||||
|
||||
- 검토일: 2026-09-20
|
||||
- 대상: 91 Record / 10 TechViz SVG / 92 historical run ledger
|
||||
- 판정: **아직 더 봐야댐**
|
||||
|
||||
## 결론
|
||||
|
||||
전면 재작성 대상은 아니다. prose/voice 91/91 PASS, 기존 TechViz 10/10은 Text/Overlap/Provenance PASS이며 모두 실제 Record에서 사용된다.
|
||||
|
||||
완료 차단은 여섯 축이다.
|
||||
|
||||
1. 2026-09-17에 관측해 닫힌 DNS-01 사실이 SSOT·Question·Setup·Decision에 끝까지 전파되지 않았다.
|
||||
2. certificate lineage가 현재 raw evidence와 정반대로 적힌 Record가 여러 편 있다.
|
||||
3. raw evidence가 65개 생겼지만 Record와 SSOT 일부는 아직 증거가 없다고 적고 evidence가 claim에 연결되지 않았다.
|
||||
4. Setup의 pinned version 표기와 mutable installer/image가 충돌한다.
|
||||
5. SSOT+Record 685 shell block의 current project command audit이 없다.
|
||||
6. 현재 TechViz 문법으로 S4를 다시 판단해야 할 Concept이 최소 네 편 있다.
|
||||
|
||||
## 1. DNS-01 current truth
|
||||
|
||||
`final/evidence/raw/lab-state-before-rebuild/58-authenticator-and-token.txt`는 `authenticator = dns-cloudflare`를 실제로 기록한다. SSOT도 DNS-01 관측 완료라고 적는다.
|
||||
|
||||
하지만 `setup-wildcard-certificate-with-dns-01-and-a-deploy-hook.md` 앞부분은 아직 unknown이고 끝부분은 observed DNS-01이다. `question-is-this-lab-issuing-certificates-with-http-01-or-dns-01.md`도 `questionStatus: OPEN`이다. teardown Setup과 no-public-tunnel Decision도 같은 열린 Question을 정책 근거로 사용한다.
|
||||
|
||||
Question을 현재 canonical 종료 상태로 닫고, stale unknown/relation을 현재 사실에 맞춘다. historical run은 수정하지 않는다.
|
||||
|
||||
## 2. certificate lineage 정반대 설명
|
||||
|
||||
`60-doc04-step5-path-defect.txt`의 현재 관측은 `live/auth.hyeonworks.com/`이 실제 lineage이고 `live/hyeonworks.com/`으로 `nginx -t`를 하면 실패한다.
|
||||
|
||||
그런데 다음은 반대로 적는다.
|
||||
- `reference-verify-a-build-guide-in-execution-order.md`
|
||||
- `decision-dns-01-because-the-lab-is-not-on-the-public-internet.md`
|
||||
- `case-renewal-succeeded-while-the-old-certificate-kept-serving.md`
|
||||
- `question-is-this-lab-issuing-certificates-with-http-01-or-dns-01.md`
|
||||
|
||||
또 TLS Setup main path가 먼저 known-wrong `live/hyeonworks.com/`을 실행하게 하고 뒤에서 정정한다. canonical Setup main path에는 현재 정답만 두고 historical wrong command는 Case/Reference 또는 reference-mode block으로 분리한다.
|
||||
|
||||
## 3. evidence reconciliation
|
||||
|
||||
현재 evidence는 raw 67 files / non-README 65다. SSOT 머리표는 아직 raw 43이라고 적는다.
|
||||
|
||||
중요한 후속 raw:
|
||||
- `14-cloudinit-schema-check.txt`
|
||||
- `15-k3s-precheck.txt`
|
||||
- `21-k3s-token.txt`
|
||||
- `22-k3s-agent-install.txt`
|
||||
- `58-authenticator-and-token.txt`
|
||||
- `60-doc04-step5-path-defect.txt`
|
||||
|
||||
raw 65개 중 Record에서 exact filename으로 연결된 것은 0개이며 layout도 65개를 unreferenced로 센다. 동일 사건 재현 / 동일 원인의 follow-up / 단순 snapshot으로 분류해 claim에 연결한다. 삭제부터 하지 않는다.
|
||||
|
||||
## 4. Setup version contract
|
||||
|
||||
`setup-install-k3s-server-and-agent.md`는 k3s `v1.36.4+k3s1`을 pinned라고 적지만 실제 설치는 `https://get.k3s.io` stable을 사용한다. 2026-09-17에는 우연히 그 버전이 stable이었을 뿐 재실행 계약은 아니다.
|
||||
|
||||
`setup-create-three-guests-with-cloud-init.md`도 cloud-init 22.4.2를 고정한 것처럼 보이지만 Debian image URL은 `bookworm/latest`다. Arch host package도 `pacman -S --needed`라 설치 버전이 고정되지 않는다.
|
||||
|
||||
정확 재현을 원하면 실제 installer/image까지 pin하고, 아니라면 `pinnedVersions`를 observed/tested 의미로 분리한다.
|
||||
|
||||
## 5. command pedagogy
|
||||
|
||||
Record: 22 command-bearing docs / 289 shell blocks / 25 findings / major 2. SSOT: 396 shell blocks / 40 findings / major 1. 전체 shell block은 685다.
|
||||
|
||||
대표 major는 `cloud-init schema ...; rm -f ...`처럼 검증과 삭제를 묶은 historical command다. Setup에서는 이미 안전한 split flow가 있으므로 historical block을 삭제하지 말고 reference disposition을 명시한다. Case에서도 historical/reference 성격을 분명히 한다.
|
||||
|
||||
SETUP 9편 250 block은 tutorial/operator 관점으로 별도 검토한다. historical schema 1/2 run을 소급 수정하지 말고 project-level current command audit을 만든다.
|
||||
|
||||
## 6. S4
|
||||
|
||||
기존 10장은 그대로 유지한다. 새로 재판정할 우선 후보:
|
||||
- `concept-two-l7-hops-and-the-entry-point-recursion.md`
|
||||
- `concept-inside-a-qcow2-file-the-mapping-table-and-its-clusters.md`
|
||||
- `concept-memory-pressure-reclaim-swap-oom.md`
|
||||
- `concept-qemu-block-backend-forms.md`
|
||||
|
||||
특히 memory-pressure Concept의 그림 생략 이유인 도구가 sequence 하나만 지원한다는 설명은 현재 하네스 기준으로 stale이다. 현재 visualizer는 component-flow, two-zone-pipeline 등도 지원한다.
|
||||
|
||||
## 7. provenance
|
||||
|
||||
Tree는 `9465582...`가 exact snapshot commit이 아니라 반입 당시 HEAD라고 명시한다. 반입 14개 중 commit과 같은 것은 3개뿐이고 11개가 다르며 exact snapshot commit은 찾지 못했다.
|
||||
|
||||
그런데 34개 Record가 `sourceRevision: 9465582...`만 적어 exact commit provenance처럼 보인다. import-head와 snapshot을 분리해 의미를 약화해야 한다.
|
||||
|
||||
`README.md`는 `source/`를 final에 반영하면 지우라고 하지만 현재 source는 exact commit이 없는 uncommitted working-tree 상태의 provenance snapshot 역할을 한다. 지금 삭제하지 말고 durable snapshot으로 둘지 정책부터 통일한다.
|
||||
|
||||
## 8. 수정 순서
|
||||
|
||||
1. DNS-01 / lineage current truth 통일
|
||||
2. evidence 65개 분류와 stale 증거 문장 갱신
|
||||
3. Setup 버전 계약 정리
|
||||
4. 685 shell block current audit
|
||||
5. 네 Concept S4 재판정
|
||||
6. sourceRevision / source snapshot provenance 통일
|
||||
7. 실제 수정분만 current remediation run
|
||||
8. project gates → whole pipeline → unittest 순차 검증
|
||||
|
||||
> **virtualization = 아직 더 봐야댐**
|
||||
Reference in New Issue
Block a user