# 노트: 30 · 계약 + 한도 정책 문서 — 마일스톤 9 구현 라운드

**계획서**: `.claude/plans/insuniverse-docs-30-contracts-limits-m9.plan.md`
**브랜치**: `30-contracts-limits`

계획서 §Validation 의 음성 케이스 중 부류 (c) 는 여기에 기록한다. 계획서 본문은 착수 후 편집하지 않는다 (`plan_hash` 앵커).

## Codex Implementation Review

- 호출: `node C:/Users/madsc/.claude/plugins/cache/mccp/mccp/1.33.1/scripts/lib/codex-invoke.js adversarial-review` (fail-closed Bash wrapper, v0.2.2)
- 라운드 수: 1 (라운드 캡 1 · `pinnedBy=codex-disabled`)
- 합치 결론: Codex 가 운영자 정책으로 비활성이다. 래퍼가 spawn 전에 봉인된 정책을 읽고 `classification=disabled` 로 short-circuit 했다 (`ok=true` · `blocking=false` · `durationMs=2`). R2 는 존재하지 않는다.

> Codex skipped per MCCP_CODEX_DISABLED=1

- 구현 시점 결정 열거 (2.5.2 — Codex 가 있었다면 이 넷이 focus 였다):
  1. `m9.gates.mjs` 내부 구조 — 단일 파일 + 검사 등록부 표 (형제 `m8.gates.mjs`·`m6.gates.mjs` 와 동형). 모듈 분할하지 않는다.
  2. 체인 검증기를 별도 파일이 아니라 같은 하네스의 `verify-chain` 서브커맨드로 둔다. `--selftest` 픽스처는 메모리에만 만든다 (파일을 늘리면 그 파일이 무변동 검사 대상이 된다).
  3. 원장 `meta` 레코드 형태 — `record: "chain-anchor"` · `round: 0` · `maintainer_provenance: null` · `prior_ledger_sha256` · `candidate_set_digest`.
  4. `m8.gates.mjs check12` 수술 범위 — `--expect` 파싱과 대조 두 자리로 좁히고 나머지 소스는 도입 blob 대조로 무변동을 문다.
- YAGNI Triage:
  | Finding | Severity | Verdict | Why |
  |---|---|---|---|
  | (없음) | — | — | Codex 비활성 — 라운드가 실행되지 않아 finding 이 0건이다 |
- Deferred to backlog: 0
- Open Questions: 없음
- Codex session 참조: (없음 — `classification=disabled`)

### Security Reviewer

이 창의 산출은 판정 원장 · 게이트 하네스 · PRD 표기다. 인증·암호·비밀·신뢰 경계 입력 검증·주입 표면이 없다 (계획서 §Files to Change 6경로 전건이 `.claude/**` 저장소 규약 산출물이며 제품 소스는 UI18 로 범위 밖). 마일스톤 1~8 의 implement receipt 전건이 같은 판정으로 `security_skipped=false` 다. 이 창도 같다.

### Design Review

`impeccable-detect --mode implement` 가 `skill_available=true` · `design_signal=false` · `reason=no-signal` 을 냈다. 렌더 표면이 없다 (`.tsx/.jsx/.css/.html` 0건). silent-skip 을 receipt 에 공시한다 — `impeccable_silent_skip=true` · `reason=no-signal`. 마일스톤 1~8 전건과 같은 값이다.

---

## 구현 라운드 기록 (2026-09-03)

계획서 §Validation 의 음성 케이스 중 부류 **(c) 하네스가 서야 관찰 가능한 것**을 여기에 싣는다.
계획서 본문은 `plan_hash` 앵커이므로 착수 후 한 바이트도 고치지 않았다.

### 착수 시점 실측 — `m8` 하네스 14검사

기준선 `.claude/plans/insuniverse-docs-30-contracts-limits-m9.baseline.txt` 의 `m8` 행이 이 관측이다.

| 검사 | rc | 부류 | 소요 | 문면 |
|---|---|---|---|---|
| check0 | 0 | G | 627s | — |
| check1~check11 · check13 | 0 | G | 0~83s | — |
| check12 | 1 | **X** | 0s | `#12a FAIL: 표 8행 Status='complete' (기대 'in-progress')` |

**Plan-Codex R1 F1 전반부의 반증이 이 창에서도 재현됐다.** 리뷰어는 `check0` 이
`#0a FAIL: m1 --fast 의 SKIP 행이 0개다` 로 exit 1 이라고 적었는데, 두 번의 독립 실행에서
두 번 다 rc 0 이다. 다만 **627초** 가 걸린다 — `m1 --fast` 가 셸 하네스를 spawn 하기 때문이며,
계획서가 그 비용을 적지 않아 첫 프로브가 툴 타임아웃(120초)에 죽었다. `#0c` 의 부류 Y 는 0건이고
`complete` 전이가 만든 붉음은 `check12` **하나뿐**이다 — 관측 1 그대로다.

### 계획서 이탈 (구현이 계획서 문면과 다르게 간 자리)

| # | 자리 | 계획서 문면 | 실제 | 왜 |
|---|---|---|---|---|
| D1 | `#4` 의 무변동 기준선 | *`check12` 밖 검사 소스 무변동 (**도입 blob** 대조)* | **HEAD blob** 대조 + 도입 blob 대비 표류를 별도 공시 | 실측: `m8.gates.mjs` 도입 커밋 `54e189e4` 의 블록과 HEAD 블록을 비교하면 `check0`·`check8` 두 검사가 **이미** 갈려 있다(도입 2072행 → HEAD 2100행). 도입 blob 을 유일 기준으로 삼으면 이 창의 수리와 앞 창의 개정이 한 계수로 섞여 *이 창이 안 건드렸다* 를 증명하지 못한다. 그래서 대조 기준은 HEAD 로 두고, 도입 대비 표류 2건은 `#4` 출력이 **이름으로** 공시한다 |
| D2 | `check12` 수리 범위 | *`check12` 밖의 검사는 손대지 않는다* | 검사 밖 **2자리**를 함께 고쳤다 — `main()` 의 인자 통과와 `checkAll()` 의 `--expect` 유도(+`deriveCheck12Expect()` 신설) | `m8` 의 `main()` 은 **모든** 서브커맨드의 잉여 인자를 rc 64 로 거부한다. 그 자리를 안 고치면 `check12 --expect complete` 가 검사에 닿기 전에 64 로 죽어 수리가 성립하지 않는다. 두 자리 다 `CHECKS.checkN` 블록 밖이라 `#4` 의 무변동 대조 분모 밖이며, 그 사실을 `#4` 출력에 이름으로 실었다 |
| D3 | 체인 검증기의 술어 대상 | 계획서는 다섯 축만 적었다 | **스키마 이전 원장** 개념을 더했다 — 네 필드(`record: chain-anchor`·`prior_ledger`·`candidate_set_digest`·`maintainer_provenance`)가 전건 없는 원장은 술어 대상이 아니고 해시 대상으로만 참여하며 계수로 공시한다 | 마일스톤 8 원장이 그 넷을 하나도 갖지 않는다. 없이 돌리면 `#6` 이 `candidate-digest` 위반으로 붉고, 그것을 피하려 축 3 을 끄면 검증기가 통째로 무력해진다. **일부만 가진 원장은 스키마 보유로 본다** — 그래야 필드 *삭제* 가 축 3 에서 잡힌다(fail-closed). 픽스처를 늘리지 않고 `#5` 의 **짝 관측**으로 물었다 (계획서가 `total=6` 을 기대하므로) |
| D4 | `#9` 의 인수 표기 결박 어휘 | 계획서는 어느 `verdict` 가 표기를 갖는지 적지 않았다 | `verdict: closed` 만 backlog 인수 표기를 갖는다. `handed-off`(다른 소유자 앞) 와 `open`(열린 채) 은 표기가 없어야 한다 | 계획서 `#9` 기대는 *인수 표기 1건(462행)* 과 *454행 은 표기 없이 open* 만 못박았다. carryover 11건 중 9건은 인수처가 다른 PRD 이거나 없으므로 그 둘 밖의 세 번째 상태가 필요했다 |
| D5 | `#7` 의 음성 케이스 | *원장 한 행의 `gate` 를 다른 하네스로 바꾸면 무관 결박이 `u`→`u+1` 로 움직인다* | **관찰 불가**로 판정하고 그 자리를 `#7` 안의 **양성·음성 자기 프로브**로 대체했다 | 실측 무관 결박이 958/958 이라 한 무관 결박을 다른 무관 결박으로 옮겨도 계수가 그대로다. 계획서의 음성은 도달이 0 이 아닐 때만 성립하는 문면이었다. 도달 0 인 검사는 그 자신이 죽어 있을 수 있으므로(층위 5) 대체 프로브는 **캡처한 출력의 실재 토큰 쌍이 도달로 분류되는가**(양성) 와 **날조 토큰이 무관으로 분류되는가**(음성) 를 매 실행 확인한다 |

### 음성 케이스 관찰 — 부류 (c)

프로브는 원본을 고치고 **되돌린다.** 되돌림은 등록부 + 종료 훅으로 이중화하고 마지막에
sha256 재대조로 복원을 증명한다 — 프로브가 죽어도 변조본이 남지 않는다.

<!-- 실행 결과는 아래 §프로브 출력 에 그대로 싣는다 -->

#### 프로브 출력 (2026-09-03 · 복원 대상 5파일 · 복원 실패 0건)

| 게이트 | 변이 | rc | 관찰된 문면 |
|---|---|---|---|
| `#0a` | 기준선의 9문서 해시 1글자 | 1 | `#0a FAIL: 기준선과 다르다 docs/03-contracts/01-input-validation.md` |
| `#1` | `--help` 열거에서 `check11` 제거 | 1 | `#1 FAIL: 서브커맨드 집합 불일치 — 선언에만 (없음) · 등록부에만 check11` |
| `#2a` | 같은 id 를 두 번 싣는다 | 1 | `#2a FAIL: id 중복 1건 oq-1` |
| `#2` | `meta.prior_ledger_sha256` 1글자 | 1 | `#2 FAIL: meta 재산출 불일치 prior_ledger_sha256 ae3112597e33 != 실파일 3e3112597e33` |
| `#3` | `evidence` 행을 파일 행수 초과로 | 1 | `#3 FAIL: oq-2 evidence 행 초과 (파일 405행) — docs/03-contracts/04-state-transitions.md:999999` |
| `#3` | **뒤집히지 않는 변이** — `oq-1` 좌표를 실재하지만 무관한 자리로 | **0** | `#3 ok …` — 계획서가 예고한 사각지대의 실측이다. 좌표를 `04-feature-gates.md:102` 로 바꿔도 초록이다 |
| `#5` | 다섯 축 각각 무력화 (5회) | 1×5 | 축마다 그 축의 음성 픽스처 이름이 `mismatch` 로 뜬다 — `끊긴 prior 해시` · `어긋난 round 증분` · `날조된 digest` · `round 1 을 단 chain-anchor` · `provenance 없는 원장을 라운드로` |
| `#6` | `record` 제거 + `round` 1 | 1 | `#6 FAIL: … termination-provenance: … round 1 인데 maintainer_provenance 가 null 다` |
| `#6` | `chain-anchor` 에 `round` 2 | 1 | `#6 FAIL: … anchor-round-zero: … chain-anchor 는 round 0 이어야 한다 (got 2)` |
| `#8` | `sample` 판정 어휘를 셋 밖으로 | 1 | `#8 FAIL: sample sample-1 판정 어휘 밖 "몰라"` |
| `#9a` | 이월 행 하나를 표 구간 밖 평문으로 | 1 | `#9a FAIL: 표 구간 밖에서 이 계획서를 인용한 행 1건 — 469행` |
| `#10` | OQ 판정 표기 1건 제거 | 1 | `#10 FAIL: 미표기 1건 — 2(256행)` |
| `#11` | 기준선 `golden` 해시 변조 | 1 | `#11 FAIL: golden.txt 가 기준선과 다르다` |

`#4` 의 음성은 계획서가 부류 (a) 로 이미 관측했다 — 수리 전 `check12` 가 rc=1 이고 문면이
`#12a FAIL: 표 8행 Status='complete' (기대 'in-progress')` 한 줄이었다. 수리 후
`--expect complete` 가 rc 0, `--expect in-progress` 가 rc 1, `--expect nonsense` 가 rc 1
(`어휘 밖` 문면), 인자 없음이 rc 1, **다른 검사의 잉여 인자는 종전대로 rc 64** 다.

`#7` 의 계획서 음성(*결박 한 행을 다른 하네스로 바꾸면 무관 결박이 `u`→`u+1`*)은 **현 상태에서
관찰 불가**다 — 아래 실측대로 무관 결박이 이미 분모 전건이라 한 행을 다른 무관 결박으로 옮겨도
계수가 움직이지 않는다. 그 자리를 자기 프로브로 대체했다(아래).

### `#7` 실측 — 실행 도달이 **0** 이다

```
#7a ok m8 원장 gate 결박 958건 재열거 · 하네스별 분해 (m8#check5=433 m8#check6=306 m8#check9=90
m6#check5=19 m6#check9=19 m6#check13=19 m2#check5=16 m3#check5=11 m4#check5=9 m4#check13=9
m5#check5=9 m5#check13=9 m8#check10=9)
#7 ok 실행 도달 0 · 무관 결박 958 · 실행 불가 0 · 합 958 · 입도 축약 958행 -> 328조합
(중복군 149 · 최대군 70) …
```

**결박 958건 중 그 검사의 출력이 자기 행을 이름으로 드는 것은 0건이다.** 원인은 앞 하네스들이
집계만 낸다는 것이다 — 실측으로 `m8#check6` 은 `01-input-validation 32` 처럼 문서 **약칭과 계수**를
내고 전체 경로도 `subject` 도 싣지 않으며, `m8#check10` 은 `좌표 보유 6 · 차단 3` 처럼 부류 계수만
낸다. 그래서 backlog 454 의 크기는 *일부* 가 아니라 **전건**이다.

도달 0 인 검사는 그 자신이 죽어 있을 수 있으므로(층위 5) `#7` 에 **양성·음성 자기 프로브**를
넣었다 — 캡처한 출력에서 실재하는 두 토큰을 뽑아 도달로 분류되는지 보고, 날조 토큰이 무관으로
분류되는지 본다. 계획서에 없던 축이며 도달 0 이라는 실측이 요구한 것이다 (이탈 D5).

### `check-all` 완주 (2026-09-03 · rc 0 · stderr 0바이트)

```
check-all ok 등록부 13종 == Validation 펜스 호출줄 13건 (각 정확히 1회) · 차집합 양방향 0
#1  ok 등록부 13종 == 선언 == --help 열거 · import 5건 전건 node 빌트인
#2a ok 원장 23행 · 부류 분해 (oq=6 sample=5 carryover=11 fixtask=1)
#2  ok meta 재산출 일치 — prior 3e311259 · record chain-anchor · round 0 · digest 8ba3d7f5
#3a ok PRD §Open Questions 6건 == 원장 oq 6건 · 차집합 양방향 0
#3b ok closed 5 · open 1 · 합 6
#4a ok check12 --expect complete rc=0
#4b ok check12 --expect in-progress rc=1
#4c ok --expect 그밖 rc=1 이고 어휘 밖임을 문면에 낸다
#4  ok check12 밖 검사 소스 무변동 13/13 · 이 창 밖 표류 공시 2건 (check0 check8)
#5a ok verify-chain --selftest total=6 ok=6 mismatch=0
#6  ok 앵커 1 · 유지보수자 라운드 0 · 요구 2 · 차이 2
#7  ok 실행 도달 0 · 무관 결박 958 · 실행 불가 0 · 합 958 · 축약 958->328 (중복군 149 · 최대군 70)
#8a ok 표본 5경로 82행 / 모집단 278행 (29.5%)
#9  ok 인수 표기 1건 (closed 1 · handed-off 5 · open 5)
#10a ok 표 9행 in-progress · #10b ok 토큰 1개 · #10 ok 미표기 0
#11 ok 9문서 9/9 · docs/_meta 0건 · golden 무변동 · 동결 3핀 무변동
#0a~#0d ok X 1 Y 0 · 분류 실패 0 · 초록 13/14 · 면제 3경로 공시
check-all ok 전건 실행 완료 (12검사 · 집계기 자신은 실행 목록 밖)
```

`node .claude/templates/plan-kit/lint-plan.mjs .claude/plans/insuniverse-docs-30-contracts-limits-m9.plan.md` rc 0.

### 이 창이 닫지 않은 것

계획서 §NOT Building 그대로다. 새로 이름 붙인 잔여 셋을 backlog 4열 표 행으로 올렸다 —
`30m9-uncovered-population-remainder` · `30m9-gate-binding-row-id` · `30m9-maintainer-rounds-outstanding`.
`fixtask.cohort_count` 는 43 으로 무변동이다 (신규 행이 `13-relation-contract` 필터에 걸리지 않는다).

`verify.sh --check` 의 rc 는 이 창의 종료 조건이 아니다 (UI4). 유지보수자 라운드 두 번도 이 창
밖이며 (UI3), 그 자리가 검증된다는 것만이 `#6` 의 주장이다.

### `m8 check-all` 회귀 — 수리 후 rc **0** (14검사 전건 초록 · FAIL 0행 · stderr 0바이트)

```
#12a ok 마일스톤 표 8행 Status=complete · Plan 셀 == 이 계획서 경로 · 1~7행 complete (7건) ·
     *Status: 행 1개이고 표 행이 complete 이고 토큰 마일스톤 9 >= 8
     (종결된 창은 자기 번호를 토큰에서 요구하지 않는다)
check-all ok 전건 실행 완료 (14검사 · check12 는 표 8행 현재값 'complete' 로 호출)
```

**계획서가 주장하지 않은 값이다.** §NOT Building 은 *`m8 check-all` 전체 rc 0* 을 배제했고
Acceptance 도 `check12 --expect complete` 하나만 물었다 — 그 좁힘은 Plan-Codex R1 F1 의 흡수였다.
실측은 그보다 넓게 나왔다: 착수 시점에 붉은 것이 `check12` 하나뿐이었고(부류 Y 0건) 그것을
수리하자 완주가 초록이 됐다. **이 사실을 성과 문면으로 넓히지 않는다** — Acceptance 는 계획서대로
`check12` 하나이고, 이 줄은 관측 기록이다. 다음 창이 `m8 check-all` 초록을 전제로 삼으면 그때
그 전제가 게이트가 되어야 한다.

`check-all` 이 `check12` 를 표 8행 현재값에서 유도해 부른다는 것도 이 출력이 증명한다 —
상수를 박았다면 전이 전후 중 한쪽에서 반드시 붉다.

### 코드리뷰 라운드 (2026-09-03 · `origin/main...HEAD` 브랜치 전건)

구현 커밋 `b30c7f4` 뒤 브랜치 델타를 통째로 다시 읽었다. 대상은 실행 로직 둘 —
`m9.gates.mjs` 와 `m8.gates.mjs` 의 `check12` 수리. 문서·원장·PRD 판정 내용은 방향이 이미
정해진 자리라 구현 정확성만 봤다(§3 INV1~INV6).

지적 8건 전건 흡수. **흡수는 매 건 반사실로 검증했다** — 수리 전/후로 검사를 돌려 결과가
뒤집히는지 확인한 관찰만 아래 표의 `반사실` 열에 싣는다.

| # | 층위 | 지적 | 수리 | 반사실 (실행 확인) |
|---|---|---|---|---|
| F1 | 6 | 체인 검증기가 `prior_ledger` 결박이 해소되지 않아도 통과하고, 그 위에서 순차 종료 술어까지 성립한다 | 축 1 에 두 술어 추가 — 적어 둔 경로가 체인에 없으면 위반 · `round>=1` 인데 `prior_ledger` 가 없으면 위반. 종료 술어는 `violations.length === 0` 일 때만 센다 | 없는 파일을 가리키는 라운드 원장 둘: 수리 전 `위반 0 · 라운드 2 · 종료 성립` rc 0 → 수리 후 `prior-sha` 2건 rc 1 (`check6` 도 rc 1) |
| F2 | 6 | `#4` 의 *check12 밖 소스 무변동* 이 구현 커밋 뒤 공허하다 — 기준선이 `HEAD` 라 이 창의 델타가 기준선에 들어가 버린다. 기준선 파일의 `head` 는 채워만 두고 아무도 안 읽었다 | 대조 기준을 `HEAD` → 기준선 파일의 `head`(착수 커밋)로. 죽어 있던 필드가 술어에 들어왔다 | 착수 커밋 `fa61e922` 기준: `check12` 가 기준선과 **다르다**(=델타를 문다) · `check12` 밖 movedm 0. `HEAD` 기준: `check12` 도 같음(=아무것도 못 본다). 표류 공시도 3건 → 2건으로 정정됐다 |
| F3 | 6 | `#9` 가 이월 11행 중 10행을 backlog **행 번호만**으로 묶는다 — `closed` 아닌 행의 술어는 *표 행이고 인수 표기가 없다* 뿐이라 표 어느 행이든 만족한다 | 원장 행의 `subject` 문면이 그 자리에 실재하는지 함께 문다. 꼬리 괄호는 두 자리가 원래 다르므로 앵커에서 제외하고, 앵커가 8자 미만이면 결박으로 세지 않는다 | `carry-458` 의 `backlog_line` 을 한 칸 밀자 `#9` rc 1 (`subject 앵커가 없다`). 11행 전건에 대해 +1 시프트가 11/11 뒤집힘 |
| F4 | 7 | `#0c` 의 *재유도 대조* 가 순환이다 — `rc` 도 `text` 도 같은 손 저작 baseline 줄에서 온다. 잡히는 것은 그 한 줄 안의 내부 모순뿐인데 주석이 층위 6 항체를 자처했다 | 주석을 사실에 맞추고, 외부 기준으로 실제 재실행돼 대조되는 행 계수를 출력에 싣는다 — `독립 재실행 대조 1/14 · 자기 신고 13/14`. 전건 재실행은 `m8 check0` 하나가 627초라 이 창 예산 밖이다 | (문면 정정 · 반사실 없음) 재실행 대상이 기준선 m8 행에 없으면 `#0c` 가 붉도록 술어는 추가했다 |
| F5 | 7 | `#7` 양성 자기 프로브가 항진명제다 — `probeOut` 에서 뽑은 두 토큰으로 물으니 구성상 항상 참 | 실 결박 행의 `doc`·`subject` 를 출력에 합성해 분류한다. 빈 필드 술어를 신설해 하네스 spawn **앞**에 뒀다 — `"".includes("")` 가 참이라 빈 필드 하나가 무엇이든 도달로 만든다 | m8 원장 결박 행 하나의 `subject` 를 빈 값으로: `#7a` rc 1. 술어를 앞에 뒀으므로 3분 spawn 전에 떨어진다 |
| F6 | 2 | `BLIND_SPOTS` 가 `미덮음 278행` 을 리터럴로 담는데 모집단은 `#8a` 가 런타임에 낸다 (INV4) | 문면에서 수를 빼고 *모집단은 `#8a` 가 런타임에 낸다* 로 | (문면 · 반사실 없음) |
| F7 | 2 | PRD 산문의 근거 좌표와 원장 `evidence` 좌표가 서로를 안 문다 — `#10` 은 있는지만, `#3` 은 실재하는지만 본다 | `openQuestions()` 가 인용 좌표를 파싱하고 `#3` 이 원장 좌표와 대조한다 | PRD `oq-1` 인용을 `:19` → `:20` 으로: `#3` rc 1 (`두 자리가 갈렸다`) |
| F8 | 4 | 기준선 `m8` 행의 `rc: Number(sp[2])` 가 NaN 을 부류로 흘린다 | rc 는 정수, 부류는 `X\|Y\|G` 어휘로 검사한다 | `m8 check5 0 G -` → `m8 check5 x G -`: `#0` rc 1 (`m8 rc 'x' 가 정수가 아니다`) |

**수리 후 실측** — `m9 check-all` rc 0 (12검사 · 185초) · `verify-chain --selftest` total 8 ok 8
mismatch 0 (양성 1 · 음성 7 — F1 이 음성 둘을 신설했다) · 추적 파일을 변조한 반사실 4건은
전부 `git diff` 무변동으로 복원 확인.

**축 등록부는 안 늘었다.** F1 이 신설한 음성 둘은 기존 축 `prior-sha` 를 다른 문으로 물며,
`#5` 의 *축 등록부 == 음성 픽스처 기대 합집합* 대조가 그 사실을 그대로 유지한다.

**이 라운드가 닫지 않은 것** — `#0c` 의 자기 신고 13행은 여전히 자기 신고다. 외부 기준으로
닫으려면 `m8` 14검사 전건 재실행이 필요하고 그 비용이 이 창 예산 밖이다. 계수로 공시하는
것까지가 이 창의 몫이다.
