bugs (4): #10 restore + pending_jobs / #12 trashCount cap / #45 autostart 풀림 / #46 hidden-start race cleanup (12 → 9 cluster): KST helper / TrayCallbacks 객체 / refreshTrayFailedCount singleton / AiFailedReason union / hasNoteId predicate / hydrate as any[] / Banner shared component / exhaustiveness check / microfixes (channel rename + VOCAB_TOP_N + Modal URL pre-check + ratio 코멘트) dogfood telemetry 필요 14건은 v0.2.7 영역. 별도 brainstorm 4건도 v0.2.7+. 게이트 추정: 단위 413 → 427 (+14). version 0.2.5 → 0.2.6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.0 KiB
v0.2.6 Bugs + Cleanup — Design Spec
작성: 2026-05-05 · 정식 v0.2.6 cut. backlog 16건 (bug 4 + cleanup 12, 13 task 로 cluster) 통합 처리. dogfood telemetry 미수집 영역 (#7/#16/#18/#25/#33/#35/#36/#39/#40 등 14건) 은 v0.2.7 brainstorm 영역으로 별도.
1. Goal
dogfood UX 마찰 (autostart 풀림, trashCount 부정확, restore 시 AI 미재처리) 즉시 해소 + 코드베이스 cleanup (KST helper 통합, TrayCallbacks 객체화, AiFailedReason union 통합 등) 으로 v0.2.7 brainstorm 시 신규 feature 작업 friction 제거.
2. Scope (16 backlog 항목 → 13 task)
Bug fixes (B1~B4)
| Task | 항목 | 작업 요약 |
|---|---|---|
| B1 | #10 | NoteRepository.restoreNote(id) 가 ai_status='failed' 인 노트 복구 시 ai_status='pending' reset + pending_jobs INSERT |
| B2 | #12 | NoteRepository.countTrashed() 추가 + IPC inbox:trashCount 가 SQL 정확 N 반환 (UI 200 cap 제거) |
| B3 | #45 | autostart 풀림: app.getLoginItemSettings({ args: ['--hidden'] }) (args 비교 정확도) + path canonicalization 검토. fallback: 진단 로그만 추가 시 backlog 유지 |
| B4 | #46 | app.requestSingleInstanceLock(additionalData) + second-instance(event, argv, cwd, additionalData) 에서 hidden flag 체크 → 두 번째 hidden 이면 inbox 창 안 띄움 |
Cleanup refactor (C1~C9)
| Task | 항목 (cluster) | 작업 요약 |
|---|---|---|
| C1 | #3 + #19 + #34 | KST helper 통합 → src/shared/util/kstDate.ts. 4 callsite migrate (TelemetryService.todayKstIso, telemetryStats.kstDate, AiWorker.todayKstAsDate/Iso, store snoozeExpired/snoozeRecall) |
| C2 | #4 + #23 + #26 | interface TrayCallbacks + createTray(callbacks: TrayCallbacks) 1-arg refactor. positional 10개 → object |
| C3 | #27 | refreshTrayFailedCount module-scoped state 제거 → TrayCallbacks 객체 안 reactive 함수 또는 store-driven 패턴 |
| C4 | #5 | export type AiFailedReason = 'unreachable' | 'schema' | 'timeout' | 'other' 단일 export + zod z.enum 의 z.infer 로 type 파생. 3 callsite migrate |
| C5 | #21 | hasNoteId(ev: TelemetryEvent): ev is TelemetryEventWithNoteId type predicate helper → tests/unit/TelemetryService.test.ts 의 4-line narrowing 체인 단축 |
| C6 | #22 | NoteRepository hydrate 의 as any[] → Record<string, unknown>[] (또는 explicit row interface) 일괄 cleanup |
| C7 | #24 + #41 | <Banner severity="warning"|"error"|"info"> shared component → ExpiryBanner / OllamaBanner / FailedBanner / RecallBanner / OllamaSettingsModal 5 callsite migrate |
| C8 | #8 | telemetryStats.aggregateStats if/else if 끝에 else { const _: never = ev; } exhaustiveness check |
| C9 | #15 + #29 + #42 + #9 | microfixes 묶음: inbox:delete→inbox:trash rename / getTopUsedTags(20) → VOCAB_TOP_N const / OllamaSettingsModal zod URL pre-check / 휴지통 회수율 ratio 코멘트 1줄 |
3. Out of scope
- Telemetry 데이터 필요 (14건): #7 reason 분포 / #16 permanent_delete 빈도 / #18 loadExpired consumer / #20 telemetry .catch silent / #25 HealthChecker dedup / #28 unreachableBackoffStep / #29 top-N 튜닝값 (extract 만 본 cut, 튜닝은 v0.2.7) / #30 LIMIT-then-filter 정책 / #31 vocabSet COLLATE / #32 per-tag emit 병렬화 / #33 promptVersion payload / #35 recall_shown lifetime / #36 IPC handle vs on / #39 ollama reason PII / #40 Settings race flicker
- 별도 brainstorm 영역 (3건): #11 restoreNote precondition / #14 ARIA 패턴 / #17 dialog 버튼 순서 / #37 NoteCard id ref-forwarding
4. Architecture changes
대부분 cosmetic refactor 또는 isolated bug fix. 주목할 architecture-level 변경:
4.1 KST helper 통합 (C1)
- 신규
src/shared/util/kstDate.ts(main + renderer 양쪽 import 가능) - 기존 4 callsite 의 inline KST 계산 제거
- API:
kstTodayIso(now?: Date): string,nextKstMidnightMs(now?: Date): number - KST_OFFSET_MS 상수 단일
4.2 TrayCallbacks 객체화 (C2 + C3)
interface TrayCallbacks— 10+ 개 callback + state gettercreateTray(callbacks: TrayCallbacks): void— 1-arg signature- module state (_failedCount, _todayCount, _ollamaOk) 는 TrayCallbacks 의 reactive getter / setter 패턴 또는 explicit refresh 함수 (
refreshTray(state: { todayCount, failedCount, ollamaOk }))
4.3 Banner shared component (C7)
<Banner severity="warning"|"error"|"info" icon? title? children>— wrapping/styling 일원화- 5 callsite 가 themed inline style 제거 → severity prop
- CSS variables 또는 hardcoded theme map (single source)
4.4 NoteRepository.restoreNote behavior change (B1)
- 기존:
UPDATE notes SET deleted_at = NULL WHERE id = ? - 변경: 추가로
ai_status='failed'였을 경우 →ai_status='pending'reset +INSERT OR IGNORE INTO pending_jobs - atomic transaction
- AiWorker 가 자동으로 다음 loop iteration 에서 처리
5. Tests
추정 +17 cases (413 → 430):
| Task | 신규 단위 |
|---|---|
| B1 | +3 (restore failed note re-enqueues, restore done note 영향 X, restore cancelled note 영향 X) |
| B2 | +2 (countTrashed 정확, dialog message 정확 N) |
| B3 | +1-2 (autostart args 비교, 가능하다면 mock electron app) |
| B4 | +1 (additionalData hidden flag 가 second-instance 에 전달, mock test) |
| C1 | +2 (kstTodayIso, nextKstMidnightMs) — 기존 4 callsite test 가 자동 검증 |
| C2 | refactor only, 기존 tray 테스트 유지 |
| C3 | refactor only |
| C4 | refactor only |
| C5 | +2 (hasNoteId predicate) |
| C6 | refactor only |
| C7 | refactor only (UI 컴포넌트 unit test X 패턴) |
| C8 | +1 (exhaustive guard 컴파일 단계) |
| C9 | +1 (Modal URL pre-check), 나머지 refactor only |
총 신규: ~13-15 (보수적). 단위 413 → ~426-428 예상.
6. Privacy invariant
- B1/B2: telemetry 영향 없음
- B3/B4: telemetry emit 없음 (autostart event 미수집)
- C 시리즈: 모두 cosmetic refactor — invariant 영향 0
- 본 cut 에서 신규 telemetry kind 추가 0
7. Gates (roadmap §3.1)
- typecheck 0
- 단위 413 →
427 (+1315) - e2e 1/1
- backward compat: 기존 사용자 데이터 + UI 동작 영향 0 (단 B1 은 의도적 동작 추가, B2 는 UI N 표시 정확화)
8. Risk + Fallback
B3 (autostart 풀림) 진단 불확실
가장 risky. Windows registry 디버깅 결과 깨끗한 fix 안 나올 수 있음. Fallback 정책:
- 진단 절차 적용해도 fix 안 되면 → 진단 로그만 추가 (
logger.info('autostart.state', { stored, current, mismatch })) → backlog #45 유지 → 본 cut 에서 task drop - 다른 task 영향 없음 (각 task 독립적)
C1 KST helper 의 alias 경계
src/shared/util/kstDate.ts 가 main + renderer 양쪽에서 import 되어야. 기존 @main/util/kstDate.ts 는 renderer 에서 import 불가 (alias 분리). src/shared/ 가 양쪽 가능 패턴. 검증 필요.
C2 TrayCallbacks 객체화 의 backward compat
기존 createTray 호출자 (index.ts 1곳) 한 군데만 변경 → 안전. tray 테스트 영향 최소.
9. 작업 순서
순서대로 subagent dispatch. 의존성:
- B1, B2: 독립
- B3: 독립 (Windows-specific, mock 어려움)
- B4: 독립
- C1 → 다른 task 영향 X (shared util 추가)
- C2 → C3 (TrayCallbacks 객체에 refreshTrayFailedCount 흡수)
- C4, C5, C6, C7, C8, C9: 독립
권장 순서: B1 → B2 → B4 → B3 → C1 → C4 → C5 → C6 → C8 → C2 → C3 → C7 → C9.
이유: B3 (위험) 을 cleanup 시작 직전에 두어 fail 시 빠르게 회피. C2/C3 cluster 는 묶어서. C7 (Banner shared) 는 isolated UI cleanup, 마지막 그룹.
10. Roadmap relation
- v0.2.6 정식 cut (이전 v0.2.4/v0.2.5 는 patch / hotfix)
- 머지 후 binary 빌드 v0.2.6 (Windows + Mac) + Gitea release
- v0.2.7 brainstorm 트리거: dogfood ≥1주 soak + telemetry export 모인 후, 잔여 backlog 14건 (data-dependent) + 신규 피드백 일괄 triage
- backlog file 본 cut 후 prune (16 건 처리 완료 표기) + rename 검토 (
v027-backlog.md또는feature-backlog.md)