# [Lv.2] 소식지 검토 Workflow Phase 3a — 검토 REST API (목록/상세/전이/보험사마스터)

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "server/main.py"
    - "server/newsletter_review.py"
    - "server/routes/**"
    - "server/tests/**"
  forbidden_paths:
    - ".github/**"
    - "extension/**"
    - "src/**"
    - "server/migrations/**"
    - "server/newsletter_validation.py"
  commands: ["pytest","python3","git"]
  merge_policy: "none"
  ttl_hours: 12
```
> ★ base=현 origin/main(Phase 2 머지됨 7e4c672: newsletter_validation 9규칙 + run_review_pipeline 배선 + transition_review_status). **Phase 3a = 검토 UI가 소비할 관리자 REST API만.** UI는 Phase 3b(별도). 근거: `memory/plans/newsletter-review-workflow_260805/`.

## 배경
Phase 2로 업로드→추출→자동검증→pending_review 라우팅 파이프라인 완성. 그러나 사람이 검토·교정·승인할 **REST 엔드포인트가 없다**(transition_review_status 는 함수로만 존재). Phase 3a = 그 API를 노출. **상태전이는 반드시 기존 `newsletter_review.transition_review_status` 를 호출**(fail-closed 재구현 금지).

## 작업 — 관리자 전용 엔드포인트 4종 (전부 `Depends(verify_admin)`)
소식지 테이블=`newsletters`(PK=UUID). 컬럼(Phase1 017): review_status, insurer/title/body _extracted/_edited, validation_flags(jsonb), field_confidence(jsonb), source_file_ref, model_version, reviewer_id, reviewed/approved/published_at.

1. **GET `/api/insuro/newsletters/review-queue`** — 검토 대기 목록.
   - 필터: review_status IN ('pending_review','editing','auto_validated','extract_failed'). 쿼리파라미터 `status`(옵션)로 단일 상태 필터 허용.
   - 정렬: HIGH 플래그 수 desc → MED 수 desc → created_at desc(이상항목 우선).
   - 각 항목: id, company_name(=insurer_edited??insurer_extracted), title, month_key, review_status, flag_high_count, flag_med_count, created_at. **본문·field_confidence 미포함(경량).**
2. **GET `/api/insuro/newsletters/{id}/review`** — 상세.
   - insurer/title/body 의 _extracted·_edited 양쪽, validation_flags 전체, source_file_ref, model_version, review_status, reviewer_id, 타임스탬프.
   - grounding: validation_flags/저장된 grounding 구조 그대로 반환.
   - ★ **field_confidence 는 응답에 절대 미포함**(plan §9: UI 미노출, 내부보존만).
3. **POST `/api/insuro/newsletters/{id}/transition`** — 교정·전이(핵심).
   - body: `to_status`, `action`(문자열), `edited`(옵션 dict: insurer/title/body 중 교정분), `reason`(옵션).
   - 교정분(edited)이 오면 **`*_edited` 컬럼에만 저장**(원본 `*_extracted` 절대 불변). reviewer_id = JWT sub, actor_type='human'.
   - 반드시 `transition_review_status(doc_id=id, from_status=현재 review_status, to_status=..., actor=JWT sub, actor_type='human', action=..., reviewer_id=JWT sub, sb=...)` 호출. **허용 전이/ published 3중잠금은 함수가 강제** — 라우트에서 우회·재구현 금지.
   - 함수가 거부(부적합 전이)하면 409/422 로 매핑, DB 흔적 0 유지.
4. **GET `/api/insuro/newsletters/insurer-master`** — 보험사 마스터 목록.
   - `newsletter_validation.DEFAULT_INSURER_WHITELIST` 를 **정렬된 배열**로 반환(3b 의 필수 select 소스). import 만, validation.py 수정 금지.

## 제약
- 서버만. migration(017)·validation.py·src·extension·.github 불변. 기존 소식지/업로드/newsletter-chat 회귀 0.
- 숫자·보험사명·날짜: 이 API는 **저장·전달만**, 자동교정 로직 신규 추가 금지(교정은 사람 입력 edited 만).
- 인증: 전부 verify_admin. 비관리자 403.

## 검증
- `pytest server/tests/` 회귀 0. 신규 테스트: 4엔드포인트 정상 + 비관리자 403 + transition 이 함수 위임(부적합 전이 거부→409/422·흔적0) + edited 는 *_edited 에만 기록(_extracted 불변) + review 응답에 field_confidence 부재 + queue 정렬(HIGH>MED>시간) + insurer-master 정렬배열.

## 완료 (★ 순서 B)
- 변경 = server/ 만(main.py + routes + tests). validation.py·migration·src·extension·.github 불변.
- **dev6 금지** — dev1(헤르메스, Phase2 파이프라인 구현 연속성). 커밋 → push → **★ push 반드시 확인**(Phase1 push 누락 사고 재발방지) → finish-task foreground 1회 → .done. background wait 금지. ANU 독립검증·머지·Phase 3b(UI) 위임은 ANU.

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