# [Lv.2] 소식지 추출 — 표 영역 좌표기반 격자 복원(평탄화 방지) + 표페이지 육안확인 플래그

## allowed_resources
```yaml
allowed_resources:
  paths:
    - "server/pdf_extract.py"
    - "server/tests/**"
  forbidden_paths:
    - ".github/**"
    - "extension/**"
    - "src/**"
    - "server/migrations/**"
    - "server/main.py"
    - "server/newsletter_review.py"
    - "server/newsletter_cross_verify.py"
    - "server/newsletter_validation.py"
  commands: ["pytest","python3","pip","git"]
  merge_policy: "none"
  ttl_hours: 16
```
> ★ base=현 origin/main(pdf_extract.py 존재). 근거: 회장 실사용 — DB생명 매거진 p16(상품×고지사항 매트릭스 표)이 평문으로 뭉개짐. ANU 스파이크로 좌표기반 격자 복원 가능 확인.

## 배경 (ANU 실측 확정)
`pdf_extract.extract_markdown` 이 표 영역을 마크다운 평문으로 평탄화 → 매트릭스 표(셀값 N/3/5/개월)의 행·열 구조가 사라져 의미불명. pdf-inspector 는 표 페이지를 인식은 함(`process_pdf_bytes().pages_with_tables`). pdfplumber(선 기반)는 테두리 없는 표라 실패. **좌표(extract_text_with_positions)로 Y→행, X→열 클러스터링하면 헤더+데이터행 복원됨**(실증: p16에서 `상품|구분|고지사항` 헤더 + `3|3|개월|N|N|5|5` 데이터행 복원).

## 작업 (pdf_extract.py 만)
1. **표 영역 판정**: pdf-inspector `pages_with_tables`(process_pdf_bytes) 또는 영역 내 텍스트 아이템의 격자성(같은 Y밴드에 X가 여러 개 정렬)을 신호로, 페이지/영역이 표인지 판정.
2. **좌표기반 격자 복원**(표 영역):
   - **행**: TextItem 을 Y 근접(예: 폰트높이 기반 톨러런스)으로 묶어 행 생성(위→아래).
   - **열**: 전체 행의 X 시작좌표 분포에서 **열 경계 클러스터링**(정렬된 X 그룹 = 열). 각 셀 = (행,열) 위치의 텍스트. 빈 셀 허용, 멀티라인 셀은 Y인접 병합.
   - **마크다운 표로 출력**: `| h1 | h2 | ... |` + 구분선 + 행들. 헤더는 첫 행(또는 굵은/큰 폰트 행) 추정.
3. **저신뢰 폴백**: 열 정렬이 불규칙해 격자 신뢰도 낮으면(열 수 편차 큼 등) → 격자 강제 대신 **행별 텍스트를 줄바꿈 보존**으로 출력(현재의 한 줄 뭉갬보다 나음) + 그 페이지에 **`[[TABLE_REVIEW page=N]]` 마커**(호출측/검토에서 "표-육안확인"으로 노출 가능하게 pages 결과에 flag).
4. 기존 텍스트(비표) 영역 처리·좌표정규화·XY-cut·graceful 는 불변. 표 영역만 격자 경로 추가.

## 제약
- pdf_extract.py + 테스트만. main.py·review·validation 불변(pages 반환 구조에 표 flag 추가는 가능하되 하위호환 유지). 기존 텍스트페이지 추출 회귀 0.

## 검증
- `pytest server/tests/` 회귀 0. 신규: 격자 픽스처(헤더+행 복원→마크다운 표) / 저신뢰 시 줄보존+TABLE_REVIEW 마커 / 비표 페이지 불변.
- ★ **L1 실측 필수**: 실제 DB생명 매거진 p16 을 `extract_markdown` → **`상품|구분|고지사항` 헤더가 있는 마크다운 표**로(또는 최소 줄보존+육안플래그) 나오는지, 현재의 한 줄 뭉갬이 사라졌는지 확인. 결과를 보고서에 첨부.

## 완료 (★ 순서 B)
- pdf_extract.py + 테스트만. **dev6 금지** — dev1(헤르메스, pdf_extract 원저자). 커밋 → push → **★ push 확인** → finish-task foreground 1회 → .done. ANU 독립검증(실 p16 육안)·머지·배포는 ANU.

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