_validate_sql_content가 PL/pgSQL의 BEGIN을 트랜잭션 제어문으로 오탐.
guard check를 제거하고 CREATE UNIQUE INDEX 자체의 중복 실패에 의존.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
기존 UNIQUE(document_id, stage, status)는 pending+processing 동시 존재를
허용해서 stale 복구 시 충돌 발생. 2-layer 방어로 근본 차단:
1) DB: partial unique index uq_queue_active — 활성 행(pending/processing)은
(document_id, stage)당 최대 1개만 허용
2) App: enqueue_stage() 중앙 함수 — INSERT ON CONFLICT DO NOTHING으로
모든 9개 경로의 check-then-insert TOCTOU race 제거
migration 117은 guard check 포함 — 활성 중복이 남아있으면 RAISE EXCEPTION
으로 중단, 수동 정리 유도.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stale processing 행을 pending으로 bulk UPDATE 시 이미 같은
(document_id, stage, pending) 행이 존재하면 unique constraint 위반으로
APScheduler consume_queue 잡 전체가 크래시. 2-step 접근으로 변경:
1) pending 중복 있는 stale processing 행은 DELETE
2) 나머지만 pending으로 UPDATE
+ 예외 삼키기로 stale reset 실패가 전체 큐 소비를 죽이지 않게 방어
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
폴더 미선택 상태에서 업로드하면 doc_purpose='business'만 설정되고
@library/ 태그가 빠져서 자료실에 문서가 표시되지 않던 버그 수정.
백엔드: business 업로드에 library_path 없으면 @library/미분류 자동 태깅.
프론트: activePath 없을 때 기본값 '미분류' 전송.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
공통 유틸 memoRenderer.ts 분리 (drift 방지):
- checkbox regex 속성 순서 독립으로 수정 (버그 원인)
- due date: checkbox line 마지막 @YYYY-MM-DD만 badge 변환
overdue=빨강, soon(3일)=노랑, normal=dim, checked=dim
- toggleTaskLine: taskIndex 기반 안전한 토글
- 날짜 비교 로컬 기준 (TZ 이슈 회피)
메모 페이지:
- 렌더링/토글 공통 유틸 import
- 툴바에 📅 마감일 버튼 추가
대시보드:
- 핀 메모 체크박스 토글 가능 (optimistic + rollback)
- stopPropagation으로 details 토글 충돌 방지
- renderMdSimple → renderMemoHtml 통일
QuickMemoButton:
- 체크리스트 + 마감일 버튼 2개 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
asyncpg는 prepared statement에 여러 명령을 넣을 수 없음.
CREATE TYPE + ALTER TABLE을 단일 DO $$ 블록으로 합침.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
메모 입력/편집:
- 선택적 제목 토글 (기본 숨김, "제목" 버튼으로 활성화)
- 툴바 버튼: 체크리스트/굵게/제목 (모바일에서 마크다운 수동 입력 불필요)
- 편집 모드에도 동일 툴바
대시보드 핀 메모:
- 클릭 시 /memos 이동 대신 인라인 펼침/접힘 (details)
- 제목이 있으면 제목 표시, 없으면 첫 줄
- 펼치면 마크다운 렌더링된 본문 + "메모함에서 보기" 링크
Backend:
- MemoCreate/MemoUpdate에 선택적 title 파라미터 복원
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hover 기반 opacity가 모바일에서 동작하지 않아 편집/삭제/핀 등
액션 버튼 접근 불가. md 이상에서만 hover 숨김, 모바일은 항상 표시.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
대시보드를 통계판에서 상황판으로 전환:
- 헤더 + 시스템 상태 인라인 (비클릭)
- 핀 메모 최상단 조건부 (컴팩트 띠, 최대 3개)
- 카드 4개 (문서함/메모/뉴스/승인대기) 모바일 2×2
- 최근 활동 전체 너비 7건, 2줄 스캔형 + 법령 배지
- 파이프라인 details 접힘 (실패 시 자동 open)
- 제거: 도메인 분포, 법령/시스템 별도 카드, 8:4 분할
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이전 재설계에서 위젯을 과도하게 제거해 퇴화.
원래 12칸 그리드 + 8:4 2열 구조 복원하면서 개선 유지:
- 행1: 4개 카드 (문서함/메모/뉴스/승인대기)
- 행2: 파이프라인(8) + 도메인 분포(4)
- 행3: 최근 문서(8) + 법령/시스템(4)
- 핀 메모 상단 조건부 표시
- CalDAV stub → 법령 알림 + 시스템 상태 카드
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
전체 문서 1개 카드를 6개로 분리: 문서함, 메모, 뉴스, 승인대기,
법령알림, 시스템. 단일 FILTER 쿼리로 효율적 카운트.
각 카드 클릭 시 해당 페이지로 이동.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
상단 nav를 질문|메모|뉴스|Inbox 4개 핵심 기능으로 재정렬.
설정/로그아웃은 더보기(⋮) 드롭다운으로 이동.
메모 링크가 모바일에서 사이드바 없이 바로 접근 가능.
active 상태 표시(startsWith), 접근성 속성, 오버레이 닫기.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
대형 PDF(14~40MB)에서 kordoc 파싱 timeout(60초) 실패하던 문제.
10MB당 60초 추가, 최소 60초 최대 300초로 조정.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
asyncpg prepared statement가 multi-command를 지원하지 않아 시작 실패.
105 단일 파일을 105-112 개별 statement로 분리.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
flex 체인에서 min-width: auto 기본값이 카드 shrink를 막아
모바일에서 콘텐츠가 뷰포트를 초과하던 문제 수정.
- +page.svelte line 418: flex-1 → flex-1 min-w-0
- +page.svelte line 693: overflow-x-hidden 추가
- DocumentCard.svelte: button에 min-w-0 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DocumentTable: 분류/타입/크기 컬럼 모바일 숨김 (hidden md:flex/block)
- DocumentCard: gap/padding 축소 (gap-2 p-2 sm:gap-3 sm:p-3), data_origin 모바일 숨김, 태그 모바일 1개
- 검색바: 검색모드 select 모바일 숨김, AI답변 텍스트 모바일 숨김
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SCMP(www.scmp.com)처럼 HTTPS 원본이 HTTP로 301 redirect하는 소스에서
redirect target이 차단되던 문제 수정. allow_http를 원본 스킴이 아닌
소스 도메인의 allowlist 등록 여부로 판단하도록 변경.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[1][2][4] 같은 citation 마커의 숫자가 evidence 에 없다고 판정되어
모든 정상 답변이 refuse(2+strong) 되는 critical bug.
answer 에서 \[\d+\] 제거 후 숫자 추출.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
home-caddy 의 request_body max_size 100MB 한도 (infra_inventory.md D8 /
Cloudflare 섹션 참조) 에 걸리는 업로드 시 사용자 콘솔에 의미 없는 413 만
나오던 문제. 이제:
1. 클라이언트 사전 검사: 100MB 초과 파일은 업로드 자체를 시도 안 하고
즉시 toast 로 안내 (파일명 + 크기 + NAS 우회 경로)
2. 서버 fallback: 사전 검사를 통과했으나 인프라 한도에 걸려 413 응답이
오는 경우에도 같은 안내 메시지
NAS 우회 경로: NAS 의 PKM 폴더에 직접 두면 file_watcher 가 5분 간격으로
자동 인덱싱. 이게 100MB+ 파일의 정식 처리 경로 (infra_inventory.md
Cloudflare 섹션의 413 정책).
embed_worker:
- extracted_text[:6000] → title + ai_summary + tags(top 5) metadata 입력
- 500k자 문서의 표지+목차가 임베딩되는 구조적 버그 해결
- Ollama 기본 context 안전 (~1500자 이하), num_ctx 조정 불필요
- ai_summary < 50자 시 본문 800자 fallback
- ai_domain 은 초기 제외 (taxonomy 노이즈 방지)
extract_worker:
- kordoc / 직접 읽기 / LibreOffice 3 경로 모두 \x00 strip
- asyncpg CharacterNotInRepertoireError 재발 방지
queue_consumer:
- str(e) or repr(e) or type(e).__name__ fallback
- 빈 메시지 예외(24건 발생) 다음부터 클래스명이라도 기록
plan: ~/.claude/plans/quiet-meandering-nova.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inbox 가 review_status=pending 서버 필터로 받는데 pending 이 291 건 이라
page_size 100 으론 191 건 누락. inbox 는 작업 큐 성격이라 한 번에 보는 게 UX.
500 으로 상향: data 폭발 없음(filter 로 boundedness 보장), latency 영향 미미.
전략적 임시 — Phase 4.5 UI 작업에서 inbox 에 infinite scroll 또는 pagination
추가하면 le=100 으로 다시 내려도 됨.
Inbox 페이지가 /documents/?page_size=200 를 호출하는데 백엔드 Query 가
le=100 이라 422 발생 — Phase 2 첫 commit(2026-04-02)부터 dormant 버그.
inbox 코드 안에 'TODO(backend): review_status filter 지원 시 page_size 축소'
주석이 있던 상태.
backend:
- list_documents 에 review_status: str | None Query 파라미터 추가
- WHERE 절에 review_status 매칭 분기 추가
frontend:
- /documents/?review_status=pending&page_size=100 으로 변경
- 클라이언트 필터링 코드 제거 (서버 필터로 대체)
100 미만 안전. pending 이 100 넘으면 다음 페이지 로직 추가 필요 (별도 작업).
raw text() SQL + asyncpg 조합에서는 pgvector Vector(1024) 컬럼이
'[0.087,0.305,...]' 형태의 string 으로 반환되며 numpy 변환이 실패함
(ORM 을 쓰면 type 등록되지만 raw SQL 은 안 됨).
_to_numpy_embedding 에서 string 이면 json.loads 로 먼저 파싱한 뒤
numpy.asarray. 변환 실패 시 None 반환 (해당 doc 자동 drop).
Phase 4 deploy 워커 첫 실행 검증 중 발견.
text(sql) 은 SQLAlchemy 가 :name 패턴을 named bind parameter 로 해석하므로
SQL 주석이나 literal 안에 콜론이 들어가면 InvalidRequestError 발생.
ai_summary[:200] 같은 표기가 들어간 101_global_digests.sql 적용 시 fastapi
startup 자체가 떨어지는 문제가 발생.
exec_driver_sql 은 SQL 을 driver(asyncpg) 에 그대로 전달하므로 콜론 escape 불요.
schema_migrations INSERT 만 named bind 가 필요하므로 그건 그대로 유지.
Phase 4 deploy 검증 중 발견. 다음 마이그레이션부터는 자동 적용 가능.