# notes — 50 · 서비스별 각론 마일스톤 8 (server 각론 8종 + 리뷰)

**Plan**: `.claude/plans/insuniverse-docs-50-domain-m8.plan.md`
**Decision**: `insuniverse-docs-50-domain-m8`

이 파일은 계획 단계에서 §종료 검증 기록 만 갖고 열린다. 나머지 원장(모듈 배정 · 온보딩 답변 · 코드값 충돌 · 리뷰 라운드 1 · 계획서 이탈)은 `/mccp:prp-implement` 창이 Task 2·6·7 에서 채운다.

## 종료 검증 기록

**여기 있는 이유** — 계획서 Codex 절이 아니라 이 파일이 자리다. `plan_hash` 는 계획서 본문을 묶으므로 검증값을 계획서에 적으면 그 기록 자체가 편집이 되어 방금 검증한 해시가 즉시 stale 이 된다(R12-F0 흡수). 이 파일은 그 해시 대상 밖이다.

### 절차 완주 (2026-08-28)

| # | 단계 | 결과 |
|---|---|---|
| 1 | 계획서 최종 편집 | R12 흡수 반영 후 종료 |
| 2 | plan-codex 게이트 재실행 | R13 (nonce `3d14e0bc-1413-496b-9745-af93b81aa4d4`) — **지적 0건**, awaiting 아티팩트 미생성 |
| 3 | 계획서 무편집 상태에서 validate | `ok=true` · blocking 0 · stale 0 · missing 0 · open_critical 0 |
| 4 | 두 값 기록 | 아래 |

```
hash-plan   sha256:b1cc6e718b9004434ad7c2e4d5cd52592e1600758e1ae034220f695faba72a4e
receipt     sha256:b1cc6e718b9004434ad7c2e4d5cd52592e1600758e1ae034220f695faba72a4e
```

일치. 사용한 validate 명령은 `--decision` 과 `--plan` 을 모두 준 형태다 — 둘 중 하나라도 빠지면 검증이 성립하지 않는다(계획서 관측 15 의 세 변형 실측).

```
node <plugin>/scripts/receipt/cli.js validate --command mccp:prp-implement \
  --decision insuniverse-docs-50-domain-m8 \
  --plan .claude/plans/insuniverse-docs-50-domain-m8.plan.md
```

### receipt 상태

| 필드 | 값 |
|---|---|
| `resolution.codex_verdict` | `converged` |
| `resolution.converged` | `true` |
| `meta.intent_gate_verdict` | `skipped` |
| `meta.intent_skip_proof` | `no_codex_findings` |
| `meta.impeccable_silent_skip` | `true` (`no-signal`) |

`intent_gate_verdict=skipped` 는 결함이 아니다 — R13 이 지적 0건이라 판정할 finding 이 없었고, 게이트가 그 사실을 `no_codex_findings` 로 증명해 둔 것이다. `impeccable_silent_skip` 은 validate 가 warning 1건으로 내며 blocking 이 아니다(M1 관측 등급).

### 라운드 곡선

| 라운드 | 지적 | 성격 | 재제기 |
|---|---|---|---|
| R1 | 3 | 범위 종료 · 원장 전수성 · probe 오독 | — |
| R2 | 4 | 지표 Goodhart · 분모 누락 · 사각지대 · 도구 정지 | 0 |
| R3 | 3 | receipt 순환 · 명령 실행 불가 · 원장 결박 비대칭 | 0 |
| R4 | 3 | Task 순환 · 완주 정의상 불가 · 흡수 자신의 구멍 | 0 |
| R5 | 1 | R4 흡수 둘의 결합 모순 | 0 |
| R6 | 1 | `#10` 데드락 (`SUB003` 넣어도 빼도 실패) | 0 |
| R7 | 1 | 종료 창의 연속 0 이 성립 불가 | 0 |
| R8 | 1 | 관계 판정을 단항 라벨로 모델링 | 0 |
| R9 | 1 | 종료 validate 가 거짓 초록 | 0 |
| R10 | 3 | Task 0 청결 불가 · verify 집계 밖 · 허용집합 자기모순 | 0 |
| R11 | 1 | 원장 모집단 ↔ 창 범위 갈라짐 | 0 |
| R12 | 1 | 종료 절차가 자기 검증 해시를 무효화 | 0 |
| **R13** | **0** | — | 0 |

지적 23건 전건 흡수, 기각 5건은 근거와 함께 계획서 Codex 절에 기록. **재제기는 열세 라운드 전부 0** — focus 에 봉인 목록을 박은 결과다. 새 **주제**는 R2 이후 없고 R3 이후는 전부 앞 흡수가 남긴 내부 모순이다.

### 이 창이 남긴 이탈 조건 둘

1. **`#10`(코드값 충돌) 축** — R2·R6·R8 세 라운드가 쳤다. 다시 지적받으면 네 번째로 다듬지 않고 축을 창 밖 backlog 전용 창으로 낸다. 마일스톤 8 Outcome 이 그 축을 요구하지 않으므로 이탈이 마일스톤을 깨지 않는다(계획서 관측 7).
2. **인용 운용 명령 규칙** — 계획서가 인용하는 운용 명령은 이 창에서 한 번 그대로 돌려 본 것만 적는다. `--probe=history`(위치 인자 없이 rc 2)와 `validate`(`--plan` 없이 거짓 초록) 둘이 그 기록이다(계획서 관측 15).

### 복구 사고 (2026-08-28)

R11 흡수 중 `io.open(path,'w')` 가 truncate 한 뒤 `"\n".join()` 이 타입 오류로 던져 계획서가 **0바이트로 잘렸다.** 미추적 파일이라 git 사본이 없었다.

- Validation 펜스(193행)는 스크래치 추출본이 최신이라 verbatim 복구
- 본문은 R1~R11 흡수 전건을 반영해 재작성
- R12 focus 에 *"복구본이니 흡수 누락·절 간 어긋남이 있으면 그것이 새 층위"* 를 명시해 재검을 시켰고, R12 는 복구 누락이 아니라 R3-F0 흡수가 남긴 별개 모순 1건만 냈다. R13 이 0건으로 닫았다

이후 계획서 편집은 **임시 파일에 쓰고 `mv`** 하는 형태로만 한다.

## Codex Implementation Review

decision-set already converged in mccp-plan-codex review. No new implement-time decisions detected. Cross-gate dedupe applied.

**이 절이 계획서가 아니라 notes 에 있는 이유** — `plan_hash` 는 계획서 본문을 묶는다. 계획서에 이 절을 주입하면 방금 `ok=true` 로 검증된 `mccp-plan-codex` receipt 가 그 순간 stale 로 떨어진다(§종료 검증 기록 R12-F0 과 같은 형태).

### 대조한 세 조건 (2026-08-31)

| 조건 | 관측 |
|---|---|
| 계획서에 `## Codex Adversarial Review` + `합치 결론` | 있다. 13라운드 · 지적 23건 전건 흡수 · 기각 5건 근거 보유 · Open Questions 없음 |
| 계획 승인 이후 신규 결정 0 | 파일 배치(8편 + 하네스 + 원장 4종) · 추상 경계(`row_kind` · 동치 클래스 분할 · 모집단 유도) · 외부 의존(INV1 신규 0) · 동시성(해당 없음) 전건이 계획서에 선결정돼 있다 |
| `git diff --name-only origin/main...HEAD` ⊆ Files to Change | 0건. `HEAD 1d3d25f` 가 `origin/main fa8ad07` 의 조상이라 3-dot diff 가 비었다 |

### Design Review

> impeccable silent-skip (auto-detect): `skill_available=true` · `design_signal=false` · reason `no-signal`. 이 창의 산출은 markdown 문서와 Node 하네스뿐이고 렌더 표면(`.tsx`/`.css`/`.html`)이 0 이다. receipt 에 `--impeccable-silent-skip` 으로 정직하게 실린다.

### Security Reviewer

> 해당 없음 — 이 창은 auth·crypto·secrets·입력 검증·SQL/cmd injection·SSRF·path traversal·권한 상승 어느 것도 건드리지 않는다. 산출은 문서 저작과 읽기 전용 검사 하네스다. 자동 폴백(skip)이 아니라 **적용 대상 밖**이므로 `security_skipped` 를 달지 않는다.

## 모듈 배정 원장

**입력은 동결 트리다.** 배정 집합은 `insuniverse-server` 동결 HEAD `4d798f55` 의 `apps/api/src/modules/` 에서 매 실행 유도하며, 상위 체크아웃(워킹트리)이 아니다 — 둘은 다르고 `loss-analysis` 가 그 차이의 실례다(계획서 관측 3). 검사 `#3` 이 이 표를 그 유도 집합과 양방향으로 대조하고, 나아가 **대상 좌표가 실재하는 정책 id 인지**와 **그 정책의 앵커가 그 모듈 경로를 포괄하는지**까지 문다.

**`05-final-spec.md` §4 를 그대로 쓸지에 대한 판정(UI8)** — §4 는 파일 이름 8개만 정하고 모듈→문서 대응을 담지 않는다. 그래서 파일 이름 8개는 그대로 따르고 **대응은 이 창이 정했다.** 그 대응이 도메인상 타당한가는 기계 밖이며 계획서 Acceptance 가 `(미검증)` 으로 남긴다 — 이 표의 `근거` 열이 그 판단의 유일한 기록이다.

**제외의 뜻** — `총론 <문서 경로>` 는 "이 창이 안 쓴다" 가 아니라 **"그 모듈의 정책 서술을 총론이 이미 앵커로 지고 있다"** 는 주장이며, `#3` 이 그 주장을 좌표 실재와 앵커 포괄로 검사한다. 총론이 앵커로 덮지 않는 모듈은 제외할 수 없다 — `authority` 와 `allow-ip` 가 각론으로 온 이유가 그것이다.

| 모듈 | 배정 | 대상 좌표 | 근거 |
|---|---|---|---|
| admin | 총론 docs/02-access/02-authz-roles.md | ROLE_GUARD_WIRING | 관리자 계정 축은 권한 층 자체이며 총론이 결선 자리를 앵커로 진다. 도메인 자원이 아니다 |
| admin-view-token | 총론 docs/02-access/05-admin-view-token.md | VIEW_TOKEN_ISSUE | 열람 단명토큰 전용 총론 문서가 발급·결박·검증을 전부 앵커로 진다 |
| advertisement | 07-content.md | ADS_METRIC_INCREMENT | 운영이 걸어 두는 콘텐츠이며 위치·종류·재생방식 세 축과 지표 입구가 이 문서의 축이다 |
| agency | 05-agency-corp.md | AGENCY_CONTRACT_BILLING | 기업 자료형과 계약 과금방식이 이 문서의 중심 축이다 |
| alarm | 06-communication.md | ALARM_TWO_TRACKS | 사용자·관리자 두 갈래 알림 자료가 발송 축의 원소다 |
| alimtalk | 06-communication.md | ALIMTALK_STATE_AND_TYPE | 발송 채널 하나이며 상태·종류 두 축이 이 문서의 축이다 |
| allow-ip | 05-agency-corp.md | ALLOW_IP_MASTER | 총론 어느 정책도 이 모듈을 앵커로 갖지 않는다. 통제 층은 총론이 지고 이 문서는 그 층이 읽는 운영 마스터를 진다 |
| analyze | 01-analysis.md | ANALYSIS_RESULT_AXES | 분석 결과 축의 본체다 |
| auth | 총론 docs/02-access/01-authn.md | SESSION_ESTABLISHMENT | 로그인 축 자체이며 총론 인증 문서가 앵커로 진다 |
| authority | 05-agency-corp.md | AUTHORITY_NAME_MASTER | 총론 어느 정책도 이 모듈을 앵커로 갖지 않는다. 역할 어휘는 총론이 지고 이 문서는 그 이름 마스터를 진다 |
| b2c | 08-b2c-insurance.md | B2C_STATE_VOCAB | 자기 상태 어휘·입구·자료를 따로 갖는 별도 축이라 일반 분석에 접지 않았다 |
| banner | 07-content.md | BANNER_POPUP_LOCATION | 위치 축을 팝업과 공유하는 노출 콘텐츠다 |
| board | 07-content.md | BOARD_TYPE_AND_ACCESS | 게시 축의 본체이며 종류·접근허용 두 축이 여기 있다 |
| card | 04-settlement-money.md | CARD_PRIMARY_SELECTION | 결제 수단 보관 축이라 정산 문서가 진다 |
| client | 05-agency-corp.md | CLIENT_ANALYSIS_SEPARATE | 고객사 단위 축이며 일반 분석과 자료·입구가 갈린다 |
| clinical-result | 02-treat.md | CLINICAL_STANDARD_MASTER | 검사 기준 마스터이며 진료 해석의 입력이다 |
| common | 총론 docs/03-contracts/01-input-validation.md | input:type-coercion | 공용 입력 변환·검증 축이며 총론 계약 문서가 앵커로 진다 |
| compare | 01-analysis.md | COMPARE_MASTER_TABLE | 분석 결과를 해석하는 대조 기준표다 |
| courses | 02-treat.md | COURSES_BOOKMARK | 강의 책갈피가 로그인 주체에 묶이는 축이라 이 문서가 진다. 강의 공개 범위는 07 소관이라 책갈피만 |
| disease | 02-treat.md | DISEASE_TAXONOMY_MASTER | 질병 분류 마스터이며 진료 해석의 입력이다 |
| fax | 06-communication.md | FAX_CONFIG_AND_HISTORY | 발송 채널 하나이며 과금 설정과 이력이 이 문서의 축이다 |
| fc | 05-agency-corp.md | CORP_FC_WRITE_SCOPE | 기업FC 축이며 조직 범위 쓰기 판정이 이 문서의 축이다 |
| fcm | 06-communication.md | FCM_EXCEPTION_SHAPE | 푸시 전송 채널이며 전용 예외 형태가 이 문서의 축이다 |
| institution-block | 07-content.md | INSTITUTION_BLOCK_SWITCH | 운영이 켜고 끄는 안내 표시 축이라 콘텐츠 문서가 진다 |
| invoice | 04-settlement-money.md | INVOICE_ASSEMBLY | 청구서 조립 축이라 정산 문서가 진다 |
| loss-adjustment | 01-analysis.md | LOSS_CASE_INTAKE | 손해사정 접수 건의 상태·열람 축이며 분석 결과 뒤에 붙는 자리다 |
| loss-analysis | 01-analysis.md | LOSS_ANALYSIS_INVITE | 손해사정 분석의 초대·수집 워크플로다 |
| medical | 02-treat.md | MEDICAL_RAW_EXPORT | 수집 원본 자료 내려받기 축이라 진료 문서가 진다 |
| medication-standard | 02-treat.md | CLINICAL_STANDARD_MASTER | 투약 기준 마스터이며 검사 기준과 같은 정책이 두 마스터를 함께 진다 |
| menu | 총론 docs/02-access/04-authz-db-menu.md | ADMIN_MENU_TREE | 메뉴 노출 축이며 총론 권한 문서가 비차단 층으로 앵커를 진다 |
| money | 04-settlement-money.md | MONEY_WALLET_SOURCES | 잔액·출처 자료의 본체다 |
| order | 03-subscription-order.md | ORDER_TWO_LAYERS | 주문·주문상세 두 층의 본체다 |
| payment | 04-settlement-money.md | PAYMENT_STATE_AND_METHOD | 결제 상태·수단 축의 본체다 |
| popup | 07-content.md | BANNER_POPUP_LOCATION | 위치 축을 배너와 공유하는 노출 콘텐츠다 |
| product | 03-subscription-order.md | PRODUCT_CYCLE_AXES | 무엇을 사는가의 축이며 구독과 같은 문서가 진다 |
| push | 06-communication.md | PUSH_SCHEDULE_AND_TARGET | 발송 채널 하나이며 예약·대상 축이 이 문서의 축이다 |
| referral | 05-agency-corp.md | REFERRAL_BENEFIT_MASTER | 추천 혜택 마스터이며 조직·주체 축에 붙는다 |
| refund | 04-settlement-money.md | REFUND_OUTCOME_CODES | 환불 결과 코드 축이라 정산 문서가 진다 |
| sales-agency | 05-agency-corp.md | SALES_CHANNEL_TREE | 영업 대리점·채널 두 단이 이 문서의 조직 축이다 |
| search | 01-analysis.md | DISEASE_SEARCH_MASTER | 질병 검색 마스터이며 분석 결과 조회의 보조 축이다 |
| statistics | 01-analysis.md | ANALYSIS_STATISTICS_SCOPE | 분석 집계의 분모 축이다 |
| subscribe | 03-subscription-order.md | SUBSCRIPTION_STATE_VOCAB | 구독 상태 어휘의 본체다 |
| treat | 02-treat.md | TREAT_COLLECT_DISPATCH | 진료 수집 축의 본체다 |
| treat-test | 02-treat.md | TREAT_VENDOR_TESTBED | 수집 벤더 시험대이며 진료 축의 보조 자리다 |
| user | 총론 docs/02-access/02-authz-roles.md | ROLE_MODULE_MATRIX | 회원 계정 축이며 총론 역할 문서가 모듈 대응표로 앵커를 진다 |

## 약한 인용 사유 원장

`#7` 이 8편의 `anchors[]` 와 본문 코드스팬 인용 전건을 동결 blob 에서 다시 읽어, 그 행이 빈 줄·`import` 행·주석 행인지를 본다. **약한 인용은 0 을 요구하지 않는다** — 본문이 그 행의 주석 처리 사실 자체를 주장하는 자리가 실재하기 때문이다. 다만 그런 인용은 전건 여기에 사유를 갖는다.

이 창의 실측은 **약한 인용 1건** 이며 그 1건은 리뷰 라운드 1 의 흡수가 낳았다. 저작 시점 원장은 0건이었고 표식 행 하나였다 — 지적 F1 을 흡수해 주석 처리된 결선 줄을 앵커로 들이자 `#7` 이 `사유 없는 약한 인용 3건` 으로 뒤집혔고(같은 인용의 3개 등장), 아래 행을 넣자 다시 초록이 됐다. 그 뒤집힘이 이 검사가 실제로 무는 증거다.

| 문서 | 인용 | 사유 |
|---|---|---|
| docs/10-server/05-agency-corp.md | insuniverse-server/apps/api/src/modules/allow-ip/allow-ip-admin.controller.ts:15 | **주석 처리 사실 자체가 주장이다.** 이 문서 `ALLOW_IP_MASTER` 는 그 입구에 판정 층이 없다고 적고, 그 근거가 결선 줄이 주석으로 남아 있다는 관측이다. 강한 행을 가리키면 없는 결선을 있다고 가리키게 된다 |

## 관계 축 결박 원장

`#13` 은 `docs/20-site/**` · `docs/30-admin/**` 변경을 **0 으로 금지하지 않고 결박으로** 허용한다 — 어느 검사가 무엇을 붉혔는지와 고친 파일이 이 표에서 짝지어져야 하고 짝 없는 변경은 위반이다(계획서 관측 19). 반대로 이 표에 있는데 실제 변경이 없는 행도 차집합으로 걸린다.

이 창의 실측은 **관계 축 변경 0건** 이다 — `#5`·`#8` 이 관계 축을 붉히지 않았으므로 고칠 이유가 생기지 않았다.

| 파일 | 붉힌 검사 | 사유 |
|---|---|---|
| — | — | 해당 없음 — 관계 축 변경 0건 (#5·#8 이 관계 축을 붉히지 않았다) |

## 온보딩 답변 원장

질문 집합은 계획서 리터럴이 아니라 `docs/_meta/onboarding-questions.yaml` 에서 `service: server` 로 매 실행 유도한다. `#9` 가 그 집합과 이 표를 양방향으로 대조하고, **좌표를 해석까지** 한다 — 답 문서 실재 · 인용한 정책 id 가 그 문서 `policies[].id` 에 실재 · 인용한 절 표제 실재 · 총론 링크 대상과 fragment 해석.

**미성립 0 을 요구하지 않는다.** 요구하면 얕은 답을 성립으로 적는 쪽으로 기울기 때문이다(마일스톤 7 판정). 대신 숨길 수 없게 한다 — 미성립 행은 `이월 id` 가 필수이고(`#9`), 그 id 의 실재를 `#12` 가, 계수를 `*Status:` 문면에서 `#14` 가 각각 문다.

| 질문 id | 답이 있는 문서 | 좌표 | 총론 링크 | 판정 | 이월 id |
|---|---|---|---|---|---|
| Q20 | docs/10-server/02-treat.md | TREAT_COLLECT_DISPATCH · 강제 지점 (개발) | — | 미성립(완료 이후 팬아웃의 모듈 순서는 `ANALYSIS_COMPLETION_FANOUT` 이 답하나 요청 진입부터 수집 개시까지의 모듈 관여 순서를 잇는 서술이 8편 어디에도 없다) | 50m8-q20-entry-order |
| Q21 | docs/10-server/03-subscription-order.md | ORDER_TWO_LAYERS · 강제 지점 (개발) | ../03-contracts/04-state-transitions.md#state:subscription-lifecycle | 미성립(구독·주문·정산의 연결은 답해지나 총론 한도 규칙이 이 도메인에서 실제로 어떻게 적용되는가를 잇는 서술이 없다 — 한도 정의는 총론에 있고 그 소비 자리를 이 창이 추적하지 않았다) | 50m8-q21-limit-application |
| Q34 | docs/10-server/02-treat.md | ANALYSIS_COMPLETION_FANOUT · 강제 지점 (개발) | — | 성립 | — |
| Q35 | docs/10-server/03-subscription-order.md | SUBSCRIPTION_POST_PAYMENT_ATOMICITY · 강제 지점 (개발) | ../05-schedules/03-monitoring.md#monitoring:alimtalk-status | 성립 | — |

## 묶음 토큰 전개 규칙

인수 backlog 행의 문면은 `MEM002` · `POP001` · `SUB00x` 셋이다. 앞 둘은 코드 그 자체지만 **`SUB00x` 는 묶음 토큰이라 전개해야 한다.** 전개를 글자 일치로 하면 `menu_hardcoded.yaml` 의 corp 접두 계열(`CRP_SUB002` · `CRP_SUB003`)을 놓치고, 그러면 그 두 코드의 중복 여부가 판정되지 않은 채 인수 행이 닫힌다.

그래서 전개 규칙을 **접두 선택형**으로 둔다 — sales 무접두 코드와 corp 접두 코드를 한 문면이 가로지르기 때문이다. `#10` 이 이 펜스를 읽어 정규식을 다시 만들고 `codes/*.yaml` 전건에 적용해 원장의 `token` 행과 양방향 대조한다.

```
^(CRP_)?SUB[0-9]{3}$
```

## 코드값 충돌 판정 원장

분모는 `docs/_meta/index/codes/*.yaml` **전건**에서 유도한다. `domain_status ∩ menu_db` 로 잡으면 `SUB` 계열 3건이 통째로 빠지고, 그 3건이 인수 행이 명시한 `SUB00x` 이므로 일부만 판정하고 행을 닫으면 미판정 충돌이 추적에서 사라진다(계획서 관측 7).

**행 종류가 둘이고 두 집합은 겹친다.** `cross` 는 둘 이상 파일에 걸친 코드, `token` 은 위 전개 규칙의 결과다. `SUB001`·`SUB002`·`SUB004` 는 양쪽에 있다.

**`cross` 는 행 라벨로 답하지 않는다.** `동일 개념`·`글자 충돌` 은 두 발생점 사이의 **관계**인데 행마다 단항 라벨로 적으면, 발생점이 셋인 `MEM002`·`POP001` 에서 행끼리 상충하는 값을 적어도 집합·어휘·좌표 검사가 전부 통과한다. 그래서 코드마다 발생점 집합을 **그룹으로 나눠** 적는다 — 같은 그룹 안이 `동일 개념`, 다른 그룹 사이가 `글자 충돌` 이다. 분할은 반사·대칭·추이를 정의상 만족하므로 모순된 원장이 구성 자체로 불가능하다. `#10` 이 각 분할의 완전성과 배타성을 문다.

**`token` 의 단독 코드는 관계 판정을 갖지 않는다.** `SUB003` 은 어느 삼중에도 들어가지 않는 singleton 인데 인수 행의 묶음 문면은 그것도 답해지기를 요구한다 — 두 요구가 한 집합에서는 서로를 배제했다(계획서 관측 7 의 데드락). `row_kind` 로 갈라 `token` 행은 중복 여부만 답하고 그룹·발생점 열을 비운다.

| row_kind | 코드 | 그룹 | 발생점 | 판정 | 근거 좌표 | 문서 좌표 |
|---|---|---|---|---|---|---|
| cross | MEM002 | G1 | insuniverse-server:apps/api/src/modules/fc/fc.entity.MemberType.기업FC | — | insuniverse-server/apps/api/src/modules/fc/fc.entity.ts:14 | docs/10-server/05-agency-corp.md#CORP_FC_WRITE_SCOPE |
| cross | MEM002 | G1 | insuniverse-server:apps/api/src/modules/push/push.dto.MemberType.MEM002 | — | insuniverse-server/apps/api/src/modules/push/push.service.ts:192 | docs/10-server/05-agency-corp.md#CORP_FC_WRITE_SCOPE |
| cross | MEM002 | G2 | stg-db:tn_menu.mnu_code.MEM002 | — | docs/_meta/index/codes/menu_db.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| cross | POP001 | G1 | insuniverse-server:apps/api/src/modules/institution-block/institution-block.entity.InstitutionBlockPopType.텍스트 | — | insuniverse-server/apps/api/src/modules/institution-block/institution-block.entity.ts:13 | docs/10-server/07-content.md#INSTITUTION_BLOCK_SWITCH |
| cross | POP001 | G1 | insuniverse-server:apps/api/src/modules/popup/popup.entity.PopUpType.텍스트 | — | insuniverse-server/apps/api/src/modules/popup/popup.entity.ts:6 | docs/10-server/07-content.md#BANNER_POPUP_LOCATION |
| cross | POP001 | G2 | stg-db:tn_menu.mnu_code.POP001 | — | docs/_meta/index/codes/menu_db.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| cross | SUB001 | G1 | insuniverse-server:apps/api/src/modules/subscribe/subscribe/subscribe.entity.SubscribeType.구독 | — | insuniverse-server/apps/api/src/modules/subscribe/subscribe/subscribe.entity.ts:5 | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| cross | SUB001 | G2 | insuniverse-admin:src/features/authority/constants/menu.salesMenus.SUB001 | — | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| cross | SUB002 | G1 | insuniverse-server:apps/api/src/modules/subscribe/subscribe/subscribe.entity.SubscribeType.해지 | — | insuniverse-server/apps/api/src/modules/subscribe/subscribe/subscribe.entity.ts:5 | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| cross | SUB002 | G2 | insuniverse-admin:src/features/authority/constants/menu.salesMenus.SUB002 | — | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| cross | SUB004 | G1 | insuniverse-server:apps/api/src/modules/subscribe/subscribe/subscribe.entity.SubscribeType.실패 | — | insuniverse-server/apps/api/src/modules/subscribe/subscribe/subscribe.entity.ts:5 | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| cross | SUB004 | G2 | insuniverse-admin:src/features/authority/constants/menu.salesMenus.SUB004 | — | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| token | SUB001 | — | — | 중복 | docs/_meta/index/codes/domain_status.yaml | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| token | SUB002 | — | — | 중복 | docs/_meta/index/codes/domain_status.yaml | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| token | SUB003 | — | — | 단독 | insuniverse-server/apps/api/src/modules/subscribe/subscribe/subscribe.entity.ts:5 | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| token | SUB004 | — | — | 중복 | docs/_meta/index/codes/domain_status.yaml | docs/10-server/03-subscription-order.md#SUBSCRIPTION_STATE_VOCAB |
| token | SUB005 | — | — | 단독 | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| token | SUB006 | — | — | 단독 | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| token | SUB008 | — | — | 단독 | docs/_meta/index/codes/menu_db.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| token | CRP_SUB002 | — | — | 단독 | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |
| token | CRP_SUB003 | — | — | 단독 | docs/_meta/index/codes/menu_hardcoded.yaml | docs/10-server/05-agency-corp.md#AUTHORITY_NAME_MASTER |

### 판정의 뜻

- **`MEM002` G1 대 G2** — G1 둘은 회원 종류 축의 같은 개념(기업 측 회원)을 두 자리에서 선언한 것이고, G2 는 메뉴 코드 이름공간의 값이다. 다른 그룹이므로 **글자 충돌**이다.
- **`POP001` G1 대 G2** — G1 둘은 팝업 표시 종류 축의 같은 값(텍스트형)이고, G2 는 메뉴 코드다. 다른 그룹이므로 **글자 충돌**이다.
- **`SUB001`·`SUB002`·`SUB004`** — G1 은 구독 상태 어휘, G2 는 영업 콘솔 메뉴 코드다. 축이 다르므로 전건 **글자 충돌**이다.
- **`SUB003`** — 구독 상태 어휘에만 있는 singleton 이라 중복이 아니다. 관계 판정 대상이 아니다.
- **`CRP_SUB002`·`CRP_SUB003`** — corp 접두 메뉴 코드이며 각각 한 파일에만 있다. 무접두 `SUB002`·`SUB004` 와 **다른 코드**이므로 그 중복에 합류하지 않는다.

## 계획서 이탈 기록

계획서는 봉인돼 있고(`plan_hash`) 이 창은 본문을 고치지 않았다. 아래는 **구현이 계획서 문면과 갈린 자리**와 그 사유다. 각 항목은 계획서가 겨냥한 것을 지키면서 문면만 바꾼 것이거나, 계획서의 전제가 실측으로 틀린 자리다.

### D1 — 인수 backlog 판정 접미 어휘

- **계획서 문면**: Task 8 (a) — 인수 행에 `ADJUDICATED <날짜>: …` 접미를 단다.
- **실제**: `— CLOSED-BY-DECISION 2026-08-31: ADJUDICATED 2026-08-31: …` 로 달았다.
- **사유**: `ADJUDICATED` 는 `docs/_meta/verify/fixtask-projection.mjs` 의 `VOCAB`(`FIXED-M8` · `RESOLVED-ALREADY` · `CLOSED-BY-DECISION` · `OUT-OF-OWNERSHIP` · `RESOLVED`) 밖이다. `stripMarker` 는 **첫 VOCAB 마커 자리에서** 본문을 자르고 그 본문으로 digest 를 낸다. 어휘 밖 토큰을 달면 아무것도 잘리지 않아 이 행의 digest 가 움직이고, `golden.txt` `fixtask.cohort_m8` · `fixtask.cohort_count` 가 함께 움직인다. 그러면 `verify.sh --check` 가 붉어져 검사 `#1` 이 실패하는데, 이 PRD 에는 `docs/_meta/verify/**` 개정 위임이 없어 `--update` 로 재동결할 수 없다.
- **반사실 실측**: 접미 부착 전후로 `node docs/_meta/verify/fixtask-projection.mjs digest-set` 을 돌려 43행 집합이 **바이트 동일**함을 확인했다. VOCAB 마커를 앞에 두었기 때문이다.
- **겨냥한 것은 지켰다**: 계획서가 요구한 `ADJUDICATED <날짜>:` 문면이 그 뒤에 그대로 실려 있고, 검사 `#12` 가 그 문면을 정규식으로 문다.

### D2 — 이 창의 신규 이월은 전부 파이프 표 행이다

- **계획서 문면**: Task 8 (a) — 결손을 `id=` 를 가진 표 행으로 올린다.
- **실제**: 그대로 따랐고, **평문 행을 하나도 쓰지 않았다**는 사실을 여기 명시한다.
- **사유**: 같은 D1 의 기제다. `parseBacklogRow` 는 `^\d{4}-\d{2}-\d{2} \| …` 만 잡으므로 평문 행은 동결 코호트에 합류하고 표 행은 합류하지 않는다. 신규 이월을 평문으로 쓰면 `fixtask.cohort_count` 가 43 → 51 로 움직인다.
- **대가**: mccp 쪽 backlog 소비기는 표 헤더 아래만 읽고 `fixtask-projection.mjs` 는 평문만 읽는다 — 두 소비기가 서로의 절반에 눈이 멀어 있다. 이 창의 8행은 앞쪽 소비기에만 보인다. 양쪽에 보이게 하려면 같은 결함을 두 물리 행으로 써야 하는데 그것은 원장의 유일성을 깬다. 이 어긋남 자체는 이미 backlog 에 올라 있는 축이다.

### D3 — 검사 `#11` 에 흡수 기록 축을 더했다

- **계획서 문면**: Task 7 — *"흡수는 매 건 반사실로 확인한다: 같은 추출을 흡수 전후로 돌려 결과가 뒤집히는 것을 원장에 남긴다."* `#11` 의 `# 기대:` 는 그 축의 검사를 명시하지 않는다.
- **실제**: 원장에 `kind:"finding"` 행을 두고 `#11` 이 그 행마다 `mark_before ∈ {사실과다름, 빠짐}` · `doc ∈ 모집단` · 반사실 기록 실재 · 미흡수분의 이월 id 를 검사하며, 미흡수 지적 수와 잔여 행 수가 짝지어지는지까지 문다.
- **사유**: 수렴 계수 0 은 **두 경로**로 나온다 — 지적이 없었거나, 있었고 고쳤거나. 판정 행만 보면 그 둘이 구별되지 않아 *"고치지 않고 0 을 적는"* 형태가 통과한다. 계획서가 요구한 반사실을 기계가 무는 자리가 필요했다.
- **이것은 계획서에 없는 요구를 더한 것이 아니다** — 계획서 산문이 요구한 것을 검사로 옮긴 것이며, 검사 번호도 새로 만들지 않았다.

### D4 — 비어 있어도 되는 원장에 표식 행 규약을 세웠다

- **계획서 문면**: `#7` 은 *"약한 인용 <w>건 전건 의도 사유 보유"*, `#13` 은 관계 축 변경의 결박을 요구한다. 둘 다 0 이 정상값이다.
- **실제**: 두 원장이 비어 있을 때 표를 지우지 않고 `| — | — | 해당 없음 — <표식> |` 한 행을 두게 했고, `optionalLedger` 가 그 표식을 검사한다.
- **사유**: 표를 지우면 파서의 0건 방어가 함께 꺼져 **빈 원장과 저작 누락이 구별되지 않는다.** 표식 행을 요구하면 저자가 "비었다" 를 명시적으로 주장해야 하고 그 주장이 출력에 남는다.
- **이 규약이 실제로 물었다**: 라운드 1 F1 을 흡수하자 약한 인용이 0 → 1 이 되어 표식 행이 실제 행으로 교체됐다. 그 사이 `#7` 이 한 번 붉었다(아래 D5 의 반사실).

### D5 — 리뷰 라운드 1 이 낸 지적 3건과 그 반사실

계획서 Task 7 은 라운드 1 의 흡수를 요구하고 `#11` 이 흡수 후 수렴 0 을 문다. 이 창이 실제로 낸 지적과 흡수 전후의 게이트 뒤집힘은 원장 `kind:"finding"` 행에 있고, 요지는 이렇다.

| id | 부류 | 자리 | 사실 | 반사실 |
|---|---|---|---|---|
| R1-F1 | 사실과다름 | `05-agency-corp.md#ALLOW_IP_MASTER` | *"관리자 입구에서만 열린다"* 가 거짓 — 인증 결선 줄이 주석 처리돼 있고 다른 결선이 없다 | 흡수 전 `#7` 초록(약한 인용 0) → 주석 줄을 앵커로 들이자 `#7 FAIL: 사유 없는 약한 인용 3건` → 의도 사유를 원장에 넣자 다시 초록. **두 번 뒤집혔다** |
| R1-F2 | 빠짐 | `05-agency-corp.md#AUTHORITY_NAME_MASTER` | 관리자 제한을 주장하면서 그 강제 지점을 앵커로 갖지 않았다 | `#7` 의 anchors 계수 181 → 185 |
| R1-F3 | 빠짐 | `02-treat.md#MEDICAL_RAW_EXPORT` | *"판정 층을 세지 않았다"* 가 아니라 **없다** — 원본 진료자료 내려받기가 건 식별자만으로 열린다 | 한계 서술이 *"세지 않았다"* → *"결선돼 있지 않다는 것까지가 관측이다"* 로 바뀌고 `#7` 이 새 앵커를 동결 blob 에서 확인했다 |

F1·F3 은 코드 결함이기도 하다. 코드 수정은 우산 PRD Out of scope 이므로 문서에 관측으로 싣고 backlog 두 행(`id=50m8-allow-ip-unauthenticated` · `id=50m8-medical-download-unauthenticated`)으로 내보냈다.

### D6 — 계획서 관측 10 의 전제가 이 창에서 반증됐다

- **계획서 문면**: 관측 10 — *"`verify.sh --check` 가 이 환경에서 10분 안에 출력을 내지 않았다. 전경 10분 출력 0행 · SIGTERM. 배경 실행도 출력 0행."*
- **실측**: 이 창의 배경 실행이 **stdout 136행**을 내고 마지막 줄이 `check PASS`, rc 0 이었다. 재현되지 않는다.
- **판정**: 앞선 관측은 도구 결함이 아니라 실행 환경 쪽 실패(fork 고갈 등)로 보인다. **검사 `#1` 의 설계는 바꾸지 않았다** — 그 설계가 겨냥한 것은 "느리다" 가 아니라 **"침묵 + rc 0 을 통과로 치지 않는다"** 이고, 그 위험은 반증되지 않았기 때문이다. `#1` 은 여전히 `timeout` 으로 감싸고 stdout 마지막 줄을 대조하며 모든 비정상 종료코드를 부분 stdout 행수와 함께 이름 있는 실패로 가른다.
- 기록은 backlog `id=50m8-verify-silence-refuted`.

### D7 — 온보딩 답변을 위해 정책 2건을 추가로 저작했다

- **계획서 문면**: Task 3 은 8편 저작을, Task 6 (b) 는 온보딩 원장을 요구한다. 어느 정책을 몇 건 쓸지는 계획서가 정하지 않는다.
- **실제**: 1차 저작 뒤 온보딩 4문항을 대조하니 `Q34`·`Q35`(부분 실패 시 잔여 상태)에 답할 서술이 없어 **미성립 4/4** 였다. 소스를 다시 읽어 원자성 경계가 실제로 관측 가능함을 확인하고 정책 2건을 신설했다 — `02-treat.md#ANALYSIS_COMPLETION_FANOUT` 과 `03-subscription-order.md#SUBSCRIPTION_POST_PAYMENT_ATOMICITY`.
- **결과**: 성립 2 · 미성립 2. 남은 둘(`Q20` 진입부 모듈 순서 · `Q21` 총론 한도 규칙의 도메인 적용)은 해소에 호출 사슬 재정독과 타 PRD 축이 필요해 이월 id 를 달아 남겼다.
- **미성립을 0 으로 만들지 않은 것은 규약대로다** — 얕은 답을 성립으로 적는 쪽으로 기울지 않기 위해서이고, 대신 세 자리(`#9` 이월 id · `#12` id 실재 · `#14` Status 계수)가 그것을 숨기지 못하게 한다.

### D8 — `String.replace` 치환문자열 사고

Task 8 의 backlog 편집을 `s.replace(ANCHOR, SUFFIX)` 로 돌렸는데, `SUFFIX` 에 정규식 리터럴 `` `^(CRP_)?SUB[0-9]{3}$` `` 가 들어 있었다. `String.prototype.replace` 는 **치환문자열의 `$` 시퀀스를 특수 취급**하고 그중 하나가 "매치 앞부분 전체" 로 펼쳐져 backlog 가 통째로 복제됐다(421행 → 두 배). 추적 파일이라 `git checkout -- ` 로 복구했고, 이후 이 창의 모든 문자열 치환은 `split().join()` 으로만 한다. 같은 사고가 그 사고를 고치려던 패치 스크립트에서 한 번 더 났다 — 스크래치 파일이라 무해했다.

### D9 — 로컬 코드 리뷰(`/mccp:code-review` Local Review Mode) 흡수 5건

리뷰 대상은 이 창의 미커밋 변경 18건 전부(하네스 · 8편 · 계획서 · 원장 · PRD · backlog)다. CRITICAL·HIGH 0. 지적 5건 전건을 하네스에서 흡수했다. 문서 8편과 계획서 본문은 손대지 않았다 — `plan_hash` 를 움직이지 않기 위해서이고, 지적이 전부 하네스 코드에 있었기 때문이다.

| # | 층위 | 자리 | 지적 | 흡수 | 반사실 |
|---|---|---|---|---|---|
| R1 | 8 | `insuniverse-docs-50-domain-m8.gates.mjs` `check11` | `Number(counts.미검증 ?? unver) !== unver` 가 **fail-open**. 원장이 `counts.미검증` 키를 아예 안 적으면 `??` 가 `unver` 로 채워 정의상 통과한다 — 이 검사가 겨냥한 바로 그 형태를 못 잡는다 | `??` 제거. 부재는 `NaN !== unver` 로 붉는다. 실패 메시지에 원장값(부재 포함)과 재계수를 병기 | 키 삭제 → **수정 전 rc 0 · 수정 후 rc 1** (`원장 counts.미검증 (부재) · 판정 행 재계수 522`). 원복 바이트 일치 확인 |
| R2 | 8 | 같은 파일 `changedSince` | `core.quotepath` 가 unset(=true)이라 비-ASCII 경로가 8진 이스케이프로 나오고, 따옴표만 벗기는 파싱 뒤 `changedDocs` 의 `exists` 가 false 를 내 **`#11` 모집단이 조용히 준다**. `#13` 은 같은 경로를 허용집합 밖으로 붉히는데 `#11` 만 침묵한다 | `git` 두 호출에 `-c core.quotepath=false` 를 앞세운다 | `docs/20-site/09-한글프로브.md` 를 두고 대조 — **수정 전 rc 0(조용히 통과) · 수정 후 rc 1** (`모집단에 있고 원장에 없는 문서 1건`). 프로브 삭제 후 rc 0 재확인 |
| R3 | 7 | 같은 파일 `check-all` | `notRun` 이 `e.runnable` 을 자기 자신과 비교해 **정의상 빈 집합**이었다 — 죽은 검사가 `실행 집합 == 등록부 집합` 주장을 지지하는 것처럼 보였다 | 죽은 대조를 지우고, 그 등식이 **루프의 구성**으로 성립함을 주석에 적고 출력에 **실행한 번호를 그대로 열거**한다. 검사로 지지되는 등식(`등록부 == Validation 호출`)만 `차집합 0` 으로 말한다 | 삭제라 반사실 없음. `check-all` 출력 문면이 바뀐 것이 관측이다 (아래 관측 참고) |
| R4 | 4 | 같은 파일 `docAnchors`·`docCitations` | 0건과 형식 이탈을 **오류 메시지 문자열**(`/0건/`)로 갈랐다. 문면이 바뀌면 형식 이탈이 0건 취급으로 샌다 | `docAnchors` 가 `{ error, empty: true }` 를 내고 호출부가 `!r.empty` 로 가른다. `error` 는 유지 — `--selfprobe` 변이 축이 빈 입력에서 `error` 를 요구한다 | 현재 입력에서 **관측 불가**. 8편 전건이 인용을 갖고(274건) 0건 문서가 없어 이 분기가 타지 않는다. 구조 변경이며 동작 변경이 아니다 — 숨기지 않고 그대로 적는다 |
| R5 | 4 | 같은 파일 `check12` | `prd.slice(prd.search(...))` 가 존재 검사보다 **먼저** 돈다. 부재 시 `search` 가 −1 이라 `slice(-1)` 이 마지막 한 글자를 준다 | `oqAt` 를 먼저 얻어 `< 0` 을 먼저 붉히고 그 다음 슬라이스 | 다음 줄 `die` 가 이미 막고 있었으므로 **관측 불가**. 순서 위생이며 동작 변경이 아니다 |

**관측 — 계획서 `#17` 기대 문면과 실제 출력이 갈렸다.** R3 흡수로 `check-all` 의 마지막 줄이 계획서 `# 검사 #17:` 의 `# 기대:` 가 적은 `실행 집합 == 등록부 집합 == Validation 호출 집합 · 차집합 0` 에서 `실행 <번호 열거> · 등록부 집합 == Validation 호출 집합 (차집합 0 — reenumerate) · 실행 집합은 그 등록부를 직접 돈다` 로 바뀌었다. 계획서 본문을 고치면 `plan_hash` 가 움직여 `mccp-plan-codex` 영수증이 stale 이 되므로 **고치지 않았다**. 새 문면이 종전보다 약한 주장이며(검사로 지지되는 등식만 `차집합 0` 으로 말한다) 기계 검사는 어느 쪽 문면도 파싱하지 않는다.

**관측 — 계획서 `#8` 의 `# 음성:` 한 줄이 성립하지 않는다.** *"사각지대 출력행을 지우면 `#17 FAIL`"* 이라 적었으나 `reenumerate` 는 계획서와 등록부만 읽고 **검사의 stdout 문면은 보지 않는다**. `check-all` 도 `^#N ok` 만 본다. 즉 `#8` 의 사각지대 출력행을 지워도 어느 검사도 붉지 않는다. 계획서 본문 수정은 위와 같은 이유로 하지 않았고 여기 기록으로 남긴다.
