# task-2951 · InsuRo 실손 활성화 Phase A — codex 지적 3건 해소 + 항목별 검증 판정

- **작업일**: 2026-08-14
- **팀**: dev2-team (오딘)
- **기준 커밋**: `origin/main` = `1c1bf1c4ebd3aed742de5a7160f1d810a2809363` (PR #212 MERGED)
- **repo**: `/home/jay/projects/InsuRo`
- **변경 파일**: **0건** (판정·문서 위주 task. 코드/데이터 값 무변경 — repo `server/silson/` git status clean 확인)
- **PR**: 불필요 (task 명세대로)

---

## S — Situation (상황)

실손 지식 모듈(`server/silson/`)은 PR #212 머지로 main에 들어왔으나 **`primary_source_verified`(이하 psv) 전 항목 false = 고객 자동판정 hard-block** 상태의 휴면 모듈이다. 회장 승인 방침은 "일괄 true 금지, **항목별 검증완료분만 선택적 true**"이며, codex↔ANU 조율에서 지적 3건이 도출됐다. Phase A는 이 지적 해소 + 항목별 검증 판정이고, 값 전환은 Phase B다.

## C — Complication (문제)

검증 결과 **task-2951의 전제와 실제 코드 구조가 어긋난다.**

`primary_source_verified`는 **손으로 적는 값이 아니라 계산값**이다 (`server/silson/provenance.py:21`, 축자):

> 레코드의 ``primary_source_verified`` 는 **계산 결과**이며 손으로 적는 값이 아니다.
> JSON 에 함께 저장돼 있더라도 :func:`audit_primary_source_flags` 가 매번 재계산해
> 대조한다(데이터에 몰래 ``true`` 를 박아 넣는 경로를 막는다).

계산식(`compute_primary_source_verified`, provenance.py:312):

> ``True`` ⟺ ``sources[]`` 중 하나 이상이 registry 의 tier1 항목으로 매핑되고,
> **그 항목의 ``primary_verification.verified`` 가 참**이다.

즉 psv의 유일한 입력은 `sources[]`의 tier1 보유 여부 + 해당 tier1 자료의 원문 대조 완료 여부다. **codex 지적 1~3(MRI 한도 / plan_type 스키마 / coverage_gap 재분류)은 이 계산식의 입력이 아니다.** 1~3을 아무리 검증해도 psv 값은 바뀌지 않는다.

## Q — Question (질문)

그렇다면 (a) codex 지적 3건은 각각 어떤 판정인가, (b) 실제로 활성화 가능한 항목은 무엇이며 그 조건은 무엇인가?

## A — Answer (답)

**축을 둘로 분리해 판정한다.** 지금까지 하나로 뭉뚱그려져 있던 "검증 통과"와 "활성화 가능"은 서로 직교하는 별개 축이다.

---

# 1. MRI 300 독립원천 재확인 — **활성화 불가** (값 확정은 보류)

담당: 토르(백엔드) · 팀장 교차검증 완료

## 1-1. 독립원천 여부 → **독립원천 아님 (반증됨)**

`comparison_matrix.json`의 GEN3/coverage_limit conflict note는 현재 이렇게 주장한다 (축자):

> "KB 계열은 서로 다른 2개 페이지(KB02·KB11)에서 **독립적으로** 300만원을 기재하는 반면 A2 는 200 이 단 1회 등장한다."

**이 주장은 원문 대조로 반증된다.**

`manifest.json` 실측 — KB00~KB15는 전부 동일 파일 시리즈다:

```
"filename": "총정리(KB손보)/KakaoTalk_20250808_122716321[_NN].png"   (NN = 00..15)
"source_tier": 2   (전 페이지 동일)
```

OCR 원문의 페이지 표기로 교차확인:

| 파일 | 페이지 표기 (축자) | 위치 |
|---|---|---|
| KB02 | `실손의료비보장 변경 ㅣ 03` | HISTORY 요약(2~3p)의 3쪽 |
| KB11 | `12 \| 실손의료보험 총정리` | 3세대 상세(12p) |
| KB13 | `14 \| 실손의료보험 총정리` | 4세대 상세(14p) |

→ KB02·KB11·KB13은 **KB손해보험 사내교육자료 「실손의료보험 총정리」 한 권의 3쪽·12쪽·14쪽**이다. matrix 자체의 `sources.KB` 정의도 이들을 "16장" 단일 그룹으로 묶고 있어, note의 "독립적으로"는 **matrix 내부 자체모순**이다.

**구분해서 판정한다:**
- **독립 출처(independent source)로서의 가치: 없음.** 같은 저자·같은 인쇄물이 요약표/상세표 두 형식으로 같은 숫자를 반복한 것. 2회 반복은 독립 증거가 아니다 — codex 지적이 정확했다.
- **내부 일관성(internal consistency) 증거로서의 가치: 있음(제한적).** 같은 문서 안 3개 표에서 300이 일치. 단, **저자 원천 오류라면 3페이지 모두 같은 오류를 반복하는 것이 오히려 자연스럽다** — 다수결 논리는 성립하지 않는다.

## 1-2. 같은 보장차원인가 → **같은 축일 개연성 높음, 단 시간단위 미확정**

A2 축자 (`A02_변천사_보장제외.md` L25, 행 이름 = **가입금액**):

> `기본 : 좌동 3대비급여특약 : 도수치료 350, 주사료 250, MRI 200`

KB02 축자 (3세대 최대보장금액):

> `* 특약(3대 비급여) : 도수(350만/50회),<br>주사제(250만/50회), MRI(300만/제한없음)`

- **도수치료 350·주사료 250이 완전 일치** → 동일 축(3대 비급여 특약 한도)일 개연성이 높다. matrix `cols_meta`도 이 열을 `가입금액/보장한도`로 병기 정의한다.
- 다른 차원 혼입(1회당 한도 / 횟수제한)은 **아님**으로 판정. KB11/KB13은 "1회당 2만원 또는 30% 중 큰 금액 공제"라는 별개 축을 따로 명기하고 있어 축이 분리돼 있다.
- **미해소**: A2에는 `연간`이라는 시간단위 수식어가 **원문 미기재**다(KB는 "1년 단위로 연간" 명시). "가입금액"↔"보상한도" 등치도 원문에 정의문이 없다.

## 1-3. A2=200이 단일 셀 오기인가 → **보류 (확정 불가)**

A02 전문 전수 검색 실측:

| 검색어 | 등장 |
|---|---|
| `300` | **0건** |
| `자기공명` | **0건** |
| `MRI` | 1건 (L25 가입금액 행) |
| `200` | 2줄 — ① 자기부담금 행 "연간 자부담 200만 원 한도"(2세대 열만) ② 가입금액 행 "MRI 200"(3세대 열) |

- **OCR 인접 오배치 가설은 기각된다**: 두 행은 5개 행 떨어져 있고, "MRI 200"은 "도수치료 350, 주사료 250, MRI 200"처럼 MRI에 명시적으로 결부된 라벨을 달고 있다. 인접 셀 유출(bleed)로 보기 어렵다.
- GEN4의 `비급여3대특약 좌동`은 좌측 3세대 열을 가리킨다 → **GEN3·GEN4의 "200 vs 300"은 두 개의 독립 증거가 아니라 A2 셀 하나에서 파생된 단일 이슈**다.
- 백의 자리 하나(2↔3)만 다른 패턴은 전사 오류의 전형이나, 이는 **패턴 추정이지 원문 근거가 아니다**. A2는 `source_tier=null`(출처 표기 0개)이고, A1(협회공시)에는 3·4세대 특약 한도 항목 자체가 없어 대조 불가.

## 1-4. 판정

| 질문 | 판정 |
|---|---|
| MRI 300을 활성화 근거로 사용 | **불가** — 근거였던 "독립 2출처"가 반증됨 |
| MRI 대표값(200 vs 300) 확정 | **보류** — 원문만으로 판별 불가 |
| 현행 조치 | `primary_wins`(A2=200) + `corrected_representative_value` 없음 유지 |

**재판정 조건**: ① 원본 이미지(`변천사&보장안하는손해.jpg`) 확대 블라인드 재판독, 또는 ② KB/A2 계열 밖 제3 독립출처(협회 공시·표준약관) 확보.

## 1-5. ★ 부수 발견 — matrix note 사실오류 1건

`comparison_matrix.json` GEN3/coverage_limit note의 "독립적으로"라는 서술은 사실과 다르다(동일 문서 3쪽·12쪽). **데이터 값이 아니라 note 문구의 오류**이며 정정이 필요하나, Phase A는 값·문서 무변경 원칙이므로 **수정하지 않고 보고만 한다**(Phase B 또는 별도 surgical 정정 대상).

---

# 2. plan_type 스키마 고정 — **보류** (규칙 산출 완료, 코드 강제 미비)

담당: 미미르(스키마 설계) · 팀장 교차검증 완료

## 2-0. 전제 정정

task 명세는 `plan_type`을 `standard/selective/single/null` **평면 스칼라 4값**으로 기술했으나, 실측 결과 `plan_type`은 **중첩 객체**다. `"plan_type": "single"` 같은 스칼라는 데이터에 **0건** 존재한다. 아래는 실제 구조 기준 판정이다.

## 2-1. 현황 실측

| 항목 | 실측값 |
|---|---|
| 전체 레코드 | 10 (generations 8 + product_kinds 2) |
| `plan_type` 키 보유 | 5/10 레코드 (deductible·payout_rate 양쪽 대칭, 비대칭 0건) |
| `scheme: "single"` 명시 | **2노드** — GEN4_2021_07/deductible, /payout_rate |
| `scheme` 키 **누락** | **8노드** — GEN2_STD1·GEN2_STD2·GEN2_STD3·GEN3 × {deductible, payout_rate} |
| leaf `{value,source_id,ref}` | 32개 (value=null 8개, 전부 사유 note 보유 → 환각 0) |

**즉 2-type 레코드 4건(8노드)은 `scheme`을 암묵 표현하고 GEN4만 명시한다** — codex가 지적한 "single 의미가 스키마 수준에 없다"의 근본 원인이다.

## 2-2. 이중진실원천 — 실재 위험 확인

`models.py:199-211`에 `GenerationProfile.plan_type(field_name)` 메서드가 이미 존재하며 docstring에 "축 자체가 없으면 None / `scheme:"single"`이면 단일 체계 — 호출부가 구분할 수 있다"고 설계 의도가 적혀 있다.

**그러나 이 메서드는 코드 전체에서 호출 0건이다** (`grep -rn "\.plan_type(" server/` → **0**). 설계만 되고 미사용인 dead API다.

실제 소비 경로:
- `analysis_bridge.py:107-108` `describe_for_analysis()` — `deductible`/`payout_rate` 딕셔너리를 **평면키와 plan_type이 뒤섞인 원형 그대로** 페이로드에 실어 보낸다. 우선순위 해석 로직 **0건**.
- `knowledge.py` `validate_bundle()` — plan_type/평면키 정합성 검사 **0건**.
- `gating.py:26-33` — 게이트는 **고객 자동판정 경로만** 차단하며 `analysis_bridge`(설계사 참고 경로)는 **명시적으로 건드리지 않는다**.

**실재 불일치 1건**: GEN2_STD2/payout_rate — 평면 원문 `"선택 급여 90%\n비급여 80%\n표준 20%"` vs `plan_type.standard` = KB07 출처 `"표준형: …80%…"`. 같은 "표준형" 개념에 **20% vs 80%**가 공존한다. 다행히 `conflicts[]`에 이미 문서화돼 있어 **미문서화 불일치는 0건**이다.

## 2-3. 스키마 규칙 문서 (산출물, 그대로 채택 가능)

### R-1. `scheme` 3상태 고정

| 상태 | 표현 | 의미 |
|---|---|---|
| 축 미적용/미조사 | `plan_type` 키 **부재** (현행 유지) | "표준형/선택형 축을 아직 조사·적용하지 않았다." **"그런 구분이 원천적으로 없다"는 도메인 단정이 아니다** |
| 축 있음·2종 실재 | `scheme: "standard_selective"` **명시 추가** | 원문에 2종 체계가 실재하며 값이 각각 구조화됨 |
| 축 있음·단일 체계 | `scheme: "single"` (현행 유지) | 원문 어디에도 유형 라벨 없음. standard/selective는 반드시 null |

→ **`single` = "축은 존재하나 유형이 1종"으로 택일 고정.** "축 자체가 없음"은 `single`이 아니라 **키 부재**로 표현한다(모호성 제거).

### R-2. 평면키 ↔ 축 우선순위

1. `plan_type` **키 부재** → 평면키가 정본.
2. `scheme == "single"` → 평면키가 정본(standard/selective는 null이므로 비교대상 아님).
3. `scheme == "standard_selective"` → **평면키는 정본이 아니다.** 소비측은 대상 고객의 실제 plan type을 알아야 하며, 알면 해당 서브키를 읽는다. **plan type 미상 상태에서 평면키를 대표값으로 자동 사용하는 것은 금지(fail-closed 권장)** — GEN2_STD2 실측상 표준형 고객에게 틀린 숫자를 줄 수 있음이 확인됐다.
4. `원문` 키는 어떤 경우에도 정본이 아니다(감사·재검증용 보존 필드).

### R-3. 불변식 4종

```
PT-1  plan_type 존재 시 scheme ∈ {"standard_selective","single"} 必
PT-2  scheme=="single"  → standard/selective 반드시 null
PT-3  scheme=="standard_selective" → standard·selective 모두 존재,
      각 leaf {value,source_id,ref}, value=null 이면 note 必
PT-4  평면값이 존재하고 scheme=="standard_selective" 이며 평면값의 숫자가
      standard/selective 어느 leaf 에도 재현되지 않으면 → conflicts[] 에
      해당 field 항목이 반드시 존재해야 한다 (미문서화 시 validate_bundle FAIL)
```

### R-4. 마이그레이션 영향도 (실측)

| 불변식 | 위반 |
|---|---|
| PT-1 (`scheme` 누락) | **8노드 / 4레코드** — GEN2_STD1·STD2·STD3·GEN3 |
| PT-2 | **0건** |
| PT-3 | **0건** (leaf 32개 전수, null 8개 전부 note 보유) |
| PT-4 (미문서화 dual-source 불일치) | **0건** |

→ **데이터 값을 고쳐야 할 위반은 0건.** 필요한 변경은 8개 노드에 `"scheme": "standard_selective"` **키 추가뿐이며 순수 additive**다. (Phase A 원칙에 따라 **적용하지 않고 목록만 보고**한다.)

## 2-4. 판정: **보류**

규칙은 완성됐고 데이터 값 위반은 0건이나:
1. 해소책(scheme 명시화·우선순위 규칙)이 **텍스트 규칙일 뿐 코드로 강제되지 않는다**. `plan_type()` 0회 호출, `describe_for_analysis()`는 여전히 미해석 노출.
2. PT-1 additive 마이그레이션과 `validate_bundle()` 검증 로직 추가가 **미적용**.
3. `analysis_bridge`(설계사 화면 경로)는 애초에 게이트 대상이 아니므로 plan_type 오독 위험이 열려 있다.

---

# 3. coverage_gap 4건 재확인 — **검증 통과 (재분류 4/4 유지)**

담당: 헤임달(QA) · 팀장 교차검증 완료

## 3-1. 건별 판정

codex가 물은 "(a) 같은 비교축 위의 단순 누락 vs (b) 비교불가한 다른 상품범위" 중 **4건 전부 (a)**로 확인됐다. 시행기간·세대·필드축이 전부 정합한다.

| # | 셀 | A2 축자 | KB 축자 | 시행기간 대조 | 판정 |
|---|---|---|---|---|---|
| ① | GEN2_STD1/deductible | `입원 10%(연간 자부담 200만 원 한도)…` | KB01 `표준형 20%<br>선택형 10%` | A2 `'09.8(10)월~'13.1(4)월` ↔ KB05 `[09년 8월 1일 ~ 12년 12월 31일]` **일치** | **재분류 유지** |
| ② | GEN2_STD2/deductible | `입원 20%(연간 자부담 200만 원 한도)…` | KB01 `표준형 20%<br>선택형 10%[~2015.8)…` | A2 `'13.1(4)월~'15.9월` ↔ KB07 `[13년 1월 1일 ~ 15년 8월 31일]` **일치** | **재분류 유지** |
| ③ | GEN2_STD3/deductible | `입원 급여 10% 비급여 20%…` | KB01 `선택형 II 10%[2015.9~)` / 비급여 `선택형 II 20%[2015.9~)` | A2 `'15.9월~'17.4월` ↔ KB09 `[15년 9월 1일 ~ 17년 3월 31일]` **일치** | **재분류 유지** |
| ④ | GEN3/deductible | `좌동 특약 - 2만 or 30%` | KB02 `표준형 20%<br>선택형 10%` / KB11 `1회당 2만원 또는 …30%중 큰 금액 공제` | KB02 `2017.04~2021.06` ↔ KB11 `[17년 4월 1일 ~ 21년 6월 30일]` **일치** | **재분류 유지** |

**반증 시도 결과**: 4건 모두 **반증 근거 미발견**. 특히 시행기간 서브라벨(`[~2015.8)` / `[2015.9~)`)이 A2 연표와 정확히 정렬되어 "다른 상품범위" 가설이 성립하지 않는다.

**공통 관찰**: A2가 일치하는 유형이 세대마다 다르다(STD1=선택형, STD2=표준형, STD3=선택형II, GEN3=선택형). 이 비일관성은 "A2가 매번 한 유형만 기재한다"는 사실을 강화할 뿐 판정 기준을 위반하지 않는다.

## 3-2. conflict vs coverage_gap 판정규칙 (산출물, 예시 포함 고정)

```
STEP 0 — 비교축 정합성 (모든 판정의 선행조건)
  두 소스가 같은 세대/시행기간을 가리키는지 A2 `period` 행 또는 KB 챕터 제목
  날짜로 먼저 확인. 어긋나면 STEP 1~2 로 가지 않고 즉시 not_comparable.

STEP 1 — conflict
  같은 축 위에서 A·B 가 동일 항목에 서로 다른 값을 각각 확정적으로 주장하고,
  어느 한쪽이 상대의 하위유형 중 하나와 정확히 일치하는 관계로 환원되지 않을 때.
  예) GEN1_2003_10/excluded_coverages — A2 `치매=X` vs KB03 `치매(F00~F03) 보상함`
      GEN3/coverage_limit — A2 `MRI 200` vs KB02·KB11 `MRI 300`

STEP 2 — coverage_gap  (아래 5개 전부 충족해야 성립)
  (1) B 의 셀이 이름 붙은 하위축(plan_type={표준형,선택형})으로 ≥2 멤버로 분해됨
  (2) A 의 셀은 하위축 라벨 없이 flat 값 1개만 기재
  (3) A 의 flat 값이 B 의 멤버 중 정확히 하나와 (단위 정규화 후) 완전 일치
  (4) A 는 B 의 나머지 멤버에 상반된 값을 주장하지 않는다 — 침묵할 뿐이다
  (5) STEP 0 통과
  예) GEN2_STD1/deductible — A2 `입원 10%` = KB01 `선택형 10%`, `표준형 20%` 는 침묵
      GEN2_STD2/deductible — A2 `입원 20%` = KB01 `표준형 20%`, `선택형 10%` 는 침묵

STEP 3 — not_comparable  (신설 제안)
  STEP 0 에서 시행기간·세대·대상범위가 애초에 어긋나는 경우.
  → 현재 130셀 중 해당 사례 0건 (4건 + 13 conflicts 전부 STEP 0 통과).
    향후 새 소스 추가 대비로 카테고리 신설만 권고.
```

## 3-3. 대안 우선규칙 (A1>A2>KB 금지 시)

**규칙**: coverage_gap 셀은 primary(A2) 단독 채택을 금지하고 **축으로 분해**한다. B가 제시한 하위유형 라벨마다 슬롯을 만들고, 각 슬롯에 그 유형의 원문을 그대로 싣는다. 어느 유형에도 원문이 없으면 `null` + 사유 note (허구 채움 금지).

**4건 대입 검증 결과 — 현재 `silson_generations.json`(revision 5)이 이미 이 규칙대로 구현되어 있으며 불일치 0건**:

| 셀 | plan_type.standard | plan_type.selective | OCR 대조 |
|---|---|---|---|
| GEN2_STD1 | 급여/비급여 `표준형 20%`(KB01) | `선택형 10%`(KB01) | verbatim 일치 |
| GEN2_STD2 | `표준형 20%` + 통원 KB07 verbatim | `선택형 10%[~2015.8)` + 통원 KB07 verbatim | verbatim 일치 |
| GEN2_STD3 | `표준형 20%`(통원 null+사유) | 급여 `선택형 II 10%` / 비급여 `선택형 II 20%` | verbatim 일치, KB09 통원행 부재도 정확 반영 |
| GEN3 | 급여 `표준형 20%` / 비급여 null+사유 / 통원 KB11 verbatim | 급여 `선택형 10%` / 비급여 null | verbatim 일치 |

원문에 없는 값은 전부 null+사유로 비어 있다 — **환각 0 재확인**.

## 3-4. 판정: **검증 통과 (4/4)**

> ★ 팀원(헤임달) 원 보고는 이 4건을 "활성화 가능"으로 라벨했으나, 팀장 판정에서 **"검증 통과"로 재라벨**한다. 아래 §4에서 실증하듯 검증 통과와 psv 활성화는 직교하는 별개 축이며, 이 4건이 속한 레코드는 psv=true가 **구조적으로 불가능**하기 때문이다.

---

# 4. ★★★ 항목별 검증 판정 (활성화 게이트) — 핵심 발견

## 4-1. 실증: psv 계산 구조

실제 `provenance.primary_source_verified_map()`을 **실행**한 결과 (원본 파일 무변경, /tmp 스냅샷):

```
[현재] 실제 계산 psv: true=0  false=10
audit_primary_source_flags(현재 데이터) 위반: 0건   ← 저장값 = 계산값, 위조 없음
```

`sources_registry` 실측 — **`primary_verification.verified == true`인 항목이 0개**:

| source_id | tier | verified | reason (축자) |
|---|---|---|---|
| A1_KLIA_COMPARE | **1** | false | `협회 공시 원문 미확보 — 각주만 존재하고 문서명/공시항목/URL/조회일자가 없어 원문 대조 불가` |
| A2_HISTORY_EXCLUSIONS | null | false | `출처 표기 자체가 이미지에 없다(각주 0개) — 1차 출처 미상이므로 tier 추정 승격 금지` |
| KB_TOTAL_00 ~ 15 (16건) | 2 | false | `KB손해보험 제작 2차 자료 — 협회 공시/표준약관 등 1차 원문이 아니며, 자료 내에 1차 출처 인용이 없다` |

## 4-2. 실증: 레코드별 tier1 보유 여부

`map_source_token` 매핑 규칙: `A:표1`→A1(tier1), `A:표2`/`A:표3`→A2(tier null), `B:`/`C:`→KB(tier2).

| 레코드 | tier1 출처 | unmapped |
|---|---|---|
| GEN1_PRE_2003_10 | **NONE** | 0 |
| GEN1_2003_10 | **NONE** | 0 |
| GEN2_STD1_2009_08 | **NONE** | 0 |
| GEN2_STD2_2013_01 | **NONE** | 0 |
| GEN2_STD3_2015_09 | **NONE** | 0 |
| GEN3_2017_04 | **NONE** | 0 |
| GEN4_2021_07 | **NONE** | 0 |
| GEN5 | A1_KLIA_COMPARE | 0 |
| SENIOR_SILSON | A1_KLIA_COMPARE | 0 |
| PREEXISTING_SILSON | A1_KLIA_COMPARE | 0 |

## 4-3. 실증: 반사실 시뮬레이션 (A1 verified=true 가정)

메모리상 복사본에서 `A1_KLIA_COMPARE.primary_verification.verified = true`로 바꾸고 재계산 (**원본 파일 무변경**):

```
GEN1_PRE_2003_10   -> False        GEN5                -> True
GEN1_2003_10       -> False        SENIOR_SILSON       -> True
GEN2_STD1_2009_08  -> False        PREEXISTING_SILSON  -> True
GEN2_STD2_2013_01  -> False
GEN2_STD3_2015_09  -> False        true=3 / false=7
GEN3_2017_04       -> False
GEN4_2021_07       -> False
```

## 4-4. 결론 — 3가지 구조적 사실

### (가) codex 지적 1~3의 해소로 활성화 가능한 항목 = **0건**

지적 1~3의 대상 레코드는 MRI(GEN3·GEN4) / plan_type(GEN2_STD1~GEN3, GEN4) / coverage_gap(GEN2_STD1~3, GEN3)이다. **이들은 전부 tier1 출처 미보유 레코드**다. A1이 검증되어도 영구 false다. 즉 **1~3 검증 작업은 psv 활성화의 입력이 아니다.**

### (나) psv=true의 유일한 경로는 A1(협회 공시) 원문 확보이며, 1~3과 무관하다

활성화 후보가 될 수 있는 3건(GEN5 / SENIOR_SILSON / PREEXISTING_SILSON)은 **codex 지적 1~3과 아무 관련이 없다.** 조건은 오직 "협회 공시 원문 확보 → 축자 대조 → `A1.primary_verification.verified=true`"다.

### (다) "항목별 선택적 true"는 셀 단위로는 **구조적으로 불가능**

psv 축의 세분성은 **레코드 단위(10개)**다. 셀/필드 단위 psv 축은 **존재하지 않는다**. 게다가 레코드 단위 스위치조차 `sources[]`의 tier1 보유 여부로만 결정되므로, A1 검증 시 **3건이 동시에 켜진다** — 실질적으로 "A1 그룹 all-or-nothing"이다.

→ **codex 권고 (4) "항목별 검증완료분만 선택적 true"는 현재 코드 구조에서 그대로 실행할 수 없다.** 실행하려면 Phase B에서 psv 축의 세분화 설계(레코드 하위 필드/셀 단위 플래그) 또는 별도 게이트 축 신설이 선행돼야 한다.

## 4-5. 항목별 판정표 (task 완료조건 §4 산출물)

| # | 항목 | 검증 축 판정 | 활성화 축 판정 | 근거 |
|---|---|---|---|---|
| 1 | MRI 300 독립원천 | **불가** (근거 반증) | **불가** | KB02·KB11·KB13 = 동일 문서 3·12·14쪽. 대상 GEN3·GEN4 = tier1 미보유 |
| 1' | MRI 대표값 확정(200 vs 300) | **보류** | — | 원문만으로 판별 불가. 원본 재판독/제3출처 필요 |
| 2 | plan_type 스키마 고정 | **보류** | **불가** | 규칙 산출 완료·값 위반 0건이나 코드 강제 미비(`scheme` 8노드 누락, `plan_type()` 0회 호출). 대상 GEN2~GEN4 = tier1 미보유 |
| 3 | coverage_gap 4건 재분류 | **통과 (4/4)** | **불가** | 재분류 유지 타당, JSON 불일치 0건. 대상 GEN2_STD1~3·GEN3 = tier1 미보유 |
| 4 | GEN5 / SENIOR_SILSON / PREEXISTING_SILSON | 해당없음 | **조건부 후보 (3건)** | tier1(A1) 보유. 조건 = 협회 공시 원문 확보·대조. **1~3과 무관** |

### 집계 (콜백용)
- **검증 통과**: 1건 (coverage_gap 4셀)
- **보류**: 2건 (MRI 대표값 확정, plan_type 스키마)
- **불가**: 1건 (MRI 300 독립원천 승격)
- **활성화 가능(즉시)**: **0건**
- **활성화 조건부 후보**: 3건 (GEN5·SENIOR·PREEXISTING — 조건: A1 협회 공시 원문 확보)
- **psv 값 변경**: **0건** (Phase A 원칙 준수)

---

# 5. 회장 결정 필요 사항

1. **[P0] Phase B 설계 변경** — "항목별 선택적 true"가 현 구조에서 실행 불가함이 실증됐다. 택일 필요:
   - **(A) 세분화 개발**: psv 축을 셀/필드 단위로 확장하는 설계·구현 (범위 큼, 게이트 재설계 동반)
   - **(B) A1 확보 우선**: 협회 공시 원문을 확보해 GEN5·SENIOR·PREEXISTING 3건만 활성화. 1~4세대는 휴면 유지
   - **(C) 현행 유지**: 전 항목 false 유지, 활성화 보류
   - ※ 팀장 권고 = **(B)**. 1~4세대는 애초에 tier1 근거가 없어 세분화해도 켤 수 있는 셀이 0개이므로, (A)는 현 데이터에서 실익이 없다.
2. **[P1] MRI 대표값(200 vs 300)** — 원본 이미지 확대 블라인드 재판독을 발주할지 결정. (기존 미결 사항, 이번에 "KB 2출처=독립" 논거가 반증되어 300 승격 근거가 약화됨)
3. **[P2] matrix note 사실오류 정정** — GEN3/coverage_limit note의 "독립적으로" 문구. surgical 정정 PR 발주 여부.
4. **[P2] plan_type PT-1 additive 마이그레이션** — 8노드 `scheme` 키 추가 + `validate_bundle()` 검증 로직 추가. 값 무변경 순수 additive. 발주 여부.

---

# 6. 발견 이슈 및 해결

| 이슈 | 상태 | 처리 |
|---|---|---|
| 로컬 InsuRo 워킹트리가 stale(`ba63045`, task-2938 시점) | 해결 | `git fetch` 후 `origin/main`=`1c1bf1c` 확인. 워킹트리를 건드리지 않고 `git archive`로 /tmp 읽기전용 추출 |
| 디스크 96%(23G 여유) — worktree 생성 위험 | 해결 | worktree 미생성. `git archive` 580KB 추출로 대체 (디스크 영향 무시가능) |
| `silson` 패키지 import가 policy_extract/normalizer/analyzer 체인 의존 | 해결 | `provenance/knowledge/models` 3파일 + data만 담은 최소 shim 패키지(/tmp/task-2951-sim)로 실제 함수 실행 |
| task 명세의 `plan_type` 평면 스칼라 전제가 실제 구조와 불일치 | 해결 | §2-0에 전제 정정 명시 후 실제 중첩 객체 구조 기준으로 판정 |
| matrix note 사실오류("독립적으로") 발견 | **미해결(범위 외)** | Phase A는 값·문서 무변경 원칙 → 보고만. §5-3 회장 결정 대상 |

---

## L1 스모크테스트

이 작업은 **코드/데이터 변경 0건의 판정 task**이므로 서버 기동·API 경로가 없다. 대신 **판정의 근거가 되는 런타임 함수를 실제로 실행**하여 검증했다 (SKIP 0건).

- **서버 재시작**: 해당없음 (코드 변경 0건, 런타임 배선 없는 휴면 모듈)
- **API 응답 확인**: 해당없음 (실손 모듈은 런타임 호출자 0 — 노출 경로 없음)
- **스크린샷**: 해당없음 (프론트엔드 변경 0건)
- **실행 검증 (실제 수행·통과)**:
  1. `provenance.primary_source_verified_map(raw)` 실행 → `true=0 / false=10` 반환 **PASS**
  2. `provenance.audit_primary_source_flags(raw)` 실행 → 위반 **0건** (저장값=계산값, 위조 경로 없음 확인) **PASS**
  3. `provenance.map_source_token()` 전 토큰 매핑 → unmapped **0건**, 레코드별 tier1 보유 판정 **PASS**
  4. 반사실 시뮬(A1 verified=true, 메모리 복사본) 재계산 → `true=3 / false=7` **PASS** — §4-4 결론의 직접 증거
  5. `plan_type` scheme 키 전수 스캔 → 누락 8노드 / 보유 2노드 **PASS**
  6. `grep -rn "\.plan_type(" server/` → 호출 **0건** (dead API) **PASS**
  7. A02 OCR 전수 검색 → `300` 0건 / `자기공명` 0건 / `MRI` 1건 / `200` 2줄 **PASS**
- **부작용 검증**: `cd /home/jay/projects/InsuRo && git status --porcelain server/silson/` → **출력 0줄** (repo 무변경 확인)

---

## 8. 모델 사용 기록

| 팀원 | 역할 | 모델 | 담당 |
|---|---|---|---|
| 토르 (Thor) | 백엔드 | **sonnet** | §1 MRI 300 독립원천 재확인 |
| 미미르 (Mimir) | UX/UI·스키마 설계 | **sonnet** | §2 plan_type 스키마 고정 |
| 헤임달 (Heimdall) | 테스터/QA | **sonnet** | §3 coverage_gap 4건 재확인 |
| 오딘 (Odin) | 팀장 | opus | 설계·분배·교차검증·§4 활성화 게이트 실증·통합 |

- **haiku 사용 0건** — 전 서브태스크가 분석/판정 작업이므로 규칙에 따라 sonnet 이상만 사용.
- 프레이야(프론트엔드) 미소집 — 프론트엔드 변경 0건인 판정 task로 역할 해당 없음.
- 팀장 직접 개입 범위: 코딩 0건. §4 활성화 게이트 실증은 팀원 3인의 판정 전제를 검증하는 **팀장 교차검증**으로 수행(읽기 전용 실행).

---

# 9. 머지 판단

- **머지 필요**: **No**
- **브랜치**: 생성 안 함 (worktree 미생성)
- **PR**: 불필요 — task 명세 "PR 불필요(판정·문서 위주)" 준수
- **머지 의견**: 코드·데이터 변경 0건. 산출물은 본 보고서(판정 + 스키마 규칙 + 판정규칙)뿐이다. 데이터 값 변경이 필요한 항목(matrix note 정정, plan_type `scheme` 8노드 추가)은 §5에 회장 결정 대상으로 분리 보고했으며, 승인 시 별도 surgical PR로 발주해야 한다.

---

## 9-1. QC 게이트 결과 (정직 보고)

`finish-task.sh task-2951 dev2` (FINALIZE_ONLY=1, G4_GATE_ENABLED=0) 2회 실행 결과:

| 회차 | 결과 | 비고 |
|---|---|---|
| 1회차 | **6 PASS, 2 FAIL, 13 SKIP, 2 WARN** | `l1_smoketest_check` FAIL(섹션 헤더 레벨) + `git_evidence` FAIL |
| 2회차 | **7 PASS, 1 FAIL, 13 SKIP, 2 WARN** | L1 해소(`# 7.` → `## L1 스모크테스트`). **`git_evidence` 1건만 잔존** |

### 잔존 FAIL — `git_evidence` (미해소, 우회하지 않음)

```
resolved_via=env_var dir=/home/jay/workspace
FAIL COMMIT_EXISTS : task-2951 커밋 0건 + mergeCommit evidence 없음
FAIL NO_UNCOMMITTED: uncommitted 변경 존재 (51 unstaged, 54 staged)
```

**원인 — 둘 다 이 작업이 만든 상태가 아니다:**

1. **COMMIT_EXISTS**: 이 task는 명세상 **코드/데이터 변경 0건**(“PR 불필요(판정·문서 위주)”)이므로 task-ID 커밋이 원천적으로 생기지 않는다. 산출물(보고서·3문서)을 커밋하려 시도했으나 workspace `main` 직접 커밋이 훅에 의해 차단된다:
   ```
   $ git commit -m "[task-2951] …" -- memory/reports/task-2951.md memory/plans/tasks/task-2951/
   [BLOCKED] main direct commit prohibited
   ```
2. **NO_UNCOMMITTED**: workspace 작업트리에 **1,692건의 기존 미커밋 변경**(`memory/backups/` 대량 삭제, `dispatch/*.py`, `hooks/`, 타 작업 staged 55건 등)이 이미 존재한다. **전부 타 작업 소유**이며 내 작업 산출물이 아니다.

**우회하지 않은 이유 (의도적 선택):**

- `git_evidence.py`는 자체적으로 "non-code task (문서만/리서치만) → SKIP" 면제를 갖지만, 발동 조건이 **task 파일의 `## 레벨` 섹션 키워드**(`_is_non_code_task`, git_evidence.py:79-94)다. `memory/tasks/task-2951.md`에는 해당 섹션이 없다.
- 이 면제를 발동시키려면 **task 파일을 수정**해야 하는데, 이는 capability 모델이 명시적으로 차단하는 **mutable task 파일 self-bypass** 패턴이다(dispatch 시점 immutable snapshot이 존재하는 이유). 분류 자체는 사실에 부합하나(실제로 코드 수정 0건), **게이트를 통과시키기 위해 내가 소유하지 않은 dispatch 산출물을 고치는 행위는 하지 않았다.**
- `git commit --no-verify`로 훅을 우회하는 선택도 하지 않았다.
- 타 작업의 1,692건을 커밋해 `NO_UNCOMMITTED`를 만족시키는 선택도 하지 않았다(중대한 scope 위반).

**따라서 `.qc-done` / `.qc-result`는 생성되지 않았고, 이 판정은 ANU(개발실장) 몫으로 넘긴다.** QC 결과를 손으로 써서 PASS로 만들지 않았다.

### 마커 상태

| 마커 | 상태 |
|---|---|
| `task-2951.done` | 존재 (11:05) — **`task-timer.py end`가 발행**. 수동 생성 0건 |
| `task-2951.qc-result` / `.qc-done` | **미생성** (게이트 FAIL로 미도달) |
| `task-2951.followup.txt` | 생성 — 회장 결정 4건 명시 |
| `task-2951.anu-notified` | 생성 (11:16:39) |
| ANU normal callback cron | **등록 완료** — `scripts/extract_followup.py send task-2951` → `{"ok": true, "status": "sent", "bot": "bot-c", "message_len": 1802}`. ANU key `c119085addb0f8b7` 사용(self-key 0). 발사는 OS-level pickup runner 소유이며 executor 자가발사 0 |

---

# 10. 산출 파일

| 경로 | 성격 |
|---|---|
| `/home/jay/workspace/memory/reports/task-2951.md` | 본 보고서 (판정 + 규칙 문서 2종) |
| `/home/jay/workspace/memory/plans/tasks/task-2951/{plan,context-notes,checklist}.md` | 3문서 |
| `/tmp/task-2951-ro/`, `/tmp/task-2951-sim/` | 읽기전용 검증 스냅샷 (임시, 산출물 아님) |

**변경된 프로젝트 파일: 0건.**

## 세션 통계
- 총 도구 호출: 0회

