# task-2986 — InsuRo 실손계산기 연간 보장한도(annual_limit) 미적용 결함 수정

**레벨**: Lv.2 (금소법 직결 결함) · **팀**: dev5-team (마르둑) · **회장 승인 완료** (2026-08-20)

## 배경 — 이미 프로덕션에 라이브인 결함이다
task-2958(PR#219)은 2026-08-15 머지됐고 t2960으로 배포까지 됐다.
그런데 ANU 가 머지 차단 사유로 올렸던 결함이 **수정되지 않은 채 머지**됐고, 지금 프로덕션에서 동작 중이다.

**결함**: `server/silson/calc/engine.py` (467줄)가 **1회당 한도(`per_visit_limit`)만** 절단하고
**연간 보장한도(`annual_limit`)는 계산 경로에서 참조 0회**다. 테이블에는 수집해놓고 엔진이 쓰지 않는다.
→ 지급 예상액이 실제 상한을 크게 넘겨 표시된다.

**ANU 실측 재현치**:
- 4세대 비급여 MRI 80만원 × 10건 → 표시 **5,600,000원** / 약관 연간한도 3,000,000원 = **1.87배 과대**.
  게다가 `calculation_status: "complete"` 로 표시되고 **경고가 없다**. 현실적으로 흔한 입력이다.
- 4세대 MRI 2,000만원 단건 → 14,000,000원 (한도 300만) = **4.7배**
- 4세대 입원 급여 1억원 → 80,000,000원 (한도 5,000만) = 3,000만 과대
- 영향 범위: CONFIRMED 54셀 중 **32셀** (전부 per_visit_limit 없음 = 무제한 계산)

**설계 결정이 아니다**: `test_silson_calc_table.py:423` 이 입원 셀에 `annual_limit` 을 **필수로 강제**해놓고
엔진은 쓰지 않는다. 수집만 하고 죽은 필드다.

**Why 중요**: 설계사가 고객에게 실제 상한의 2~5배를 안내할 수 있다. 금소법 직결이다.

## 두 번째 결함 — 고지 문구 방향이 반대
`src/pages/SilsonCalculator.tsx:129`
```
const ANNUAL_CAP_NOTICE_TEXT = "연간 누적 자기부담 한도는 반영되지 않았습니다(단건 추정).";
```
**자기부담 상한은 지급액을 늘리는 항목**이다. 즉 이 문구는 "적게 나올 수 있다"는 경고인데,
실제 문제는 **과대추정**이다. 연간 **보장**한도 미반영은 화면 어디에도 고지가 없다.
현재 문구로는 커버되지 않는다.

## 요구사항
1. **항목별 지급액을 `annual_limit` 으로 절단**한다.
2. **동일 셀 항목의 합산분도 절단**한다. 항목별로만 자르면 여러 건 입력 시 합계가 다시 한도를 넘는다.
   (위 MRI 10건 사례가 정확히 이 경우다. 항목별 절단만으로는 해결되지 않는다.)
3. 한도가 실제로 적용되면 **화면에 명시**한다. 기존 `per_visit_limit` 이 `applied_rules` 에
   `"1회당 한도 N원 적용"` 을 남기는 방식(engine.py:284)을 그대로 따른다.
4. 고지 문구를 **실제 리스크 방향으로 교체**한다. 연간 보장한도 미반영 상태가 남는다면 그 사실을 명시하고,
   완전히 해결됐다면 오해를 부르는 기존 문구를 정리한다.

## 검증 (필수 순서)
1. **재현 테스트를 먼저 작성**한다. 수정 전에 **FAIL** 해야 한다.
   최소 3케이스: MRI 80만×10건 / MRI 2,000만 단건 / 입원 급여 1억.
   기대값은 각 셀의 `annual_limit` 이다.
2. 수정 후 위 테스트가 **PASS 로 전환**되는 것을 증거로 남긴다. 이게 없으면 "고쳤다"의 근거가 없다.
3. `annual_limit` 이 있는 **32셀 전수**에 대해 한도 초과 입력을 넣어 전부 절단되는지 확인한다.
4. `per_visit_limit` 기존 동작에 **회귀가 없어야** 한다.
5. `npm run build` EXIT=0 · `npx vitest run` 회귀 유지 · 서버 테스트 전체 통과
6. **실브라우저**로 MRI 10건 입력 → 표시 금액이 3,000,000원으로 절단되고 한도 적용 문구가 뜨는 것을 확인. 스크린샷 첨부.

## 범위 (엄수)
**수정 허용**: `server/silson/calc/engine.py` · `src/pages/SilsonCalculator.tsx` · 관련 테스트

**수정 금지**:
- `src/components/PwaInstallPrompt.tsx` · `src/App.tsx` — task-2984 작업 중
- `src/pages/CustomerChat.tsx` — task-2985 작업 중
- **실손 지식 원본 데이터**(`silson_generations.json` 등) — 계산 로직 문제이지 데이터 문제가 아니다.
  원본을 고쳐서 해결하려 하지 말 것.

## 선행 조건
`origin/main` 최신으로 `git pull --ff-only` 후 시작한다.

## allowed_resources (본 task의 capability)

```yaml
allowed_resources:
  paths:
    - "server/silson/calc/engine.py"
    - "server/tests/**"
    - "src/pages/SilsonCalculator.tsx"
    - "src/pages/__tests__/**"
    - "tests/**"
    - "e2e/**"
    - "memory/reports/task-2986.md"
  forbidden_paths:
    - "src/components/PwaInstallPrompt.tsx"
    - "src/App.tsx"
    - "src/pages/CustomerChat.tsx"
    - "server/silson/data/**"
    - ".github/**"
  commands:
    - "npm run build"
    - "npx vitest run"
    - "pytest"
    - "npx playwright test"
  merge_policy: "tiered"
  ttl_hours: 48
```

## 완료 조건
재현 테스트 FAIL→PASS 전환 증거 · 32셀 전수 절단 확인 · per_visit_limit 무회귀 · 빌드 0 · 회귀 유지 · 실브라우저 스크린샷 · 고지 문구 교체

## 보고
**PR 생성까지가 범위다. 머지는 ANU 가 한다.** `memory/reports/task-2986.md` 작성 후 ANU 콜백.
콜백 프롬프트는 **UTF-8 3900 bytes 이하**, 봉투에는 경로·PR 번호·한 줄 요약만 담는다.

## goal_assertions (auto-generated)
- `npm run build`
- `npx vitest run`
