추정 원인 (a)/(b)/(c):
- (a) Windows registry path mismatch (NSIS 설치 위치 변경)
- (b) electron path canonicalization
- (c) args 비교 mismatch — getLoginItemSettings 가 args 와 함께 read 해야 매치
Fix:
- tray.ts: getLoginItemSettings({ args: ['--hidden'] }) 명시 — 트레이 checkbox
의 checked 상태가 실제 LoginItem args 와 정합하게 비교
- index.ts firstRun 후: autostart.state 진단 로그 (withArgs vs noArgs 비교
+ executableWillLaunchAtLogin) — dogfood 에서 실제 동작 확인
Fix 가 충분하지 않으면 dogfood 로그 분석 후 v0.2.7 deeper fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #23 single-instance lock 의 second-instance handler 가 무조건 inbox 창
띄움. NSIS installer 직후 사용자 클릭 + autostart --hidden 동시 시도 시
두 번째가 hidden 이어도 창 띄워서 "트레이만" 의도 위반.
Fix: requestSingleInstanceLock 에 additionalData = { hidden: startedHidden }
전달, second-instance 콜백 signature (event, argv, cwd, additionalData) 의
4번째 인자에서 hidden flag 확인 → true 면 early return (창 안 띄움).
PR #23 round 1 reviewer Important deferred 처리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
기존 UI 가 listTrash 200 limit 후 length 사용 → 350개 trash 시 dialog
"200개 영구 삭제" 표시되지만 실제 350 모두 삭제. 사용자 혼동 해소.
- NoteRepository.countTrashed() 신규 — SELECT COUNT(*) WHERE deleted_at IS NOT NULL
- IPC inbox:trashCount → countTrashed 사용
- 단위 +2 cases (>200 not capped, empty 0)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
restore 가 deleted_at = NULL 만 했음 → ai_status='failed' 인 노트는
영구 fail 상태로 복구. atomic transaction 안에서 ai_status='pending' reset
+ INSERT OR IGNORE INTO pending_jobs.
- failed → pending + pending_jobs 재처리 path 복구
- done 은 영향 X (이미 결과 있음)
- pending 은 pending_jobs 재생성 (defensive — trash 도중 jobs 미정상 상태 가능)
- 단위 +3 cases (failed/done/pending 각 케이스)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dogfood 발견 — 앱 아이콘 클릭 시마다 새 process 가 떠서 트레이 아이콘 여러 개,
SQLite 동시 접근 + AiWorker 중복 처리 + HealthChecker 중복 polling 등
**데이터 corruption 위험**.
원인: app.requestSingleInstanceLock() 호출 부재. Electron default 가
multi-instance 라 .exe 실행마다 별도 process.
Fix:
- app.requestSingleInstanceLock() 첫 줄에서 호출
- 두 번째 인스턴스 → app.quit() 즉시 종료
- 'second-instance' 이벤트 → 기존 inbox 창 restore + show + focus
(사용자 의도는 "앱 보기" 라 가정)
게이트: typecheck 0 / 단위 413 / e2e 1
version: 0.2.4 → 0.2.5 (critical hotfix patch)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dogfood 발견 #44 fix — 사용자가 설치된 버전 확인 path 부재 해소.
- 트레이 메뉴 마지막 항목 (종료 직전): "Inkling 정보..."
- 클릭 시 native dialog (showMessageBox):
- title: Inkling 정보
- message: Inkling {version}
- detail: 버전, Electron, Node, OS platform/release, 데이터 위치
- 버튼 3개: 확인 / 데이터 위치 열기 (shell.openPath) / 정보 복사 (clipboard)
- 디버그 정보 노출로 사용자가 issue report 시 첨부 가능
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- onDeleted: () => void → onDeleted?: () => void (inbox mode 전용 명시)
- handleDelete 내부 onDeleted() → onDeleted?.()
- App.tsx 의 trash mode NoteCard 가 onDeleted prop 미전달 (dead-code 제거)
- API 시그니처 정리 — trash mode 는 onPermanentDelete/onRestore 만 의미
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- #2: 24*60*60*1000 magic number → 모듈 상단 const DAY_MS
cleanupOldFiles + readAllRecent 두 callsite 통일
- #6: gc.run() 의 .catch 누락 → backup.runDaily 패턴 통일
실패 시 logger.warn('media.gc.failed', { reason })
Note: backlog #1 (now() 2번 호출) 은 PR #13 round 1 review 시 이미 fix —
backlog 항목 stale. v0.2.5 brainstorm 시 backlog 정리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #21 머지 후 dogfood 중 사용자 발견:
- #44: 버전 / 빌드 정보 표시 surface 부재 (트레이 / Inbox footer / About 모달)
- #45: 윈도우 자동 실행 옵션 재시작 후 풀려있는 버그
(tray.ts:47-58, app.setLoginItemSettings + getLoginItemSettings 비대칭)
PR review deferred 와 별개의 raw UX/bug 발견. 신설 섹션 "v0.2.3 / v0.2.3.1
dogfood 발견" 으로 분리 — v0.2.4 brainstorm 시 우선순위 결정.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
- 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>
v0.2.3 cut 7항목 동안 final reviewer + PR review 에서 발견된 minor/nit
중 의도적 deferred 38건 누적. 기존엔 user-level memory 에만 있어
사용자가 직접 보거나 편집 어려움 → repo 안으로 lift.
dogfood 1주 soak 동안 user 가 직접 prune / 우선순위 표시 / 새 항목 추가
가능. v0.2.4 brainstorm 진입 시 본 doc 가 1차 backlog reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #20 직후 Mac arm64 dist 시도 중 발견:
- Cannot detect repository by .git/config 경고 (3회)
- ⨯ Cannot read properties of null (reading 'channel')
at computeChannelNames (updateInfoBuilder.ts:47:74)
원인: electron-builder 가 auto-update 메타파일 (latest-mac.yml) 생성 시
publish config 또는 git remote 에서 채널 정보 추론 실패 → null 접근 crash.
DMG 자체는 빌드 성공 (dist/Inkling-0.2.3-arm64.dmg) — 후처리 단계 crash.
Fix: build.publish = null 명시 — auto-update 메커니즘 미사용 (개인 dogfood)
이라 latest-mac.yml / latest.yml 생성 단계 skip. Windows 빌드도 동일 경고
3회 떴는데 이번 fix 로 함께 사라짐.
검증: npm run dist:dir on Windows → "Cannot detect repository" 경고 사라짐, 정상 빌드.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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>
- 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>