★★★ 작업 취소됨 (CANCELLED) ★★★

# [Lv.3] 소식지 검토 Phase 3b-1 — 교차모델 검증(Codex/GPT 2차 검증) 파이프라인 추가

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "server/newsletter_review.py"
    - "server/newsletter_cross_verify.py"
    - "server/tests/**"
  forbidden_paths:
    - ".github/**"
    - "extension/**"
    - "src/**"
    - "server/migrations/**"
    - "server/newsletter_validation.py"
    - "server/main.py"
  commands: ["pytest","python3","git"]
  merge_policy: "none"
  ttl_hours: 16
```
> ★ base=현 origin/main(a914594, Phase 3a 머지됨). **Phase 3b-1 = 교차모델(다른 엔진) 2차 검증 패스 추가.** 프론트 UI는 Phase 3b-2(병렬 별도 태스크, src/ 만) — 이 태스크는 server/ 만. 근거: `memory/plans/newsletter-review-workflow_260805/`.

## 배경 (회장 260805 승인)
현 파이프라인 T3b는 **같은 모델(Claude)이 자기 출력을 자기검증** → 자기 환각을 못 잡는 상관오류 한계. 회장 지시 = **다른 엔진(GPT/OpenAI)으로 2차 교차검증** 추가. 서로 다른 아키텍처가 같은 숫자·보험사명을 똑같이 오독할 확률은 낮음 → 두 모델 불일치 = 고신뢰 의심신호를 사람에게 제시.

## 작업
### 1) `server/newsletter_cross_verify.py` 신규 (async)
- 함수 `async def cross_verify(source_text, extracted: dict, model_version: str) -> list[dict]`.
- **OpenAI API 직접 호출**(httpx async, `OPENAI_API_KEY` env). 모델=gpt-4o(또는 가용 최신). **codex CLI subprocess 쓰지 말 것**(지연·불안정).
- 프롬프트: 원문(source_text)만 근거로 insurer·title·body 핵심 필드를 **독립적으로 재판독**하고, 1차 추출(extracted)과 **필드별 일치/불일치 + 원문 인용**을 JSON으로 반환하게. **추출값을 GPT에 먼저 보여주고 "맞냐"고 묻지 말 것**(확증편향) — 원문에서 GPT가 먼저 뽑게 한 뒤 비교.
- ★ **extractive-only·플래그 only**: 어떤 필드값도 수정/생성하지 않음. 반환은 아래 플래그 리스트뿐.
- **graceful degradation**: OPENAI_API_KEY 부재 / 호출 실패 / 타임아웃(예: 30s) / JSON 파싱 실패 → 파이프라인 중단 금지, `cross_model_unavailable` INFO 플래그 1건만 반환.

### 2) `run_review_pipeline` 에 배선 (newsletter_review.py)
- 결정론 9규칙 + T3b 자기검증 **직후** `cross_verify` 호출 → 반환 플래그를 **기존 validation_flags 에 append**(기존 플래그 구조 불변, 병합만).
- 불일치 HIGH 플래그가 생기면 기존 라우팅 로직대로 pending_review 로 감(별도 라우팅 재구현 금지 — 플래그 존재 → 기존 pending_review 경로 재사용).
- 비동기·best-effort: cross_verify 실패해도 나머지 파이프라인·전이 정상.

## ★ 교차검증 플래그 스키마 (Phase 3b-2 UI와 계약 고정 — 정확히 이 형태)
`newsletter_validation._mk_flag` 와 동일 `{rule, level, field, message, detail}`:
```json
{
  "rule": "cross_model_disagree",
  "level": "HIGH",              // insurer·숫자·날짜 불일치=HIGH, 그 외=MED
  "field": "insurer",           // insurer|title|body|<필드>
  "message": "교차검증(GPT)과 1차 추출 불일치",
  "detail": {
    "primary_value": "<Claude 1차 추출값>",
    "verifier_value": "<GPT 재판독값>",
    "source_quote": "<원문 인용 span>",
    "verifier": "gpt-4o"
  }
}
```
미가용 시: `{"rule":"cross_model_unavailable","level":"INFO","field":"_meta","message":"교차검증 미완(검증기 미가용)","detail":{"reason":"..."}}`

## 제약
- server/ 만(newsletter_review.py + 신규 cross_verify.py + 테스트). validation.py·main.py·migration·src·extension·.github 불변.
- 숫자·보험사명·날짜 자동수정 절대 금지(플래그만). AI/GPT는 발행 승인권 0.
- 기존 소식지/업로드/파이프라인 회귀 0.

## 검증
- `pytest server/tests/` 회귀 0. 신규: 불일치→cross_model_disagree HIGH(insurer/숫자/날짜) / 일치→플래그 0 / OPENAI 키 없음→cross_model_unavailable INFO·파이프라인 계속 / 호출 예외→graceful·중단없음 / *_extracted 절대 불변 / validation_flags 병합(기존 플래그 보존). OpenAI 호출은 mock 으로 결정론 테스트.

## 완료 (★ 순서 B)
- 변경 = server/ 만. **dev6 금지** — dev1(헤르메스, 소식지 파이프라인 연속성). 커밋 → push → **★ push 반드시 확인** → finish-task foreground 1회 → .done. background wait 금지. ANU 독립검증·머지·배포는 ANU.

## goal_assertions (auto-generated)
- `pytest server/tests/`
