Hyungi Ahn
0c63c0b6ab
feat(ui): Phase B — sidebar drawer + SystemStatusDot + 키보드 nav
...
- +layout.svelte: 햄버거 → IconButton, 우측 nav → Button ghost,
sidebar overlay → Drawer (uiState 단일 slot),
Esc 글로벌 핸들러 ui.handleEscape() 위임 (5대 원칙 #2 )
- lib/stores/system.ts (신규): dashboardSummary writable + 60s 폴링,
단일 fetch를 SystemStatusDot(B)와 dashboard(C)가 공유
- SystemStatusDot.svelte (신규): 8px 도트 + tooltip,
failed > 0 → error / pending > 10 → warning / 그 외 → success
- Sidebar.svelte: 트리에 ArrowUp/Down 키보드 nav,
활성 도메인 row에 aria-current="page"
2026-04-07 13:52:24 +09:00
Hyungi Ahn
a4eb71d368
feat(search): Phase 1.1a 모듈 분리 — services/search/ 디렉토리
...
검색 로직을 services/search/* 모듈로 분리. trigram 도입은 Phase 1.2 인덱스와 함께.
신규:
- services/search/{__init__,retrieval_service,rerank_service,query_analyzer,evidence_service,synthesis_service}.py
- retrieval_service는 search_text/search_vector 이전 (ILIKE 동작 그대로)
- 나머지는 Phase 1.3/2/3 placeholder
이동:
- services/search_fusion.py → services/search/fusion_service.py (R100)
수정:
- api/search.py — thin orchestrator로 축소 (251줄 → 178줄)
동작 변경 없음 — 구조만 분리. 회귀 검증 후 Phase 1.2 진입.
2026-04-07 13:46:04 +09:00
Hyungi Ahn
e0f45f9ce0
fix(deploy): primary endpoint 8801로 갱신
...
ai-gateway 환경 변수 PRIMARY_ENDPOINT를 8800 → 8801로 갱신.
mlx-proxy 경유 라우팅에 맞춰 정합성 확보.
2026-04-07 13:26:55 +09:00
Hyungi Ahn
378fbc7845
feat(chunk): Phase 0.1 chunk 인덱싱 — ORM/worker/migration 정리
...
GPU 서버에 untracked로만 존재하던 Phase 0.1 코드를 정식 commit:
- app/models/chunk.py — DocumentChunk ORM (country/source/domain 메타 포함)
- app/workers/chunk_worker.py — 6가지 chunking 전략 (legal/news/markdown/email/long_pdf/default)
- migrations/014_document_chunks.sql — pgvector + FTS + trigram 인덱스
- app/models/queue.py — ProcessingQueue enum에 'chunk' stage 추가
- app/workers/queue_consumer.py — chunk stage 등록, classify→[embed,chunk] 자동 연결
Phase 1 reranker 통합 작업의 전제 조건. document_chunks 테이블 기반 retrieval에 사용.
2026-04-07 13:26:37 +09:00
Hyungi Ahn
a2941487fe
fix(documents): detail view에 raw markdown fetch fallback 추가
...
A-8 작전 후 사용자 보고: 마크다운 전체보기에서 "텍스트 추출 대기 중"
fallback이 뜨는 문서가 있음.
원인: split view의 DocumentViewer는 extracted_text 없으면 원본 .md
파일을 fetch해서 보여주는데, detail view (routes/documents/[id]/+page.svelte)
는 fetch fallback이 없어 즉시 fallback 메시지로 떨어짐. 두 view의 동작
불일치가 A-8 작업 중 사용자 시각 검증 과정에서 드러남.
A-8 회귀 아님 — 이 페이지는 routes 잔존 그룹(36 hits)이라 A-8 batch에서
한 줄도 변경 안 됨 (git diff fcce764..c294df5로 검증).
해결: DocumentViewer와 동일한 fetch fallback 로직을 detail view에도 추가.
fallback 우선순위:
1. doc.extracted_text 있으면 사용
2. 없으면 raw markdown fetch 시도
3. 둘 다 없으면 "*텍스트 추출 대기 중*" 메시지
scope:
- script onMount: vt가 markdown/hwp-markdown이고 extracted_text 없으면
/api/documents/{id}/file fetch
- template: renderMd fallback chain에 rawMarkdown 추가
routes 색상 토큰 swap (이 페이지의 36 hits)은 별도 이슈 — Phase D에서
정식 처리. 본 hotfix는 콘텐츠 표시 문제만 해결.
2026-04-07 12:55:59 +09:00
Hyungi Ahn
c294df5987
refactor(tokens): A-8 Batch 3 — PreviewPanel / DocumentViewer / +layout(toast)
...
가장 큰 위험 batch. Phase A 디자인 시스템 정착 마지막 mechanical refactor
(8 파일 8/8 누적 — core components 0 hit 달성).
PreviewPanel (53 hits → 0):
- bg-[var(--sidebar-bg)] → bg-sidebar (메인 aside)
- bg-[var(--bg)] → bg-bg (input 배경)
- bg-[var(--surface)] → bg-surface (hover)
- bg-[var(--accent)] → bg-accent + hover:bg-accent-hover (저장 버튼)
- bg-[var(--error)] → bg-error (삭제 확인)
- text/border 토큰 일괄 swap
- focus:border-accent (input)
- confidence 색상 (green/amber/red palette)은 plan B3 명시 없어 그대로
DocumentViewer (28 hits → 0):
- 뷰어 본체 bg-surface border-default
- 툴바 bg-sidebar
- 마크다운 편집 탭 bg-surface, edit textarea bg-bg
- 상태별 hover 토큰 swap
- 뉴스 article 태그 blue-900/30 그대로 (lint:tokens 미검출)
+layout.svelte (10 hits → 0):
- nav 잔여 var() (햄버거, 로고, 메뉴 링크) 토큰 swap
- 로딩 텍스트 text-dim
- toast 영역 의미 swap (plan B3 명시):
* green-900/200 → bg-success/10 + text-success + border-success/30
* red-900/200 → bg-error/10 + text-error + border-error/30
* yellow-900/200 → bg-warning/10 + text-warning + border-warning/30
* blue-900/200 → bg-accent/10 + text-accent + border-accent/30
- class:* 디렉티브 8개 → script TOAST_CLASS dict + dynamic class binding
(svelte 5에서 슬래시 포함 클래스명을 class: 디렉티브로 못 씀)
검증:
- npm run lint:tokens : 360 → 269 (-91, B3 파일 0 hit)
- 누적 진행: 421 → 269 (-152 / 8 파일 완료, plan 정정 목표 정확 달성)
- npm run build : ✅
- npx svelte-check : ✅ 0 errors
- ⚠ 3-risk grep : hover/border-border/var() 잔여 0건
A-8 종료 시점 상태:
- core components 8 파일: lint:tokens 0 hit ✅
- routes 7 파일 잔존 (~269): news 92, settings 47, documents/[id] 36,
+page 28, documents 26, inbox 25, login 15
- lint:tokens 강제화 (pre-commit hook)는 Phase D + F 완료 후 별도 commit
플랜: ~/.claude/plans/compressed-churning-dragon.md §A.4 Batch 3
2026-04-07 12:14:48 +09:00
Hyungi Ahn
8ec89517ee
refactor(tokens): A-8 Batch 2 — Sidebar / DocumentCard / DocumentTable
...
목록/사이드바 영역의 var() 토큰을 의미 토큰으로 swap. Phase A 디자인
시스템 정착의 두 번째 mechanical refactor batch (8 파일 중 5/8 누적).
Sidebar:
- bg-[var(--sidebar-bg)] → bg-sidebar (이름 변경)
- border-[var(--border)] → border-default
- text-[var(--text)] → text-text
- text-[var(--text-dim)] → text-dim
- bg-[var(--accent)]/15 → bg-accent/15
- hover:bg-[var(--surface)] → hover:bg-surface
- domain 색상 inline style (DOMAIN_COLORS)은 그대로 유지
DocumentCard:
- bg/border/text/hover 토큰 일괄 swap
- DOMAIN_COLORS의 var(--domain-*) 유지 (plan B2 비고)
- blue-400/blue-900/30 (news icon, data_origin work) 그대로
(lint:tokens 미검출 + plan 명시 없음)
DocumentTable:
- 헤더 + 행 + selected 상태 + 컬럼 텍스트 일괄 swap
- border-l-[var(--accent)] → border-l-accent
- border-default/30 opacity suffix (행 구분선) v4 시각 검증 필요
검증:
- npm run lint:tokens : 407 → 360 (-47, B2 파일 0 hit)
- npm run build : ✅
- npx svelte-check : ✅ 0 errors
- ⚠ 3-risk grep : hover/border-border/var() 잔여 0건
플랜: ~/.claude/plans/compressed-churning-dragon.md §A.4 Batch 2
2026-04-07 12:04:37 +09:00
Hyungi Ahn
451c2181a0
refactor(tokens): A-8 Batch 1 — TagPill / UploadDropzone
...
색상 시스템을 의미 토큰으로 swap. Phase A 디자인 시스템 정착의 첫
mechanical refactor batch (8 파일 중 2 파일).
TagPill: 4가지 prefix별 색상을 의미 토큰화
- @상태/ amber → warning
- #주제/ blue → accent
- $유형/ green → success
- !우선순위/ red → error
- fallback bg-[var(--border)] → bg-default,
text-[var(--text-dim)] → text-dim
UploadDropzone: 드래그 오버레이 + 업로드 진행 영역
- bg-[var(--accent)]/10 → bg-accent/10
- bg-[var(--surface)] → bg-surface
- border-[var(--border)] → border-default
- text-[var(--text-dim)] → text-dim
- 상태별 텍스트: text-success / text-error / text-accent / text-dim
검증:
- npm run lint:tokens : 421 → 407 (-14, B1 파일 0 hit)
- npm run build : ✅
- npx svelte-check : ✅ 0 errors
- ⚠ 3-risk grep : hover/border-border/var() 잔여 0건
플랜: ~/.claude/plans/compressed-churning-dragon.md §A.4 Batch 1
참고: 본 plan은 161ff18(search Phase 0.5 commit)에 styleguide 2개 파일이
의도와 다르게 묶여 main에 들어왔음. 기능 영향 0 — Option A 결정으로
commit history 미수정.
2026-04-07 11:44:29 +09:00
Hyungi Ahn
fcce764e9d
chore: pre A-8 token swap snapshot
2026-04-07 09:39:45 +09:00
Hyungi Ahn
6b2747de96
chore: allow /__styleguide in dev public paths
...
A-9 styleguide 라우트가 dev 환경에서 auth gate를 우회할 수 있도록
PUBLIC_PATHS / NO_CHROME_PATHS에 /__styleguide 추가.
production 영향 0 — +page.ts의 dev 가드가 비-dev 환경에서는 /로
redirect하므로 styleguide 라우트 자체에 도달 못 함.
A-8 토큰 swap 작전과 의미적으로 무관한 dev-only 변경이라
revert 단위 분리를 위해 단독 commit.
2026-04-07 09:39:39 +09:00
Hyungi Ahn
8021a1debd
test(search): Phase 0.5 fusion 전략 A/B 비교 결과
...
23개 평가셋 × 3 전략(legacy/rrf/rrf_boost) 측정 + 분석.
핵심 발견:
- 전체 NDCG: legacy 0.705 → rrf 0.699 → rrf_boost 0.700 (미세 차이)
- RRF가 약간 나쁜 이유: kw_001(산업안전보건법 제6장)에서 RRF가 4041
(근로기준법 안전과 보건)을 false positive로 promotion. NDCG 1.000→0.906.
- boost가 가치 입증한 사례: news_004(guerre en Iran)에서 RRF의 미스를
완벽 보정해 legacy NDCG 복원.
- RRF의 진짜 가치는 Phase 1+ 다중 신호(trigram, reranker, multi-query)
통합 시 발휘됨. 현 평가셋은 너무 단순해서 차이가 noise에 묻힘.
결정: rrf_boost를 default로 유지. Phase 1 후 재측정.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 09:25:49 +09:00
Hyungi Ahn
161ff18a31
feat(search): Phase 0.5 RRF fusion + 강한 신호 boost
...
기존 weighted-sum merge를 Reciprocal Rank Fusion으로 교체.
정확 키워드 매치에서 RRF가 평탄화되는 문제는 boost로 보완.
신규 모듈 app/services/search_fusion.py:
- FusionStrategy ABC
- LegacyWeightedSum : 기존 _merge_results 동작 (A/B 비교용)
- RRFOnly : 순수 RRF, k=60
- RRFWithBoost : RRF + title/tags/법령조문/high-text-score boost (default)
- normalize_display_scores: SearchResult.score를 [0..1] 랭크 기반 정규화
(프론트엔드가 score*100을 % 표시하므로 RRF 원본 점수 노출 시 표시 깨짐)
search.py:
- ?fusion=legacy|rrf|rrf_boost 파라미터 (default rrf_boost)
- _merge_results 제거 (LegacyWeightedSum에 흡수)
- pre-fusion confidence: hybrid는 raw text/vector 신호로 계산
(fused score는 fusion 전략마다 스케일이 달라 일관 비교 불가)
- timing에 fusion_ms 추가
- debug notes에 fusion 전략 표시
telemetry:
- compute_confidence_hybrid(text_results, vector_results) 헬퍼
- record_search_event에 confidence override 파라미터
run_eval.py:
- --fusion CLI 옵션, call_search 쿼리 파라미터에 전달
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:58:33 +09:00
Hyungi Ahn
1af94d1004
fix(search): timing 로그를 setup_logger로 출력
...
logging.getLogger("search")만 사용하면 uvicorn 기본 설정에서 INFO가
stdout에 안 나옴. 기존 core.utils.setup_logger 패턴 사용:
- logs/search.log 파일 핸들러
- stdout 콘솔 핸들러
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:43:26 +09:00
Hyungi Ahn
473e7e2e6d
feat(search): Phase 0.4 debug 응답 옵션 + timing 로그
...
?debug=true로 호출 시 단계별 candidates + timing을 응답에 포함.
디버그 옵션과 별개로 모든 검색에 timing 라인을 구조화 로그로 출력
(사용자 feedback: 운영 관찰엔 debug 응답만으론 부족).
신규 응답 필드 (debug=true 시):
- timing_ms: text_ms / vector_ms / merge_ms / total_ms
- text_candidates / vector_candidates / fused_candidates (top 20)
- confidence (telemetry와 동일 휴리스틱)
- notes (예: vector 검색 실패 시 fallback 표시)
- query_analysis / reranker_scores: Phase 1/2용 placeholder
기본 응답(debug=false)은 변화 없음 (results, total, query, mode).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:41:33 +09:00
Hyungi Ahn
e104d1b47c
feat: Layer 프리미티브 (Drawer / Modal / ConfirmDialog / Tabs)
...
UX/UI 개편 Phase A-7. uiState와 결합한 layer/dialog 컴포넌트.
신규 컴포넌트 (lib/components/ui/)
- Drawer.svelte: 단일 slot drawer (id: 'sidebar' | 'meta').
ui.isDrawerOpen(id)로 표시 여부 결정. 새 drawer 열면 이전 drawer 자동 close.
side(left/right) + width(sidebar/rail). backdrop 클릭으로 close.
z-drawer 사용. 8대 원칙 #2 .
- Modal.svelte: stack 지원 modal (5대 원칙 #2 — confirm 위에 nested 가능).
native <dialog> 대신 div 기반 — top-layer가 단일이라 <dialog>로는 stack 불가.
z-index = z-modal + (stackIndex * 2): backdrop과 panel을 별개의 stacking
context로 두기 위해 *2. 최상단 modal만 focus trap + 키보드 nav 활성,
아래는 inert 처리. 수동 Tab/Shift+Tab cycling.
closable + IconButton(X) 헤더, footer snippet 지원.
- ConfirmDialog.svelte: Modal 위 얇은 wrapper. 삭제/되돌릴 수 없는 작업에
사용. tone(danger/primary), confirmLabel/cancelLabel, onconfirm 콜백.
ui.openModal(id)로 호출.
- Tabs.svelte: ARIA tablist + tab + tabpanel.
좌우 화살표 / Home / End 키 nav, \$props.id() 기반 SSR-safe ID.
tabs: { id, label, disabled? }[], value \$bindable.
children snippet은 (activeId) => UI 시그니처 — DocumentViewer 편집/미리보기
토글 등 단일 컨테이너 레이아웃에 쓰기 좋게 설계.
이로써 Phase A 프리미티브 13종 완비:
Button, IconButton, Card, Badge, Skeleton, EmptyState,
TextInput, Textarea, Select,
Drawer, Modal, ConfirmDialog, Tabs.
모든 컴포넌트는 Svelte 5 runes mode strict, @theme 토큰만 사용,
focus-visible ring 통일, slot은 {@render children?.()}로 작성.
svelte-check: 0 errors / 8 warnings (전부 기존 latent, 새 코드 무관)
build: 2.07s 무경고
남은 Phase A:
- A-8 토큰 swap (Sidebar/TagPill/UploadDropzone/PreviewPanel/DocumentCard/
DocumentTable/+layout toast — baseline 421건 → 0건)
- A-9 __styleguide 라우트 (전체 시각 검증 + Modal stack 데모)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:40:08 +09:00
Hyungi Ahn
ad23925ed5
feat: 입력 프리미티브 (TextInput / Textarea / Select) + tsconfig 보정
...
UX/UI 개편 Phase A-6.
신규 컴포넌트 (lib/components/ui/)
- TextInput.svelte: \$bindable value, label/error/hint, leading/trailing icon,
\$props.id() 기반 SSR-safe 자동 id, aria-describedby 자동 연결.
- Textarea.svelte: TextInput과 동일 구조 + autoGrow 옵션
(\$effect로 scrollHeight 동기화, maxRows 지원).
- Select.svelte: 네이티브 <select> 래퍼, ChevronDown 표시.
options: { value, label, disabled? }[]
빌드 환경 보정
- frontend/tsconfig.json 신규: svelte-kit 자동 생성 .svelte-kit/tsconfig.json을
extends. 이게 없으면 svelte-check가 \$lib path mapping과 .svelte.ts 모듈
resolution을 못 잡아 "Cannot find module" 에러 발생. SvelteKit 표준 패턴.
strict는 false로 시작 (기존 코드 implicit any 다수 — 점진적 정리 예정).
- Button/IconButton/EmptyState/TextInput의 icon prop 타입을 IconComponent(any)로
완화. lucide-svelte v0.400은 legacy SvelteComponentTyped 기반이라 Svelte 5의
Component<P, E, B> 시그니처와 호환 안 됨. v0.469+ 업그레이드 후 좁힐 예정.
svelte-check: 0 errors / 8 warnings (전부 기존 latent, 새 코드 무관)
build: 2.07s 무경고
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:39:44 +09:00
Hyungi Ahn
70b27d4a51
fix(search): confidence 임계값 완화 + hybrid +vector boost 가산
...
baseline 평가셋 실행 시 'summary+vector' top_score 2.39가 임계값 2.5에
미달해 정답 쿼리(산업안전보건법 제6장)가 low_confidence로 잘못 잡힘.
- 텍스트 매치 임계값 0.5씩 완화 (실측 분포 반영)
- '+vector' 접미사가 있으면 hybrid 합성 매치이므로 confidence +0.10 가산
- 정답률 5/5 → 4/5 false-positive 1건 제거 기대
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:37:13 +09:00
Hyungi Ahn
50e6b5ad90
fix(search): confidence 휴리스틱 vector-only amplify 버그 수정
...
vector-only 매치(match_reason == 'vector')에서 raw 코사인 0.43이
0.6으로 잘못 amplify되어 low_confidence threshold(0.5)를 못 넘기던 문제.
- vector-only 분기: amplify 제거, _cosine_to_confidence로 일관 환산
- _cosine_to_confidence: bge-m3 코사인 분포 (무관 텍스트 ~0.4) 반영
- 코사인 0.55 = threshold 경계(0.50), 0.45 미만은 명확히 low
smoke test 결과 zzzqxywvkpqxnj1234 같은 무의미 쿼리(top cosine 0.43)가
low_confidence로 잡히지 않던 문제 해결.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:33:25 +09:00
Hyungi Ahn
f005922483
feat(search): Phase 0.3 검색 실패 자동 로깅
...
검색 실패 케이스를 자동 수집해 gold dataset 시드로 활용.
wiggly-weaving-puppy 플랜 Phase 0.3 산출물.
자동 수집 트리거 (3가지):
- result_count == 0 → no_result
- confidence < 0.5 → low_confidence
- 60초 내 동일 사용자 재쿼리 → user_reformulated (이전 쿼리 기록)
confidence는 Phase 0.3 휴리스틱 (top score + match_reason).
Phase 2 QueryAnalyzer 도입 후 LLM 기반으로 교체 예정.
구현:
- migrations/015_search_failure_logs.sql: 테이블 + 3개 인덱스
- app/models/search_failure.py: ORM
- app/services/search_telemetry.py: confidence 계산 + recent 트래커 + INSERT
- app/api/search.py: BackgroundTasks로 dispatch (응답 latency 영향 X)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:29:12 +09:00
Hyungi Ahn
7fa7dc1510
feat: 디자인 시스템 기반 — 유틸 헬퍼 + CI rule + 첫 6개 프리미티브
...
UX/UI 개편 Phase A-3 / A-4 / A-5. 후속 phase가 곧바로 소비할 수 있도록
디자인 시스템의 코어 자산을 한꺼번에 도입한다.
A-3 — 유틸 헬퍼 (lib/utils/)
- pLimit.ts: 동시 실행 N개 제한 (5대 원칙 #4 — 일괄 PATCH/DELETE에서
GPU 서버/SSE 부하 방지). 외부 의존성 없음.
- mergeDoc.ts: PATCH/SSE 응답을 로컬 cache에 머지할 때 updated_at으로
stale 갱신 차단 (5대 원칙 #6 — optimistic update conflict resolution).
dropDoc 헬퍼 포함.
A-4 — CI 토큰 차단 (5대 원칙 #1 )
- scripts/check-tokens.sh: bg-[var(--*)] 등 임의값 토큰 우회 grep 차단.
- npm run lint:tokens 등록.
- 현재 baseline 421건 — A-8 토큰 swap에서 0으로 떨어진 후 pre-commit 강제화.
A-5 — 첫 6개 프리미티브 (lib/components/ui/)
- Button.svelte: variant(primary/secondary/ghost/danger) × size(sm/md),
loading/disabled, icon 슬롯, href 자동 a 변환, focus-visible ring.
- IconButton.svelte: 정사각형, aria-label 필수, Button과 동일 variant 체계.
- Card.svelte: bg-surface + rounded-card + border-default 패턴 1군데화.
padded/interactive 옵션, interactive면 button 시맨틱.
- Badge.svelte: 의미적 tone(neutral/success/warning/error/accent) 표시.
TagPill과 별개 (TagPill은 도메인 prefix 코드 전용).
- Skeleton.svelte: ad-hoc animate-pulse div 통합. w/h/rounded prop.
- EmptyState.svelte: icon + title + description + action slot.
모든 프리미티브는 Svelte 5 runes mode strict (\$props/\$derived/\$bindable),
@theme 토큰만 사용 (bg-surface, text-dim, border-default 등 — bg-[var(--*)] 미사용),
focus-visible ring 통일, slot은 {@render children?.()}로 작성.
svelte-check: 0 errors, 8 warnings (모두 기존 latent 이슈, 새 코드 무관).
build: 1.95s 무경고.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:26:35 +09:00
Hyungi Ahn
8742367bc2
refactor: stores 분리 — toast / uiState 단일 책임화
...
UX/UI 개편 Phase A-2. lib/stores/ui.ts에 섞여 있던 toast 시스템과
UI layer 상태(미사용 dead export 포함)를 의미 단위로 분리한다.
한 파일이 비대해지는 시나리오를 처음부터 차단(plan 8대 원칙 #7 ).
- lib/stores/toast.ts 신규 — toasts/addToast/removeToast (Toast interface export)
- lib/stores/uiState.svelte.ts 신규 — drawer 단일 slot + modal stack 클래스 (5대 원칙 #2 )
· openDrawer/closeDrawer/isDrawerOpen
· openModal/closeTopModal/isModalOpen/modalIndex/topModal
· handleEscape (modal stack 우선 → drawer)
- lib/stores/ui.ts 삭제 — sidebarOpen/selectedDocId는 어디서도 import되지 않은 dead export였음
- 11개 파일 import 경로 갱신: \$lib/stores/ui → \$lib/stores/toast
uiState는 아직 어디서도 사용 안 함 — Phase B에서 sidebar/meta drawer가 전환될 때
ui.openDrawer('sidebar') 형태로 채택. 동작 변경 0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:26:11 +09:00
Hyungi Ahn
ec36ea3d6d
test(search): Phase 0.2 baseline 측정 결과
...
23개 쿼리에 대한 현재 검색(FTS+ILIKE+Vector hybrid) baseline.
Phase 1+ 개선 비교 기준점으로 보존.
전체: Recall@10 0.788 / NDCG@10 0.705 / Top-3 0.95 / p95 1695ms
핵심 약점 (Phase 1+ 타겟):
- news_crosslingual catastrophic (Recall 0.14) → domain-aware 필수
- failure-case precision 0/3 → confidence threshold 부재
- p95 1695ms (목표 500ms의 3배) → trigram/parallel retrieval
- nl 쿼리 top-3 ordering 약함 → chunk-level + reranker
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:22:53 +09:00
Hyungi Ahn
8490cfed10
test(search): Phase 0.2 평가셋 + 평가 스크립트
...
22개 쿼리(6개 카테고리)와 Recall/MRR/NDCG@10 + latency p50/p95
측정 스크립트 추가. wiggly-weaving-puppy 플랜 Phase 0.2 산출물.
- queries.yaml: 정확키워드/한국어자연어/crosslingual/뉴스/실패 케이스
실제 코퍼스(2026-04-07, 753 docs) 기반 정답 doc_id 매핑
- run_eval.py: 단일 평가 + A/B 비교 모드, CSV 저장
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:19:38 +09:00
Hyungi Ahn
f523752971
feat: Tailwind v4 @theme 토큰 도입 — 디자인 시스템 기반 마련
...
UX/UI 개편 Phase A-1. CSS 변수를 Tailwind 유틸리티로 노출해서
이후 컴포넌트가 bg-surface / text-dim / border-default 형태로 작성될
수 있도록 한다. bg-[var(--*)] 임의값 패턴은 후속 lint 규칙으로 차단 예정.
- app.css에 @theme 블록 추가 (color/radius/z/spacing/domain 토큰)
- 기존 :root 변수는 .markdown-body 호환 위해 공존 유지
- +layout.svelte nav 한 줄 swap으로 v4 빌드/HMR 인식 검증 (동일 색상값)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-07 08:15:33 +09:00
Hyungi Ahn
fc50008843
feat: 뉴스 페이지 모바일 최적화 — 데스크톱/모바일 공존
...
데스크톱: 사이드바 필터 + 하단 미리보기 (기존 유지)
모바일: 드롭다운 필터 + 전체화면 미리보기 + 하단 원문 버튼
- body scroll lock (모바일 전체화면 시)
- 스크롤 위치 복원
- active 터치 피드백
- 안읽음 건수 표시
- 페이지네이션 10개 제한
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:56:33 +09:00
Hyungi Ahn
db34a06243
feat: 뉴스 미리보기 — AI 요약 상단 + 본문/메모 분리
...
- AI 요약: 파란 박스로 상단에 별도 표시
- 본문 입력: extracted_text에 추가 (기사 전문 붙여넣기)
- 메모: user_note에 저장 (개인 메모)
- 기사 선택 시 편집 상태 초기화
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:42:33 +09:00
Hyungi Ahn
e10b0f2883
fix: 뉴스 분야 필터 — file_path 폴더명 기반 매칭
...
경향신문/문화 → file_path LIKE 'news/경향신문 문화/%'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:27:01 +09:00
Hyungi Ahn
8bb2ea4f29
fix: 뉴스 필터 트리 — 신문사명 정확 추출 + API datetime 수정
...
- PAPER_NAMES 매핑으로 'Le Monde', 'Der Spiegel' 등 정확 분리
- NewsSourceResponse datetime 타입 수정
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:18:50 +09:00
Hyungi Ahn
3cd65e4c26
fix: 사이드바 트리에서 News 제외 + 뉴스 페이지 ☰ 숨김
...
- tree API: ai_domain != 'News' 필터
- +layout: /news 경로에서 사이드바 토글 버튼 숨김
- DB: 뉴스 ai_sub_group을 신문사명으로 재설정
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:15:15 +09:00
Hyungi Ahn
557165db11
feat: 뉴스 필터 트리 (신문사 → 분야) + ai_summary 우선 표시
...
- 좌측 필터: 신문사 펼침 → 분야별 필터 (News/경향신문/문화)
- API: source 파라미터 '신문사' 또는 '신문사/분야' 지원
- 리스트: ai_summary 있으면 우선, 없으면 extracted_text fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:08:50 +09:00
Hyungi Ahn
2eeed41f5c
fix: @const 위치 에러 수정
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:06:29 +09:00
Hyungi Ahn
be20edd0cd
fix: 뉴스 리스트 — ai_summary 우선 표시 (없으면 extracted_text fallback)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 15:05:46 +09:00
Hyungi Ahn
49cc86db80
feat: summarize 전용 stage — 뉴스 AI 요약 (classify 없이)
...
- 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 >
2026-04-06 15:00:14 +09:00
Hyungi Ahn
4f7cd437f5
feat: 뉴스 리스트에 RSS 요약 1줄 표시 + 상세 링크 현재 탭
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:52:57 +09:00
Hyungi Ahn
7d6b5b92c0
fix: 뉴스 페이지네이션 리셋 버그 + 상세 링크 새 탭
...
- $effect에서 필터 변경 시에만 page 리셋 (페이지 클릭과 충돌 방지)
- 상세 링크 → 새 탭으로 열기
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:47:27 +09:00
Hyungi Ahn
ef6f857a6d
fix: 뉴스 페이지 — 닫기 버튼 + 페이지네이션 + 상세 링크 + 본문 입력
...
- 미리보기 닫기 버튼 추가
- 페이지네이션 (30건 단위)
- "상세" 링크 → /documents/{id}
- "본문/메모 입력" → user_note 저장
- DocumentUpdate에 is_read 필드 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:38:42 +09:00
Hyungi Ahn
7ca3abf17c
feat: 뉴스 전용 페이지 + 분류 격리 + 읽음 상태
...
- /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 >
2026-04-06 14:16:00 +09:00
Hyungi Ahn
cd5f1c526d
fix: 상세 페이지에도 뉴스 전용 뷰어 적용 (source_channel=news → article)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 14:01:39 +09:00
Hyungi Ahn
2b457a8305
feat: 뉴스 전용 뷰어 + 카드 구분 + 설정 UI
...
- DocumentViewer: source_channel=news → article 전용 뷰어
(제목/소스/날짜/요약/원문 링크 rel=noopener)
- DocumentCard: 뉴스 카드에 📰 아이콘
- settings: 뉴스 소스 관리 (목록/추가/삭제/토글/수집/마지막 시간)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 13:55:49 +09:00
Hyungi Ahn
d03fa0df37
fix: source_channel enum에 'news' 추가 (ORM 누락)
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 13:41:20 +09:00
Hyungi Ahn
a6c19ef76c
feat: 뉴스 자동 수집 시스템 — 6개국 신문 RSS/API
...
- news_sources 테이블 (소스 관리, UI 동적 제어)
- news_collector 워커: RSS(feedparser) + NYT API
- 중복 체크: hash(title+date+source) + URL normalize
- category 표준화, summary HTML 정제, timezone UTC
- 30일 이내만 embed, source별 try/catch
- News API: 소스 CRUD + 수동 수집 트리거
- APScheduler: 6시간 간격 자동 수집
- 대상: 경향/아사히/NYT/르몽드/신화/슈피겔
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 13:38:07 +09:00
Hyungi Ahn
bf8efd1cd3
feat: 임베딩 모델 변경 — nomic-embed-text → bge-m3 (1024차원, 다국어)
...
- config.yaml: embedding model → bge-m3
- document.py: Vector(768) → Vector(1024)
- embed_worker.py: 모델 버전 업데이트
- migration 011: 벡터 컬럼 재생성 (기존 임베딩 초기화)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:49:45 +09:00
Hyungi Ahn
204c5ca99f
fix: AI 요약 마크다운 렌더링 — 상세페이지는 렌더링, 카드는 기호 제거
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:36:29 +09:00
Hyungi Ahn
c885b5be27
fix: 3+4단계 — 반응형/에러분기/a11y/Synology URL
...
- DocumentCard: window.innerWidth → matchMedia (반응형 정확)
- documents/[id]: 로딩 상태 3분기 (loading/not_found/network)
- documents/[id]: Synology URL 하드코딩 → edit_url fallback
- DocumentCard: aria-label 추가
- Toast: aria-live 이미 적용 (1단계)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:24:32 +09:00
Hyungi Ahn
1b21d9bb53
feat: 2단계 — DEVONthink 스타일 테이블 뷰 + 카드/테이블 토글
...
- DocumentTable.svelte: 컬럼 정렬(stable sort), domain 색상 바, 포맷 아이콘
- 뷰 모드 토글 버튼 (카드 ↔ 테이블)
- localStorage로 뷰 모드 + 정렬 상태 기억
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:16:45 +09:00
Hyungi Ahn
3374eebfc6
fix: 프론트엔드 1단계 — XSS 수정 + Svelte 5 변환 + 필터/아이콘/a11y
...
- [critical] DOMPurify 적용 (FORBID_TAGS/ATTR, ALLOW_UNKNOWN_PROTOCOLS)
- [high] $: → $derived 변환 (documents/[id])
- [high] 태그/소스 필터 구현 (filterTag, filterSource)
- FormatIcon: docx/xlsx/pptx/odt/ods/odp/dwg/dxf 추가
- editTab 선언 순서 수정
- debounceTimer 미사용 변수 제거
- Toast role="status" aria-live 추가
- marked 옵션: mangle/headerIds false
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 12:15:02 +09:00
Hyungi Ahn
24142ea605
fix: Codex 리뷰 5건 수정 (critical 1 + high 4)
...
1. [critical] config.yaml → settings 객체에서 taxonomy 로드 (import crash 방지)
2. [high] ODF 변환: file_path 유지, derived_path 별도 필드 (무한 중복 방지)
3. [high] 법령 분할: 첫 장 이전 조문을 "서문"으로 보존
4. [high] Inbox: review_status 필드 분리 (pending/approved/rejected)
5. [high] 삭제: soft-delete (deleted_at) + worker 방어 + active_documents 뷰
- 모든 조회에 deleted_at IS NULL 일관 적용
- queue_consumer: row 없으면 gracefully skip
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-06 07:15:13 +09:00
Hyungi Ahn
6c92e375c2
feat: Markdown 뷰어/편집기 개선
...
- startEdit(): extracted_text || rawMarkdown fallback
- split editor → 편집/미리보기 탭 전환 방식
- GitHub Dark 스타일 markdown-body CSS (테이블/코드/인용/리스트)
- prose 클래스 → markdown-body로 교체
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 15:48:41 +09:00
Hyungi Ahn
06da098eab
fix: 법령 분할 — 조문키 000 기반 장(章) 단위 분할로 변경
...
국가법령 XML은 <편>/<장> 태그가 아닌 <조문단위 조문키="xxxx000">에
"제X장 ..." 형태로 장 구분자가 포함됨. 이를 파싱하여 분할.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 15:05:48 +09:00
Hyungi Ahn
749ed51dd7
fix: Markdown 뷰어 — extracted_text 없으면 원본 파일 직접 렌더링
...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com >
2026-04-03 14:55:51 +09:00