GPU 회선에서 moel.go.kr 첫 TLS 연결이 간헐 드랍(curl rc=35, 직후 재시도 5/5 성공,
맥북 무발생·단일 A 레코드) → 사이클당 1회 fetch 인 피드가 ConnectError('') 누적,
입법행정예고 circuit open. ConnectError/ConnectTimeout 만 1.5s 후 1회 재시도,
HTTP 상태 오류 비대상. 회귀 테스트 3건 (42 passed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
httpx 의 Response.is_redirect 는 3xx 전체(304 Not Modified 포함)에 True 라,
조건부 GET 으로 304 를 받으면 location 없는 같은 URL 을 3회 재요청 후
'redirect 3회 초과'로 오류 처리 → ETag/Last-Modified 받는 안정 피드(SEP/HSE/OSHA
/철학 RSS 등)가 2번째 사이클부터 전멸하던 systematic 버그.
- 304 처리를 redirect 루프보다 앞으로 이동.
- redirect 판별을 has_redirect_location(=location 헤더 있는 진짜 redirect)으로 교체.
news_collector._fetch_rss + crawl_politeness.fetch_page 동일 함정 양쪽 수정.
- 사이클 1 파일럿(경향)은 304 를 받은 적 없어 잠복했고, 안정 피드 첫 304 에서 발현.
- 회귀 테스트 3건(304 비-redirect / 진짜 redirect / 코드 패턴 audit).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- fulltext_worker.reconcile_unresolved: EXISTS 서브쿼리 aliased(ProcessingQueue) —
auto-correlation 이 FROM 전부 제거해 매 실행 InvalidRequestError (안전망 dead code).
SQLAlchemy 2.0.50 컴파일 재현·수정 확인.
- news_collector._fetch_rss: ETag/Last-Modified/content-hash 영속을 bozo 파싱 검증
뒤로 이동 — 부패 응답 워터마크 저장 시 영구 304-skip 차단.
- news_collector.run: 모듈 락으로 수동 collect vs 6h 스케줄 동시 실행 차단 —
_get_or_create_health 동시 INSERT 의 uq_source_health_source_id 위반이
사이클 전체를 죽이는 경합 봉쇄.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BBC Technology 매 사이클 MultipleResultsFound (06-04~) 해소.
- 저장 edit_url=raw vs 조회 normalized 비대칭으로 URL dedup 무력화돼
교차게시(HN x BBC) 시 2행 동시매칭 -> scalar_one_or_none raise.
- _normalize_url: query 전체 제거 -> tracking 파라미터만 제거로 교정
(hada.io/topic?id= 등 query-식별 사이트 870건 붕괴 방지, 리뷰 게이트).
- 조회 .first() + edit_url IN (normalized, raw) 레거시 행 내성. RSS/NYT 양쪽.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
document_chunks.country 가 7일 분포 기준 99.9% NULL 이었던 root cause = news_collector 가
summarize + embed 만 enqueue 하고 chunk 를 enqueue 하지 않아 chunk_worker 가 news 문서에 한 번도 안 돌고 있었음.
queue_consumer.next_stages 의 summarize 키 부재가 follow-up 미연결 원인.
news 외 summarize 흐름 부수영향 회피를 위해 next_stages 가 아니라 news_collector RSS/API 양쪽에 chunk
enqueue 1줄씩 명시 추가. days_old <= 30 가드 안에서 embed 와 동일 정책.
scripts/news_chunk_country_backfill.py — doc 단위 small batch, 실패 doc skip,
50건마다 progress. queue 우회 직접 chunk_worker.process 호출로 timing 통제.
Gate (PR closure):
A) chunked_doc_pct > 95% 최근 7일 news doc 중 chunk 보유 비율
B) country null_pct < 5% 최근 7일 news chunk country NULL 비율
plan: ~/.claude/plans/7-whimsical-crab.md (PR-News-Prep-Layer-1)
Co-Authored-By: Claude Opus 4.7 (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>
SCMP(www.scmp.com)처럼 HTTPS 원본이 HTTP로 301 redirect하는 소스에서
redirect target이 차단되던 문제 수정. allow_http를 원본 스킴이 아닌
소스 도메인의 allowlist 등록 여부로 판단하도록 변경.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- summarize_worker: 요약만 생성 (분류 안 함)
- queue_consumer: summarize stage 추가 (batch 3)
- news_collector: summarize + embed 큐 등록
- process_stage enum에 'summarize' 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- /news 전용 페이지: 신문사 필터, 읽지않음 필터, 시간순 리스트, 미리보기
- 뉴스 분류 격리: ai_domain='News', classify 제거, embed만 등록
- is_read: 클릭 시 자동 읽음, 전체 읽음 API
- documents 목록에서 뉴스 제외 (source_channel != 'news')
- nav에 뉴스 링크 추가
- GET /api/news/articles, POST /api/news/mark-all-read
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>