# CALC_INS_AGE_FIX — Calculators.tsx 보험나이 근사 결함 수정

> **회장 승인 2026-07-20.** 실사용 화면에 노출되는 값이라 실영향 있음.

## base / branch
- **신규 브랜치** `task/<ALLOCATED_TASK_ID>-calcage-<TEAM>` 을 **`cc7476bfbd9f6b3ae3c99f9a7136cc9f94cd3ada`**(현재 `origin/main`)에서 분기
- worktree 신규 생성

## expected_files
```yaml
allowed_resources:
  paths: ["src/pages/Calculators.tsx", "src/**/__tests__/**", "src/**/*.test.ts", "src/**/*.test.tsx"]
  new_file_limit: 2
  commands: ["git","npm","node","npx"]
  merge_policy: "none"
```
⚠️ `extension/**`·`server/**` 수정 **금지**(다른 track 영역).

## 문제 (ANU 실측)
`src/pages/Calculators.tsx` 의 `calcInsuranceAge()` 가 **일수 나눗셈 근사**를 쓴다:
```js
const totalDays = (today.getTime() - birth.getTime()) / MS_PER_DAY;
const exactYears = totalDays / 365.25;
const insAge = Math.round(exactYears);      // ← 결함
```
**6개월 경계 하루 전에 이미 올림**되어 보험나이가 1살 많아진다.

**ANU 실측 (생년월일 1983-08-09)**
```
날짜           정확규칙   현재구현
2026-02-07       42        42     OK
2026-02-08       42        43     ★불일치
2026-02-09       43        43     OK
2026-07-20       43        43     OK
2026-08-09       43        43     OK
2027-02-07       43        43     OK
2027-02-08       43        44     ★불일치
2027-02-09       44        44     OK
```
→ **1년에 이틀, 화면에 틀린 보험나이가 표시된다.**

## ★ 올바른 규칙 (회장 확정 2026-07-20)
**보험나이 = 만나이 + (마지막 생일로부터 6개월 이상 지났으면 +1)**
= 생일 기준 **-6개월 ~ +6개월** 구간이 해당 나이.
```
만나이     = 올해 생일 지났으면 (연도차), 아니면 (연도차 - 1)
마지막생일  = 만나이 기준 생일 날짜
보험나이   = 만나이 + (오늘 >= 마지막생일 + 6개월 ? 1 : 0)    // 정확히 6개월이면 올림
```

## 수정 요구
1. **일수 나눗셈 근사 제거.** `365.25`·`365.2425`·`MS_PER_DAY` 기반 연수 환산 **금지**
2. **월 단위 날짜 연산**으로 구현. 윤년·월말일(예: 8/31 + 6개월 = 2/28 or 3/1?) 처리 방식을 **정하고 테스트로 고정**
3. **만나이 계산은 현행 유지** — 이미 올바르다(`mDiff`/`dDiff` 방식)
4. 함수를 **테스트 가능하게 분리**할 것(현재 `useCallback` 안에 갇혀 있어 단위테스트 불가). 순수 함수로 추출하고 컴포넌트는 그것을 호출
5. **기준일 주입 가능**하게 할 것(`new Date()` 직접 호출 금지 — 테스트가 실행 시각에 따라 깨진다)

## 검증
- **경계 테스트 필수** — 위 실측표 8케이스 전부:
  `2026-02-07→42` · `2026-02-08→42` · `2026-02-09→43` · `2026-07-20→43` · `2026-08-09→43` · `2027-02-07→43` · `2027-02-08→43` · `2027-02-09→44`
- 추가: **2월 29일생**(윤년), **월말일생**(1/31·8/31), 생일 당일, 미래 생년월일(거부)
- 기존 다른 계산기 기능 **회귀 0**
- `npx vitest run` (또는 저장소 표준 명령) 전체 PASS. **ANU 재실행 대상**

## ★ 절대 제약
- **PR 생성 0 · main merge 0.** 커밋 **정확히 1개**
- **금지**: skip · xfail · assertion 완화 · `extension/**`·`server/**` 수정 · 근사식 유지
- ⚠️ **`Math.round(exactYears)` 를 "기존 동작 보존" 명목으로 남기지 말 것.** 그것이 결함이다

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

## 보고
- `/home/jay/workspace/memory/reports/calc-insurance-age-fix.md` (저장소 밖 · git stage 0)
- diff / 월단위 구현 근거 / **경계 8케이스 실행 원문** / 윤년·월말일 처리 결정과 근거 / 테스트 카운트 전후 / MATCH·GAP
- 봇 종료 전 ANU callback(envelope만·UTF-8 ≤3900B, collector=ANU key c119085addb0f8b7)

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