# 관리자 RBAC 마일스톤 7c (보완 admin-rbac-m7c) 기록

## 마감 요약 (2026-10-20)

- 상태: PRD 마일스톤 7 (행 7, 프런트 호환) 을 complete 로 올렸다. 판정 기준은 메인 세션이 확정한 기준이다. PRD Outcome 의 코드 몫 여섯 항목이 섰는지로 봤고, 아래 1) ~ 6) 은 마감 단계에서 파일을 다시 열어 확인했다.
  1. 권한 집합 · 메뉴 응답: `admin-me.service.ts` 가 `permissions` · `permissionsAvailable` · `menus` · `menusAvailable` · `superAvailable` 을 준다 (`insuniverse-server/apps/api/src/modules/menu/admin-me.service.ts:38-50`).
  2. 권한 거부 전용 code `E0607` 과 로그아웃 `E0601` 분리: 서버 `auth/admin/permission-denied.ts:7`, 관리자 `shared/api/index.ts:53-58` (E0607 은 안내만 내고 로그아웃 신호는 E0601 한 곳뿐이다).
  3. 관리자 · site admin-view 403 처리: 관리자 `shared/ui/PermissionDeniedNotice.tsx`, site `pages/admin-view/PermissionDeniedBlock.tsx` 를 세 화면 (`AdminViewMemberAnalysisPage` · `AdminViewB2cAnalysisPage` · `AdminViewClientAnalysisPage`) 이 쓴다.
  4. 버튼 필터 훅: `shared/auth/usePermission.ts:55` 의 `usePermission` 을 `ConsoleNavEntry.tsx:21` · `ConsolePage.tsx:46` 이 `rbac.manage` 에 쓴다. 범위는 최소 조각이다 (버튼 전체가 아니다).
  5. 영업 · 기업 상수 메뉴 서버 파생: 서버 `menu/sales-corp-menu.ts` · 카탈로그 · 골든 (서버 `2d9835c6`). 관리자 프런트의 상수 네 개와 메뉴 함수를 지웠다 (관리자 `1301b664`, 소비처 0).
  6. 판정 실패 시 나머지 계속: `admin-me.service.ts:94-98` 이 최고관리자 판정 실패를 `superAvailable=false` 로 받고 권한 · 메뉴 계산을 이어 간다. m7 · m7b 의 「던지면 메뉴를 묻지 않는다」를 뒤집은 정정이다 (「정정」 절).
- 게이트: `admin-rbac-m7c.gates.sh` 의 #1 ~ #14 를 현재 HEAD (서버 `16141f29` · 관리자 `1301b664` · 사이트 `fc8872a1` · 부모 직전 `d58e86f`) 에서 다시 돌려 전부 rc 0 이다 (2026-10-20). 서버 tsc 23 = 기준 23, 관리자 tsc node 764 = 기준 764. 게이트 #14 는 관리자 서브모듈에 `tsconfig.node.tsbuildinfo` 를 남기므로 돌린 뒤 지웠다 (추적 대상 아님).
- 사용자 지시: `verify-user-directive.sh` 를 이 실행에서 다시 돌려 exit 0 으로 원문을 확인했다 (2026-10-19T02:21:22Z). 라운드 상한 3 의 마지막 라운드는 skip-receipt 로 진행하는 지시다.
- 계획 게이트: 수렴 (hybrid, R3 판 해시 `sha256:54d92de9…`). 봉인 receipt `.claude/receipts/mccp-plan-codex/admin-rbac-m7c.json`.
- 구현 receipt: 수렴 (`--codex-verdict converged`). 보안 리뷰어는 이 환경에 없어 호출하지 않았다 (`security_skipped`, 「구현 기록」 편차 2).
- 코드 리뷰: 2라운드 (「코드 리뷰」 절). 최종 리뷰: 1라운드 (「최종 리뷰」 절). 요약 `[{"r":1,"found":6,"absorbed":2,"high_absorbed":0,"high_open":0,"codex":"ok"}]`. 지적 6건 (두 리뷰어가 같은 지적을 낸 행 1개 포함) 이 표 5행이 된다. 흡수 2행, 기각 3행, HIGH 미흡수 0. 백로그는 추가하지 않았다.
- (미검증) · 사용자 몫 (complete 를 막지 않는다. 정책 §2 의 defer-unverified):
  1. 메뉴 파생 스위치 `MENU_SOURCE_DERIVED` ON (정책 §3 · M9 체크리스트). 백로그 1652 · 1683.
  2. 운영 `tn_menu` · `tn_menu_auth_map` 행과 역할별 사이드바 대조. 백로그 1653 · 1684 의 운영 부분.
  3. 실 배포 번들 · 열린 탭의 403 처리 (sha 대조 포함). 백로그 1657 · 1687. 다시 볼 조건은 데이터 API 하나 (예: `GET /admin/user`) 에 E0607 주입이다.
  4. 관리자 · site admin-view 렌더: 장애 안내 · 재시도 · 버튼 숨김 · /403 경합 · 경로 전환 빈 화면 시간. 자동 검사가 없다.
  5. 증명 #12 (`probe-compare.mjs` 의 `ME_KEYS`) 재동결 (격리 스택 필요). 백로그 1680.
  6. `agcAnalysisView` 조회 SQL 의 격리 MySQL 실행 (이연).
  7. 배포 순서: 서버 먼저, 관리자 프런트 나중. 반대면 영업 · 기업 사이드바가 옛 서버의 `tn_menu` 값으로 그려진다.
  8. stg 병합 sha 대조 (정책 §1-1).
- 완료 판정이 봐야 할 잔여 (코드 · 격리로 할 수 있는데 하지 않은 것. 메인 세션 확인 대상):
  - 내부 메뉴 ↔ 권한 매핑은 3 URL 뿐이다 (`menu/menu-permission-map.ts`: `/user/user` · `/money/general` · `/subscription/treat-monitor`). 나머지 내부 메뉴는 미매핑이다 (정책과 다르게 옮긴 자리 4).
  - 증명 행 「역할별 메뉴 집합 행 대조 (현행 사이드바 = 파생 집합)」 의 합성 부분 (내부 역할 대 파생 집합) 은 돌리지 않았다. 이번 보완의 대조는 영업 · 기업 리프 집합 (검사 #5, 골든) 뿐이다. 백로그 1684 (미달 B) 가 이 잔여다.
  - 이 두 잔여를 판정에서 뺀 것은 「PRD 7 행 Outcome 의 코드 몫」 기준에 따른 것이다. 기준을 더 엄격히 잡으면 행 7 은 in-progress 로 되돌리고, 백로그 1683 · 1684 에 「다음 계획 반영 대상 · 보완 계획 admin-rbac-m7cb」 를 붙인다. 백로그 1683 · 1684 는 이 판에서 열린 채 둔다.
- 편차 · 사용자 확인 대상: 판정 실패 중 최고관리자는 `getMyMenuTree(admin, false)` 로 좁은 트리를 받는다. 좁은 트리 밖 경로는 /403 이 아니라 안내로 간다. 안내 문구 「메뉴를 불러오지 못했습니다」 는 영구 환경 상태에서도 같은 문구다 (최종 리뷰 기각 행). 문구 변경은 사용자 몫이다.
- 다음 마일스톤이 알아야 할 것:
  1. 착수 조건의 「M7 닫힘」 (M8 · M9 · M10) 은 이 판정 (complete) 을 기준으로 본다. 스위치 ON 은 M9 체크리스트 몫이고, 켜기 전에 위 내부 매핑 3 URL 과 합성 대조 잔여를 먼저 처리한다.
  2. `/admin/me` 응답 키 (`superAvailable` · `permissionsAvailable` · `menusAvailable` · `menuSource`) 는 M8 · M9 가 기대는 계약이다. 키를 바꾸면 관리자 프런트 `meDecision` 과 site 소비자를 같이 고친다.
  3. 착수 HEAD (`m7c` 시작): 부모 `6d32aab` · 서버 `ec01f883` · 관리자 `a9159d2a` · 사이트 `fc8872a1`. 마감 HEAD: 서버 `16141f29` · 관리자 `1301b664` · 사이트 `fc8872a1` (사이트는 이 보완에서 바뀌지 않았다).

## 계획 기록 (2026-10-20)

- 계획서: `.claude/PRPs/plans/admin-rbac-m7c.plan.md` (커밋 `6984906`, 인용 경로 정정 뒤 커밋이 하나 더 있다). 사이드카: `.claude/PRPs/plans/admin-rbac-m7c.gates.sh`.
- 착수 HEAD: 부모 `6d32aab` · 서버 `ec01f883` · 관리자 `a9159d2a` · 사이트 `fc8872a1` (feat/admin-rbac). 검사 `#1` 이 본다.
- 범위: 마일스톤 7 의 미달 가운데 네 조각 (1677 판정 실패 정정 · 버튼 필터 최소 조각 · 영업 · 기업 메뉴 서버 파생 · 스위치 OFF 고정). 나머지는 이연 (계획서 「이월 지적 반영」 표).
- 계획 게이트 1회차 진입: `pre.sh 7c` exit 0 (`ROUND=1/3`, L1 통과). 리뷰 패널은 상위 워크플로가 발사한다.

## 계획 시점 관측

- 판정 실패 분기: `insuniverse-server/apps/api/src/modules/menu/admin-me.service.ts:80-95` 가 `isSuperAdminStrict` 던짐을 `superAvailable: false` · `menusAvailable: false` 로 받고 돌아간다. 관리자 프런트는 `insuniverse-admin/src/layouts/AdminLayout.tsx:379-381` 에서 메뉴 장애 안내만 그린다.
- 버튼 · 게이트 사용처: `ConsoleNavEntry.tsx:26` (버튼) · `ConsolePage.tsx:52` · `SamsungIntakePage.tsx:51` · `SalesCommissionPage.tsx:74` (페이지 게이트). `DetailAdminPage.tsx` 의 `isSuper` 는 조회 대상 행의 속성 표시라 제외.
- 영업 · 기업 상수는 `insuniverse-admin/src/features/authority/constants/menu.ts` 의 `salesMenus` · `salesCode` · `corpMenus` · `corpCode` 이고, 소비처는 `AdminLayout.tsx:399-432` 하나다.
- 타입 기준선: 서버 `npx tsc --noEmit -p tsconfig.json` 23건 · 관리자 `npx tsc -p tsconfig.node.json --noEmit` 764건.
- 구현 전 검사 관측: 검사 `#1` · `#10` · `#11` · `#12` · `#14` 녹색, 나머지 (`#2` ~ `#9` · `#13`) 붉음. 사이드카 Validation 블록의 관측 기록과 같다.

## 정정 (앞 계획과 다른 동작)

- m7 · m7b 는 판정 조회가 던지면 권한 집합과 메뉴를 읽지 않았다 (`admin-me.service.spec.ts` 의 「메뉴를 묻지 않는다 (음성)」 제목, m7b 검사 `#3`). 이 계획은 PRD M4 원칙 (「최고관리자 판정이 불가하면 최고관리자 전용 권한만 닫고 나머지 판정은 계속한다」) 에 맞춰 반대로 바꾼다. 근거: 백로그 1677 (HIGH) · 1688 · m7b 노트 「마감 재판정」 I6 (`fix-now`). 앞 계획서와 앞 노트는 고치지 않는다.
- 판정 실패 시 최고관리자 전용 메뉴만 빠진다. 사이드바는 넓어지지 않는다 (fail-closed). 이 문장은 일반 관리자에게만 참이다. 최고관리자는 판정 실패 때 그룹 매핑 트리로 좁아진다 (서버 `admin-me.service.ts` 의 `getMyMenuTree(admin, false)`). 그래서 좁은 트리 밖 경로는 /403 이 아니라 안내와 재시도로 간다 (`superUnknown`, 아래 「코드 리뷰」 R2 MEDIUM 행).

## 정책과 다르게 옮긴 자리

1. 메뉴 원천: 영업 · 기업 메뉴 정의를 서버 카탈로그로 옮기고 프런트 상수를 지운다 (계획서 「정책과 다르게 옮긴 자리」 3). 「상수 유지 + 권한 코드만 서버」 는 메뉴 정의가 프런트에 남으므로 기각했다.
2. 버튼 필터의 최고관리자 전용 코드는 `rbac.manage` 하나다 (`SUPER_ONLY_FRONT`). 다른 전용 코드를 읽는 소비자가 생기면 목록을 옮긴다.
3. 폴백 표지 (`fromFallback`): 영업 · 기업 사이드바가 `/admin/menu/my` (tn_menu 값) 로 잘못 그려지지 않게 한다.

## 이연 · 사용자 몫 (미검증)

1. 메뉴 파생 스위치 `MENU_SOURCE_DERIVED` ON 전환 (정책 §3 · M9 체크리스트). 백로그 1652 · 1683.
2. 운영 `tn_menu` · `tn_menu_auth_map` 과 역할별 사이드바 대조 (stg 읽기 또는 운영 확인, 사용자 몫).
3. 실 배포 번들 · 열린 탭의 403 처리. 백로그 1657 · 1687 의 219 재계수는 이연.
4. 관리자 프런트 렌더 (재시도 버튼 · 장애 안내 · 콘솔 버튼 숨김 · `/403` 경합). 자동 검사가 없다.
5. 배포 순서: 서버 먼저, 관리자 프런트 나중. 반대면 영업 · 기업 사이드바가 옛 서버의 tn_menu 값으로 그려진다.
6. `agcAnalysisView` 조회 SQL 의 격리 MySQL 실행 (`m7c-iso-` 접두 컨테이너, 합성 행 1건).
7. 증명 #12 (`probe-compare.mjs` 의 `ME_KEYS`) 재동결 (격리 스택 필요).
8. M5 감시 슬롯 잔여 (1560 · 1563 · 1564 · 1577), M6 보완 (1593–1633), 1574 분류 정정, 1509 (M2 와일드카드) 는 이 보완 범위 밖이다. 각각 해당 마일스톤의 보완 계획 몫이다.
9. 마일스톤 7 상태는 `in-progress` 로 둔다 (complete 표기 없음).

## 계획 게이트

- 판정: 수렴 (`converged`, 방식 `hybrid`). 계획서 해시 `sha256:54d92de99c40f61da6df295d3dbef357e5cdee53fdfce16619df966879e8424c` (HEAD `e0a3041`) 를 R3 가 검토했고, 그 뒤 계획서는 바뀌지 않았다.
- 구성: L1 = 기계 검사 · L2 = 4관점 패널 (architect · security · test · invariant, 정족수 3/4) · L3 = Codex 교차 리뷰.
- receipt: `.claude/receipts/mccp-plan-codex/admin-rbac-m7c.json` (`seal-converged.sh 7c` 로 봉인, 검증 exit 0). 리뷰 기록: `.claude/reviews/plan-review-admin-rbac-m7c.md` (R3 기준, 판정 `converged`).
- 계획서 「리뷰 라운드 기록」 표는 동결이라 R3 행을 더하지 않았다. 수렴 사실은 이 절에 적는다.

### 라운드별 결과

| 라운드 | L2 (4관점) | L3 (Codex) | 판정 | 층위 |
|---|---|---|---|---|
| R0 자가 1패스 (게이트 전) | - | - | 자가 점검 | 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 |
| R1 (계획 `18469bb`) | divergent, passed=false, blocking 2건 (test HIGH · test FAIL) | divergent (high 1 · medium 1) | divergent, 5.2e 에서 중단 | 2 · 1 · 6 |
| R2 (계획 `92c94046`) | pass 4/4, 지적 10건 (MEDIUM 4 · LOW 6) | divergent (medium 1) | divergent | 2 · 1 · 6 · 3 |
| R3 (계획 `54d92de9`, HEAD `e0a3041`) | pass 4/4, 지적 6건 (MEDIUM 1 · LOW 5) | converged | converged (L1 · L2 · L3 모두 converged) | 3 · 2 · 6 · 1 |

- 층위 번호 (작성 체크리스트): 1 설계 결함 · 2 주장과 검사 불일치 · 3 명령을 게이트로 착각 · 4 셸 실패 규약 · 5 게이트 실행 불가 · 6 게이트가 보는 대상 · 7 검사 자체 주장 · 8 검사의 사각지대.
- R0 · R1 · R2 의 층위는 계획서 「리뷰 라운드 기록」 표의 값이다. R3 의 층위는 이 절에서 지적별로 나눈 값이다 (계획서 표에 R3 행이 없다).
- R3 는 상한 3 의 마지막 라운드였지만 수렴으로 끝났다. 상한 도달 경로가 아니라서 backlog 추가와 divergent 기록은 하지 않았다.

### R3 미흡수 지적 (수렴 판정 뒤, 계획서 동결 · 기록만)

1. architect LOW: 최고관리자 전용 코드 목록 (`SUPER_ONLY_FRONT`) 이 서버 목록과 두 벌이다. 새 전용 코드 소비자가 생겨도 기계 검사가 없다 (계획 Risks 3). 층위 2.
2. security LOW: 프런트 표지 (`fromFallback` · `usePermission`) 결과는 화면 노출만 정한다. 실제 경계는 서버 가드다. 층위 2.
3. test MEDIUM: 검사 `#4` 는 호출 자리 계수만 본다. `yes` → super 같은 매핑과 `ConsoleNavEntry` 의 버튼 조건은 보지 않는다. 층위 3.
4. test LOW: 판정 실패 때 `getMyMenuTree(admin, false)` 를 부른다는 제목이 없다 (Task 2 (c)). 층위 2.
5. test LOW: 검사 `#9` 는 문자열 존재만 본다. 경쟁 차단은 `createLoadGate` 단위로만 증명되고 레이아웃에서는 (미검증). 층위 6.
6. invariant LOW: `permissionDecision` 은 `superAvailable === false` 만 unknown 으로 본다. 키가 없고 `isSuper: true` 면 `rbac.manage` 가 yes 가 된다 (옛 서버 형상). 계획 Risks 12 (옛 서버 지원 종료) 와 `meFallback` 이 막는다. 층위 1.

### 우회 기록

- 봉인 중 intent 게이트가 같은 write 를 막았다. 감사 우회 (`intent_gate_force_override=true`) 로 같은 write 를 다시 불렀고, receipt 에 `intent_gate_verdict=incomplete` 가 남았다. 우회 사유 (receipt 원문): 「hybrid 패널과 L3 Codex 가 모두 수렴해 intent 판정 대상 지적이 없다 — mccp 1.33.6 hybrid write 의 intent gate 결함 우회」.
- receipt 최상위 `round` 필드는 1 이고, `resolution.rounds` 와 `round_ledger_count` 는 3 이다. receipt 가 쓴 값 그대로 두었다.

## 다음 단계

- 계획 게이트 라운드 진행은 상위 워크플로가 한다 (`post-a.sh` · `post-b.sh` 순서는 gatekit README 를 따른다). 라운드 상한 3 에서 CRITICAL 이 없으면 사용자 지시 (UI6) 에 따라 skip-receipt 로 구현에 들어간다. 그 판정은 `verify-user-directive.sh` 로 확인한다.
- 구현 뒤 커밋 전에 별도 세션 code-review (UI3).

## 구현 기록 (2026-10-20, implement 단계)

- 범위: 계획서 Task 1 ~ 5 (Task 6 의 백로그 행은 d16ed41 에 이미 있어 추가하지 않았다. 검사 #13 이 7건과 삭제 0 을 본다).
- 커밋: 하지 않았다 (사용자 지시 — 구현 뒤 별도 세션에서 code-review 를 돌린다). 부모 · 서브모듈 gitlink 는 그대로다.
- 워킹트리 변경:
  - 서버 (`insuniverse-server`): `auth/admin/admin-identity.ts` (선택 칸 4개) · `menu/admin-me.service.ts` · `menu/admin-me.service.spec.ts` · 신규 `menu/sales-corp-menu.ts` · `menu/sales-corp-menu.catalog.ts` (생성) · `menu/sales-corp-menu.spec.ts`.
  - 관리자 (`insuniverse-admin`): `api/meDecision.ts` · `api/meDecision.test.ts` · `api/menuApi.ts` · `api/alertApi.ts` · `api/salesCommissionApi.ts` (주석) · `api/superAdminApi.ts` 삭제 · `shared/auth/usePermission.ts` · `shared/auth/menuConvert.ts` · `shared/auth/menuConvert.test.ts` · `layouts/adminGate.ts` · `layouts/adminGate.test.ts` · `layouts/AdminLayout.tsx` · `features/authority/constants/menu.ts` (네 상수 삭제) · `features/authority/ui/ConsoleNavEntry.tsx` · `pages/Console/ConsolePage.tsx` · `pages/SamsungIntake/SamsungIntakePage.tsx` · `pages/SamsungIntake/api.ts` (주석) · `pages/Admin/Sales/Commission/SalesCommissionPage.tsx`.
  - 부모 (`.claude/`): 신규 `meta/2026-10-20-admin-rbac-m7c/build-catalog.mjs` · `golden.json` (생성 결과 사본) · 이 노트.
- 골든 생성 방식: 고정 sha `a9159d2a` 의 blob 에서 `filterMenusByAuthority` · `getCorpMenuCodes` · 상수 선언을 잘라 `stripTypeScriptTypes` 로 벗겨 실행한다. 서버 포트를 거치지 않은 현행 결과다. 카탈로그 TS 와 골든은 같은 생성기가 쓰고 `--check` 가 바이트로 비교한다.
- 계획과 다르게 간 자리 (편차):
  1. 골든 위치: 계획은 골든 JSON 을 meta 디렉터리에 두라고 했다. spec 이 부모 저장소 경로를 읽으면 서브모듈 단독 빌드가 깨지므로, 골든을 생성 카탈로그 TS 안 (`GOLDEN_LEAVES`) 에도 싣고 meta 의 `golden.json` 은 사람이 읽는 사본으로 둔다. 둘 다 `--check` 가 본다.
  2. 화면 브레드크럼: 영업 · 기업의 `findMenuItemAtDepth` 는 이제 서버가 준 (권한으로 거른) 사이드바 트리에서 찾는다. 이전에는 전체 상수 트리에서 찾았다. 숨겨진 메뉴의 경로는 어차피 `hasAuthorityForPath` 가 /403 으로 보내므로 보이는 화면의 브레드크럼은 같다. 렌더 검사가 없어 (미검증).
  3. 영업 · 기업 메뉴 요청은 `/admin/me` 를 마운트 · 경로 변경마다 한 번 부른다. 이전에는 로컬 상수라 요청이 없었다. 계획 Risks 4 의 범위 안이다.
  4. 권한 판정 훅은 캐시를 두지 않는다. 소비자 넷이 마운트마다 한 번씩 묻는다 (계획 Risks 4).
  5. 판정 실패 시 최고관리자가 아닌 관리자 경로를 탄다. 최고관리자 전용 코드는 `allowedForNonSuper` 가 뺀다 (계획대로). 정정 사유는 `admin-me.service.ts` 주석과 위 「정정」 절.
  6. 영업 · 기업 `/admin/me` 응답에 `menuSource: 'derived'` 를 달고, 프런트는 `derivedMenusReady` 로 키가 없거나 `fromFallback` 이면 장애로 닫는다 (계획 Task 4 (d)).
- 검증 (계획서 Validation 블록 = 사이드카 `admin-rbac-m7c.gates.sh` 전체):
  - `#1` ~ `#14` 전부 rc 0. 세부 출력은 계획서 「Validation」 절 참조.
  - 서버 menu 디렉터리 jest: 6 suites · 48 tests 통과.
  - 관리자 node 러너: `meDecision.test.ts` 18건 · `menuConvert.test.ts` 1건 · `adminGate.test.ts` 3건 통과.
  - 타입: 서버 `tsc -p tsconfig.json` 23 (기준 23) · 관리자 `tsc -p tsconfig.node.json` 764 (기준 764). 관리자 변경 파일에 새 오류는 없다.
  - eslint (관리자 변경 파일 · 서버 변경 파일, legacy 모드): 오류 0. `AdminLayout.tsx` 의 기존 fast-refresh 경고 1건은 원래 있던 `filterMenusByAuthority` export 다.
  - 임팩트 판정 (impeccable detect, implement 모드): `no-signal` · silent skip. 서브모듈 UI diff 를 부모에서 볼 수 없는 기존 한계다 (메모 `impeccable-detect-blind-to-submodules`).
- 미검증 (계획서 Acceptance 의 (미검증) 행과 같다): 관리자 렌더 (장애 안내 · 재시도 버튼 · 콘솔 버튼 숨김 · /403 경합) · 재시도 성공 경로 · 운영 `tn_menu` · `tn_menu_auth_map` 대조 · 스위치 ON · 실 배포 번들 · `agcAnalysisView` SQL 의 격리 MySQL 실행 · 증명 #12 재동결.
- 정리: 관리자 `tsc -p tsconfig.node.json` 이 만든 `tsconfig.node.tsbuildinfo` 는 삭제했다 (`composite` 설정이 만드는 산출이며 추적 대상이 아니다).

## Codex Implementation Review

- 호출: `node ${CLAUDE_PLUGIN_ROOT}/scripts/lib/codex-invoke.js adversarial-review` (fail-closed Bash wrapper, v0.2.2). 결과 원문은 `mccp/tmp/codex-r1.stdout` · `codex-r2.stdout` · `codex-r3.stdout` (작업트리 git 디렉터리 아래).
- 라운드 수: 3 (상한 3, 원장 `mccp-implement-codex__admin-rbac-m7c.json` rounds 3).
- 합치 결론: R3 approve (마지막 라운드, 수렴). R1 needs-attention (high 1 · medium 1), R2 needs-attention (medium 1) 은 모두 흡수했다.
- R1 대상 주의: R1 은 작업트리가 아니라 커밋된 브랜치 대 main 으로 잡혔다. 그래서 R1 의 「구현이 없다」 는 진술은 워킹트리 사실과 다르다 (흡수 뒤 R3 가 워킹트리를 읽고 승인했다). 2.5.3 의 focus 는 서브모듈 경로를 명시해야 워킹트리 검토가 된다 (R3 focus 가 그렇게 했다).
- YAGNI Triage:
  | Finding | Severity | Verdict | Why |
  |---|---|---|---|
  | R1 F1 super-admin 판정 실패 시 전 화면 장애 | HIGH | ACCEPT_NOW | `admin-me.service.ts` 가 판정 실패 뒤에도 일반 권한 · 메뉴를 계속 읽는다 (Task 2). R3 승인 |
  | R1 F2 응답 순서 가드 없음 | MEDIUM | ACCEPT_NOW | `createLoadGate` 로 가장 최근 응답만 화면 상태를 바꾼다 (Task 5). R3 승인 |
  | R2 F1 경로 전환 첫 렌더가 옛 메뉴로 /403 판정 | MEDIUM | ACCEPT_NOW | `AdminLayout.tsx` 에 `menuPath` 를 더해 받은 경로가 현재 경로일 때만 메뉴를 인정한다. 응답 전에는 로딩 (빈 화면). 렌더 검사는 없다 (미검증) |
- Deferred to backlog: 0 (이 구현 리뷰에서 이월할 HIGH 없음).
- Open Questions: 없음. CRITICAL · HIGH 미해소 0.
- Codex session 참조: R1 `01a15864-0e8b-7be3-871b-60c9ff06cfa0` · R2 `01a1586b-4b7c-7411-aa8f-a7d2b9e9d26f` · R3 `01a15870-2fff-7370-b119-67e79924940e`.

### Security Reviewer

> security-reviewer unavailable, skipped (auto-fallback): 이 실행 환경에는 Task 도구가 없어 security-reviewer 서브에이전트를 호출하지 못했다. 보안 민감 영역 (인가 판정 · 권한 메뉴 · 관리자 가드) 은 Codex 세 라운드가 검토했다.

### 임팩트 판정 (impeccable)

- 2.5.5b: `impeccable-detect.js detect --mode implement` = `no-signal` · `silent_skip=true` (서브모듈 UI diff 는 부모 저장소에서 보이지 않는다, 메모 `impeccable-detect-blind-to-submodules`).
- 3.6: 신호 0 이라 건너뛴다. 3.7: 설계 방향 캡처 산출이 없어 no-op.

### 최종 결과 (implement 마무리, 2026-10-20)

- 이어받은 상태: 앞 implement 에이전트가 Task 1~5 코드를 워킹트리에 썼고 검사 `#1`~`#14` 가 통과했다 (반환 봉투 `halt`, stage implement). 백그라운드 격리 실행은 없었다 (`m7c-iso-` 컨테이너 0, 완료 신호 파일 없음). 이어받은 Codex 라운드는 R1 · R2 였다.
- 이번 단계에서 한 것:
  1. R2 지적 (AdminLayout 경로 전환 첫 렌더가 옛 메뉴로 /403 판정) 을 고쳤다. `menuPath` 상태를 더하고 메뉴를 받을 때 받은 경로를 기록했다. `AdminLayout` 은 `menusLoaded`(메뉴 유무) 와 `menusFresh`(받은 경로가 현재 경로) 를 따로 `gateDecision` 에 넘긴다. 옛 메뉴는 허용 판정에만 쓰고, 거부는 현재 경로의 응답이 온 뒤에만 낸다 (규칙 정본: 아래 「코드 리뷰」 R1 행). 대가: 경로 전환 때 옛 메뉴가 허용하지 않는 경로는 응답 전까지 빈 화면이다. 렌더 검사가 없어 (미검증). 이 항목의 규칙은 코드 리뷰 R2 에서 다시 확인했다.
  2. R3 (상한 3 의 마지막 라운드) 를 워킹트리 대상으로 돌렸다. 결과 approve, 새 결함 없음. 상한에 걸리지 않아 skip-receipt · divergent 봉인은 하지 않았다. HIGH 이월도 없다 (backlog 추가 없음).
  3. 구현 리뷰 절을 이 notes 의 `## Codex Implementation Review` 에 적었다 (계획서에는 붙이지 않았다, 메모 implement-review-goes-to-notes).
  4. implement receipt 를 썼다 (`.claude/receipts/mccp-implement-codex/admin-rbac-m7c.json`, 커밋 안 함). 플래그: `--codex-verdict converged` · `--security-skipped` (Task 도구 없음) · `--impeccable-silent-skip` (no-signal). `validate --command mccp:prp-implement` exit 0.
  5. 2.5.5b 임팩트 판정 no-signal · 3.6 건너뜀 (신호 0) · 3.7 no-op (캡처 산출 없음).
  6. 검사 `#1`~`#14` 를 최종 상태에서 다시 돌렸다. `GATE_RC=0`. 서버 tsc 23 = 기준 23 · 관리자 tsc 764 = 기준 764. 관리자 `adminGate.test.ts` 3건 통과. 관리자 eslint 오류 0 (기존 fast-refresh 경고 1).
  7. 관리자 `tsconfig.node.tsbuildinfo` (tsc composite 산출) 는 삭제했다. 서브모듈에 줄바꿈만 바뀐 파일은 없다 (CR 상태가 바뀐 수정 파일 0).
- 편차 (계획 대비):
  1. 구현 리뷰 R1 은 커밋 대 main 으로 잡혔다 (워킹트리 아님). 그래서 R1 의 「구현이 없다」 진술은 무효이고, 그 지적 두 건 (HIGH 판정 실패 · 응답 순서) 은 워킹트리 구현으로 흡수됐다는 사실을 R3 승인이 확인했다.
  2. security-reviewer 는 호출하지 않았다 (이 실행 환경에 Task 도구 없음). `security_skipped` 를 receipt 에 남겼다. 보안 민감 영역은 Codex 세 라운드가 봤다. 이 receipt 는 `/mccp:pr` 에서 security_skipped 로 막힐 수 있다 (사용자 몫 판정).
  3. Phase 5 보고서 파일 (`.claude/PRPs/reports/admin-rbac-m7c-report.md`) 은 쓰지 않았다. 이 서브에이전트는 보고서 .md 를 쓰지 않도록 되어 있고, 보고 내용은 이 절과 반환 요약에 있다. 필요하면 상위 세션이 쓴다.
- 미검증 (계획 Acceptance 와 같다): 관리자 렌더 (장애 안내 · 재시도 · 콘솔 버튼 숨김 · /403 경합 · 경로 전환 빈 화면 시간) · 운영 `tn_menu` 대조 · 스위치 ON · 실 배포 번들 · `agcAnalysisView` SQL 의 격리 MySQL 실행 (이연, 실행 안 함) · 증명 #12 재동결.
- 커밋: 하지 않았다. 영향 범위: 관리자 서브모듈 git status 16 항목, 서버 서브모듈 6 항목 (수정 3 · 신규 3), 부모는 notes · receipt · meta 만.

## 코드 리뷰 (2026-10-20 · code-review R1)

| 라운드 | 출처 | 심각도 | 지적 | 판정 | 증거 | 층위 |
|---|---|---|---|---|---|---|
| R1 | code-review · typescript | HIGH (MEDIUM 1 과 합침) | 경로 전환마다 레이아웃이 비고 `/admin/me` 가 연속 2회 불린다 | 흡수 | `adminGate.ts` 규칙 재정의: 옛 메뉴는 허용에만 쓰고 거부는 응답 뒤에만 낸다. `AdminLayout.tsx:469-470` (`menusLoaded` 는 메뉴 유무, `menusFresh` 는 받은 경로가 현재 경로). 빨강→초록: `adminGate.test.ts:51-58` 신규 제목 (수정 전 `menusFresh:false` 거부가 redirect403, 수정 뒤 loading). 렌더 전환은 미검증 | 1 |
| R1 | security · typescript | MEDIUM (security LOW 1 과 합침) | 판정 실패 중 최고관리자가 일반 코드를 `no` 로 받는다 (불완전 집합 + `permissionsAvailable=true`) | 흡수 | `meDecision.ts:52` (`superAvailable=false` 면 전 코드 `unknown`). 빨강→초록: `meDecision.test.ts` 신규 제목 (수정 전 `no`). 서버 계약 주석 `admin-me.service.ts:40-43` 에 불완전 집합 명시 | 1 |
| R1 | typescript | MEDIUM | `menus` 가 `any` 라 검사 #14 의 구조 검사가 공허하다 | 흡수 | `menu.service.ts:8-10 · 92-94` (`MenuTreeNode` 구체형, `buildMenuTree` 반환형). 반사실 프로브 (임시, 제거됨): 수정 전 서버 tsc 23 (프로브 미검출) · 수정 뒤 24 (TS2322) · 프로브 제거 뒤 23 | 5 |
| R1 | code-review | LOW | `ConsoleNavEntry` 가 마운트마다 `/admin/me` 를 부른다 | 흡수 (부분) | 헤더가 경로 전환에 다시 마운트되지 않는다 (HIGH 행). `usePermission` 효과 의존 `[key, round]` (`usePermission.ts:49`). 레이아웃의 경로별 1회 조회는 HEAD 와 같은 갱신 주기이고 계획 Risks 4 범위 | 1 |
| R1 | code-review | LOW | `adminGate` 테스트 제목 「(백로그 1667)」 이 배선까지 증명한다고 읽힌다 | 부분 흡수 | 제목은 검사 #8 이 정확 일치로 고정하므로 바꾸지 않았다. 본문 주석에 판정만 본다고 적었다 (`adminGate.test.ts:61-63`). 배선 (옛 메뉴 비우기 · 응답 순서) 은 검사 #9 의 grep 계수만 본다 (미검증) | 2 |
| R1 | code-review | LOW | 사이드바 아이콘 · 그룹 일치를 검사 #5 가 보지 않는다 | 이연 | 검사 #5 의 범위는 리프 집합이다 (계획 Acceptance). 계획 Design Critique 의 「아이콘 · 순서」 주장은 계획서 동결 규칙상 고치지 못한다. 다음 계획에서 주장을 정정하거나 키에 그룹을 넣는다 | 2 |
| R1 | code-review · typescript | LOW (서식 LOW 1 과 합침) | 관련 없는 줄의 서식 변경이 diff 에 섞였다 | 흡수 | 서식 전용 hunk 8건을 역적용 (`git apply -R --unidiff-zero`, HEAD 기준 시작 줄 72 · 83 · 92 · 109 · 132 · 164 · 195 · 250). `filterMenusByAuthority` 는 M7c 로 호출자 0 이 되어 삭제 (`AdminLayout.tsx` 의 export 제거 · 린트 경고 1건도 함께 사라짐) | - (위생) |
| R1 | code-review | LOW | `getMe().then` 에 `catch` 가 없어 형상이 깨지면 빈 화면이 조용히 남는다 | 흡수 (렌더 미검증) | `AdminLayout.tsx:408-413` (`catch` → `setMenuUnavailable(true)`, 안내와 재시도). 관리자 분기에 파생 응답이 오면 장애로 닫는다 `AdminLayout.tsx:356` | 1 |
| R1 | code-review | LOW | 죽은 권한 규칙 (`getCorpMenuCodes`) 과 삭제된 `corpMenus` 를 가리키는 주석 | 흡수 (부분) | `corpRole.ts` 의 `getCorpMenuCodes` 삭제 (호출자 0). 주석 `corpRole.ts:40` · `CorpOrganizationPage.tsx:7` 정정. `CORP_ORG_HIDDEN_MENU_CODES` · `CORP_ANALYSIS_MENU_CODE` 는 소비처 0 이지만 정책 상수로 남겼다 (이연). 서버 `corpMenusFor` 와의 복제는 남아 있다 | 2 |
| R1 | code-review · typescript | LOW | `B2` 범위 밖 파일 (notes · backlog) 을 계획이 허용하고 `B2` 검사가 Validation 에 없다 | 이연 | 계획서 동결 (작업 규칙 4). `B2` 는 CLAUDE.md §0 에서 4곳으로 정해졌고 notes 는 계획 Task 1 이 적은 기록 표면이다. 다음 계획에서 허용 근거를 고치거나 검사를 더한다 | 2 |
| R1 | security | LOW | `permissionsAvailable=true` 가 판정 실패 때 잠복 계약 혼동을 만든다 | 흡수 (MEDIUM 행에 합침) | 소비자 쪽 닫기 (`meDecision.ts:52`) 와 서버 계약 주석 (`admin-me.service.ts:40-43`). 서버 `permissionsAvailable` 값은 spec 제목이 고정하므로 바꾸지 않았다 | 1 |
| R1 | typescript | LOW | `ApiMeResponse` 가 판별자 없는 합집합이라 `as` 캐스트 2곳이 검사를 피한다 | 부분 흡수 | 관리자 분기에 `me.menuSource === "derived"` 장애 가드 (`AdminLayout.tsx:356`), 파생 분기는 `derivedMenusReady` 가 `menuSource` 를 요구한다. 타입 판별 합집합은 `meFallback` 의 타입 전파가 커서 이연 (캐스트 자체는 남았다) | 1 |
| R1 | typescript | LOW | `ConsoleNavEntry` 주석이 동작과 반대로 쓰였다 | 흡수 | `ConsoleNavEntry.tsx` 주석 정정 (`rbac.manage` 가 yes 일 때만 렌더) | 2 |
| R1 | typescript | LOW | `corpScope` 가 `'SUPER' \| 'ORG' \| string` 이라 리터럴 선언이 접힌다 | 흡수 | `admin-identity.ts:15` 를 `'SUPER' \| 'ORG'` 로. 서버 tsc 23 유지 (`corpMenusFor` 입력은 `string` 으로 두었다) | 1 |
| R1 | typescript | LOW | 테스트 오라클 `GOLDEN_LEAVES` 가 런타임 카탈로그 모듈에서 export 된다 | 흡수 (서식 부분 미해결) | 골든을 `sales-corp-menu.golden.ts` 로 분리. 생성기 `build-catalog.mjs` 가 세 산출을 쓰고 `--check` 가 모두 본다. spec 은 새 파일을 import. 생성 산출의 JSON 식 따옴표 키는 prettier 와 어긋난 그대로다 (미해결 · LOW) |
| R2 | code-review · typescript | MEDIUM | 판정 실패 중 최고관리자가 좁은 메뉴 트리 밖 경로를 /403 으로 받는다 (「모름」 인데 「거부」 로 표시) | 흡수 | `adminGate.ts` 입력 `superUnknown`: 거부는 이 값이 참이면 `notice`, 아니면 `redirect403`. `AdminLayout.tsx` 가 관리자 응답의 `superAvailable === false` 를 `superUnknown` 상태로 넘긴다. 빨강→초록: `adminGate.test.ts` 신규 제목 「최고관리자 판정이 모르면 거부도 확정이 아니라 안내로 간다」 (수정 전 not ok, 수정 뒤 ok). 렌더 전환은 미검증 | 1 |
| R2 | code-review · security | LOW | 판정 실패 때 최고관리자의 메뉴가 `getMyMenuTree(admin, false)` 로 좁혀진다. 정정 절의 「전용 메뉴만 빠진다」 가 최고관리자에게 틀렸다 | 흡수 (표시 · 정정) · 서버 동작은 유지 | 정정 절 (앞부분) 을 고쳤다. 서버 `admin-me.service.ts:151` 은 fail-closed 라 넓어지지 않는다. 화면은 위 MEDIUM 행이 바꿨다. 서버를 바꾸지 않은 이유: 메뉴 원천 변경은 계획 밖이고 스위치 OFF 경로는 불변 (검사 #11). 잔여: 판정 실패 중 좁은 트리 안의 화면은 좁은 사이드바로 열린다 (미검증) | 1 |
| R2 | code-review · typescript | LOW | 레이아웃 `.catch` 가 오류 객체를 버려 프로그래밍 오류가 장애 안내로만 보이고 원인을 남기지 않는다 | 흡수 | `AdminLayout.tsx` `.catch((e: unknown) => console.error(...))`. 장애 안내 · `loads.accept` 는 그대로다. 반사실: 수정 전 catch 본문의 콘솔 호출 0 (관찰: `.catch(() =>`), 수정 뒤 1 (grep). 콘솔 출력과 렌더는 미검증 | 1 |
| R2 | code-review · typescript | LOW | `Header.Breadcrumb` 한 줄 서식 변경이 diff 에 남았다 (R1 서식 역적용의 잔여) | 흡수 | `AdminLayout.tsx` 세 줄 형태를 HEAD 와 같게 복원. `git -C insuniverse-admin diff -U0 -- src/layouts/AdminLayout.tsx` 의 Breadcrumb 줄 0 | - (위생) |
| R2 | code-review | LOW | 이 노트 「최종 결과」 1항의 gateDecision 규칙이 R1 행과 달랐다 (`menusLoaded` 에 `menuPath` 를 섞어 적었다) | 흡수 (문서 정정) | 1항을 R1 행 기준으로 고쳤다. 정본은 R1 행 · `AdminLayout.tsx` 의 `menusLoaded` · `menusFresh` 두 입력 | 2 |
| R2 | code-review | LOW | 삼성 인테이크 화면 주석이 헤더 버튼 · 콘솔도 `useSuperAdmin` 을 쓴다고 적었다 | 흡수 | `SamsungIntakePage.tsx:40-42` 를 두 훅의 역할 분담으로 고쳤다 (인테이크 · 영업비 = `useSuperAdmin`, 헤더 · 콘솔 = `usePermission("rbac.manage")`). `ConsoleNavEntry.tsx:21` · `ConsolePage.tsx:46` 과 맞다 | 2 |
| R2 | code-review | LOW | 조직관리 라우트 주석이 소비처 없는 프런트 상수를 차단 원천으로 적었다 | 흡수 | `organization.routes.tsx:5-8` 를 서버 파생 트리 (`sales-corp-menu.ts` `effectiveCorpCodes` · `CORP_ORG_HIDDEN_CODES`) 기준으로 고쳤다. `CORP_ORG_HIDDEN_MENU_CODES` 는 소비처 0 이라 R1 행의 이연을 그대로 둔다 | 2 |
| R2 | code-review | LOW | `superDecision` (본문 모양 깨짐 = denied) 과 `permissionDecision` (= unknown → 장애) 이 같은 응답을 다르게 읽어 화면 문구가 갈렸다 | 흡수 | `meDecision.ts` `superDecision`: 본문이 없거나 모양이 다르거나 `isSuper` 가 불리언이 아니면 unavailable. 두 판정이 같은 분류를 낸다. 빨강→초록: `meDecision.test.ts` 제목 「superDecision: 본문이 없거나 모양이 다르면 unavailable」 · 「superDecision 과 permissionDecision(rbac.manage) 은 모양이 깨진 본문을 같이 장애로 읽는다」 (수정 전 not ok 2건, 수정 뒤 ok). 콘솔 · 인테이크 화면 문구의 실측은 미검증 | 1 | 1 |

- 층위: 1 설계 결함 · 2 주장과 검사 불일치 · 3 명령을 게이트로 착각 · 4 실패 규약 · 5 게이트가 실행 불가 · 6 게이트가 보는 대상 · 7 검사 자체 주장 · 8 검사 사각지대. `-` 는 체크리스트 밖의 위생 지적이다.
- 백로그: 흡수하지 못한 HIGH · MEDIUM 이 0건이라 `codex-findings-backlog.md` 에 추가하지 않았다.
- 검사 (수정 뒤 전체 실행): `#1`~`#14` 전부 rc 0. 서버 tsc 23 = 23, 관리자 tsc node 764 = 764 (변경 파일 새 오류 0). 서버 menu jest 6 suites · 48 tests 통과. 관리자 node 러너 `meDecision` 19 · `adminGate` 4 · `menuConvert` 1 통과. 서버 · 관리자 변경 파일 eslint 오류 0.
- 미검증: 관리자 렌더 (경로 전환 시 빈 화면이 없는지 · 장애 catch 안내 · 헤더 버튼 재마운트 횟수) 는 실행하지 못했다. 게이트 #9 는 grep 계수만 본다.
- R2 검사 (수정 뒤 전체 실행): 검사 `#1`~`#14` 전부 rc 0 (sidecar 전체 실행). 관리자 node 러너 `meDecision` 20 · `adminGate` 5 · `menuConvert` 1 · 합계 26 통과 (수정 전 신규 3건 not ok). 관리자 tsc node 764 = 764. 관리자 변경 파일 eslint (legacy 모드) 오류 0. 서버 파일은 이번 라운드에 바꾸지 않았다.
- R2 미흡수: 0건 (HIGH · MEDIUM 전부 흡수). 백로그 추가 없음.
- R2 잔여 (미검증): 판정 실패 중 최고관리자는 좁은 사이드바로 열린 화면을 쓴다. 좁은 트리 밖 경로는 안내로 간다. 렌더 확인은 하지 않았다.

## 최종 리뷰 (2026-10-20 · 최종 리뷰 라운드 1/3)

- 리뷰어: fable (지적 5건) · codex (지적 1건). 같은 지적이 두 리뷰어에게서 나온 것은 한 행으로 합쳤다 (검사 #1).
- 사용자 지시 확인: `verify-user-directive.sh` rc 0 (type=user · userType=external, 2026-10-19T02:21:22Z). 마지막 라운드는 CRITICAL 이상이 아니면 skip-receipt 로 진행하고 HIGH 는 백로그로 넘기라는 지시다. 이 라운드에 CRITICAL · HIGH 지적은 없다.

| 라운드 | 리뷰어 | 심각도 | 지적 | 판정 | 증거 | 층위 |
|---|---|---|---|---|---|---|
| 최종 R1 | fable | MEDIUM | ON 경로의 영업 spec 이 역할 가드 (`admin.role === 'admin'`) 상실을 잡지 못한다. 제목은 「현행 트리를 참조하지 않는다」 인데 mock 맵이 `/a` · `/b` 뿐이라 영업 URL 이 하나도 매핑되지 않는다 | 흡수 | `admin-me.service.spec.ts` 의 `onService` mock 맵에 영업 리프 `'/user/user': 'user-admin.read'` (USR001) 를 넣었다. 반사실: `admin-me.service.ts:161` 의 `&& admin.role === 'admin'` 을 지운 사본에서 「영업 역할은 스위치와 무관하게…」 제목이 not ok (1 failed · 23 passed). 복구 뒤 menu jest 6 suites · 48 tests 통과 | 2 |
| 최종 R1 | fable (LOW) · codex (MEDIUM) | MEDIUM | 검사 #1 이 착수 HEAD 의 동일성 (`rev-parse == 착수 sha`) 을 봐서 구현 커밋 (server 2d9835c6 · admin 1301b664) 뒤 `bash gates.sh` 가 줄 94 에서 rc 1 로 멈춘다. 계획 Acceptance 의 「조상이다」 와 어긋난다 | 흡수 | `admin-rbac-m7c.gates.sh` 검사 #1 을 `git merge-base --is-ancestor` 로 바꿨다 (착수 기록 sha 4건 · 선행 병합 sha 6건). 커밋 뒤 HEAD 에서 `bash gates.sh` 전체 rc 0. 음성: `2d9835c6` 는 `ec01f883` 의 조상이 아니어서 `merge-base --is-ancestor` rc 1. 계획서 본문은 고치지 않았다 | 5 |
| 최종 R1 | fable | LOW | `superUnknown` 거부가 「메뉴를 불러오지 못했습니다. 잠시 후 새로고침하세요」 안내로 간다. 원인이 DDL 미적용 같은 영구 환경 상태인데 일시 장애로 읽힌다 (`adminGate.ts:37` · `AdminLayout.tsx:503`) | 기각 | 노출은 넓어지지 않는다 (fail-closed · 인가 경계는 서버). 문구는 계획 Design Critique 의 「한 줄 안내」 결정 범위이고 카피 변경은 사용자 몫이다. 분기를 넣으려면 계획 개정이 필요하다. 렌더는 (미검증). 다음 계획의 결정 항목으로 넘긴다 (LOW 라 백로그에는 적지 않았다) | 1 |
| 최종 R1 | fable | LOW | m7b 검사 #3 의 제목 「판정 조회가 던지면 superAvailable 이 false 이고…」 이 spec 에서 사라져 m7b Validation 이 영구히 붉다 (`admin-rbac-m7b.gates.sh:130`) | 기각 | m7c 가 이 제목의 의미를 뒤집은 것은 의도된 정정이다 (PRD M4 원칙 · 백로그 1677, 본 노트 「정정」 절 `:18`). 앞 계획서 · notes 는 동결 규칙이라 m7b 에 표지를 붙이지 않았다. m7b 검사 #3 이 붉은 것은 m7c 정정의 결과다 | 5 |
| 최종 R1 | fable | LOW | 계획 Files to Change 표 밖 파일 5건이 바뀌었다 (`corpRole.ts` · 주석 2곳 · `menu.service.ts` · `sales-corp-menu.golden.ts`). 동작 변화는 없다 | 기각 | 계획서는 plan_hash 로 동결돼 표를 고칠 수 없다. 사유는 「코드 리뷰」 R1 · R2 행에 있다. 검사 #14 (tsc 23 · 764) 는 그대로다. 다음 계획에서 「리뷰 흡수로 추가될 수 있는 파일」 허용 문장이나 `diff --stat` 대조 검사를 둔다 (이연) | 2 |

- 검사 (수정 뒤 전체 실행): `bash .claude/PRPs/plans/admin-rbac-m7c.gates.sh` 의 #1~#14 전부 rc 0. 검사 #2 · #5 · #11 제목은 각 4 · 8 · 4 건 passed · 실행 실패 0. 서버 tsc 23 = 23 · 관리자 tsc node 764 = 764.
- 백로그: 흡수하지 못한 HIGH · MEDIUM 이 0건이라 `codex-findings-backlog.md` 에 추가하지 않았다. 이 라운드의 CRITICAL · HIGH 도 0건이다.
- 미검증: superUnknown 안내의 렌더 (사용자에게 어떻게 보이는지) 는 실행하지 않았다.
