Header / 처리 이력 / next-step 섹션 outdated 반영: - 최종 갱신 2026-05-05 v0.2.5 critical hotfix 완료 - 처리 이력 표 — v0.2.4 5건 처리 + v0.2.5 single-instance lock (out-of-backlog hotfix) - #46 신규 추가: PR #23 reviewer Important deferred (hidden-start race) - #45 우선순위 v0.2.4 → v0.2.6 으로 이동 표기 - post-cut next-step (#38) status 갱신 — v0.2.5 release 완료, 다음 v0.2.6 brainstorm - "v0.2.4 brainstorm" → "v0.2.6 brainstorm" 표현 통일 - 명명 노트 추가: 파일명 historic, v0.2.6 cut 시 prune + rename 검토 총 항목 46 / 잔여 40건. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 KiB
v0.2.x Backlog
누적 backlog. v0.2.3 cut (7항목 / PR #13~#19) 시점부터 PR review deferred + dogfood 발견 모두 합산. 파일명은 historic (
v024-backlog.md) — v0.2.4 / v0.2.5 cut 후에도 이어 사용. v0.2.6 brainstorm 시 신규 피드백 + 잔여 일괄 triage.
누적 시작일: 2026-05-01 (#7 telemetry skeleton 머지 시점) 최종 갱신: 2026-05-05 (v0.2.5 critical hotfix 완료) 총 항목 수: 46 (#1 stale 포함) 잔여: 40건 (=46 − 처리 5 − stale 1)
처리 이력 / 진행 흐름
| 항목 | 상태 | Cut |
|---|---|---|
#1 (now() 2번 호출) |
✅ 이미 fix (PR #13 round 1 — backlog stale) | - |
#2 (DAY_MS magic) |
✅ 처리 | v0.2.4 (commit ef5d3da) |
#6 (media.gc.run() .catch) |
✅ 처리 | v0.2.4 (commit ef5d3da) |
#13 (NoteCard onDeleted dead-code) |
✅ 처리 | v0.2.4 (commit c87c248) |
| #44 (버전 정보 surface) | ✅ 처리 (트레이 "Inkling 정보..." + native dialog) | v0.2.4 (commit d3dfe1e) |
| #45 (자동실행 풀림 버그) | 진단 대기 — Windows registry 분석 필요 | v0.2.6 영역 (별도 cut 가능) |
| #46 (hidden-start race) | PR #23 Important deferred — 신규 항목 | v0.2.6 |
| out-of-backlog: multi-instance bug (single-instance lock) | ✅ critical hotfix | v0.2.5 (PR #23, 7187aea) |
잔여 40건 (= 46 − 처리된 5건 − stale 1건). v0.2.6 brainstorm 시 일괄 triage.
명명 노트
- v0.2.3.1 / v0.2.4 / v0.2.5 는 dogfood unblock patch (semver bump 강제 / hotfix)
- v0.2.6 가 다음 정식 feature cut (backlog triage + 신규 피드백 기반 brainstorm)
- 본 backlog 파일은 v0.2.6 cut 시점에 prune + rename 검토 (
v026-backlog.md또는 stable 한feature-backlog.md)
Defer 사유 카테고리
각 항목은 머지 전 inline fix 보다 v0.2.4 영역으로 미룬 명시적 사유 가짐:
- Cross-cutting refactor — 한 PR 안에서 부분만 고치면 inconsistency. 일괄 cleanup task 영역. (예: KST helper 4 callsite 통합,
createTraypositional callbacks 전체 객체화) - Data-dependent — dogfood telemetry 분포 보고 결정해야 의미. (예: top-N 튜닝, recall_shown lifetime dedup 정책)
- Cosmetic / style — 동작 영향 0, 다른 일괄 cleanup task. (예:
now()두 번 호출,as any[]통합)
How to apply
v0.2.6 brainstorm 시 본 리스트를 1차 backlog 로 사용. 항목별로:
- (a) 그대로 cleanup
- (b) #4~#6 영향 받아 변형
- (c) defer-further 결정
- (d) drop (만에 하나 outdated 또는 v0.2.4/v0.2.5 patch 가 우회 처리)
v0.2.3 #7 Telemetry skeleton 누적 (2026-05-01)
-
now()두 번 호출 —TelemetryService.emit(src/main/services/TelemetryService.ts:58, :60) 가 같은 emit 안에서this.now()두 번. 이론적 midnight straddle 가능 (ts vs filePath 다른 KST 일자), 실제 영향 cosmetic. cleanup:const nowDate = this.now()한 번 추출. -
DAY_MS = 24*60*60*1000magic number —cleanupOldFiles:39+readAllRecent:78(+KST_OFFSET_MS간접). 모듈 상단에const DAY_MS = 24 * 60 * 60 * 1000;추출. -
KST helper duplication —
TelemetryService.todayKstIso+telemetryStats.kstDate+AiWorker.todayKstAsDate/todayKstAsIso. 4번째 caller (예: 회상 schedule, 만료 batching) 등장 시src/main/util/kst.ts로 통합. -
createTraypositional 폭주 —tray.ts:51가 7 positional callbacks. #1 ollama 회복 / #4 휴지통 비우기 등 트레이 메뉴 추가 시 8+ 도달 → readability threshold 넘김.TrayCallbacksobject 로 refactor. -
AiFailedReasonunion 3 곳 중복 —'unreachable' | 'schema' | 'timeout' | 'other'가telemetryEvents.ts:15(zod),TelemetryService.ts:21(EmitInput),AiWorker.ts:19, :34(classifier + emitter) 에 분산.export type AiFailedReason하나로 통합. (단 zod enum + TS literal 의 inherent dual-define 은 어쩔 수 없음 —z.infer통해 type 파생만) -
media.gc.run()의.catch누락 — T11 에서telemetry.cleanupOldFiles의.catch일관성 처리 시media.gc도 같은 패턴 (.catch없음) 발견.backup.runDaily()와 컨벤션 통일 위해.catch((e) => logger.warn('media.gc.failed', { reason: String(e) }))추가. -
stats.md 의 reason 분포 미포함 —
telemetryStats.aggregateStats가 AI 성공률만 계산,ai_failed.payload.reason의 분포 (unreachable/schema/timeout/other counts) 는 미집계. roadmap §6.2 의 "Ollama unreachable 빈도?" 질문이 부분적으로만 답해짐. v0.2.3 dogfood 후 실제 reason 분포 보고 결정.
v0.2.3 #4 휴지통 누적 (2026-05-01)
-
stats.md exhaustiveness check —
telemetryStats.aggregateStats의 7-arm if/else if 가 union 확장 시 silent fall-through.else { const _: never = ev; }추가로 컴파일 단계 가드. -
휴지통 회수율 ratio 의미 코멘트 —
restore / trash가 event-level ratio (한 노트 trash-restore 반복 시 100% 가능). spec §6.2 의 "회수 도구 동작?" 질문에는 충분, 단 unique-note 회수율로 오해할 여지. 코드 옆 1줄 코멘트. -
restore시 AI 결과 보존 + pending_jobs 미재생성 — restore 가deleted_at = NULL만, pending_jobs 안 재생성. 사용자가 trash 도중 AI fail 한 노트를 restore 시 재처리 경로 부재. v0.2.3 dogfood 에서 빈도 보고 결정 — drop / per-note retry 버튼 / 자동 재처리 중. -
restoreNote(id)precondition 노출 — store 의 낙관적 갱신이trashNotes에 노트가 있어야 동작. 명령 팔레트 / 프로그래밍 호출 케이스 시 silently no-op. 현재는 trash view 한정이라 OK. main 이 trash/restore 시pushNoteUpdated보내도록 변경하면 더 견고. -
inbox:trashCountcap 200 silent undercount — UI 만 200 cap,repo.emptyTrash()SQL 은 unbounded. 350 노트 trash 시 dialog "200개 영구 삭제" 표시되지만 실제 350 모두 삭제.repo.countTrashed()추가로 둘 다 정확히. (잠재 UX 버그 — pull-forward 후보) -
NoteCard
mode='trash'의onDeleteddead-code — trash 카드는onPermanentDelete/onRestore만 사용.onDeletedprop 은 호출되지 않음 (App.tsx 가 pass-through). API 깔끔히 —onDeleted?optional + trash 분기 미전달. -
탭 ARIA 패턴 —
aria-pressed로 toggle 버튼 표현. canonical 은role="tab"+aria-selected. screen reader 동작 OK 지만 a11y audit 시 정정 후보. -
inbox:delete채널 rename — semantic 이 hard → soft 인데 채널 이름 그대로. v0.2.4 에서inbox:trash로 rename 검토 (기존 호출 0건 보장 후). -
per-note 영구 삭제 telemetry 사용량 — v0.2.3 dogfood 에서
permanent_deleteevent 빈도 확인. 거의 0 이면 v0.2.4 에서 per-card "영구 삭제" 버튼 제거 + bulk emptyTrash 만 (UX 단순화). 빈번하면 유지.
v0.2.3 #5 만료 추천 누적 (2026-05-01)
-
dialog 버튼 순서 vs spec §5.3 — spec 은
['취소','옮기기'], default=0, 구현은['옮기기','취소'], defaultId=1, cancelId=1(inboxApi.ts:117). 효과 동일 (default = cancel). v0.2.4 에서 spec 또는 impl 한쪽 통일. -
loadExpired()미사용 —loadInitial/refreshMeta가 inline fetch, App.tsx 도 호출 안 함 (test 만 exercise). v0.2.4 dogfood 후에도 consumer 미발생 시 제거 검토. -
store
KST_OFFSET_MSinline duplication —store.ts:166의snoozeExpired가 inline KST 계산.@main/util/kstDate.ts와 동일 알고리즘이지만 alias 경계 (main vs renderer) 로 import 불가.src/shared/util/kstDate.ts로 lift 검토. (#3, #34 와 합산 가능) -
telemetry emit
.catch(() => {})가 silent —CaptureService.listExpired/trashExpiredBatch가 그대로. v0.2.4 telemetry 하드닝 시 debug log path (project pattern 통일) 추가 검토. -
TelemetryService.test.ts 의 noteId 가드 widening —
e.kind !== 'empty_trash' && e.kind !== 'expired_banner_shown' && e.kind !== 'expired_batch_trash'체인이 #6 추가 시 더 길어짐.hasNoteId(ev)type predicate helper 추출 검토. -
NoteRepository hydrate 의
as any[]일괄 cleanup —findExpiredCandidatesround 1 review 의 nit 가 단독 fix 시 다른 hydrate-using methods 와 inconsistency.db.prepare().all()의 row type 을Record<string, unknown>[]또는 explicit row interface 로 통일하는 repo-wide refactor.
v0.2.3 #1 ollama 회복 누적 (2026-05-01)
-
createTray8 positional callbacks — #1 cut 에서 8개 도달, v0.2.4 backlog #4 와 정합 (TrayCallbacks object refactor 약속). #2 retry 또는 #6 reminder cut 에서 추가 항목 (예: "재시도 N건") 등장 시 9+ 회피 위해 본격 refactor. -
Banner CSS 스타일 inline 중복 — ExpiryBanner (
#fff7e6 / #d99500 / #946100황색) / OllamaBanner (동) / FailedBanner (#fce4e4 / #a33적색) / RecallBanner (#e8f0fe / #4a7ec0청색) 모두 색상 hardcode. v0.2.4 에서 CSS variables 또는 banner shared component (<Banner severity="warning|error|info" />) 추출 검토. -
HealthChecker
inFlight가드의 manual emit ordering — manual emit 이 inFlight 체크 전 발생해 user 가 빠르게 N번 클릭하면 N개 manual telemetry. spec 의도 (1:1 보장) 와 정합이지만, 향후 dedup 정책 (예: 1초 윈도우) 으로 변형 가능성. v0.2.4 dogfood soak 결과로 결정.
v0.2.3 #2 AI retry 누적 (2026-05-02)
-
createTray9 positional callbacks — #2 cut 에서 9개 도달 (refreshTrayFailedCount 포함). #4TrayCallbacksobject refactor 가 이제 readability blocker. #3 / #6 cut 어느 쪽이든 추가 callback 더 들어오기 전에 우선 처리. -
refreshTrayFailedCountexported singleton state —tray.ts에_failedCountmodule-scoped state + setter 패턴. 모듈 캡슐화로 작동하지만 multi-window 또는 multi-tray 시 broken. v0.2.4 refactor 시 TrayController class 또는 store-driven 으로 정리. -
AiWorker.unreachableBackoffStep단일 카운터 vs job-level — 모든 job 이 step counter 공유. 1 job timeout → step↑, 다른 job 정상 처리해도 step reset. 현재는 cross-job correlation 없으니 OK 가정 (Ollama daemon 단일이라 모든 job 이 같은 백엔드 의존). multi-provider 가 들어오면 provider-level step 으로 분리 필요.
v0.2.3 #3 태그 vocab 누적 (2026-05-02)
-
getTopUsedTags(20)magic number —AiWorker.processJob:137가repo.getTopUsedTags(20)hardcoded. spec §7 Out 에 "top-N 튜닝" 명시. v0.2.4 dogfood telemetry (tag_vocab_hit/missratio) 보고VOCAB_TOP_N모듈 상수 추출 + 튜닝 결정. -
getTopUsedTagsLIMIT-then-filter 의미 — SQL 가 limit 만큼 가져온 후 JS regex 가 후처리 → top-20 안에 한글/공백 태그 섞이면 결과 length < limit. dogfood 규모 OK 가정 + 테스트 lock-in (v0.2.3 round 1 m2 fix). v0.2.4 에서 vocab pool 확장 시 SQLGLOB으로 SQL-side 필터 대안 검토 (또는LIMIT ?*2overfetch+slice). -
vocabSetstrict-eq vs DB COLLATE NOCASE 불일치 —vocabSet = new Set(vocab)은 JS 대소문자 strict,tags.name은 COLLATE NOCASE. 현재는 kebab-case 필터로 vocab 이 항상 lowercase + AI prompt 도 lowercase 강제라 충돌 없지만, vocab pool 확장 시 (예:'Design'사용자 직접 추가)getTagIdByName('Design')은 매치하지만vocabSet.has('Design')은 miss → tagId 없는 hit 가 silently skip. v0.2.4 에서vocabSet = new Set(vocab.map(v => v.toLowerCase()))+vocabSet.has(tagName.toLowerCase())로 normalize 검토. -
AiWorker per-tag emit serial await —
for (const tag of new Set(...))안의await this.telemetry.emit(...)가 직렬. 3 태그 시 file-append 3 round-trip.Promise.all로 병렬화 가능, 단ai_succeededemit 도 serial 이라 패턴 일관성 우선 skip. v0.2.4 telemetry 하드닝 시 일괄 변경 검토. -
PROMPT_VERSIONtelemetry payload 미포함 — v0.2.3 cut 에선 단일 버전 (4) 만 굴러가서 무의미. v0.2.4/v0.2.5 prompt 튜닝 후 어느 버전이 어떤 hit-rate 만든지 추적 시tag_vocab_hit/misspayload 에promptVersion추가 검토. spec §7 Out 명시.
v0.2.3 #6 RecallBanner 누적 (2026-05-02)
-
KST midnight inline calc 4번째 복제 —
store.ts의snoozeRecall(#6) +snoozeExpired(#5) +NoteCard.todayKstIso+ 다른 1곳, 그리고kstDate.tsutil 도 별도 존재. 4 callsite 모두 동일 알고리즘. v0.2.4 에서nextKstMidnightMs()/kstTodayIso()단일 util 통합 + alias 경계 (main vs renderer) 해결책. backlog #3, #19 와 합산. -
recall_shownper-banner-lifetime emit 보장 — useState→useRef 로 race 차단했지만 RecallBanner 컴포넌트 unmount/remount 시 reset. 사용자가 페이지 이동 후 돌아오면 같은 노트가 재emit 가능. v0.2.4 dogfood telemetry 에서 동일 noteId 의recall_shown빈도 보고 결정 (per-noteId 24h dedup 또는 per-noteId 영속 마커). -
emitRecallShown/emitRecallSnoozed가 fire-and-forget 인데ipcMain.handle사용 — 더 honest 한 패턴은ipcMain.on(return value 없음). 현재는 다른 IPC 와 패턴 일관성 우선. v0.2.4 IPC 정리 시handlevson구분 일괄 검토. -
NoteCard
id="note-${id}"load-bearing — RecallBanner 의scrollIntoViewtarget. 단순 DOM lookup 이라 shadow DOM / portal 미지원. v0.2.4 에서 다른 surface (예: 검색 결과에서 스크롤) 등장 시 ref-forwarding 패턴 검토.
v0.2.3.1 Ollama Settings 누적 (2026-05-04)
-
ollama_unreachable.reason에 endpoint URL 노출 (PII 우회) —LocalOllamaProvider.healthCheck가 catch err 시reason: \unreachable: ${err.message}`로 emit.err.message안에http://192.168.x.x:11434/api/tags같은 LAN endpoint URL 포함 가능. v0.2.3.1 의 in-app endpoint UI 가 LAN 사용을 흔하게 만들어 PII 우회 노출 경로 확대. v0.2.4 telemetry 하드닝 시: error class only (network/dns/timeout/...) 또는 host 마스킹 (:11434`) 정책. PR #21 round 1 m2 deferred. -
Settings 저장 vs HealthChecker 60s tick race —
saveOllamaSettingsIPC 가health.runOnce()호출, 동시에 60s 주기 tick 도inFlight가드 통해 같이 실행 시도. 정확성 영향 0 (가드로 dedup), 단 modal 닫기 직전 banner flicker 가능. PR #21 round 1 i1 acknowledge only. v0.2.4 dogfood 에서 실제 빈도 확인 후 결정 (visible 빈도 낮으면 무시). -
OllamaSettingsModal인라인 스타일 — 60+ 줄 inline style. backlog #24 (banner CSS 추출) 와 합산. v0.2.4 에서 CSS module / theme variables 추출 시 함께. -
Modal 의 client-side URL validation 부재 — endpoint freetext 가 잘못된 형식 (예: 빈 문자열, 한글) 일 때 server-side healthCheck 만 검증. zod URL error message 가 opaque ("Invalid url"). v0.2.4 에서 client-side z.string().url() pre-check + 친화적 에러 메시지.
-
createTray10번째 positional callback — v0.2.3.1 cut 에서 10개 도달 (runOpenOllamaSettings추가). backlog #4/#26 (TrayCallbacks object refactor) blocker 수준. v0.2.4 첫 cleanup 항목 후보.
v0.2.3 / v0.2.3.1 dogfood 발견 (2026-05-05)
본 cut 들의 머지 후 사용자가 dogfood 중 발견한 항목. PR review deferred 와 달리 raw UX/bug 발견.
-
버전 및 프로그램 정보 표시 방법 부재 — 현재 사용자가 설치된 Inkling 의 버전 (package.json
0.2.3.1) 을 UI 에서 확인할 path 없음. 트레이 메뉴 / Inbox 푸터 / 별도 "About Inkling" 모달 어느 surface 에도 정보 없음. 핸드오프 후 다른 머신에서 같은 버전인지 사용자가 직접 검증 불가. v0.2.4 에서 트레이 메뉴 "Inkling 0.2.3.1 정보..." 또는 Inbox 우하단 footer 형태로 추가 검토. 곁들여: 빌드 commit SHA, electron/node 버전, OS, profileDir 경로 등 디버그 정보 노출 (사용자가 issue report 시 첨부 가능). -
윈도우 자동 실행 옵션이 재시작 후 풀려있는 버그 — 트레이 메뉴 "윈도우 시작 시 자동 실행" 체크 → 종료 → 재실행 시 체크박스가 풀려서 표시됨. 코드 (
src/main/tray.ts:47-58) 가app.setLoginItemSettings({ openAtLogin, args: ['--hidden'] })호출 후 다음 부팅 시app.getLoginItemSettings().openAtLogin이 false 반환. 추정 원인:- (a) Windows registry 에 쓴 exe path 와 현재 프로세스 path 가 다름 (NSIS 설치 위치 변경 / 버전 업데이트 시 새 디렉터리)
- (b) Electron
setLoginItemSettingsWindows 구현 의 path canonicalization 이슈 - (c) 우리
args: ['--hidden']와 actual launch 시 args 비교 mismatch - 영향: dogfood UX 핵심 마찰 — autostart 가 핸드오프 시 매번 수동 재설정 필요. 자동 실행 의도 자체가 dogfood "잊지 않고 매일 사용" 목적인데 깨짐.
- v0.2.6 에서 우선순위 높음. 진단 절차: (1)
app.getLoginItemSettings({ args: ['--hidden'] })형태로 args 전달해 비교 정확도 올리기, (2) registry 직접 inspect (HKCU\Software\Microsoft\Windows\CurrentVersion\Run\inkling) 로 path/args 확인, (3) executable path canonicalization (electron 이 short path 변환 적용 여부).
v0.2.5 critical hotfix 누적 (2026-05-05)
v0.2.5 single-instance lock hotfix (PR #23) 의 reviewer deferred 항목.
- Hidden-start race (NSIS installer 자동 실행 + 사용자 클릭 충돌) — NSIS installer 가 설치 직후 사용자가 시작메뉴 / 데스크톱 아이콘 클릭 (
inkling.exe) + autostart entry (inkling.exe --hidden) 을 짧은 간격에 둘 다 시도 시 — 첫 lock 보유자에 따라 visible 여부 race. 본 cut 의second-instancehandler 는 무조건 inbox 창 띄움 (사용자 클릭 = 보고 싶다는 강한 시그널 가정). 매우 드문 시나리오 + lock 자체는 정상 동작 (한 쪽만 살아남음).- 영향: drm-edge 케이스만, 실 사용 거의 X
- v0.2.6 에서:
app.requestSingleInstanceLock(additionalData)의additionalData: { hidden: startedHidden }전달 →second-instance(event, argv, cwd, additionalData)에서 두 번째 호출이 hidden 이면 창 안 띄우는 정책. 첫 instance 가 자기 자신의 hidden 상태와 비교해 visible 결정. - PR #23 round 1 reviewer Important — acknowledge only, defer to v0.2.6.
post-cut next-step (status, not backlog)
- 빌드 / release 흐름 (status) — v0.2.3 cut 7/7 (PR #13~#19) → binary v0.2.3 release → 11434 포트 reserved 발견 → v0.2.3.1 attempt (PR #21) → semver 거부 → v0.2.4 (PR #22, backlog 5건 + Ollama 설정 UI) → release → multi-instance bug 발견 → v0.2.5 critical hotfix (PR #23, single-instance lock) → release ✅ (2026-05-05). 다음: dogfood ≥1주 soak → telemetry export + 신규 피드백 → v0.2.6 brainstorm 트리거 (잔여 backlog 40건 일괄 triage).
v0.2.3 cut 후 final reviewer 가 칭찬한 부분
- 2-layer privacy invariant (zod outer + payload
.strict()) 가 강한 defense - KST 처리 일관성 — 4 callsites 동일 패턴
- backward compat — 기존 13 테스트 (Capture 4 + AiWorker 9) 무수정 통과
- 신규 dep 0 (zip 회피로 폴더 + 2 file 정책)
- TelemetryService surface 가 깔끔한 foundation — 다음 항목들이 (a) zod schema 추가, (b) EmitInput arm 추가, (c) emit 호출만 하면 됨