# task-2908 보고서 — 소식지 교차검증 원본 렌더를 매직바이트로 감지

## Situation
회장이 올린 PDF 소식지가 codex 교차검증을 못 돌리고 `cross_model_unavailable`로 빠졌다. 원인은 codex가 아니라 **codex에게 줄 원본 이미지가 없어서**였다.

## Complication
`_render_source_page_images(file_bytes, filename)` (server/main.py:5305)가 **filename이 `.pdf`로 끝날 때만** 렌더했다(`if ext != ".pdf": return []`). 업로드 경로에서 filename이 확장자 없이(예: 소식지 제목 "KB금융그룹") 넘어오면 PDF인데도 0장 렌더 → source_images 빈 리스트 → codex 교차검증 스킵. fitz 렌더 엔진 자체는 정상(ANU 실증).

## Question
filename 확장자에 의존하지 않고, **파일 내용(매직바이트)**으로 형식을 판정해 확장자 없는 filename에서도 PDF/이미지를 렌더할 수 있는가?

## Answer (surgical fix)
`_render_source_page_images` 함수 본문을 매직바이트 기반 형식감지로 교체(filename 확장자 의존 완전 제거):
- **PDF**: `file_bytes[:5] == b"%PDF-"` → 기존 fitz 렌더 로직 그대로(dpi=150, max_pages, 부분 실패 허용).
- **이미지**: PNG(`\x89PNG\r\n\x1a\n`)·JPEG(`\xff\xd8\xff`)·GIF(`GIF8`)·WEBP(`RIFF....WEBP`) → 원본 바이트를 그대로 임시파일 1건으로 저장해 경로 반환(codex가 -i로 직접 판독).
- **그 외/PPTX/zip/손상**: graceful `[]`.
- `not file_bytes` 방어 추가. filename은 로그 용도로만 사용.
- 기존 계약 유지: 예외 삼키고 `[]`, 부분 실패 허용, 임시파일 정리는 호출측 책임(경로만 반환).

## 수정 파일
- `server/main.py` — `_render_source_page_images` 함수 (76 ins / 31 del, 1 hunk)
- `server/tests/test_render_source_page_images.py` — 신규, 9개 테스트

수정 범위는 task allowed_resources(server/main.py 이 함수 + server/tests/**)로 한정. cross_verify·review·validation·migrations·src·extension 불변.

## 테스트 결과
- 신규 테스트: **9 passed** (0.15s) — 확장자 없는 filename+PDF→렌더 성공, 정상 PDF, PNG, JPEG, PPTX/zip 시그니처→[], 손상/랜덤/빈/None 바이트→graceful.
- 전체 회귀: **1229 passed, 1 failed** (131.79s).
  - 유일 실패: `test_consultation_history_get.py::test_cors_fail_closed_when_ext_origin_unset`
  - **회귀 아님(독립검증 완료)**: 내 변경을 `git stash`한 base 상태에서 동일 테스트 실행 → **동일하게 실패**. 이 CORS 테스트는 `_render_source_page_images`와 접점 없음. 로컬 `.env`의 EXT_ORIGIN 오염 pre-existing 이슈(메모리 기록 "CORS 1건=.env오염" 패턴 일치). 신규 회귀 0.
- Pyright diagnostics(sb_helpers/composite_calculator/main import unresolved): 기존 import 전부 동일하게 발생하는 module-path 환경 문제로 신규 회귀 아님.

## L1 스모크테스트 결과 (필수 기록)
- **서버 재시작**: 해당없음 (함수 단위 fix — 서버 기동 없이 함수 직접 호출로 실동작 검증)
- **API 응답 확인**: 해당없음 (내부 헬퍼 함수)
- **함수 직접 호출 실동작 검증** (실제 fitz 생성 바이트):
  - `_render_source_page_images(pdf_bytes, "KB금융그룹")` (★ 확장자 없는 filename = 라이브 버그 재현) → **2 paths 반환, 실제 디스크에 PNG 생성**: `/tmp/cross_src_0_*.png`(12526B), `/tmp/cross_src_1_*.png`(12768B). 수정 전이면 0장.
  - PNG 바이트 + 확장자 없는 filename → 1 path 반환, 저장 파일 내용이 원본 바이트와 **완전 일치**(bytes-match=True).
  - zip/PPTX 시그니처(`PK\x03\x04`) → 0건 graceful.
  - **L1 SMOKE: PASS**
- 스크린샷: 해당없음 (백엔드 함수)

## 머지 판단
- **머지 필요**: Yes (ANU가 수행 — merge_policy: none, 순서 B)
- **브랜치**: task/task-2908-dev1
- **워크트리 경로**: /home/jay/projects/InsuRo/.worktrees/task-2908-dev1
- **base**: 8c26989 (origin/main)
- **HEAD**: a6cbb53 — push 완료, `git ls-remote`로 원격==로컬 검증(PUSH_VERIFIED)
- **머지 의견**: additive surgical(함수 1개 본문 교체 + 신규 테스트 파일). 신규 9 passed, 회귀 0(CORS 1건 base 재현으로 pre-existing 확정). L1 실동작 PASS. 충돌 가능성 낮음(단일 함수).

## 모델 사용 기록
- 불칸(백엔드): sonnet — 구현+테스트 작성. (haiku 미사용)
- 팀장(헤르메스, opus): 설계/검토/L1검증/CORS 독립검증/통합.

## 발견 이슈 및 해결
- CORS 테스트 1건 실패 → base(stash) 재현으로 pre-existing 확정, 범위 밖(.env 오염). 본 작업과 무관하여 미수정 유지.

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

