# [Lv.4] 생보 3대질병 Phase A — 확장 생보 캡처 + 서버 life_matrix 계약

## repo / 배포
- InsuRo `/home/jay/projects/InsuRo`, origin/main. 확장+서버 계약 동시.

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "extension/content.js"
    - "extension/background/**"
    - "extension/__tests__/**"
    - "server/schemas/consultation_history_v1.py"
    - "server/routes/consultation_history_v1.py"
    - "server/migrations/**"
    - "server/tests/**"
    - "tests/contract/**"
    - "docs/contracts/**"
    - "src/lib/**"
    - "src/components/composite/**"
    - "src/**/__tests__/**"
  forbidden_paths:
    - ".github/**"
    - "extension/manifest.json"
  commands: ["npx vitest","pytest","npx tsc","node","git"]
  merge_policy: "none"
  ttl_hours: 20
```
> ★ merge_policy=none. base=현 origin/main(복원태그 restore-260729-pre-life 존재). **골든 계약(확장 OHMY_PPD_V2 ↔ 서버) 원자적 동기 필수.**

## 배경 (회장 확정 + 미팅/Codex 합의)
손보 복합설계에 **생보 3대질병 진단비(암진단비·뇌혈관질환진단비·허혈성심장질환진단비)** 추가 비교를 얹는다. Phase A = **캡처·저장 계약**(표시는 Phase B).
- 생보 캡처 = 손보와 완전 동일. 오마이매니저 상단 **생손보유형 드롭다운**에서 생보 선택할 뿐. 확장 `#selInsuranceType`(LF=생손보/F=손보) 값으로 손보/생보 판정.
- 데이터모델(불칸/Codex 확정): **`life_matrix: Optional[AnalysisMatrix] = None`**(단일 레코드). analysis_matrix(손보)와 **별도 슬롯**, 병합 금지.
- 병합: **클라이언트가 손보+생보를 한 페이로드로 누적 후 1회 저장**(단일 idempotency_key/reference_id/consent, 사후매칭 금지).

## 작업

### A. 확장 — 생보 캡처 라우팅 + 단일 페이로드 누적
- `content.js` `scrapeMmlfcpMatrix` 재사용(담보명 하드코딩 없어 생보 화면 동일 캡처). **생손보유형(#selInsuranceType) 읽어 손보(F)→`analysis_matrix` / 생보(LF)→`life_matrix` 슬롯 라우팅.**
- **누적 흐름**: 손보 캡처 후, 사용자가 오마이매니저를 생보로 전환해 "생보 이어서 캡처" → 생보 matrix를 **chrome.storage.session에 누적**(기존 taint-guard 모델A 저장소 활용) → 저장 시 손보 `analysis_matrix` + 생보 `life_matrix`를 **한 페이로드**로 전송.
- 생보 없이 손보만 저장도 정상(life_matrix 생략/null). **single-egress·taint-guard·consent 로직 불변**, 슬롯 추가만.
- 생보 캡처의 대상 담보는 3개(암진단비·뇌혈관질환진단비·허혈성심장질환진단비) 중심이나, 캡처는 화면 선택 담보 그대로(제한은 Phase B 계산에서).

### B. 서버 — life_matrix 계약 수용
- `server/schemas/consultation_history_v1.py`: 최상위 요청 모델에 **`life_matrix: Optional[AnalysisMatrix] = None`** 추가(AnalysisMatrix 스키마 **재사용**). `extra="forbid"`이므로 필드 선반영 필수.
- `server/migrations/015_*.sql`: `life_matrix JSONB NULL` 컬럼 additive(014 analysis_matrix와 동형). 신규 테이블 금지.
- `routes/consultation_history_v1.py`: 저장 시 life_matrix persist. GET/LIST 응답에 포함(있으면). **record_status/ok_only 뷰**: 생보 캡처가 REQUIRES_REVIEW로 전체 레코드를 숨기지 않게(life_matrix 검증 실패가 손보 레코드 노출을 막지 않도록 — 손보 OK면 노출, life_matrix는 있으면 표시).
- **★ 골든 계약 원자적 동기 (task-2864 선례)**:
  1. 서버 스키마 먼저(extra=forbid 대응).
  2. 확장 payload가 life_matrix 전송.
  3. **골든 픽스처 재생성 회피 우선 검증**: life_matrix가 **None일 때 canonical 직렬화(stableStringify)에서 키 생략**되면 기존 손보 골든(tests/contract/ohmy_ppd_v2.golden.json 등) 불변 → 재생성 회피. **회피 불가 판정 시** golden 재생성 전수(server/tests/test_contract_golden.py·extension/__tests__/contract-golden*.test.ts) + 신규 **생보 포함 골든 1개** 추가.
  4. request_fingerprint(dedup) 4요소 불변 — life_matrix를 fingerprint에 넣지 말 것.
  5. docs/contracts 갱신.
- **하위호환**: life_matrix 없는 기존 레코드 정상(Optional/None). 손보-only 저장 정상.

## 제약
- Phase A = 캡처·저장·계약까지. **표시(차액 계산·결과지 렌더)는 Phase B**(별도).
- 확장 manifest 불변. consent/egress/taint-guard 로직 불변(슬롯 추가만). analysis_matrix에 생보 병합 절대 금지.

## 검증
- `npx vitest run extension/__tests__/` 전체 PASS(골든 포함). 신규: 생손보유형 판정(F/LF)·생보→life_matrix 라우팅·단일 페이로드(손보+생보)·손보only(life_matrix null) 하위호환.
- `pytest server/tests/` — life_matrix Optional 수용(있음/없음)·계약 골든·마이그레이션 015·fingerprint 4요소 불변·ok_only 뷰가 손보 OK 레코드 노출 유지.
- 골든 재생성 회피 여부 실증(None 직렬화 생략) 보고. 확장↔서버 계약 정합.
- **생보 셀렉터 live 확인**: content.js가 실제 생보 화면 값(LF) 읽는지(불가 시 ANU 에스컬레이션).

## 완료 (★ 순서 B)
- 변경 = allowed_resources 내부만. manifest·.github 불변.
- **dev6 금지** — dev2. 커밋 → push → finish-task **foreground 1회** → .done 즉시 종료. **background wait 금지.** ANU 검증·Codex 재검토·머지는 ANU. **골든/셀렉터 문제 시 ANU 에스컬레이션.**

## goal_assertions (auto-generated)
- `npx vitest run extension/__tests__/`
- `pytest server/tests/`
