# task-3004 — 실손 판정 미탐(false negative) 해소 (task-3002 부수발견)

**레벨**: Lv.2 · **팀**: dev7-team (이참나) · **회장 승인 완료** (2026-08-24)

## 배경 — task-3002(묵은 PR 분석)에서 부수로 드러난 결함

`server/silson/analysis_summary.py` 의 `is_silson_relevant()` 가 실손 증권을 **놓칠 수 있다.**
놓치면 설계사 화면에 **실손 세대지식이 아예 뜨지 않는다**(무성 실패 — 오류도 안 남).

### 현재 판정 경로 (origin/main 실측)
```python
def is_silson_relevant(validated: dict) -> bool:
    # ① coverages 중 category == '실손' 이 1건 이상
    # ② meta.product_name 또는 meta.product_type 문자열에 '실손' 포함
```

### ★ ANU 실측 — ①은 사실상 작동하지 않을 가능성이 높다
```
server/policy_analyzer.py:188-192  (LLM 프롬프트의 출력 예시)
  "coverages": [ { "category": "진단", "name": "암진단비", "amount": 3000, ... } ]
                              ^^^^^^ 예시가 "진단"

- category 를 '실손' 로 세팅하는 프로덕션 코드 = 0건
  (git grep 결과 '실손' 값 설정은 server/tests/** 픽스처에만 존재)
- category 허용값 enum/Literal 정의 = 없음 → LLM 자유 기입 필드
```
즉 ①이 참이 되려면 **LLM 이 우연히 "실손" 이라는 값을 넣어야** 한다. 보장되지 않는다.

### 그러면 ②만 남는데, ②도 구멍이 있다
상품명에 "실손"이 있으면 잡힌다(예: `무배당 ○○ 실손의료비보험`).
그러나 **종합보험에 실손 특약이 붙은 형태**는 상품명에 "실손"이 없다.
이 경우 담보명에는 `실손의료비` 등이 있는데 **현재 로직은 담보명을 보지 않는다.**

### 참고 — 닫힌 PR #215 에는 이 매칭이 있었다
```python
# PR #215 (64111fbd) _detect_silson()
_SILSON_KEYWORDS: tuple[str, ...] = ("실손",)
elif any(keyword in name for keyword in _SILSON_KEYWORDS):
    basis.append(f"담보명에 실손 키워드 포함: {name}")
```
PR 자체는 낡아서 닫았지만(task-3002 판정 (a)), **이 아이디어는 유효하다.**
그 PR 을 되살리지 말고 **현재 main 기준으로 새로 구현**하라.

---

## 고칠 것

### ① 담보명 기반 판정 추가
`is_silson_relevant()` 가 `coverages[].name` 도 검사하도록 확장한다.

★ **PR #215 가 지킨 불변식을 그대로 유지하라**:
> 담보명 매칭은 "실손이라는 **사실**"을 감지하는 데만 쓴다.
> **담보명으로 세대(1~4세대)를 추정하지 않는다.** (계약서 §1-3)

세대 판정은 기존 경로(계약일 등)를 그대로 쓴다. 이 태스크는 **"실손인지 아닌지"만** 넓힌다.

### ② 오탐(false positive) 검증
"실손"이 담보명에 들어갔지만 실손이 아닌 경우가 있는지 확인하라.
- 있으면 그 사례를 보고하고, 배제 규칙이 필요한지 ANU 에 판단을 요청하라
- **임의로 복잡한 배제 규칙을 만들지 마라.** 단순함이 우선이다

### ③ 판정 근거를 남겨라
왜 실손으로 판정했는지(카테고리/상품명/담보명 중 무엇 때문인지) 추적 가능하게 하라.
지금은 `bool` 만 돌려줘서 오판 시 원인을 알 수 없다.
★ 다만 **기존 호출부 계약을 깨지 마라.** 반환 타입을 바꿔야 한다면 먼저 호출부를 전수 조사하고
   영향 범위를 보고하라.

---

## 검증 (실측 강제)
1. **결함 재현 먼저** — `category` 가 실손이 아니고 `product_name/type` 에도 "실손"이 없지만
   **담보명에는 "실손"이 있는** 증권 데이터를 만들어, 현재 코드가 `False` 를 돌려주는 것을 보여라.
2. **수정 후** 같은 입력이 `True` 가 되는지 확인.
3. **기존 동작 무손상** — 기존에 `True` 이던 케이스가 전부 그대로 `True` 인지,
   기존 `False` 케이스 중 의도치 않게 `True` 로 바뀐 것이 없는지 확인.
4. ★ **실데이터 확인** — 프로덕션/샘플 증권 분석 결과에서 `category` 필드에 **실제로 어떤 값들이
   들어가 있는지** 분포를 뽑아라. "실손"이 한 번이라도 나오는지 확인하고 **수치로 보고**하라.
   (ANU 는 코드에서 "실손 세팅 경로 0건"까지만 확인했다. 실데이터는 미확인이다.)
5. **화면 도달 확인** — 판정이 `True` 가 됐을 때 설계사 화면에 실손 세대지식이 **실제로 뜨는지**
   확인하라. 판정 함수만 고치고 화면에 안 뜨면 의미가 없다. 스크린샷 첨부.
6. **봉인 증명** — 새 테스트를 **변이시켜** 실제로 FAIL 하는지 확인하고 복원하라.
7. `pytest` 회귀 유지 (**base 재측정 기준선**. 명세 수치 아님).

## 범위 · 금지
**수정 허용**: `server/silson/analysis_summary.py` · `server/tests/test_silson_analysis_summary.py` · 관련 테스트

**금지**:
- **세대(1~4세대) 판정 로직 변경** — 이 태스크는 "실손인지"만 다룬다
- **담보명으로 세대를 추정하는 코드 추가** (계약서 §1-3 불변식 위반)
- 닫힌 PR #215 의 diff 를 그대로 되살리기 (스키마가 다르다. 무의미하고 위험하다)
- `server/policy_analyzer.py` LLM 프롬프트 변경 — 파급이 크다. 필요하면 **보고만** 하라
- `src/pages/PolicyAnalysis.tsx` — task-3002 후속 영역 · 신규설계 비교 작업 대기 중
- `supabase/functions/send-push/**` — task-3003 진행 영역
- `scripts/youtube-pipeline/**` — 다른 저장소

## allowed_resources

```yaml
allowed_resources:
  paths:
    - "server/silson/analysis_summary.py"
    - "server/silson/__tests__/**"
    - "server/tests/test_silson_analysis_summary.py"
    - "server/tests/**"
    - "memory/reports/task-3004.md"
  forbidden_paths:
    - "server/policy_analyzer.py"
    - "server/ai_parser.py"
    - "src/**"
    - "supabase/**"
    - "ops/**"
    - ".github/**"
  commands:
    - "pytest"
    - "python3"
  merge_policy: "tiered"
  ttl_hours: 48
```

## 운영 계약
- `origin/main`(=`51718e8`) 기준 `git pull --ff-only` 후 시작
- ★ `gh` 호출 시 `GH_TOKEN="$BOT_GITHUB_TOKEN"` 주입 필수 (회장 개인 PAT 금지 — 감사기록 오염)
- 워크플로우 `/home/jay/workspace/prompts/DIRECT-WORKFLOW.md` · QC `/home/jay/workspace/teams/shared/QC-RULES.md`
- `WORKSPACE_ROOT=/home/jay/workspace` · `CHAT_ID=6937032012` · 수집자 key `ANU_KEY=c119085addb0f8b7`
- 완료 경로는 `finish-task.sh` 실행이 유일하다. 수동 `.done` 금지.
- **명세와 실측이 다르면 임의 판단하지 말고 ANU 에 보고하라.**

★ **배포 주의**: 이 변경은 `server/` 다. 현재 백엔드 서빙 sha 는 `eadd0c9` 로 **main(`51718e8`)보다
뒤처져 있다**(미배포 상태). 머지해도 자동 반영되지 않는다. 배포는 ANU 가 판단한다.

## 완료 조건
결함 재현 → 수정 → 재현 불가 실증 · 기존 판정 무손상 · **category 실데이터 분포 수치 보고** ·
화면 도달 스크린샷 · 변이로 봉인 실증 · 회귀 유지

## 보고
**★★★ PR 생성까지가 범위다. 머지는 ANU 가 한다. 절대 직접 머지하지 마라.**
`memory/reports/task-3004.md` 작성 후 표준 완료 콜백 등록. 콜백 프롬프트 **UTF-8 3900 bytes 이하**.