# 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 ``` 은 전부 PASS한다. 즉 verifier는 현재: - evidence gate가 하나 존재하는지 - recorded exit가 0인지 등은 보지만, > **S3/S5/S6가 계약에 적힌 `check_evidence.mjs --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 --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 = 아직 더 봐야댐** 문서 내용보다 **검증 계약과 영수증의 진실성**을 한 번 더 고쳐야 한다.