# [Lv.4] Integration Phase1 PR-C — CRM 후보검색 + 명시적 고객생성 (전면 FA확인 기반)

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "server/routes/**"
    - "server/main.py"
    - "server/customer_match.py"
    - "server/pii_crypto.py"
    - "server/tests/**"
  forbidden_paths:
    - "extension/**"
    - "src/**"
    - ".github/**"
    - "server/utils/legacy_write_guard.py"
    - "server/routes/consultation_history_v1.py"
    - "server/migrations/**"
    - "supabase/migrations/**"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "git"
  merge_policy: "tiered"
  ttl_hours: 24
```

## 배경·단일소스
- 설계: `/home/jay/workspace/memory/plans/insuro-composite-design/integration-design-260721.md` (§2 매칭·CRM 흐름, Codex 교정 6).
- 리포 `/home/jay/projects/InsuRo`, base=현 origin/main(a23f867, 계약V2+allowlist 반영됨).
- **Integration Phase1 PR-C**. Phase2(확장·웹앱)는 별도. 이 task 는 **후보검색 + 명시적 생성 엔드포인트만**.

## 목표 (전면 FA확인 매칭을 서버가 지원)
웹앱이 "인슈로로 보내기" 후 **전면 FA 확인** 매칭을 하려면 서버에 2개 엔드포인트가 필요하다:
1. **후보검색**: 이름+생년월일+성별로 그 FA 자기 고객 후보를 찾아 **5항목**(이름·생년월일·성별·핸드폰·지역) 반환.
2. **명시적 고객생성**: 무매칭 시 FA 확인 후 신규 생성 — **핸드폰 필수**. (기존 `customer_match.upsert_customer_for_capture` 의 조용한 자동생성과 달리 **명시적**.)

## 구현
1. **후보검색 엔드포인트** (신규):
   - **fa_account_id 서버파생 스코프**(`consultation_history_v1.py:_require_fa_account_id` 와 동일 패턴 = `verify_jwt→_verify_incar_member`). **타 FA 고객 절대 반환 0**(서버 쿼리 `.eq("agent_id", fa_account_id)`, 클라 필터 금지).
   - 입력 = 이름+생년월일+성별. **near-match = 단순 정규화 방식**(trigram/similarity 인프라 신설 금지 — 없음, Codex): 이름을 **trim + 연속공백 collapse** 정규화한 뒤 ILIKE(정확+공백차이 흡수), **생년월일·성별은 정확 일치**. 과한 유사도 매칭은 타 고객 오노출이라 금지.
   - 반환 = **5항목**(이름·생년월일·성별·**핸드폰**·**region**). ★ PR-A 가 `customers.region` 컬럼만 추가했으니 이 조회가 **region 을 실제로 SELECT·반환**해야 한다(Codex #6).
   - 마스킹 정책은 기존 `mask_*` 관례 따르되, 매칭 확인에 필요한 식별정보(핸드폰 등)는 FA 본인 고객이므로 확인 가능한 수준으로.
2. **명시적 고객생성 엔드포인트** (신규):
   - fa_account_id 스코프. 이름·생년월일·성별 + **핸드폰 필수**(비면 400) + 지역 선택.
   - `pii_crypto` 로 encrypted_name/dob + customer_key_hash 채움(기존 customers 관례·dual-write 유지). **★ customer_key_hash 는 반드시 기존과 동일 계산 재사용**(Codex): 동일 `PII_HASH_SALT` + `normalize_dob`(6/8자리 정규화) + gender canonicalization(`customer_match.py`/`pii_crypto.py` 그대로). hash 계산이 어긋나면 기존 고객·웹앱 hash 그룹핑·`(agent_id,customer_key_hash)` unique 와 불일치 → 중복/오매칭. `upsert_customer_for_capture` 의 hash 로직을 **재사용(중복 구현 금지)**.
   - `(agent_id, customer_key_hash)` unique 로 **중복 생성 방지**(이미 존재하면 409 또는 기존 반환 — 조용한 덮어쓰기 금지).
   - **조용한 자동생성 아님** — 이 엔드포인트는 FA 가 명시적으로 호출. 기존 `upsert_customer_for_capture` 의 auto-create 경로(main.py ohmy-capture)는 **건드리지 마라**(guard-blocked·미사용, Codex).
   - 라우터 등록: **신규 라우터 파일**(server/routes/) 로 만들고 `main.py` 는 **import + include_router 2줄만**(Codex 실측 — 1줄 불가). 다른 부분 손대지 마라.

## 제약
- **타 FA 유출 0** — 모든 조회·생성이 서버파생 fa_account_id 스코프. 클라 주장 id/agent 불신.
- consultation_history_v1.py(ingest)·allowlist·migration·계약 **변경 금지**(PR-A/B 로 완료).
- extension/src **손대지 마라**(Phase2).
- feature-flag·allowlist env 켜지 마라.

## 검증 (전부 통과)
- 타 FA 고객 검색/생성 시도 → **반환·생성 0**(tenant 경계 테스트).
- near-match: 오타/띄어쓰기 이름 → 기존 고객이 후보로 뜸(무매칭→중복 방지 증명).
- 핸드폰 없이 생성 → 400. 있으면 생성.
- region 저장·조회 round-trip.
- 중복(같은 customer_key_hash) 생성 → 조용한 덮어쓰기 0(409/기존반환).
- 기존 회귀 0(현 main 기준 감소 0).

## 완료
- 지정 경로 외 변경 0, main.py 는 include 1줄만 (`git diff --stat` 증명)
- worktree finish(STRICT+정확base+worktree cwd) → PR → Gemini High 0 → ANU 독립검증. **머지 금지**.
- **ANU callback**(UTF-8 ≤3900 bytes, envelope). 자기완료 scope-guard 막힘 시 result.json+ANU callback escalate.