diff --git a/docs/superpowers/specs/2026-05-05-v026-bugs-cleanup-design.md b/docs/superpowers/specs/2026-05-05-v026-bugs-cleanup-design.md new file mode 100644 index 0000000..9708f31 --- /dev/null +++ b/docs/superpowers/specs/2026-05-05-v026-bugs-cleanup-design.md @@ -0,0 +1,133 @@ +# 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[]` (또는 explicit row interface) 일괄 cleanup | +| **C7** | #24 + #41 | `` 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 getter +- `createTray(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) +- `` — 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 (+13~15) +- 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`)