# task-2787+4 — 진단빌드 한정 "현재선택 DOM 스냅샷" 스파이크 (읽기전용 관찰, 구현 아님)

## 레벨
Lv.3 (진단 관찰 로직 + 테스트). **same-branch `task/task-2787+2-dev5` 계속 커밋. 코드+테스트+worktree 커밋+ANU 보고까지. PR·merge·배포·운영활성화 금지.**

## 단일 소스 (먼저 읽을 것)
`memory/reports/task-2787-data-contract-consensus-260718.md` (GPT·Codex·ANU 3자 합의) + `memory/tasks/task-2787+2.md`.

## 배경 (합의)
self-smoke #1: 캡처 경로는 VERIFIED, 그러나 `selected_coverage_count=null`(user_coverages 2개·활성식별 불가)로 어댑터가 전체 담보(64~74개)를 합산 → 화면총액 불일치(메리츠 화면343,414 vs adapter481,359). 확정 계약 = **DOM=현재 선택담보+가입금액 / API=보험료 원천 / 화면표시=검증 oracle**. 이번 작업은 **구현이 아니라**, DOM 현재선택을 **병렬 관찰 필드**로만 추가해 실데이터 증거(MATCH/GAP/UNKNOWN)를 얻는 스파이크.

## ★ 불변식 (절대)
- **기존 user_coverages 경로/필드 파괴 금지** — DOM 관찰은 전부 **새 병렬 필드**로만 추가. 기존 `selected_coverage_count`/`per_insurer` 그대로 유지.
- 모든 신규 DOM 로직은 **진단 게이트(inject.js:469 `__INSURO_DIAG_ACTIVE__===true` → runRealInjection) 안**에서만 실행 = 프로덕션 무동작. 순수함수는 `isTestEnvironment` 시 export만.
- 어댑터 로직 위치 = **inject.js 내부**(buildDiagnosticSummary). `extension/lib/ohmy_premium_adapter.js` 참조 금지(존재하지 않음).

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "extension/inject.js"
    - "extension/__tests__/diagnostic_build.test.ts"
    - "extension/__tests__/dom_selection_snapshot.test.ts"
    - "memory/reports/task-2787+4.md"
  forbidden_paths: ["src/**","server/**","supabase/**","**/migrations/**",".github/**",".env*","extension/content.js","extension/background.js","extension/manifest.json","extension/scripts/build-diagnostic.mjs"]
  new_file_limit: 1
  commands: ["git","npm","node","npx"]
  merge_policy: "none"
  ttl_hours: 8
```
※ dist-diagnostic/ 재생성은 `node extension/scripts/build-diagnostic.mjs` 실행으로(스크립트 자체 수정 금지). ANU가 재빌드+hash 재고정 예정.

## 구현 요구 (관찰 필드 추가 — inject.js, 진단 게이트 내부)
### S1. checked 담보 추출
`#bojang_lists input[type="checkbox"][data-cd]` 중 **`el.checked===true` 인 것만** 수집 → `dom_selected_cds[]`(data-cd 값), `dom_checked_count`. (기존 "전체 수집"과 별개.)

### S2. 가입금액 실소스 병렬 실사
각 checked 담보 행에서 **(a) `el.getAttribute("guide_coverage_amount")`(attr)** 와 **(b) 그 담보 행의 편집가능 `<input>`의 `.value`** 를 **둘 다** 읽어 병렬 기록: `dom_amounts[] = {cd, amount_attr, amount_input}`. 행에서 input을 못 찾으면 `amount_input:"UNKNOWN_NO_INPUT"`(추측·0 대입 금지). ※input 탐색은 해당 담보 행(checkbox의 조상 row) 범위로 한정.

### S3. coverage_cd join 검증 (코드만, 담보명 금지)
`dom_selected_cds` ↔ API `detailList[].coverage_cd` 코드 join. 회사별로 아래 카운트 기록: `join_matched`(API에 코드 존재+premium 유한) / `join_gap_missing`(코드가 API detailList에 없음) / `join_gap_premium_null`(코드는 있으나 premium 키부재·null) / `join_duplicate`(**회사별** 동일 coverage_cd 중복). 1 DOM담보→1 API코드 확정 안 되면 `fail_closed:true` 표기(임의 채택 금지).

### S4. DOM 선택 기반 재계산 (별도 필드, 기존 경로 미변경)
`dom_selected_cds` 를 selectedCdSet 으로 쓰는 **optional param 또는 sibling helper**로 회사별 `dom_selected_adapter_sum` 을 **신규 필드**로 산출(buildDiagnosticSummary 의 user_coverages 내부생성 경로는 그대로 두고, DOM 집합을 주입/전달하는 경로 추가). premium 유한수만 합산(0/null 미변환).

### S5. 검증 대조 분류 (신규)
회사별 `dom_selected_adapter_sum` vs 화면 회사 표시총액 → `EXACT`(diff==0) / `WITHIN_TOLERANCE`(|diff| ≤ **원 단위 반올림 허용치: 담보수 × 1원**, 명시) / `OUT_OF_TOLERANCE`(초과). 회사별 `{company_name, dom_selected_adapter_sum, screen_total, diff, classification}`.

### S6. race 증거
`api_captured_at`(ppBuffer.capturedAt) 와 `dom_snapshotted_at`(DOM 스냅샷 시각) 타임스탬프를 요약에 기록(둘 사이 gap = DOM이 캡처 후 바뀌었을 위험 증거).

## 출력
기존 `[InsuRo Diagnostic]` 요약에 위 신규 필드 추가: `dom_checked_count · dom_selected_cds · dom_amounts · per_insurer_dom[{company_name, join_matched, join_gap_missing, join_gap_premium_null, join_duplicate, dom_selected_adapter_sum, screen_total, diff, classification}] · fail_closed · api_captured_at · dom_snapshotted_at`. 기존 필드 전부 유지.

## 검증 (봇 자체 + ANU 재실행)
- 신규 순수함수(코드 join·tolerance 분류·checked 추출) 단위테스트 신규 파일 `dom_selection_snapshot.test.ts`: jsdom으로 #bojang_lists 체크박스(일부 checked)+가입금액 input+ #premium_lists 셀 + API fixture 를 구성해 → dom_checked_count/ join 카운트/ tolerance 분류/ fail_closed 를 assert. 기존 `diagnostic_build.test.ts` 전부 유지.
- 라이브-DOM 미지수(실 input 존재·checked 제출상태·data-cd==API코드)는 **회장 self-smoke #2** 로 판정(코드로 단정 금지).
- **ANU 재실행**: `npx vitest run` 전체 + 신규 파일. 정확한 파일수/전체수/신규수 보고.

## goal_assertions
- `npx vitest run extension/__tests__/dom_selection_snapshot.test.ts`
- `npx vitest run`

## STOP_REPORT 조건
기존 user_coverages 경로를 건드려야만 DOM경로 산출 가능 · 진단 게이트 밖에서 실행해야 함 · content.js/background.js/manifest 수정 필요 · new_file_limit 1 초과 · DOM 보험료 셀을 주 계산원으로 써야 함.

## 이번 스파이크 금지 (명시)
- USER_COVERAGES_NOT_UNIQUE 의 "전체행 합산" fallback **실제 변경 금지**(이번은 병렬 관찰만). scrapeScreenTotals 필터화 금지. → 증거 후 별도 승인.
- DOM 보험료 셀 주 계산원 금지 · 담보명 단독매칭 금지 · 실패시 DOM 자동 fallback 금지 · 기존 취약 DOM matrix 주경로 복구 금지.
- PR·merge·배포·운영활성화 · 서버/DB/CRM/UI 수정 · 자동전송/push · 실고객 저장.

## 산출물 & 보고 (PR 없음)
1. same-branch `task/task-2787+2-dev5` 커밋. PR 금지.
2. 보고: inject.js diff / 신규 필드 예시 출력 / 신규+기존 vitest 카운트 / 라이브-DOM 미지수 목록 / MATCH·GAP·UNKNOWN.
3. 봇 종료 전 ANU callback(envelope만·UTF-8 ≤3900B, collector=ANU key c119085addb0f8b7).