---
task_id: task-2949-rework
timer_id: task-2949+1
scope: task
team: dev2 (오딘)
date: 2026-08-14
status: COMPLETE (local commit, 미푸시/미PR)
branch: task-2949-silson-evidence
head: 22ba76d
base: fbff8ca (= origin/main e0edd6a, PR#211 squash)
worktree: /home/jay/projects/InsuRo-worktrees/task-2949
active: false
---

# task-2949 재작업 — 실손 세대 지식 오독·구분 정정

회장 지적 + Codex 조율 결과에 따른 정정 3건. 공통 근본원인은
**`primary_wins` 대표값 규칙의 맹목 적용** — 한 자료가 틀리거나(MRI 200)
빠뜨린(표준형) 값을 그대로 대표값으로 굳혔다.

## 1. 정정 결과

### 정정 1) MRI 특약 한도 200 → 300 (세대별 구분)

| 세대 | 최종 대표값 | 근거 OCR 축자 |
|---|---|---|
| GEN3_2017_04 (3세대 착한실손) | **MRI 300** | KB02.md:19 `MRI(300만/제한없음)` · KB11.md:32 `1년 단위로 연간 300만원 이내에서 실제로 부담한 비급여의료비(조영제, 판독료 포함)보상` |
| GEN4_2021_07 (4세대) | **MRI 300** | KB02.md:19 (4세대 열) · **KB13.md:32** `1년 단위로 연간 300만원 이내 보상` |

- A2 `MRI 200`은 **협회 요약표 오기**로 판정·폐기. 단 값은 삭제하지 않고
  `conflicts[].primary` + `corrections[0].previous_source`에 축자 보존(근거봉인).
- 오기 판정 논리: 3대 비급여 중 **도수 350·주사 250은 A2·KB 일치**하고 MRI만
  100만원 어긋난다 → A2 표 전체의 계통 오류가 아니라 **단일 셀 오기**.
  반면 KB는 서로 다른 2개 페이지에서 독립적으로 300을 기재.
- **세대 뭉침 제거**: GEN4의 `비급여3대특약 좌동` 상속을 절단하고
  `도수치료 350, 주사료 250, MRI 300`을 자기 세대 값으로 명시.
- 세대 독립성 추가 증거(원문 발견): 3대비급여 1회당 공제금액이
  **KB11(3세대)=`1회당 2만원`, KB13(4세대)=`1회당 3만원`** — 두 세대가 같은
  표를 재사용한 것이 아님이 원문으로 증명된다.
- `resolution`: `primary_wins` → `secondary_corroborated_primary_typo` (2건)

### 정정 2) plan_type(표준형/선택형) 축 도입

`deductible`/`payout_rate` 하위에 `plan_type {standard, selective}` 승격.
기존 평면 키(`급여`/`비급여`/`특약`/`원문`)는 **유지**(하위호환), `원문` 무변경.

| 세대 | deductible 표준형 / 선택형 | payout_rate 표준형 / 선택형 |
|---|---|---|
| GEN2_STD1 (KB01 `2009.08~2012.12` 열) | `표준형 20%` / `선택형 10%` | **null / null** — 원문 미기재 |
| GEN2_STD2 (KB07=13.1.1~15.8.31) | `표준형 20%` / `선택형 10%[~2015.8)` + 통원 공제 상세 | `표준형: 보장대상의료비의 80%(본인부담금 200만원 한도)` / `선택형: …90%…` |
| GEN2_STD3 (KB09=15.9.1~17.3.31) | `표준형 20%` / `선택형 II 10%[2015.9~)`(급여)·`선택형 II 20%[2015.9~)`(비급여) | `보장대상의료비의 80%(본인부담금 200만원 한도)` / `급여 90% · 비급여 80%` |
| GEN3 (KB02 3세대 열, KB11) | `표준형 20%`(급여) / `선택형 10%`(급여) + 통원 공제 상세. 비급여는 null | `보장대상의료비의 80% [본인부담금 200만원 한도]` / `급여 90% · 비급여 80%` |
| GEN4 | `scheme:"single"`, standard/selective **전부 null** | 동일 |

- 2015.9 경계는 추론이 아니라 KB01 셀 안의 시점 라벨(`[~2015.8)` / `[2015.9~)`)로 분리.
- null 값에는 전부 사유(`note`)를 붙였다. **자기부담률에서 보장률 역산 같은
  추론은 하지 않았다.**

### 정정 3) primary_wins 예외 = coverage_gap 재분류

규칙 명문화 위치: `provenance.py`(`RESOLUTION_*` 상수 + docstring),
`silson_generations.json._readme`, `comparison_matrix.json._readme`.

> 한 소스가 상대 소스가 열거한 분류축의 **한 멤버만** 기재하고, 그 값이
> 상대의 해당 멤버와 **정확히 일치**하며, 나머지 멤버에 **상반된 값을
> 주장하지 않으면** → `conflict`가 아니라 `coverage_gap`.

재분류 4셀 (전부 deductible):

| 셀 | 사유 |
|---|---|
| GEN2_STD1/deductible | A2 `입원 10%` = KB01 선택형 10% 정확히 일치, 표준형 기재 부재 |
| GEN2_STD2/deductible | A2 `입원 20%` = KB01 표준형 20% 일치, 선택형 부재 |
| GEN2_STD3/deductible | A2 `급여 10% 비급여 20%` = KB01 선택형 II 일치, 표준형만 부재 |
| GEN3/deductible | A2 `좌동` = KB02 선택형 10% 일치, 표준형 20%만 부재 |

counts: `conflict 17` → **`conflict 13 + coverage_gap 4`**. 셀 총수 130 불변,
불일치 총수 17 불변.

**보수적으로 conflict 유지한 셀**(재분류 확대는 회장 판단 대기):
`GEN1/upper_grade_room`(KB 분류축이 가입금액 구간이라 명명된 유형축 아님),
`GEN1/deductible`(0% vs 20% = 진짜 값 충돌), `GEN2_STD3/waiting_period`(범위 밖),
`GEN3·GEN4/coverage_limit`(진짜 값 충돌, resolution만 변경).

## 2. 변경 파일 (7)

| 파일 | 요지 |
|---|---|
| `server/silson/data/silson_generations.json` | revision 4→5, MRI 정정 + `corrections[]` 신설, plan_type 축, conflicts 6건 resolution 재분류, `_readme` 규칙 명문화 |
| `server/silson/data/sources/comparison_matrix.json` | 4셀 coverage_gap 재분류(+`reclassified_from`), `coverage_gaps[]` 신설, counts·_readme 갱신 |
| `server/silson/data/sources/manifest.json` | 매트릭스 재봉인만. **OCR 18장 `ocr_sha256` 전부 무변경** |
| `server/silson/models.py` | `PLAN_TYPE_KEY`/`PLAN_TYPES` + `GenerationProfile.plan_type()` 접근자 |
| `server/silson/provenance.py` | `RESOLUTION_*` 3종 + `VALID_CONFLICT_RESOLUTIONS` + 예외규칙 docstring |
| `server/tests/test_silson_provenance.py` | 기존 6개 갱신 + 신규 18함수/23케이스 |
| `server/tests/test_silson_analysis_bridge.py` | 중첩 plan_type dict 허용하도록 assert 정합 |

`server/silson/data/sources/ocr/*.md` 18장 — **git diff 0바이트**.

## 3. 검증 (팀장 독립 재현)

| 항목 | 결과 |
|---|---|
| 전체 회귀 `server/tests/` | 기준선 **1715 passed / 0 failed** → **1738 passed, 3 skipped, 0 failed** (168.67s). 신규 실패 0 |
| MRI 세대별 300 | PASS — GEN3·GEN4 각각 확인, `좌동` 상속 없음 |
| KB13 4세대 MRI 300 실재 | PASS — KB13.md:14 제목 `4 세대 …[21년 7월 1일 ~]`, :32 `연간 300만원 이내 보상` 직접 확인 |
| plan_type 2·3세대 값 | PASS — GEN2 Ⅰ/Ⅱ/Ⅲ + GEN3 standard/selective 존재, GEN4는 전부 null |
| **수치 환각 0** | PASS — 비-null plan_type/MRI 값 **전량**을 봉인 OCR 코퍼스에 축자 대조, 미발견 **0건**. GEN4 창작값 0 |
| coverage_gap 재분류 | PASS — 선언 counts ↔ 실제 셀 집계 일치, 합 130 |
| 면책 275/365 무변경 | PASS — `275일 이후 90일, 275일 이내 365일` 그대로, 해당 conflict `primary_wins` 유지·`reclassified_from` 없음 |
| primary_source_verified | PASS — 전 레코드 `False`, 고객 자동판정 hard-block 유지 |
| source_tier 정직마킹 | PASS — tier1×1(A1), tier2×16(KB), None×1(A2). 승격 0 |
| 봉인 OCR sha256 | PASS — manifest 18항목 재계산 mismatch **0** |
| 음성대조(mutation) | 토르 실시 6종 전부 발화(MRI 복귀·환각 주입·GEN4 조작·면책 변조·OCR 1줄 추가·셀 삭제) — 테스트 공허하지 않음 |

### QC 검증기 (오탐 판정)
- `code-validator.py all`: **Execution Test FAIL** — 패키지 모듈을 단독 스크립트로
  실행해 `from .knowledge import load_raw` relative import가 깨진 것. 기존부터 동일.
  Workflow/Code Quality/Security/Deps는 전부 PASS.
- `red-team-auto-review.py scan`: **critical 2건 = 오탐** —
  `re.compile(r"^(표\d+)")` / `re.compile(r"^page_(\d+)$")` 리터럴 정규식을
  Code Injection으로 오판. 두 줄 모두 base fbff8ca:90~91에 **이미 존재**(+53줄
  문서 추가로 행번호만 143~144로 밀림). **net-new 결함 0.**
- pyright 신규 경고 2건도 동일하게 기존 코드(행번호 시프트 / 의도적 `_` 폐기변수).

## 4. 상태 / 잔여

- 로컬 커밋 **22ba76d** (브랜치 `task-2949-silson-evidence`). **푸시·PR 안 함** —
  검증·머지는 ANU 권한. `ACTIVE=false` 유지(머지≠활성화).
- 회장 판단 대기 4건:
  1. GEN3 `coverage_limit` 첫 줄 `기본 : 좌동`(기본형 한도 상속)은 정정 범위 밖으로 판단해 유지 — 함께 절단할지?
  2. GEN2_STD1 `payout_rate.plan_type` = null(원문 표준형/선택형 분리 기재 없음). 역산 금지 유지?
  3. GEN3 `deductible` 비급여 유형별 값 null(KB02가 `20% (3대 비급여 : 30%)` 단일 기재).
  4. coverage_gap 재분류 확대 여부(보수 유지한 2셀).
- 리스크: 봉인 OCR 오독 잔존 가능성은 여전(원문 자체가 판독물). 대표값이 바뀐
  만큼 고객 노출 전 `primary_source_verified` 해제는 **절대 자동으로 하지 말 것**.
