# task-2787 보고서 — InsuRo 복합설계 A1 캡처·어댑터 스파이크 (flag OFF)

**팀**: dev5-team (마르둑 팀장) · **레벨**: Lv.3 · **작성**: 2026-07-17 KST
**모델 사용 기록**: 엔키(백엔드) sonnet / 닌기르수(테스터) sonnet / 팀장(마르둑) opus 검토·통합·L1. haiku 미사용.

---

## S — Situation (상황)
InsuRo 크롬 확장(MV3)은 ohmymanager 보험료 화면을 DOM 스크래핑해 복합설계 matrix를 만든다. 그러나 phase0.5 실증에서 `/api/ProductPremiums` 응답에 선택담보·가입금액·보험료·미제공·주계약·납입기간이 전부 있고 고객 PII는 없음이 확인되어, 어댑터를 "거의 API-only"로 전환할 계약(선택=user_coverages.details / 스케일링 공식 / 미제공=키 누락)이 CONFIRMED 되었다.

## C — Complication (복잡성)
정상 조회 1건으로 계약이 확정됐지만 반올림·비선형·활성플랜 직접 연결키·선택담보 실제 미제공은 UNKNOWN이다. DB에 계약을 고착하기 전에 **어댑터 계약을 실측·고정하는 스파이크**가 필요하며, 운영 오염(자동전송·기존 DOM 경로 변경·다중탭 오귀속)을 0으로 유지해야 한다.

## Q — Question (질문)
`/api/ProductPremiums` 응답을 탭별로 안전하게 read-only 복제하고 허용 필드만 정규화하여, 명시 버튼 클릭 시 개발자 검증용 matrix(기존 계산엔진 입력 스키마)를 flag OFF로 생성할 수 있는가? 계약이 실응답과 정합하는가?

## A — Answer (답변)
**PASS — STOP_REPORT 해당 없음.** MV3 `world:"MAIN"` content_script 별도 엔트리(Chrome 111+ 표준)로 interceptor 주입 성공. 어댑터·리졸버·버퍼가 확정 계약을 충실히 구현하고, 생성 matrix가 실제 파이썬 계산엔진 schema 검증을 통과했다. 기존 경로·테스트 무변경 회귀 445/445 PASS.

---

## 생성/수정 파일 (신규 4 + 수정 4 = new_file_limit 준수)

**신규(4)**
- `extension/lib/ohmy_premium_adapter.js` (250줄) — `normalizeCoveragePremium`(EXACT/SCALED_LINEAR/API_FINAL_FIELD/키부재≠0) + `buildMatrixFromResponse`(matrix + provenance 이중 정밀도) + `isProductPremiumsUrl`. rule_version="2787.1".
- `extension/lib/ohmy_active_plan_resolver.js` (124줄) — `resolveActivePlan`(user_coverages fail-closed: UNIQUE_MATCH/MULTIPLE_CANDIDATES/NO_MATCH/INSUFFICIENT, 배열 첫 항목 자동선택 금지) + `hashCoverageDetails`.
- `extension/__tests__/ohmy_premium_adapter.test.ts` (565줄) — 8 describe 33 케이스.
- `extension/__tests__/fixtures/productpremiums.min.json` (197줄) — 비식별 fixture, `_fixture_meta{kind:SANITIZED_FIXTURE,pii:NO_CUSTOMER_PII,auth:NO_AUTH_DATA}`. 정상스케일·미제공·premium 0/null·복수 user_coverages 커버.

**수정(4)**
- `extension/inject.js` (273줄) — no-op(DEPRECATED)을 **MAIN world read-only interceptor 신규 구현**으로 교체. 원본 fetch/XHR 반환·헤더·예외 불변, `response.clone()` 관찰 브랜치 분리, `sanitizeProductPremiums`로 허용 필드만(consultant_id/ga_id/토큰 제외) postMessage(nonce·origin).
- `extension/content.js` (+113/-4) — 삭제 4줄은 **주석만**. `INSURO_DIAG_FLAG=false` + MAIN→ISOLATED relay 리스너 + flag-gated `runDevMatrixDiagnostic()`. 기존 스크래퍼·플로팅 버튼 핸들러 무변경.
- `extension/background.js` (350줄) — `TabScopedBuffer`(sender.tab.id 기준, responseHash dedup, TTL 10분, 전역 latest 없음) + 신규 메시지 2종(`INSURO_PP_BUFFER_PUT`/`GET`). 기존 핸들러 무변경. 자동 서버전송 0.
- `extension/manifest.json` (46줄) — ISOLATED 엔트리(lib 2개 추가) + MAIN 엔트리(inject.js, mmlfcp.ohmymanager.com 한정) 분리.

## 테스트 결과
- `npx vitest run extension/__tests__/ohmy_premium_adapter.test.ts` → **33/33 PASS** (8 describe: interceptor 원본불변 / 자동전송0 / 복수후보 fail-closed / 키부재≠0원 / 정규화정확성 / matrix schema / 다중탭버퍼 / fixture sanity).
- `npx vitest run` 전체 회귀 → **445/445 PASS** (35 파일, 기존 host-matching·matrix-detection 무변경).

## L1 스모크테스트 결과 (필수 기록)
- **서버 재시작**: 해당없음 (확장 라이브러리·순수 로직 스파이크 — 서버 코드 무수정).
- **API 응답 확인**: 해당없음 (실 ohmymanager 접속 불가). 대신 **어댑터 파이프라인 실동작 + 계산엔진 실입력**으로 대체.
- **스크린샷**: 해당없음 (UI 산출물 없음 — src/ 수정 금지 스파이크).
- **실동작 검증(pytest PASS 이상)**:
  1. Node ESM 스모크: fixture → `resolveActivePlan(resp,{})`=**INSUFFICIENT**(복수 user_coverages fail-closed) → context 부여 시 **UNIQUE_MATCH** → `buildMatrixFromResponse`=OK(3사×4담보).
  2. 키 부재≠0원 실측: b002는 HN 미제공(키 부재), d004는 HA premium=null → **둘 다 premiums 키 부재**(0 삽입 0건).
  3. **생성 matrix → 실제 `server/composite_calculator.py::calculate_composite_from_matrix` 입력 → schema 검증 PASS**: `{single_best:DB 91333, dual_best:None, triple_best:None, data_count:10}`. 91333=5800(a001)+85500(b002)+0(c003)+33(d004) — evidence 검증값 일치. dual/triple None은 HN·HA 담보 결손으로 유효 조합 부재(정확한 fail-closed).

## MATCH / GAP / UNKNOWN

**MATCH (계약 실증 일치)**
- 선택담보 authoritative source = `user_coverages.details` (fixture·어댑터 정합).
- 스케일링 `premium × (selected/base)`: a001 580×10000/1000=5800, b002 51300×5000/3000=85500 — 화면 검증값 재현.
- 미제공 = 회사 detailList의 coverage_cd 누락 → premiums 키 부재. 계산엔진 key-absent 로직과 정합.
- 생성 matrix가 기존 계산엔진 입력 schema 검증 PASS (별도 변형 없이 직접 입력).

**GAP (스파이크 범위 밖 — 후속 필요)**
- 실 ohmymanager 접속 불가로 **실환경 transport(fetch/XHR) 실측 미완**. fetch 우선 + XHR 최소 지원으로 구현했으나 어느 것이 실제 발생하는지 실환경 확인 필요.
- `runDevMatrixDiagnostic()`는 flag ON 전용이며 UI 미결선(src/ 수정 금지) — 실 버튼 흐름 E2E는 A2/후속.

**UNKNOWN (fixture로 좁혔으나 실환경 확정 대기)**
- 반올림 계약: SCALED_LINEAR은 정밀도 보존(provenance)하고 matrix만 Math.round. 담보별 floor/round 실제 규칙은 여전히 UNKNOWN → 화면총액 억지보정 금지 원칙 유지.
- 활성플랜 직접 연결키: user_plan_id ↔ 요청 plan_id 직접 매칭 필드 존재 여부 미확인(현재 explicit_link_key 훅만 마련). 복수 user_coverages 시 context 단서 필요.
- 비선형 담보: `coverage_amount_ratio≠1` 시 진단 unknowns 기록만(차단 아님). 실 케이스 미관찰.
- 선택담보 실제 미제공: synthetic fixture로 검증. 실환경 트리거 관찰은 출시 전 보강.
- 운영 JWT 상태: setInsuroJwt ACK 없음(기존 미해결, 범위 밖).

## A2 저장계층 계약 제안 (다음 단계)
1. **캡처 원본 계층**: `sanitizeProductPremiums` 출력(허용 필드 + request_fingerprint + page_session_id)만 저장. consultant_id/ga_id/토큰/PII 비저장. normalized input hash 필드 필수(불변조건).
2. **분석 스냅샷 계층**: `buildMatrixFromResponse` 결과의 matrix + **provenance(raw/base/selected/method/rule_version)** 를 불변 저장. `normalization_rule_version`을 스냅샷에 박아 규칙 변경 시 재현 가능.
3. **활성플랜 귀속**: resolver status가 UNIQUE_MATCH일 때만 저장. MULTIPLE_CANDIDATES/NO_MATCH/INSUFFICIENT는 저장 차단(fail-closed) + 사유(validation_flags) 로깅.
4. **capture_session_id 결선**: 탭 버퍼(TabScopedBuffer)는 로컬 단기(TTL 10분)만. 서버 귀속은 A2에서 조직·설계사 검증 세션으로. 확장 로컬 장기저장 금지.
5. **상태 삼원화(v2.2 §17)**: capture_lifecycle / validation_status / analysis_status 축을 A2 스키마에 반영. SCALED_LINEAR 담보 포함 분석은 validation_status=PENDING 시작 권장.

## 발견 이슈 및 해결
- content.js 4줄 삭제 감지 → diff 확인 결과 **주석 업데이트만**(inject.js no-op 관련 stale 주석), 기존 동작 코드 무변경 확인. 해결.
- 임시 검증 테스트 파일(엔키 작성) → 커밋 전 삭제 확인, git status 신규 4개만 잔존 확인. 해결.

## 머지 판단
- **머지 필요**: **No** (task 지시: PR 생성은 ANU 독립검증 후 별도 승인. merge·운영 활성화 금지).
- **브랜치**: task/task-2787-dev5
- **워크트리 경로**: /home/jay/projects/InsuRo/.worktrees/task-2787-dev5
- **커밋**: 6a3e3ea (worktree 커밋 완료, PR 미생성)
- **머지 의견**: 회귀 445/445 PASS, L1 계산엔진 검증 PASS, flag OFF·자동전송0·기존경로 무변경. 코드 품질 양호. 다만 **본 스파이크는 계약 실측·고정 목적**이며 실환경 transport·활성플랜 연결키가 GAP으로 남아 있어, ANU 독립검증에서 실환경 스모크(신규설치·실 ProductPremiums 발생 transport 확인) 후 merge 승인 권장.

## 비고
- flag OFF 기본, 운영 활성화 0, 자동 push/서버전송 0, 실고객 저장 0, PR merge 0 — task 금지사항 전부 준수.
- callback_schedule_created: false (OS-level pickup runner 소유). relay_hints 부재(green deterministic closeout).

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

