# M12 — 축 종료 전이 · 구현 notes

**계획서**: `.claude/plans/insuniverse-docs-50-domain-m12.plan.md`
**하네스**: `.claude/plans/insuniverse-docs-50-domain-m12.gates.mjs`
**원장**: `.claude/plans/insuniverse-docs-50-domain-m12.ledger.jsonl`
**판정 산출물(계획 단계 동결)**: `.claude/plans/insuniverse-docs-50-domain-m12.verdict.jsonl`

## Codex Implementation Review

- 호출: `node /home/madsc/.claude/plugins/cache/mccp/mccp/1.33.6/scripts/lib/codex-invoke.js adversarial-review` (fail-closed Bash wrapper, v0.2.2)
- 라운드 수: 1
- 합치 결론: **승인 아님.** Codex 는 sandbox 에서 읽기 전용 git 명령이 거부돼(`bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted`) 실제 diff 를 읽지 못했고, 그 사실을 스스로 적으며 `verdict: needs-attention` 을 냈다. `findings: []` 는 **안전의 증거가 아니라 관측 부재**다 — 구조화 읽기(`codex-review-payload.deriveGateVerdict`, source=`structured`)가 그 값을 게이트 어휘 `divergent` 로 옮겼고 receipt 가 그대로 싣는다. cross-gate dedupe 는 fail-closed 로 남고 `/mccp:pr` 단계에서 PR-Codex 가 발화한다.
- YAGNI Triage:

  | Finding | Severity | Verdict | Why |
  |---|---|---|---|
  | (없음) | — | — | R1 findings 0건. 아래 `next_steps` 는 finding 이 아니라 권고이며 그 자격으로 처리했다 |

- `next_steps` 처분 (권고 → 이 창의 처분):

  | # | 권고 | 처분 | 근거 |
  |---|---|---|---|
  | 1 | 환경 복구 후 diff 재검토 | 미이행 `(미검증)` | sandbox 제약은 이 창 밖. 그래서 verdict 를 `divergent` 로 싣고 승인으로 읽지 않는다 |
  | 2 | manifest 파서 수리에 회귀 검증 동반 | **흡수** | `#1` 이 `meta.pins` ↔ manifest 를 차집합 0 양방향으로 물고, `--selfprobe` 에 잘못된 핀(따옴표 없는 값·head 결손)을 거부하는 음성 프로브를 더했다 |
  | 3 | designation 변경 후 digest 재계산 | **흡수** | 원장 저작 순서를 meta → correction → designation → (digest 계산) → maintainer-verdict 로 고정했다. 재계산 누락은 `#4` 의 `ledger_digest 결박 불일치` 로 붉다 |
  | 4 | designation 은 실측에서 쓰고 최종 상태에서 재실행 | **흡수** | Task 7 이 4대상을 돌려 관측 집합을 만들고, `#10` 이 매 실행 그 4대상을 다시 돌려 양방향 대조한다 |
  | 5 | 앵커 기준 범위 검사가 최신 main 통합 검증을 대신하지 못한다 | **흡수(부분)** | 상류 14커밋과 이 창 소유 경로의 교집합이 **0** 임을 실측했다(아래 §상류 델타). 통합 자체는 `/mccp:pr` 병합 시점 몫이며 그 사실을 여기 적는다 |
  | 6 | closure 는 최종 바이트에서 stamp 하고 해시 재검증 | **흡수** | Task 8 이 closure 3종을 먼저 확정하고 그 바이트에서 sha256(EOL 정규화)을 계산해 PRD 에 실었다. `#8` 이 매 실행 재계산해 대조한다 |
  | 7 | `#6` 어휘 검사는 의미 판정을 대신하지 못한다 | **미흡수 · 명시** | 옳은 지적이다. `#6` 은 문단 단위 술어(주어 어휘 × 달성 어휘 × 부정 부재)이며 **부정어를 끼워 넣은 실질 종료 주장은 통과시킨다**. 그 사각지대는 이 창이 닫지 않고 `(미검증)` 으로 이름을 낸다 — 층위 8 |

- Deferred to backlog: 0
- Open Questions: 없음 (auto-CRITICAL 카탈로그 — 보안 경계 · 원자적 상태 · 스키마 파괴 — 해당 0건)
- Codex session 참조: threadId `01a07a40-1274-7b00-9510-ce65fbbd3197`

### Security Reviewer

해당 없음 — 이 창의 변경은 문서·원장·PRD 표이며 인증·암호·비밀·입력 검증·주입·SSRF·경로 탐색·권한 상승 어느 면에도 닿지 않는다. `docs/` 산출 문서와 서브모듈 포인터는 `#9` 가 변경 0 을 문다.

### 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` 로 싣는다. 이 창의 산출은 마크다운 문서와 JSONL 원장뿐이다.

## 상류 델타 — 이 창 소유 경로와의 교집합

`git diff --name-only HEAD...origin/main` 실측(2026-09-07). 상류가 앞선 14커밋은 형제 축
(`20-foundation-access` M8 · `70-viewer` M9)의 산출이며, **이 창 소유 경로와 교집합 0** 이다:

- `.claude/plans/insuniverse-docs-50-domain-m*` · `.claude/prds/insuniverse-docs-50-domain.prd.md` ·
  `.claude/notes/insuniverse-docs-50-domain-m*` · `.claude/milestone-closures/{9,10,11}*` — 전건 0건
- 겹치는 유일한 파일은 `.claude/plans/codex-findings-backlog.md` 다. 이 창도 이월 행을 더하므로
  병합 충돌 후보이지만 **행 추가끼리라 의미 충돌이 아니다**. 해소는 `/mccp:pr` 병합 시점 몫이다.

**상류를 이 창에서 다시 병합하지 않는다.** 앵커는 기존 병합 커밋 `afa2c913` 이고 `#9` 는
워킹트리를 그 앵커와 대조한다 — 새 병합은 형제 축 경로를 diff 에 끌어들여 허용집합 밖 변경으로
붉어진다. 계획서 Task 0 의 Validate(`HEAD..origin/main` 이 0)는 병합 시점 `afa2c913` 에서
만족됐고 그 사실이 앵커에 남아 있다. 통합 검증은 이 창의 술어가 아니다 `(미검증)`.

## 종료 술어 — 이 창이 `complete` 로 무엇을 주장하는가

**술어는 PRD 8행 선례다.** 8행 Outcome 이 문면으로 적었다 — *"이 축에서 `complete` 는
Outcome 증명이 아니라 산출 인도 + 잔여 이월이다"*. 9·10·11행의 전이도 **같은 정의**를 쓴다:
그 창들이 만들기로 한 산출물이 트리에 섰고, 만들지 못한 것은 이름과 이월 id 로 원장에 남았다는
뜻이지 지표가 서 있다는 뜻이 아니다.

**지표 두 축은 이 창에서도 서지 않는다.** 독립 재판정(fable5 · 43 에이전트 · 블라인드 1차 +
불일치분 적대적 반증 2렌즈)이 두 축 모두에 `미성립` 을 냈고 `can_close_axis: false` 다.
그 판정의 정본은 계획 단계에 동결된 `.claude/plans/insuniverse-docs-50-domain-m12.verdict.jsonl`
이며 `#3` 이 그 바이트를 앵커 blob 과 매 실행 대조한다 — 이 창은 자기 판정을 다시 쓰지 못한다.

### 미달분 — 이름과 계수

| 축 | 목표 대비 실측 | 미달 |
|---|---|---|
| 단독 온보딩 성립 | Target 100% · 실측 56.5% (13/23) | 10건 |
| 리뷰 불일치 수렴 | Target 2라운드 연속 0건 · 실측 Round 1: 48건, Round 2: 12건 | 연속 0 미달성 |

온보딩 미달 10건의 이름 — `Q20` `Q21` `Q35` `Q36` `Q49` `Q50` `Q51` `Q53` `Q54` `Q55`.
성립은 13건이고 이 계수는 판정 산출물의 `final` 열에서 세며 이 문서가 리터럴로 단언하지 않는다
(`#6` 이 그 이름 전건의 실재를 여기서 문다).

리뷰 축의 회차 2 지적 12건은 동결 `insuniverse-site 767d1e44` 의 라우터 112건 중 화면 97종을
문서(`docs/20-site/00-ia.md`)와 대조해 나온 **문서가 빠뜨린 라우트**다. 회차 1(48건)은 방향이
`주장 → 원천` 이고 회차 2 는 `원천 → 주장` 이라, 두 회차를 "연속" 으로 셀 수 있는지 자체가
판정의 질문이었고 답은 부정이었다.

### 이 창이 하지 않는 것

- 두 축의 목표를 낮추지 않는다. §Success Metrics 표는 앵커와 바이트 동일하며 `#6` 이 그것을 문다
- 미달 10건의 각론을 다시 쓰지 않는다. 정정 5건은 판정의 정정이지 문서 저작이 아니다
- 봉인된 앞 창 산출물(m9·m10·m11 계획서·하네스·원장·notes)을 고치지 않는다. `#5` 가 무변동을 문다
- 12행 자신을 닫지 않는다. 그 전이의 주체는 이 창 밖이며 `id=50m12-row12-self-transition-unowned` 로 이월했다

## 기록의 과대평가 5건 — 정정으로 반입

블라인드 판정이 기록과 8건 갈렸고 그중 5건이 반증 **2/2 합의**로 확정됐다. 나머지 3건은 반대
방향이다 — `Q41`·`Q46` 은 블라인드가 미성립을 냈으나 반증이 기록(성립)을 지지했고, `Q36` 은
블라인드가 성립을 냈으나 반증이 기록(미성립)을 지지했다. 재판정이 한 방향으로만 움직이지
않았다는 것이 이 절의 논거이며 `#3` 이 그 계수를 출력에 싣는다.

| 질문 | 기록 | 재판정 | 뒤집은 창 | 근거의 성격 | 이월 id |
|---|---|---|---|---|---|
| `Q20` | 성립 | 미성립 | m11 | 계산 로직의 소재 모듈이 각론 8종 어디에도 없다 | `50m12-q20-analysis-compute-locus-undocumented` |
| `Q21` | 성립 | 미성립 | m10 | 정산 생성 조건이 03↔04 상호 위임으로 비어 있다 | `50m12-q21-settlement-linkage-mutual-deferral` |
| `Q35` | 성립 | 미성립 | m9 이래 | 잔여 한도·영수증 두 절이 각론·총론 어디에도 없다 | `50m12-q35-quota-receipt-clauses-absent` |
| `Q50` | 성립 | 미성립 | m11 | `docs/30-admin/01-admin-console.md:331` 이 스스로 미판정을 적었다 | `50m12-q50-excel-export-row-failure-undetermined` |
| `Q54` | 성립 | 미성립 | m11 | `docs/30-admin/01-admin-console.md:425` 가 스스로 미판정을 적었다 | `50m12-q54-concurrent-write-notification-undetermined` |

**기계가 거짓을 말한 것이 아니다.** m11 `#7`·`#8` 은 **좌표 실재**만 물었고 *질문의 모든 절이
답해지는가* 는 그 창의 `check9.unverified-residual` 이 `(미검증)` 으로 이미 이름을 낸 사각지대다.
이 창은 그 사각지대를 닫지 않고 판정만 정정해 원장에 남긴다.

## 실측 회차 — 전이 **후** 바이트에서 완주

전이 커밋 `82fd952` 위에서 하네스 전건을 같은 바이트로 돌렸다. `rc` 만 보지 않는다 — 침묵 +
`rc 0` 은 이 저장소에서 실측된 fork 고갈 양상이라 각 명령의 **문면**을 함께 적는다.

| # | 명령 | 문면 | rc |
|---|---|---|---|
| 1 | `gates.mjs --help` | 서브커맨드 11종 + `--selfprobe`/`check-all` 목록 | 0 |
| 2 | `gates.mjs check-all` | `checkall ok  11/11` | 0 |
| 3 | `gates.mjs --selfprobe` | `selfprobe ok  12종` | 0 |
| 4 | `gates.mjs check999` (음성) | `알 수 없는 서브커맨드: check999` | 1 |
| 5 | `plan-kit/lint-plan.mjs <plan>` | 출력 0행 (위반 0) | 0 |
| 6 | `lint-plan.mjs <Gate 한 행 삭제 사본>` (음성) | `PL4 ### Task 6 …: '- **Gate**:' 0개` | 1 |

5번은 **침묵 + rc 0** 이라 그 자체로는 게이트가 도는지 말하지 않는다. 6번이 그 자리의 양성
증거다 — 같은 lint 가 깨진 입력에서 규칙 이름(`PL4`)과 행 번호를 찍고 rc 1 을 낸다.

검사별 문면은 `check-all` 출력이 정본이고 여기 옮겨 적지 않는다(리터럴 중복은 낡는다).
지표 실측 계수는 `#3`·`#6` 출력에서만 나오며 이 문서가 스스로 단언하지 않는다(INV4).

## 계획 대비 이탈 — 3건

### 이탈 1 · 하네스 `manifestPins()` 가 통과 불가였다 (층위 5)

**무엇** — `#1` 이 `manifest 에서 핀을 0건 유도했다` 로 **영구 붉음**이었다. 구현 창에서 파서를
고쳤다.

**왜** — 최초 판의 두 정규식이 실제 형식과 어긋났다. `docs/_meta/extraction-manifest.yaml` 의
`repos:` 원소는 선두가 `- name:` 이고 `path:`·`head:` 는 그 다음 들여쓴 줄이며 `head:` 값은
**큰따옴표로 감싸여 있다**. `/^\s*-\s+path:/` 도 `/^\s+head:\s*([0-9a-f]{7,40})/` 도 한 줄도
못 문다. 계획 단계에서 이 검사를 실제로 돌린 적이 없다는 뜻이다 — CLAUDE.md §2 층위 5 가
겨냥한 자리 그대로다.

**어떻게** — 파싱을 `repos:` 블록 안으로 가두고(파일 뒤쪽에 `path:` 와 `repos[].head` 를 인용하는
산문이 여럿이라 경계 없이 읽으면 그것들이 핀으로 섞인다) 따옴표·후행 주석을 받게 했다.
`--selfprobe` 에 음성 4종을 더했다 — 따옴표 head 를 읽는가 · `repos:` 밖 값을 안 받는가 ·
`head` 결손 원소를 버리는가 · `repos:` 블록 자체가 없으면 0건인가.

### 이탈 2 · `#1` 의 핀 검증이 서브모듈 객체를 열려 했다 (층위 5)

**무엇** — `git cat-file -t <head>` 로 핀 객체가 열리는지 묻던 자리를 **상위 저장소 gitlink 대조**
로 바꿨다.

**왜** — 그 커밋은 서브모듈 저장소의 것이고 이 워크트리는 서브모듈이 초기화돼 있지 않다
(`git submodule status` 세 항목 전건 `-` 접두). 세 핀 모두 `fatal: git cat-file: could not get
object info` 다. 판정 산출물 `meta.unverified` 도 같은 사실을 적는다 — *"서브모듈은 이 워크트리
에서 비어 있어 소스 대조는 사정거리 밖이다"*.

**어떻게** — `git ls-files --stage` 의 `160000` 엔트리를 독립 열거로 삼아 `meta.pins` ·
manifest · gitlink 셋을 차집합 0 양방향으로 문다. 이것은 INV2 자신을 직접 무는 것이라 객체
열림보다 **강한** 단언이고, 동시에 이 워크트리에서 실제로 측정된다. 못 무는 것(그 커밋이
서브모듈 저장소에 실재하는가)은 출력이 `(미검증)` 으로 이름을 낸다.

### 이탈 3 · `#10` 의 귀속 어휘가 실측을 담지 못했다

**무엇** — 귀속 어휘를 3종에서 5종으로 넓히고 `causes` 배열을 필수로 만들었다. 계획서 Acceptance
의 *"각 붉음이 이 창으로 귀속된다"* 는 실측에 못 미친다.

**왜** — 실측된 붉음 4건 중 둘이 원래 어휘에 안 들어간다.

- `m11/check1` 은 **이 창의 전이가 원인이 아니다.** 환경 선행검사(핀 객체 열림)에서 죽고 그
  자리는 워킹트리 행 검사보다 **앞**이라 전이에 도달하지도 않는다. 원인은 이탈 2 와 같은
  서브모듈 미초기화다. 창 귀속 어휘 셋 중 하나로 적으면 원장이 거짓을 담는다
- `m11/check10` 은 원인이 **둘**이다 — 이 창 산출물 6종이 `m11.` 접두 밖인 것(계획서 관측 4 가
  예고)과, Task 0 의 상류 병합이 형제 축 8종을 m11 앵커 이후 구간으로 들여온 것(관측 4 가 적지
  않은 원인). 실측으로 확인했다: 그 8종 전건이 `e4d0aa21..afa2c913` 구간 커밋을 갖는다

**어떻게** — `upstream-merge` 와 `environment-precondition` 을 더하고, `attributed_to` 는 반드시
`causes` 안에 있어야 하며, **창 귀속 여부를 원장 선언이 아니라 `causes` 에서 유도**해 출력이
`이 창 귀속이 아닌 붉음 1/4건` 을 직접 드러내게 했다. 저자가 그 계수를 뒤집을 자리를 남기지
않는다.

### 이탈이 아닌 것 — 상류 재병합

계획서 Task 0 은 `origin/main` 병합을 요구하고 그 Validate(`HEAD..origin/main` = 0)는 병합 시점
`afa2c913` 에서 만족됐다. 그 뒤 상류가 14커밋 더 앞섰으나 **이 창에서 다시 병합하지 않는다** —
앵커가 `afa2c913` 이고 `#9` 가 워킹트리를 그 앵커와 대조하므로 새 병합은 형제 축 경로를 diff 에
끌어들여 허용집합 밖 변경으로 붉어진다. 교집합이 0 임은 §상류 델타 에 실측으로 적었다. 통합
검증은 `/mccp:pr` 병합 시점 몫이며 이 창의 술어가 아니다 `(미검증)`.

## 판정 원장 — 이 창이 남기는 `(미검증)`

| 항목 | 왜 기계 밖인가 |
|---|---|
| 재판정 산문의 참 | `#3` 은 판정 산출물이 앵커 blob 과 바이트 동일한지까지만 묻는다. fable5 의 사유가 옳은가는 이 저장소에 판정 기계가 없다 |
| 신원 검증 | 원장과 정본 표가 모두 저자 통제 아래 있다. 결박이 막는 것은 *옛 판정 위에서 닫기* 이지 *판정 주체* 가 아니다. 원장 밖 provenance 가 최대치다 |
| 12행 자신의 전이 | 이 창은 자기 행을 닫지 않는다 — `#7` 이 12행 `in-progress` 를 **요구**하므로 여기서 닫으면 그 검사가 붉다. 주체는 이 창 밖이며 `id=50m12-row12-self-transition-unowned` 로 이월했다 |
| 앞 창 하네스 전건 완주 | `#10` 은 지정 대상 4건만 돈다. 중첩 실행 비용 때문이며 그 사실을 출력이 적는다 |
| `#11` 자기 실패 | `check11` 은 재실행 집합에서 자기를 빼고 두 출력행을 직접 낸다. 그 자리는 `check-all` 의 rc 와 `--selfprobe` 가 진다 |
| `#6` 종료 주장 술어의 사각지대 | 문단에 부정어가 하나라도 있으면 판정 대상에서 빠진다 — 회피 문면이 구조적으로 통과한다. `id=50m12-check6-closure-claim-predicate-blindspot` 로 이월했다 |
| 핀 객체 실재 | 서브모듈 미초기화. `#1` 은 gitlink 일치까지만 묻는다 |
