# Implementation Report: 스케줄 · 벤더 · 법적 정책 문서 M3 — 벤더 응답 의미의 두 축 분리

- **Plan**: `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m3.plan.md` (제자리 유지)
- **Notes**: `.claude/notes/insuniverse-docs-40-runtime-vendor-legal-m3.md`
- **Branch**: `40-runtime-vendor-legal`
- **Gate**: `node .claude/plans/insuniverse-docs-40-runtime-vendor-legal-m3.gates.mjs all` → `all ok 14/14 통과`

## Summary

`docs/06-vendors/02-failure-semantics.md` 1건을 신설하고 그 문서를 검사하는 하네스
(`#0`~`#13`)를 문서보다 먼저 세웠다. 문서가 지는 것은 인덱스의 재서술이 아니라 **응답 의미의
두 축 분리**(treat 5벤더 / non-treat 3벤더)와 **그 의미가 화면에 도달하는가**의 판정이다.

온보딩 질문 두 개의 답이 문서 안에서 닫혔다.

- **Q17 — 장애를 자료없음으로 오처리하지 않으려면 무엇을 보아야 하는가**: `category` 이고
  `retryable` 이 아니다. 자료없음의 재시도성은 거짓이고 여러 실패도 거짓이라 재시도 신호로는
  둘이 갈리지 않는다.
- **Q40 — 결과가 갈리면 무엇을 채택하는가 · 자료없음과 장애가 사용자 화면에서 구별되는가**:
  전반부는 **첫 성공 채택**(폴백은 공급자측 장애와 예외에만, 사용자측 실패는 그대로 반환).
  후반부는 **구별되지 않는다** — 사용자 경로에 닿는 신호가 `retryable` 하나뿐이고 그 값이 두
  경우에 모두 거짓이다.

**후반부 판정은 문서의 주장이 아니라 게이트 `#6` 이 동결 3트리(server `4d798f55` · site
`767d1e44` · admin `fb001e1d`)에서 신호 7종의 소비 파일 수를 다시 세어 확정한 값이다.**

## Assessment vs Reality

| Metric | Predicted (Plan) | Actual |
|---|---|---|
| Complexity | Large | Large — 하네스 1,257행 · 문서 781행 (코드리뷰 흡수 후) |
| 정책 건수 | 25 (벤더×결과 24 + 라우팅 1) | 25 · 게이트 `#2b` 편차 0 |
| 본문 정책 | 0 (관측 4) | 0 · 게이트 `#3c` 가 자격 0건과의 항등을 확인 |
| 대조표 | 2개 (treat 5×3 / non-treat 3×3) | 15행 / 9행 · 게이트 `#4b` axis 집합 exact |
| Files Changed | CREATE 2 · UPDATE 2 | CREATE 2 · UPDATE 2 (+ 워크플로 산출물 — 아래 Deviations) |
| Confidence | — | 게이트 14/14 · 음성 케이스 20건 + 코드리뷰 반사실 7건 전건 rc=1 |

## Tasks Completed

| # | Task | Status | Notes |
|---|---|---|---|
| 0 | 게이트 하네스를 문서보다 먼저 세운다 | 완료 | 문서 부재 시점에 `check1`~`#10`·`#12`·`#13` 12건 전건 rc=1 확인 (notes §음성 케이스 (가)) |
| 1 | 문서 골격 + 정책 25건 프론트매터를 인덱스에서 유도 | 완료 | id·status·placement·anchors 전건 `derive` 출력에서 옮겼다. 손으로 만든 id 0건 |
| 2 | 두 축 대조표 분리 · 24칸 | 완료 | 게이트 `#4`·`#5`·`#12` 통과. 라우팅 행은 두 표 어디에도 없다 |
| 3 | 구별 신호 소비 사슬 + Q17·Q40 | 완료 | 7신호 · 21칸 재계수 일치 · 도달 3어휘 재계산 일치 |
| 4 | 부록 25건 격리 + 미판정 사유 결박 | 완료 | `unknown_reason` 25건 · pending 9건이 게이트 부재를 담는다 |
| 5 | 연혁 4열 | 완료 — 재작업 1회 | 첫 저작 7행 중 3행을 `#9` 가 반려. 앵커 접촉 커밋 65건에서 9행 재선정 |
| 6 | 강제 지점 · 적용 범위 · 예외·폴백 + lint | 완료 | `--strict-refs` 5문서 위반 0 |
| 7 | 전 게이트 재실행 · PRD · 이월 | 완료 | 이월 11행(코드리뷰 흡수 4행 포함)을 게이트 **앞에** 기록. PRD 는 3행 Status·Plan 셀만 |

## Validation Results

| Level | Status | Notes |
|---|---|---|
| Static Analysis | 통과 | `lint-docs.mjs --root . --strict-refs` 5문서 위반 0 (게이트 `#1`) · `node --check` 하네스 rc=0 |
| Unit Tests | 통과 | 하네스 자기검증 `#11` (양성 `--help` + 음성 rc + `normAnchor` 양성 4 · **음성 4** 케이스). 프로젝트 테스트 러너 없음 — `package.json` 부재 |
| Build | N/A | 문서 저장소. 빌드 산출물 없음 |
| Integration | 통과 | `#0` 이 M1 `all 15/15` · M2 `all 13/13` 회귀와 05-schedules 4문서 워킹트리 불변을 확인 |
| Edge Cases | 통과 | 음성 케이스 20건 + 코드리뷰 반사실 7건 전건 rc=1 · 문서 digest 복원 확인 (notes §음성 케이스 · §코드리뷰 라운드) |

### 게이트 전건 출력 (요약)

```
#0  M1 all 15/15 · M2 all 13/13 · 05-schedules 4문서 워킹트리 변경 0 · 벤더 8종×결과 3+라우팅 1 == 인덱스 25행
#1  doc-kit lint --strict-refs 통과 (5개 파일)
#2  인덱스 25행 = 24 + 1 · 문서 id 집합 편차 0
#3  status 25쌍 == 승계 16 + pending 유도 9 · placement 편차 0 · 본문 0건 == 자격 0건
#4  대조표 2개 · treat 5종 / non-treat 3종 exact · 행수 15 / 9
#5  24행 · 사각 재계산 일치 24 (없음 16 · 분류 부재 1 · 개념 부재 3 · 화면 미분기 4)
#6  7신호 편차 0 · 소비 표면 수치 없음 7 · 21칸 재계수 일치 (server 69 · site 28 · admin 42) · 도달 (사용자 경로 1 · 진단 표면만 2 · 미도달 4)
#7  unknown_reason 25건 전건 · pending 9건 게이트 부재 · assigned 16건 인덱스 사유 승계
#8  앵커 29건 해석 · 행 내용 대조 29 · 기대 집합 출처 인덱스 26 / 하네스 리터럴 3
#9  연혁 9행 · 인덱스 해석 9 · 앵커 경로 접촉 9
#10 헤더 == actors 5 + services 3 · 세 표 id 집합 편차 0 · 빈 칸 0 (200칸) · 값 재유도 일치 (적용 55 · — 145)
#11 --help 성공 · nosuchcheck rc=1 · 서브커맨드 17종 · normAnchor 8건 (양성 4 · 음성 4)
#12 격자 24칸 == 표 A∪B (미커버 0 · 잉여 0)
#13 코드값 리터럴 1건 전건 허용 목록 [CF-12103] 안
```

### Design Grounding

**N/A (no design trigger).** `impeccable-detect.js detect --mode implement` 가
`skill_available=true · design_signal=false · silent_skip=true (reason=no-signal)` 를 냈다.
렌더 표면이 없어 Phase 2.5.5c 캡처가 돌지 않았고 그 결과 Phase 3.6 · 3.7 은 아티팩트 부재로
no-op 이다. receipt 에 `impeccable_silent_skip=true` 로 기록했다.

## Files Changed

| File | Action | Lines |
|---|---|---|
| `docs/06-vendors/02-failure-semantics.md` | CREATED | +781 |
| `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m3.gates.mjs` | CREATED | +1257 |
| `.claude/plans/codex-findings-backlog.md` | UPDATED | +11 / -0 |
| `.claude/prds/insuniverse-docs-40-runtime-vendor-legal.prd.md` | UPDATED | +1 / -1 |

계획 밖 산출물(아래 Deviations 참조):

| File | Action | Lines |
|---|---|---|
| `.claude/notes/insuniverse-docs-40-runtime-vendor-legal-m3.md` | CREATED | +248 |
| `.claude/receipts/mccp-implement-codex/insuniverse-docs-40-runtime-vendor-legal-m3.json` | CREATED | 게이트 산출물 |

## Deviations from Plan

**1. 계획서 Files to Change 에 없는 파일 2건이 생겼다 — 둘 다 mccp 워크플로 산출물이다.**

`.claude/notes/…-m3.md` 는 `/mccp:prp-implement` Phase 2.5.4 가 요구하는 `## Codex
Implementation Review` 절의 자리다. **계획서 본문에 쓰지 않았다** — `mccp-plan-codex` receipt 가
`plan_hash` 로 계획서를 결박하고 있어 본문 편집이 그 receipt 를 stale 로 떨어뜨리고, 계획서
§NOT Building 도 착수 후 본문 편집을 명시적으로 금지한다. `.claude/receipts/…` 는 게이트
산출물이다. **INV3** 이 이 부류를 PRD §배타 소유 경로의 대상이 아니라고 판정해 뒀다.

**2. 계획서 관측 10 의 계수가 한 칸 틀렸다 (실측으로 정정).**

관측 10 은 동결 admin `fb001e1d` 의 `src/**` 에서 `NO_DATA` 가 0건이라고 적었으나 실측은 1건이다
(`…/TreatMonitorPage.tsx:80` 의 주석 `// NO_DATA, DUPLICATE 등`). **최초 판단은 이 사각이 판정에
안 닿는다는 것이었고 같은 창의 코드리뷰가 그것을 반증했다** — 그 주석 한 줄이 admin 축 파일
집합을 세우던 유일한 근거였고, `category`·`userMessage`·`NO_DATA` 세 행의 `도달` 판정 전부가
그 위에 서 있었다. 흡수 뒤 축 판별과 신호 교집합이 코드행 등장만 보고 `NO_DATA` 행이
`진단 표면만` → `미도달` 로 뒤집혔다(반사실 CF4·CF6'). 상세는 notes §코드리뷰 라운드 CR1.
이월 원장의 해당 행을 정정했다(`id=40m3-obs10-admin-nodata-count`). 계획서 본문은 고치지 않았다.

**3. `#4` 음성 케이스의 실제 실패 문면이 계획서 예상과 다르다.**

계획서는 `#4a FAIL: 대조표가 1개다 (기대 2)` 를 예상했는데 실행 관찰은 `'## 응답 의미 대조 —
treat 축' 절에 표가 2개 이상이다` 였다. 원인은 변이 방식이며(절 제목을 지우지 않고 H3 로 낮췄다)
**두 경로 모두 뭉침을 rc=1 로 잡는다.** notes 에 관찰 그대로 기록했다.

**4. 연혁 3행을 게이트가 반려해 재선정했다.**

첫 저작의 `3a80536f` · `0afb594e` · `4d798f55` 가 이 문서의 앵커 경로를 건드리지 않아 `#9` 가
떨어뜨렸다. **게이트가 옳다.** 앵커 접촉 커밋 65건에서 응답 의미가 바뀐 9행을 재선정했고,
분류·재시도성이 응답에 실린 시점(예외 필터, 이 문서의 앵커가 아니다)은 **만들 수 없는 행**으로
연혁 절 머리 산문에 사유와 함께 남겼다.

**PRD 는 규범 문면을 편집하지 않았고 마일스톤을 `complete` 로 전이하지 않았다** — Delivery
Milestones 3행의 Status(`pending` → `in-progress`)와 Plan 셀만 움직였다 (`git diff` 1행 1행).

## Issues Encountered

**1. receipt write 가 자기 앞 세션의 claim 에 막혔다.** `/clear` 직전 세션(`76f14ccd`)이
`insuniverse-docs-40-runtime-vendor-legal-m3` work unit 을 쥐고 있어
`mccp-receipt write` 가 `other-live-holder` 로 exit 1 했다. **가드를 끄지 않고 TTL(15분) 만료를
기다렸다** — 8번째 재시도에서 성공. `MCCP_EVIDENCE_CONFLICT_GUARD=warn` 우회는 쓰지 않았다.

**2. 동결 트리 파일 단위 독출이 느렸다.** 초기 프로토타입이 파일마다 `git show` 를 spawn 해
server 트리 697파일에서 2분 타임아웃에 걸렸다. `git grep -l -w -F -e <token> <sha> -- <pathspec>`
로 바꿔 트리당 토큰당 1회 spawn 으로 줄였다(0.3초). rc 1(0건)과 그 외 비정상 rc 를 갈라 죽인다 —
계수 0 과 도구 실패를 같은 값으로 접으면 `#6b` 가 조용히 거짓 통과한다.

**3. Codex 가 env 정책으로 꺼져 있다.** `MCCP_CODEX_DISABLED=1` 이 `~/.claude/settings.json` 4행에
전역으로 박혀 있어 Implement-Codex 가 `classification=disabled` 로 단락됐다(최소 프로브
`durationMs=1` 로 확인 — 프로세스 spawn 없음). receipt 는 `codex_verdict=skipped` ·
`meta.codex_disabled=true` 다. **`converged` 가 아니므로 하류 PR-Codex 를 건너뛰게 만들지
않는다.** 이 창에서 리뷰를 받지 못한 결정 7건을 notes §2.5.2 에 열거했다.

## Tests Written

프로젝트 테스트 러너가 없다(`package.json` 부재). 검증은 게이트 하네스가 진다.

| Test File | Tests | Coverage |
|---|---|---|
| `.claude/plans/insuniverse-docs-40-runtime-vendor-legal-m3.gates.mjs` | 검사 14종 (`#0`~`#13`) | 문서 전 절 · 인덱스 조인 · 동결 3트리 재계수 · 선행 M1·M2 회귀 |
| 동 파일 `check11` | 자기검증 3종 | 양성 `--help` · 음성 서브커맨드 rc · `normAnchor` 8케이스 (양성 4 · 음성 4) |
| (일회용) 음성 프로브 | 20건 | 검사별 깨진 입력 → rc=1 관찰. 기록은 notes, 스크립트는 스크래치패드 |
| (일회용) 코드리뷰 반사실 | 7건 | 흡수 매 건의 수정 전/후 결과 반전 확인 (notes §코드리뷰 라운드) |

## 남은 `(미검증)` 항목

계획서 Acceptance 가 명시한 것 그대로다. 이 창이 닫지 않았다.

- 착수 후 계획서 본문 미편집 — `mccp-plan-codex` receipt 의 `plan_hash` 재검증으로 확인
- 소비 계수의 다의어 오탐이 `도달` 판정을 뒤집지 않았는가 — 계수는 상한이고 판정은 축 전용 토큰이 좌우한다. **주석 오탐 축은 코드리뷰가 닫았다**(CR1 · 축 판별이 코드행만 본다). 다의어 축과 블록 주석 fail-open 축은 남으며 한계는 `#6b` 출력에 적힌다
- 주석 4벤더 표기와 코드 5종 중 어느 쪽이 옳은가 — 저장소 소유자 소관. 문서는 둘을 앵커와 함께 싣는다
- 연혁 행의 커밋이 정말 응답 의미를 바꿨는가 — 게이트는 앵커 경로 접촉까지만 본다
- `bash docs/_meta/verify/verify.sh --check` — 이 worktree 는 서브모듈 워킹트리가 없어 실행 불가
- 문서가 Q17·Q40 을 문서만으로 답하게 하는지 — 유지보수자 판정
- PRD P-2 의 배치 예측 정정 수용 여부 — 이월(`id=40m3-p2-placement-prediction-refuted`), PRD 소유자 판단

## 코드리뷰 흡수 (`/mccp:code-review` Local Review Mode · 2026-08-26)

게이트가 `all ok 14/14` 로 통과한 트리를 대상으로 돌렸다. **지적 7건 전건 흡수 · 기각 0건.**
라운드 표·층위·반사실 관찰은 notes §코드리뷰 라운드에 있다.

| # | 지적 | 층위 | 조치 |
|---|---|---|---|
| CR1 | `#6c` admin 축 판별이 주석 한 줄에 걸려 `NO_DATA` 도달을 오판 | 8 · 1 | 축 판별·신호 교집합을 코드행 등장만 보도록 좁히고 `VENDOR_ERROR` 를 축 토큰에 추가. 문서 `NO_DATA` 행 `진단 표면만` → `미도달` |
| CR2 | `소비 표면` 산문 열의 수치가 재유도되지 않는다 | 2 | `#6a` 에 수치 금지 검사 신설 · 세 행에서 수 제거 |
| CR3 | `#8` 기대 앵커 3건이 하네스 리터럴 | 7 | 출력을 인덱스/리터럴로 분리 + 사각 줄 |
| CR4 | `#10b` 가 200칸의 값·유도 규칙을 안 본다 | 2 | `#10c` 신설 — 2어휘 대조 + 유도 재계산, `#6c` 도달에 결박 |
| CR5 | 죽은 코드 2건 | 1 | 제거 |
| CR6 | 항상 0 인 계수기 5건 | 7 | 출력에서 제거 |
| CR7 | `#11` normAnchor 자기검증이 전부 양성 | 8 | 음성 4건 추가 |

**판정 하나가 실제로 뒤집혔다.** CR1 은 계수의 사각이 아니라 판정의 결박이었다 — 동결 admin
`fb001e1d` 의 `src/**` 에서 계획서가 지명한 축 토큰 둘의 코드행 등장이 0이라, admin 축 파일
집합 전체가 `TreatMonitorPage.tsx:80` 의 주석 한 줄로 서 있었다. 반사실 CF7 이 `VENDOR_ERROR`
를 빼면 `category`·`userMessage` 가 거짓 `미도달` 로 떨어짐을 보여 그 토큰이 하중임을 확인했다.

**계획서 본문은 고치지 않았다** — `plan_hash` 앵커다. 게이트 축 토큰이 계획서 Risks 행보다
하나 넓어진 편차는 이월 원장으로 냈다(`id=40m3-axis-token-widened`).

## Next Steps

- [ ] `/mccp:prp-commit` — 커밋. **슬러그가 브랜치에서 나오므로** PR 단계에서 마일스톤 슬러그를 손수 확인한다
- [ ] `/mccp:pr` — PR. `MCCP_CODEX_DISABLED=1` 이 살아 있는 동안 PR-Codex 도 같은 env 정책으로 skip 된다. 마일스톤을 닫기 전에 토글을 되돌리면 이중 리뷰가 복구된다
- [ ] PRD 소유자 판단 — P-2 문면 정정 (이월 원장)
