# [microfix] task-2884+2 — 생보 계단식 계산 견고화 (담보매칭 정확화 + baseline 3사 게이트)

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "src/lib/lifeThreeCritical.ts"
    - "src/lib/__tests__/lifeThreeCritical.test.ts"
    - "src/components/composite/LifeThreeCriticalDiff.tsx"
    - "src/components/composite/__tests__/LifeThreeCriticalDiff.test.tsx"
  forbidden_paths:
    - "server/**"
    - "extension/**"
    - ".github/**"
    - "tests/e2e/**"
  commands: ["npx vitest","npx tsc","git"]
  merge_policy: "none"
  ttl_hours: 8
```
> ★ base=**origin/task/task-2884-dev1**(이어서 커밋). 서버 멱등은 Codex NO-BLOCKING 통과 — **건드리지 말 것**. 웹앱 계산 2건만.

## 배경 (Codex 리뷰 — MEDIUM 2건, 고객노출 숫자 정확도)
task-2884 생보 계단식이 Codex 반증리뷰에서 MEDIUM 2건. 고객에게 보이는 차액 숫자라 배포 전 수정.

### FIX-1 (담보 매칭 오매칭 위험) — src/lib/lifeThreeCritical.ts:53-90
현재 target 매칭이 **substring**(`keyword:"암진단"`, `norm.includes(keyword)`). 문제: 손보 canonical 암 담보 = **`암진단비(유사암제외)`(code b002)**, 그리고 생보 side에 "유사암진단비"·"암진단비(유사암제외)" 등 인접 담보가 함께 있으면 `암진단` substring이 **여럿 매칭 → 첫 배열 히트가 승** → 엉뚱한 담보 보험료를 차액에 사용. exact 매칭(`norm===keyword`)은 keyword가 "암진단"이라 실제 담보명과 절대 일치 안 해 항상 fallback.
- **수정**: 매칭을 **① coverage code 우선**(양쪽 code 존재+동일 시 그걸로), **② code 없으면 정규화명 allowlist/정확매칭**으로. 그리고 **"유사암" 담보는 암진단 target에 절대 매칭 금지**(제외 규칙).
  - 3 target canonical(정규화·공백제거 기준): 암=`암진단비` 또는 `암진단비(유사암제외)` (단 **`유사암`포함명 제외**) / 뇌혈관=`뇌혈관질환진단비` / 허혈성=`허혈성심장질환진단비`.
  - 매칭 우선순위: (a) code 동일 → 채택 (b) 정규화명 == canonical(허용목록 중 하나) → 채택 (c) 그 외 substring 매칭은 **유사/무관 담보 오채택 방지 위해 제거**하거나, 최소한 "유사암" 등 배제 후 canonical 접두 일치만.
  - 손보 배정가와 생보 최저가 **둘 다 동일 target 규칙**으로 뽑아 대칭. 다건 매칭 시 임의 첫히트 금지 → 규칙상 유일 담보 선택.
- 목표: "암진단비(유사암제외)" 정타, "유사암진단비"·기타 오매칭 0. exact 원(반올림 금지) 유지.

### FIX-2 (baseline 3사 라벨 정합) — lifeThreeCritical.ts pickCombo(123-156) / LifeThreeCriticalDiff.tsx:82-87
`pickCombo`가 `triple_best ?? dual_best ?? single_best` fallback인데 UI 라벨은 항상 "손보 3사 복합 총액". triple_best 없는 계약은 2사/1사 총액에 3사 라벨 → 불일치.
- **수정(택1, 단순 우선)**: 이 위젯은 **triple_best 있을 때만 계단식 렌더**(baseline=triple_best 총액). triple_best 없으면 계단식 미표시(기존 placeholder/graceful). → "손보 3사 복합" 서사와 정합. (relabel 방식보다 게이트가 단순·안전.)

## 제약
- 서버/확장/e2e 불변. 계산 로직·게이트만. "절감" 단어 금지, 곡선 금지, exact 원. 기존 계단식 시각/testId 유지.

## 검증
- `npx tsc` 클린. `npx vitest run` 회귀 0. 신규/갱신:
  - FIX-1: 생보 coverages에 "암진단비(유사암제외)"+"유사암진단비" 공존 시 **유사암 미채택·정타 채택** / code 우선 매칭 / 3 target 각각 정확. 오매칭 0.
  - FIX-2: triple_best 있음→계단식 렌더(baseline=3사 총액) / triple_best 없음(dual/single만)→계단식 미표시 graceful.

## 완료 (★ 순서 B)
- 변경 = src/lib/lifeThreeCritical.ts + 컴포넌트(+테스트) 4파일. push to task/task-2884-dev1.
- **dev6 금지** — dev1. 커밋 → push → finish-task **foreground 1회** → .done 즉시 종료. **background wait 금지.** ANU가 PR#160 재검증·머지·배포.

## goal_assertions (auto-generated)
- `npx tsc`
- `npx vitest run`
