Commit Graph

828 Commits

Author SHA1 Message Date
hyungi 96bd849bcb fix(documents): 절뷰(hasSections) 본문을 MarkdownDoc로 렌더 — 수식·이미지 살림
★진짜 원인: 절 있는 문서(useSectionView)는 절 본문을 plain marked(renderMd)로 렌더해
수식(katex 없음 → raw $$)·이미지(docimg → DOMPurify 미지원프로토콜 제거 → 사라짐)가
전부 깨졌다. 앞선 renderDocMarkdown 수정들은 !hasSections 경로뿐이라 절뷰 문서엔 미적용.
절 bodyText 에 docimg·$$ 실재 확인(3791: docimg 21·blockmath). 데스크탑/모바일 절 본문
{@html renderMd} → <MarkdownDoc documentId mdContent={bodyText}> 로 교체 → pre-render
(수식·이미지 placeholder) + swap(실제 이미지) 적용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 06:42:24 +09:00
hyungi db7ede04b7 fix(markdown): 이미지 ref pre-render — 렌더러 미발화 시에도 placeholder 표시
docMarked image 렌더러가 런타임 미발화 시 ![](docimg:img_NNN) 가 기본 <img src=docimg:>
로 떨어지고 DOMPurify(미지원 프로토콜)가 제거 → placeholder·이미지 둘 다 사라지던 문제
(수식 토크나이저 미발화와 동형). marked 이전에 image ref 를 placeholder figure 로 직접
pre-render(슬롯 보호, 수식과 동일 우회). 이후 MarkdownDoc swap effect 가 실제 <img> 로 교체.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:34:34 +09:00
hyungi ac7de71ecd feat(review): 검토 대기 자동검토 워커 — 고신뢰 자동승인 + 저신뢰 잔류
auto_review_worker(interval 3분·배치 300): review_status='pending' + ai_domain
+ ai_confidence>=0.9 인 문서를 review_status='approved' 자동승인 + audit
(source_metadata.auto_reviewed). 저신뢰/미분류는 수동 큐 잔류. 재-LLM 호출 없음
(classify confidence 게이트 = 맥미니 부하 0). review_status 는 검색/RAG/digest 필터
미사용(게이트 실측) → 노출 변동 없이 검토 큐만 드레인. 되돌리기=audit 마커로 식별.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:36:56 +09:00
hyungi a6d5734f6c feat(memos): 자료로 보내기 P2 — 메모→문서 26B 문서화 워커
memo_draft_worker(interval 2분): promote 가 찍은 source_metadata.needs_draft=true
문서를 26B(call_primary, acquire_mlx_gate BACKGROUND)로 구조화 마크다운(md_content)
생성. content_origin='ai_drafted'+md_draft_status='draft'(mig212 제약 준수), 원본은
extracted_text 보존. promote 엔드포인트에 needs_draft 마커 + main.py add_job.
큐 enum/컨슈머 무변경(derived-worker 패턴) = 저위험.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:50:44 +09:00
hyungi fe8235d726 feat(memos): 자료로 보내기 — 메모를 문서함 정식 문서로 승격 (P1)
새 POST /memos/{id}/promote-to-document: in-place 승격(별 row X) —
source_channel→manual, file_type note→editable, category=library,
content_origin=manual + classify/embed/chunk 재큐(도메인 재부여·요약·심층분석).
메모 카드에 always-visible '자료로 보내기' 버튼(지식 메모=ai_event_kind note 포함).
P2(거친 메모→구조화 마크다운 draft 워커)는 후속.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:32:04 +09:00
hyungi b0a73f8506 feat(nav): 상단 nav 질문·이드 제거 + 메모 추가
데스크탑 상단 nav 와 모바일 하단 탭바 모두에서 질문(/ask)·이드(/chat) 링크 제거,
메모(/memos) 추가(모바일은 기존 존재). 라우트 코드는 보존(nav 노출만 제거).
미사용 아이콘 import(HelpCircle·MessageCircle) 정리.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:16:23 +09:00
hyungi 2d6d1b8e8a fix(markdown): 수식 pre-render(katex 직접) + TL;DR 마크다운 렌더
본문 $$수식$$가 raw로 노출되던 문제: marked-katex 토크나이저가 개요 anchor
splice/런타임 환경 영향으로 미발화 → marked 이전에 katex.renderToString 으로
직접 렌더 후 placeholder 복원(위치·인접 무관). TL;DR(ai_tldr)도 plain-text
보간이라 마크다운 미렌더 → renderDocMarkdown 경유로 교체(+summary-md 스타일).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:05:35 +09:00
hyungi 4c111ca7f2 fix(observability): BackgroundJobItem 응답 모델에 machine 필드 추가 (직렬화 누락 수정)
f325bd0 이 서비스 payload·frontend 타입엔 machine 을 넣었으나 API Pydantic
response_model(BackgroundJobItem)에 누락 → FastAPI 가 직렬화 시 탈락. 한 줄 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 03:43:55 +00:00
hyungi f325bd0509 feat(observability): digest/briefing 을 처리 보드에 맥미니 작업으로 노출 (background_jobs)
큐 밖 cron 생성 작업(global_digest/morning_briefing)이 processing_queue stage 가
아니라 보드에 안 잡혀, 맥미니가 11분짜리 digest 를 돌려도 idle 처럼 보였다.
ebbcaf8 의 background_jobs 메커니즘 재사용:
- digest_worker/briefing_worker = start_job→finish_job (best-effort, 본작업 무해)
- pipeline = cluster 완료마다 heartbeat(processed/total) → 진행바
- queue_overview = kind→machine 맵으로 payload 에 machine 필드 (맥미니 귀속)
- 보드 = 머신 레인에 dot 점등 + "생성 중: <label> N/T" 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 03:36:57 +00:00
hyungi d4e1f76e81 fix(news)!: mlx_gate_concurrency 4→2 롤백 — gate=4 가 대형 프롬프트(digest/briefing+deep 6764tok) 동시성으로 맥미니 mlx_vlm OOM/연결드롭 유발(08:45 서버 크래시·재시작 실측). digest cap 3000→5400(gate=2 보정). timeout/deep-split 유지
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 23:55:44 +00:00
hyungi a82b0724df fix(news): digest/briefing 생성 LLM 타임아웃 게이트 단일소스화 + deep_summary 컨슈머 분리
2026-06-11 맥미니 모델 교체(Gemma4 26B→Qwen3.6-27B-6bit, 콜당 ~90~300s)의
타임아웃 상향 sweep 이 config.yaml/synthesis 만 갱신하고 digest/briefing 코드의
하드코딩 LLM_CALL_TIMEOUT=25(빠른 Gemma 기준)를 누락 → digest 600s 하드캡 초과로
06-10 이후 미생성, briefing 4/4 LLM 폴백(status=failed). (적대 리뷰로 블로커 정정:
concurrency=1 사설 세마포로는 digest 44~68 클러스터가 하드캡에 여전히 걸림 + llm_gate
영구 룰 위반.)

- 타임아웃·재시도·하드캡을 config.pipeline 단일소스로 이관(digest_llm_timeout_s=300,
  attempts=2, pipeline_hard_cap_s=3000). 다음 모델 교체 때 재발 차단.
- digest/briefing LLM 호출을 사설 Semaphore 제거하고 전역 MLX gate(BACKGROUND)
  경유로 변경 — llm_gate 영구 룰(같은 endpoint 단일 게이트, 새 Semaphore 금지) 준수 +
  ask/eid(FOREGROUND)와 조율. 동시성 lever = 기존 mlx_gate_concurrency 2→4
  (continuous batching 실측 — 3동시콜 wall 121s ≈ 단일콜, 직렬 대비 ~3배).
- digest/briefing pipeline cluster 루프를 asyncio.gather 동시 실행으로 전환
  (실동시성은 게이트가 제한, rank/순서 보존).
- deep_summary(70~300s)를 메인 consume_queue 에서 분리해 consume_deep_queue 신설
  (markdown/fast split 선례) — 단일 deep 호출이 1분 틱 초과로 메인 큐를 영구 coalesce
  시키던 문제 제거.
- 죽은 PIPELINE_HARD_CAP=600(briefing/pipeline.py) 제거, summarizer docstring 갱신,
  deep 컨슈머 disjoint/hold 테스트 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 23:29:56 +00:00
hyungi b2949d26ff fix(search): documents.embedding HNSW 인덱스(마이그 358) + ef_search=100 — docs vector leg seq scan 제거
documents.embedding 에 벡터 인덱스가 없어 검색마다 40k row Parallel Seq Scan
(콜드 448ms, 코퍼스 성장에 선형 악화)이었음. study_questions 와 동일 패턴의
HNSW 부분 인덱스 추가 → docs vector leg 448ms → 7.9ms (EXPLAIN Index Scan 확인).

docs vector leg LIMIT=limit*4(기본 80)이라 HNSW recall 위해 ef_search>=80 필요 →
ivfflat.probes 와 동일하게 ALTER DATABASE pkm SET hnsw.ef_search=100.

PROD 적용: CREATE INDEX CONCURRENTLY 로 수동 빌드(무중단, /dev/shm 회피 위해
max_parallel_maintenance_workers=0 단일 스레드, 316MB) + schema_migrations(358)
수동 기록. runner 는 단일 트랜잭션이라 CONCURRENTLY 불가 → 본 파일은 fresh-init
재현용 non-concurrent IF NOT EXISTS.

검증(snapshot freeze 43958/195671, eval both, exact vs HNSW):
- graded NDCG 0.575 → 0.575 (±0.000, 전 카테고리·Recall byte-identical)
- ef_search=100 이 top-80 에 사실상 exact recall → 랭킹 손실 0
- prod smoke 'pressure vessel design code ASME' 작업전 5263ms → 650ms

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 23:18:36 +00:00
hyungi 151c1ee518 fix(search): text-leg 본문 스코어링 2000자 절단 + bge-m3 keep_alive 로 검색 latency 개선
코퍼스 ~52배 성장(코드 가정 765 → 실제 40k docs) 후 search_text ORDER BY 가
후보 행마다 extracted_text(평균 3.7KB·최대 1.6MB) 전체에 similarity() +
to_tsvector() 재토큰화를 재연산 → broad/영어 쿼리 text_ms 최대 4960ms.
scoring/match_reason 의 extracted_text 를 left(...,2000) 으로 절단(후보 CTE 의
FTS 매칭은 전체 본문 유지 → recall 불변). embed() 요청에 keep_alive:-1 추가로
ollama bge-m3 GPU 상주 → sparse 검색의 cold reload(~6s) 제거.

검증(snapshot freeze docs 43958/chunks 195671, 51 case, eval-version both):
- graded NDCG 0.575 → 0.575 (±0.000, 전 카테고리 byte-identical)
- Recall g>=2 0.691 / g>=3 0.739 불변, v0.1 NDCG/Recall/Top-3 불변
- latency p50 760→586ms (-23%) / p95 5230→832ms (-84%)
- EXPLAIN 단일쿼리: V0 4917ms → left(2000) 285ms (17x)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 04:34:24 +00:00
hyungi ebbcaf86d8 feat(observability): 큐 밖 백그라운드 작업(backfill)을 처리 머신 보드에 노출
processing_queue 는 파이프라인 stage 전용이라 hier_overnight_backfill 같은 off-queue
관리 스크립트 작업이 대시보드 보드에 안 잡혀, 다른 세션이 모르고 fastapi 를 재생성해
in-flight 재분해를 끊는 사고가 발생(2026-06-14). 사각지대 해소.

- migrations/357_background_jobs.sql: background_jobs 테이블(kind/label/state/processed/
  total/heartbeat). worker_jobs(user_id 필수, worker-pool 전용)와 별개.
- services/background_jobs.py: start/heartbeat/finish 헬퍼 — 자율 트랜잭션(즉시 commit →
  실시간 가시화) + best-effort(관측 실패가 본작업 안 깸).
- hier_overnight_backfill: 작업 시작/절 ~10개마다 heartbeat/종료 계측.
- queue_overview: /api/queue/overview 응답에 background_jobs 추가(running + 최근 6h 완료,
  stale=heartbeat 끊김 추정). SAVEPOINT 로 테이블 부재/오류 시 보드 본체 무영향.
- ProcessingFlowBoard: "백그라운드 작업" 패널(진행/경과/state, stale 끊김 경고).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 12:27:18 +09:00
Claude Code 6d978289b8 feat(papers): B-3 P2-PR1 oa_url 승격 분기 (arXiv 외 doi.org/KISTI/PMC OA)
arxiv_id 없는 OA 논문(oa_status gold/hybrid/green/diamond + oa_url)도 전문 승격 대상에 포함.
url = arxiv.org/pdf 또는 oa_url(friendly OA host). paywall/비-PDF 는 헤더검증서 skip(실패 격리).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 03:16:47 +00:00
Claude Code 73c6f123b8 feat(papers): B-3 P2-PR1 — arXiv 논문 전문 in-place 승격 + classify paper 요약-스킵
plan safety-library-b3-1 Phase-2. 논문을 초록(signal-only)에서 전문 md/검색으로 승격.
- paper_fulltext_promote.py: 미승격 arXiv 논문(file_format='article') → arxiv.org/pdf/{id} 다운로드
  (kosha 패턴·50MB cap·PDF 헤더검증) → NAS crawl_raw/papers/arxiv/ → in-place 갱신
  (file_format=pdf·file_type=immutable·file_path·md_status=pending, file_hash·extract_meta.paper 보존)
  → 'extract' enqueue. 1-Document(2행 분리 회피, 기존 display 스택 재사용). per-run cap 10(GPU 보호).
  arXiv=공개 프리프린트라 전문 검색/RAG 무난(restricted 불요; 유료 구매분만 Papers_Purchased restricted).
- classify_worker: material_type='paper' 가드 추가 — 요약/분류 LLM 스킵(맥미니 큐 무접촉),
  queue_consumer 가 embed/chunk/markdown 은 chain. law_monitor 스킵 패턴 동형.

CLI 전용(Phase-2 deliberate 승격·GPU 부하 사용자 통제). 파이프라인=extract→classify[skip]→embed/chunk/markdown,
marker 표시 md + hier 절구조 + 전문 검색 청크. 배포 후 라이브 검증.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 03:04:02 +00:00
hyungi 57c1805a8d Merge pull request 'Feat/safety library b3' (#39) from feat/safety-library-b3 into main
Reviewed-on: #39
2026-06-14 08:05:09 +09:00
hyungi cbdd4a3df7 Merge pull request 'Feat/docpage open as default' (#38) from feat/docpage-open-as-default into main
Reviewed-on: #38
2026-06-14 08:04:59 +09:00
Claude Code bf0348a3e0 feat(papers): B-3 PR5 — 구매 PDF parent_doi 스탬프 (paper_doi_reconcile 통합)
plan safety-library-b3-1 PR5. Papers_Purchased 수동 드롭 PDF(license.restricted=true)를 서지 holder 에
연결: 본문 DOI 파싱 → paper.parent_doi 링크(child, doi 미보유=인덱스 밖, unique 무충돌).
- doi.py: parse_doi_from_text(본문 전체 DOI 정규식 — PDF 구조 무관).
- paper_doi_reconcile: restricted 분기 — restricted 행은 본문 DOI→parent_doi(child),
  그 외(레거시 arXiv)는 holder 스탬프(PR4). 쿼리에 parent_doi IS NULL 추가(링크분 재처리 회피).
- file_watcher merge-only license 주입 clobber-safe 존중. enqueue 0(콘텐츠 무변경).

단위 29 passed(+parse_doi_from_text). ephemeral PASS: 합성 restricted 행 → parent_doi 링크·
paper.doi 부재·restricted 보존·스키마 수용(insert+rollback). reconcile 멱등(재실행 0 변경).
실 구매 PDF 라이브 검증 = 사용자 첫 논문 구매·드롭 시(로직 검증 완료).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:58:19 +00:00
Claude Code 244d526ae2 feat(papers): B-3 PR4 — 레거시 arXiv DOI reconcile + arXiv DataCite DOI 통일 (keyless)
plan safety-library-b3-1 PR4. paper.doi 없는 paper 행을 arXiv DataCite DOI 로 스탬프해
partial-unique 인덱스 편입 → 재유입 차단('동일-DOI 재유입 차단만').
- doi.py: parse_arxiv_id(본문→arXiv id) + arxiv_doi(10.48550/arxiv.{id}, OpenAlex canonical 실측 일치).
- ★arXiv DOI 통일: arxiv_collector 도 프리프린트(저널 DOI 없음)에 arxiv_doi 부여 → PR2/PR3/PR4 가 같은
  함수로 같은 paper.doi → 교차소스 dedup 성립(이전엔 프리프린트 paper.doi 부재로 PR2↔PR3 dup 갭).
- paper_doi_reconcile.py: 전용 worker(dedup_reconcile=file_hash 캐시와 별개 — 적대리뷰 B·C major).
  keyless·결정적(OpenAlex 호출 0)·in-DB·enqueue 0(콘텐츠 무변경). 선재 DOI holder 시 parent_doi
  마킹(unique 위반 회피). add_job daily 03:50 KST. __main__ CLI.

단위 28 passed(+parse_arxiv_id·arxiv_doi). 라이브 PASS (prod, running fastapi 무접촉):
레거시 197행 arXiv DataCite 스탬프·ASME 2행 skip·선재중복 0 / dedup 불변식 206 distinct 206(인덱스 무위반) /
paper summarize active 0(signal-only). 멱등.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:54:24 +00:00
hyungi c5bc1f773d fix(docpage): 비인접 window 를 parent_id 로 split-parent 에 흡수 (빈 본문 절 수정)
split-parent(절 헤딩)와 그 window 조각이 chunk_index 상 비인접인 경우(예: 5180 FOREWORD
헤딩 idx 1143, window idx 1233~)가 있어, 인접 흡수만 하던 collapseWindows 가 split-parent 를
빈 본문 행으로 남기고 window 들은 따로 대표 행을 만들어 "같은 제목 2행(빈 것 + 본문 있는 것)" 이
됐다. 사용자가 "본문 없는 절" 로 본 것.

- /sections API 에 parent_id 반환 (window.parent_id = 그 split-parent chunk_id, 100% _split 링크)
- collapseWindows 가 window 를 parent_id 로 split-parent 대표에 흡수(비인접 허용), 인접 heading
  fallback 유지(legacy window). 흡수 멤버에서 본문/분석 집계.
- 회귀 테스트: 비인접 parent_id 흡수 (12/12 pass)

실데이터 검증(빈 본문→0): 5180 outline 85→58·5210 318→277·5178 73→49·5151 45→40, 전부 EMPTY_BODY=0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 07:46:18 +09:00
Claude Code fdabca2a2f feat(papers): B-3 PR6 — OpenAlex ISSN 소스 시드 (KR/JP 안전 저널 직접 커버)
plan safety-library-b3-1 PR6 (revised). 라이브 정찰: KoreaScience=깨끗한 API 없음(OAI 404)·
J-STAGE=ToS bulk 금지, 그리고 Phase-1 메타는 OpenAlex 가 이미 전수 색인(한국안전학회지 1766건 실측)
→ 전용 스크래퍼 대신 검증된 OpenAlex 수집기에 도메인 저널 ISSN 시드 추가(전용 무료 전문 PDF=Phase-2 park).
- _JOURNAL_ISSNS(OpenAlex sources 실측): 한국안전학회지 1738-3803·한국가스학회지 1226-8402·
  KSME A/B 1226-4873·1226-4881·KSME Intl 1226-4865·JP 고압 0917-639X.
- _seeds() = ISSN 시드(cap 우선) + 키워드. build_issn_filter(primary_location.source.issn:).
  run() 루프 통합(종류별 필터, 워터마크 시드별). 적재/parser/cap/signal-only = PR3 재사용.

단위 8 passed(+ISSN 시드). 라이브 PASS: 키주입 run → 한국안전학회지 5건 적재(ISSN 우선 확인),
running fastapi 무접촉. KoreaScience/J-STAGE 전용 fulltext 수집기 = Phase-2 강등(park).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 22:42:30 +00:00
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
hyungi d007ad5492 fix(docpage): windowed 절에 조각별 분석(유형/신뢰도/요약) 집계 노출
절-레벨 분석(chunk_section_analysis)은 코퍼스 전역에 이미 있으나(절 보유 344문서 중 336)
window 조각의 chunk_id 에 붙어 있고, D3 는 window 를 split-parent 대표로 collapse 하며 버려서
windowed 절은 요약/유형/신뢰도가 안 떴다(분석은 대표가 아닌 조각에 있음).

- collapseWindows 가 멤버(대표+흡수 window)에서 절-레벨 분석 집계:
  sectionType=다수결(동률 첫등장) · confidence=평균 · summaries=조각 요약 배열(빈 것 제외)
- D3 트리/focus/모바일카드/이전다음이 it.sectionType/it.confidence/it.summaries 사용
- 요약은 단일 절=문단, windowed 절="절 요약 · N개 부분" 번호목록
- headingPath.test.ts: 집계 회귀 테스트 추가 (11/11 pass)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 07:27:41 +09: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
hyungi b6a4821cac fix(docpage): 절 본문을 청크 text로 렌더 + window 조각 collapse
대형 split 문서는 marker 가 md_content 를 앞 5만 자만 보존하고 char_start 도 NULL 이라
char_start 슬라이스로는 절 본문이 비었다. 전체 본문은 document_chunks.text 에 절별로 보존됨.

- /sections API 가 청크 text 반환 (SectionItem.text; 소비자=D3 단독, additive)
- collapseWindows 가 window 조각 본문을 대표 절 bodyText 로 합본 (split-parent heading 제외)
- D3 페이지가 outline(collapseWindows) 단위로 렌더 → window 파편화 제거
  (5180 = 27 논리 절이 562 동일제목 조각으로 쪼개지던 문제)
- useSectionView=hasSections 로 단순화(partial/대형 문서도 절뷰), 모바일 본문 lazy 파싱
- headingPath.test.ts: bodyText 누적 회귀 테스트 추가 (10/10 pass)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 07:10:59 +09: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
Claude Code 345e2cedf0 feat(papers): B-3 PR1 — DOI 정규화·dedup 코어 (normalize_doi 단일 함수 + 서지 holder 조회)
plan safety-library-b3-1 PR1 (keyless·마이그 0). 모든 논문 수집기·reconcile·구매 스탬프 공유 토대.
- normalize_doi(): 소문자·URL/doi: prefix 제거·인용 구두점(.,;) 정리. 저장=조회 단일 함수.
  괄호 '()' 보존 — 과삭제는 다른 논문 병합(데이터 손상)이라 near-dup 보다 위험.
- paper_doi_hash(): 서지 holder file_hash 키 = sha256('paper|{doi}')[:32] (statute 다중부 키 선례).
- with_paper_doi/with_parent_doi/read_paper_doi: 2-Document 계약(holder doi / child parent_doi 상호배타) extract_meta 헬퍼 (merge-safe).
- find_paper_holder(): 공유 dedup 조회 — lower(extract_meta #>> '{paper,doi}'), .scalars().first()(BBC 다중행 선례),
  EXPLAIN 으로 uq_documents_paper_doi(마이그 351 라이브) 인덱스 사용 확인.

단위 12 passed. holder DB 조회 = PR2 arXiv 실수집서 라이브 검증. 소비자 없는 순수 코드(배포·런타임 변화 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 21:50:09 +00:00
hyungi b461559d2f fix(docpage): 절 없는 문서도 인사이트 항상 표시 (fallback 개선)
사용자 "절이 없더라도 인사이트는 보여야지" — fallback(절 데이터 없는 ~92% 문서)이
모바일에서 인사이트 레일을 긴 본문 아래에 묻던 문제 수정. bodyViewer 스니펫 분리 후:
- 모바일: 인사이트 레일을 본문 위에 상시 표시
- 데스크탑: 본문 | 인사이트 레일(sticky)
(별개: 절 트리/집중 뷰는 절 분석 있는 문서에서만 활성 — 현재 4358중 333. 커버리지 확대는 후속.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:39:44 +09:00
hyungi 9b9790f05d fix(docpage): D3 시안 스타일 그대로 포팅 + 모바일 길이/접근성 수정
사용자 "시안대로 안했다" → 앱 토큰 재해석을 폐기하고 d3-deepened 시안의 inline
스타일을 그대로 포팅(데이터만 바인딩): 트리 좌측 색바(3×16)+연결선(ㄴ자)+활성+
저신뢰 맥동배지, 절차색 #7a8b3f, 헤더 PDF아이콘+pill칩+분류/원본/링크/관리, 절 집중
뷰(요건 requirement 배지·신뢰도 바·절요약 인용박스), 슬림 레일 카드(시안 동일).
모바일: 절구조/인사이트 안보임+무한길이("쭉 아래까지") → pill sticky + 절 본문
카드마다 접기('본문 보기', 기본 요약만)로 컴팩트화. svelte-check 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:29:04 +09:00
hyungi b49596135e fix(docpage): 모바일을 확정 시안 그대로 — 나란한 토글 pill + 패널 + 본문 연속
직전 모바일이 세로 details 2개라 시안(나란한 pill 토글)과 불일치
(사용자 "시안에 모바일용도 있잖아 그걸 안 만들었다") → d3-deepened 모바일 프래그먼트
충실 복제: 절 구조|인사이트 나란한 pill(기본 둘 다 접힘) + 절 구조 패널(유형 범례·
점프 링크·저신뢰·들여쓰기) + 인사이트 패널(TL;DR·핵심점·심층DEEP·불일치·분류·태그) +
본문 절 카드 연속(#m-sec 앵커, pill 탭→본문 이동). svelte-check 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:12:59 +09:00
hyungi 0a82a5b1bc feat(docpage): 모바일을 시안대로 — 본문 연속 절 카드 + 접이 + 탭 이동
기존 모바일(데스크탑 focus 단일절)이 시안 모바일과 불일치(사용자 "모바일은 변한게
없잖아") → 시안 모바일 충실 구현:
- 모바일(<xl) = 절 구조/인사이트 접이(기본 절구조 닫힘·인사이트 열림) + 본문이 절
  카드로 연속(각 절 제목·유형배지·절요약·본문) + 절 구조 탭하면 #m-sec 본문 앵커 이동
- 데스크탑(xl+) = 트리 | 절 집중 | 레일 (focusView 스니펫으로 분리)
- treeNav(jumpMode): 데스크탑=절 선택 / 모바일=앵커 점프
svelte-check 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:03:59 +09:00
hyungi 74e29e510e feat(docpage): D3 상세 페이지를 확정 시안 그대로 재구현
기존 컴포넌트 재사용/배치변경(불충실)을 폐기하고 deepened 시안을 충실히 구현:
- 좌 절 트리: 유형 색칩(정의/절차/요건)·신뢰도 dot·저신뢰 경고·레벨 들여쓰기·클릭=절 선택
- 중 절 집중 뷰: breadcrumb + 제목 + 유형 배지 + 신뢰도 막대 + 절 요약 인용 + 절 본문
  (md_content 를 char_start 로 슬라이스) + 이전/다음 절
- 우 슬림 레일: TL;DR · 핵심점 · 심층(DEEP) · 불일치 · 분류 · 태그 (읽기) + 정보/관리 접이(편집 보존)
- 절 없음 fallback: 전체 본문/뷰어 + 레일 (D3 빈 절 graceful)
- 모바일: 본문(절 집중) 메인 + 절구조/인사이트 접이
svelte-check 0. 시안=comparisons/2026-06-13-ds-docpage-d3-deepened.html.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 15:53:34 +09:00
hyungi c1555fd6ab feat(docpage): 전체 문서 목록 클릭 시 인라인 미리보기 대신 D3 상세로 이동
사용자 결정 "개선된 페이지가 앞으로 표시되야지" — /documents 브라우저에서
문서를 열면 인라인 DocumentViewer(구) 대신 개선된 /documents/[id](D3 절 구조
탐색기)로 이동. /documents = 브라우즈/검색/필터/일괄 목록(풀폭 중앙) 역할로 정리:
- selectDoc → goto(/documents/[id]) (행 클릭·키보드 enter 공통)
- 인라인 리더(DocumentViewer)·인스펙터 패널 제거, 목록 max-w-5xl 중앙
- AI 답변 카드(질문형 검색)는 목록 상단 고정으로 이동(보존)
- 검색·필터칩·일괄작업·업로드·페이지네이션 전부 유지

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 15:41:59 +09:00
hyungi 1d5755b279 Merge pull request 'feat(docpage): D3 절 구조 탐색기 — 슬림 인사이트 레일 + 절 트리 (frontend only)' (#37) from feat/ds-docpage-d3 into main
Reviewed-on: #37
2026-06-13 15:23:05 +09:00
hyungi a3e0d30569 Merge pull request 'Feat/ds board merged' (#36) from feat/ds-board-merged into main
Reviewed-on: #36
2026-06-13 15:22:53 +09:00
hyungi 540bc00dba feat(docpage): D3 절 구조 탐색기 — 슬림 인사이트 레일 + 절 트리 (frontend only)
문서 상세 /documents/[id] 재구성 (BE 무변경):
- 우측 탭(정보/AI/관리) → 슬림 전역 인사이트 레일: 요약·심층·불일치를 탭
  게이트 없이 상시 노출(details open, 모바일은 접기 가능), 정보/관리는 접이.
  → 가공 자료가 탭/온디맨드에 묻히던 IA 문제(G1) 해소.
- SectionOutline 절 목차 레벨 기반 들여쓰기(평탄→트리 모양).
- 모바일: 본문 메인 + 절목차/인사이트/정보/관리 접이 + 절 탭 본문 이동(기존 구조 활용).
관련 문서(See Also)는 v1 제외(자리만 유지). 심화 목업 = comparisons/2026-06-13-ds-docpage-d3-deepened.html.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 15:18:15 +09:00
hyungi 30c235e4c1 Merge feat/safety-library-a1 (C-1 후속 version_status+facets) into ds-board-merged
검색 결과 wrapper decoration: 법령 version_status + facets 집계(ranking 무관·additive).
2026-06-13 15:08:24 +09:00
hyungi 8a3bea6b31 feat(safety): C-1 후속 — version_status decorate + facets 집계
검색 엔드포인트 wrapper decoration(run_search 코어 무접촉·ranking 무관):
- version_status: 법령 결과(material_type=law)에 legal_meta.version_status 부착
  (decorate_version_status, law 무결과 시 query skip). SearchResult.version_status 신설.
- facets=true: top-K 결과 분류 축(material_type/jurisdiction/version_status) 분포 라벨
  (compute_facets). 미요청=None(byte 불변). SearchResponse.facets 신설.
- result_decorate.py 신설. 단위 4건.
freshness incident 변경(law_365d 제거+흡수)=ranking 변경이라 별 슬라이스 defer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 15:07:57 +09:00
hyungi cd439b0ff4 Merge feat/safety-library-a1 (B-4 licensed_restricted + watch 폴더 license) into ds-board-merged
B-4 PR①②: licensed_restricted 단일 술어(retrieval 3-leg/digest/briefing/study 풀이 공유)
+ file_watcher Books/Manuals/Papers_Purchased license 주입. prod 통합 브랜치 배포용.
2026-06-13 14:53:34 +09: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 595f4b7d5e feat(board): 통합 보드 v3 — 머신 레인 + 정직 번다운/ETA (B-1·2·3·5)
ProcessingFlowBoard 를 통합안으로 재작성:
- 머신 3레인(GPU/맥미니/맥북) = "누가 일하나" + 요약 오프로드 가시화
  (요약 칩 분담 막대 맥미니 vs 맥북 + 맥북 레인 '요약 합류' 칩, summarize_by_machine 소비)
- 지배 백로그 스트립 + 정직 ETA(summarize_eta, 유입 차감 / null=소진 불가)
- 24h 번다운 SVG(유입 vs 소화) + 맥북 합류 변곡점 + 단계별 정직 ETA 미니리스트
- 신선도 '갱신 N초 전' + stale 경고(queueUpdatedAt, B-4)
- 실패 드로어 + 노드 상세 패널은 v2 자산 그대로 재사용
- 레인 stack + 칩 wrap 으로 모바일 반응형

svelte-check: 변경 파일 에러 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 14:01:50 +09:00
hyungi b630c31077 feat(board): expose summarize_by_machine for offload visibility (A-1)
요약 풀의 머신별 완료 실적(맥미니 vs 맥북)을 /api/queue/overview 응답에
summarize_by_machine 로 노출. rows_to_summarize_split 이 이미 계산하던 값의
additive 투영 — 신규 수집 SQL/마이그 0. 통합 보드 레인의 오프로드 가시화
(맥북이 요약 86% 처리) 재료. + FE 타입 동기 + store 신선도 timestamp(B-4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 13:54:39 +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 60cb48bbe4 fix(safety): C-1 fusion 재구성 시 분류 축 메타 전파 — 3 SearchResult 재생성 지점
fusion legacy(line 66)·RRF(122)·multi_query rewrite(pipeline 456)가 명시 필드 나열로
SearchResult 재구성 → material_type/jurisdiction/published_date 누락(필터는 정확, D-1
유형 표시만 None). 세 지점 동기화. 흉터: SearchResult 필드 추가 시 재구성 지점 전수 동기 필요.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 13:03:27 +09:00
hyungi 79deae0644 feat(safety): C-1 검색 명시 필터 — material_type/jurisdiction/year 3-leg 동등 + documents exclude 해제
plan safety-library-1 C-1 (검색 핵심 경로 — byte 불변 invariant):
- AxisFilter + _axis_sql 헬퍼: 미지정 시 모든 SQL 절 빈 문자열(run_eval 회귀 0 보장)
- 3 leg 동등 적용: search_text(JOIN 후 WHERE) / _search_vector_docs(prod+cand) /
  _search_vector_chunks(★inner topk JOIN — R6 결정: outer post-filter면 ANN top-k 후
  좁은 필터 후보 붕괴. 미지정 시 JOIN 없음=byte 불변)
- SearchResult + material_type/jurisdiction/published_date (3 leg SELECT additive)
- year = COALESCE(published_date, created_at) (freshness 동일 사상)
- GET /documents/: material_type 지정 시 기본 exclude(news·law_monitor·note) 해제
- _axis_sql 단위 테스트 PASS (미지정=빈문자열+param0 / active 4절 / alias 분기)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:59:18 +09:00
hyungi 9a7e231dcc fix(safety): verify_statute_chain sys.path — /app 루트 자동 탐지 (workers import)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:44:58 +09:00
hyungi 1646617a31 feat(safety): B-1 PR③ — 법령 체인 검증 3술어 스크립트 (read-only 진단)
plan safety-library-1 B-1 PR③. E-1 법령 게이트 도구 겸용 (반복 실행 안전):
- ① 존재성: watch family 각 primary current 정확 1건 + annex 시리즈당 ≤1
- ② 노출 유일성: primary current 보유 family당 노출 1건 (③a에 흡수)
- ③ 고아 그물: 정규화 동등 매핑 — flip 누락(current family 노출 레거시)·무매핑(매핑 구멍) 0
- repealed family ①② 면제. 종료코드 0/1 (관찰 게이트용)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:44:25 +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