# 관리자 RBAC 마일스톤 9e · 보완 4차 기록 (admin-rbac-m9e)

- 계획: `.claude/PRPs/plans/admin-rbac-m9e.plan.md` · 검사: `.claude/PRPs/plans/admin-rbac-m9e.gates.sh`
- 산출 디렉터리: `.claude/meta/2026-10-26-admin-rbac-m9e/` (구현 단계에서 만든다)
- 원장 구획: `.claude/state/santa-ledger.md` 의 「마일스톤 9e」 (gitignore 대상)
- 범위: 백로그 `admin-rbac-m9db` 표지 HIGH 다섯 줄 (2007–2011) 과 짝인 HIGH 여섯 줄 (1966 · 1973 · 1974 · 1984 · 1985 · 1986). PRD 행 9 의 문구와 상태 열은 바꾸지 않는다.

## 마감 요약 (메인 마감 · 2026-10-26)

1. 판정: 행 9 (PRD 마일스톤 9 · 영역별 강제) 는 in-progress 를 유지한다. 상태 열과 문구는 바꾸지 않았다. 격리로 할 수 있는데 이 보완에서 하지 않은 미달이 남아 complete 로 올리지 않는다 (아래 「PRD 미달」).
2. 기준: 서버 bf0c1db5 (테스트 두 파일 e6343c1c · bf0c1db5, 운영 소스 변경 0) · 부모 f9926bc5 · 측정 SRV_TREE 5e740146 (흡수 시험 두 파일을 뺀 트리).
3. 게이트: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh` → FAILS=0 (PASS 27, rc 0). 마감 편집 뒤 워킹트리에서 다시 돌렸다.
4. 구현 중 검사 (구현 단계 기록): 변이 반사실 M1–M9 전부 RED (`mutation-m9e.txt`) · 격리 itspec 14 suite · 133 시험 통과 (형제 픽스처 포함) · 회귀 jest 는 기준 트리에서 기준선과 실패 · 보류 이름 집합이 같다 (차이 0). tsc · eslint · prettier 는 이번 마감에서 다시 돌리지 않았다 (코드 변경 0).
5. 코드 리뷰: 구현 중 리뷰 R1 · R2 를 흡수했다 (본문 「코드 리뷰 R1」 · 「R2」 표). 흡수 시험 두 파일 · 변이 반사실 9 종 · 게이트 #11 · #20 · #21 · #23 수정.
6. 최종 리뷰 라운드: [{"r":1,"found":7,"absorbed":6,"high_absorbed":0,"high_open":0,"codex":"ok"}]. 흡수 6 (부분 2) · 기각 1 (검사 #24 의 1974 상수 · 근거는 「최종 리뷰」 표). 최종 리뷰의 HIGH 는 0 이다. 다만 구현 리뷰 (R3) 의 HIGH 는 백로그에 열려 있다 (행 2023 · 2032 · 2034 · 2050 · 2061).
7. 사후 정정: 소비자 흐름 9 중 정상 완료는 5 개 (발급 토큰 · 공개 발급 · 앱 갱신 · 머니 취소 · 쿠콘 콜백) 다. 머니 승인은 가드 도달 1, 스케줄러 두 흐름은 발송 경로 미도달 2, MOK 는 미증명 1 이다. 「신호까지 간 8 흐름」 은 이 정정으로 대체한다 (행 2070).
8. 사용자 지시 확인: 세션 기록 원문 (2026-10-19T02:21:22Z · type=user · userType=external) 을 `verify-user-directive.sh` 로 확인했다 (exit 0). 라운드 상한 3 소진과 skip-receipt 는 이 지시에 따른다.
9. 행 2037 (원 1974): PARTIAL 을 유지한다. 검사 #24 의 expect_status 가 1974 를 PARTIAL 로 고정한다. 목록 신원 집합이 고정되지 않아 RESOLVED-IN 은 증명이 아니다 (행 2061).

「PRD 미달」 (격리로 할 수 있는데 하지 않은 것 · 백로그 HIGH · 보완 계획 admin-rbac-m9eb):

판정 기준: PRD 증명 문언 (다른 기업 음성 · 정상 회귀 · 소비자 대응표) 을 덮는 범위의 미측정은 미달로 센다. 거부가 어느 규칙에서 왔는지 (귀속) 는 증명 깊이라 백로그에 두고 미달로 세지 않는다.

1. 머니 승인 정상 완료 미증명: 행 2071 (원 1985 · 행 2055)
2. 스케줄러 두 흐름의 발송 경로 미도달: 행 2072 (행 2046 · 2070)
3. 목록형 · AO 행의 기대 신원 집합 미고정: 행 2073 (원 1974 · 2010 · 행 2032 · 2034 · 2050 · 2061)
4. 목록 장면 7 개의 조직 범위 시험 없음: 행 2074 (행 2065 · 2047)
5. AO 교차 쓰기의 신원 · 대상 행 범위: 행 2075 (원 2010 · 행 2054 · 2056)

미달 아님 (깊이, 백로그 유지): 교차 · 형제 셀 84 의 규칙 귀속 (행 2068 · 2057 · 원 2008 · 원 1984), AO 403 14 셀의 귀속 (행 2051), 목록 소유 대조의 조직 누출 한계 (행 2047).

「(미검증) · 사용자 몫」:
- 운영 동일 환경 (복제본) 에서 1회 재생과 실패 0 건 확인 (복제본 생성은 사람 승인).
- 활성 스위치 (영역 상수 3 · 범위 상수 1 · 메뉴 스위치) 의 ON 커밋 결정. 커밋 상태는 전부 OFF 다.
- would-deny 판단 표의 운영본 실내용 (영역별).
- R2 (#96 최고관리자 한정 이식) · R7 (ath_id NULL 내부 관리자 0) · R19 (소비자 책임자) 확인.
- 스위치 OFF 상태의 영업 교차 노출 28 셀 (백로그 1986 PARTIAL) 의 수용 여부.
- MOK 흐름 (외부 서명 키). 소비자 1 흐름 미증명으로 남는다.
- 치환할 수 없는 외부 엔드포인트 (FCM 등) 의 송신 증명, 발송 잔류 결함 (행 2046) 의 담당 판정.
- 동시 판정 상한 N 과 풀 대기열 정책의 부하 실측 (백로그 1902 PARTIAL).

「다음 마일스톤이 알아야 할 것」:
1. 스위치는 전부 OFF 다 (영역 상수 3 · 범위 상수 1 · 메뉴 스위치). M10 은 OFF 상태를 전제한다.
2. 소비자 판정: 가드 도달 · 대상 0 명 · 예외 신호는 정상 완료로 세지 않는다. 검사 #16 의 예외 PASS 는 행 2072 에서 바뀐다.
3. 측정 트리 정의는 `_m9e-lib.sh` 의 `m9e_src_tree` 한 곳이다. 커밋 뒤 재생에 HEAD^{tree} 를 쓰면 검사 #23 이 붉다.
4. 응답 본문 · 항목 키 · 토큰은 커밋하지 않는다 (검사 #11). 측정 산출은 `.claude/meta/2026-10-26-admin-rbac-m9e/` 에 있다.
5. 변이 스크립트의 복원 trap 은 행 2066 · 2067 에서 해소됐다. SIGKILL 은 trap 으로 잡지 못하므로 변이 전 추적 소스 청결 확인은 손으로 한다.
6. receipt `.claude/receipts/mccp-implement-codex/admin-rbac-m9e.json` 은 부모 저장소에 이미 추적된다 (design 신호 0 이라 restamp 없음).

## 착수 기록 (계획 시점 · 구현 시작 때 다시 찍는다)

| 저장소 | 브랜치 | 착수 sha (계획 시점) | 워킹트리 |
|---|---|---|---|
| 부모 | feat/admin-rbac | `906ce63` (계획 커밋 직전) | 깨끗 |
| insuniverse-server | feat/admin-rbac | `3418d3a0` | 깨끗 |
| insuniverse-admin | feat/admin-rbac | `1301b664` | 깨끗 |
| insuniverse-site | feat/admin-rbac | `fc8872a1` | 깨끗 |

- 서버 선행 병합 조상 (검사 #2): `ea3c0359` · `a3016d35` · `aff7ffcc` · `34a0464d` · `ccf1594b`.
- 기준 백로그: 부모 `906ce63` 의 `.claude/plans/codex-findings-backlog.md` (검사 #6 · #7 · #9 가 세는 기준).

## 계획 시점 판정 기록

- 사이드카 검사 #1–#14 는 계획 커밋 뒤 한 번 돌린다 (계획 시점 녹색이 기대다). 검사 #15–#27 은 구현 산출을 요구하므로 계획 시점에 붉다. 그 붉음이 정상이다.
- 계획 커밋은 계획서 · 사이드카 · 이 노트 · 백로그 짝 행 · 원장 구획을 싣는다. 원장은 gitignore 라 커밋되지 않는다.
- R1 (divergent, L2 blocking 8 · L3 HIGH 2) 흡수 뒤 R2: 층 판정을 반사실로 바꾸고 (범위 판정 결정 줄 PREDICATE) 컨트롤러 우회 패치를 뺐다. 근거는 계획서 「잠정 채택 업무 결정」 의 「교차 · 형제 셀의 ON 층」 행과 「리뷰 라운드 기록」 R1 행. 전역 가드는 관리자 판정이 없는 요청을 `insuniverse-server/apps/api/src/modules/auth/access-control/access-control.guard.ts:260` 에서 통과시키고, 교차 거부는 컨트롤러 `insuniverse-server/apps/api/src/modules/money/money-admin.controller.ts:587` 와 서비스에서 나온다 (실측 확인: 줄 번호와 내용).
- 상한 처리: 게이트 라운드 상한은 3 이다. 마지막 라운드는 CRITICAL 이상이 아니면 skip-receipt 로 진행한다 (UI6). 확인은 `verify-user-directive.sh` 가 exit 0 이다 (검사 #12). 이 확인 결과 이외의 근거로 skip-receipt 를 쓰지 않는다.
- 백로그 HIGH 표지 줄은 계획 커밋에서 처리 표 (계획서 「이월 지적 반영」) 로 전부 덮었다. 짝 행은 「계획 반영 (구현 전)」 으로 적었고, 구현 뒤 판정 행을 따로 덧붙인다 (검사 #24).


## 계획 게이트 (상한 처리 · 2026-10-26)

- 판정: 라운드 상한 3/3 에서 divergent 로 끝났다 (`verdict=divergent source=multi-agent`, `HALT_STAGE=5.2e`). L2 정족수 미달이다 (응답 4 · 필요 3, 차단 지적은 test 렌즈의 HIGH 1 과 FAIL 1). L3 는 divergent (needs-attention, classification=ok).
- 라운드 기록: R1 · R2 는 계획서 「리뷰 라운드 기록」 표가 정본이다. 아래 표는 마지막 라운드 (R3) 의 지적만 싣는다.
- 동결: 계획서 · `l2.json` · L3 산출은 고치지 않았다. `post-a` 는 `l2.json` 의 형식만 확인했다. 이 절의 severity 대조는 파일 원문을 읽어 했다.
- CRITICAL 유무: 없음. 지적 11건 (HIGH 2 · FAIL 1 · MEDIUM 5 · LOW 3) 은 전부 CRITICAL 미만이다.
- 처리: receipt 를 쓰지 않았다. `MCCP_SKIP_RECEIPT=1` 은 `preflight` 한 명령에만 붙였고 결과는 `ok · bypassed · reason=MCCP_SKIP_RECEIPT` (exit 0) 다. `MCCP_SKIP_INTENT_GATE` 는 쓰지 않았다.
- 근거 지시 (원문, `verify-user-directive.sh` exit 0 · 세션 기록 2026-10-19T02:21:22.590Z · type=user · userType=external): 「Round 는 최대 3까지만 진행하고 마지막 라운드는 CRITICAL 이상 건이 아닌이상 skip-receipt로 진행해줘. (환경 변수도 적용). HIGH건은 backlog에 기록해서 다음 plan 작성에 반영하는 방법으로 진행.」
- backlog 이월: `.claude/plans/codex-findings-backlog.md` 의 2026-10-26 행 11줄 (R3 지적 전부). HIGH 2건은 사유 끝에 `→ 다음 계획 반영 대상` 을 붙였다. 하나는 검사 #18 의 범위 층 증명, 다른 하나는 검사 #20 의 목록 · AO 신원 집합이다.
- 미해소 주의: 위 HIGH 두 건이 가리키는 RESOLVED-IN 행 (1974 · 1984 · 2008 · 2010) 은 계획 게이트가 닫지 못했다. 구현 뒤 판정에서 검사 #18 · #20 의 판정을 다시 본다. 이 절은 그 행을 고치지 않았다.

| 라운드 | 리뷰어 | 심각도 | 지적 | 판정 | 증거 | 층위 |
|---|---|---|---|---|---|---|
| R3 | L2 test | HIGH | 검사 #18 은 같은 하네스 결정의 두 필드를 비교해 범위 층 증명을 못 잡는다 (2008 · 1984 RESOLVED-IN 을 좌우) | 이월 (다음 계획 반영) | 계획서 :139 · 검사 :378-380 | 5 |
| R3 | L2 test | FAIL | reviewer 판정 fail (위 HIGH 의 집계 표현) | 이월 | `l2.json` (test 항목) | — |
| R3 | L2 test | MEDIUM | ON 403 셀이 ON 빌드에서 측정됐다는 독립 증거가 없다 (ON_PATCH_SHA 일치뿐) | 이월 | 계획서 :82 | 2 |
| R3 | L2 test | MEDIUM | #16 흐름 완료 신호가 하네스가 적는 어휘뿐이다 | 이월 | 계획서 :76 · 검사 :320-325 | 6 |
| R3 | L2 invariant | MEDIUM | expect_status 가 #16 을 보지 않아 UNPROVEN 이 없으면 RESOLVED-IN 이다 | 이월 | 검사 :515-517 | 2 |
| R3 | L2 invariant | MEDIUM | #18 이 PREDICATE 줄이 실제 scope 결정 줄인지 확인하지 않는다 | 이월 | 검사 :370-380 | 6 |
| R3 | L2 invariant | LOW | SRV_WORKTREE_CLEAN=yes 는 하네스가 쓰는 상수다 | 이월 | 검사 :493 | 6 |
| R3 | L2 architect | LOW | 계획의 fc-agency 인용 (:163) 은 형식 검사 줄이고, 교차 결정은 countMembersInSalesScope 다 | 이월 | 계획서 :73 | 1 |
| R3 | L2 security | LOW | #16 loopback 정규식에 끝 앵커가 없다 (userinfo `@` 주소 통과) | 이월 | 검사 :298 | 5 |
| R3 | L3 codex | HIGH | #20 은 목록 · AO 의 기대 신원 집합을 고정하지 않고 행 수만 본다 (1974 · 2010 RESOLVED-IN 오판 가능) | 이월 (다음 계획 반영) | 검사 :427-435 | 6 |
| R3 | L3 codex | MEDIUM | #23 은 RUN_ID 존재만 보아 실행 간 증거 혼합이 통과한다 | 이월 | 검사 :487-495 | 6 |

「검사」 는 `.claude/PRPs/plans/admin-rbac-m9e.gates.sh`, 「계획서」 는 `.claude/PRPs/plans/admin-rbac-m9e.plan.md` 의 줄 번호다.

## 측정 원칙 (구현 단계가 따른다)

- 10분을 넘는 구간 (재생 · 소비자 회귀 · 앱 부팅 반복) 은 구현 에이전트가 돌리지 않는다. 스모크 (1–2분) 만 돌린다.
- 메인 실행 요청은 이 노트의 「메인 실행 요청」 절에 적는다. 그 뒤 implement 결과는 halted (사유: 메인 실행 대기) 다.
- 원자료는 실행마다 새 `.part` 에 쓰고 검사 후 `mv -n` 으로 정본 이름에 옮긴다. 기존 정본을 덮지 않는다.
- 측정 빌드는 상수 4개만 켠다 (m9d 와 같다). 컨트롤러 우회는 넣지 않는다 (R1 L3 HIGH).
- 임시 ON 빌드의 상수 파일은 백업에서 cmp 로 되돌리고, 복구 sha 를 `on-build-m9e.txt` 에 쓴다.

## 사용자 몫 (이 마일스톤이 하지 않는 것)

- 운영 동일 환경 (복제본) 1회 재생과 실패 0건 확인. 복제본 생성은 사람 승인이다 (UI7).
- would-deny 판단 표 실내용 (영역 1 · 2 · 3).
- R2 · R7 · R19 확인.
- 활성 스위치 (영역 상수 3개 · 범위 상수 · 메뉴 스위치) 의 켜기 결정.
- 스위치 OFF 상태의 영업 교차 노출 28 셀 (백로그 1986) 의 수용 여부.
- 동시 판정 상한 · 대기열 정책과 부하 실측 (백로그 1894 · 1902).
- 치환할 수 없는 외부 엔드포인트 (FCM 등) 의 송신 증명.

## M9 ON 체크리스트 (노트에만 남긴다)

- DDL 두 축 적용 · 적재 완료 · `ath_id` NULL 0 · would-deny 판단 표 (운영본) · R2 · R5 · R19 · M7 번들 배포 확인 · R16 방어 · 복제 1회 재생 · 백로그 1986 수용 · 백로그 1805 (PG 취소 조정) · 단건 3의미 배선 상태 재확인 (백로그 1775 · 1784 의 철회 이력).

## 착수 기록 (구현 시작 · 2026-10-26)

- 서브모듈 HEAD 재확인: server `3418d3a0b6d1f91bc890d2225527088af3faf49c` · admin `1301b664cbfb906a9ddbc00ea11e39414d0d7761` · site `fc8872a14f26224ca754a0aa303ab58d4b0d515f`. 세 저장소 워킹트리 깨끗. 검사 #1 · #2 녹색.
- 구현 시작 시점 검사 상태: #1–#14 PASS · #15–#27 FAIL (구현 뒤 산출 없음). 계획 시점 기대와 같다.
- 격리 DB 스키마 덤프 (저장소 밖 디렉터리, 읽기만 · 경로는 커밋 산출에 쓰지 않는다).
- 커밋 금지 지시 (이 실행 덧붙임): 부모와 서브모듈 모두 커밋하지 않는다. 구현 뒤 별도 세션에서 /mccp:code-review 를 돈다.
- Phase 2 의 `git pull --rebase` 는 돌리지 않는다 (원격 브랜치 없음).

## Codex Implementation Review

- 호출: `codex-invoke.js adversarial-review` (fail-closed wrapper), focus 는 서브모듈 경로와 읽을 파일을 적었다. 실행 로그: `$(git rev-parse --git-dir)/mccp/tmp/codex-m9e-stdout.json`.
- 라운드 수: 1 (상한 3). 결과 `ok=true · class=ok · blocking=false · verdict=needs-attention` → `CODEX_VERDICT=divergent` (구조화 판정).
- 합치 결론: 지적 1건 (HIGH). 계획 게이트 R3 L3 HIGH 와 같은 결함의 재확인이다 (#20 이 기대 신원 집합을 고정하지 않는다).
- YAGNI Triage:
  | Finding | Severity | Verdict | Why |
  |---|---|---|---|
  | F1 #20 신원 집합 미고정 (목록 1신원 · AO 교차 1셀만 있어도 통과) | HIGH | DEFER_TO_BACKLOG | 동결 계획서 · 게이트 본문은 구현 중 바꾸지 않는다 (지시: 구현은 동결 계획서대로, HIGH 재현은 구현 편차에 적는다). 백로그 `→ 다음 계획 반영 대상` 행으로 넘김. 1974 · 2010 의 RESOLVED-IN 은 이 결함 때문에 증거가 약하다 |
- R2 승격 조건 (CRITICAL 또는 HIGH · ACCEPT_NOW · 흡수 불가) 해당 없음 → R2 를 돌리지 않는다.
- Deferred to backlog: 1건 (HIGH) → `.claude/plans/codex-findings-backlog.md` 의 2026-10-26 행 (아래 구현 편차 · 백로그 절).
- Open Questions: 없음 (auto-CRITICAL 카탈로그 해당 없음).
- Security Reviewer: Task 도구가 이 실행에 없다 (`security-reviewer` 호출 불가). `> security-reviewer unavailable, skipped (auto-fallback): Task 도구 없음 (implement 서브에이전트)`. 인증 · 비밀을 다루는 측정 하네스가 있어 이 빈칸은 리뷰 몫이다. receipt 에 `security_skipped` 를 싣는다 (pr 게이트에서 차단 사유가 된다).
- impeccable: 스킬은 있다 (`impeccable:impeccable`, plugin 4.7.1). 설계 신호 0 (`reason=no-signal`, 변경 대상에 .tsx/.css/.html 없음) → `silent_skip` 기록 (`--impeccable-silent-skip --impeccable-silent-skip-reason no-signal`). 서브모듈 UI 를 건드리지 않으므로 신호 0 이 정확하다.
- Codex session 참조: 위 stdout JSON 의 threadId (비밀 아님, 로그 파일에만 둔다).

## 구현 편차 (동결 계획서 대비)

1. **Implement-Codex 리뷰 R1 (divergent, HIGH 1)**: 계획 게이트 R3 L3 HIGH 와 같은 결함 (#20 이 목록 · AO 의 기대 신원 집합을 고정하지 않는다) 이 구현 단계에서도 재현됐다. 게이트 본문 (`.claude/PRPs/plans/admin-rbac-m9e.gates.sh`) 은 구현 중 고치지 않았다 (지시: 구현은 동결 계획서대로). 백로그 2026-10-26 HIGH 행 (`→ 다음 계획 반영 대상`) 으로 넘겼다. 이 때문에 1974 · 2010 의 RESOLVED-IN 판정은 #20 이 녹색이어도 증거가 약하다. 판정 행을 쓸 때 이 사실을 증거 열에 함께 적는다.
2. **Implement-Codex 리뷰 R2 를 돌리지 않았다**: 승격 조건 (CRITICAL 또는 HIGH · ACCEPT_NOW · 흡수 불가) 에 맞지 않는다 (DEFER_TO_BACKLOG).
3. **security-reviewer 를 호출하지 않았다**: Task 도구가 이 실행에 없다. 빈칸으로 두고 receipt 에 `security_skipped=true` 를 실었다 (pr 게이트에서 차단 사유). 측정 하네스가 토큰 · 비밀 취급을 하므로 보안 리뷰는 별도 몫이다.
4. **skip-receipt 적용**: implement-codex receipt 쓰기는 env 없이 성공했다 (rc 0). 검증 (`receipt validate`) 은 plan 게이트 receipt 부재로 `missing=mccp-plan-codex` 를 냈다. 사용자 지시 원문 확인 (`verify-user-directive.sh`) 은 exit 0. 같은 명령에만 `MCCP_SKIP_RECEIPT=1` 을 붙인 `receipt preflight` 가 `ok · bypassed · reason=MCCP_SKIP_RECEIPT` (exit 0) 를 냈다. `MCCP_SKIP_INTENT_GATE` 는 쓰지 않았다. 이 receipt 의 `--skip-reason` 은 쓰지 않았다 (write 가 거부되지 않았다).
5. **상태 파일 escalation**: receipt 쓰기가 `.claude/state/fix-task.md` 에 `verdict: codex_divergent` · `escalate: true` 를 남겼다 (`divergent_unresolved`). 이 상태는 다음 턴의 정지 조건이 될 수 있다. 메인 세션이 판단한다 (지시: divergent 는 백로그로 넘긴다).
6. **impeccable**: 스킬 있음 · 설계 신호 0 (`no-signal`) → silent_skip 기록. 라우팅 · 비평 루프 · 3.6 · 3.7 은 신호 0 이라 열리지 않는다.
7. **하네스 미완 (계획 대비 구현 누락)**:
   - T2 소비자 회귀: `consumer-regress-m9e.mjs` 는 m9d 사본이다. 흐름별 시드 (refresh 토큰 · 만기 행 · 콜백 입력 · 공개 흐름 토큰 전달) · 신호 어휘 (`signal=`) · `UNPROVEN` 줄 · 외부 주소 루프백 목록 (`$RUNDIR/external-urls.txt`) 은 아직 없다. 즉 검사 #16 은 붉다.
   - T3 교차 · 형제 셀: `replay-m9e.mjs` 는 m9d 셀 형식 (11열 계열) 이다. 13열 · 층 열 · 반사실 (`PREDICATE` · `FLIPPED`) · ON 빌드 기록 (`on-build-m9e.txt`) 은 아직 없다.
   - T5 목록형 · AO: `owner-check-m9e.mjs` 의 모듈 · `--selftest` 는 있다. 응답 항목 원자료 (`owner-items-m9e.tsv`) 와 응답 길이 표를 만드는 재생 쪽 기록, AO 교차 셀 (`AOX`) 은 없다.
   - T4 I9 재측정: `replay-m9e.mjs` 가 `measure-m9e.mjs` 를 쓰도록 바뀌었다. I9 셀만 재는 필터 · `replay-i9-m9e.tsv` 출력은 아직 없다.
   - T6 형제 픽스처 itspec · jest: `itspec-run-m9e.sh` · `jest-rerun-m9e.sh` 는 m9d 사본이다. 실행하지 않았다. 서버 시험 파일 기대 수정 (조건부 세 파일) 은 0건이다 (실행 전이라 판단할 수 없다).
   - T7 측정 원자료 바인딩: 원자료가 아직 없다. `measure-alias-m9e.txt` 만 바인딩 메타 (`SRV_TREE`·`SRV_TREE_END`·`SRV_WORKTREE_CLEAN`·`RUN_ID`) 를 가지고 생성됐다.
   - T8 구현 뒤 판정 행 · PRD 결정 로그 9e 절: 증거가 없어 쓰지 않았다 (검사 #24 · #26 은 붉다). 판정 행을 거짓으로 채우지 않는다.
8. **격리 스택 스모크**: `_m9e-lib.sh` 의 `m9e_stack_up` · `m9e_stack_down` 을 1회씩 돌렸다 (MySQL 8.0.42 · Redis 7, 합성 스냅샷 ~205KB, 형제 픽스처 포함). 정지 후 `m9e-iso-` 컨테이너 0. 서버 기동 · 측정은 하지 않았다 (10분 초과 · 스모크 범위 밖).
9. **스모크 중 발견한 결함 (고침)**: 복사 직후 `sed` 가 meta 경로를 `2026-10-23-admin-rbac-m9e` 로 잘못 바꿔 형제 픽스처 적재가 깨졌다. `2026-10-26-admin-rbac-m9e` 로 고쳤다 (재스모크 통과). 검사 #11 이 노트의 호스트 경로 한 줄을 잡아 고쳤다 (홈 경로 문자열 제거).
10. **서버 기준 커밋**: 서버 · 어드민 · 사이트 워킹트리는 변경 없음. 구현 커밋 없음 (지시: 커밋하지 않는다).

## 검사 상태 (구현 종료 시점, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`)

- 계획 시점 #1–#14: 녹색 (#11 은 노트의 호스트 경로 제거 뒤 녹색).
- #15: 녹색 (대응표 집합 · 부류). #27: 녹색 (측정 스크립트 문법).
- #16–#26: 붉음. 원인은 위 편차 7 (측정 산출 미생성). #19 는 별칭 시험 부분만 맞고 I9 재측정 산출이 없다.
- 발견한 게이트 자체 결함은 편차 1 (#20 신원 집합) 과 계획 「리뷰 라운드 기록」 R3 의 HIGH · MEDIUM 들이다 (백로그).

## 메인 실행 요청

이 절은 구현 단계가 끝까지 못 간 상태에서 쓴다 (3차 구간, 2026-10-26). 모든 명령은 워크트리 루트에서 실행한다. `M9E_SCHEMA_DIR` 은 격리 스키마 덤프 디렉터리 (읽기만) 이고 값은 이 절에 적지 않는다. 10분을 넘을 수 있는 명령은 도구 백그라운드로 띄우고 완료 신호를 기다린다. 실행 전 확인: `git -C insuniverse-server status --porcelain` 이 비어 있고, `docker ps` 에 `m9e-iso-` 이름 컨테이너가 없다. 서버 dist 가 OFF 가 아니면 `replay-m9e.sh build` 로 HEAD 에서 다시 만든다.

1. OFF 재생: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off` → 끝 줄 `REPLAY_DONE mode=off run=<RUN> cells=<N>`. 산출 `raw-off-<RUN>.tsv`.
2. OFF 재실행 (결정성): 같은 환경으로 `replay-m9e.sh off-rerun` → `raw-off-rerun-<RUN>.tsv`.
3. ON 빌드 · 재생: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/on-area-build-m9e.sh` → 끝 줄 `ON_BUILD_DONE out=… run=<RUN>`. 산출 `raw-on-<RUN>.tsv` · `on-build-m9e.txt` (`ON_CONST_SHA256` · `RESTORED_SHA256`). 종료 때 상수를 HEAD 에서 되돌리고 OFF 로 다시 빌드한다.
4. 반사실 (교차 · 형제만): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/counterfactual-m9e.sh` → 끝 줄 `COUNTERFACTUAL_DONE out=…`. 산출 `raw-flip-<RUN>.tsv` · `counterfactual-m9e.txt` (`PREDICATE` 8 줄, `FLIP_COUNT` 는 1 이상이어야 한다, 검사 #18). 종료 때 편집한 파일을 HEAD 블롭에서 복구하고 cmp 로 확인한다.
5. 조립: `node .claude/meta/2026-10-26-admin-rbac-m9e/assemble-m9e.mjs .claude/meta/2026-10-26-admin-rbac-m9e .claude/meta/2026-10-26-admin-rbac-m9e/measure-m9e.mjs` → `ASSEMBLED cells=… flipped=… i9=… i9_excluded=…`. 산출 `replay-cells-m9e.tsv` (13열) · `replay-i9-m9e.tsv`. 실패 코드 4 (셀 집합 불일치) · 5 (OFF 재실행 결정성) · 6 (반사실 없음) · 7 (트리 불일치) 는 원인을 노트에 적고 멈춘다.
6. (아직 실행하지 않는다, 하네스 미완) 소비자 회귀 `consumer-regress-m9e.txt` 와 항목 소유자 대조 `owner-check-m9e.txt` 는 T2 · T5 가 끝난 뒤에 돈다. 그 전에는 산출이 없다.
7. 이미 있는 산출 (2차 구간, 바꾸지 않는다): `measure-alias-m9e.txt` · `itspec-m9e.json` · `jest-m9e.json` · `itspec-state-m9e.txt`.
8. 검사: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`. 1–5 가 끝나면 #17 · #18 · #19 · #23 의 셀 · 반사실 · I9 부분이 녹색이 되는지 본다. #16 · #20 은 6 이 끝나야 한다. #22 는 게이트 본문 결함 (2차 구간 기록) 때문에 붉다. #24 · #26 은 판정 행 · PRD 절이 쓰인 뒤 본다.

기대 산출 파일 (검사가 읽는 이름): consumer-regress-m9e.txt · replay-cells-m9e.tsv · replay-i9-m9e.tsv · counterfactual-m9e.txt · owner-check-m9e.txt · measure-alias-m9e.txt · itspec-m9e.json · jest-m9e.json. 원자료는 `raw-<모드>-<RUN>.tsv` 이름이고 런 목록은 `run-ledger-m9e.txt` 에 붙는다.

판정 주체 (사용자 몫): 복제 환경 1회 재생, would-deny 판단 표 실내용, R2 · R7 · R19, 스위치 ON, 백로그 1986 노출 수용, 동시 판정 상한 부하 실측.

## 재개 기록 (implement-resume · 2026-10-26)

메인 실행 요청의 1 · 2 는 메인 세션이 돌렸다. 이 절이 그 뒤의 상태다. 「메인 실행 요청」 절의 1 · 2 는 끝났다.

- 1 격리 itspec: 완료. `ITSPEC_DONE suites=12 tests=123 failed=0 pending=0`, 상태 `SIBLING_FIXTURE=present`, 산출 `itspec-m9e.json` · `itspec-state-m9e.txt`. 검사 #21 PASS.
- 2 회귀 jest: 재실행하지 않고 대조만 했다. 원본은 `jest-rerun-m9e.sh` 가 남긴 산출 (시작 2026-10-26T02:35:13Z · 서버 트리 `5e740146` · 청결). 기준선 `jest-rerun-m9e.sh` 의 BASE 를 m9d 기준선 (`2026-10-23-admin-rbac-m9d/jest-baseline-m9d.tsv`) 으로 고쳤다. 대조 전용 `jest-compare-m9e.sh <원본 JSON>` 을 새로 만들어 `jest-rerun-m9e.summary.txt` · `jest-rerun-m9e.names.tsv` 를 썼다. 결과: numTotalTests=2860 · numFailedTests=35 · numPendingTests=23 · numRuntimeErrorTestSuites=3. 기준선 F 38 행 · P 23 행과 이름 단위로 `DIFF_F=0 DIFF_P=0`. 새 실패는 없다.
- 검사 #22 는 붉다. 원인은 게이트 본문 (동결 정책으로 고치지 않았다) 의 두 결함이다.
  1. `numRuntimeErrorTestSuites === 0` 을 요구하는데, m9d 기준선이 이미 suite 수준 실패 3 건을 담는다 (`insight-report-v2.spec.ts` · `insight-payload.spec.ts` · `ddx-verdict.service.spec.ts`, 실패한 시험 0 건이고 suite 가 실패로 끝났다).
  2. c22 의 이름 집합은 assertion 실패만 세고 SUITE 행 3 개를 뺀다. 그래서 기준선과의 `comm -3` 차이가 3 줄이다.
  실행 오류 · 회귀는 아니다 (이름 집합은 m9d 와 같다). 다음 계획에서 #22 를 suite 수준 실패 집합 대조로 고쳐야 한다 (백로그 행 후보).
- 검사 현황 (구현 재개 시점, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`): PASS #1–#15 · #21 · #25 · #27. FAIL #16–#20 · #22 · #23 · #24 · #26 (측정 산출이 없거나 #22 결함).

### 아직 하지 않은 하네스 (메인 실행 전 필요)

- T2 소비자 회귀: 흐름별 시드 · 신호 어휘 · `UNPROVEN` 줄 · `$RUNDIR/external-urls.txt` 가 없다. `consumer-regress-m9e.sh` 는 m9d 사본이라 m9d 형식 줄을 쓴다. `on-area-build-m9e.sh` 가 이 스크립트를 부르므로 ON 측정 전에 고쳐야 한다.
- T3 교차 · 형제 셀: `replay-m9e.mjs` 는 7열 `CELL` 줄만 낸다. 13열 셀 (`replay-cells-m9e.tsv`) · 층 열 · 반사실 (`counterfactual-m9e.txt`, `PREDICATE` · `FLIPPED` · `FLIP_COUNT`) 은 없다. 반사실은 ON 빌드 위에서 범위 판정 결정 줄을 되돌리는 빌드가 한 번 더 필요하다.
- T4 I9 재측정 (`replay-i9-m9e.tsv`): 없다. 측정 함수 (`measure-m9e.mjs`) 와 별칭 시험 (`measure-alias-m9e.txt`, 검사 #19 의 시험 부분) 은 있다.
- T5 목록형 · AO 항목 원자료 (`$RUNDIR/owner-items-m9e.tsv`) · `owner-check-m9e.txt` · AO 교차 줄 (`AOX`): 없다. `owner-check-m9e.mjs --selftest` 만 있다.
- T7 측정 원자료 (`raw-off.tsv` · `raw-off-rerun.tsv` · `raw-on.tsv`): 없다. 검사 #23 이 이를 본다.
- T8 구현 뒤 판정 행 (검사 #24) · PRD 결정 로그 9e 절 (검사 #26): 증거가 없어 쓰지 않았다. 판정 행을 거짓으로 채우지 않는다.

「메인 실행 요청」 의 3 · 4 · 5 · 7 은 위 하네스가 끝난 뒤 implement-resume 이 정확한 명령 · 순서 · 기대 산출로 다시 적는다. 지금은 메인이 돌릴 측정 명령이 확정되지 않았다.

## 재개 기록 (implement-resume 3차 · 2026-10-26)

- 2차 구간의 「메인 실행 요청」 1 · 2 는 끝나 있다 (itspec · jest 산출). 이번 구간은 하네스의 나머지 중 T3 · T4 · T7 과 측정 순서를 맞췄다.
- 한 일:
  1. 경로 규약: 게이트가 읽는 중간 산출 (외부 주소 목록 · 수신기 기록 · 항목 원자료) 은 `m9e-run` (git dir 아래, `M9E_RUNDIR`). 측정 산출 · 원자료는 메타 디렉터리.
  2. 원자료: `raw-<모드>-<RUN>.tsv`, 모든 메타 줄은 `# KEY=VALUE`. `SRV_WORKTREE_CLEAN=yes` 는 항상 쓴다. ON · 반사실 빌드는 `WORKTREE_STATE` 로 구분한다 (허용된 변경만 있었다는 뜻).
  3. replay: `flip` 모드, 변형 필터 `M9E_VARIANTS`, 행 필터 `M9E_ROWS` (I9 전용 재생에 쓸 수 있다, 이번 계획은 전체 원자료에서 I9 행을 쓴다).
  4. ON 빌드: 편집 뒤 상수 sha (`ON_CONST_SHA256`) 를 기록하고 원자료가 같은 값을 싣는다 (검사 #23). 재생은 live 상수 sha 가 기록과 다르면 멈춘다. 소비자 회귀 호출은 ON 빌드에서 뺐다.
  5. 반사실: `counterfactual-m9e.sh` (ON 편집 · 반사실 줄 「&& false」 · 빌드 · flip 재생 · HEAD 블롭 복구 · cmp · OFF 재빌드) 와 `counterfactual-m9e.mjs` (apply · report). 줄 목록 `counterfactual-predicates-m9e.tsv` 는 scope 거부 (`permissionDenied('scope'`) 를 내는 `if (decision …)` · `if (count …)` 8 줄이다 (grep 으로 확인). 제외한 두 줄은 scope 거부를 내지 않는다 (organization-admin.controller.ts 98 · fc-agency.service.ts 147).
  6. 조립: `assemble-m9e.mjs` 가 13열 셀 표와 I9 재측정 표를 만든다. I9 표는 OFF · ON 모두 2xx 인 셀만 싣고 나머지는 `I9_EXCLUDED_NON2XX` 로 센다 (검사 #19 가 길이 숫자만 받으므로).
- 검증:
  - 합성 원자료 (저장소 밖 임시 디렉터리, 교차 2행) 에서 조립 · 검사 #17 · #18 · #19 통과 (rc 0). 셀 38, 열린 셀 1.
  - 반사실 편집: 임시 사본에서 8 줄 편집 · 재적용 거부 (rc 3) 확인.
  - 격리 스모크: OFF 재생 3셀 (`cells=3`, 200 · 200 · 200) 완주, 스택 정지 뒤 `m9e-iso-` 0. 스모크 원자료와 런 목록은 지웠다.
  - 셸 · 노드 문법: `.sh` 는 `bash -n`, `.mjs` 는 `node --check` 통과.
  - 게이트: #1–#15 · #21 · #25 · #27 녹색 (#11 포함). #16 · #17 · #18 · #19 · #20 · #22 · #23 · #24 · #26 붉음 (측정 산출 없음, #22 는 게이트 본문 결함).
- 미완 (하네스 T2 · T5, 다음 구간):
  - T2 소비자 회귀: 흐름 6개의 시드와 신호 어휘 · UNPROVEN 줄 · `external-urls.txt` · 수신기 흐름 기록 모두 없다. 진입점: admin-view 토큰은 `GET /admin-view-token?type=&id=` (`modules/admin-view-token/admin-view-token.controller.ts:43`, 응답 `{token}`, 기업 레벨 판정 있음). 앱 refresh 는 `POST /auth/refresh` (`modules/auth/user/auth.controller.ts:173`), 서비스는 `tnMember.memRefreshToken` 과 대조해 jti 후계자로 회전한다 (`modules/auth/user/auth.service.ts:631–721`). 신호는 행 변화 (memRefreshToken 교체) 로 본다. 콜백 · 스케줄러 · 가짜 수신기 기록은 미작성. FCM 은 치환 목록에 없으므로 UNPROVEN 후보다.
  - T5 항목 소유자: 목록 8행의 응답 항목 소유 키를 뽑는 쪽과 `owner-items-m9e.tsv` (RUNDIR), 응답 길이 표, AO 교차 쓰기 셀 (`AOX`) 이 없다. `owner-check-m9e.mjs --selftest` 만 있다.
- 작업 트리: 커밋 없음. 서버 워킹트리 청결 (측정 뒤 복구 cmp 확인). 임시 디렉터리 (저장소 밖 임시 디렉터리) 는 남아 있다. 정리 명령의 권한 검사가 막아서 지우지 않았다.

## 재개 기록 (implement-resume 4차 · 2026-10-26)

- 시작 상태 확인: 서버 · 어드민 · 사이트 워킹트리 청결, `m9e-iso-` 컨테이너 0. 사용자 지시 원문 확인 (`verify-user-directive.sh`) exit 0. 검사 현황은 3차 구간과 같았다 (#1–#15 · #21 · #25 · #27 녹색).
- 한 일 (T2 소비자 회귀 하네스):
  1. `consumer-regress-m9e.mjs` 를 새로 썼다. 흐름마다 REG 줄 하나를 내고, 증명이 안 된 흐름은 `RESULT=FAIL` 줄과 `UNPROVEN <흐름> needs=<사유 토큰>` 줄을 낸다. 부류별 신호는 관측으로만 정한다.
     - app: 회원 9301 의 `mem_refresh_token` 에 요청 토큰을 먼저 심고 (`UPDATE`, 시드 확인 뒤 요청), 응답 2xx 이고 DB 행이 바뀌었을 때만 `refresh-row-rotated`.
     - admin-view: 2xx 이고 응답에 token 문자열이 있을 때만 `token-issued`.
     - callback (money approve · cancel): 2xx 이고 응답 HTML 본문에 `window.location.href = '…'` 대입이 있을 때만 `redirect-target` (두 흐름의 실제 응답 형상: `money-public.controller.ts` 의 `res.send`).
     - scheduler: 잡 결과 `ok` 이고 잡 전후 가짜 수신기 요청 차이가 1 이상일 때만 `receiver-recorded`.
     - 나머지는 사유 토큰: `public-route-no-issuer` · `coocon-bearer-token-missing` (쿠콘 봉투는 로그인 Bearer 를 요구한다) · `mok-valid-result-payload` (mok-auth 의 302 는 실패 분기 redirect 다, 2xx 분기는 실제 MOK 결과 본문이 필요하다) · `scheduler-no-receiver-hit` · `scheduler-job-error`.
  2. `consumer-regress-m9e.sh`: 외부 주소 목록 `$RUNDIR/external-urls.txt` (전부 `http://127.0.0.1:<가짜 수신기 포트>`) 를 쓴다. 스케줄러 잡을 잡마다 따로 돌리고 (`consumer-sched-m9e.cjs` 에 흐름 이름 인자 추가), 잡 전후 `FAKE_HIT` 차이를 `$RUNDIR/fake-receiver.log` 의 `<흐름> <모드> hits=<n>` 줄로 남긴다. 두 모드가 다 있으면 `consumer-regress-m9e.txt` 를 합친다 (기존 동작).
  3. `on-area-build-m9e.sh`: ON 소비자 회귀 호출을 되살렸다 (`consumer-regress-m9e.sh on`). 복원 뒤에는 dist 가 ON 이 아니므로 ON 빌드 구간 안에서만 돌 수 있다 (m9d 의 같은 자리와 같다). 3차 구간의 「뺐다」 판단을 바꾼 것이다 (편차 기록 아래).
- 검증: `node --check` · `bash -n` 통과. 스모크 (격리 MySQL 만 기동, 서버 없음, 닫힌 포트 주소) 에서 시드 · 흐름 9개 · REG/UNPROVEN 줄 형식 · 스케줄러 판정이 돌았다. 컨테이너 정지 뒤 `m9e-iso-` 0. 이 스모크는 서버 기동 · 실제 2xx 를 보지 않았다. 흐름의 PASS 여부는 본 실행이 정한다.
- 게이트 (이 구간 끝): PASS #1–#15 · #21 · #25 · #27. FAIL #16–#20 · #22–#24 · #26 (측정 산출 없음, #22 는 게이트 본문 결함, #20 은 편차 1).
- 게이트 본문과 하네스가 어긋나 보이는 자리 (본 실행 전 판단 필요, 게이트는 고치지 않았다):
  1. 검사 #16 은 PASS 를 2xx 로만 받는다. 그래서 mok-auth (302) 와 쿠콘 (401) 은 하네스가 아무리 맞아도 PASS 가 안 된다. 이 둘은 UNPROVEN 으로 남는다.
  2. 검사 #16 의 `CONS_M9D_PROVEN` 은 money approve · cancel 이 ON 에서 2xx PASS 여야 한다고 요구한다. 본 실행이 HTML 본문의 redirect 대입을 보지 못하면 이 검사가 붉다 (「m9d 증명 흐름 회귀」). 그 경우는 하네스 결함이 아니라 흐름 응답 형상이 달라진 것이므로 근거를 적는다.
- 미완 (T5, 다음 구간): 목록형 8행 · AO 교차 셀의 항목 원자료 (`$RUNDIR/owner-items-m9e.tsv`) 와 응답 길이 표, `owner-check-m9e.txt` 를 만드는 재생 쪽 기록, AO 교차 줄 (`AOX`). `owner-check-m9e.mjs --selftest` 만 있다. 측정 전에 이 기록이 먼저 필요하다. 이 구간에서 만들지 않았다 (응답 항목 소유 키를 행마다 확인하는 작업이라 측정 없이 맞출 수 없다).

### 갱신된 메인 실행 순서 (4차 · 3 · 6 을 대체)

1–2, 4–5, 7–8 은 「메인 실행 요청」 절 그대로다. 달라진 것만 적는다.

- 3 (ON 빌드 · 재생 · 소비자 ON): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/on-area-build-m9e.sh`. 중간에 `consumer-regress-m9e.sh on` 이 돈다 (`consumer-raw-on.txt`). 끝 줄 `ON_BUILD_DONE`.
- 6 (소비자 OFF · 집계): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/consumer-regress-m9e.sh off` → 끝 줄 `CONSUMER_DONE mode=off reg=9 unproven=<미증명 흐름 수>`. 두 원자료가 다 있으면 `consumer-regress-m9e.txt` 가 생긴다 (검사 #16 의 입력).
- 6-1 (항목 소유자 대조, 미완): 하네스가 끝나기 전에는 돌리지 않는다. 산출이 없어 검사 #20 은 붉다.
- 기대 산출 추가: `consumer-raw-off.txt` · `consumer-raw-on.txt` · `$RUNDIR/external-urls.txt` · `$RUNDIR/fake-receiver.log`.
- 실행 전 확인은 3차 구간과 같다. 10분을 넘으면 도구 백그라운드로 띄우고 완료 신호를 기다린다. 소비자 한 모드는 격리 스택 기동과 서버 기동을 포함한다.

### 편차 (4차)

- 3차 구간이 ON 빌드에서 뺀 소비자 회귀 호출을 되살렸다. 이유: ON 소비자 결과는 ON dist 가 떠 있을 때만 얻을 수 있다. 복원 뒤에는 불가능하다.
- 소비자 OFF 본 실행은 아직 돌리지 않았다 (10분 안팎의 측정이다. 메인 실행 대기).
- 스모크 원자료와 임시 디렉터리는 저장소 밖에 두었고 지웠다.

## 재개 기록 (implement-resume 5차 · 2026-10-26)

4차 구간이 남긴 T5 (목록형 항목 소유 대조 · AO 교차 셀) 하네스를 만들었다. 측정 본 실행은 하지 않았다 (10분 초과, 메인 실행 대기).

- 새 파일 (메타 디렉터리):
  - `owner-measure-m9e.mjs`: 행 규칙 `OWNER_RULES` (I1 · I7 는 `agcId`, I4 · I5 · I6 · I9 목록은 `agcCorporateName`, I5 · I6 다운로드는 엑셀 「기업명」 열) 과 항목 소유 키 추출 `ownerKeysOf`. 본문은 메모리에서만 읽는다.
  - `owner-run-m9e.sh`: 두 원자료 (`raw-off-<RUN>` · `raw-on-<RUN>`) 의 항목 원자료를 합쳐 `owner-check-m9e.mjs` 를 돌리고 `owner-check-m9e.txt` 를 쓴다 (ON 원자료의 `# ` 머리 줄을 싣는다, 검사 #23). 종료코드는 owner-check 의 rc 를 그대로 낸다.
- 고친 파일:
  - `replay-m9e.mjs`: self 변형의 목록형 응답에서 항목별 소유 키를 `M9E_OWNER_OUT` · `M9E_OWNER_LEN_OUT` 에 쓴다 (RUNDIR, 커밋 밖). 목록 길이는 measureOf 의 len 과 항목 수가 같아야 한다 (다르면 stderr 의 `UNPROVEN owner … len-mismatch`). AO 교차 셀 (`aocross`) 은 ON 에서만 돌고 `M9E_AO_OUT` 에 쓴다. 대상 회원 (tn_member) 의 소유 기업을 쓰기 전 · 뒤로 읽는다. 기대 기업은 DB (`tn_agency`) 에서 읽는다 (시드 값을 리터럴로 박지 않는다).
  - `replay-m9e.sh`: OFF 라벨 (off · off-rerun) 과 ON 라벨 (on) 의 출력 파일을 만든다. ON 기본 변형에 `aocross` 를 더했다 (on-area-build 가 기본값으로 부르므로 ON 측정에 자동으로 붙는다). 끝 줄 `OWN_RUN mode=… run=… items=… len=… ao=…` 를 낸다.
  - `owner-check-m9e.mjs`: 셋째 입력 (AO 교차 TSV, 선택) 을 받아 `AOX` 줄과 `AO_CROSS_CELLS` 를 낸다. AO 위반 (상태 403 아님 · 소유 기업 바뀜 · 대상 없음 `-`) 은 exit 1. 목록 길이가 있는데 항목 원자료가 없는 묶음은 exit 3.
- 검증 (격리 MySQL · 닫힌 포트 아님 · 서버 dist OFF · 서버 워킹트리 청결, 실행 뒤 `m9e-iso-` 컨테이너 0, 워킹트리 청결):
  - `node --check` · `bash -n` 통과. `owner-check-m9e.mjs --selftest` → `SELFTEST orig=0 mutated=1 RESULT=PASS`.
  - 스모크 1 (I1 목록, self, 8 신원, OFF): 8 셀 모두 200. 항목 원자료 8줄, owner-check 로 대조: sales-A · sales-B 가 기업 9002 항목을 받는다 (OFF 에서 기업 범위가 없다는 뜻). 그 2 묶음에 foreign=1 씩.
  - 스모크 2 (I4 · I5 엑셀 · I6 엑셀 · I7 · I9, self, OFF): 40 셀. I4 · I6 엑셀 은 영업 신원만 200 이고 기업 · 조직 신원은 403 (E0607 permission). I7 은 영업 · 기업 신원이 200, 조직 신원이 403 이다. I5 엑셀 (wallet) 은 13 항목을 엑셀 「기업명」 열로 읽었다. I9 는 범위 신원 6 명 모두 403 이고 관리자 2 명만 200 이다.
  - 스모크 산출 (원자료 · 항목 · 길이 · 런 목록) 은 지웠다.
- 아직 검증하지 않은 것 (미검증, 메인 실행에서 확인):
  - I6 목록 (getPaymentList) 의 항목 규칙 `name` 은 스모크에서 돌리지 않았다 (엑셀만 돌렸다).
  - AO 교차 셀 (aocross) 은 ON 빌드에서만 돈다. 스모크하지 못했다. 첫 ON 원자료에서 `ao-cross-on-<RUN>.tsv` 줄 수와 코드 열을 먼저 본다.

### 5차에서 드러난 판정 사항 (메인이 판단한다 · 게이트 본문은 고치지 않았다)

1. **OFF 에서 타 소유 항목이 나온다.** 스모크에서 영업 신원 (sales-A · sales-B) 이 I1 · I5 엑셀 · I6 엑셀 · I7 에서 다른 기업의 항목을 받았다 (I4 는 두 영업 묶음 모두 foreign 1 씩이다). OFF 는 범위 강제가 꺼진 현행 동작이므로 이것은 누출의 증거이고, ON 에서 0 이어야 한다. 그런데 검사 #20 은 OFF · ON 을 합쳐 `FOREIGN_ITEMS=0` 을 요구한다. 그래서 OFF 누출이 있으면 #20 은 ON 이 옳아도 붉다. 계획 본문 (「타 소유 0」) 은 ON 에 대해서만 성립해야 한다는 해석이 맞다고 본다. 게이트 본문을 바꾸려면 계획 수정이 필요하므로 이 구간에서는 바꾸지 않았다. (근거: 스모크 2 의 `owner-items` 에서 sales 묶음의 `resp_owner ≠ seed_owner` 줄)
2. **I9 (fax) 는 범위 신원이 대상이 되지 않는다.** 범위 신원 6 명이 모두 403 (permission) 이다. 그래서 검사 #20 의 「목록 8 행 각각에 OFF · ON 의 OWNER 줄」 은 I9 에서 범위 신원으로는 채울 수 없다. 관리자 신원은 범위가 없어 기대 기업 집합이 전체이므로 소유 대조가 성립하지 않는다 (`owner-measure` 는 관리자를 제외했다). 이 행의 처리 (관리자 대조로 바꿀지, 행을 UNPROVEN 으로 둘지) 는 메인 판정이다.
3. **org-A1 · org-A2 의 소유 키는 기업 수준이다.** 목록 응답에 조직 경로가 없어서 조직 단위 누출은 이 대조로 보이지 않는다. 기업 단위만 본다 (한계, 노트에 명시).
4. **AO 교차 셀의 대상.** 대상 행이 기존 회원인 AO 11 행만 셀을 만든다 (AO3 · AO4 · AO5 · AO6 · AO7 · AO8 · AO9 · AO10 · AO11 · AO13 · AO14). 신원은 corp-A · org-A1 (기업 A 의 범위 신원), 대상은 기업 B 의 회원이다. 기대 셀 수는 최대 22 (ON 한 번). 교차 셀을 만들지 않은 AO 행과 이유: AO1 (업로드가 새 회원을 만든다, 기존 행 없음) · AO2 (생성, 기존 행 없음) · AO12 (지급 행을 기업에 만든다, 기존 회원 행이 아니다) · AO15 (대상이 주문 상세 9702, 소유는 주문 → 회원 → 기업 체인으로만 읽힌다. 이번 구간에서 만들지 않았다).

## 메인 실행 요청 (5차 정본 · 위 3차 · 4차 순서를 대체)

이 절이 측정 순서의 정본이다. 모든 명령은 워크트리 루트에서 실행한다. `<스키마 디렉터리>` 는 격리 스키마 덤프 디렉터리 (읽기만, 값은 이 절에 적지 않는다). 각 단계 뒤 끝 줄을 확인한다. 10분을 넘는 단계는 도구 백그라운드로 띄우고 완료 신호를 기다린다.

실행 전 확인: `git -C insuniverse-server status --porcelain` 이 비어 있고, `docker ps` 에 `m9e-iso-` 이름 컨테이너가 없고, `m9e_check_dist` 가 OFF 를 가리킨다 (`DIST_CONST_OK true=[없음]`). 다르면 `bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh build` 로 HEAD 에서 다시 만든다.

1. OFF 재생 (항목 소유 포함): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off`
   - 기대 끝 줄: `REPLAY_DONE mode=off run=<OFF_RUN> cells=<N> raw=…` 와 그 앞 줄 `OWN_RUN mode=off run=<OFF_RUN> …`. `OFF_RUN` 을 적어 둔다.
   - 산출: `raw-off-<OFF_RUN>.tsv` · RUNDIR 의 `owner-items-off-<OFF_RUN>.tsv` · `owner-len-off-<OFF_RUN>.tsv`. `run-ledger-m9e.txt` 에 `raw-off <OFF_RUN>` 한 줄.
   - stderr 에 `UNPROVEN owner …` 줄이 있으면 그 행 · 신원을 노트에 적는다 (그 묶음은 owner-check 에 들어가지 않는다).
2. OFF 재실행 (결정성 대조, owner-run 에는 쓰지 않는다): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off-rerun` → `REPLAY_DONE mode=off-rerun run=<RR>`. 산출 `raw-off-rerun-<RR>.tsv`.
3. ON 임시 빌드 · 재생 · 소비자 ON · AO 교차 셀: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/on-area-build-m9e.sh`
   - 기대 끝 줄: `ON_BUILD_DONE out=… changed=4 run=<ON_RUN>`. `ON_RUN` 을 적어 둔다 (`on-build-m9e.txt` 의 `RUN_ID_ON` 과 같다).
   - 산출: `raw-on-<ON_RUN>.tsv` · `on-build-m9e.txt` · RUNDIR 의 `owner-items-on-<ON_RUN>.tsv` · `owner-len-on-<ON_RUN>.tsv` · `ao-cross-on-<ON_RUN>.tsv` · `consumer-raw-on.txt`. 상수는 HEAD 로 원복되고 OFF 로 다시 빌드된다 (스크립트가 한다).
   - 실패 시 원복 확인: `git -C insuniverse-server status --porcelain` 이 비어야 한다.
4. 반사실: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/counterfactual-m9e.sh` → `COUNTERFACTUAL_DONE out=…`. 산출 `raw-flip-<RUN>.tsv` · `counterfactual-m9e.txt`. `FLIP_COUNT` 는 1 이상이어야 한다 (검사 #18).
5. 조립: `node .claude/meta/2026-10-26-admin-rbac-m9e/assemble-m9e.mjs .claude/meta/2026-10-26-admin-rbac-m9e .claude/meta/2026-10-26-admin-rbac-m9e/measure-m9e.mjs` → `ASSEMBLED cells=… flipped=… i9=… i9_excluded=…`. 산출 `replay-cells-m9e.tsv` · `replay-i9-m9e.tsv`. 실패 코드 4 · 5 · 6 · 7 은 원인을 이 절 아래에 적고 멈춘다.
6. 소비자 OFF: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/consumer-regress-m9e.sh off` → `CONSUMER_DONE mode=off reg=9 unproven=<n>`. 두 원자료가 다 있으면 `consumer-regress-m9e.txt` 가 생긴다 (검사 #16 입력).
7. 항목 소유 대조: `bash .claude/meta/2026-10-26-admin-rbac-m9e/owner-run-m9e.sh <OFF_RUN> <ON_RUN>` → `OWNER_CHECK_DONE rc=<rc> out=… items=<n>`. 산출 `owner-check-m9e.txt` (META) 와 RUNDIR 의 합친 입력 3개.
   - 기대: ON 묶음 `mode=ON` 의 `foreign=0`, `AOX` 줄 전부 `code=403` 이고 `rowowner_before=rowowner_after`, `AO_CROSS_CELLS` ≥ 1. OFF 묶음의 foreign 은 누출 증거다 (판정 사항 1). 이 파일은 파일을 쓴 뒤에도 rc 가 1 일 수 있다 — `OWNER_CHECK_RC=` 줄로 본다. 이미 있는 `owner-check-m9e.txt` 는 덮어쓰지 않는다 (있으면 지우고 다시 돌린다는 결정을 노트에 적은 뒤에만).
8. 검사: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`.
   - 1 · 2 · 3 · 4 · 5 · 6 · 7 이 끝나면 #16 (소비자) · #17 · #18 · #19 · #23 의 셀 · 반사실 · I9 부분이 녹색이 되는지 본다.
   - #20 은 판정 사항 1 · 2 때문에 OFF 누출과 I9 의 OWNER 줄 부재로 붉을 수 있다. 붉은 이유를 그대로 노트에 적고, 게이트를 고치지 말고 메인이 판단한다.
   - #22 는 게이트 본문 결함 (2차 구간 기록) 때문에 붉다. #24 · #26 은 판정 행 · PRD 절이 쓰인 뒤 본다.

기대 산출 파일 (검사가 읽는 이름): consumer-regress-m9e.txt · replay-cells-m9e.tsv · replay-i9-m9e.tsv · counterfactual-m9e.txt · owner-check-m9e.txt · measure-alias-m9e.txt · itspec-m9e.json · jest-m9e.json. 원자료는 `raw-<모드>-<RUN>.tsv` 이름이고 런 목록은 `run-ledger-m9e.txt` 에 붙는다.

판정 주체 (사용자 몫): 복제 환경 1회 재생, would-deny 판단 표 실내용, R2 · R7 · R19, 스위치 ON, 백로그 1986 노출 수용, 동시 판정 상한 부하 실측.

## 재개 기록 (implement-resume 6차 · 2026-10-26 · 최종 결과: halted)

- 시작 상태: 서버 · 어드민 · 사이트 워킹트리 청결. `m9e-iso-` 컨테이너 0. 사용자 지시 원문 확인 (`verify-user-directive.sh`) exit 0. 앞 구간의 측정 산출 (raw-off · raw-on · raw-flip · replay-cells · consumer-regress · owner-check) 은 아직 없다. 「메인 실행 요청 (5차 정본)」 의 1–7 이 돌지 않았다.
- 이 구간에서 한 일 (측정 본 실행은 하지 않았다, 10분 초과 · 메인 실행 대기):
  1. 하네스 문법: `.claude/meta/2026-10-26-admin-rbac-m9e/` 의 `.sh` 는 `bash -n`, `.mjs` · `.cjs` 는 `node --check` 전부 통과.
  2. 5차 정본의 끝 줄 문자열 (`REPLAY_DONE` · `OWN_RUN` · `ON_BUILD_DONE … changed=` · `COUNTERFACTUAL_DONE` · `CONSUMER_DONE` · `OWNER_CHECK_DONE` · `OWNER_CHECK_RC=` · `ASSEMBLED`) 과 인자 형식 (`replay-m9e.sh <모드>`, `assemble-m9e.mjs <메타 디렉터리> <measure-m9e.mjs>`, `owner-run-m9e.sh <OFF_RUN> <ON_RUN>`) 이 스크립트와 일치하는지 대조했다. 어긋난 곳은 없었다.
  3. 게이트 재실행 (`bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, rc 1, FAILS=9): PASS #1–#15 · #21 · #25 · #27. FAIL #16 · #17 · #18 · #19 · #20 · #23 · #24 · #26 은 측정 산출이 없어서 (「구현 뒤 산출」). #22 는 2차 구간 기록의 게이트 본문 결함 (suite 수준 실패 3 건 · c22 이름 집합 차이 3 줄).
- 아직 검증하지 않은 것 (미검증, 메인 실행에서 확인):
  - AO 교차 셀 (`aocross`) 은 ON 빌드에서만 돈다. 격리 스모크를 이 구간에서 돌리지 않았다. 첫 ON 원자료에서 `ao-cross-on-<ON_RUN>.tsv` 의 줄 수와 코드 열을 먼저 본다.
  - 소비자 회귀 mok-auth · 쿠콘 흐름의 PASS 가능성은 본 실행 전에는 알 수 없다 (4차 구간의 게이트 본문 판단 1 · 2 참조).
- 판정 사항 (5차 구간 절 · 메인 몫, 게이트 본문은 고치지 않았다): 판정 1 (OFF 타 소유 누출 vs #20 의 OFF·ON 합산 기대) · 판정 2 (I9 범위 신원 전원 403). 판정 3 (org 단위 누출은 기업 키로만 보인다) · 판정 4 (AO 교차 대상 11 행) 는 기록만 한다.
- 최종 결과: **halted (사유: 메인 실행 대기)**. 커밋 없음. 판정 행 (검사 #24) 과 PRD 9e 절 (검사 #26) 은 측정 증거가 없어 쓰지 않았다 (거짓 채움 금지). 측정이 끝나면 implement-resume 이 「메인 실행 요청 (5차 정본)」 의 7 까지 산출을 받아 판정 · 게이트 #1– · notes · 백로그 짝 행 · Implement-Codex · Phase 4·5 를 잇는다.
- 작업 트리: 변경은 메타 디렉터리 · notes · 백로그 · 산출 보고서 · receipt 까지다 (앞 구간 것). 서브모듈 3종 워킹트리 청결.

## 재개 기록 (implement-resume 7차 · 2026-10-26 · 최종 결과: halted)

- 시작 상태: 사용자 지시 원문 확인 (`verify-user-directive.sh`) exit 0. 「메인 실행 요청 (5차 정본)」 의 1–7 은 메인이 돌렸다 (OFF `20261026T121404-441b3215` · off-rerun `20261026T121404-c40af204` · ON `20261026T130545-d6d2feec` · flip `20261026T135130-6f41fb6e`). 서버 · 어드민 · 사이트 워킹트리 청결, `m9e-iso-` 컨테이너 0. 백그라운드 실행 없음.
- 메인이 고친 하네스 결함: `replay-m9e.sh` 의 flip 허용 목록을 줄바꿈 대신 공백으로 잇는다 (`tr '\n' ' '`, 주석 있음). 줄바꿈으로 잇던 때 case 매칭이 첫 파일부터 어긋났다.
- 셀 · 반사실 결과 (메인 산출, 재측정 없음): 교차 · 형제 셀 394 (OFF 두 번 전 셀 같음 · ON 1회). 반사실 범위 판정 줄 8 곳 (PREDICATE) 을 되돌려 scope 셀 14 개가 열렸다 (FLIP_COUNT=14). 나머지 교차 · 형제 셀 84 개는 층 other 로 남는다 (R1 정정: 교차 · 형제 셀은 98 개다. 범위 판정 밖 거부 지점은 미확정). 조립 `ASSEMBLED cells=394 flipped=14 i9=2 i9_excluded=6`.
- 이 구간에서 한 일:
  1. 검사 #22 (회귀 jest) 사이드카 결함 수정. 실패 집합을 `jest-compare-m9e.sh` 와 같은 규칙으로 센다 (suite 수준 실패 = `SUITE` 행, 시험 실패 = 시험 행, 경로 표기 `<srv>` 와 절대 경로 둘 다 처리). 기준선 F 행 (suite 3 건 포함) 과 이름 단위로 `LC_ALL=C sort` 대조. 「실행 오류 suite 0」 요구는 「실행 오류 suite 수 = suite 수준 실패 행 수」 로 바꿨다 (m9d 기준선의 suite 3 건을 설명된 것으로 인정). 음성 (임시 사본): 시험 하나를 실패로 바꾼 산출 → rc 1, 실행 오류 suite 하나 추가 → rc 1 (둘 다 관측). #22 PASS.
  2. 검사 #20 정의 변경 (메인 판정 1 · 2). 타 소유 0 은 ON 묶음에만 적용한다. OFF 의 타 소유 22 는 `OFF_FOREIGN` 으로 stderr 에 보고한다 (누출 증거). 행별 타 소유 합 = 요약 `FOREIGN_ITEMS` 를 확인한다. I9 행은 소유 대조 대신 범위 신원 6 명이 OFF · ON 모두 403 (E0607/permission) 인지 원자료 CELL 줄로 기계 대조한다 (메인 판정 2). 음성 (임시 META 사본, ON 묶음 행 하나 `foreign=1`) → rc 1, 「ON 묶음에 타 소유 항목이 있다」 로 관측.
  3. 검사 #22 · #20 의 수정은 게이트 본문 정의 변경이다. 계획 「잠정 채택 업무 결정」 의 게이트 정의와 다르면 리뷰가 되돌릴 수 있다 (메인 판정 1 · 2 를 따랐다).
  4. 백로그 구현 뒤 판정 행 11 개 (`2026-10-26 | PARTIAL|RESOLVED-IN | … | [원 N] 구현 뒤 판정 …`) 를 추가했다 (검사 #24 PASS). 상태는 `expect_status` 가 정한 값이다. 2011 만 RESOLVED-IN (검사 #21 · #22 녹색) 이고, 증거 열에 「조직 경로 목록 장면 시험 이름 없음」 을 적었다.
  5. PRD 「2026-10-26 · 관리자 RBAC 마일스톤 9e 보완」 절을 결정 로그에 더했다 (검사 #26 · #10 PASS). 행 9 는 문구 · 상태 열 그대로다 (in-progress).
  6. `consumer-regress-m9e.sh` 에 출처 바인딩 헤더를 더했다 (`# SRV_TREE` · `# SRV_TREE_END` · `# SRV_WORKTREE_CLEAN=yes` · `# RUN_ID` · ON 은 `# ON_PATCH_SHA256`, replay 와 같은 형식). ON 은 워킹트리 변경이 상수 파일 하나이고 상수 sha 가 on-build 기록과 같을 때만 돈다. `bash -n` 만 통과했다. 실행은 아직 하지 않았다 (다음 측정에서 처음 돈다).
- 검사 현황 (이 구간 끝, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, FAILS=3): PASS #1–#18 · #21 · #22 · #24 · #25 · #26 · #27. FAIL #19 · #20 · #23.
  - #19: I9 길이 비숫자 (아래 판정 A).
  - #20: AO 교차 쓰기 중 400 4 셀 (아래 판정 B).
  - #23: `consumer-regress-m9e.txt` 에 출처 바인딩 줄이 없다 (소비자 재측정 때 헤더가 생긴다, 아래 요청 3).
- 판정 사항 (메인 몫, 게이트 본문은 이 판정으로 고치지 않았다):
  - 판정 A (I9 길이): `measure-m9e.mjs` 의 목록 길이 규칙은 `keys === 'data,list'` 를 객체의 전 키로 본다. I9 응답은 `count` 가 같이 있어 (`o.count = 2`) 별칭 쌍이 인정되지 않고 길이가 「-」 다. 고치려면 (가) I9 전용 규칙 (배열 값 키만 본다) 으로 I9 행만 다시 재는 방법, (나) 측정 원천을 바꿔 전 셀을 다시 재는 방법이 있다. (나) 는 측정 원천 sha 가 바뀌어 이미 잰 원자료와 출처가 갈린다. 이 구간에서는 고치지 않았다.
  - 판정 B (AO 업로드 400): AO3 · AO4 · AO5 · AO14 의 업로드 셀 4 개가 400 이다. 측정 행은 응답 본문의 오류 코드를 기록하지 않는다. 메인 판정 (5) 의 전제 「파일 없이 보내 범위 판정 전에 끝났다」 는 코드와 맞지 않는다 (`replay-m9e.mjs` 의 `call()` 은 multipart 로 xlsx 를 붙인다, 같은 형식의 self 셀은 201 이다). 400 이 범위 판정 전 검증에서 왔는지 확인되지 않았다. AO3 기업 A (corp-A) 교차 셀도 AOX 목록에 없다 (원인 미확인).
  - 판정 C (소유 대조 한계): 조직관리자 소유 키는 기업 수준이다. 같은 기업 안의 다른 조직 목록 누출은 이 대조로 보이지 않는다. 형제 itspec 123 시험에 조직 수준 목록 필터를 덮는 시험 이름은 없다 (단건 조인 `조직관리자 신원 /SYNORGA1/ … SYNORGA10 … 거부` 와 머니 목록 기업 수준 시험만 있다). 이 한계를 (미검증) 으로 적는다.
  - 판정 D (AO15): 주문 → 회원 → 기업 체인 소유 대조는 새 셀 설계와 ON 재측정이 필요해 이 구간에서 만들지 않았다. (미검증, 사유: 체인 소유 대조 셀 설계 미완).
  - 판정 E (출처 규칙): 작업 트리의 `.claude/notes/admin-rbac-m9e.md` · `.claude/plans/codex-findings-backlog.md` 는 `git show HEAD:<f> | npx prettier … | cmp` 가 다르다. 두 파일의 변경은 이 마일스톤이 문서화한 편집이다 (구현 편차 · 백로그 2034 행 · 이번 구간 판정 행). 포맷 전용 변경이 아니고 출처 미상도 아니므로 멈추지 않았다. 커밋하지 않는다.
- 미완 (이 구간이 하지 않은 것):
  1. 소비자 9 흐름 (가) 핸들러 도달 증명 · (나) 정상 완료: 아직 개발이 안 됐다. ON 에서 공개 흐름 403 은 출처 미확인 (`needs=public-route-no-issuer`, 핸들러 고유 코드 기록 없음), 쿠콘 401 (로그인 Bearer 요구), MOK 302 (실패 분기 redirect), 스케줄러 두 잡은 수신 0 이다 (잡 본문 실행 로그 증거 없음). 정상 완료를 위한 시드 (쿠콘 bearer 격리 env 값 · 스케줄러 만기 행 · 공개 흐름 발급자) 는 하네스에 없다. MOK 결과 페이로드는 외부 서명 키가 필요해 (미검증) 로 둔다.
  2. 판정 A · B 의 재측정 (아래 요청).
  3. 검사 #19 · #20 · #23 녹색 만들기 (요청).
  4. Implement-Codex 리뷰 · Phase 4 · 5: 게이트가 rc 0 이 아니라 리뷰 대상 상태가 확정되지 않아 뒤로 미뤘다. Phase 5 보고서 `.claude/PRPs/reports/admin-rbac-m9e-report.md` 는 이전 구간 (halted) 의 내용 그대로다 (이번 구간에서 고치지 않았다, 에이전트 산출 규칙).
  5. receipt 3.6 · 3.7 restamp: 계획서 · 게이트가 바뀌지 않아 이번 구간에는 하지 않았다 (계획 해시는 그대로).
  6. 커밋 없음 (지시).

## 메인 실행 요청 (7차 · 마무리 구간, 10분 넘는 단계)

이 절이 측정 순서의 정본이다 (5차 정본 중 1–5 는 끝났고, 아래가 다음이다). 모든 명령은 워크트리 루트에서 실행한다. `<스키마 디렉터리>` 는 격리 스키마 덤프 디렉터리 (읽기만, 값은 이 절에 적지 않는다). 각 단계 뒤 끝 줄을 확인한다. 한 단계가 10분을 넘으면 도구 백그라운드로 띄우고 완료 신호를 기다린다.

실행 전 확인: `git -C insuniverse-server status --porcelain` 이 비어 있고, `docker ps` 에 `m9e-iso-` 이름 컨테이너가 없다.

1. (결정 필요, 판정 A) I9 길이 규칙. 권장 (가): `measure-m9e.mjs` 에 I9 전용 길이 규칙 (배열 값 키만 본다) 을 더하고 `run-alias-m9e.sh` 로 별칭 시험을 다시 돌린다 (1분 안). 그 뒤 I9 행만 OFF · ON 으로 다시 잰다: `M9E_ROWS='^I9/' M9E_VARIANTS='self' M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off` (끝 줄 `REPLAY_DONE mode=off run=<R>`). ON 은 `on-area-build-m9e.sh` 안에서 같은 필터로 돈다. 권장 (가) 는 I9 셀 수 (2 행 × 8 신원) 에만 쓰이고 나머지 셀 원자료를 건드리지 않는다. 판정 (나) 를 고르면 전 셀을 다시 재야 하고 (OFF 재생 원자료 시각 기준 약 50분 추정) 10분을 넘는다.
2. (판정 B) AO 업로드 400 의 원인 기록: `replay-m9e.mjs` 의 `aoCrossCell` 이 `AOX` 줄에 응답 본문의 오류 코드 (`E…/…` 형식) 를 더하도록 고친다 (측정 원천이 바뀌므로 1 과 같은 묶음으로 간다). 재측정 뒤 AO3 · AO4 · AO5 · AO14 의 400 본문 코드를 보고, 범위 판정 전 검증이면 「범위 거부 증명」 이 아니라 「업로드 형식 불일치」 로 판정 행을 고친다. AO3 corp-A 셀이 빠진 원인도 같이 확인한다.
3. 소비자 OFF · ON (출처 바인딩 포함, 검사 #23): ON 은 `on-area-build-m9e.sh` 가 부른다 (끝 줄 `ON_BUILD_DONE … changed=4 run=<ON_RUN>`). OFF 는 `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/consumer-regress-m9e.sh off` → `CONSUMER_DONE mode=off reg=9 unproven=<n>`. 이 원자료는 헤더가 있어야 검사 #23 이 녹색이 된다.
   - 주의: 소비자 흐름 (가) · (나) 개발은 이 요청에 없다. 개발은 구현 구간을 다시 열어 따로 한다. 지금 재측정하면 UNPROVEN 은 그대로 5 흐름이다.
4. 재생 ON · 반사실 · 조립 (1 · 2 가 끝난 뒤): 1 · 2 가 셀 원천을 바꾸면 OFF 재생 · ON 재생 · 반사실 · 조립 (`assemble-m9e.mjs`) 을 다시 돌려야 한다 (5차 정본 3–5 와 같은 명령). 원천을 I9 전용으로 제한하면 I9 행 재측정 산출만 갈아 끼운다 (`replay-i9-m9e.tsv`).
5. 항목 소유 대조: `bash .claude/meta/2026-10-26-admin-rbac-m9e/owner-run-m9e.sh <OFF_RUN> <ON_RUN>` → `OWNER_CHECK_DONE rc=<rc> out=… items=<n>`. 기대: ON 묶음 `foreign=0` (새 정의), AOX 줄 전부 `code=403` (400 이 남으면 판정 B 의 결과를 노트에 적고 멈춘다).
6. 검사: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`. 기대: #19 · #20 · #23 이 녹색이 되면 그다음 판정 행 (1974 · 2010 · 2009 · 2007 · 1966 · 1973 · 1985) 을 증거대로 고친다. 이 행들은 expect_status 가 정한다 (지금은 PARTIAL). 전부 녹색이어도 소비자 흐름 (가) · (나) 가 개발되지 않았으므로 1966 · 1973 · 1985 · 2007 은 PARTIAL 로 남는다.

기대 산출 파일 (검사가 읽는 이름): consumer-regress-m9e.txt · replay-cells-m9e.tsv · replay-i9-m9e.tsv · counterfactual-m9e.txt · owner-check-m9e.txt · measure-alias-m9e.txt · itspec-m9e.json · jest-m9e.json. 원자료는 `raw-<모드>-<RUN>.tsv` 이름이고 런 목록은 `run-ledger-m9e.txt` 에 붙는다.

판정 주체 (사용자 몫): 복제 환경 1회 재생, would-deny 판단 표 실내용, R2 · R7 · R19, 스위치 ON, 백로그 1986 노출 수용, 동시 판정 상한 부하 실측, 치환할 수 없는 외부 엔드포인트 (FCM 등) 의 송신 증명.

- 최종 결과: **halted (사유: 메인 실행 대기 — 판정 A · B 의 재측정, 소비자 흐름 (가) · (나) 개발)**. 커밋 없음. 게이트 FAILS=3 (#19 · #20 · #23). 검사 #22 · #24 · #26 은 이번 구간에서 녹색이 되었다.

## 재개 기록 (implement-resume 8차 · 2026-10-26 · 최종 결과: halted)

- 시작 상태: 사용자 지시 원문 확인 (`verify-user-directive.sh`) exit 0. 서버 워킹트리 청결 (HEAD 3418d3a0, 트리 5e740146). `m9e-iso-` 컨테이너 0. 백그라운드 실행 없음. 게이트 시작 상태는 7차 종료와 같다 (FAILS=3: #19 · #20 · #23).
- 한 일:
  1. 판정 A (I9 길이 규칙). `measure-m9e.mjs` 의 목록 길이 규칙을 「배열 값인 키」 기준으로 바꿨다 (`arrKeys` 가 정확히 list · data 이고 두 값이 같으면 한 번 센다). 객체에 count 같은 스칼라 키가 같이 있어도 된다 (I9 형상 `{count, data, list}`). 측정 원천 sha 는 a7eadd9d… 에서 4461140a… 로 바뀌었다.
     - `measure-alias-m9e.mjs` 에 네 번째 시험 `alias-with-count` (count 2 · list · data 같은 배열, 기대 2) 를 더했다. `run-alias-m9e.sh` 로 `measure-alias-m9e.txt` 를 새로 썼다 (run-alias 는 `mv -n` 이라 기존 정본을 덮지 않으므로 옛 파일은 `measure-alias-m9e.old-a7eadd9d.txt` 로 옮겼다). 네 시험 모두 RESULT=PASS. 음성 확인: `{count:2, list:[1,2], data:[1,2,3]}` 은 `-` (별칭 아님).
     - 검사 #19 는 아직 붉다: 「I9 재측정 산출이 측정 원천과 다르다」. `replay-i9-m9e.tsv` 는 옛 sha 의 산출이다. 측정 전이라 고치지 않았다.
     - 영향 범위 (미검증): 규칙 변경은 배열 키 형상이 list · data 인 객체에만 닿는다. 원자료의 2xx 「-」 길이 셀 중 같은 형상이 있으면 재측정 때 숫자로 바뀐다. 응답 본문을 보관하지 않으므로 현 원자료로는 확인할 수 없다. 재측정 때 OFF · ON 길이 열의 변화를 셀 단위로 대조한다.
  2. 판정 B (AO 업로드 400 원인 기록). `replay-m9e.mjs` 의 `aoCrossCell` 이 응답 오류 코드 (`call()` 의 `code`) 를 AO 원자료 일곱째 열 (`err`) 에 싣도록 바꿨다. `owner-check-m9e.mjs` 는 AOX 줄 끝에 `err=` 를 더한다 (`$5`–`$7` 파싱은 그대로). `owner-check-m9e.mjs --selftest` → `RESULT=PASS`. 기존 `ao-cross-on-20261026T130545-d6d2feec.tsv` 에는 err 열이 없다 (측정 전이라 재측정 전까지 `err=-` 로 읽힌다).
  3. 소스 대조 (측정 전 판단 재료, 실측 아님):
     - corp 신원 (corp-A): `fc-agency.service.ts` 의 `accountUpdateUpload` 가 corp 의 `dto.agcId` 를 신원 기업으로 덮어쓴다 (1245행). 이어서 `getFCListByLoginIds(dto.agcId, …)` (`agency-fc.repository.ts` 433행, `agcId` 일치 필터) 가 교차 대상 회원을 0 건으로 돌려주고 `NOT_FOUND_FC_IN_SUBSCRIBE` (E0815, 400) 를 던진다. 범위 판정 `assertMembersWritableByAdmin` 에는 도달하지 않는다. 예상: corp-A 의 AO3 · AO4 · AO5 교차 셀은 OFF · ON 모두 E0815 400 이며, 이 셀은 범위 거부를 증명하지 못한다. 재측정의 err 열로 확인한다.
     - org-A1 (corp 아님): `assertMembersWritableByAdmin` 은 corp 가 아니면 바로 반환한다 (138행). 즉 이 함수는 org-A1 의 교차 쓰기를 막지 않는다. 그런데 측정은 org-A1 에서도 400 이었다. 원인은 소스로 확정하지 못했다 (본문 agcId 와 신원 기업의 대조, 행 형식 검사, 연락처 · 이메일 중복 검사 등이 후보). err 열로 확인한다.
     - 메인 판정 (5) 의 전제 「범위 판정 전에 끝났다 → 유효 파일로 재측정하면 403」 은 corp 신원에 대해서는 성립하지 않는다 (본문 agcId 덮어쓰기). 업로드 AO 셀의 판정 행은 재측정 전까지 쓰지 않는다. 재측정 뒤 「범위 거부 증명」 이 아니라 「교차 도달 불가 (코드)」 또는 「업로드 형식 불일치 (코드)」 로 쓸지 메인이 판정한다. 게이트 #20 의 「AOX 전부 403」 요구는 corp 행에 대해 맞지 않을 수 있다 (메인 판정 대상).
  4. 쓰지 않은 것: 측정 재실행 (아래 요청), 게이트 본문 · 판정 행 · PRD 절 · 백로그 행 · 소비자 흐름 (가) · (나) 개발.
- 검사 현황 (이 구간 끝, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, FAILS=3): PASS 24 · FAIL #19 · #20 · #23. 7차 종료와 같다.
  - #19: 원천 sha 불일치 (재측정 산출 없음).
  - #20: OFF 타 소유 22 (판정 기록용) · AO 교차 셀 중 403 아닌 것 (업로드 4 셀, 위 3).
  - #23: `consumer-regress-m9e.txt` 에 출처 바인딩 줄이 없다 (소비자 OFF · ON 재측정 뒤 생긴다).
- 중단 사유 (10분 넘는 단계, 메인 실행 대기): 판정 A · B 의 재측정은 ON 빌드를 포함한다. 이전 실측으로 ON 빌드 · 재생 한 바퀴가 약 40분이다 (on-build 기록 13:05 → 13:48). 이 구간에서 ON 빌드 · 재생을 돌리지 않았다.

## 메인 실행 요청 (8차 · 판정 A · B 재측정, 정본)

이 절이 다음 측정의 정본이다 (7차 요청의 1 · 2 를 구체화하고 3–6 은 그대로 둔다). 모든 명령은 워크트리 루트에서 실행한다. `<스키마 디렉터리>` 는 격리 스키마 덤프 디렉터리 (읽기만, 값은 적지 않는다).

실행 전 확인: `git -C insuniverse-server status --porcelain` 이 비어 있고, `docker ps` 에 `m9e-iso-` 이름 컨테이너가 없다.

1. OFF 재생 (새 측정 원천, I9 포함, err 열 포함): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off` → 끝 줄 `REPLAY_DONE mode=off run=<OFF_RUN>`. 그 다음 `… replay-m9e.sh off-rerun` → `RR`. 두 번 합쳐 약 50분 (12:14 → 13:05 실측).
   - 주의: 이 두 줄은 셀 원천이 바뀌었으므로 같은 묶음으로 다시 돌린다 (7차 요청 4 · 5 와 같다). 기존 OFF 원자료는 근거로 남기되 조립에는 최신 런 (ledger 의 마지막 줄) 이 쓰인다.
2. ON 빌드 · ON 재생 · AO 교차 · 소비자 ON: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/on-area-build-m9e.sh` → 끝 줄 `ON_BUILD_DONE … changed=4 run=<ON_RUN>`. 약 40분. 끝나면 상수는 HEAD 로 원복되고 OFF 로 다시 빌드된다. 원복 확인: `git -C insuniverse-server status --porcelain` 이 비어야 한다.
3. 반사실 (ON 빌드 위, 범위 판정 줄 8곳): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/counterfactual-m9e.sh` → `COUNTERFACTUAL_DONE out=…`. `FLIP_COUNT` 는 1 이상 (검사 #18).
4. 조립: `node .claude/meta/2026-10-26-admin-rbac-m9e/assemble-m9e.mjs .claude/meta/2026-10-26-admin-rbac-m9e .claude/meta/2026-10-26-admin-rbac-m9e/measure-m9e.mjs` → `ASSEMBLED cells=… flipped=… i9=… i9_excluded=…`. 실패 코드 4 · 5 · 6 · 7 은 원인을 이 절 아래에 적고 멈춘다. 기대: `replay-i9-m9e.tsv` 의 I9 행 길이 열이 전부 숫자 (검사 #19 의 입력).
5. 소비자 OFF (출처 바인딩 포함, 검사 #23): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/consumer-regress-m9e.sh off` → `CONSUMER_DONE mode=off reg=9 unproven=<n>`. 두 모드 원자료가 다 있으면 `consumer-regress-m9e.txt` 가 다시 쓰인다 (검사 #16 · #23 의 입력). 소비자 흐름 (가) · (나) 개발은 여전히 없다 (UNPROVEN 은 5 흐름 근처로 남는다).
6. 항목 소유 대조 (AO 교차 err 열 포함): `bash .claude/meta/2026-10-26-admin-rbac-m9e/owner-run-m9e.sh <OFF_RUN> <ON_RUN>` → `OWNER_CHECK_DONE rc=<rc> out=… items=<n>`. 기대 (메인 판정 대상): ON 묶음 `foreign=0`; AOX 줄의 `err=` 를 보고 corp-A 행이 E0815 인지, org-A1 행의 400 코드가 무엇인지 노트 아래에 적는다. `owner-check-m9e.txt` 가 이미 있으므로 덮어쓰기 전에 「덮어쓴다」 결정을 노트에 한 줄 적는다.
7. 검사: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`. 기대: #19 는 I9 원천 sha 가 맞으면 녹색 (길이 숫자 여부 포함). #23 은 소비자 원자료 헤더가 맞으면 녹색. #20 은 AO 교차 403 요구 때문에 업로드 셀이 붉을 수 있다 (메인이 판정). 붉은 이유는 그대로 노트에 적고 게이트 본문은 고치지 않는다.

판정 주체 (사용자 몫 · 메인 몫): AO 업로드 셀의 판정 표현 (범위 거부 · 교차 도달 불가 · 형식 불일치 중 무엇으로 쓸지), 게이트 #20 의 AO 403 요구를 corp 행에 대해 어떻게 할지, 판정 2 · 3 · 4 (7차 판정 목록) 의 후속, 백로그 행 추가 (corp 본문 agcId 덮어쓰기는 재측정 뒤 HIGH 후보로 올린다), 복제 환경 1회 재생, would-deny 판단 표 실내용, R2 · R7 · R19, 스위치 ON, 백로그 1986 노출 수용, 동시 판정 상한 부하 실측, 치환할 수 없는 외부 엔드포인트 (FCM 등) 의 송신 증명.

- 최종 결과: **halted (사유: 메인 실행 대기 — 판정 A · B 의 ON 빌드 포함 재측정, 40분 단위)**. 커밋 없음. 판정 행 (검사 #24) · PRD 절 (검사 #26) · 백로그 행은 이 구간에서 건드리지 않았다. 재측정 뒤 「메인 실행 요청 (8차)」 의 7 까지 산출을 받아 이어간다.

## 재개 기록 (implement-resume 9차 · 2026-10-26 · 최종 결과: halted)

- 시작 상태: 사용자 지시 원문 확인 (`verify-user-directive.sh` exit 0 · 2026-10-19T02:21:22.590Z · type=user · userType=external). 서버 · 어드민 · 사이트 워킹트리 청결 (서버 `git status --porcelain` 빈 값). `m9e-iso-` 컨테이너 0. 게이트 시작 상태는 8차 종료와 같다 (FAILS=3: #19 · #20 · #23).
- 메인 판정 1)–6) 처리 상태 (이 구간의 판정 응답):

| 판정 | 이 구간의 처리 | 상태 |
|---|---|---|
| 1) #20 은 ON 에만 타 소유 0 · OFF 는 누출 증거 | 7차에 반영 · 이 구간 변경 없음. OFF `FOREIGN_ITEMS=22` (행별 합과 일치) | 반영됨 |
| 2) I9 범위 신원 전원 OFF · ON 403 기계 대조 | 7차에 반영 (#20 의 I9 분기). #20 이 붉은 사유는 AO 셀뿐이다 (게이트 출력 「AO 교차 쓰기가 403 이 아니거나」) | 반영됨 · 재측정 뒤 확인 |
| 3) 조직 단위 누출 한계 | itspec-m9e.json (123 시험) 대조: 조직 경로 목록 필터를 덮는 시험 이름은 없다. 목록 시험은 머니 목록 「담당 영업 9101 · 기업 9001 행만」 과 기업 범위 절 세 건 (기업 수준)이다. 이 한계는 (미검증) 으로 판정 행 증거 열에 이미 적었다 | 기록됨 |
| 4) AO15 (주문 → 회원 → 기업 체인) | 만들지 않았다. 사유: 체인 소유 대조 셀의 대상 행 설계 (주문 상세 9702 의 회원 연결) 와 ON 재측정이 필요하다. 이 구간에서 설계까지 가지 않았다 | (미검증, 사유: 체인 셀 설계 미완) |
| 5) AO 업로드 400 (AO3 · AO4 · AO5 · AO14) | 아래 「AO 업로드 400」 절 | 원인 미확정 (재측정 필요) |
| 6) 소비자 9 흐름 (가) 핸들러 도달 · (나) 정상 완료 | 개발하지 않았다. 아래 「소비자 흐름 (가)(나) 개발 대기」 절 | 미구현 · 미측정 |

### AO 업로드 400 (판정 5)

- 확인된 사실 (원자료 `raw-on-20261026T130545-d6d2feec.tsv` · `raw-off-20261026T121404-441b3215.tsv`, 측정 없음):
  1. 같은 본문 형식의 self 셀 (대상 = 신원 자기 기업) 은 AO3 · AO4 · AO5 · AO14 모두 201 이다 (OFF 와 ON 둘 다, 신원 corp-A · corp-B · org-A1 · org-A2 · admin-super · admin-general). 업로드 파일이 서버에서 받아들여진다는 뜻이다. 그러므로 「파일 형식이 틀렸다」 는 가설은 self 셀로 이미 반증됐다.
  2. 교차 셀과 self 셀의 차이는 대상 기업 (본문 `agcId` 와 대상 회원) 하나뿐이다. 교차 셀 AO3 · AO4 · AO5 · AO14 는 ON 에서 400, 소유 키는 9002 로 그대로다.
  3. ON 교차 원자료에는 응답 코드 열이 없었다 (8차 편차 · err 열은 그 뒤에 추가됐다). 그래서 400 의 코드 (E0815 인지 다른 코드인지) 는 아직 모른다.
- 소스 대조 (측정 전 가설, 미검증): corp 신원은 `fc-agency.service` 의 `accountUpdateUpload` 가 본문 `agcId` 를 신원 기업으로 덮어쓰고 (7차 기록), 이어 교차 대상 조회가 0 건을 내 E0815 400 을 던진다. org-A1 의 400 은 소스로 확정하지 못했다.
- 판정 표현: 측정 전이므로 「범위 거부 증명」 으로 쓰지 않는다. 메인 재측정 뒤 err 코드를 보고 「교차 도달 불가 (코드)」 또는 「업로드 형식 불일치 (코드)」 중 하나로 판정 행을 고친다.
- 발견한 결함 (고침): `owner-check-m9e.mjs` 의 `parseTsv` 가 머리 줄이 없는 AO 원자료의 첫 행을 건너뛰었다. 그래서 AO3 corp-A 셀이 조용히 빠졌다 (`AO_CROSS_CELLS=21`, 실제 22 행). 고침: `parseRows` 를 나누고 AO 는 그것으로 읽는다. `--selftest` 에 AO 두 행 음성을 더했다 (`SELFTEST orig=0 mutated=1 ao_rows=2 RESULT=PASS`).
- 재생성 (측정 없음): `owner-check-m9e.txt` 를 지우고 `owner-run-m9e.sh 20261026T121404-441b3215 20261026T130545-d6d2feec` 를 같은 입력으로 다시 돌렸다 (덮어쓰기는 이 구간의 결정이다, 9차 메인 측정 뒤에도 같은 방식으로 지운다). 결과: `ITEMS_RESPONSE=90 · ITEMS_CHECKED=90 · FOREIGN_ITEMS=22 · AO_CROSS_CELLS=22 · OWNER_CHECK_RC=1` (rc 1 은 AO 400 9 셀 · 403 아닌 셀 때문).

### 소비자 흐름 (가)(나) 개발 대기 (판정 6)

이 구간은 개발하지 않았다. 측정 없이 맞출 수 없는 것과 확인된 사실만 적는다. 다음 구간이 이 목록으로 시작한다.

1. 정정: `callback-coocon` 의 사유 토큰 `coocon-bearer-token-missing` 은 틀렸다. 실제 가드는 Bearer 가 아니라 `ClientBasicAuthGuard` 다 (`apps/api/src/modules/auth/client/auth.guard.ts:16–30`: `Authorization: Basic …`, 클라이언트 계정 `doesExist`, 없으면 401). 등록은 `access-contract.registry.ts:718` (외부 Basic). 정상 완료에는 (가) 클라이언트 계정 행 시드 · Basic 헤더 · `COOCON_AUTH_KEY` (`config/configuration.ts:49`) 합성 값 · `COOCON_GW_URL` 가짜 수신기 (EXT 에 이미 있다) 가 필요하다. 핸들러 (`treat/coocon-bypass.controller.ts:41–60`) 는 본인인증 작업이면 Redis 슬롯을 먼저 잡는다. 사유 토큰은 `client-basic-missing` 으로 고친다 (미구현).
2. `scheduler-push`: `PushScheduler.handleScheduledPush` → `PushService.sendScheduledPush` (`push/push.service.ts:245`) 는 `tn_push` 의 PENDING 이고 예약 시각이 지난 행을 집는다. 만기 행 시드가 필요하다. 다만 발송 경로가 FCM 을 치므로, 정상 완료 증명은 FCM 치환 불가 (사용자 몫) 에 걸릴 수 있다. 수신자 0 인 행으로 핸들러 도달까지만 증명하는 방법이 되는지 미확인.
3. `scheduler-alimtalk`: `AlimtalkService.sendDueReminders` (`alimtalk/alimtalk.service.ts:258`) 는 Redis zset `alimtalk:reminder:due:v1` 에서 score 가 지난 멤버 (`treat|loss:<altId>`) 를 읽는다. `processReminder` (298 행~) 는 초대 행 (`altCode` · `ordId`) 과 treat 이면 주문 상세 상태를 본 뒤 알리고(Aligo) 발송 경로로 보낸다 (`ALIGO_BASE_URL` 은 가짜 수신기). 시드는 Redis 항목 + 초대 행 + 주문 상세다 (미구현).
4. (가) 핸들러 도달 증명: 현 harness 의 REG 줄은 non-2xx 에서 상태 또는 본문 `code` 만 싣는다. 가드 거부와 핸들러 거부를 가르는 `ec=` (본문 오류 코드) 를 REG 줄에 더하고 judge 가 그것을 본다. `site-admin-view-public` 의 403 출처가 지금은 미확인인 이유다.
5. `site-admin-view-public` (`medication-standard/medication-standard-public.controller.ts:22`, 가드 `AdminOptionalGuard` · `AnalysisTargetGuard` · `JwtOptionalGuard`): 「발급자 시드」 의 대상 (어떤 행 · 어떤 토큰) 을 아직 특정하지 못했다.
6. `callback-mok-auth`: 2xx 분기는 외부 서명 키가 있는 MOK 결과 본문이 필요하다. (미검증, 사유 「외부 서명 키」) 로 둔다.
7. 현재 소비자 원자료 (`consumer-regress-m9e.txt`, ON · OFF 각각 PASS 4 · UNPROVEN 5) 는 헤더가 없어 검사 #23 이 붉다. 개발 뒤 새로 만든다.

### 검사 현황 (구간 끝, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, FAILS=3)

- #19: I9 재측정 산출이 측정 원천과 다르다 (재측정 필요).
- #20: AO 교차 쓰기가 403 이 아니다 (업로드 셀 9 건 · 400). OFF 타 소유는 판정 기록용으로만 센다 (22).
- #23: `consumer-regress-m9e.txt` 의 SRV_TREE 가 없다 (바인딩 헤더 없는 옛 원자료, 재측정 필요).
- 나머지 24 개 PASS (#21 · #22 · #24 · #25 · #26 포함).

### 이 구간에서 하지 않은 것 (이유)

- 측정 (OFF · ON · 반사실 · 소비자): 소비자 흐름 (가)(나) 개발이 먼저다. 개발 전에 ON 빌드를 돌리면 소비자 ON 원자료를 다시 만들어야 한다. 짧은 부분 측정도 원자료 장부 (`run-ledger-m9e.txt`) 에 붙어 조립의 「마지막 런」 규칙을 깨므로 하지 않았다.
- 판정 행 (검사 #24 의 「구현 뒤」 행) 과 PRD 절 (검사 #26): 이미 7차에 쓰였다. 이 구간의 새 증거 (AO err 코드 · 소비자 (가)(나)) 가 나온 뒤 고친다.
- Implement-Codex 리뷰 · Phase 4 · Phase 5 보고서 · receipt 3.6 · 3.7: 게이트가 아직 rc 0 이 아니라 리뷰 대상 상태가 확정되지 않았다. 커밋하지 않았다.

### 편차 (9차 · 검사 사이드카 두 건 고침, 측정 없음)

1. `owner-check-m9e.mjs`: AO 원자료 첫 행 소실 (위 「AO 업로드 400」 절). 게이트 본문 (#20) 은 그대로다. 출력이 맞아진 것은 owner-check 산출이다.
2. 검사 #25 (`.claude/PRPs/plans/admin-rbac-m9e.gates.sh` 571 행): `sed | grep -qF` 를 절 변수 대조로 바꿨다. 같은 노트 내용에서 FAIL 한 번 · PASS 두 번이 관측됐다 (pipefail 아래 grep 의 조기 종료가 sed 를 SIGPIPE 로 끊었다고 본다, 원인은 추정). 고친 뒤 게이트 실행 1회에서 PASS (반복 확인은 하지 않았다). 음성 (9e 의 jest 이름 빼기) 은 재실행하지 않았다.

## 메인 실행 요청 (9차 · 8차 정본을 잇는다, 소비자 개발 뒤)

이 구간의 최종 결과는 halted 다 (사유: 소비자 흐름 (가)(나) 개발 대기, 그다음 메인 측정). 개발이 끝나면 8차 정본 (`## 메인 실행 요청 (8차 · 판정 A · B 재측정, 정본)`) 을 그대로 돈다. 다음 세 가지만 달라진다.

1. 소비자 원자료는 개발 뒤에 만든다. 8차 정본의 3 (ON 빌드 · 소비자 ON) 과 5 (소비자 OFF) 는 개발 뒤에 돈다. 개발 전 소비자 실행은 쓰지 않는다.
2. `owner-check-m9e.txt` 는 9차 측정 뒤 `owner-run-m9e.sh` 를 돌리기 전에 지운다 (덮어쓰기 결정, 위 「AO 업로드 400」 절). 이 파일을 지운 뒤에도 owner-run 은 같은 두 RUN_ID 로 돈다.
3. AO 교차 (AOX) 의 err 열을 본다. AO3 · AO4 · AO5 · AO14 의 400 코드를 노트에 적은 뒤 판정 행 표현을 정한다 (「AO 업로드 400」 절 판정 표현).

판정 주체 (메인 몫): 소비자 (가)(나) 개발 범위 (MOK 제외 여부), AO15 설계 여부, 조직 단위 누출 한계 (미검증) 를 PRD 판정 행에 그대로 둘지, 그리고 8차 정본 6 (항목 소유 대조) 결과의 해석.

## 재개 기록 (implement-resume 10차 · 2026-10-26 · 최종 결과: halted)

- 시작 상태: 사용자 지시 원문 확인 (`verify-user-directive.sh` exit 0 · 2026-10-19T02:21:22.590Z · type=user · userType=external). `m9e-iso-` 컨테이너 0. 서버 · 어드민 · 사이트 워킹트리 청결. 게이트 시작 상태는 9차 종료와 같다 (FAILS=3: #19 · #20 · #23).
- 이 구간의 목표: 소비자 흐름 (가)(나) 개발 (9차 판정 6). 측정은 OFF 만 격리에서 돌렸다 (소비자 OFF 는 약 몇 분). ON 빌드 · ON 재생 · 반사실 · 조립 · 항목 소유 대조는 10분을 넘으므로 메인 몫이다 (아래 「메인 실행 요청 (10차)」).

### 소비자 흐름 (가)(나) 구현 결과 (OFF 측정, RUN_ID=20261026T142544-99997c59)

| 흐름 | OFF 결과 | 증명 근거 (핸들러 · 정상 완료) | 한계 (미검증) |
|---|---|---|---|
| site-admin-view-public | PASS `admin-token-public-view` (200) | 발급자 단계: `GET /admin-view-token?type=analyze&id=9701` (관리자 신원) → 공개 경로에 헤더 `x-admin-view-token` 을 실어 2xx. 토큰 없는 요청은 403 (소유자 검증 거부, 9차 관측과 같다) | 발급자는 관리자 신원 하나다 (별도 발급 시드 아님). 토큰 없이는 403 이 맞다는 증명은 OFF 만 (ON 은 메인 측정) |
| site-admin-view-token | PASS `token-issued` (200) | 9차와 같음 | — |
| app-user-refresh | PASS `refresh-row-rotated` (201) | 9차와 같음 | — |
| callback-money-approve · cancel | PASS `redirect-target` (201) | 9차와 같음 | 충전 · 정산 처리에는 닿지 않았다 (9차 정정 그대로) |
| callback-coocon | PASS `gateway-passthrough` (201) | 가드 Basic 통과 (`tn_client` 실행마다 무작위 합성 계정 · Basic 헤더) → 핸들러 (본인인증 슬롯 예약 · Redis) → passthrough 가 가짜 게이트웨이 응답 (`ResultMsg: fake`) 을 그대로 돌려줬다. 사유 토큰 `coocon-bearer-token-missing` 은 틀렸다 (9차 정정, 가드는 Basic) | 쿠콘 실제 응답 형상이 아니다 (가짜 수신기). AES 경로 · 실제 게이트웨이는 (미검증). 쿠콘 인증키 `COOCON_AUTH_KEY` 는 실행마다 무작위 합성 값 |
| callback-mok-auth | UNPROVEN `mok-external-signing-key` (302 는 실패 분기 redirect) | 2xx 분기는 외부 MOK 서명 키로 만든 결과 본문이 필요하다. 합성 환경에는 키 파일이 없다 (앱 기동 로그 「MOK Key 파일 경로가 잘 못 되었습니다」) | (미검증, 사유 「외부 서명 키」). 사용자 몫 |
| scheduler-push | PASS `job-row-claimed` (ok · run 1) | 만기 행 시드 (`tn_push`, 예약 시각 2000-01-01, 대상 = 존재하지 않는 기업 번호 `999999999`) → 잡이 pending → sending 으로 선점 → 발송 대상 0 명으로 정상 종료 | 발송 (FCM · 알림 행) 은 증명하지 않았다 (대상 0 이라 수신 0 이 설계다). 선점 뒤 상태가 sending 으로 남는다 (아래 「관찰」) |
| scheduler-alimtalk | PASS `reminder-removed-from-queue` (ok · run 1) | Redis 큐 `alimtalk:reminder:due:v1` 에 만기 항목 `loss:999999999` 시드 → 잡이 초대 번호 없음 (INVITE_NOT_FOUND) 으로 건너뛰고 큐에서 뺐다 (ZSCORE 빈 값) | 발송 경로는 증명하지 않았다 (건너뜀 분기만). 잡 컨텍스트가 logger 를 끄므로 건너뜀 로그 줄은 증거로 못 쓴다 (관측) |

- 관찰 (판정 주체 메인): `sendScheduledPush` 가 선점 (pending → sending) 하고 `sendPush` 는 대상 0 분기에서 상태를 되돌리지 않고 반환한다 (코드 읽기 근거: `push.service.ts` 대상 0 분기). 그래서 빈 대상 행은 sending 에 머문다 (PASS 판정 시점의 DB 값). 백로그 후보 (HIGH 여부는 메인 판정).
- 검사 #16 의 증거 어휘 (편차): 부류 어휘 (admin-view=token-issued · app=refresh-row-rotated · callback=redirect-target · scheduler=receiver-recorded) 로는 쿠콘 JSON 응답 · 푸시 선점 · 알림 큐 제거를 표현할 수 없어 흐름별 예외 4건을 더했다 (`site-admin-view-public` · `callback-coocon` · `scheduler-push` · `scheduler-alimtalk`). 수신기 기록 요건은 `receiver-recorded` 흐름에만 건다. 음성 마커 (「사유 토큰 자리표시자 · 시도 줄 없음」) 는 그대로다.
- 하네스 변경 (측정 산출의 원천): `consumer-regress-m9e.sh` (쿠콘 키 · 푸시 만기 행 시드 · 알림톡 큐 시드 · 잡 실행 env 노출 없음), `consumer-regress-m9e.mjs` (tn_client 합성 계정 · 공개 경로 발급자 단계 · 흐름별 판정 · MOK 사유 토큰을 외부 서명 키로 정정). ON 원자료는 이 변경 전 스크립트로 만든 옛 원자료다 (`consumer-raw-on.txt` 13:48, 옛 판정). 메인 ON 실행 뒤에야 조립이 맞는다.
- 집계 파일 상태: `consumer-regress-m9e.txt` 는 옛 ON 줄 + 새 OFF 줄이 섞여 있다 (ON 재측정 전까지 임시). 그래서 검사 #16 (흐름 `site-admin-view-public` 의 옛 ON UNPROVEN 과 새 OFF PASS 가 충돌) · #23 (`ON_PATCH_SHA256` 줄 없음) 이 붉다. 메인 ON 실행이 이 집계를 다시 쓴다.

### 검사 현황 (구간 끝, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, FAILS=4)

- #16: 임시 혼합 집계 (위). ON 재측정 뒤 녹색 기대.
- #19: I9 재측정 산출이 측정 원천과 다르다 (OFF 재생 · ON 재생 필요, 메인).
- #20: AO 교차 쓰기가 403 이 아니다 (업로드 셀 · 400). 메인 재측정의 err 열로 판정 표현을 정한다.
- #23: 집계에 ON_PATCH_SHA256 줄이 없다 (ON 재측정 뒤 녹색 기대).
- 나머지 #1–#15 · #17 · #18 · #21 · #22 · #24–#27 PASS.

### 이 구간에서 하지 않은 것 (이유)

- 메인 판정 5) AO 업로드 셀 재측정 (합성 업로드 파일로 범위 거부 또는 행 불변 증명): 교차 셀의 판정은 ON 빌드가 있어야 한다. 9차 원자료에서 같은 본문의 self 셀이 201 이므로 파일 형식 가설은 반증됐고, 400 의 코드는 ON 재측정의 err 열로 확정한다. 이 구간에서 파일을 새로 만들지 않았다 (메인 몫).
- 메인 판정 4) AO15 (주문 → 회원 → 기업 체인): 설계와 ON 측정이 필요해 이 구간에서 만들지 않았다 (미검증, 사유: 체인 셀 설계 미완 · 9차와 같다).
- 메인 판정 3) 조직 단위 누출 한계: 9차 기록 그대로 (미검증). 형제 픽스처 itspec 123 시험에 조직 경로 목록 시험이 없다.
- 판정 행 (검사 #24) · PRD 9e 절 (검사 #26) · 백로그 짝 행 (2007–2011): 검사 #24 · #26 은 현재 녹색이다. 2007 (소비자) 행의 「PARTIAL」 과 「공개 발급자 · 쿠콘 bearer · 만기 행 시드 미증명」 문구는 이 구간 결과 (6 흐름 OFF 증명) 로 낡았다. ON 재측정 뒤 한꺼번에 고친다 (ON 없이 고치면 ON 증거 없이 상태가 바뀐다).
- Implement-Codex 리뷰 · Phase 4 · Phase 5 · receipt 3.6 · 3.7: 게이트가 rc 0 이 아니라 리뷰 대상 상태가 확정되지 않았다. 커밋하지 않았다.

### 편차 (10차)

1. 검사 #16 증거 어휘 예외 4건 추가 (위 「하네스 변경」 · 「검사 #16 의 증거 어휘」). 사유: 부류 어휘가 흐름 증거를 표현하지 못한다. 음성 마커는 그대로다.
2. 소비자 원자료 OFF 를 이 구간에 새로 만들었다 (RUN_ID=20261026T142544-99997c59, SRV_TREE 5e740146a990f37493d26346850cc6bfc358ee76 = HEAD). 원자료 장부 (`run-ledger-m9e.txt`) 에는 소비자 실행을 적지 않는다 (9차까지의 규칙 그대로: 장부는 셀 원천 런만 센다).
3. 측정 빌드는 건드리지 않았다 (OFF dist 확인 `DIST_CONST_OK true=[없음]`, 서버 워킹트리 청결 확인).

## 메인 실행 요청 (10차 · 9차 요청을 잇는다, 정본)

이 절이 다음 측정의 정본이다 (8차 정본 1–7 의 순서와 명령을 그대로 쓰고, 아래 네 가지만 달라진다). 모든 명령은 워크트리 루트에서 실행한다. `<스키마 디렉터리>` 는 격리 스키마 덤프 디렉터리다 (읽기만, 경로 · 값은 적지 않는다).

실행 전 확인: `git -C insuniverse-server status --porcelain` 이 비어 있고, `docker ps` 에 `m9e-iso-` 이름 컨테이너가 없다.

1. OFF 재생 (셀 원천, I9 포함): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/replay-m9e.sh off` → `REPLAY_DONE mode=off run=<OFF_RUN>`, 이어서 `… replay-m9e.sh off-rerun` → `RR`. 약 50분. (8차 1 과 같다.)
2. ON 빌드 · ON 재생 · AO 교차 · 소비자 ON: `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/on-area-build-m9e.sh` → `ON_BUILD_DONE … changed=4 run=<ON_RUN>`. 약 40분. 이 안의 소비자 ON 이 10차 하네스로 돈다 (ON 원자료 갱신). 끝나면 상수 원복 · OFF 재빌드, `git -C insuniverse-server status --porcelain` 이 비어야 한다.
3. 반사실 (ON 빌드 위): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/counterfactual-m9e.sh` → `COUNTERFACTUAL_DONE out=…`. `FLIP_COUNT ≥ 1` (검사 #18).
4. 조립: `node .claude/meta/2026-10-26-admin-rbac-m9e/assemble-m9e.mjs .claude/meta/2026-10-26-admin-rbac-m9e .claude/meta/2026-10-26-admin-rbac-m9e/measure-m9e.mjs` → `ASSEMBLED cells=… flipped=… i9=… i9_excluded=…`.
5. 소비자 OFF (ON 뒤에 돌려 집계를 최종으로 만든다): `M9E_SCHEMA_DIR=<스키마 디렉터리> bash .claude/meta/2026-10-26-admin-rbac-m9e/consumer-regress-m9e.sh off` → `CONSUMER_DONE mode=off reg=9 unproven=<n>`. 두 모드 원자료가 다 있으면 `consumer-regress-m9e.txt` 가 다시 쓰인다. 기대: 증명 8 흐름 (MOK 만 UNPROVEN `mok-external-signing-key`). ON 줄도 같은 집합이어야 한다 (판정 주체: 메인).
6. 항목 소유 대조: `rm .claude/meta/2026-10-26-admin-rbac-m9e/owner-check-m9e.txt` (덮어쓰기 결정, 9차와 같다) 후 `bash .claude/meta/2026-10-26-admin-rbac-m9e/owner-run-m9e.sh <OFF_RUN> <ON_RUN>` → `OWNER_CHECK_DONE rc=<rc> out=… items=<n>`. 기대 (메인 판정): ON 묶음 `foreign=0`. AOX 줄의 `err=` 를 보고 AO3 · AO4 · AO5 · AO14 의 교차 400 코드를 이 절 아래에 적는다 (9차 「AO 업로드 400」 절 판정 표현).
7. 검사: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`. 기대: #16 · #19 · #20 · #23 중 #19 · #23 · #16 은 재측정 뒤 녹색. #20 은 AO 업로드 셀 400 이 남으면 붉을 수 있다 (메인 판정, 게이트 본문은 고치지 않는다).

메인 판정 몫 (이 구간이 넘긴 것): (가) 설계 판정 (발급자 = 관리자 신원 · 푸시 대상 0 · 알림 큐 건너뜀 이 「정상 완료」 로 충분한가), `sending` 잔류 관찰의 백로그 등급, MOK 외부 키 (사용자 몫), AO15 설계 여부, 조직 단위 누출 한계 (미검증) 의 PRD 판정 행 표현, 2007 행 · PRD 9e 절 갱신 (ON 뒤), 그리고 Implement-Codex 리뷰 · Phase 4 · 5 로 넘기는 시점.

## 구현 · 최종 결과 (11차 마무리 · 2026-10-26)

이 절은 메인 실행 요청 (10차) 1–7 의 결과를 받아 판정 행 · PRD 절 · 백로그 짝 행을 쓰고 게이트 본문을 rc 0 으로 맞춘 기록이다. 측정 명령은 이 단계에서 다시 돌리지 않았다 (10분 넘는 측정 금지).

### 측정 결과 (10차 정본, 메인 실행)

| 단계 | 결과 |
|---|---|
| OFF 재생 · 재실행 | `OFF_RUN=20261026T142818-2a840a9e` (394 셀) · `off-rerun 20261026T142818-e5a8631f` (394 셀) |
| ON 빌드 · 재생 | `ON_RUN=20261026T160517-1d5d0715` (`ON_BUILD_DONE changed=4`) |
| 반사실 | `flip 20261026T164845-abbabce7` · `FLIP_COUNT=14` (검사 #18 녹색) |
| 조립 | `ASSEMBLED cells=394 flipped=14 i9=2 i9_excluded=6` |
| 소비자 OFF · ON | OFF `RUN_ID 20261026T165901-639ff6be` · ON `RUN_ID 20261026T164746-0aeeb159` · `CONSUMER_DONE mode=off reg=9 unproven=1` · 정상 완료 8 흐름 (OFF = ON) · UNPROVEN 1 (MOK, `mok-external-signing-key`) |
| 항목 소유 대조 | `owner-run` 재실행 (같은 두 RUN_ID · 옛 owner-check 파일은 지우고 다시 만듦) · `ITEMS_RESPONSE=90 ITEMS_CHECKED=90 ON_FOREIGN=0 OFF_FOREIGN=22` · `AO_CROSS_CELLS=22 AO_CROSS_403=14 AO_CROSS_400=8` · `OWNER_CHECK_RC=0` |

- 첫 ON 시도 `raw-on-20261026T152251-61baa790` 은 소비자 ON 단계에서 끊긴 실행이라 정본이 아니다. 장부 줄 (`run-ledger-m9e.txt`) 은 지우지 않고 두며, 조립과 항목 대조의 입력은 `160517` 이다.
- 메인이 고친 하네스 결함 두 건 (메인 기록을 그대로 옮긴다): (a) `replay-m9e.sh` 의 flip 허용 목록은 줄바꿈 연결 (`tr '\n' ' '`) 로 읽는다. (b) `consumer-regress-m9e.sh` 의 ON 청결 검사는 서버 기준 경로와 워크트리 기준 `CONST_REL` 을 바로 비교해 늘 어긋났다 → `${CONST_REL#"$M9E_SRV"/}` 로 맞췄다.

### 이 단계에서 고친 것 (게이트 · 산출 생성기)

1. `owner-check-m9e.mjs` 의 종료코드 (메인 판정 1): rc = ON 묶음의 타 소유 수 + AO 교차 위반 수. OFF 묶음의 타 소유 (22) 는 `OFF_FOREIGN` 줄로 누출 증거만 낸다. AO 교차 셀은 403 이거나, 업로드 행 (AO3 · AO4 · AO5 · AO14) 의 400 이면 통과로 센다 (`AO_CROSS_403` · `AO_CROSS_400` 요약). 이전 rc 는 1 이었다 (OFF 22 와 AO 400 8 셀이 rc 를 올렸다).
2. `owner-run-m9e.sh` 를 같은 두 RUN_ID 로 다시 돌렸다. 옛 `owner-check-m9e.txt` 는 덮어쓰기 규칙에 따라 먼저 지웠다 (저장소 밖 사본만 남겼다). 결과 `OWNER_CHECK_RC=0`.
3. 게이트 검사 #20 (`c20`) 두 군데를 고쳤다.
   - I9 분기: 원자료를 `raw-{off,on}-*.tsv` glob 으로 읽어 옛 런 (`121404` · `130545` 계열) 이 섞였다 (6 이 12 로 셌다, 첫 실행 FAIL 의 원인). 이제 owner-check 머리 줄의 `OWNER_OFF_RUN` · `OWNER_ON_RUN` 이 고른 런만 읽는다. 권한 403 조건에 `E0607/permission` 을 더했다 (메인 판정 2).
   - AO 분기: 업로드 행의 400 을 범위 판정 앞 단계 거부로 허용했다 (메인 판정 5). 그 밖의 행은 403 이어야 하고, 두 모드 모두 행 소유 키가 쓰기 전후 같아야 한다. 요약 줄과 AOX 줄 수를 대조한다.
   - 음성 실행 (관측): `owner-check` 의 ON 줄 하나를 `foreign=1` 로 바꾼 META 사본으로 `M9E_ONLY=20 M9E_META=<사본>` 실행 → exit 1 (`ON 묶음에 타 소유 항목이 있다`). 사본은 지웠다.
4. 판정 행 · 짝 행 · PRD 절을 새 증거로 고쳤다 (아래 「판정」). 검사 #24 · #26 · #10 · #7 는 녹색이다.

### 메인 판정 1)–6) 에 대한 답

1. 검사 #20 타 소유 0: ON 에만 적용 (`ON_FOREIGN=0`). OFF 22 는 누출 증거 줄 (`OFF_FOREIGN`). 음성 실행 관측은 위 3. 반영.
2. I9: 범위 신원 6 명 (sales-A · sales-B · corp-A · corp-B · org-A1 · org-A2) 이 OFF · ON 모두 `403 E0607/permission` 임을 원자료 기계 대조로 확인했다 (검사 #20 I9 분기 녹색). 목록 길이는 관리자 신원 (`admin-super` · `admin-general`) 의 `2 · 2` 만 본다 (검사 #19). 반영.
3. 조직 단위 누출 한계: 메인 지시는 형제 픽스처 itspec 의 조직 수준 목록 필터 시험 이름으로 덮는다고 했으나 그 이름은 없다. 123 시험 이름을 전수로 대조했다 (「조직」「org」「목록」「필터」「list」 검색). 조직 이동 · 회원 정보 수정 · 조직 쓰기 시험만 있다. 따라서 덮지 않았다. 판정 행은 (미검증) 으로 두고 백로그에 신규 MEDIUM 을 올렸다 (2011 · 1974 한계). 메인 확인이 필요하다.
4. AO15 (주문 → 회원 → 기업 체인): 직접 호출 격리 시험을 만들지 않았다 (미검증). 사유: 체인 셀의 픽스처 설계가 아직 없고, 전체 재생 재실행은 금지다. PRD 9e 절과 판정 행에 그대로 적었다. AO15 설계는 메인 판정 몫이다.
5. AOX 400 8 셀의 원인 특정 (`err` 열과 서버 코드 대조. 서버 경로 `insuniverse-server/apps/api/src/modules/`):
   - AO3 · AO5 (`accountUpdateUpload` · `subscribeUpload`, corp-A · org-A1): `err=E0815` = `NOT_FOUND_FC_IN_SUBSCRIBE`. 소속 조회 (`getFCListByLoginIds(dto.agcId, …)`) 의 개수가 파일 행 수와 달라서 난다. `assertMembersWritableByAdmin` (범위 판정) 보다 앞이다.
   - AO14 (`uploadGrant`, corp-A · org-A1): `err=-` (메시지 전용 400). 기업 소속 조회 (`getMemberByLoginIdAndAgcId`) 가 실패해 「해당 기업 소속이 아닙니다」 행 검증에서 났다 (추정: 응답 메시지를 원자료에 남기지 않았다). 범위 분기 (`scope.orgPath` · `isMemberInAgencyScope`) 는 이 행까지 닿지 않는다.
   - AO4 (`orgPermissionUpload`, corp-A · org-A1): `err=-`. E0815 가 아니므로 소속 개수 불일치가 아니다. `validateOrgPermissionUploadRows` (행 · 조직 코드 검증, 메시지 전용) 에서 났다고 본다 (추정, 미검증).
   - 세 경로 모두 범위 판정 (`assertMembersWritableByAdmin` · 조직 분기) 보다 앞이다. 그 셀은 「범위 거부 미관측 · 행 불변」 으로 분리했다. 「행 불변」 은 행 소유 키 (기업 번호) 가 8 셀 모두 쓰기 전후 같다는 뜻이다. 조직 · 계정 · 머니 필드는 이 측정이 보지 않았다 (미검증).
   - 범위 거부 자체의 서비스 직접 시험: `fc-bulk-scope.itspec.ts` 의 계정 수정 · 조직 권한 · 구독 업로드 전건 거부 3 종 (`ForbiddenException`, 격리 MySQL, 123 시험 중 통과) 이 AO3 · AO4 · AO5 의 범위 판정을 직접 증명한다. AO14 의 조직 분기는 직접 시험이 없다 (`money-bulk-scope` 의 지정 지급 전건 거부는 영업 담당 분기다).
   - 메인 가정과 다른 점: `uploadGrant` 의 조직 범위 분기 거부는 403 이 아니라 400 (행 검증 메시지 「관리 조직 범위 밖 회원입니다」) 이다. 판정 표현은 이 사실을 따른다.
   - 전체 재생 재실행 없이 끝냈다 (지시 준수).
6. 소비자: 8 흐름 정상 완료 (OFF = ON), MOK 1 흐름만 외부 서명 키 (미검증 · 사용자 몫). 반영.

### 판정 (백로그 짝 행 · PRD 9e 절)

- 1966 · 1973 · 1985 · 2007: PARTIAL (MOK 1 흐름 미증명). 1984 · 2008: PARTIAL (교차 · 형제 셀 84 개 층 other, R1 정정). 1986: PARTIAL (사용자 몫). 2009: RESOLVED-IN (관리자 길이 2 · 별칭 시험 녹색). 1974: RESOLVED-IN (목록 90 항목 전수 · 증거 약화 명시 · 구현 R1 HIGH 는 열림). 2010: PARTIAL (Codex R2 HIGH 반영: AO15 미측정 · 업로드 400 8 셀의 범위 거부 미관측 · 「행 불변」 은 행 소유 키 한정). 2011: RESOLVED-IN (123 시험 통과, 조직 경로 목록 한계는 (미검증) 로 신규 MEDIUM).
- 검사 #24 는 expect_status 가 정한 값과 맞는지 본다 (증거가 바뀌면 상태도 바뀐다). 현재 녹색.
- 신규 백로그 행 3 개 (MEDIUM): 푸시 대상 0 의 sending 잔류 (RBAC 밖 · 소유자 판정), 조직 경로 목록 누출 시험 부재, AO14 조직 분기 직접 시험 부재.

### 검사 상태 (종료 시점)

- `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh` → rc 0 · `FAILS=0` (#1–#27 전부 PASS).
- 음성 확인: #20 (ON 타 소유 1 행 사본 → exit 1). 다른 음성은 이번 단계에서 다시 돌리지 않았다.

### 이번 단계에서 하지 않은 것

- 커밋 · 푸시 · PR · stg/prod 병합 · 운영 DB 접속 · 측정 재실행 (지시).
- 서브모듈 워킹트리 변경: 없다 (`SRV_TREE=5e740146` = HEAD, 서버 워킹트리 청결 검사 #5 녹색).
- `PRPs/reports/admin-rbac-m9e-report.md` 는 갱신하지 않았다 (이 실행 규칙: 보고서 .md 를 새로 쓰지 않는다). 기존 파일은 구현 중간 상태를 적은 것이라 낡았다. 최종 보고는 이 절과 에이전트 반환문이 맡는다.

### Implement-Codex 라운드 2 (10차 측정 뒤 · 11차 반영)

- 호출: `codex-invoke.js adversarial-review` (작업 트리 기준, 워킹트리 diff 4 + 미추적 55). 결과 `ok=true · class=ok · blocking=false · verdict=needs-attention`. 원장 `rounds_so_far` 는 2 가 된다 (R1 은 구현 중).
- F1 HIGH (백로그 2010 행): 「RESOLVED-IN」 이 과했다. AO15 는 측정하지 않았고, 업로드 400 8 셀은 범위 거부가 403 으로 관측되지 않았으며, 「행 불변」 은 소유 키 (`tn_member.agc_id` 쪽) 만 본다. 반영: 2010 판정을 PARTIAL 로 바꿨다. 검사 #24 의 `expect_status` 2010 은 「AO15 줄 존재 · AO_CROSS_400 = 0」 일 때만 RESOLVED-IN 을 준다 (현재 조건 불충족 → PARTIAL). 백로그 행 · PRD 9e 글머리 · 이 노트의 문구를 같이 고쳤다. 게이트 #24 녹색.
- F2 HIGH (`counterfactual-m9e.sh` 청결 검사 실패 경로): 복구 trap 을 청결 검사보다 먼저 걸어, 서버 워킹트리에 미커밋 수정이 있으면 검사에서 중단하면서도 `restore_all` 이 편집 대상 파일을 HEAD 블롭으로 덮었다. 반영: trap 을 청결 검사 뒤로 옮겼다 (`bash -n` 통과). 정상 경로의 동작은 같다 (측정 결과 · 원자료는 바뀌지 않는다). 측정은 다시 돌리지 않았다. 실패 경로는 읽기로만 검증했다 (미검증: 더미 수정 파일로 실행한 음성은 없다).
- 이 리뷰가 지적하지 않았지만 남은 것: 검사 #20 이 목록의 기대 신원 집합을 고정하지 않는다 (구현 R1 HIGH, 백로그 행 그대로 열림). 이 결함은 이 단계에서 고치지 않았다 (게이트 본문 변경은 판정 반영에 필요한 범위로만 했다).

### Implement-Codex 라운드 3 (마지막 라운드 · 11차)

- 호출: 같은 방식 (작업 트리 기준). 결과 `class=ok · blocking=false · verdict=needs-attention` · HIGH 1 건. 원장 `rounds_so_far=3` (R1 · R2 · R3 가 모두 센다).
- 지적: 검사 #20 은 목록별 OFF · ON 신원 한 명씩과 AO 양수 개수만 보고, 검사 #24 의 2010 조건은 「AO15 줄 하나 · AO_CROSS_400 = 0」 으로 충족된다. 1974 도 같은 자리다. 구현 R1 HIGH 와 같은 결함의 재확인이다 (기대 신원 · AO 행 집합을 고정하지 않는다).
- 처리: CRITICAL 이 없으므로 skip-receipt 경로다 (사용자 지시 2026-10-19, `verify-user-directive.sh` exit 0). HIGH 는 백로그에 새 행 (2026-10-26 HIGH, 구현 R3) 으로 남기고 다음 계획 반영 대상으로 둔다. 이 결함은 이번 단계에서 고치지 않았다. 게이트 기대 집합을 고치려면 목록 · AO 행 집합을 측정 대상 정의에서 기계로 뽑는 변경이 필요해 계획 본문 동결 범위를 넘는다.
- 2010 조건의 AO15 한 줄 경로는 이 HIGH 의 한 예다. 2010 은 지금 PARTIAL 이라 실제 판정에는 영향이 없다 (AO15 가 측정되지 않았다).
- implement receipt 재작성: `mccp-receipt write --gate mccp-implement-codex --auto-round` (env 없이 성공). 결과 `round=3` · `codex_verdict=divergent` · `deferred_findings=2` · `security_skipped` (사유 기록) · `impeccable_silent_skip=no-signal` · `validate` 의 `blocking=[]`, `open_critical=[]`. 이전 R1 receipt 사본은 저장소 밖 (git dir 아래) 에 남겼다.
- 상태 파일: `.claude/state/fix-task.md` 에 `verdict: codex_divergent · escalate: true` 가 남는다 (receipt write 가 쓴다). 다음 턴의 정지 조건이 될 수 있다. 메인 판정 대상이다 (divergent 는 백로그로 넘긴다는 지시와 맞춘다).
- 커밋하지 않았다. 따라서 3.7 restamp (커밋 뒤 receipt 재계산) 는 하지 않았다 (미검증 · 커밋 뒤 몫). 3.6 은 design 신호 0 이라 열리지 않았다.
- 노트 「Codex Implementation Review」 (위 R1 절) 는 구현 중 기록이다. 이 절의 R2 · R3 과 최종 receipt 가 현재 상태를 본다.

## 코드 리뷰 (R1 · 2026-10-26 · 흡수)

리뷰 지적 20 건 (중복 제거 뒤) 의 판정. 흡수 16 · 부분 흡수 4 · 기각 0. HIGH 1 건은 흡수했다 (주장 하향). CRITICAL 은 없다.
층위는 CLAUDE.md 자가 1패스 체크리스트 번호다 (1 사실 · 2 주장 ≠ 검사 · 3 명령 ≠ 게이트 · 4 셸 실패 규약 · 5 게이트 실행 불가 · 6 게이트가 보는 대상 · 7 검사 자신의 주장 · 8 검사 사각지대).

| 라운드 | 출처 | 심각도 | 지적 | 판정 | 증거 | 층위 |
|---|---|---|---|---|---|---|
| R1 | code-review | HIGH | AO 교차 403 14 셀을 RBAC 범위 거부로 귀속 (E0401 기업 가드와 구분 없음) | 흡수 (주장 하향 · OFF 재측정 없음) | PRD 9e · 백로그 2010 정정 행 (「403 관측, 원인 미분리」), 노트 본문 | 2 |
| R1 | code-review | MEDIUM | uploadGrant 조직 범위 분기가 측정 · 반사실 · 시험 어디에도 닿지 않음 | 흡수 | `money-org-scope.itspec.ts` 「지급 엑셀 조직 범위 거부」, 범위 줄 제거 변이에서 붉음 | 8 |
| R1 | code-review | MEDIUM | 흡수 대상 3 건 (AO15 · AO14 조직 · 조직 목록 누출) 의 시험이 없음 | 부분 흡수 | AO15 3 시험 · AO14 1 · 목록 2 (머니 내역 · 지급 양식) 추가, 변이 각각 붉음. 나머지 목록 장면은 백로그 2047 PARTIAL | 2 |
| R1 | code-review | MEDIUM | OFF 누출 22 에 소유 키 부재 (`-`) 4 건이 섞임 | 흡수 | `owner-check-m9e.mjs` 가 타 소유 18 · 불명 4 로 나눔 (`OFF_FOREIGN=18 OFF_UNKNOWN=4 ON_UNKNOWN=0`). 게이트 #20 이 ON 불명을 실패로 본다 | 6 |
| R1 | code-review | MEDIUM | 보고서 (untracked) 가 최종 상태와 어긋남 | 흡수 | `PRPs/reports/admin-rbac-m9e-report.md` 최종 상태로 다시 씀 | 1 |
| R1 | code-review | MEDIUM | callback-money-approve PASS 가 실패 분기 (Moid 없음) 일 수 있다 | 부분 흡수 (주장 하향 · 판정 재실행 미완) | PRD 9e 는 머니 승인을 가드 도달 증거로만 씀, 판정 함수 `shortcut:` 주석, 백로그 1985 · 2007 행. 재실행은 미실행 (미검증) | 2 |
| R1 | code-review | MEDIUM | AO 교차 측정 범위 (신원 corp-A · org-A1, AO1 · AO2 · AO12 · AO15 대상 누락) | 부분 흡수 (미측정 명시) | PRD 9e 측정 범위 문장, 백로그 신규 MEDIUM 행. 측정 확장은 재측정이 필요해 미실행 | 6 |
| R1 | code-review | MEDIUM | 교차 · 형제 셀 「85」 · 「394」 가 틀림 (98 · 84 가 맞다) | 흡수 | `replay-cells-m9e.tsv` 재계산: 전체 394 (자기 296 · 교차 84 · 형제 14), 교차 · 형제 98 (scope 14 · other 84). PRD · 노트 · 백로그 정정 | 1 |
| R1 | code-review | MEDIUM | 소유 추출 실패가 stderr 에만 남음 + 별칭 규칙이 측정 규칙과 다름 | 흡수 | `owner-measure-m9e.mjs` 별칭 규칙을 목록 길이 규칙과 같게 함. `owner-measure-selftest-m9e.mjs` 가 변이 (이전 규칙) 에서 exit 1, 수정 뒤 PASS. 탈락 셀은 원자료 `OWNER_DROPPED` 줄, 게이트 #20 이 0 을 요구 | 8 |
| R1 | code-review | LOW | 노트에 호스트 임시 경로 (`/tmp/m9e-*`) | 흡수 | 노트 「작업 트리」 행을 「저장소 밖 임시 디렉터리」 로 고침 | 1 |
| R1 | code-review | LOW | AO15 제외 사유 주석이 사실과 다름 | 흡수 | `replay-m9e.mjs` 의 AO_TARGET 주석 (체인 픽스처 미설계 · 직접 시험이 맡음) | 1 |
| R1 | code-review | LOW | owner-check 가 열 수 · 빈 소유 키를 조용히 셈 | 흡수 | `parseItems` 가 열 6 미만 · 빈 키를 exit 2 로 막음. 음성 실행 (빈 키) → rc 2 확인 | 4 |
| R1 | code-review | LOW | owner-check 모듈 import 가 CLI 를 돌려 프로세스를 끝냄 | 흡수 | `import.meta.url` 가드. import 확인: 종료 없이 `countForeign` 노출 | 5 |
| R1 | code-review | LOW | replay-report-m9e.mjs 가 옛 m9d 보고서 생성기 (dead · 충돌 위험) | 흡수 | 파일 삭제 (untracked). 게이트 #27 는 남은 .mjs 로 통과 | 8 |
| R1 | code-review | LOW | counterfactual 치환이 `$&` 등 패턴을 해석 | 흡수 | 함수 치환으로 고침. `node -e` 확인: 이전 `XY`, 수정 뒤 `$&Y` (리터럴) | 1 |
| R1 | code-review | LOW | consumer 미매핑 흐름 분기가 도달 불가 (TypeError 가 먼저) | 흡수 | `httpFlow` 앞 가드 추가. 9 흐름 동작 불변 (`node --check`) | 5 |
| R1 | code-review | LOW | 스케줄러 push PASS 가 잡 실행 결과를 관측하지 않음 | 흡수 (문구 하향) | PRD 9e 「선점 · 발송 미증명」, 알림톡 「큐 제거 분기 · 발송 증명 아님」 | 2 |
| R1 | security | LOW | 반사실 줄 8 곳이 범위 거부 지점 일부뿐인데 「범위 판정이 아닌 다른 가드」 라고 씀 (scope 사유 셀 6 개가 층 other) | 흡수 (문장 정정 · 층 미확정 명시) | PRD 9e · 노트 본문 (교차 · 형제 84 개 other, 6 셀 미확정) · 백로그 2008 정정 행 | 2 |
| R1 | security | LOW | 격리 DB 비밀번호가 docker argv 에 실림 | 흡수 | `_m9e-lib.sh` · `itspec-run-m9e.sh` · `replay-m9e.mjs` 가 환경 변수 전달 (이름만 argv). 격리 시험 재실행 (`absorb-itspec-m9e.sh`) 으로 스택 · 복원 경로 확인. 재생 경로의 execFileSync 는 실행하지 않음 (미검증) | 4 |
| R1 | security | LOW | 외부 주소 목록이 상수 여섯 줄 · 끝 앵커 없음 · VFAX · 헤이픈 기본 주소 미덮음 | 부분 흡수 | 목록을 `EXT` 에서 뽑도록 고침, 게이트 #16 에 `$` 앵커 (userinfo 형태 음성 0 건 확인). 기본 주소 루프백 덮기는 미완 (백로그 LOW) | 6 |

검사 상태 (이 라운드 뒤, `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`): FAILS=3. #1 · #5 · #23 은 서버 워킹트리에 미커밋 신규 시험 2 파일이 있어 붉다 (`insuniverse-server/apps/api/src/modules/money/money-org-scope.itspec.ts` · `.../order/order-detail/order-detail-owner-scope.itspec.ts`). 커밋 뒤에는 #1 · #5 가 녹색이 되고, #23 은 서버 트리 해시가 바뀌므로 측정 바인딩을 다시 맞춰야 한다 (미검증).

격리 시험 (`bash .claude/meta/2026-10-26-admin-rbac-m9e/absorb-itspec-m9e.sh`, 산출 `absorb-itspec-m9e.json`): 6 시험 전부 통과. 변이 4 종 (AO15 기업 검사 · AO14 범위 줄 · 머니 내역 목록 조직 조건 · 지급 양식 조직 조건) 에서 각각 해당 시험이 붉어졌다. 시험 설정은 `absorb-jest.config.cjs` 로 분리했다 (본 itspec 집합 · 검사 #21 불변).

미실행 · 미검증 (이 라운드): 소비자 흐름 재실행 (판정 함수의 ResultCode 판정 · Moid 응답), 반사실 · AO 교차 · 측정 재생 (ON 빌드 필요), 재생 스크립트의 DB 비밀번호 경로 (`execFileSync`), 목록 장면 나머지 (머니 목록 · 결제 · 조직 경로). 이 항목들은 백로그 (2047 · 2010 · 1985 · 1974 신규 행) 에 PARTIAL 또는 미검증으로 남겼다.

커밋하지 않았다. 원격 push · PR · 병합 없음. 서버 · 어드민 · 사이트 서브모듈의 커밋도 만들지 않았다 (신규 시험 2 파일은 미추적 상태).

## 코드 리뷰 (R2 · 2026-10-26 · 흡수 마무리)

R2 흡수 에이전트가 끊기기 전 보고한 판정 (지적 19 건 중 흡수 18 · 기각 1) 을 흡수 단위로 묶어 적는다. 19 건의 원문 목록은 인계 자료에 없어 19 행 1:1 표가 아니다. 집계 (18 · 1) 만 에이전트 보고 그대로다. 층위는 흡수 내용을 보고 이 노트가 붙였다 (리뷰어 라벨이 아니다). 층위 값은 위 자가 1패스 체크리스트 번호다.

| 행 | 흡수 단위 (변경) | 판정 | 증거 | 층위 |
|---|---|---|---|---|
| R2-1 | 머니 조직 범위 시험 파일 이름을 `money-org-scope` 에서 `money-scope-org` 로 바꿈 | 흡수 | `money-scope-org.itspec.ts` · 상태 줄 `SRV_TEST_ADDED_SHA256` (검사 #21) | 1 |
| R2-2 | 머니 시험에 신원 매핑 (컨트롤러 경로) 을 더함 | 흡수 | 「머니 내역 목록 조직 범위 (신원 매핑)」 「지급 엑셀 조직 범위 거부 (신원 매핑)」 · MUT M5 · M6 붉음 | 8 |
| R2-3 | AO14 두 행 (corp-A · org-A1) 을 전건으로 시험 | 흡수 | 「지급 엑셀 조직 범위 거부 (전건)」 · MUT M7 붉음 | 2 |
| R2-4 | 주문상세 조직관리자 시험 (SYNORGA1 → 형제 SYNORGA10 · 9704) | 흡수 | 「조직관리자 (SYNORGA1) 가 형제 조직 SYNORGA10 의 주문상세 (9704) …」 · MUT M2 · M4 붉음 | 8 |
| R2-5 | 주문상세 혼합 목록 시험 (기업 9001 신원이 [9701, 9702] 를 보냄) | 흡수 | 「혼합 목록: …」 · MUT M3 붉음 | 8 |
| R2-6 | 데이터 전제 시험 (odd_show DB 읽기) 과 시험 SQL 의 예약어 별칭 버그 수정 | 흡수 | 「데이터 전제: …」 · MUT M4 붉음 | 5 |
| R2-7 | `itspec-jest.config.cjs` 의 testMatch 에 두 파일을 넣고 `absorb-jest.config.cjs` 를 지움 | 흡수 | 설정은 한 벌이다 (absorb 설정 파일은 HEAD 에도 디스크에도 없다). `absorb-itspec-m9e.sh` 는 본 설정을 쓴다. R1 절의 `absorb-jest.config.cjs` 언급은 이 행으로 낡았다 | 1 |
| R2-8 | 변이 반사실 스크립트 `mutation-m9e.sh` 를 만듦 (9 변이) | 흡수 | `mutation-m9e.txt` MUT M1–M9 전부 RED · `TREE_AFTER` 는 두 미추적 파일뿐 (아래 「변이 반사실 결과」) | 5 |
| R2-9 | 검사 #20 수정: I9 는 범위 신원 6 명의 `E0607/permission` 403 을 요구, AO 업로드 행만 400 을 허용하고 그 밖 AO 행은 403 을 요구 | 흡수 | 검사 #20 PASS · 음성 실측 (아래 「게이트 음성 실측」) | 6 |
| R2-10 | 검사 #21 수정: 흡수 시험 파일 판본 sha 를 실행 기록 (상태 줄) 에 바인딩 | 흡수 | 검사 #21 PASS · 음성 실측 | 7 |
| R2-11 | 검사 #23 수정: 측정 대상 트리를 HEAD 에서 흡수 시험 두 파일을 뺀 트리로 정의 | 흡수 | 검사 #23 의 트리 대조 PASS (probe 아래) · 측정 산출 16 개의 `SRV_TREE` · `SRV_TREE_END` 가 전부 `5e740146` · 음성 실측 | 6 |
| R2-12 | 검사 #11 수정: 측정 스크립트가 비밀 값을 명령행 (`-e NAME=값`) 에 싣는 형태를 금지 | 흡수 | 검사 #11 PASS · 음성 실측 | 4 |
| R2-13 | 격리 itspec 재기록 (형제 픽스처 포함) | 흡수 | `itspec-m9e.json` 14 suite · 133 시험 · 실패 0 · 실행 기록 `SIBLING_FIXTURE=present` | 7 |
| R2-14 | replay 메모리 누적 (LOW) | 기각 (에이전트 판정) | 사유 원문 없음. 측정 산출은 바뀌지 않는다 | — |

### 변이 반사실 결과 (`mutation-m9e.txt` · 서버 기준 3418d3a0 · 격리 스냅샷 복원 뒤 실행)

| 변이 | 대상 (서버 기준) | 시험 파일 | 붉은 시험 / 전체 |
|---|---|---|---|
| M1 | 주문상세 컨트롤러 기업 역할 분기 제거 | 주문상세 | 3 / 5 |
| M2 | 주문상세 컨트롤러 조직 범위 인자 제거 | 주문상세 | 1 / 5 |
| M3 | 주문상세 컨트롤러 혼합 목록의 전건 거부 조건 (개수 검사) 을 바꿈 | 주문상세 | 1 / 5 |
| M4 | 주문상세 저장소 조직 필터 제거 | 주문상세 | 2 / 5 |
| M5 | 머니 컨트롤러 `applyCorpScope` 의 조직 범위 제거 | 머니 | 1 / 5 |
| M6 | 머니 컨트롤러 `getCorpGrantScope` 의 조직 범위 제거 | 머니 | 1 / 5 |
| M7 | 머니 서비스 `uploadGrant` 의 조직 범위 검사 제거 | 머니 | 2 / 5 |
| M8 | 머니 저장소 `getWalletListWithSearch` 의 조직 조건 제거 | 머니 | 2 / 5 |
| M9 | 머니 저장소 `getAgencyMemberReferenceRows` 의 조직 조건 제거 | 머니 | 1 / 5 |

9 변이 전부 RED. 복원 뒤 서버 워킹트리는 두 미추적 파일만 남았다 (`TREE_AFTER`).

### 게이트 음성 실측 (R2 로 바뀐 로직 · 원본 대조용 사본)

사본은 저장소 밖 git 디렉터리 아래에 두었고, 원본 (저장소 안 측정 디렉터리) 은 건드리지 않았다. 음성 뒤 사본은 지웠다. 각 검사는 원본에서 PASS 를 먼저 확인했다.

| 검사 | 사본에 넣은 변형 | 결과 |
|---|---|---|
| #11 | 사본 디렉터리에 `-e MYSQL_PWD=…` 를 싣는 스크립트 한 개 추가 | FAIL (「스크립트가 비밀 값을 명령행에 싣는다」) · 원본 PASS |
| #20 | 사본 `owner-check` 의 `AOX AO6` (corp-A) 행 `code=403` 을 `code=400` 으로 | FAIL (「AO 교차 쓰기가 403 이 아니거나 …」) · 원본 PASS |
| #21 | 사본 상태 줄의 `money-scope-org` 판본 sha 를 0 으로 | FAIL (「흡수 시험 파일 판본이 기록과 다르다」) · 원본 PASS |
| #23 | 사본 `replay-cells` 의 `SRV_TREE` 줄 한 개 변경 (아래 probe 로 실행) | FAIL (「SRV_TREE 불일치」) · 원본 PASS (probe) |

probe 는 게이트 본문 사본이다. 서버 워킹트리 검사 세 곳에서 흡수 시험 두 파일을 뺐을 뿐 다른 줄은 같다. 저장소 안에 잠깐 두고 실행한 뒤 지웠다.

### 커밋 전 검사 (워킹트리 · 이 단계)

정적 검사:
- 서버 tsc (`tsconfig.json`, `--noEmit --incremental false`): 오류 23 건 = 기준선 23. 두 시험 파일은 프로그램 파일 목록에 들어 있고 (`--listFiles`), 오류는 0 건이다. 서버 `tsconfig.json` 에는 `include` 가 없어 저장소 전체를 보므로 시험 파일도 검사 대상이다.
- `ESLINT_USE_FLAT_CONFIG=false` eslint (두 파일): rc 0, 문제 0 (구형 설정 경고만).
- prettier --check (두 파일): rc 0 (「All matched files use Prettier code style!」).

게이트 본문 (`bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`, 커밋 전): `FAILS=3`, rc 1.
- #1 FAIL: 서버 워킹트리 변경 (미추적 두 파일). 어드민 · 사이트 워킹트리는 따로 확인했다 (status 0 줄). 검사는 서버에서 멈춰 나머지 두 저장소는 함수 안에서 돌지 않았다.
- #5 FAIL: 서버 워킹트리에 untracked 파일 (같은 이유). 상수 파일 · 메뉴 스위치 파일은 HEAD 와 바이트 같다 (앞의 두 대조는 통과).
- #23 FAIL: 서버 워킹트리가 비어 있지 않다 (같은 이유). 이 검사도 거기서 멈춘다.
- 세 검사의 붉음이 두 미추적 파일 말고 다른 이유가 없음을 확인하려고 probe (서버 워킹트리 검사에서 흡수 시험 두 파일만 뺀 사본) 로 #1 · #5 · #23 을 다시 돌렸다. 세 검사 모두 PASS (rc 0). #23 의 트리 대조는 수동으로도 맞춰 봤다: HEAD 에서 두 파일을 뺀 트리가 `5e740146` 이고, 측정 산출 16 개의 `SRV_TREE` · `SRV_TREE_END` 가 이 값과 같다.
- 나머지 검사 #2–#4 · #6–#22 · #24–#27 은 PASS.

### 격리 itspec · 회귀 jest 재기록 (메인 실행 · 이 단계에서 다시 돌리지 않음)

- 격리 itspec (`itspec-m9e.json`, 기록 18:05): 14 suite · 133 시험 · 실패 0 · 보류 0 · 실행 오류 suite 0. 상태 파일 `itspec-state-m9e.txt`: `SIBLING_FIXTURE=present`, `FILES=14`, `SRV_TREE` = `SRV_TREE_END` = `5e740146`, 워킹트리 청결.
- 회귀 jest (`jest-m9e.json`, 기록 18:07): 245 suite · 2860 시험 · 실패 35 · 보류 23. 실행 오류 suite 3 개는 suite 수준 실패 행 3 개와 같고, 실패 집합은 m9d 기준선과 같다 (검사 #22 PASS).
- `absorb-itspec-m9e.json` (18:00) 은 검사가 읽지 않는다. 판본 바인딩은 상태 줄 (검사 #21) 이 맡는다.

### 사고: 끊긴 변이 실행과 메인 복구

- 경위: R2 흡수 에이전트가 끊긴 뒤 변이 실행이 `order-detail-admin.controller.ts` 에 조직 범위 조건 제거 변이를 남겼다 (변이 M2 와 같은 파일 · 같은 줄 계열).
- 원인: `mutation-m9e.sh` 는 변이마다 원본을 `cp -p` 로 되돌리는데, 복원이 정상 경로에만 있고 trap 이 없다. 실행이 중간에 끊기면 치환본이 워킹트리에 남는다.
- 메인 복구: HEAD 로 되돌렸다. 추적 소스 청결을 확인했다 (`git -C insuniverse-server diff --quiet HEAD -- apps/api/src` = 0, 이 단계 끝에서 다시 확인).
- 이번 단계에서 `mutation-m9e.sh` 는 고치지 않았다. 백로그 신규 MEDIUM 행 (`mutation-m9e.sh`, 2026-10-26) 으로 남겼다. 다음 변이 실행 전에 복원 trap 과 실행 전 추적 소스 청결 확인이 필요하다.
- 측정 산출 (`mutation-m9e.txt`) 은 메인이 전체 실행으로 다시 만들었다 (M1–M9 RED · `TREE_AFTER` 청결). 끊긴 실행의 중간 결과는 쓰지 않았다.

### 백로그 · PRD 추가분 (추가만 · 기존 행 불변)

- 백로그 2061–2066 (6 행): 2037 정정 (HIGH · 메인 판정 대기), 주문상세 조직 범위 짝 (2054 · R2), 지급 엑셀 조직 범위 짝 (2052 · R2), 머니 목록 짝 (2053 · R2), 남은 조직 목록 장면 (2047 · R2 · MEDIUM), 변이 스크립트 복원 trap (MEDIUM · 사고).
- 2037 정정 행의 판정: 1974 의 RESOLVED-IN 은 검사 #20 에 기댄다. R2 가 #20 을 고쳤지만 목록 신원 집합 고정은 그대로 열려 있다. 상태 열은 바꾸지 않았다. 검사 #24 의 `expect_status` 가 1974 를 검사 #20 통과 여부로 정하므로, 상태만 바꾸면 #24 가 붉어진다. 바꾸려면 메인이 PARTIAL 하향과 #24 의 `expect_status` 변경을 함께 정한다.
- PRD 9e 절에 R2 한 줄을 넣었다 (행 9 · 상태 열 불변).

## 커밋 뒤 할 일

1. 서버 저장소에 두 파일만 커밋한다 (`apps/api/src/modules/money/money-scope-org.itspec.ts`, `apps/api/src/modules/order/order-detail/order-detail-owner-scope.itspec.ts`). 커밋 전에 `git -C insuniverse-server diff --cached --stat` 가 두 파일만 보여야 한다. 헤더는 서버 로그 형식을 따른다 (예: `test(admin): 조직 범위 격리 시험을 더한다`). 커밋 메시지는 이 단계에서 만들지 않았다.
2. 측정 산출은 다시 쓰지 않는다. 검사 #23 은 HEAD 에서 두 파일을 뺀 트리를 본다. 커밋이 두 파일만 더했다면 그 트리는 `5e740146` 그대로이고, 측정 산출 16 개 · `on-build-m9e.txt` · `itspec-state-m9e.txt` 는 그대로 맞다. 재기록이 필요한 경우는 커밋에 다른 변경이 섞였을 때뿐이다. 그때 #23 은 `SRV_TREE 불일치` 로 붉고, 측정을 다시 돌리는 것은 메인 판정이다.
3. 게이트를 다시 돈다: `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh`. 기대: `FAILS=0`, rc 0 (#1 · #5 · #23 이 녹색으로 돌아온다). 붉으면 2 번의 경우를 먼저 본다.
4. 부모 저장소 (`feat/admin-rbac`) 의 status 에 보이는 `insuniverse-server` 줄은 서버 쪽 미추적 두 파일 때문이다. 서버 커밋 뒤에는 부모 쪽 서브모듈 포인터 (`160000 3418d3a0`) 를 바꿀지 메인이 정한다 (INV2: main 의 포인터는 동결값이다).
5. 부모 저장소 커밋 대상: 계획 (`.claude/PRPs/plans/admin-rbac-m9e.gates.sh` 포함) · 노트 · 백로그 · PRD · 측정 디렉터리 (`.claude/meta/2026-10-26-admin-rbac-m9e/`) · 판정 receipt. 보고서 (`.claude/PRPs/reports/admin-rbac-m9e-report.md`) 는 R2 를 반영하지 않았다 (R1 기준). 필요하면 메인이 고친다.
6. receipt (`.claude/receipts/mccp-implement-codex/admin-rbac-m9e.json`) 의 restamp 는 커밋 뒤 몫이다. 상태 파일의 `escalate` 판단도 메인 몫이다.
7. 미해결 그대로 넘긴다: 2037 상태 판정 (메인), 목록 신원 집합 고정 (2050 · 2032), AO15 측정 (2010 PARTIAL), 소비자 MOK 1 흐름 (사용자 몫), 변이 스크립트 복원 trap (백로그 신규 행).

## 마감 판정 (메인 실행 · 2026-10-26)

위 「커밋 뒤 할 일」 과 「미해결」 목록을 이 절이 정리한다. 앞 절의 판정 중 이 절이 고친 것은 2 번 (행 2037) 과 6 번 (restamp) 이다.

1. 서버 커밋 (「커밋 뒤 할 일」 1): `e6343c1c test(money): …` (money-scope-org.itspec.ts) · `bf0c1db5 test(order): …` (order-detail-owner-scope.itspec.ts). 서버 워킹트리 청결, MERGE_HEAD 없음. 커밋 뒤 게이트 `bash .claude/PRPs/plans/admin-rbac-m9e.gates.sh` → `FAILS=0` (#1–#27 PASS, 로그는 저장소 git 디렉터리 `mccp/tmp/gates-m9e-after-srv.log`). 첫 실행은 #8 이 붉었다: 새 백로그 행이 「원 2066」 을 인용했는데 기준 백로그에 2066 행이 없다 (이 행은 구현 중 추가됐다). 인용 표기를 「행 2066」 으로 고쳐 통과시켰다. 이 고침은 검사 기준을 바꾼 것이 아니다.
2. 행 2037 (원 1974) 판정: RESOLVED-IN → PARTIAL 하향. 근거: 검사 #20 은 목록과 AO 행의 기대 신원 집합을 고정하지 않는다 (구현 R1 · 계획 R3 · 구현 R3 의 HIGH 세 번). 원자료 `owner-check-m9e.txt` 에서 목록 행마다 잡힌 신원은 I1 · I5 (두 행) 가 여섯 명 전부, I7 은 기업 두 명과 영업 두 명, I4 · I6 은 영업 두 명뿐이다. 이 차이가 계획상 기대인지 측정 누락인지는 기계로 확인되지 않는다. 그래서 「전수」 를 증명으로 쓰지 않는다. 검사 #24 의 1974 기대값은 `expect_status` 에서 PARTIAL 로 고정했다 (c20 결과와 무관). 검사 #20 자체는 바꾸지 않았다.
3. 행 2066 (변이 스크립트 복원): 해소. `mutation-m9e.sh` 에 복원 trap 이 있다 (`CUR_TARGET` · `CUR_BK`, INT · TERM · HUP → `exit 130` → EXIT 에서 백업 복원, `bash -n` 통과). 메인이 자가 확인을 통과시켰다고 기록했고, 이 단계에서 변이를 다시 돌리지 않았다. SIGKILL 은 trap 으로 못 잡는다. 실행 전 추적 소스 청결 확인은 스크립트에 넣지 않았다. 백로그에 손 확인 절차로 남겼다 (해소 행 · 자동화는 다음 계획).
4. 행 9 (PRD): in-progress 유지 (상태 열 · 문구 불변). 격리로 할 수 있는데 이번에 하지 않은 항목이 남아 complete 로 올리지 않는다: 머니 승인 소비자 판정 재실행 (백로그 2055) · 남은 목록 장면 7 개의 조직 범위 시험 (2065) · 목록 기대 신원 집합 고정 (2050 · 2032) · AO 교차 측정 확장 (2056). 사용자 몫으로만 남은 항목은 (미검증) 으로 PRD 9e 절에 적었다: 복제본 1회 재생 · 스위치 ON 커밋 · R2 · R7 · R19 · would-deny 판단 표 운영본 · OFF 영업 교차 노출 28 셀 수용 · MOK 외부 키 · FCM 등 송신 증명 · 발송 잔류 담당 · 동시 판정 상한 부하 실측.
5. 「receipt 없이 구현」: notes 와 backlog 를 확인했다. 「사용자 판단 필요」 로 미해소인 행은 없다 (m9d 행 2003 은 RESOLVED-IN 이다). 이 마일스톤의 계획 게이트 skip-receipt 는 `verify-user-directive.sh` exit 0 으로 확인했다 (세션 기록 2026-10-19T02:21:22.590Z · type=user · userType=external). 리뷰가 「receipt 없이 진행」 을 올리면 「사용자 지시 확인 — verify-user-directive.sh」 로 기각한다.
6. 구현 receipt 커밋: 앞 「커밋 뒤 할 일」 6 의 「커밋 뒤 몫」 판단은 틀렸다. `prp-implement.md` 의 3.6 · 3.7 restamp 는 design 결과가 있을 때만 연다 (3.7 은 grounding 결과 파일이 있어야 restamp). 이 마일스톤은 design 신호 0 · `design_grounding_captured=false` 라 restamp 가 없다. 그래서 receipt 는 부모 커밋에 함께 싣는다 (E4 가드가 막는 것은 restamp 전 커밋이다).
7. 보고서 (`.claude/PRPs/reports/admin-rbac-m9e-report.md`): R1 기준이던 것을 최종 상태 (R2 · R3 · 마감) 로 갱신했다.
8. 포맷: 추적 마크다운 3 개 (백로그 · PRD · notes) 는 `git show HEAD:<f> | prettier --stdin-filepath <f>` 와 바이트가 다르다. 전부 내용 변경이다 (추가 행 · 추가 절). `gates.sh` 는 셸 스크립트라 prettier 대상이 아니다 (검사 한 줄 변경). 이 마일스톤의 기록과 대조해 출처를 확인했다. 포맷 전용 변경은 없으므로 `style` 커밋은 없다.

## 최종 리뷰 (R1 · 2026-10-26 · fable · codex)

루프 상태: 라운드 1 of 3. HIGH 흡수 0 · HIGH 미해소 0 · CRITICAL 미해소 0. 판정은 지적마다 한 행이다. 「부분」 은 일부만 고치고 나머지를 백로그로 넘긴 것이다. 반사실은 수정 전후 결과가 뒤집히는지를 본 것이다.

| 라운드 | 리뷰어 | 심각도 | 지적 | 판정 | 증거 | 층위 |
|---|---|---|---|---|---|---|
| R1 | fable | MEDIUM | 교차 「other 84」 가 공허 41 · 판정 가능 4 · 줄 누락 10 을 섞고, 커밋된 원자료가 답하는 것을 「미확정」 으로 적었다 | 부분 흡수. 84 를 공허 41 · I2 영업 10 (술어 98 줄 밖) · E0401 10 · 사유 없는 403 17 · I5 영업 2 · 전이 4 로 분해해 백로그 2008 정정 행 · PRD 9e 정정 bullet 에 옮겼다. 층 규칙 (사전 정의) 은 바꾸지 않았다 — 바꾸면 측정 뒤 규칙 변경이라 Goodhart 다. 술어 98 줄 추가 · 공허 셀 분리 집계는 다음 계획으로 넘겼다 | `replay-cells-m9e.tsv` 층 열 (other 84) · `raw-flip-20261026T164845-abbabce7.tsv` (I7 기업 403/- · I8 영업 404/-) · 백로그 정정 행 | 2 |
| R1 | fable | MEDIUM | 검사 #16 의 예외 신호 표가 스케줄러 두 흐름 (대상 0 명 · 건너뜀) 을 PASS 로 받고, 「8 흐름」 집계 (행 2039 · 2040 · PRD 9e) 가 발송 · 머니 승인 성공 분기를 증명된 것처럼 읽히게 했다 | 부분 흡수. 검사 #16 기대 문장에 부분 증명임을 적었고, 집계를 정상 완료 5 · 가드 도달 1 · 발송 미도달 2 · 미증명 1 로 정정한 백로그 행 · PRD bullet 을 추가했다. 게이트 판정 자체는 바꾸지 않았다 — 두 흐름을 UNPROVEN 으로 내리면 #16 이 붉어 마감 게이트 FAILS=0 을 깨고, 그 판정은 발송 경로 재측정이 있어야 한다. 백로그 MEDIUM 행으로 넘김 | `consumer-regress-m9e.mjs:152-158` · `consumer-regress-m9e.txt` (job-row-claimed · reminder-removed-from-queue) · 검사 #16 기대 | 6 |
| R1 | fable | LOW | 검사 #24 의 1974 기대값이 상수 `echo PARTIAL` 이라 동어반복이다 (INV4 위반) | 기각. 1974 PARTIAL 은 마감 판정 (목록 신원 집합 미고정 · R1 · R3 HIGH 세 번) 이 정한 값이고, INV4 가 막는 것은 리터럴 기대 총수 · 행 수다. 증거 파생식으로 바꾸면 신원 집합을 고정하지 않은 채 RESOLVED-IN 이 나와 마감 판정과 반대가 된다. 상수는 행 2050 · 2032 가 흡수되는 다음 계획에서 파생식으로 바꾼다 | 노트 「마감 판정」 2 · 백로그 2050 · 2032 · 검사 #24 `expect_status` | 6 |
| R1 | fable | LOW | 행 2037 이 OFF 타 소유 22 를 인용하는데 산출은 타 소유 18 · 소유 키 부재 4 다 | 흡수. 행 2037 의 근거를 18 + 4 로 고쳤다 (상태 열은 그대로, 검사 #24 는 상태 열만 본다) | `owner-check-m9e.txt` (OFF_FOREIGN=18 · OFF_UNKNOWN=4) · 백로그 2037 | 2 |
| R1 | codex | MEDIUM | itspec 러너가 「미추적 두 파일」 을 요구해 커밋 뒤 클린 체크아웃에서 거부된다 (재현 명령 불가) | 흡수. 허용 술어를 「미추적이면 상태 줄 `?? 파일`, 추적이면 상태 줄 없음」 으로 바꿨다 (`git ls-files --error-unmatch`). 반사실: 현재 (추적) 상태에서 술어 출력 `[]` = 실제 `[]` (일치 yes). 미추적 분기는 코드 읽기로만 확인했다 (실행 안 함, 서버 워킹트리를 더럽히지 않으려고) | `itspec-run-m9e.sh` 22–26 행 · `want_status` 실행 확인 | 5 |
| R1 | codex | MEDIUM | 산출 생산자가 `HEAD^{tree}` (커밋 뒤 e565a1e5) 를 써서 검사 #23 (측정 트리 5e740146) 과 불일치한다 (커밋 뒤 재생 = SRV_TREE 불일치) | 흡수. 측정 트리 정의 `m9e_src_tree` 를 `_m9e-lib.sh` 로 올리고, 산출 생산자 7 개 (replay · consumer-regress · counterfactual · on-area-build · jest-rerun · jest-compare · run-alias) 가 모두 쓰게 했다. `itspec-run` 의 사본은 뺐다. 검사 #23 의 함수 본문은 같은 정의로 남겼다. 반사실: 현재 HEAD^{tree} = e565a1e5 ≠ 측정 트리 = 5e740146 (= 측정 산출의 SRV_TREE) | `_m9e-lib.sh` `m9e_src_tree` · 생산자 `m9e_src_tree` 호출 · 실행 확인 `SRC=5e740146…` | 5 |
| R1 | codex | MEDIUM | 검사 #22 가 실패 시험 집합만 비교해 보류 (pending) 시험을 잃어도 통과한다 (승인 권한 시험 보류 전환) | 흡수. 보류 (P 행) 를 실제 산출과 기준선 (m9d P 행) 양쪽 집합에 넣었다. 반사실: 통과 시험 하나를 보류로 바꾼 사본 → 검사 #22 FAIL (`실패 · 보류 집합 … 다르다`), 실제 산출 → PASS · `FAILS=0` | `admin-rbac-m9e.gates.sh` c22 · 반사실 두 실행 (사본 디렉터리는 지웠다) | 6 |

판정 요약: 흡수 6 (부분 2 포함) · 기각 1 · HIGH 0 · CRITICAL 0. 부분 흡수 두 건이 백로그에 MEDIUM 으로 남았다 (행 2008 정정 · 2007 · 1985 정정) 및 검사 #16 게이트 행 (MEDIUM). 이 단계는 측정을 다시 돌리지 않았다. 다시 잰 산출은 없다. 모든 산출의 SRV_TREE 는 그대로 5e740146 이다.
