# hyungi_Document_Server — Claude Code 작업 가이드

## Infrastructure Reference 📌

운영 사실 (모델명 / 엔드포인트 / IP / 컨테이너 / 포트 / drift) 의 단일 진실 소스(SSOT):

**`~/.claude/projects/-Users-hyungiahn/memory/infra_inventory.md`**

이 파일과 inventory 가 충돌하면 **inventory 가 정답**. 본 CLAUDE.md 는 코딩 규칙·워크플로우·코드 구조에 집중하고 운영 값은 박지 않는다.

운영 변경 정책 (inventory → config → deploy → verify):
1. `infra_inventory.md` 먼저 갱신
2. `config.yaml` / `credentials.env` 갱신
3. deploy (commit → push → GPU pull → `docker compose up -d --build`)
4. verify (smoke endpoint, postgres count, 모니터링)

순서 어기면 drift. 발견 시 inventory `Drift Log` 등록.

**Search experiment soft lock**: Phase 2 search refactor / QueryAnalyzer / run_eval 진행 중일 때 GPU 서버의 `docker compose restart`, `config.yaml` 수정, Ollama pull 금지. flag = `~/.claude/.search-experiment-active`.

---

## 프로젝트 개요

Self-hosted PKM(Personal Knowledge Management) + 다국 뉴스 비교 분석 웹 애플리케이션.
GPU 서버가 메인 (Docker Compose / DB / 검색 / OCR / 마커), Mac mini = MLX 추론 + Whisper STT, Synology NAS = 파일 원본.

## 핵심 문서

1. `README.md` — 외부 소개 (기술 스택 / 주요 기능 / Quick Start)
2. `docs/architecture.md` — 전체 시스템 아키텍처
3. `docs/deploy.md` — Docker Compose 배포 가이드
4. `docs/development-stages.md` — Phase roadmap (역사적 맥락)

## 기술 스택

| 영역 | 기술 |
|------|------|
| 백엔드 | FastAPI (Python 3.11+), SQLAlchemy 2.0 async, APScheduler |
| DB | PostgreSQL 16 + pgvector + pg_trgm (단일 `pkm` DB) |
| 프론트엔드 | SvelteKit 5 (runes mode) + Tailwind CSS 4 |
| 문서 파싱 | kordoc (HWP/HWPX/PDF → MD), LibreOffice headless (오피스), marker (PDF → markdown) |
| OCR | Surya OCR (docker compose `ocr-service`, GPU) |
| STT | MLX Whisper (Mac mini), GPU faster-whisper 는 legacy profile |
| 리버스 프록시 | Caddy (HTTP only, 앞단 home-caddy 가 HTTPS 종료) |
| 인증 | JWT (access) + HttpOnly cookie (refresh) + TOTP 2FA |
| 컨테이너 | Docker Compose |

## 머신 역할 (자세한 IP / 포트 → inventory)

| 머신 | 역할 |
|------|------|
| GPU 서버 | Docker Compose 메인: fastapi · frontend · postgres `pkm` · kordoc · ocr-service · marker-service · reranker (TEI) · caddy. Ollama (embedding / 4B 추론). home-gateway 별 compose (ingress + 나노클로 + searxng) |
| Mac mini | MLX 26B 추론 endpoint + MLX Whisper STT. ingress 역할 0 |
| Synology NAS | 파일 원본 (`/volume4/Document_Server/PKM/` → GPU `/mnt/nas/Document_Server` NFS), Synology Office/Drive/Calendar/MailPlus |
| VPS-2 (OVH) | 메일 relay (`relay.hyungi.net:587`), Gitea bare mirror, Secondary MX |

## AI 파이프라인 (역할 기준 — 실제 모델 매핑은 inventory)

| 역할 | 위치 |
|------|------|
| 분류/심층 요약 primary | Mac mini MLX 26B |
| Triage (1차 분류) / Fallback / Chat | GPU Ollama 4B |
| Embedding | GPU Ollama (1024d, 다국어) |
| Reranker | GPU TEI 컨테이너 |
| OCR | docker compose `ocr-service` (Surya OCR GPU) — `ai.models.vision` 미사용 |
| STT | Mac mini MLX Whisper large-v3 |
| Premium (수동 trigger) | Anthropic API (`require_explicit_trigger`, 일일 한도) |

호출 시 반드시 `app/ai/client.py` 의 `AIClient` 사용 (`call_triage` / `call_primary` / `call_fallback`). 직접 HTTP 호출 금지.

## 문서 처리 파이프라인

```
파일 업로드 (드래그 앤 드롭 or file_watcher)
    ↓
extract (텍스트 추출)
  - kordoc:       HWP, HWPX, PDF → Markdown
  - LibreOffice:  xlsx, docx, pptx 등 → txt/csv
  - 직접 읽기:    md, txt, csv, json, xml, html
    ↓                                    ↓
classify_worker (tier triage)        preview / marker
  - 4B Ollama → TriageOutput              - LibreOffice → PDF 변환
  - escalate_to_26b 시 deep_summary       - marker → PDF → markdown
  - ai_tldr / ai_bullets / inconsistencies
    ↓
embed_worker (bge-m3 1024d, doc-level)
chunk_worker (문서 유형별 chunking)
```

핵심 원칙:
- 파일은 업로드 위치에 그대로 유지 (물리적 이동 없음)
- 분류 (`ai_domain` / `ai_sub_group` / `ai_tags` / `category` / `tier`) 는 DB 메타데이터로만 관리
- preview / marker 는 classify 와 병렬

## 워커 / 스케줄러 (`app/main.py` 의 scheduler.add_job)

- queue_consumer (interval 1m), file_watcher (5m), upload_cleanup (10m)
- study_q_embed (1m), study_q_related_refresh (1m), study_queue (1m), study_session_queue (1m)
- tier_backfill (30m)
- law_monitor (07:00 KST), mailplus_archive (07/18:00 KST)
- daily_digest (20:00 KST)
- **global_digest** (04:00 KST) — Phase 4 country×topic 7일 rolling
- **morning_briefing** (05:10 KST) — 야간 KST 0~5h 수집 뉴스 topic×country 비교

scheduler timezone = `Asia/Seoul`.

## 데이터 계층

1. **원본 파일** — NAS `/volume4/Document_Server/PKM/`. 유일한 원본, 위치 변경 없음
2. **가공 데이터** — PostgreSQL `pkm` (텍스트, AI 분류, 검색 인덱스, 메모, 태그, briefing, digest, …)
3. **파생물** — pgvector embedding, PDF preview 캐시 (`.preview/`), marker 결과 (markdown + extracted_images NAS 저장)

## 코딩 규칙

- Python 3.11+, asyncio, type hints
- SQLAlchemy 2.0+ async 세션
- Svelte 5 runes mode (`$state`, `$derived`, `$effect` — `$:` 금지)
- 인증 정보는 `credentials.env` 에서 로딩 (하드코딩 금지)
- 로그는 `logs/` (Docker 볼륨)
- AI 호출은 반드시 `app/ai/client.py` 의 `AIClient` 경유
- 한글 주석 사용
- Migration: `migrations/NNN_*.sql`, `init_db()` 자동 실행 (`schema_migrations` 추적)
  - SQL 에 `BEGIN/COMMIT` 금지 (외부 트랜잭션 깨짐)
  - asyncpg `prepared statement` 가 multi-statement 불허 → 1 statement 1 파일 분리
  - 기존 DB 에서는 `schema_migrations` 수동 이력 등록 필요할 수 있음
- 디자인 시스템 토큰 only (`bg-surface`, `text-dim`, `border-default`, `text-accent`, …). `bg-[var(--*)]` 금지 (`lint:tokens` 차단)
- 커밋 메시지: `type(scope): summary` (`feat` / `fix` / `refactor` / `ops` / `incident` / `docs`)

## 개발 / 배포 워크플로우

```bash
# 개발 (MacBook Pro)
cd ~/Documents/code/hyungi_Document_Server/
# 코드 작성 → git commit → push (Gitea)

# 배포 (GPU 서버)
ssh gpu
cd ~/Documents/code/hyungi_Document_Server/
git pull
docker compose up -d --build fastapi frontend
```

PR 머지는 Gitea UI **Rebase and merge** 기본 (선형 히스토리 + force-push 충돌 회피). 단독 작업 확증 시만 로컬 rebase+FF.

## v1 코드 참조

v1 (DEVONthink 기반) 코드는 `v1-final` 태그로 보존:
```bash
git show v1-final:scripts/law_monitor.py
git show v1-final:scripts/pkm_utils.py
```

## 주의사항

- `credentials.env` 는 git 에 올리지 않음 (`.gitignore`)
- NAS NFS 마운트: Docker 컨테이너 내 `/documents`. FastAPI 시작 시 `/documents/PKM` 존재 확인
- 법령 API (LAW_OC) 는 승인 대기 중
- Ollama 는 127.0.0.1 바인딩 (외부 접근 차단)
- Caddy 는 `auto_https off` + `http://` only (HTTPS 종료는 앞단 home-caddy 가 처리)
- Synology Office 편집은 새 탭 열기 방식 (iframe 미사용, `edit_url` 수동 등록)
- 한국어 NFS 경로는 NFC↔NFD 비대칭 — 경로 수신 시 NFC→NFD→parent glob fallback 필수
