Commit Graph

13 Commits

Author SHA1 Message Date
altair823
6985db3505 fix(v032): AiWorker vocabSet COLLATE NOCASE 정합 (#31)
DB tags.name 가 COLLATE NOCASE 인데 vocabSet 은 strict-eq 였음 →
대문자/소문자 vocab 과 AI tag 가 다를 때 silently skip.

vocab.toLowerCase() + tagName.toLowerCase() 양쪽 normalize 로 정합.
2026-05-10 13:55:52 +09:00
altair823
2179cfbf39 feat(v031): AiWorker vision integration — note.media + visionModel + 5MB cap 2026-05-10 04:53:21 +09:00
altair823
9fef2edb6e feat(ollama): ProviderHolder + AiWorker/HealthChecker refactor (v0.2.3.1)
- ProviderHolder: mutable holder + listeners, indirection layer
- AiWorker: constructor InferenceProvider → ProviderHolder
  this.provider.x → this.holder.get().x 전환
- HealthChecker: 동일 패턴
- src/main/index.ts: provider 를 ProviderHolder 로 감싸서 생성
- 기존 AiWorker / HealthChecker 테스트의 constructor 호출에 ProviderHolder wrap
- 단위 +2 cases (ProviderHolder)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 23:32:20 +09:00
altair823
d8621d55e0 fix(tag-vocab): PR review round 1 — i1 dedup + m2 test gap (#3 v0.2.3)
- i1 (Important): AiWorker per-tag emit 루프에 res.tags Set dedup
  AI 가 같은 태그 중복 응답 시 hit count 2번 emit 되던 통계 왜곡 수정
  + 테스트 1개 (중복 태그 1 hit + 1 miss 검증)
- m2 (Minor): NoteRepository.getTopUsedTags LIMIT-then-filter 테스트 갭
  + 테스트 1개 (limit=3 + 한글 1 + kebab 2 → 결과 length=2 lock-in)

skip: m1 (per-tag serial await — ai_succeeded 패턴 일관),
      n1 (prompt 빈 줄 cosmetic), n2 (tagId positive — AUTOINCREMENT 1+)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 12:49:36 +09:00
altair823
727eeb1919 fix(tag-vocab): T7 review nit 2건 — test 코드 ergonomics (#3 v0.2.3)
- nit1: tag_vocab_hit/miss 테스트 payload cast dedupe (한 번에 typed 바인딩)
- nit2: { kind: string; payload: unknown } 반복을 EmittedEvent 타입 alias 로 hoist

skip: Minor1 (serial await — ai_succeeded 와 패턴 일관), Nit3 (magic number VOCAB_TOP_N — v0.2.4 backlog), Nit4 (한국어 코멘트 — 기존 코드와 일관)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 12:36:16 +09:00
altair823
3e0f710c70 feat(tag-vocab): AiWorker — vocab fetch + per-tag hit/miss emit (#3 v0.2.3)
- processJob 가 generate 직전 repo.getTopUsedTags(20) fetch
- provider.generate 에 vocab 전달 (LocalOllamaProvider 가 prompt 에 주입)
- ai_succeeded emit 후 per-tag 분류 → tag_vocab_hit/miss emit
  - hit: vocabSet.has + getTagIdByName lookup → { tagId, vocabSize }
  - miss: { vocabSize }
- AiTelemetryEmitter union 4종 (ai_succeeded/ai_failed/tag_vocab_hit/tag_vocab_miss)
- 단위 +4 cases (vocab passthrough, hit+miss, vocab=[] all miss, per-tag emit count)
- collectingTelemetry mock → AiTelemetryEmitter 타입 적용 (typecheck 통과)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 12:33:16 +09:00
altair823
449eb76683 feat(retry): AiWorker unreachable/timeout 무한 retry — 15분 cap (#2 v0.2.3) 2026-05-02 03:19:43 +09:00
altair823
78c10e8817 feat(trash): AiWorker.processJob deletedAt guard (#4 v0.2.3) 2026-05-01 21:00:09 +09:00
altair823
7e8e2b598d fix(telemetry): 회차 1 review 반영 — attempts 의미 통일 + DI 우회 제거 + 매직 슬라이스 제거
PR #13 회차 1 리뷰의 actionable 1건 + suggestion 3건 반영.

- `AiWorker` 의 `attempts` 필드가 success/failure 경로에서 비대칭 의미 (0-index vs count) 였던 문제. 둘 다 `attempt + 1` (실제 시도 횟수, 1-based) 로 통일. stats markdown 의 평균/분포 해석이 일관됨.
- `Date.now()` 직접 호출이 `opts.now` DI 를 우회하던 두 곳을 `this.now().getTime()` 으로 교체. 추후 durationMs 분포 테스트 작성 가능.
- `TelemetryService.emit` 의 `this.now()` 두 번 호출을 한 번 캐시로 통합. KST 자정 경계에서 ts 와 파일명 일자 불일치 가능성 제거.
- `readAllRecent` 의 `n.slice(7, 17)` 매직 슬라이스를 정규식 capture 그룹으로 교체. prefix 변경 시 한 곳만 수정.

테스트: AiWorker 성공 케이스의 `attempts: 0` → `attempts: 1` 갱신.
게이트: typecheck 0 errors, 245/245 unit tests pass.

Deferred (v0.2.4 backlog): 'aborted' user-cancel false-positive, tray menu submenu 분리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:41:26 +09:00
altair823
01447ddaad feat(telemetry): AiWorker emits ai_succeeded/ai_failed with reason (#7 v0.2.3) 2026-05-01 17:21:08 +09:00
altair823
723dccd61d feat(ai): AI-primary due_date flow — rule as prompt candidates only
Flow 반전 (F7-D 채택):
- 기존: rule.iso ?? ai.dueDate (rule 우선)
- 신규: ai.dueDate ?? null (AI 우선)

규칙은 parseAllCandidates 로 모든 매치를 추출 → prompt 에 후보
힌트로 주입. AI 가 종합 판단. AI 실패 시 due_date null (별 fallback 없음).

해결되는 케이스: '내일 모레' → AI 가 ambiguous 인지 → null.

PROMPT_VERSION → 3. GenerateInput.dueDateCandidates 신규.
buildPrompt(rawText, todayKst, candidates) — 빈 배열일 때 hint 섹션 생략.

Tests:
- AiWorker.test.ts — 'rule priority' 테스트 → 'AI dueDate wins' flip
- AiWorker.test.ts — passes todayKst 테스트 확장 (dueDateCandidates 도 검증)
- AiWorker.test.ts — 신규 'passes parseAllCandidates result as dueDateCandidates'
- LocalOllamaProvider.test.ts / ollama-golden.test.ts — generate 호출에 dueDateCandidates: [] 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 13:06:12 +09:00
altair823
adae90eb61 feat(ai): AiWorker merges rule parser + AI due_date
GenerateInput gains todayKst field. AiWorker computes KST-aligned
date once per job, runs parseDueDate on rawText, calls provider.generate
with todayKst, then merges: rule.iso wins if matched (deterministic),
else AI's due_date, else null. Logs dueDateSource (rule|ai|none) for
debugging. now() injection for testability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 11:14:46 +09:00
altair823
0c38fcaf85 feat(ai): AiWorker with sequential queue, 3-attempt backoff
Task 13 of the slice plan. Drives the pending → done/failed
transitions:
- enqueue() pushes a Job and kicks the loop; loadFromDb()
  rehydrates pending_jobs at startup so app restart resumes
  in-flight work.
- drain() exposes a Promise for tests + graceful shutdown.
- Concurrency 1: a single async loop awaits each provider call
  before the next, matching spec §2.2.
- 3-attempt backoff (default [0, 30s, 120s]; tests inject [0,0,0]).
  Each failure logs ai.retry, increments pending_jobs.attempts,
  and on the final attempt calls markAiFailed and emits onUpdate.
- emit() pushes the freshly-hydrated note to onUpdate (used by
  Task 30 to fan out IPC note:updated events).

Verification: `npx vitest run tests/unit/AiWorker.test.ts`
4 passed. Suite total 41 / 41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:10:15 +09:00