# TRACK_A_AGE_CROSSCHECK — age 교차검증 추가 + 실사값 박제

> **근거**: 2026-07-20 회장 라이브 실사. **stale `age=42`(만나이) 가 게이트를 통과할 수 있는 결함**을 Codex 가 실물 코드로 확인.

## base / branch
- **기존 브랜치 `task/task-2802-dev1` 에 이어서 커밋**
- worktree: `/home/jay/projects/InsuRo/.worktrees/task-2802-dev1`
- ★ 읽을 때 **반드시 브랜치 기준**. 워킹트리는 `main` 이라 이 코드가 없다.

## expected_files
```yaml
allowed_resources:
  paths: ["extension/content.js", "extension/__tests__/**"]
  new_file_limit: 1
  commands: ["git","npm","node","npx"]
  merge_policy: "none"
```

## ★ 실사 원본 (2026-07-20, 회장 ohmymanager 로그인)
```
ppd_urls (resource timing, 시간순):
  1) plan_id=000000111041, insurance_type=F, age=42, gender=M   ← 만나이
  2) plan_id=000000111041, insurance_type=F, age=43, gender=M   ← 보험나이
  3) plan_id=000000111041, insurance_type=F, age=43, gender=M   ← 보험나이
DOM: #birth_date = "19830809"
화면 텍스트: "생년월일 ( 보험나이 : 43세 )"
```
**산출**: 생년월일 1983-08-09 / 조회일 2026-07-20 → 만나이 **42** · `Math.round(42.947)` = **43**
→ **URL `age` 는 보험나이다. 만나이가 아니다.**

## ★ 결함 (Codex 확인)
`content.js:687-691` 의 `domCrossCheck` 가
```js
age: "UNAVAILABLE_NO_DOM_SOURCE",
plan_id: "UNAVAILABLE_NO_DOM_SOURCE",
```
→ **A4 게이트가 `age` 를 대조하지 않는다.** `gender`·`insurance_type`·owner·`startTime` 만 본다.
→ **stale `age=42` entry 가 남아 있으면 그대로 통과해 보험료가 전부 틀린다.**
→ 실사로 **`#birth_date` 라는 DOM 소스가 실재함이 확인**됐으므로 `UNAVAILABLE` 은 더 이상 사실이 아니다.

## 수정 요구

### 1. age DOM 교차검증 추가 (핵심)
- `#birth_date`(형식 `YYYYMMDD`) 를 읽어 **보험나이 산출**.

#### ★★ 산출 규칙 (회장 확정 2026-07-20) — **일수 나눗셈 근사 금지**
**보험나이 = 만나이 + (마지막 생일로부터 6개월 이상 지났으면 +1)**
= 생일 기준 **-6개월 ~ +6개월** 구간이 해당 나이.
```
만나이   = 올해 생일 지났으면 (연도차), 아니면 (연도차 - 1)
마지막생일 = 만나이 기준 생일 날짜
보험나이 = 만나이 + (오늘 >= 마지막생일 + 6개월 ? 1 : 0)   // 정확히 6개월이면 올림
```
**★ `Math.round(경과일수 / 365.2425)` 같은 근사 절대 금지.** ANU 가 실측으로 경계 오류를 확인했다:
```
생년월일 1983-08-09
  2026-02-08 → 규칙 42 / 근사 43   ★불일치 (하루 일찍 넘어감)
  2027-02-08 → 규칙 43 / 근사 44   ★불일치
  나머지 날짜는 일치
```
→ 근사식은 **6개월 경계 하루 전에 이미 올림**되어 **보험나이가 1살 많아지고 보험료가 전부 틀린다.**
⚠️ `src/pages/Calculators.tsx` 의 `calcInsuranceAge()` 도 **같은 근사 결함이 있을 수 있다.** 참조하지 말고 **위 규칙대로 직접 구현**하라. (그 파일 수정은 범위 밖 — 별도 백로그)
**월 단위 날짜 연산**으로 구현할 것(일수 나눗셈 아님). 윤년·월말일(예: 8/31 + 6개월) 처리 방식을 정하고 **테스트로 고정**하라.
- 산출값 vs URL `age` **불일치 → fail-closed**(기존 실패코드 체계 사용. 새 코드 발명 금지)
- `#birth_date` 부재·형식 불량 → `UNAVAILABLE_NO_VALUE` 로 처리(기존 3상태 패턴 유지). **없다고 통과시키지 말 것** — 기존 정책에 맞춰 판단하고 근거를 보고에 남길 것
- ⚠️ **만나이로 계산하지 말 것.** 실사가 보험나이임을 보였다.

### 2. startTime 동률 처리 (Codex 지적)
`findProductPremiumsResourceEntry()` 가 `entry.startTime` **최대**를 고르는데, **동률이면 먼저 본 것이 이긴다(비결정적)**.
→ **결정적 tie-break** 를 넣어라(예: 동률 시 배열 뒤쪽 우선). 방식은 구현자가 정하되 **테스트로 고정**할 것.

### 3. 실사값 fixture 박제
- **★ 보험나이 경계 테스트 필수**: 생년월일 `19830809` 기준
  `2026-02-08`→**42** · `2026-02-09`→**43** · `2026-07-20`→**43** · `2026-08-09`→**43** · `2027-02-08`→**43** · `2027-02-09`→**44**
  (근사식을 쓰면 2026-02-08·2027-02-08 에서 실패하므로 **회귀 탐지 역할**을 한다)
- `track-a-gates.test.ts`: `getEntriesByType` fixture 로 `age=42@t100` · `age=43@t200` · `age=43@t300` → **`age=43` 선택** assert
- `track-a-preview.test.ts`: 실측 4값(`age=43`·`gender=M`·`insurance_type=F`·`plan_id=000000111041`)으로 전체 게이트 통과 → `snapshot.query_condition` 정확 일치 assert
- **신규 fixture 파일 만들지 말고 기존 인라인 스타일 유지**(현 테스트는 전부 인라인 Vitest/JSDOM)

### 4. 주석 박제 — ★ "확정" 과 "관찰" 구분
`age` 추출·사용 지점에 아래 취지를 명시:
```
OBSERVED-1 (2026-07-20, 회장 라이브 표본 1건):
query_condition.age = 보험나이(insurance age), 만나이 아님.
근거: DOB 19830809 / 조회일 2026-07-20 → 만나이 42, URL age=43, 화면 "보험나이 : 43세".
⚠️ 단일 표본이다. 다른 상품·경계일(생일 전후, ±6개월)에서 재검증되지 않았다.
회귀 방지 목적의 관찰 기록이며 "전면 증명" 으로 인용하지 말 것.
```
**만나이로 "단순화" 하려는 향후 수정을 막는 것이 목적**이다.

## 검증
- `npx vitest run extension/__tests__/` → 기존 **173 passed 유지**(감소 0) + 신규 분기 전부 PASS
- age 불일치 시 fail-closed 되는 분기 테스트 존재
- startTime 동률 tie-break 테스트 존재
- ANU 재실행 대상

## ★ 절대 제약
- **PR 생성 0 · main merge 0.** 추가 커밋 **정확히 1개**
- **금지**: `src/**`·`server/**` 수정 · skip · xfail · assertion 완화 · 서버 전송 코드 · MAIN world 인터셉터 · 신규 실패코드 발명
- A7·A1~A6 기존 동작 훼손 금지

## 봇 안전 프로토콜
- Edit·commit·finish 직전마다 **cancel 마커 재확인**
- **`.done` 자동마커는 완료 증거가 아니다** — ANU 독립검증이 권위. 커밋·보고서 반드시 남길 것

## 보고
- `/home/jay/workspace/memory/reports/trackA-age-crosscheck.md` (저장소 밖 · git stage 0)
- diff / 결함 조치 근거 / **vitest 원문(전후)** / 실사 fixture 반영 증거 / MATCH·GAP
- 봇 종료 전 ANU callback(envelope만·UTF-8 ≤3900B, collector=ANU key c119085addb0f8b7)

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