스캔 전체(Web+PKM)가 단일 세션·단일 commit 이라 한 파일 예외(rglob↔stat 사이 삭제로
FileNotFoundError, flush 오류 등)가 watch_inbox 전체를 raise·롤백 → 그 사이클 등록분을
모두 잃거나, 결정적 poison 파일이 매 사이클 같은 지점에서 중단시켜 그 뒤 파일 영구 미등록.
파일별 독립 세션+commit + try/continue 격리 (news_collector/csb_collector 동형).
file_hash 는 세션 밖에서 계산(커넥션 미점유), 무변경 파일은 쓰기/commit 없음.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
합성 번들 e2e PASS(자식 3개 합성 file_path·range, uq 위반 0 + 자식 extract range-clamp 1110자
range_ok) 후 인제스트 presegment 재활성(documents.py upload + file_watcher 3곳). 非PDF/단일=통과.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
audit 의 dup-url-normalizer-divergent 는 design intent 오탐: news._normalize_url 은 query-식별
사이트(hada.io?id=·HN item?id=) 별개 기사 붕괴 방지 위해 보수적(query 보존·sort/trailing-slash/
소문자화 안 함), file_watcher._canonicalize_url 은 web_clip dedup 위해 공격적 정규화 — 채널별
의도된 차이. 통합하면 news dedup 가 깨진다(docstring 경고). 두 함수 docstring 에 상호 cross-ref +
'통합 금지' 명시해 미래 잘못된 통합 차단. 동작 변경 0(주석만).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR① licensed_restricted 단일 술어(_license_sql) — retrieval 3-leg(text/vec-doc/
vec-chunk) + digest loader 공유. a안(U-2①): 색인 허용·구매자료 verbatim 을 RAG 증거/
digest 발행에서 구조적 제외. 술어=COALESCE(extract_meta->'license'->>'restricted',
'false')<>'true' (restricted 부재/false 미제외 → 기존 코퍼스 결과 불변). 개인 파일
열람 미차단. chunk leg 는 outer 의 documents JOIN(항상) 활용 post-rank(restricted 소수).
PR② file_watcher _TARGET_AXIS 확장 — Books/Papers_Purchased=restricted / Manuals=
non-restricted(사용자 결정) / KGS=law·KR·kogl. ingest 시 extract_meta.license
deterministic 주입(classify material IS NULL 일 때만 제안·meta 미기록=보존).
PR③(KGS 버전 flip)=별 슬라이스 deferred(파일 포맷 조사 선행).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
watch_inbox() 가 new_count/changed_count 둘 다 0 일 때 silent — PR-NAS-Watch-Folder 검증 시 fire 추적 부재 확인 후 보완. else 분기 추가해 매 5min fire 마다 "변경 없음 (idle)" info 로그 한 줄.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
D9 Track B revised (2026-05-08):
1) STT owner GPU 정식 복귀:
- docker-compose.yml: stt-service profiles:[legacy] 제거 → 상시 활성
- fastapi STT_ENDPOINT = http://stt-service:3300 (compose 내부 DNS)
- 정책: Mac mini = Gemma 26B 전용 우선이므로 STT/Whisper 는 호출량 무관
GPU 서버 소유. 이전 "Mac mini 이전본" 주석은 trace 오인 기반.
2) KGS Code 등 외부 학습 자료 추가 스캔 경로:
- ADDITIONAL_WATCH_TARGETS env (쉼표 구분, PKM 상대경로)
- app/core/config.py: additional_watch_targets list 설정 추가
- app/workers/file_watcher.py: 추가 watch path 처리
- app/workers/classify_worker.py: KGS Code 분류 분기 (가스기사 학습 자료)
- 모두 expected_category=library 처리 (md/pdf/docx 만)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
file_watcher.watch_inbox()에서 select(Document)를 사용하지만
sqlalchemy import가 빠져있어 NameError 발생.
이로 인해 큐 컨슈머가 max_instances 도달로 실행 스킵되어
embed(45건) + chunk(8건)이 pending 상태로 정체됨.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존 UNIQUE(document_id, stage, status)는 pending+processing 동시 존재를
허용해서 stale 복구 시 충돌 발생. 2-layer 방어로 근본 차단:
1) DB: partial unique index uq_queue_active — 활성 행(pending/processing)은
(document_id, stage)당 최대 1개만 허용
2) App: enqueue_stage() 중앙 함수 — INSERT ON CONFLICT DO NOTHING으로
모든 9개 경로의 check-then-insert TOCTOU race 제거
migration 117은 guard check 포함 — 활성 중복이 남아있으면 RAISE EXCEPTION
으로 중단, 수동 정리 유도.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- original_path/format/hash + conversion_status 필드 추가 (migration 007)
- extract_worker: 텍스트 추출 후 xlsx→ods, docx→odt 등 ODF 변환
- 변환본은 .derived/{doc_id}.ods 에 저장
- 원본 메타 보존 (original_path/format/hash)
- file_watcher: .derived/ .preview/ 디렉토리 제외
- DocumentViewer: ODF 포맷이면 편집 버튼 자동 표시
- edit_url 있으면 "편집", 없으면 "Synology Drive에서 열기"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>