altair823
6f95e89456
fix(ollama): PR #21 review round 1 — m1+m3+m4+n1 (v0.2.3.1)
...
- m1 (Minor): saveOllamaSettings IPC가 setOllama throw 시 try/catch
→ { ok: false, reason: 'persist failed: ...' } 대칭 응답
- m3 (Minor): Modal ESC=close + Enter=save 키 핸들러 + 첫 input autoFocus
- m4 (Minor): handleSave 첫 줄 if (saving) return; — sync double-click 가드
- n1 (Nit): 'gemma4:e4b' / 'http://localhost:11434 ' magic
→ src/shared/constants.ts 의 DEFAULT_OLLAMA_MODEL / DEFAULT_OLLAMA_ENDPOINT
defer to v0.2.4 backlog:
- m2: ollama_unreachable.reason 에 endpoint URL 노출 (PII 우회) — telemetry masking 정책
skip:
- i1 (race UX): acknowledge only, 정확성 영향 0
- m5 (abort try/catch): 현재 LocalOllamaProvider.abort 는 throw X
- m6 (first-boot blocking): 무시 가능
- n2 (offReplace): 현재 listener callsite 0건
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:53:42 +09:00
altair823
0c0327ddb6
feat(ollama): 트레이 메뉴 "Ollama 설정..." (v0.2.3.1)
...
- createTray 10번째 positional callback runOpenOllamaSettings
- 트레이 → 메뉴 클릭 → main 이 inbox:openOllamaSettings IPC push
- renderer App.tsx 가 구독해 modal open
backlog #4/#26 (TrayCallbacks object refactor) 와 합산 — v0.2.4 시 일괄 정리
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:43:00 +09:00
altair823
833a598368
feat(ollama): OllamaSettingsModal + App mount + OllamaBanner 설정 링크 (v0.2.3.1)
...
- OllamaSettingsModal: endpoint + model freetext 입력, 저장 시 healthCheck → 성공 닫기, 실패 inline 에러
- App.tsx: ollamaSettingsOpen state + onOpenOllamaSettings IPC subscribe
- OllamaBanner: onOpenSettings prop 추가, 우측 "설정" 버튼
- preload + types: onOpenOllamaSettings listener bridge
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:40:31 +09:00
altair823
4153284af1
fix(ollama): saveOllamaSettings 가 health.runOnce() 즉시 호출 (T4 review)
...
T4 fallback comment "60s polling cycle" 대신 HealthChecker 의 기존 public
method runOnce() 사용. 사용자가 settings 저장하자마자 OllamaBanner 갱신.
runOnce 는 이미 inbox:ollamaRecheck IPC 가 사용 중인 패턴.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:37:57 +09:00
altair823
cee39a90aa
feat(ollama): index 부팅 + IPC + preload + types (v0.2.3.1)
...
- index.ts: SettingsService.load() 후 endpoint/model 결정 (settings > env > default)
- IPC: inbox:loadOllamaSettings + inbox:saveOllamaSettings
- save: 임시 provider 로 healthCheck 통과 시에만 영속화 + holder.replace
- 기존 in-flight generate 는 abort?.() (optional method)
- preload + InboxApi shared types
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:36:46 +09:00
altair823
d1f36250e7
fix(ollama): InferenceProvider — abort?: () => void optional 추가 (T3 review)
...
T3 가 ProviderHolder 를 InferenceProvider 로 추상화. 단 IPC handler 가
holder.get().abort() 호출 예정 — interface 에 method 가 없으면 typecheck 실패.
abort 는 in-flight generate 중단용이라 모든 provider 가 지원할 필요는 없음
→ optional method 로 추가. caller 는 holder.get().abort?.() 패턴 사용.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:34:17 +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
c77c30be83
feat(ollama): LocalOllamaProvider — abort() + AbortController instance field (v0.2.3.1)
...
- abortController 가 method-local 에서 private instance field 로 이동
- public abort() 메서드 — 외부에서 in-flight generate 강제 중단
- ProviderHolder.replace() 시 호출되어 endpoint 변경 즉시 반영
- 단위 +2 cases (abort cancellation, model 파라미터)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:26:48 +09:00
altair823
de895b8fec
feat(settings): SettingsService — JSON 영속화 + zod 검증 (v0.2.3.1)
...
- `<profileDir>/settings.json` atomic write (temp + rename)
- 손상 JSON / 파일 없음 → 빈 객체 fallback (no throw)
- in-memory cache (load 1회 file read)
- zod .strict() schema for ollama { endpoint: URL, model: string }
- 단위 +6 cases
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-04 23:23:32 +09:00
altair823
61b6fa6c1f
fix(recall): PR review round 1 — i1 race + m1~m4 + n2 ( #6 v0.2.3)
...
- i1 (Important): RecallBanner shownIds → useRef (state setState 트리거 race 차단)
store 의 recallShownIds 필드 제거 (dead — useRef 가 대체)
- m1 (Minor): snoozeRecall candidate-null race 코멘트 (의도적 emit skip 명시)
- m2 (Minor): dismissRecallNote 후 recallSnoozeUntilMs = null clear
- m3 (Minor): CaptureService.markRecallOpened 의 dead local 'before' inline check 로 제거
- m4 (Minor): RecallBanner title 빈 케이스 fallback '(제목 없음)'
- n2 (Nit): NoteCard id load-bearing 의미 1줄 코멘트
skip: n1 (KST 4번째 inline duplicate — 프로젝트 전반 패턴, v0.2.4 nextKstMidnightMs 통합),
n3 (ipcMain.on vs handle — 다른 IPC 와 패턴 일관)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:38:52 +09:00
altair823
646fe7a7ab
feat(recall): RecallBanner + App.tsx mount + NoteCard id ( #6 v0.2.3)
...
- RecallBanner: 노트 제목 + N일 전 + 3 버튼 (열어보기/다음에/더 이상)
- 첫 렌더 시 emitRecallShown (recallShownIds Set 으로 per-session 1회 제약)
- snoozeUntilMs 만료 setTimeout (ExpiryBanner 패턴)
- 위치: ExpiryBanner 다음 (banner stack 끝)
- NoteCard 외곽 div 에 id="note-${note.id}" — "열어보기" scrollIntoView target
- 컬러 테마: 파랑 (#e8f0fe / #4a7ec0) — 다른 banner (적/황/적) 와 구별
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:28:58 +09:00
altair823
f4e1af83fe
feat(recall): renderer store — recallCandidate + 4 actions ( #6 v0.2.3)
...
- recallCandidate, recallSnoozeUntilMs, recallShownIds (Set) state
- loadInitial / refreshMeta 가 listRecallCandidate Promise.all 합류
- loadRecallCandidate / openRecall / dismissRecallNote / snoozeRecall actions
- snoozeRecall: KST 다음 자정 (snoozeExpired 패턴 일관) + emitRecallSnoozed
- openRecall / dismissRecallNote: API 호출 후 다음 후보 fetch
- 신규 store.recall.test.ts +3 cases
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:25:49 +09:00
altair823
20394bf2a3
feat(recall): IPC + preload + InboxApi — 5 channels ( #6 v0.2.3)
...
- ipcMain.handle: list/markOpened/dismiss/emitShown/emitSnoozed
- preload inboxApi: 5 entries (ipcRenderer.invoke)
- shared/types InboxApi: 5 method signatures
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:22:16 +09:00
altair823
0c59ce3715
feat(recall): CaptureService — 5 methods (list/open/dismiss/shown/snoozed) ( #6 v0.2.3)
...
- listRecallCandidate(): repo.findRecallCandidate 위임
- markRecallOpened(id): last_recalled_at 갱신 + recall_opened emit
- dismissRecall(id): recall_dismissed_at 갱신 + recall_dismissed emit
- emitRecallShown(id): ageDays 계산 + recall_shown emit
- emitRecallSnoozed(id): recall_snoozed emit
- private computeAgeDays(note): last_recalled_at ?? created_at 기준 일수
- 단위 +4 cases
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:20:44 +09:00
altair823
59cfb711cd
feat(recall): telemetryStats + EmitInput — recall 누적 + 열림율 + 평균 ageDays ( #6 v0.2.3)
...
- DailyRow +4 cols (recall_shown/opened/dismissed/snoozed)
- accumulators + 4 branches + recallAgeDaysSum
- table 컬럼 +4
- summary lines: "- 회상 추천: shown N / opened O / dismissed D / snoozed S (열림율 X%)"
"- 회상 평균 ageDays: avg"
- TelemetryService.EmitInput union 15 → 19
- 단위 +2 cases
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:17:49 +09:00
altair823
b94e68238c
feat(recall): telemetryEvents — recall_shown/opened/dismissed/snoozed zod schemas ( #6 v0.2.3)
...
- RecallShownPayload { noteId, ageDays: int>=0 } .strict()
- recall_opened/dismissed/snoozed → NoteIdPayload 재사용
- TelemetryEventSchema union 15 → 19
- 단위 +3 cases (recall_shown valid, extra field 거부, opened/dismissed/snoozed valid)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:13:49 +09:00
altair823
0eb2e6282f
feat(recall): NoteRepository — findRecallCandidate + markRecallOpened + dismissRecall ( #6 v0.2.3)
...
- findRecallCandidate(): 7일+ 안 본 + 30일+ dismiss 만료 + ai='done' + 마감 안 임박 + LIMIT 1
- markRecallOpened(id, now): last_recalled_at 갱신
- dismissRecall(id, now): recall_dismissed_at 갱신
- KST 보정 SQL date('now','+9 hours')
- 단위 +5 cases (empty/recent/old/dismiss expiry/exclude variants)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 13:11:14 +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
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
26f1db5626
feat(tag-vocab): TelemetryService EmitInput +tag_vocab_hit/miss + 테스트 narrowing 확장 ( #3 v0.2.3)
...
- EmitInput union 13 → 15
- narrowing guards (noteId 없는 kind 분기) 에 tag_vocab_hit/miss 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:29:24 +09:00
altair823
973cb1d08d
feat(tag-vocab): telemetryStats — hit/miss 누적 + summary 적중률 ( #3 v0.2.3)
...
- DailyRow +2 cols (tag_vocab_hit, tag_vocab_miss)
- accumulators + branches
- table 컬럼 +2
- summary "- 태그 vocab: hit/miss = N/M (적중률 X%)" 또는 "(데이터 없음)"
- 단위 +2 cases
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:27:22 +09:00
altair823
b81fc82621
feat(tag-vocab): telemetryEvents — tag_vocab_hit/miss zod schemas ( #3 v0.2.3)
...
- TagVocabHitPayload { tagId: int>0, vocabSize: int>=0 } .strict()
- TagVocabMissPayload { vocabSize: int>=0 } .strict()
- TelemetryEventSchema union 13 → 15
- 단위 +3 cases (hit accept, miss accept, hit extra field 거부)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:23:31 +09:00
altair823
daa8507364
feat(tag-vocab): InferenceProvider.vocab + LocalOllamaProvider 전달 ( #3 v0.2.3)
...
- GenerateInput.vocab?: string[] (optional, 미전달 시 빈 배열 처리)
- LocalOllamaProvider.generate 가 input.vocab ?? [] 를 buildPrompt 4th 인자로
- 단위 +1 case (vocab → prompt body)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:21:24 +09:00
altair823
896b374f56
fix(tag-vocab): T2 review minor/nit 2건 ( #3 v0.2.3)
...
- M1: prompt.test.ts test 4 변수명 candidateIdx → headerIdx (실제 anchor 가 'Today's date' 헤더)
- N1: prompt.ts return 직전 self-delimited block 컨벤션 1줄 코멘트
skip: N2 (PROMPT_VERSION 테스트 redundancy nit — harmless guard), N3 (vocab dedup/normalize — Task 1 caller 책임)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:19:39 +09:00
altair823
134d59ddb4
feat(tag-vocab): prompt.ts — PROMPT_VERSION 4 + vocab parameter ( #3 v0.2.3)
...
- PROMPT_VERSION 3 → 4 (marker bump, retry 트리거 X)
- buildPrompt 4번째 param vocab: string[] = []
- vocab.length > 0 시 "Existing vocabulary tags" + "Prefer reusing" 라인 추가
- vocab=[] 시 라인 자체 생략 (Q3=B 결정)
- 단위 +4 cases (신규 prompt.test.ts)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:17:17 +09:00
altair823
e2b16d44d7
fix(tag-vocab): T1 review minor/nit 4건 일괄 ( #3 v0.2.3)
...
- M1: getTopUsedTags 의 LIMIT-then-filter 의미 docstring 명시
- M2: AI+user source 통합 테스트 강화 — 카운트 차이로 정렬 검증 (toContain 만으론 약함)
updateUserAiFields 는 tags REPLACE 방식 (DELETE+reinsert) 이므로
fallback 패턴 사용: 3개 노트 각 1태그, AI/user 혼합으로 design=2 > meeting=1 검증
- N1: SQL "COUNT(*) c" → "COUNT(*) AS c" (countFailed 패턴과 일관)
- N2: kebab-case regex 모듈 상수 KEBAB_CASE_RE 로 hoist
skip: N3 (test 헬퍼 — verbosity 경미), N4 (it 블록 분리 — 코드베이스 패턴 유지)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:14:53 +09:00
altair823
df8a53aec1
feat(tag-vocab): NoteRepository — getTopUsedTags + getTagIdByName ( #3 v0.2.3)
...
- getTopUsedTags(limit=20): top-N (count desc, id asc) + kebab-case 필터 + deleted_at 제외
- getTagIdByName(name): COLLATE NOCASE lookup
- AI+user source 통합 카운트 (Q1=C 결정)
- 단위 +7 cases (정렬, 필터, source 통합, deleted 제외, limit, getTagIdByName)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 12:10:36 +09:00
altair823
8f56814186
fix(retry): review round 1 — minor/nit 4건 일괄 ( #2 v0.2.3)
...
m1 — NoteRepository.test.ts 에 retryAllFailed OR IGNORE race-safe 회귀
가드 1 case 추가. failed 노트인데 pending_jobs row 가 이미 존재하는
비정상 race 상태 시뮬레이션 → INSERT OR IGNORE 라 duplicate 안 됨,
기존 attempts/next_run_at 보존.
m2 — store.retryAllFailed 의 r.count 무시 의도 주석 1줄.
단일 process (Electron) 환경 + 모든 ai_status='failed' 가 retry 대상이라
사용자 시점 카운트는 0 reset 가 정확.
n1 — AiWorker unreachableBackoffStep increment 명료화.
Math.min(..., length-1) → 명시적 if 가드 (step < length-1) 로 cap 도달 시
no-op 의도 가시화. 동작 동일.
n2 — AiWorker.processJob 의 max 의미 주석 1줄. unreachable/timeout 분기는
attempt -= 1 로 인덱스 stay 라 max 무관 — future maintainer 위해 명시.
n3 (FailedBanner inline style) 은 v0.2.4 backlog (banner theme cleanup).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 03:47:08 +09:00
altair823
e4a0be15ae
feat(retry): tray '지금 AI 처리' 9th callback + main wiring ( #2 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 03:36:44 +09:00
altair823
406a5e61f0
feat(retry): FailedBanner + App.tsx mount ( #2 v0.2.3)
2026-05-02 03:34:09 +09:00
altair823
3ebd3bc9a5
feat(retry): store retryAllFailed action + failedCount ( #2 v0.2.3)
2026-05-02 03:32:01 +09:00
altair823
6e5f3703d7
feat(retry): CaptureService.retryAllFailed + IPC 2 channels ( #2 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 03:28:11 +09:00
altair823
12c267aabd
feat(retry): telemetry ai_retry_manual + stats AI 수동 재시도 ( #2 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 03:24:31 +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
2e3f0edffd
feat(retry): NoteRepository — findFailedIds/countFailed/retryAllFailed/setNextRunAt ( #2 v0.2.3)
2026-05-02 03:15:05 +09:00
altair823
a94c7578b7
fix(ollama): review round 1 — minor/nit 7건 일괄 ( #1 v0.2.3)
...
m1 — HealthChecker.last={ok:true} sentinel 의도 주석 (line 17).
첫 healthy=ok=true 면 transition 으로 인식 안 됨, ok=false 면 unreachable
transition 으로 정상 인식. telemetry 누락 0.
m2 — runOnce in-flight guard 추가. polling 첫 호출이 늦게 끝나는 동안
setInterval 가 두 번째 호출 시작하면 같은 promise 반환. healthCheck 가
idempotent HTTP 라 race 안전하지만, 이중 onUpdate/telemetry emit 회피.
m3 — main.ts before-quit 핸들러 통합. trayInterval cleanup 별도 핸들러
(line 349) 제거하고 health.stop() 핸들러 안에 흡수. 모든 cleanup 한 곳.
n1 — OllamaBanner 재확인 button 의 onClick 에 .catch 추가.
recheckOllama Promise rejection 시 console.warn (silent swallow 회피).
n2 — App.tsx useEffect deps array 의도 주석 1줄. onOllamaStatus 콜백이
useInbox.setState 직접 호출 — store reference 안정적이라 deps 불필요.
n3 — HealthChecker idempotent test 강화. <=2 → ===2 (정확).
두 timer 등록되면 4 (각 timer 마다 즉시+1s) 가 됨.
n4 — runOnce 의 manual emit 이 healthCheck *전에* fire 인 의도 주석.
provider 실패 시에도 manual 카운트 1:1 보장.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 02:04:25 +09:00
altair823
cdf2e4bc47
feat(ollama): OllamaBanner 재확인 button ( #1 v0.2.3)
2026-05-02 01:46:18 +09:00
altair823
557960ff5a
feat(ollama): tray 'Ollama 재확인' 메뉴 + 8th callback ( #1 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 01:44:11 +09:00
altair823
c78f3af3a6
feat(ollama): InboxApi + preload + store recheckOllama + onOllamaStatus subscriber ( #1 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 01:41:04 +09:00
altair823
410a6f494b
feat(ollama): IPC inbox:ollamaRecheck + pushOllamaStatus helper ( #1 v0.2.3)
2026-05-02 01:37:47 +09:00
altair823
e30e436051
feat(ollama): main wiring — health.start + before-quit stop ( #1 v0.2.3)
2026-05-02 01:34:33 +09:00
altair823
a68ffe0aeb
feat(ollama): telemetry 3 events — unreachable/recovered/recheck_manual ( #1 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 01:30:26 +09:00
altair823
12681e431c
feat(ollama): HealthChecker.start/stop + delta + onTelemetry hook ( #1 v0.2.3)
2026-05-02 01:25:26 +09:00
altair823
d672ec3afa
fix(expiry): review round 1 — minor/nit 6건 일괄 ( #5 v0.2.3)
...
m1 — spec §5.3 dialog 버튼 순서를 impl 패턴 (`['옮기기','취소'], defaultId=1, cancelId=1`) 으로 보정. project 의 permanentDelete/emptyTrash 와 일관 (위험 액션은 default focus = 취소).
m2 — telemetryEvents.test.ts 에 `expired_batch_trash` 의 extra-field 회귀 가드 추가. `expired_banner_shown` 과 대칭 (privacy invariant).
m3 — ExpiryBanner.InnerProps.candidates 타입을 narrow subset → `Note` 로 통일. v0.2.4 에서 Note 타입 진화 시 silent drift 방지.
m4 — onTrash 의 `void trashExpiredBatch(ids)` → `.catch((e) => console.warn(...))` 로 Promise rejection 가시화. (project-wide error toast 도입은 v0.2.4 backlog 유지)
n1 — 24h+ 앱 켜둔 상태에서 snooze 자동 만료. `setTimeout(snoozeUntilMs - now)` 으로 자정 KST 시점에 force re-render. (refreshMeta trigger 의존 제거)
n2 — CaptureService.listExpired 의 dedup signature reset on empty 의도 주석 1줄. future maintainer 위해.
n3 (`as any[]`) 은 repo 전체 hydrate 패턴 — 단독 fix 시 inconsistency. v0.2.4 backlog #22 로 합산.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-02 00:47:58 +09:00
altair823
7cbbd4dc97
feat(expiry): ExpiryBanner component + App.tsx mount ( #5 v0.2.3)
2026-05-02 00:22:38 +09:00
altair823
b7205597db
feat(expiry): zustand store extension — expiredCandidates + snooze ( #5 v0.2.3)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-02 00:18:11 +09:00
altair823
749235f65d
feat(expiry): CaptureService listExpired/trashExpiredBatch + IPC 2 channels ( #5 v0.2.3)
2026-05-02 00:13:49 +09:00
altair823
f76ca06d9e
feat(expiry): telemetry 2 events — expired_banner_shown / expired_batch_trash ( #5 v0.2.3)
2026-05-02 00:08:44 +09:00
altair823
fec80361dd
feat(expiry): NoteRepository.trashBatch atomic ( #5 v0.2.3)
2026-05-02 00:01:03 +09:00
altair823
00423fb235
feat(expiry): NoteRepository.findExpiredCandidates ( #5 v0.2.3)
2026-05-01 23:57:53 +09:00