Commit Graph

133 Commits

Author SHA1 Message Date
Claude Code 1fbb341e28 feat(papers): B-3 PR3 — OpenAlex 백본 수집기 (scaffold-first, signal-only, per-run cap)
plan safety-library-b3-1 PR3. 발견+dedup 글로벌 백본(JP/EU/US 색인+정본 DOI, 전문 안 줌).
- scaffold-first: OPENALEX_API_KEY 부재 시 FeedError explicit-skip(silent fallback 0). 키=무료.
- signal-only: inverted-index 초록 복원→색인(embed+chunk), summarize 0. PDF 절대 미fetch(oa_url=신호).
- 관련성 사전필터=title_and_abstract.search 키워드 + per-run cap 60(임베드 firehose 차단, 적대리뷰 A major)
  + cursor 페이징 + from_publication_date 워터마크 증분. 초록 없는 thin 레코드 skip(재료 품질).
- license: 명시 CC→redistribute true / OA·closed→false(restricted 부재=초록 RAG 사용가능, 비-CC 전문은 L-1 Phase-2).
- DOI→paper.doi(holder, 교차소스 dedup) / 없으면 openalex_id. enabled=False 행+add_job(daily 07:45 KST)+CLI.

순수 파서/초록복원/license_meta fixture 단위 7 passed(OpenAlex 실응답: cc-by/cc-by-nc-nd/None·초록 유무).
라이브 검증 PASS (prod, running fastapi 무접촉): 키없음→explicit-skip / 키주입→3건 적재
(paper/NULL/ai_summary NULL/region INT, cc-by→redist true·unspecified→false, green/gold,
큐 embed3+chunk3·summarize 0, distinct openalex_id=total, 교차소스 DOI 4 distinct 4 중복 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:30:36 +00:00
Claude Code 6167e03625 fix(papers): B-3 PR2 arxiv _record_success 4-arg 시그니처 (라이브 검증서 발견)
run() 종료 시 _record_success(health, now) → 누락 인자(items·not_modified) 추가
= _record_success(health, inserted, False, now) (news_collector 시그니처 일치).
일회성 compose run 라이브 검증서 TypeError 로 발견 — 배포 전 차단.

라이브 검증 PASS (prod 6건 적재, running fastapi 무접촉): material_type=paper·jurisdiction NULL·
ai_summary NULL·crawl·region=INT·license=arxiv / DOI 보유 1건 paper.doi 인덱스 진입·나머지 arxiv_id /
큐 embed6+chunk6·summarize 0(signal-only) / distinct arxiv_id=총건(dedup 불변식) / health circuit closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:22:51 +00:00
Claude Code ba943d703a feat(papers): B-3 PR2 — arXiv 키워드 필터 수집기 (signal-only, per-run cap)
plan safety-library-b3-1 PR2 (keyless). DOI 코어(PR1) 위 첫 실수집기.
- bespoke arXiv API(Atom) 수집기: cat:{category} AND (abs:키워드) — RSS 통째(firehose) 아님.
  신규 7 카테고리(eess.SY·physics.flu-dyn/comp-ph·math.OC/NA·stat.AP·cs.CE) x 압력용기/공정안전 키워드.
- signal-only: 초록만 색인(embed+chunk), summarize 절대 미enqueue(맥미니 큐 무접촉).
- DOI 보유 -> extract_meta.paper.doi(holder, partial-unique 인덱스). 없으면 arXiv id dedup.
  교차소스 dedup = find_paper_holder(PR1) + arxiv id file_hash. paper.source_region=INT(jurisdiction NULL 유지).
- per-run insert cap(_RUN_CAP=80) — 광역 수집이 GPU embed 큐 범람 방지(적대리뷰 A major), 잔여 로깅.
- etiquette: >=3s + 429 백오프 + 카테고리별 submittedDate 워터마크 증분. https 필수(http=301).
- enabled=False news_sources 행 + main.py CronTrigger(daily 07:30 KST). __main__ CLI(--bulk/--limit).

순수 파서·쿼리빌더 fixture 단위 18 passed(arxiv 실응답 박제: DOI/journal_ref/둘다없음 3경로).
적재(run/_ingest_entry)는 news_collector signal-only 패턴 미러 — 배포 후 라이브 검증.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:10:25 +00:00
hyungi a6db6c999b fix(safety): B-4 리뷰 반영 — 단일 술어 중앙화 + study/briefing 경로 커버
적대 리뷰(10에이전트) 확정 반영:
- license_filter.py 신설 — restricted_exclude_sql(raw)/restricted_exclude_orm(ORM)
  단일 정의. retrieval _license_sql·digest·briefing·study 풀이가 공유(드리프트 방지).
- major: explanation_rag(study 문제 AI 풀이 RAG)에 술어 누락 → doc_meta 쿼리에 ORM
  적용(valid_doc_ids 경유로 청크도 차단). briefing/loader 2쿼리에 누락 → digest 와
  동일 술어 추가(news restricted 부재=방어적·경로 일관성).
- blocker(low-impact): file_watcher changed-doc 경로 material/license 보정(merge 주입·
  license 부재 시만 — extract_meta clobber 회피, pre-B-4 적재분 동기화).
- 테스트: 단일-source 검증 + ORM 구성 스모크 2건 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 14:52:04 +09:00
hyungi ed7740beee feat(safety): B-4 PR①② — licensed_restricted 차단 술어 + watch 폴더 license 주입
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>
2026-06-13 14:34:42 +09:00
hyungi 235aa648ad feat(safety): B-2 KOSHA 사망사고 속보 수집기 (callApiId=1040)
data.go.kr 15119137 활용신청 전파 완료 → news_api02/getNews_api02 라이브.
collect_fatal_accidents: arno dedup(kosha-fatal|{arno}) + material_type=incident/
jurisdiction=KR + license=kogl. contents=HTML → _clean_html, published_date =
arno 접두 8자리(YYYYMMDD 등록일, 2019~ 라이브 전수 동형 검증). 첨부 API·business
필드 없는 별 채널(1040). run() 일일 잡(06:40 KST) 튜플 합류 — 소스별 실패 격리 유지.
순수 헬퍼 _fatal_fields + fixture 테스트(tests/test_kosha_fatal.py).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 13:42:12 +09:00
hyungi bacb36924b feat(safety): B-1 PR② — fetch_version(payload 리스트) + ingest 4축 + 생애주기 잡 통째 + 부트스트랩
plan safety-library-1 B-1 PR② (R8-B1: 승격·supersede·스윕·repeal = 잡 코드 통째 배포):
- kr.fetch_version: 전문 1콜 → primary+annex payload 리스트 (R4-M4)
  ★fixture 가 잡은 결함 2: 별표구분(별표/서식) 차원 누락 시 (번호,가지) 4건 충돌
  → version_key='MST|{구분}{번호}-{가지}' / 삭제 tombstone 3건(별표10·서식1·2) skip
  — KR 별표 삭제 = absence 아닌 명시 tombstone (R7-M3 absence 추론 불요 확정)
- ingest: 전 버전 pending 적재 + 4축(law/KR/COALESCE날짜/public_domain) + backfill 마커
- 생애주기 잡: 버전 시리즈 단위 승격·supersede(R7-B1) + 상태 기반 레거시 스윕(primary
  current 보유 한정) + repeal(레거시 매핑분 포함, R7-M2) — 단일 트랜잭션·KST
- 법령명 매핑: 정규화 동등 비교(prefix 금지 — 시행령 오폭 차단), 가운뎃점·공백 흡수
- 워터마크 = 파싱 검증 통과 후에만 / 스케줄 daily 07:00 KST (law_monitor 슬롯 승계)
- 테스트 14/14 (매핑 표본·시리즈 키·payload fixture)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 09:37:51 +09:00
hyungi a28f12b12e feat(safety): B-1 PR① — law_monitor 스케줄 제거 + statute KR poll_changes + fixture 박제 (mig 356)
plan safety-library-1 B-1 PR① (fixture-first):
- law.go.kr 라이브 fixture 5종 박제 (OC 새니타이즈 검증 — 응답 법령상세링크에 키 포함 함정)
- R7-M3 판정: 전문 1콜 XML = 조문 853+별표 23 전체 스냅샷(부분 실패 개념 없음)
  + 별표번호/가지번호 = 구조화 필드 — 조문 취득 = 전문 1콜+로컬 파싱 확정(R2-m1)
- legal_acts KR 시드 26행(법령ID 라이브 실측, watch=26 전부, FK 계열 9그룹)
  ★ '유해ㆍ위험작업...' 정식명 = 가운뎃점 — law_monitor 하드코딩(점 없음)은 영구 미매칭 잠복
- statute_adapters/kr.py: poll_changes(lawSearch MST diff) — 순수 파서 분리, fixture 테스트 8/8
- statute_collector.py: 관찰 전용 코어(워터마크 영속 0 — ingest=PR②), 스케줄 미등록(R8-B1)
- main.py: law_monitor 스케줄 제거 — 버전 체인 밖 레거시 매일 증식의 유일 경로 차단

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 09:01:21 +09:00
hyungi 3feddd012b feat(safety): A-2 수집기 ingest 시점 분류 축 부여 — 레지스트리 전파 + 승인 가드 (mig 352~355)
plan safety-library-1 A-2 (classify-skip 경로 전수 커버):
- news_sources 에 material_type/license_scheme/license_redistribute + 안전·공학 12행 시드
- news_collector: 레지스트리 → documents 전파 (_material_axis — paper 는 jurisdiction NULL 강제)
- kosha(사례·첨부=incident, GUIDE=guide)/csb(incident·US)/api_std(standard·US)/law_monitor(law·KR)
  /file_watcher(KGS=law·KR 타깃 매핑) deterministic 부여 + extract_meta.license 주입
- published_date: 소스별 가용 날짜 (GUIDE 공표일·CSB lastmod·API 공지일·법령 공포일·뉴스 발행일)
- classify_worker: document_type→material_type 결정적 매핑 제안 (자동 전이 금지)
- accept-suggestion: material 제안 적용 + law=jurisdiction 필수(기본값 없음) + 청크 미러 1문 동기화
- chunk_worker: 비뉴스 문서 country=jurisdiction 미러 (R3-m3: 검색측 country 소비자 0 실측)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 06:23:22 +09:00
hyungi c3d237766d feat(search): Phase 2A E-1 — Qwen 후보 3종 백필 CLI + eval 디스패처 확장 (마이그 328~333)
- 후보 섀도 테이블 6종(전부 vector 타입 — eval=exact scan 이라 인덱스 불요, halfvec 은 C-1 소관)
- workers/phase2a_cand_backfill: resumable(NOT EXISTS)·배치 커밋·동결셋 한정(--doc/chunk-id-max),
  문서/청크 입력 = production 경로 동일 구성 + plain
- CANDIDATE_BACKEND_MAP += cand_qwen06/qwen4/qwen4m (embed_kind=ollama, 쿼리측 instruct prefix
  G-1 핀 문자열, qwen4m = dimensions 1024 MRL)
- qwen4m 적재는 qwen4 에서 SQL 파생(subvector+l2_normalize) — 본 CLI 비대상

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:29:53 +09:00
hyungi 5dca5b5d28 ops(pipeline): embed/chunk 고속 컨슈머 분리 + 배치 1→10 — LLM 사이클 인질 해소
진단(2026-06-12 용량 평가): 단일 루프에서 classify(~190s×3)가 사이클을 점유,
건당 <1s 인 embed/chunk 가 사이클당 1건 캡 → 실효 ~580/일 vs 수요 최대 2,700/일,
적체 3,570 + 신규 문서 벡터 미적재(RAG 검색 누락). 4070 가동률 0% = 순수 구조 캡.
수리 = markdown 분리(05-01) 선례: consume_fast_queue 1분 잡 + 배치 10(GPU 공유 보수값,
캡 ~14,400/일). 세 컨슈머 stage 집합 disjoint(stale reset 이중 복구 방지). retrieval
로직·임베딩 모델 무접촉.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 07:50:07 +09:00
hyungi d667545185 fix(classify): 적대 리뷰 반영 — use_deep 스레딩(B1)·StageDeferred 전파(B2)·legacy 호출 deep 경유(M3)
- _run_tier_triage(use_deep) 스레딩 — 미배선 NameError(전 classify 파괴) fix
- process 의 triage try 에 except StageDeferred: raise 선행 (drain 보류 시멘틱 복구)
- legacy classify()/summarize() 에 cfg 파라미터 — use_deep 시 deep 슬롯 경유 +
  is_deferrable_error → StageDeferred 변환(첫 호출 = 최저비용 지점에서 보류, doc 쓰기 0)
- ai_model_version = 실제 처리 경로 모델 (drain=qwen-macbook 귀속)
- analyze_event model_name 스레딩 + deep triage cfg 에 top_p 동승

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 07:12:40 +09:00
hyungi 235bbf9881 ops(pipeline): fair-share 번들 — drain classify 합류 + deep 맥미니 폴백 + mlx 게이트 동시 2
사용자 '공평하게 동일한 작업' 지적의 비대칭 잔재 2건 + 예고된 배칭 레버:
- queue_drain --stage classify (use_deep: deep 슬롯 endpoint + triage sampling,
  완료 시 enqueue_next_stage 로 embed/chunk/markdown 연쇄 — DAG 단절 방지)
- deep_summary consumer = 맥북 우선, 불가 시 맥미니 primary 즉시 처리(동일 모델 —
  강등 아님). drain 은 defer_on_deep_unavailable=True 로 기존 보류-종료 유지
- llm_gate capacity 일반화 (config pipeline.mlx_gate_concurrency, 기본 1, 운영 2) —
  'MLX_CONCURRENCY=1 고정' 영구 룰의 전제(single-inference 서버) 소멸을 docstring 에 개정 박제
- analyze_events FK(users) CLI 컨텍스트 INSERT 실패 fix (models.user 명시 import)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 06:56:02 +09:00
hyungi cd0040925a ops(pipeline): 생성 LLM 홀드 게이트 held_stages — 맥미니 모델 확정까지 보류
맥북 LLM 백지화 + 맥미니 모델 재결정에 따라 DS 의 생성 LLM 소비를 일괄 보류.
held = classify/summarize/deep_summary(큐, claim 미발생·attempts 미소모) +
digest(04:00)/briefing(05:10) cron + study explanation/session_analysis/memo_card 컨슈머.
GPU 특화 스테이지·수집기·인터랙티브(ask/eid chat)는 무영향. 기본값 [] = 무동작.
/api/digest/regenerate 는 홀드 중 409 명시. 해제 = config held_stages 비우고 fastapi 재기동.
exec plan: ~/.claude/plans/ds-llm-hold-exec-20260611.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 16:52:46 +09:00
hyungi 01db4816fd feat(workers): drain 연속보류 내성 — 네트워크 플랩 흡수 (--defer-retries/--defer-wait)
실측 origin: Tailscale direct 경로 ~10분 플랩(13:25~13:34)으로 300건 run 이 32건에서
조기 종료. 보류 시멘틱 자체는 정상(무손상) — run 지속성만 보강. 연속 보류 5회까지
120s 간격 재시도, 한도 도달 = sleep 판정 종료. 성공 시 카운터 리셋.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:42:10 +09:00
hyungi 88e5893041 feat(workers): 맥북 M5 Max 분담 배선 — deep 슬롯 + 보류 시멘틱 + queue_drain CLI
plan ds-macbook-offload-1 P2 (Soft Lock 예외 박제 ds-macbook-offload-exec-20260611.md):
- config ai.models.deep optional 슬롯 (라우터 :8890 경유 qwen-macbook, 부재 시 기존 경로)
- AIClient.call_deep + is_deferrable_error + call_deep_or_defer (자동 cloud/맥미니 폴백 0)
- deep_summary_worker: deep 슬롯 시 맥북 경유 (맥미니 mlx gate 미점유) + 실모델 기록
- StageDeferred 보류 시멘틱: 503/connect/read-timeout(sleep 절단) = attempts 미소모 +
  payload.deferred_until 30분 백오프, doc 쓰기는 완주+파싱 후 단일 커밋 (부분 쓰기 0)
- queue_consumer: claim 에 deferred 필터 + StageDeferred 분기
- workers.queue_drain: 수동 burst-drain CLI (summarize/deep_summary, SKIP LOCKED 단건
  claim, per-item 커밋, 보류 시 run 종료, deep 슬롯 필수 가드)
- tests 20건 + 라우터 경유 Qwen 실응답 fixture 박제 (13.2s 라이브)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:55:16 +09:00
hyungi 321d997123 fix(news): 연결 재시도 2회로 보강 — 드랍이 연결 단위 랜덤(재시도 1회도 연속 피격 실측) + 빈 에러 로그 repr
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 07:54:13 +09:00
hyungi b75307b89b fix(news): 연결 계층(TCP/TLS) 오류 1회 재시도 — MOEL 보안장비 첫 핸드셰이크 간헐 드랍 (재실측 진단)
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>
2026-06-11 07:43:05 +09:00
hyungi 8583465c58 feat(news): crawl-24x7 사이클 3 — B-4 시그널·C-4 공학 지속·CSB sitemap·CCPS Beacon (마이그 327)
- B-4 fetch_method='signal-only': 페이지 fetch 0 + summarize 스킵(검색 색인만,
  맥미니 부하 0) + 본문 무절단(_entry_body — arXiv 초록 1.6K 보존). 다이제스트는
  ai_summary NULL 제외 규칙으로 자연 배제. 레지스트리 오설정(page) 방어 가드.
- 시드 9 소스 (전 URL 2026-06-11 live 검증): Bloomberg Markets/Technology(skip-video,
  비디오 혼재 실측)·Economist Latest·Nikkei Asia(RDF — feedparser 네이티브, 분기 불요
  fixture 박제)·ASME JPVT(site_1000037 실측 매핑)·arXiv 2종·IEEE Spectrum 2종(feed-full,
  피드 description 이 전문 7.9~14K자 실측).
- csb_collector: sitemap lastmod diff (weekly 월 06:50) — 워터마크(selector_override)
  + cap 40/회 점진 백필 + diff sanity 300 + 보고서 PDF(/assets/, recommendation 제외)
  → extract 파이프라인. 초기 일괄 = CLI --bulk.
- api_standards_collector: 공지 목록 링크 파싱(실측 — 페이지 diff 아님, 상세 URL
  10건/페이지) → 신규 상세만 ingest (monthly 5일 07:05). 초기 백필 = CLI --bulk.
- ccps_collector: aiche.org 평문 403(UA 무관 실측) → playwright-fetcher 익명 컨텍스트
  + referer 쿠키 승계 /download(base64) 신설로 월간 Beacon PDF (monthly 5일 07:20).
  헤드리스 차단 시 CrawlBlocked → health 가시화 (르몽드 PARK 선례).
- B-5 잔여: rdf/feed-reader-UA = 코드 분기 불요 실측 박제 (Economist 는 Archiver UA
  200). table-strip/gn-redirect 는 해당 소스 미진입 — 백로그 유지.
- 테스트 24건 신규 (fixture 9건 live 박제, economist/ieee 는 item trim) — 39 passed.
- 마이그 327 단일 statement (PKM 트랙과 번호 경합 주의 — 327 본 트랙 선점).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 07:13:17 +09:00
hyungi f4e5db9723 fix(news): 304 를 redirect 로 오인하던 버그 — is_redirect → has_redirect_location
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>
2026-06-11 06:32:15 +09:00
hyungi 61e5a416d0 fix(news): fetch_page content-type 허용 파라미터 — TWI sitemap(text/xml) 수집 (검증 게이트 발견)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 16:41:30 +09:00
hyungi cdf4ee0ef6 fix(news): Guardian sectionName 'World news' 카테고리 매핑 (셀프 리뷰)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 16:37:22 +09:00
hyungi 1842f27d89 feat(news): crawl-24x7 사이클 2 — B-2/B-3/C-1/C-2/C-3/C-5 (마이그 324-326)
- 채널 인지화: news_sources.source_channel(324, documents enum 재사용) →
  문서 생성 정체성(_doc_identity)·embed/chunk 30일 게이트(crawl=전량 색인)·
  extract 후속 override(crawl→classify, preview 스킵) 분기.
- B-2 Guardian Open Platform: API 디스패치(호스트 분기, 미지 호스트=명시 실패)
  + show-fields=bodyText 전문 어댑터. fixture live 박제 + call-shape 테스트.
- B-3 구독지: playwright-fetcher 격리 컨테이너(동시 1·요청당 브라우저·storage_state
  ro mount) + politeness 사람속도(30-60s) 브라우저 경로 + fulltext 인증 라우팅
  (내용 기반 probe 게이트·relogin_requested 소비=open-스킵보다 앞·본문 페이월 마커
  게이트) + source_health probe 컬럼(325) + 세션 박제 스크립트(맥북용).
- C-2 KOSHA: 3 API live 검증·fixture 박제(board/attach/guide) — 재해사례 daily diff
  +첨부 PDF/HWP→extract 파이프라인, GUIDE 일일 cap 점진 백필(silent cap 금지 로그).
  키는 URL 직결합(재인코딩 함정 회피). daily 06:40 KST.
- C-3 정적 코퍼스: National Board 86 + TWI job-knowledge 153 일괄 CLI(멱등·politeness
  ·crawl_raw 보존·fulltext_worker 승격 필드 규약 동일).
- C-1/C-5 시드(326): 전 URL live 검증 — UK HSE(feed-full)/안전신문/고용노동부 3종
  (rss/*.do)/OSHA/EU-OSHA(후보)/SEP/1000-Word(feed-full)/Doing Philosophy/Aeon/Psyche
  (skip-video quirk).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:08:18 +09:00
hyungi ab668d7990 fix(news): crawl_raw 파일명 CHAR(64) 패딩 strip + politeness 대기 로그
- documents.file_hash 실 컬럼이 character(64) — 32자 해시가 공백 패딩되어
  gz 파일명에 공백 32개 포함 (실배포 1건 실측). _raw_html_path 에서 strip.
- _respect_domain_rate silent sleep 에 대기 로그 1줄 (검증 게이트·운영 가시성).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:43:29 +09:00
hyungi dcf99b377e fix(news): 적대 리뷰 반영 — reconcile auto-correlation·워터마크 검증 후 영속·수집 락
- 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>
2026-06-10 13:34:46 +09:00
hyungi 7cd8cfde0a feat(news): crawl-24x7 A그룹 — 레지스트리 증축·조건부 GET·fulltext 승격·politeness·source_health
A-3 migrations 319-323 (news_sources 9컬럼 + source_channel 'crawl' + process_stage 'fulltext' + source_health)
A-1 조건부 GET(ETag/Last-Modified 그대로 재전송)+콘텐츠 해시 변경감지, A-4 politeness 코어(per-domain 직렬+robots+정직UA),
A-2+A-7 fulltext_worker(4-tier 재사용·NAS crawl_raw gzip 보존·격하 경로·03:40 reconcile 안전망),
A-5 circuit breaker(3/10 임계, enabled 미터치), A-6 포털 전재 2차 dedup(제목+3일, 12자 게이트).
기존 소스 fulltext_policy='none' 기본 = 무회귀. plan crawl-24x7-1, 예외 박제 crawl-24x7-exec1-20260610.md
2026-06-10 13:03:31 +09:00
hyungi acd595244a fix(news): URL dedup 정규화 저장·조회 통일 + 다중매칭 내성
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>
2026-06-09 22:26:22 +00:00
hyungi 34eb5c9411 refactor(workers)!: SMTP 메일 발송 기능 전면 제거
다이제스트/이메일수집알림/법령알림 메일 발송 폐기 (사용자 결정 2026-06-10).
근거: 게이트(if smtp_host and smtp_user)가 06-07 전엔 항상 false(silent skip),
자격증명 활성 후엔 100% 553 Sender rejected — 한 통도 전달 성공 이력 없음.
law_monitor 는 CalDAV VTODO 가 단일 알림 채널로 유지. 다이제스트 .md 생성/
90일 아카이브, 이메일 IMAP 수집은 무변경. eid dispatch 의 send_smtp_email
문자열 블랙리스트는 의도적 잔존(코드층 박탈 강화와 정합).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 22:26:22 +00:00
hyungi 8e1645dfc9 fix(markdown): news article md_status pending→skipped 정합화
news article 은 텍스트 네이티브(본문=extracted_text)라 markdown 단계를 미enqueue
하는데(summarize/embed/chunk 만), md_status 기본값 pending 이 영구 고착돼 30,903 건이
비수렴 → (1) backlog 지표 오염(실 미변환≈0인데 pending 30,930) (2) md_status_pending
partial 인덱스 비대. terminal skipped(변환 비대상)로 정합화.

- news_collector.py: RSS/API 양쪽 Document 생성 시 md_status=skipped +
  md_extraction_error 사유 명시(생성 시점부터 정합).
- documents/[id]/+page.svelte: article 뷰의 MarkdownDoc 에 mdStatus 미전달(null).
  badge 는 mdStatus 로만 구동 → skipped 라도 "Markdown 제외" 칩이 3만 기사에
  뜨지 않게(article 은 markdown 변환 비대상이라 badge 자체가 무의미).
- 기존 30,903 건 backfill UPDATE(별도 실행): pending 30,930→27, partial 인덱스 동일 축소.

검증: pending 잔여 27(eml/doc/xls/이미지/미디어 long-tail) / 검색 무영향(article
extracted_text·chunks 그대로) / md_status 만 변경.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 06:22:04 +00:00
hyungi 55216271a6 feat(markdown): hwp raster 이미지 NAS 영속 + library backfill 스크립트
pyhwp(hwp5html) 가 bindata/ 로 추출하는 raster 이미지를 NAS 에 영속한다. 기존엔
변환 tempdir 와 함께 폐기돼 경고 없이 silent 유실(도식·수식)이었다(적대 리뷰 MEDIUM).

- office_md.py: _run_hwp5html 으로 hwp5html 1회 실행 → (markdown, raster_images).
  convert_hwp_to_md_and_images() 신규 = marker_worker 이미지 경로용. hwp5html 은 이미지를
  본문 xhtml 에 <img> 앵커하지 않아(--css/--html 동일) 인라인 위치 복원 불가 → 호출부가
  말미 갤러리로 부착. OLE 수식/도형은 앵커도 raster 도 아니라 영속 제외.
- marker_worker._process_office: .hwp raster 를 marker(PDF)의 _persist_images_to_nas 로
  NAS 영속 + document_images UPSERT(_sync_document_images, 재변환 orphan 정리) + md 말미
  ## 첨부 이미지 docimg: 갤러리 + quality.warnings hwp_images_appended. docx/xlsx/pptx/
  hwpx 는 이미지 미처리(기존 동작 유지).
- scripts/backfill_hwp_library.py: 지정 PKM 폴더 .hwp 를 content-hash dedup(Inbox 중복 +
  _1/카피본 사본 흡수) 후 category=library 일회성 ingest.

검증(E2E): Knowledge/Engineering 18개 → dedup 후 신규 5개(산업안전기사 3~7과목) ingest,
5/5 success. 제4과목 raster 3장 → NAS extracted_images/35778/img_001~003.jpeg 실재 +
document_images 3 row(engine=pyhwp) + md 갤러리 docimg ref. 이미지 없는 문서는 갤러리
미생성. 텍스트/표 경로 회귀 0(기존 4건 재변환 success).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 05:10:45 +00:00
hyungi d0994a1bce fix(markdown): hwp 변환 libhwplo→pyhwp 교체 + xml 프롤로그 strip
LibreOffice 번들 libhwplo 필터가 실제 한컴 HWP5 binary 를 못 읽어(rc=0 +
"source file could not be loaded") HWP 전건 실패(0/4). 순수 Python HWP5 전용
변환기 pyhwp(CLI hwp5html)로 교체.

- office_md.py: .hwp → _via_pyhwp_html(hwp5html→index.xhtml→markdownify).
  hwp5html xhtml 의 <?xml?> 선언이 markdownify PI 파싱으로 md 본문에 새고,
  ~34자가 _MIN_BODY_CHARS(16) 빈출력 게이트를 무력화(빈 변환 false-success,
  모듈 불변식 위반) → markdownify 전 프롤로그 re.sub strip.
- .hwpx 는 pyhwp 미지원 → LibreOffice 폴백 유지.
- marker_worker.py: 엔진 라벨 .hwp→pyhwp / .hwpx→libreoffice_hwp / else→markitdown.
- requirements.txt: pyhwp + six(pyhwp 미선언 런타임 의존성).

검증: HWP5 4건(용접 WPS/PQR·산업안전기사 1·2과목·원칙요약) 4/4 success,
한글 무결·표 GFM 보존·xml 아티팩트 0. 기존 포맷 경로(docx/xlsx/pptx·pdf·
passthrough·hwpx) 회귀 없음(적대 리뷰 2렌즈 확인).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 04:19:37 +00:00
hyungi daf6a0ade9 feat(documents): S1 dedup·office-md·storage scaffold (B/C/D/E)
plan ds-s1-backend-1 잔여 구현 (A·C-1 은 16b0fe1):
- B 중복검사: services/dedup.py (OFF-list law_monitor 공용) + 업로드 채움(B-1)
  + GET /documents/duplicates(B-2) + post-upload near-dup 비동기(B-3)
  + backfill_dedup.py(B-4) + 야간 dedup_reconcile 잡(03:30 KST 멱등 재계산)
- C MD-first: marker_worker office/hwp 분기 _process_office(C-2) + md_status
  상태머신 postcondition success|failed(C-5) + backfill_nonpdf_markdown.py(C-4)
  + requirements markitdown
- D 스토리지: services/storage ABC+Range 계약 / LocalBackend / NasApiBackend 503
  (D-1) + /file resolver 경유, 로컬 동작 불변(D-2)
- E 운영: pre-change pg_dump + rollback_287.sql + apply runbook(E-3) + 테스트(E-1)

비파괴 불변식 유지(기존 응답 shape 무변경, md_status success→completed read-time 매핑).
어드버서리얼 리뷰 확정 1건(soft-delete canonical 승격 시 stale duplicate_of) → B-1
승격 정규화 + 야간 재계산으로 정합.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 03:05:30 +00:00
hyungi 68e2d7ea04 feat(documents): S1-ADD dedup·원본명 3컬럼 + md_status success→completed 매핑 (A) + office→md PoC (C-1)
plan ds-s1-backend-1 (r5 수렴). 코드만 스테이징 — migration 미적용(restart 보류, E-2 Soft Lock 예외창).

A (앱 v1 디코딩 비파괴 최소선):
- A-1 migrations/287_documents_dedup_fields.sql: original_filename TEXT / duplicate_of BIGINT FK ON DELETE SET NULL
  / duplicate_count INTEGER NOT NULL DEFAULT 0. 단일 statement·PG16 fast-path·BEGIN/COMMIT 금지. backfill 미포함(B-4).
- A-2 app/models/document.py: 1계층 블록에 3 mapped_column (+ ForeignKey import). md_* 는 기존.
- A-3 app/api/documents.py: DocumentResponse 3필드(duplicate_count=0 non-opt) + DocumentDetailResponse
  field_validator(success→completed, mode=before) — read-time DB→API 단방향, write(ORM) 미적용.
- A-4 tests/test_s1_dedup_shape.py: success→completed 동작 + 비-success 통과 + 3필드 디폴트/roundtrip
  + ds-app contract fixture 디코드(skip-if-absent). py_compile OK. ★ backend 절반 — 전체 비파괴는 S3 render 테스트와 AND.

C-1 PoC (워커 미연결 — C-2 에서 marker_worker 분기 연결):
- app/workers/office_md.py: OOXML=markitdown(신규 dep, lazy) / hwp·hwpx=LibreOffice headless→HTML→markdownify(기존 dep).
  실패·빈출력·타임아웃·dep부재 → OfficeMdError raise (success+빈md 금지 = C-5 postcondition 의 변환기 계약).
- scripts/poc_office_md.py: 표 fidelity 측정 하니스. E-1 = prod LibreOffice 버전핀 안전컨텍스트 실행(hwpx 필터 버전 의존).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 03:05:30 +00:00
hyungi 6a85087b83 feat(eid): 이드 persona substrate W2~W4 — DS compose·약점진단·egress 코드층 박탈
전 로컬 LLM 관통 '이드' persona substrate 의 Document Server 측 빌드(W2~W4).
설계 = PKM eid-persona-substrate(r1~r3 수렴) / impl = eid-persona-impl.

W2 — compose + 표면 배선:
- app/eid/compose.py: persona→rules→overlay→task 단일 system 문자열 + 정적 ROUTE_MAP
  (런타임 sniffing 아님) + rules 부재 fail-loud · persona 부재 quiet · overflow fail-loud.
- 자유-prose 3 표면(react_ask·study_subject_note·study_question_explanation) 중복 정체성·
  generic 정책 trim + compose 배선(AIClient 에 additive system 파라미터). 도메인 calibration 보존.
- STRICT JSON 기계류(briefing_comparative·digest_topic)는 persona-ZERO 동결(불변식 #3).
- app/prompts/substrate/: persona(외부 컴파일 산출물 vendor) + rules(생성 가드 서브셋) + overlay 5.

W3 — migration + 워커 + study_diagnosis:
- migration 301~305: eid_* append-only 원장(약점/복습초안/회고) + approval_requests(가변 큐) + 일정 파생뷰 2.
- app/workers/study_weakness.py: study_question_progress.pattern_state 집계로 약점 derived 산출
  (LLM 0) + bounded tier(watch/review/focus). nightly cron.
- study_diagnosis 표면: 최신 스냅샷을 코치 언어로 번역(약점 판정은 코드, LLM 은 블록 값만 인용).

W4-1 — egress 코드층 박탈:
- app/eid/ai.py EidAIClient: 이드 표면 = call_primary(내부 MLX) only. 외부 LLM fallback 경로
  구조적 봉쇄(call_fallback raise · 자동 fallback 제거 · 외부 endpoint 차단). egress 워커는 분리 유지.

load-bearing 정정 3(환경 grounding 강제, 설계 회귀 아님):
- rules = 운영 ruleset 전체 → 생성 가드 서브셋(HTML 산출물 룰이 study task 와 충돌).
- append-only = REVOKE → CREATE RULE DO INSTEAD NOTHING(단일 owner role 은 REVOKE 무효 +
  migration 검증기가 plpgsql BEGIN 거부) + actor/source_* NOT NULL 스탬프.
- 이드 LLM 봉쇄 = path discipline → EidAIClient 구조화.

검증: eid 순수 단위테스트 30 통과 + py_compile + migration 검증기 모사 + egress 적대감사 COMPLETE.
DB/LLM/httpx 의존 테스트(append-only RULE·EidAIClient·E2E)는 staging(Docker) 가동.
W4-2 네트워크 belt 은 조건부 보류(코드층 1차 충분, P0-3② 원격 실측 후 hard-gate 시 승격).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 15:13:20 +09:00
hyungi 19f544fb5e feat(study): 공부 암기노트 Phase 1 — 정정/삭제 훅 + needs_review 큐 + 알람 재료 (HR/A)
추출 파이프라인(287~298, 별 커밋) 위 HR/A. 신규 마이그레이션 0 (DDL은 295~298 재사용).

- HR 정정/삭제 훅: PATCH 본문 수정 → 파생 study_memo_cards needs_review=auto(source_changed),
  soft-DELETE → source_deleted. flag_cards_for_source 헬퍼(임시 플래그, 최종정리는 워커 supersede).
- HR needs_review: PATCH set/clear(flagged_by='user' 서버강제) + GET /study-questions/needs-review
  목록·count(부분인덱스 술어 일치, 동적 {id} 라우트보다 먼저 등록해 int 파싱 충돌 회피).
- A 알람 재료: study_topics.focused_at 공부중 토글 + study_reminder cron(09/13/19 KST, due 술어
  quiz_selection SQL 재현·시간슬롯 truncate 멱등·LLM 0) + GET /api/study-reminders/latest(없으면 204).
- 테스트: 가드/정규화 18/18 (정량=evidence 원문·cue/cloze 누출·dedup·배치).

검증: 앱 부팅 import+mapper OK · 가드 18/18 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 08:08:55 +09:00
hyungi 0a7402b327 feat(study): 공부 암기노트 Phase 1 — card_extract 추출 파이프라인 (순수 additive)
study_memo_cards 추출 파이프라인 + 버전키 폴러 + needs_review 컬럼. 운영 SR 코드(session_finalize/quiz_selection) 무수정.

- migrations 287~298: study_memo_cards/_evidence/_jobs/_progress(P1 휴면)·study_reminders·study_topics.focused_at·study_questions needs_review 3컬럼. dedup PARTIAL UNIQUE(deleted_at IS NULL).
- 워커: in-process RAG gather → MLX {cards} → 카드 가드(정량=evidence 원문 등장·cue/cloze 누출·dedup) → supersede 구버전 retire → append. 별 consumer 로 기존 study_queue 격리.
- 폴러 study_card_enqueue: 버전키 NOT EXISTS(source_version) 멱등 + ai_explanation_generated_at NOT NULL 가드 + per-poll LIMIT(thundering-herd).
- 검증: 실 prod 스키마 덤프 위 12 마이그 적용 OK + dedup/supersede/active-unique 기능 7/7 PASS + 정규화 util 15/15.

plan: PKM plans/2026-06-05-study-memo-card-p1-plan.html

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:33:12 +09:00
hyungi f269e0df27 ops(news): chunk_worker news_source 매핑 실패 가시성 가드
_lookup_news_source prefix 미일치 시 silent (None) 반환 → warn 로그 추가.
loader 의 drop 로그와 대칭, 신규 source / RSS category 오염 재발 즉시 가시. 동작 변경 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 23:39:14 +00:00
hyungi 0854c72c70 fix(search): sync doc md_status to failed on permanent markdown queue failure
marker_worker 는 변환 시작 시 doc.md_status=processing 으로 표시하는데, 변환이
_fail()/_set_skipped() 를 거치지 않고 예외(예: 대형 batch ReadTimeout)로 죽으면
queue_consumer 가 큐 행만 failed 처리하고 doc.md_status 는 processing 에 영구 고착
= orphan (큐 failed, 문서 processing). markdown consumer 분리 후 이 orphan 이
tail 재처리에서 재발(5149/5201)하여 근본 원인 차단.

_process_stage except 블록에서 큐 항목이 영구 실패(attempts>=max)할 때 stage가
markdown 이고 doc.md_status=processing 이면 failed 로 동기화. 재시도 중
(attempts<max)엔 pending 큐 행이 남아 orphan 아니므로 미터치.

검증: synthetic 영구 실패 경로 → md_status processing→failed 동기화 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:06:32 +00:00
hyungi 2edc80d4bb fix(search): split markdown into dedicated queue consumer to prevent pipeline stall
대형 PDF split 변환(5210 ≈ 40분 실측)이 단일 consume_queue 코루틴을 점유해
extract/classify/embed/chunk 등 전 파이프라인을 stall 시키던 문제 제거.

- consume_markdown_queue 신규 — markdown 전용 scheduler job (id=markdown_consumer)
- consume_queue 는 MAIN_QUEUE_STAGES (markdown 제외) 만 처리
- _process_stage / _load_workers 헬퍼로 per-stage 로직 공유
- reset_stale_items(stages, threshold_minutes) 파라미터화: main=10min(markdown 제외),
  markdown=MARKDOWN_STALE_MINUTES(기본 120). marker_worker 는 heartbeat 미기록이라
  40분 변환을 10분 stale 로 오인하던 함정 차단
- enqueue flow (classify -> embed,chunk,markdown) 불변

STT/deep_summary 분리 + GPU 동시성 튜닝은 out of scope (follow-up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:33:45 +00:00
hyungi 826f66f8f5 fix(search): correct large-doc manifest wording after commit 4 drop
PR-DocSrv-LargeDoc-Split-Markdown-1 follow-up (plan brisk-paging-quokka.md).

commit 4(marker_section→document_chunks) 드롭으로, split md_content/manifest 의
「권위 검색본 = document_chunks (source_type=marker_section)」 문구가 실제와 불일치.
실제 = 검색 인덱스는 기존 document_chunks(extracted_text long_pdf window chunks),
marker_section chunk 부재, md_content 는 Markdown 렌더링 preview.

- _build_large_md_content 헤더: 「검색 인덱스 = 기존 document_chunks long_pdf/
  extracted_text window chunks. 아래는 Markdown 렌더링 preview.」
- _split_manifest: canonical_storage(marker_section) → search_index(legacy/extracted_text)
- 상수 주석 + _process_split docstring: commit 4 드롭/이중적재 회피 반영

뷰어에 없는 source_type 으로 디버깅 오도 방지. 이미 처리된 5 docs 의 md_content 는
즉시 재처리 X — 자연 reprocess 시 갱신(사용자 결정).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 09:48:03 +00:00
hyungi cf0d75fe84 fix(search): handle markdown/fileless docs without marker conversion
PR-DocSrv-LargeDoc-Split-Markdown-1 commit 5 (plan brisk-paging-quokka.md).

이미 마크다운인 문서는 marker 변환 불필요 → _process_markdown_passthrough 로
파일 내용(없으면 extracted_text)을 md_content 에 직접 적재(success), 비면 skipped.
- _is_markdown_doc: file_format=md/markdown 또는 .md/.markdown 확장자
- 분기 위치 = file_path validation 이전 (fileless md = file_path NULL 처리 위함)
- engine=passthrough 로 marker 변환본과 구분

기존 버그 해소: fileless md 43건=「no file_path」 fail / .md 파일=unsupported extension
skip → 둘 다 md_content 미생성이었음.

검증(docker cp 격리): 13948(.md+file_path)→success md_len=1805(파일) /
23409(fileless 931자)→success(extracted_text) / 20237(fileless 6자)→success.
PDF 경로 무영향(_is_markdown_doc=False).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 08:02:30 +00:00
hyungi 7aaabe2c75 feat(search): split markdown processing for large PDFs (>threshold)
PR-DocSrv-LargeDoc-Split-Markdown-1 commit 3 (plan brisk-paging-quokka.md).

- page_count gauge 분기: 소형(<=120p)=_process_single 통째 1-shot / 대형(>120p)=_process_split
- MAX_PAGES=200 hard skip 제거 → 대형은 BATCH_PAGES=40 page-range 윈도우 순차 변환
- 각 batch /convert start_page/end_page(1-based) 호출 + slug 충돌 회피 batch별 ref rewrite + stitch
- _persist_images_to_nas seq_offset → batch 간 image_key(img_NNN) 연속
- md_status success/partial/failed (전부/일부/전무) + failed batch manifest JSON
- 대형 md_content = head+manifest (LARGE_DOC_MD_CONTENT_HEAD_CHARS=50000), canonical=document_chunks(commit 4)
- MARKER_MAX_SPLIT_PAGES=5000 초과 = skipped_too_large 안전상태

검증: G1 소형회귀 doc6675 동일(success,6292,14)/single경로 / G2 doc5180 453p→12batch success
manifest+207img(img_001~207 연속) / G4 stuck0 restart0 각batch<300s. 섹션 chunk적재(G3)=commit 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 07:39:49 +00:00
hyungi a0b11d66f3 fix(worker): summarize ai_model_version label 정정 — qwen3.5 hardcode → primary config 동적
C5 of family-adaptive-bengio. summarize_worker.py 의 doc.ai_model_version 이 실제 모델 (Gemma) 과 무관한 \"qwen3.5-35b-a3b\" hardcode 였음 — 추적/분석/로그 신뢰도 영향. client.ai.primary.model (config.yaml ai.models.primary.model = \"mlx-community/gemma-4-26b-a4b-it-8bit\") 으로 동적 swap — 향후 config model 변경 시 자동 정합.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:28:05 +00:00
hyungi 0e8d5cccaf feat(worker): summarize sliding window — 50k chunk + cumulative carry-over
P3 of family-adaptive-bengio (Mac mini 4-lever bundle).

50k 초과 input 은 CHUNK_SIZE=50000 단위로 N 분할 + cumulative carry-over (prev chunk summary 를 다음 chunk prompt 에 prefix). 50k 이하 input = 기존 동작 (변동 0). 첫 chunk = client.summarize() legacy / 후속 chunk = call_primary + SUMMARY_PROMPT_CONTINUATION. log trace: single vs sliding chunk N/M done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 07:08:23 +00:00
hyungi acd29b963e ops(triage): event_kind_hint diagnostic logging cleanup (PR-4B Apply 영구 보류)
chore-memo-NULL-backfill 6/6 H1 (historical artifact) 확정 후 Apply PR 영구 보류.
406b810 의 8-line logger.info 블록 제거 (behavior 변경 0, 진단 데이터 더 이상 불필요).

backup: app/workers/classify_worker.py.pre-eventkind-cleanup (7일 안전망 ~2026-05-25)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 11:27:29 +00:00
hyungi 406b810e28 ops(triage): PR-4B-Diagnose-EventKindHint-Layer-A — diagnostic logging (no behavior change)
Layer-A Diagnose only. classify_worker.py:691 직전에 event_kind_hint 의
raw/normalized/in_valid/confidence 값 capture (logger.info 5줄 insert,
lazy formatting + %r repr). guard 통과 X 의 specific root cause (A1 field
부재 / A2 빈 string / A3 invalid enum) 확정용.

specific fix (default note / enum mapping / prompt 강화) 는 별 PR-4B-Fix-EventKindHint-Apply.
Apply PR closure gate 에 logging cleanup (info → DEBUG 또는 제거) 흡수.

plan: ~/.claude/plans/c-1-pr-infra-drift-1-phase-1b-linear-frost.md
backup: app/workers/classify_worker.py.pre-4b-eventkind-logging.20260517
2026-05-17 06:41:32 +00:00
hyungi 8998cbea8c ops(triage): PR-4B-Diagnose — exception logging 강화 (type/repr/exc_info)
Layer 1 root cause 진단을 위해 classify_worker.py:595 의 exception logging
을 lazy formatting + exc_info=True 로 강화. f-string 1줄 → 5줄 block.
- type=%s: exception class name (TimeoutError/JSONDecodeError/ValueError/etc.)
- repr=%r: full exception state
- exc_info=True: traceback 까지 capture (wrapper 정확 지점 추적)

본 PR scope = Diagnose only. Layer 1 specific fix (H1/H2/H3/H4) + Layer 2
escalate path ai_event_kind fallback set 은 별 PR queue.

plan: ~/.claude/plans/c-1-pr-infra-drift-1-phase-1b-linear-frost.md
backup: app/workers/classify_worker.py.pre-4b-diagnose.20260517
2026-05-17 06:22:27 +00:00
Hyungi Ahn a08b620894 refactor(search): swap 10 call sites to acquire_mlx_gate(Priority.*) (B-1)
DS-Mac-mini-26B-Priority-Gate-1 — 사용자-facing 7 + worker 3 = 10 site 의
`async with get_mlx_gate():` → `async with acquire_mlx_gate(Priority.*):` 교체.

Foreground 6 (user-facing path):
- app/services/search/evidence_service.py:315 (/ask evidence stage)
- app/services/search/classifier_service.py:103 (/ask classifier stage)
- app/services/search/synthesis_service.py:299 (/ask synthesis stage)
- app/api/documents.py:1306 (수동 analyze API)
- app/api/study_topics.py:1183 (subject note 동기 생성)
- app/api/study_questions.py:1560 (study explanation 동기 API)

Background 4 (worker queue / fire-and-forget):
- app/services/search/query_analyzer.py:240 (V0 grep 확인: fire-and-forget only,
  search_pipeline.py:179 trigger_background_analysis 만, docstring rule
  "analyze() 동기 호출 금지" 부합 → BACKGROUND 확정)
- app/workers/deep_summary_worker.py:110 (classify-escalate worker)
- app/workers/study_explanation_worker.py:149
- app/workers/study_session_analysis_worker.py:237

Cleanup:
- query_analyzer._get_llm_semaphore() 제거 — self-only, unused, signature 거짓말
  (이제 get_mlx_gate 가 Semaphore 아닌 context manager 반환)

기존 get_mlx_gate() legacy wrapper 는 보존 (BACKGROUND 매핑). user-facing path
잔재 0 — closure gate grep 검증 통과 (별 commit 에서).
2026-05-17 08:51:57 +09:00
Hyungi Ahn 3627060d2a fix(ingest): devonagent extract md_status 'ready' → 'success'
documents_md_status_check 제약은 {pending/processing/success/partial/failed/skipped}
만 허용. extract_worker 의 web HTML 분기가 'ready' 박아서 CheckViolationError
로 3회 실패. plan/docs/메모리에 'ready' 로 잘못 표기됐던 것 수정.

19668 (첫 sample doc) 검증 중 발견. fix 후 queue 'failed' 행 reset 으로 재실행.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 08:42:15 +09:00
Hyungi Ahn 0cbba0ceeb feat(ingest): devonagent 트랙 Phase 1 ingest 활성화
DEVONagent/DEVONthink 가 발견한 웹페이지를 NAS Web/ drop → file_watcher
ingest → extract 4-tier fallback (trafilatura/sibling-md/readability/bs4)
→ embed + chunk 까지. classify/preview/markdown SKIP.

- source_channel='devonagent' (migration 001 dormant 활성화)
- file_watcher: SCAN_TARGETS 통합 + Web/ rglob + canonical_url dedup +
  sidecar 누락 정책 (skip 안 함, web_meta.sidecar_missing=true flag)
- extract_worker: HTML+devonagent 분기 + md_extraction_engine 4-tier 구분
  (trafilatura → sibling .md ≥200char → readability+markdownify → bs4_text)
- queue_consumer: enqueue_next_stage 의 extract stage 만 source_channel-
  aware override (devonagent → [embed, chunk])
- classify_worker: devonagent safety skip (law_monitor 패턴 mirror,
  ai_domain='Web', ai_tags=['Web/{host}'])
- requirements: trafilatura/readability-lxml/markdownify 추가
- docs: devonthink-web-bridge.md 설치 가이드 + first-wins 정책 명시

Phase 1 closure 기준 = 재료 품질 (검색 가능 + 노이즈율 + dedup + 엔진 분포).
활용처(ai_tldr/digest/PKM 회고)는 1-2주 OR 30-50건 관찰 후 별 PR 에서 결정.

Plan: ~/.claude/plans/db-snuggly-petal.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 21:23:16 +09:00