Commit Graph

5 Commits

Author SHA1 Message Date
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