# [task-2901] 소식지 검토 Phase 3b-2 — 관리자 검토 UI (목록 우선정렬 + 2-pane 교정 + 고위험 게이트)

- 작업자: 오딘(dev2 팀장, 설계/분배/검토/통합) · 구현: 프레이야(frontend, sonnet)
- 팀: dev2-team
- 레벨: Lv.3 / 한정승인(X팀) — 게이트 통과 주체=팀장
- 일자: 2026-08-05
- 프로젝트: InsuRo (`/home/jay/projects/InsuRo`), base=origin/main **a914594**(Phase 3a #179 포함)
- 브랜치: `task/task-2901-dev2` (worktree `/home/jay/projects/InsuRo/.worktrees/task-2901-dev2`)

## S (Situation)
소식지 AI 추출값은 사람이 발행 전 1회 검토·교정해야 함(금소법 오발행 방지). Phase 1(상태기계/017), Phase 2(자동검증9규칙), Phase 3a(검토 REST API 4종)까지 머지 완료. 남은 것은 관리자가 실제로 원문↔추출을 대조·교정·승인하는 **화면(Phase 3b-2)**. 병렬로 dev1(헤르메스)이 백엔드 교차검증(task-2900, GPT 2차 검증)을 진행 중이며, 두 태스크는 `cross_model_disagree` 플래그 스키마를 고정 공유.

## C (Complication)
- 신규 페이지 금지 → 기존 `AdminNewsletters.tsx`(596줄) 확장. 파일 비대화 위험.
- `field_confidence`(신뢰도 숫자) 미노출 정책 — 결론형 문구 금지, 과정형만.
- 승인은 fail-closed 이중 게이트: (1) 보험사명이 마스터 목록에 있어야 함, (2) 고위험 필드 3종 확인 체크박스 전부 체크. `editing→approved` 직행은 서버가 거부(승인은 `pending_review`에서만).
- 실 백엔드(task-2900) 병렬 미머지 → 교차검증 플래그는 **목업 스키마**로 렌더 검증해야 함.

## Q (Question)
Phase 3a API 4종을 소비하여, 우선정렬 목록·2-pane 교정·cross_model 3열 대조·fail-closed 승인 게이트를 기존 페이지 확장으로 구현하고, 실렌더로 육안 검증할 수 있는가?

## A (Answer)
구현·검증 완료. 기존 페이지에 Tabs를 추가하고 검토 UI 본체는 `src/components/newsletter/`로 분리(오케스트레이션만 페이지에). L1 실렌더 스크린샷 3종으로 핵심 시나리오 전부 육안 통과.

## 생성/수정 파일 (전부 `src/` — server/extension/.github 불변)
| 파일 | 상태 | 줄수 | 요약 |
|---|---|---|---|
| `src/pages/AdminNewsletters.tsx` | 수정 | 630 | Tabs("소식지 관리"/"검토 대기") 추가, 기존 콘텐츠 유지, ReviewQueue+ReviewDetailPanel 오케스트레이션 |
| `src/components/newsletter/reviewApi.ts` | 신규 | 117 | Phase 3a REST 4종 타입+fetch(기존 Bearer 세션 패턴 재사용) |
| `src/components/newsletter/ReviewQueue.tsx` | 신규 | 125 | 검토 대기 목록, 방어적 우선정렬(HIGH→MED→무플래그), ❌/⚠️/✅ 뱃지 |
| `src/components/newsletter/ReviewDetailPanel.tsx` | 신규 | 267 | 2-pane 교정(좌 원문/우 필드), insurer-master select, 3종 게이트, 상태전이 버튼 |
| `src/components/newsletter/ValidationFlags.tsx` | 신규 | 92 | HIGH/MED/INFO 색상 렌더 + cross_model_disagree 3열 대조 특수 렌더 |
| `src/components/newsletter/ValidationFlags.test.tsx` | 신규 | 62 | 4 tests (색상·3열) |
| `src/components/newsletter/ReviewQueue.test.tsx` | 신규 | 110 | 5 tests (우선정렬·뱃지) |

커밋: `0f51c15` (micro-commit, src/ 만).

## 게이트 검증
- **G1 설계**: affected_files 전부 `src/` 하위, allowed_resources 부합. 병렬 task-2900은 `server/`만 → 파일 무중첩(충돌 0). ✅
- **G2 구현**: 팀 구현 + 팀장 통합검증(아래 L1). ✅
- **G3 머지**: worktree `--action pr`로 PR 생성 → Gemini(sunset, static fallback) → **ANU 독립검증·머지가 소유**(순서 B). 팀장은 push+.done까지.

## 테스트 결과
- `npx tsc --noEmit` → **0 에러**
- `npm run build` → **PASS** (`✓ built in 11.85s`, PWA precache 175 entries)
- `npx vitest run src/components/newsletter/` → **9 passed / 0 failed**
- `npx vitest run`(전체 회귀) → **75 files, 1136 passed / 0 failed** (신규 9 포함, 회귀 0)

## L1 스모크테스트 결과 (실렌더 — 필수)
- **서버 재시작**: 해당없음(프론트, 로컬 vite dev 하니스 사용). 임시 `window.fetch` 목업 하니스로 Phase 3a API 4종 스텁 후 실브라우저 렌더(하니스는 커밋/머지 대상 아님, 검증 후 삭제).
- **API 응답 확인**: 해당없음(실서버 대신 목업 — 실 백엔드 task-2900 병렬 미머지).
- **스크린샷** (Playwright, console 0 errors): `/home/jay/workspace/memory/reports/task-2901-shots/`
  1. `task-2901-l1-list-flags.png` — 목록 우선정렬(없는손보사 ❌2/⚠️1 → DB손해보험 ⚠️3 → 현대해상 ✅) + cross_model_disagree **3열 대조**(1차 Claude 월12,300 / 교차 GPT 월21,300 / 원문 월21,300) + HIGH/MED/INFO 색상 구분.
  2. `task-2901-l1-detail-badmaster-approve-disabled.png` — 2-pane(좌 원문/우 필드), 미등록 보험사 → **승인 버튼 disabled** + "마스터 목록에 없는 보험사명 — 확인 필요, 승인 보류" 경고(fail-closed 실증).
  3. `task-2901-l1-detail-goodmaster-gates-approve-enabled.png` — 삼성화재(마스터 등록) + 게이트 3종 체크 → **승인 버튼 활성화**(게이트 양성 경로 실증).
- **육안 확인**: 팀장이 3장 직접 확인 — 렌더·스타일·색상·전이버튼 정상. (헤드리스 chrome 이모지 폰트 미설치로 ❌/✅ 문자만 네모박스 표기, 뱃지 자체는 정상 — 실브라우저 무관.)

★ L1 통과: 요구된 5개 시나리오(우선정렬 / 2-pane / 3열 대조 / 미등록 승인 비활성 / 게이트 미체크 승인 불가) **전부 실렌더 육안 검증**.

## 제약 준수 확인
- ✅ `src/` 만 변경(git status로 확인). server/extension/.github 무변경.
- ✅ field_confidence 미노출(응답에 없음 + 렌더 대상 제외).
- ✅ 문구 과정형("검토 대기"/"제시 증거"/"확인 필요") — 결론형 회피.
- ✅ 숫자·보험사명·날짜 자동값 채우기 없음(사람 직접 교정만, 편집은 editing 상태에서만).
- ✅ `editing→approved` 직행 버튼 없음(승인은 pending_review에서만 노출).

## 발견 이슈 및 해결
- **(해결) 중간 진단 오탐**: Edit 도중 스냅샷에서 ReviewQueue/ReviewDetailPanel "미사용" 경고 → 최종 파일 grep으로 render 465·469줄 연결 확인, tsc 0. stale 스냅샷으로 판정, 실제 통합 정상.
- **(마이너·비차단) a11y 경고 1건**: Radix `DialogContent` Missing Description(aria-describedby). 기능 무관, 기존 AdminNewsletters 다이얼로그와 동일 패턴(회귀 아님). Fast-follow에서 DialogDescription 추가 여지.
- **(범위 외 위임)** reject 사유 입력이 `window.prompt`(간단 UX) — Phase 3b-3에서 정식 Dialog 교체 여지.

## 머지 판단
- **머지 필요**: Yes (Lv.3, `--action pr`)
- **브랜치**: `task/task-2901-dev2`
- **워크트리 경로**: `/home/jay/projects/InsuRo/.worktrees/task-2901-dev2`
- **머지 의견**: 순수 additive(신규 5파일 + 페이지 확장), src/ 국한, tsc0·build PASS·회귀0, L1 실렌더 5시나리오 통과. **ANU 독립검증(실렌더 스크린샷)·머지·배포는 ANU 소유**(순서 B). 배포 시 서버 `git pull`+재시작(Phase 2·3a 실동작) + 웹앱 CF Pages 배포 필요.

## 모델 사용 기록
- 프레이야(frontend): **sonnet** — 일반 코딩/로직 구현(기본값). haiku 미사용.
- 팀장(오딘, opus): 설계/분배/통합/L1 검증만(직접 코딩 없음).

## 다음 Phase 참고
- task-2900(교차검증 백엔드, dev1) 머지 후 실 데이터로 UI 재검증 시 cross_model_disagree 3열이 실플래그로 렌더됨(스키마 고정 일치).
- Phase 3b-3 후보: reject 사유 Dialog화, published→corrected 정정 워크플로우(T8), 발행 disclaimer.

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

