# Implementation Report: 40-runtime-vendor-legal 마일스톤 5 — 약관·개인정보·감사 문서 3종

Plan: `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m5.plan.md`
Notes(리뷰 기록): `.claude/notes/insuniverse-docs-40-runtime-vendor-legal-m5.md`
Branch: `40-runtime-vendor-legal`

## Summary

`docs/07-legal/` 3문서를 `policy-doc@1` 로 신설하고, 그 판정을 지는 게이트 하네스
`#0`~`#15` 를 함께 세웠다. **이 PRD 에서 처음으로 `## 정책 (기획)` 절에 본문 정책이 들어간다** —
본문 12 · 부록 8 이다. 앞 네 창의 미판정은 전부 배포 환경이 정하는 값에 걸려 있었고 그 값이
저장소 밖이었는데, 법적 축의 핵심 사실은 저장 계층의 선언이라 저장소 안에서 확정되고 활성성도
앵커 blame 으로 판정된다.

status 는 저자가 고르지 않는다. 두 오라클의 곱이다 — `legal-surfaces.yaml` 의 `state` 가 어휘
자격을 정하고, 앵커 행 blame × `releases.yaml` `reachability.unreached` 가 활성성을 정한다.
검사 `#3`·`#5` 가 같은 유도기를 다시 돌려 문서 값과 대조한다.

## Assessment vs Reality

| Metric | Predicted (Plan) | Actual |
|---|---|---|
| Complexity | Large | Large — 계획서 Task 8개 전건 수행 |
| 정책 수 | 축 2 선언 20건 | 20건 (01=11 · 02=4 · 03=5) |
| 본문 / 부록 | 각 1건 이상(하한만) | 본문 12 · 부록 8 |
| Files Changed | 6 | 6 (신규 4 · 수정 2) |
| 검사 수 | `#0`~`#15` 16종 | 16종 + `derive` + `preflight` |

## Tasks Completed

| # | Task | Status | Notes |
|---|---|---|---|
| 0 | 게이트 하네스를 문서보다 먼저 세운다 | 완료 | `preflight` 로 문서 부재 시점 전건 non-zero 확인. 유도기 6종 구현 |
| 1 | 문서 3종 골격과 정책 프론트매터 | 완료 | evidence 등급을 앵커에서 **유도**해 `#2` 가 문다 |
| 2 | 표면 대조표 3건과 status 전사 | 완료 | `derive` 출력을 전사. `#3`·`#5` 가 재유도로 대조 |
| 3 | 01 판 관리 산문 | 완료 | 운영 사실과 운영 미도달 근거를 정책 단위로 갈랐다 |
| 4 | 02 마스킹 규칙과 통과 경로 | 완료 | 규칙표 전건이 동결 트리 재파싱 유도값 |
| 5 | 03 인덱스 사각과 기록 소재 | 완료 | 형제 스캔 재현 + 확장 우주 4부류 |
| 6 | 본문·부록 배치와 미판정 어휘 결박 | 완료 | 격리 사유가 문서마다 다르다 — 계획서 예측과 실측 배분이 갈렸다(notes S9) |
| 7 | 연혁 4열 3건과 나머지 세 절 | 완료 | 연혁 01=7 · 02=9 · 03=6 행 |
| 8 | 전 게이트 재실행 · PRD 행 갱신 · 이월 | 완료 | 이월 10건을 backlog **표 구간**에 |

## Validation Results

| Level | Status | Notes |
|---|---|---|
| Static Analysis | 통과 | `lint-docs.mjs --strict-refs` 11개 파일(신설 3 · 선행 8) 위반 0 — 검사 `#1` |
| Unit Tests | 해당 없음 | 산출물이 Markdown 문서와 게이트 하네스다. 하네스 자신의 시험은 `#11`(자기 프로브)과 음성 케이스 16종이 진다 |
| Build | 해당 없음 | 저장소 루트에 패키지 매니저 선언이 없다(Phase 0 DETECT 실측) |
| Integration | 해당 없음 | 서버 기동 대상 없음 |
| Edge Cases | 통과 | 음성 케이스 16종(주입 12 · 자연 발생 4)을 실행해 전건 exit 1 과 진단 문면을 확인 — notes §음성 케이스 기록. 실행 후 `git status --porcelain` 잔존 0 |

### 게이트 실행 결과

`node .claude/plans/insuniverse-docs-40-runtime-vendor-legal-m5.gates.mjs all` — **커밋
`43d1caf` 트리에서 완주**(RC=0 · 워킹트리 클린). 이 창은 `all` 을 세 번 걸었고 봉인 근거는
이 세 번째다:

| 회차 | 트리 상태 | 결과 | 근거로 쓰는가 |
|---|---|---|---|
| 1차 | `1e46815` — 실행 중 산문 정정이 들어갔다 | `16/16` | **아니오.** 문서 검사가 정정 전 문면을 읽었는지 후를 읽었는지 귀속되지 않는다 |
| 2차 | 정정 직후 | 배경 실행이 죽어 출력 0바이트 | 아니오 |
| 3차 | `43d1caf` — 정정본 커밋 후 | `16/16` · RC=0 | **예** |

```
#0a ok 선행 하네스 완주 — M1 15/15 · M2 13/13 · M3 14/14 · M4 16/16
#0b ok 선행 8문서 워킹트리 변경 0
#0c ok 동결 3트리 포인터 전건이 manifest repos[].head 이고 상위 체크아웃에서 commit 으로 열린다
#0 ok surface_traceability 키 7 == 후보 인덱스 axis:legal 행 7
OK — 11개 파일, 위반 0 (--strict-refs)
#1 ok 11개 파일 (신설 3 · 선행 8) 위반 0 (--strict-refs)
#2a ok 문서별 정책 수 01=11 02=4 03=5 · 합 20 · id 중복 0 · 접두 충돌 0 · 인덱스 리더가 읽은 키 67개 · 건너뛴 형제 키 91개
#2b ok 계획서 축 2 표 재파싱 20행 == 하네스 상수 표 20행 (4열 전건 일치)
#2c ok 선행 8문서 id 119종과 교집합 0
#2 ok evidence 등급 규칙 일치 20/20 · 전건 2개 이상 · 종류 분해 schema 11 · guard 7 · constant 1 · site-text 1
#2 앵커 보정: 2건 — terms:revocation-record += insuniverse-server/drizzle/schema.ts:1773 · terms:reconsent-absent += insuniverse-server/apps/api/src/modules/agency/terms/terms.service.ts:99 (계획서 표 앵커가 하중 토큰 없는 행을 가리켜 실 하중 행을 더했다. 계획서 문면은 plan_hash 앵커라 고치지 않고 여기 계수로 낸다)
#3a ok status 유도 일치 20/20 (D1 3 · D2 1 · D3 12 · D4 4)
#3b ok -unknown 정책 unknown_reason 보유 4/4 · staged-only 정책 limitations 보유 4/4
#3c ok 본문 12건 ≥1 · 부록 8건 ≥1 (하한만 본다 — 상한·비율은 보지 않는다)
#3 ok 배치 == placement 20/20
#4a ok 표 3건 헤더 6열 문자열 일치 · 행수 == 문서 정책 수 (01 11 · 02 4 · 03 5)
#4 ok 키 열 합집합 7 == surface_traceability 키 7 · 쌍별 교집합 0 · 03 은 인덱스 키 0건(감사 축)
#5a ok blame 대상 앵커 32건 · SHA 17종 · 인덱스 실재 17/17
#5b ok unreached 집합 server 64 · site 33 · admin 53 재열거 · 도달성 분해 reached 15 unreached 4 mixed 1
#5 ok 재유도 == 대조표 6열 전건 20/20 (판정식은 releases.yaml 머리말 그대로 · 미인덱스 SHA 는 미도달로 접지 않는다)
#6a ok 양성 프로브 수탁 25회 / 7파일 == 인덱스 (파일 집합 양방향 일치)
#6b ok 재동의 0회 / 0파일 == 인덱스 basis not-observed
#6 ok 철회 분류 14/21/5 == 인덱스 · rule_sha256 일치 (consent-over-business)
#7a ok 재파싱 함수 5종 · 분기 수 합 10
#7b ok 재파싱 == legal-surfaces masking.canonical.rules 5/5 (분기 수 · 출력 형태 · 앵커 3축)
#7 ok 문서 표 5행 == 재파싱 5행 · 표 셀 세로선 치환 되돌림 1행 (마크다운 표 파서가 세로선에서 쪼개므로 문서는 이중 세로선 문자로 싣는다)
#7c ok 구현 축 9종(인덱스 재열거) · 소비 축 9종(동결 트리 재열거) · 교집합 0 · 두 집합 전건이 '## 정본과 병렬 구현' 절에 경로로 실렸다
#8a ok anchor tokens 34 / bad 0 (server 32 · site 1 · admin 1)
#8 ok 하중 앵커 21건 기대 토큰 일치 (행 번호만 보면 파일이 밀렸을 때 엉뚱한 행이 통과한다)
#9a ok 3문서 연혁 행 01=7 02=9 03=6 · 행 0개 문서 없음
#9 ok SHA 전건 커밋 인덱스 실재 22/22 · 전건이 그 문서 앵커 경로 접촉
#10 ok 문서별 본문∪부록 == 강제 지점 == 적용 범위 == 예외·폴백 == 프론트매터 (01 11 · 02 4 · 03 5) · 적용 범위 표 빈 칸 0건
#11a ok --help rc=0 · 서브커맨드 목록 출력
#11b ok 알 수 없는 서브커맨드가 이름을 담고 exit 1
#11c ok 의존 지정자 5건 전건 node 빌트인 또는 doc-kit lint (UI10)
#12a ok 정본 표기 유도: glossary 용어 요청 표에서 T-02 행 1건 · 정본 '동의 철회'
#12 ok 3문서 단독 표기 0건 · 전개형 13회 · 인용(펜스·코드 스팬) 제외 3회 (단독 판별은 직전 3자가 '동의 ' 인지로 한다)
#13a ok 코퍼스 sha256 16종 재열거 · 문서 등장 0종 전건 집합 안 (집합 밖 sha256 0건)
#13b ok 코퍼스 n-gram 표본 146개 (파일 14 · 길이 12 · 보폭 401 · 파일당 상한 24) · 문서 등장 0건
#13c ok constants.yaml 이름 54종 재열거 · 등장 0건 (UI16 — 상수 정의는 30-contracts-limits 소유)
#13 ok
#13 사각: (b) 는 **표본이고 전수가 아니다** — 보폭 401자마다 길이 12 한글 구간만 뽑아 파일당 24개까지 받으므로 표본 사이 구간의 문구는 이 검사가 보지 못한다. (a) 는 sha256 형태 토큰만 보고 (c) 는 이름만 보므로 값을 다른 표기로 옮겨 적은 것도 보지 못한다
#14a ok 형제 어휘 3종 재스캔 0파일 — 30-admin 관측 재현 (범위 apps/api/src/modules)
#14b ok 확장 우주 유도: 전용 감사 기록 0종 · 행위자 컬럼 보유 표 9종 · 운영 로그 표 5종 · 업무 이력 표 7종 (행위자 컬럼 보유 표에서 tn_admin 은 제외한다 — 그 adm_id 는 자기 기본키다)
#14 ok 부류 4종 전건이 03-audit 기록 부류 표에 행으로 있다 · 차집합 0 (양방향) · 계수 2열 재유도 일치
#15a ok [커밋 후] PRD 워킹트리 diff 0건 — 갱신 여부는 물지 않고 절 범위(194~208) 안의 해당 행 내용을 직접 문다
#15b ok 변경 행이 5행 하나 · Plan 셀 == 이 계획서 경로 · Status == in-progress · complete 아님
#15 ok (모드 커밋 후)
all ok 16/16 통과
```

### Design Grounding (v1.18.22)

Design Grounding: N/A (no design trigger) — `impeccable-detect.js` 가 `design_signal=false`
(`reason: no-signal`)를 냈고 2.5.5c 캡처가 일어나지 않았다. Phase 3.6 · 3.7 은 no-op 이다.
receipt 에 `impeccable_silent_skip=true` · `impeccable_silent_skip_reason="no-signal"` 로 기록.

## Files Changed

| File | Action | Lines |
|---|---|---|
| `docs/07-legal/01-terms-and-agreements.md` | CREATED | +397 |
| `docs/07-legal/02-privacy-and-masking.md` | CREATED | +283 |
| `docs/07-legal/03-audit.md` | CREATED | +249 |
| `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m5.gates.mjs` | CREATED | +1906 |
| `.claude/prds/insuniverse-docs-40-runtime-vendor-legal.prd.md` | UPDATED | +1 / -1 (Delivery Milestones 5행) |
| `.claude/plans/codex-findings-backlog.md` | UPDATED | +10 (표 구간) |

계획서 `## Files to Change` 밖 산출물 2종: `.claude/notes/…-m5.md`(구현 리뷰 기록 — 계획서 본문
편집 금지의 귀결)와 이 리포트. 둘 다 mccp 워크플로 산출물이며 INV3 이 그 부류를 배타 소유 경로
대상에서 뺀다.

## Deviations from Plan

| # | 계획서 문면 | 실측 | 처리 |
|---|---|---|---|
| D1 | 관측 8 — `th_*` 7종 중 행위자 컬럼 3종 | 2종 (`th_money_corp` · `th_money_payment_cancel`) | 문서는 게이트 유도값을 싣는다. 이월(`id=40m5-plan-prose-count-drift`) |
| D2 | 관측 11 — `terms_corpus.sources` site 항목 10종 | 13종 (front-const 4 · front-page 4 · static-html 5), 행수 13/13 일치 | 동 |
| D3 | 축 2 앵커 `schema.ts 1772행`(동의 철회 기록) | 그 행은 소비 시각 칸. 동의 철회 시각 칸은 1773행 | 프론트매터 앵커에 하중 행을 **더했다**. `planCells` 는 원문 유지 → `#2b` 보존. `#2 앵커 보정:` 줄이 매 실행 이름으로 낸다 |
| D4 | 축 2 앵커 `terms.service.ts 98행` | 빈 행 | 동 (`:99` 추가) |
| D5 | Task 6 — 01 운영 미도달 · 02 데이터 의존 · 03 스케줄러 활성 미판정 | 01 이 두 사유를 함께 갖고(미도달 3 · 데이터 의존 3), 02 는 미도달 1, 03 은 스케줄러 1 | 부록 머리 산문이 **유도된 사유**를 적는다. 어느 게이트도 사유 배분을 검사하지 않으므로 notes 에 기록 |
| D6 | `#4` 음성 케이스 — 키 열 미대응으로 실패 | 행 삭제는 행수 대조(`#4a`)를 먼저 친다 | 둘 다 같은 검사의 팔이고 둘 다 exit 1. 관측된 대로 기록 |

## Issues Encountered

| # | 문제 | 해소 |
|---|---|---|
| I1 | `legal-surfaces.yaml` 제한 리더가 형제 블록의 같은 모양 목록을 앞 절 항목으로 삼켰다 | 절 스위치가 **선언 들여쓰기**를 함께 들게 고쳤다 |
| I2 | 빈 흐름형 리스트를 문자열로 읽어 **0건 관측이 2건으로 위조**됐다 | `#6b` 가 실측으로 잡았다. 그 키를 갈라 읽고 모르는 형태는 죽는다 |
| I3 | 흐름형 리스트 파서가 원소 안의 쉼표에서 쪼갰다 | `#7b` 가 잡았다. 인용부·괄호 깊이를 함께 보는 분해로 |
| I4 | 마크다운 표 셀이 세로선을 담을 수 없어 출력 형태 하나가 잘렸다 | 이중 세로선 문자로 싣고 게이트가 되돌려 대조. 치환 행 수를 `#7` 이 낸다 |
| I5 | `#12`(단독 표기 0건)와 `#4`(키 열 == 인덱스 키) 가 서로를 배제했다 | 계약 대상을 문서 **자신의 산문**으로 한정. 펜스·코드 스팬은 인용이므로 제외하고 제외량을 출력에 낸다 |
| I6 | **M4 하네스 `#15` 가 이 창의 미커밋 PRD 편집에 죽어 `#0a` 가 막혔다** | M4 하네스는 이 창 범위 밖이라 고치지 않는다. 해소는 순서 — PRD 편집을 커밋한 뒤 `all` 을 돌리면 두 하네스가 함께 커밋 후 모드로 간다. 이월(`id=40m5-m4-gate15-sibling-prd-edit`) · **M6 도 같은 자리에서 막힌다** |
| I7 | 산문 5자리가 **게이트가 보지 않는 축**에서 실측을 넘어섰다 (전칭 `전부`·`유일한`·`전수`, 계수 `셋`·`두 번`, 인과 `부수 효과로 보인다`) | 커밋 직후 자가 2패스에서 실측 대조로 전건 정정. notes §산문 정정 P1~P5. **이 축은 게이트로 닫히지 않는다** — 주장 단위를 문장으로 쪼개 좌표를 결박해야 하며 이 창의 범위가 아니다 |

## Codex / Security / Design 게이트

| 게이트 | 결과 | 근거 |
|---|---|---|
| Implement-Codex | `skipped` | `MCCP_CODEX_DISABLED=1` (env 정책). 래퍼가 `classification: disabled` 로 short-circuit. `converged` 가 아니므로 cross-gate dedupe 는 fail-closed 유지 |
| Security Reviewer | 트리거 미성립 | 산출물이 Markdown 3건 + 하네스 1건. 인증·암호·비밀값·입력 검증 어느 표면도 구현하지 않는다. `security_skipped` 플래그를 세우지 않았다 |
| impeccable | silent-skip | `design_signal=false` · `reason: no-signal`. receipt 에 `impeccable_silent_skip=true` |

## Next Steps

- [ ] `/mccp:pr` — receipt 체인은 `ok: true` (경고 1건: impeccable silent-skip, 비차단)
- [ ] 마일스톤 종료는 `/mccp:milestone-close` 소유. PRD 5행은 `in-progress` 로 둔다 — `#15b` 가 `complete` 전이를 막는다
- [ ] 이월 10건은 `.claude/plans/codex-findings-backlog.md` 표 구간에. 그중 `40m5-m4-gate15-sibling-prd-edit` 는 **M6 착수 전에 판단이 필요하다**
