Phase B 의 스트리밍 size 검증을 외부에서 확인할 수 있는 스크립트.
pytest 인프라가 Phase 0 상태이므로 full test harness 구축을 미루고,
`scripts/verify_upload_size.py` 단일 파일로 경계 케이스를 즉시 회귀 검증.
7 케이스:
- 0 bytes → 400 (정책)
- 1 byte → 201 (happy path)
- max_bytes - 1 → 201 (경계 하)
- max_bytes 정확 → 201 (경계 상)
- max_bytes + 1 → 413 (스트리밍 차단)
- CL slack 초과 (override 헤더) → 413 (사전 차단)
- CL 위조 (작은 헤더 + 큰 body) → best-effort (서버 거절 status 수용)
`/api/config/public` 에서 max_bytes 를 동적 획득. slack_ratio 는 비공개라
스크립트 상수로 1.05 하드코딩 (config.yaml 과 동기화 유지 주석 명시).
Cleanup: 파일명 prefix `__upload_boundary_test__` + ns timestamp 로
실데이터와 격리. 시작 시 pre-cleanup + 각 케이스 직후 + finally 블록 cleanup.
`docker compose exec fastapi python /app/scripts/verify_upload_size.py` 로 실행.
UPLOAD_TEST_TOKEN + DATABASE_URL 환경 변수 필요. scripts/ 는 이미 read-only
volume 으로 마운트돼 있어 배포·재빌드 불필요.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
asyncpg 이 TIMESTAMPTZ 파라미터에 문자열 대신 datetime 객체를 요구
(DataError: invalid input, expected datetime instance, got str).
argparse type=datetime.fromisoformat 로 CLI 단계에서 파싱.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
asyncpg 이 $N IS NULL 비교에서 Python None 의 타입 추론 실패
(AmbiguousParameterError: could not determine data type of parameter).
None 인 조건은 WHERE 에서 아예 제외 — clauses 동적 조립.
부수 효과: 조건 0개일 때 "TRUE" 반환으로 quiet fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SQLAlchemy text() 의 `:name` 파라미터가 PostgreSQL `::type` cast 와
토큰 경계 충돌로 치환되지 않아 `syntax error at or near ":"` 발생.
`:since::timestamptz` → `CAST(:since AS TIMESTAMPTZ)` 로 변경.
Reproduction: --since/--until 옵션 사용 시 모든 집계 쿼리 실패.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Implement kordoc /parse endpoint (HWP/HWPX/PDF via kordoc lib,
text files direct read, images flagged for OCR)
- Add queue consumer with APScheduler (1min interval, stage chaining
extract→classify→embed, stale item recovery, retry logic)
- Add extract worker (kordoc HTTP call + direct text read)
- Add classify worker (Qwen3.5 AI classification with think-tag
stripping and robust JSON extraction from AI responses)
- Add embed worker (GPU server nomic-embed-text, graceful failure)
- Add DEVONthink migration script with folder mapping for 16 DBs,
dry-run mode, batch commits, and idempotent file_path UNIQUE
- Enhance ai/client.py with strip_thinking() and parse_json_response()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add users table to migration, User ORM model
- Implement JWT+TOTP auth API (login, refresh, me, change-password)
- Add first-run setup wizard with rate-limited admin creation,
TOTP QR enrollment (secret saved only after verification), and
NAS path verification — served as Jinja2 single-page HTML
- Add setup redirect middleware (bypasses /health, /docs, /openapi.json)
- Mount config.yaml, scripts, logs volumes in docker-compose
- Route API vs frontend traffic in Caddyfile
- Include admin seed script as CLI fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- scripts/pkm_utils.py: 공통 유틸 (로거, dotenv, osascript 래퍼)
- scripts/prompts/classify_document.txt: Ollama 분류 프롬프트
- applescript/auto_classify.scpt: Inbox → AI 분류 → DB 이동
- applescript/omnifocus_sync.scpt: Projects → OmniFocus 작업 생성
- scripts/law_monitor.py: 법령 변경 모니터링 + DEVONthink 임포트
- scripts/mailplus_archive.py: MailPlus IMAP → Archive DB
- scripts/pkm_daily_digest.py: 일일 다이제스트 + OmniFocus 액션
- scripts/embed_to_chroma.py: GPU 서버 벡터 임베딩 → ChromaDB
- launchd/*.plist: 3개 스케줄 (07:00, 07:00+18:00, 20:00)
- docs/deploy.md: Mac mini 배포 가이드
- docs/devonagent-setup.md: 검색 세트 9종 설정 가이드
- tests/test_classify.py: 5종 문서 분류 테스트
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>