Files
inkling/docs/superpowers/specs/2026-05-05-v026-bugs-cleanup-design.md
altair823 341f55505d docs(v026): bugs + cleanup spec — 16 backlog 항목 → 13 task
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>
2026-05-05 01:08:05 +09:00

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.enumz.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:deleteinbox: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)

  • <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)