import sys
NOTES = '/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac/.claude/notes/admin-rbac-m6d.md'
META = '/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac/.claude/meta/2026-10-22-admin-rbac-m6d/README.md'
BK = '/home/madsc/work/insuniverse-temp/.worktrees/admin-rbac/.claude/plans/codex-findings-backlog.md'


def edit_lines(text, prefix, fn, must=1):
    lines = text.split('\n')
    hits = [i for i, l in enumerate(lines) if l.startswith(prefix)]
    if len(hits) != must:
        sys.exit('prefix %r hits %d' % (prefix[:40], len(hits)))
    for i in hits:
        fn(lines, i)
    return '\n'.join(lines)


def rep(text, old, new):
    n = text.count(old)
    if n != 1:
        sys.exit('count %d: %s' % (n, old[:60]))
    return text.replace(old, new)


s = open(NOTES, encoding='utf-8').read()

# 1) 마감 요약 상태
s = rep(s, '코드 리뷰 R1 흡수 완료 · complete 판정은', '코드 리뷰 R1 · R2 흡수 완료 · complete 판정은')

# 2) 게이트 결과: R2 실행 줄 추가
def add_after(lines, i):
    lines.insert(i + 1, '3) 코드 리뷰 R2 흡수 뒤 (실제 본문, 환경 변수 주입, 우회 사본 없음): `GATES_OK m6d` (rc 0, 약 67 초). 검사 #1 – #20 이 순서대로 섰다. 검사 #19 · #20 은 R2 에서 더했다. 반사실 (검사 #20 · 검사 #8 · 검사 #12) 은 임시 사본으로 따로 돌렸다 (「코드 리뷰」 R2 행).')
s = edit_lines(s, '2) 코드 리뷰 흡수 뒤 (실제 본문', add_after)

# 3) 검사별 결과 행
def set_line(prefix, new):
    def fn(lines, i):
        lines[i] = new
    return fn

s = edit_lines(s, '| #8 | 섰다 |', set_line('| #8 |', '| #8 | 섰다 | B19 잠금 순서 실물 통과 · 잠금 뒤 목적지 재판정 줄 (R2 추가) 포함 · 음성 fixture 세 건 (회원 잠금 삭제 · 회원·기업 역전 · 목적지 재판정 줄 삭제) 실패 · `getFcByMemId` 의 tx 인자 · `outer ? ` 패턴 |'))
s = edit_lines(s, '| #9 | 섰다 |', set_line('| #9 |', '| #9 | 섰다 | 회원 정보 수정 시험 10 건 전부 통과 · 실패 0 · 여섯 이름 (R2: 「잠금 뒤 목적지 재판정」 추가) 이 통과 집합에 있다 |'))
s = edit_lines(s, '| #12 | 섰다 (약한 증명) |', set_line('| #12 |', '| #12 | 섰다 (R2 재측정) | `# sha=` 첫 줄 두 개 (착수 `47fcc439` · 빌드 HEAD `47fcc439`) · 행 파일을 작업 트리 빌드로 다시 쟀다 (6336 행, 쓰기 행 12). 본문 차이는 `b19-edit` 의 readback 칸 두 줄뿐이다 (편차 16 · 의도된 변화). readback 을 뺀 본문 비교 0 줄 · 쓰기 행 이름 일곱 종 정확 일치 · write_rows 12. 한계: after_sha 는 커밋 전에 HEAD 와 자명하게 같다 (백로그 MEDIUM) |'))
def add_checks(lines, i):
    lines.insert(i + 1, '| #19 | 섰다 | m6c 회귀: `fc-bulk-scope` 14 건 · `agency-fc.race` 6 건 = 20 건 전부 통과 (R2 편입) · 나머지 m6c 검사는 이 게이트에 없다 (백로그 MEDIUM) |')
    lines.insert(i + 2, '| #20 | 섰다 | 짝 행 인용의 실재: 「검사 #K」 머리 · 「시험 「이름」」 의 jest 통과 · 「커밋 <hex>」 의 `git cat-file` (R2 추가) · 인용 행 11 건 · 실패 0 |')
s = edit_lines(s, '| #18 | 섰다 |', add_checks)

# 4) 편차 16: 응답 본문 주장 정정
s = rep(s, '응답 본문은 같다고 본다. 다만 행 파일 (`rows-after`) 은 이 수정 전 빌드라 재측정 전까지 `(미검증)` (백로그 MEDIUM).',
        'R2 에서 행 파일을 이 빌드로 다시 쟀다. 응답 상태와 대부분의 본문 해시는 같고, 차이는 `b19-edit` 의 readback 칸 하나뿐이다 (착수 빌드는 소속을 지웠고 이 빌드는 소속을 남긴다). 검사 #12 가 이 두 줄만 허용한다 (편차 23).')

# 5) 편차 17 전체 재작성 (사실 정정)
def rewrite17(lines, i):
    lines[i] = ('17) ORG 소속 해제 가드의 OFF 영향 (코드 리뷰 R1 흡수 · 기재, R2 정정): `isOrgManagerClear` 는 강제 상수와 무관하다. '
                '조직관리자가 `orgId` 를 빈 문자열 · `null` 로 보내면 HEAD 에서 200 (소속 해제) 이던 것이 403 이 된다 (정책 7). 「OFF 응답 불변」 의 예외다. '
                '시험 「ORG 빈 소속 해제 거부」 (문자열 · null) 와 파이프 형상 「\'\'」 시험이 이 경로를 본다. '
                'R2 정정: `0` · `false` 는 이 가드에 닿지 않는다. `UpdateFcDto` 의 `orgId` 는 `Member` 에서 가져온 `@ValidateIf(값이 null · undefined 가 아니면) @IsString()` 이라 전역 파이프가 HEAD 와 지금 모두 400 으로 막는다 (`fc.entity.ts` 의 orgId 데코레이터 · `fc.dto.ts` 의 `UpdateFcDto` 가 orgId 를 Pick 한다). 0 · false 는 시험할 대상이 아니다.')
s = edit_lines(s, '17) ORG 소속 해제 가드의 OFF 영향', rewrite17)

# 6) 편차 19: 증거 규칙 · 반사실 정정
def rewrite19(lines, i):
    lines[i] = ('19) 검사 #2 짝 행 증거 규칙 (코드 리뷰 R1 흡수 · R2 강화): 이 계획서의 RESOLVED-IN 짝 행은 「증거: 검사 #K」 와 「시험 「이름」」 · 「커밋 <hex>」 · 「· 게이트」 중 하나가 함께 있어야 짝으로 센다. '
                '다른 계획의 행과 REJECTED · (미검증) 행은 번호 인용만으로 짝이다 (과거 판정 기록). 계획 시점 짝 행 8 건 (1806 · 1808 · 1817 · 1819 · 1823–1826) 의 처리 칸 끝에 증거를 더했다. '
                '1820 짝 행은 주장이 부분이라 (미검증) 으로 낮췄다. '
                'R2 정정: 1840 · 1841 짝 행의 시험 이름 「tsc 서명 차집합 0 건」 · 「증거 없는 RESOLVED-IN 짝은 계수에서 뺀다」 는 실재하는 시험이 아니어서 「· 게이트」 증거로 바꿨다. 형식만으로는 위조가 통과하므로 실재는 검사 #20 이 본다. '
                '반사실 (R2 재측정, 백로그 사본 · 같은 계수 함수): 전 계획 짝 행의 증거를 지우면 계수 0 → 10. 1841 행의 증거 한 건만 지우면 계수 0 → 1. 시험 이름만 가짜로 바꾸면 계수는 0 그대로다 (이 경우는 검사 #20 이 붉다). 이전 주장 「3 → 2」 는 재현되지 않아 고쳤다.')
s = edit_lines(s, '19) 검사 #2 짝 행 증거 규칙', rewrite19)

# 7) 편차 23 · 24 추가 (22 뒤)
def add_deviations(lines, i):
    lines.insert(i + 1, '23) 코드 리뷰 R2 흡수 (게이트 · 시험 · 측정): '
                 '(a) 검사 #2 · #20: 짝 행 인용의 실재를 검사 #20 이 본다 (「검사 #K」 머리 · 「시험 「이름」」 의 jest 통과 · 「커밋」 의 `git cat-file`). 반사실: 1824 행 시험 이름을 「가짜 시험 이름」 으로 바꾼 본문 사본에서 검사 #2 는 0 (통과) · 검사 #20 은 실패 1 (실측). '
                 '(b) 검사 #8 · #9: 잠금 사슬에 「잠금 뒤 목적지 재판정」 줄 (`assertOrgWritableByAdmin(dto.orgId, admin, tx)`) 을 넣고 음성 fixture (그 줄 삭제) 를 더했다. 시험 「회원 정보 수정 잠금 뒤 목적지 재판정」 (잠금 전 범위 안인 SYNORGA1C 를 보유자가 범위 밖 경로로 옮기고 커밋 → 잠금 뒤 재판정이 403 · 소속 불변) 을 더했다 (10 건 전부 통과). 반사실: 그 줄을 지운 서비스 사본에서 이 시험이 붉다 (9 통과 · 1 실패 · 원본은 cmp 로 복구 확인). '
                 '(c) 검사 #12: 행 파일을 작업 트리 빌드로 다시 쟀다. 본문 차이는 `b19-edit` 의 readback 칸 한 개 (소속 해시) 뿐이고 편차 16 의 의도된 변화다. 검사 #12 는 차이 두 줄 · 그 이름이 `b19-edit` 인 것 · readback 을 뺀 본문 일치를 요구한다. 반사실: readback 만 바꾼 사본은 통과 · 다른 행의 상태를 바꾼 사본은 실패 (실측). 쓰기 행 이름은 `awk` 로 정확 일치 (org-delete 와 org-delete-leaf 를 구별). '
                 '(d) 검사 #19: m6c 시험 두 파일 (`fc-bulk-scope` 14 건 · `agency-fc.race` 6 건) 을 편입했다. m6c 게이트의 나머지 검사는 이 게이트에 없다 (계획 처리표 61행 문구는 백로그 MEDIUM 행으로 넘겼다). '
                 '(e) 검사 #12 의 음성은 「본문 한 줄 변경 → 실패」 가 아니라 위 (c) 의 두 반사실로 확인했다 (이전 음성 문구는 readback 차이를 못 다루었다).')
    lines.insert(i + 2, '24) 격리 스키마 확인 (계획 Task 5 · R2 흡수, LOW): 격리 DB (`m6d-iso-` 접두 컨테이너) 에 `schema-patch.sql` 의 네 문장을 적용한 뒤 `SHOW INDEX` 와 FK 를 확인했다. `tn_member` 의 `mem_org_id` (열 `org_id`) · `tn_agency_organization` 의 `idx_org_path` (열 `org_path`) · `idx_org_parent` (열 `org_parent_id`) 가 보였고, `information_schema.KEY_COLUMN_USAGE` 에 `fk_agency_org_parent` (`tn_agency_organization` → `tn_agency_organization`) 가 보였다. 적용 경로가 시험 셋업의 `applySchemaPatch` 와 같은 문장이고, 다섯 시험 파일이 이 적용 뒤 녹색이다. 시험 셋업 안의 SHOW INDEX 로그는 남기지 않았다 (`(미검증)`, 확인은 위 수동 적용 기준).')
s = edit_lines(s, '22) bcrypt 위치', add_deviations)

# 8) 코드 리뷰 표에 R2 행 추가 (R1 bcrypt 행 뒤)
R2 = [
'| R2 | 코드 리뷰 (게이트 실행) | MEDIUM | 검사 #2 의 증거 규칙이 「」 안 문자열의 존재만 봐서 위조 인용이 통과한다 (1840 · 1841 의 「tsc 서명 차집합 0 건」 · 「증거 없는 RESOLVED-IN 짝은 계수에서 뺀다」 는 실재하는 시험이 아니다) | 흡수 | 검사 #20 추가 (인용 시험 이름 · 커밋 · 검사 머리를 실재와 대조). 두 짝 행은 「· 게이트」 증거로 바꿨다 (편차 19 · 23 (a)). 반사실: 가짜 이름 사본에서 #2 통과 · #20 실패 1 (실측). 「3 → 2」 는 재현되지 않아 「0 → 1」 로 고쳤다 | 6 |',
'| R2 | 코드 리뷰 (게이트 실행) | MEDIUM | m6c 회귀 검사가 m6d 게이트 본문에 없다 (계획 처리표 61행의 「그대로 돈다」 가 게이트로 확인되지 않는다) | 부분 흡수 | 검사 #19 가 m6c 시험 두 파일 (20 건) 을 돈다 (편차 23 (d)). 나머지 m6c 검사 편입과 처리표 문구 축소는 계획 수정이라 백로그 MEDIUM 행 | 2 |',
'| R2 | 코드 리뷰 (서비스) | MEDIUM | B19 잠금 뒤 목적지 재판정 줄 (`fc-agency.service.ts` 의 `assertOrgWritableByAdmin(dto.orgId, admin, tx)`) 을 지워도 게이트 #1–#18 과 시험 9 건이 전부 통과한다 | 흡수 | 시험 「회원 정보 수정 잠금 뒤 목적지 재판정」 추가 (반사실: 그 줄을 지운 사본에서 이 시험 붉음 · 9 통과 · 1 실패 · 복구 cmp). 검사 #8 의 잠금 사슬에 줄을 넣고 음성 fixture 를 더했다. 검사 #9 는 10 건 (편차 23 (b)) | 6 |',
'| R2 | 코드 리뷰 (receipt) | MEDIUM | implement receipt 가 `resolution.converged=true` 와 `codex_verdict=divergent` 를 함께 싣고, 원 Codex 판정 (approve) 과 어긋난다. 노트의 근거 「사용자 지시 converged 위장 금지」 도 잘못 인용됐다 | 기재 정정 (흡수) · 봉인 유지 | 봉인 근거를 메모 mccp-implement-codex-has-no-diff-to-review 의 dedupe 정책으로 바꾸고 원 판정과 봉인의 차이를 「Codex Implementation Review」 에 적었다. 봉인 파일은 mccp 변조 표지 규칙 (직접 편집 · 재생성 금지) 때문에 건드리지 않았다. 재봉인 여부는 최종 리뷰 판정 (백로그 MEDIUM 행) | 1 |',
'| R2 | 코드 리뷰 (측정) | MEDIUM | 응답 불변 행 파일이 수정 전 빌드라 현 코드를 대표하지 않는다. 재측정하면 `b19-edit` 의 readback 이 바뀌어 cmp 가 깨진다 | 흡수 | 작업 트리 빌드로 재측정 (6336 행 · 쓰기 행 12). 차이는 `b19-edit` readback 칸 한 개 (편차 16 · 의도된 변화). 검사 #12 가 이 차이만 허용한다 (편차 23 (c)). 반사실: readback 만 바꾼 사본 통과 · 다른 행 상태를 바꾼 사본 실패 (실측). 빌드 식별 (트리 해시) 은 넣지 않음 → 백로그 MEDIUM 행 | 6 |',
'| R2 | 코드 리뷰 (노트) | LOW | 계획 Task 5 의 SHOW INDEX 확인 결과가 노트와 메타 README 에 없다 | 흡수 | 격리 DB 에 `schema-patch.sql` 네 문장을 적용하고 SHOW INDEX · FK 를 확인해 편차 24 와 메타 README 에 적었다 | 2 |',
'| R2 | 코드 리뷰 (계획) | LOW | 계획 Files 표에 `isolated-probe.sh` · `identities-scope.json` 이 없고, probe 쓰기 행 이름이 계획의 `org-delete` 가 아니라 `org-delete-leaf` 를 더한다 | 부분 흡수 | 검사 #12 가 일곱 쓰기 행 이름을 정확 일치로 본다. Files 표 보정은 계획 수정이라 백로그 LOW 행 | 6 |',
'| R2 | 코드 리뷰 (노트) | LOW | 편차 17 이 `0` · `false` 도 HEAD 에서 200 이던 것이 403 이 된다고 적었다 (사실 아님) | 흡수 (정정) | `UpdateFcDto` 의 `orgId` 는 `@ValidateIf` + `@IsString()` 이라 `0` · `false` 는 전역 파이프에서 400 이다 (HEAD 와 지금 같다). 편차 17 을 고쳤다 | 1 |',
'| R2 | 코드 리뷰 (주석) | LOW | probe.mjs 주석이 `org-upload` 행을 400 이라 적었고 기록은 500 이다 | 흡수 (정정) | probe.mjs 주석 (198행) 을 500 으로 고쳤다. 기록 (`status=500`) 은 그대로이고 편차 2 와 맞는다 | 1 |',
]
def add_r2(lines, i):
    for k, row in enumerate(R2):
        lines.insert(i + 1 + k, row)
s = edit_lines(s, '| R1 | 코드 리뷰 | LOW | bcrypt', add_r2)

# 9) R2 집계 (끝에 추가)
s = s.rstrip('\n') + '\n\n- 라운드 2 (2/3 · 원 지적 8 건 · 중복 없음): 흡수 8 건 (부분 흡수 2 건: m6c 편입 범위 · 계획 Files 표 · 본문 변이 미검출은 R1 에서 이미 백로그). 기각 0 건. 이연 (백로그 행으로 넘김): m6c 나머지 검사 편입 · 계획 처리표 문구 · 행 파일 빌드 식별 · receipt 재봉인 판정 · 계획 Files 표 보정. HIGH · CRITICAL 지적은 이 라운드에 없다 (high_absorbed 0 · high_open 0 · critical_open 0).\n- R2 에서 바뀐 것: 게이트 본문 (검사 #2 · #8 · #9 · #12 · #19 · #20 · 증거 규칙), 시험 `fc-agency-edit-scope.itspec.ts` (목적지 재판정 시험 · 셋업 복구 한 줄), 서버 서비스 (없음 · 줄 삭제는 반사실 사본에서만), 백로그 (1840 · 1841 짝 행의 증거 · 아홉 행 추가), 노트 (본 절 · 편차 16 · 17 · 19 · 23 · 24 · 게이트 결과 3), 측정 (`rows-after-m6d.txt` 재측정 · `probe-summary-m6d.txt` 의 before_eq_after=no · expected_diff · `probe.mjs` 주석), 봉인 파일은 손대지 않았다.\n- 커밋하지 않았다.\n'
open(NOTES, 'w', encoding='utf-8').write(s)

# 10) Codex implementation review 의 receipt 근거
m = open(NOTES, encoding='utf-8').read()
old_start = '- implement receipt: `.claude/receipts/mccp-implement-codex/admin-rbac-m6d.json` · `codex_verdict=divergent` (봉인).'
i = m.index(old_start)
j = m.index('\n', i)
new_line = ('- implement receipt: `.claude/receipts/mccp-implement-codex/admin-rbac-m6d.json` · `codex_verdict=divergent` (봉인). '
            '봉인 근거는 메모 mccp-implement-codex-has-no-diff-to-review 의 dedupe 정책이다 (divergent 로 닫아야 `/mccp:pr` 의 PR-Codex 를 건너뛸 수 없다). '
            'R2 정정: 이 봉인은 Codex 원 판정 (approve · 지적 0) 과 다르다. 이 라운드의 지적 0 은 `resolution.converged=true` 가 기록하고, 교차 모델 봉인은 `codex_verdict` 가 기록한다. '
            '두 칸이 다른 값인 것은 봉인 정책의 결과이며, 재봉인 (converged 또는 divergent 유지) 은 최종 fable·codex 리뷰가 정한다. '
            '봉인 파일은 mccp 변조 표지 규칙 (직접 편집 · 재생성 금지) 때문에 손대지 않았다. 「사용자 지시 converged 위장 금지」 는 이 봉인의 근거가 아니다 (운용 정책 §5-4 는 cap 도달 · receipt 부재 상황의 규칙이다).')
m = m[:i] + new_line + m[j:]
open(NOTES, 'w', encoding='utf-8').write(m)

# 11) 메타 README
r = open(META, encoding='utf-8').read()
r = rep(r, '| `rows-before-m6d.txt` · `rows-after-m6d.txt` | 착수 빌드와 작업 트리 빌드의 응답 행 파일 (`cmp` 로 본문 일치, 검사 #12) |',
        '| `rows-before-m6d.txt` · `rows-after-m6d.txt` | 착수 빌드와 작업 트리 빌드의 응답 행 파일. R2 에서 작업 트리 빌드를 다시 쟀다. 본문 차이는 `b19-edit` 의 readback 칸 한 개뿐이다 (편차 16 · 검사 #12) |')
r = rep(r, '| `probe-summary-m6d.txt` | `before_sha` · `after_sha` · `write_rows` · `before_eq_after` 요약 |',
        '| `probe-summary-m6d.txt` | `before_sha` · `after_sha` · `write_rows` · `before_eq_after` (R2: `no`) · `expected_diff` 요약 |')
r = r.rstrip('\n') + '\n\n## R2 확인 기록\n\n- 응답 행 재측정: 작업 트리 빌드, 행 6336 · 쓰기 행 12. 착수 행 파일과의 차이는 `write b19-edit` 의 readback 칸 한 개 (소속 해시 · 편차 16). 검사 #12 는 이 차이만 허용한다.\n- 격리 스키마 (Task 5) 확인: `schema-patch.sql` 네 문장을 격리 DB 에 적용한 뒤 `mem_org_id` · `idx_org_path` · `idx_org_parent` 와 FK `fk_agency_org_parent` 가 보인다 (노트 편차 24).\n'
open(META, 'w', encoding='utf-8').write(r)

# 12) 백로그 append (HIGH 아님 · 이미 있는 항목은 다시 쓰지 않는다)
b = open(BK, encoding='utf-8').read()
b = b.rstrip('\n') + '\n' + '\n'.join([
 '2026-10-22 | MEDIUM | .claude/PRPs/plans/admin-rbac-m6d.plan.md | 처리표 (계획 61행) 의 「m6c 게이트의 전 검사 중 이 계획이 건드리지 않는 것은 그대로 돈다」 는 게이트가 확인하는 범위 (검사 #19 의 m6c 시험 두 파일, 20 건) 보다 넓다. m6c 의 나머지 검사와 다른 시험 파일은 이 게이트에 없다 — code-review R2 미해소 (부분 흡수), 근거 .claude/PRPs/plans/admin-rbac-m6d.gates.sh 검사 #19 · .claude/PRPs/plans/admin-rbac-m6d.plan.md:61 | 처리표 문구는 계획 수정이라 이번 라운드에서 고치지 않았다 · 다음 계획에서 편입 범위 또는 문구를 정한다',
 '2026-10-22 | MEDIUM | .claude/PRPs/plans/admin-rbac-m6d.plan.md | 응답 불변 행 파일 (rows-after) 의 빌드 식별 (작업 트리 해시 · 빌드 명령) 이 행 파일 머리에 없다. 이번에 재측정으로 빌드를 맞췄지만 「after_sha = HEAD」 는 커밋 전에 자명하게 참이다 — code-review R2 미해소 (부분 흡수), 근거 .claude/meta/2026-10-22-admin-rbac-m6d/rows-after-m6d.txt:1 · .claude/PRPs/plans/admin-rbac-m6d.gates.sh 검사 #12 | 행 파일 머리에 트리 해시를 남기는 형식 변경이 따른다 · 커밋 뒤 재측정과 함께 다음 계획에서 한다',
 '2026-10-22 | MEDIUM | .claude/PRPs/plans/admin-rbac-m6d.plan.md | implement receipt (.claude/receipts/mccp-implement-codex/admin-rbac-m6d.json) 가 codex_verdict=divergent 로 봉인됐고 Codex 원 판정은 approve (지적 0) 다. 봉인 근거는 dedupe 정책 메모 한 건이다 — code-review R2 미해소 (기재 정정으로 근거만 바로잡음), 근거 .claude/notes/admin-rbac-m6d.md 「Codex Implementation Review」 | 봉인 파일은 mccp 변조 표지 규칙으로 직접 고치지 않았다 · 재봉인 (converged 또는 divergent 유지) 은 최종 fable·codex 리뷰가 정한다',
 '2026-10-22 | LOW | .claude/PRPs/plans/admin-rbac-m6d.plan.md | 계획 Files 표 (plan.md:122 부근) 에 isolated-probe.sh · identities-scope.json 이 없고, probe 쓰기 행 이름으로 org-delete 외에 org-delete-leaf 가 쓰인다 (둘 다 기록) — code-review R2 미해소 (부분 흡수), 근거 .claude/meta/2026-10-22-admin-rbac-m6d/README.md 파일 표 | 검사 #12 는 일곱 이름을 정확 일치로 본다 · Files 표 보정은 계획 수정 대상이라 다음 계획에서 한다',
 '',
])
open(BK, 'w', encoding='utf-8').write(b)
print('notes+meta+backlog patched')
