Files
inkling/CHANGELOG.md

472 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
본 파일은 Inkling 의 버전별 사용자 영향 변경 사항을 기록한다.
형식은 [Keep a Changelog](https://keepachangelog.com/) 를 느슨하게 따른다.
## [0.4.0] — 2026-05-15 (force re-tag, dogfood UX 2회 보완)
### 추가 dogfood UX 2회차 (2026-05-15 force re-tag)
dogfood 2일차 피드백 묶음. force re-tag 로 같은 v0.4.0 에 추가.
- **AI 정리하기 (default notebook batch 분류)** — 사이드바 default notebook 선택 시 main 영역 상단에 "🪄 AI 정리하기" 버튼. 클릭 → AI 가 default 의 active 노트들을 한 prompt 에 묶어 분석 → BatchMoveModal 에 noteId 별 추천 notebook + checkbox → 사용자 confirm 후 일괄 moveNote. top N=50 cap, 한국어 prompt, hallucinated notebook 이름은 null 로 coerce.
- **노트북 순서 변경** — `notebooks.sort_order` 컬럼 (m009 마이그레이션) + NotebookList row hover 시 ↑↓ 버튼. 인접 sort_order swap. drag-drop 은 v0.5+ 후보.
- **이동 UX 정리** — NoteCard 의 notebook chip 을 tag 영역 → footer 의 "이동" 버튼 옆으로 이동. tag(키워드) / notebook(컨텍스트) 의미 분리. dropdown 도 위쪽으로 펼침.
### 추가 dogfood UX 1회차 (2026-05-15)
- **NotebookChip 시각 강화** — 청색 배경 + 📓 아이콘 + ▾ caret + dropdown 헤더 "이동할 노트북". chip 클릭 시 다른 notebook 선택 dropdown 이 보이는 affordance 명확화. 다른 notebook 없으면 disabled.
- **헤더 좌측 ☰ 햄버거 버튼** — 마우스 클릭으로 사이드바 토글 (Cmd/Ctrl+B 단축키와 동일).
- **사이드바 default visible** — 새 사용자가 처음부터 사이드바 보이게 (`settings.getSidebarVisible` default false → true). 기존 사용자가 명시적으로 false 저장했다면 그 값 그대로.
- **inboxWindow 기본 크기 확장** — 900×720 → 1200×800. 사이드바 240px 가 default 가시화되므로 main 영역 확보.
### 게이트 (2회차 후)
- 단위 877 PASS
- typecheck 0 errors
---
### 원본 release (2026-05-15)
Notebooks + Lifecycle Simplification. 19일 dogfood 데이터 (archived 0건 / mlx-ops tag 가 사실상 컨텍스트 그룹 역할) 가 묶음 변경의 근거.
### 추가
- **Notebook 카테고리** — 좌측 사이드바 (Cmd+B / Ctrl+B 토글), 색 + count badge. notebooks 테이블 + notes.notebook_id FK RESTRICT. m008 마이그레이션이 default "기본" notebook 자동 생성 후 모든 기존 노트 배치.
- **AI 자동 fit 매칭** — 매 capture 시 AI 가 prompt 의 notebooks 목록을 보고 best-fit 자동 배치 (응답의 notebook_match 필드). NoteCard 의 chip 으로 1-click 변경 가능.
- **Promotion 제안** — 같은 tag 가 3건 이상 default notebook 에 누적되면 "새 notebook 으로 분리?" banner 표시. 24h snooze + 영구 dismiss 지원 (settings 의 promotion_snoozed_until_ms / promotion_dismissed_tags 영속화).
- **사이드바 UX** — NotebookCreateModal (이름 + 6색 palette + 중복 검증), NotebookList (선택 highlight + count), 모달/사이드바 toggle 단축키.
- **검색 scope 토글** — "이 노트북" / "모든 노트북" dropdown.
### 변경
- **lifecycle 3분기** — `archived` status 제거. 헤더 탭이 Inbox / 완료 / 휴지통 (3탭). MoveStatusModal 의 "보관" 옵션도 제거. classifyStatus 의 AI 응답이 archived 면 completed 로 coerce. ImportService 가 backward compat 위해 archived 노트 import 시 completed 로 변환.
- **NoteRepository.findExpiredCandidates** 의 scope 가 inbox 만 → notebook 별 scope (Task 4 의 countByStatus 옵션 등 통합 효과).
- selectedNotebookId 변경 시 list / counts 자동 refresh.
### 게이트
- 단위 테스트 851 PASS (m008 마이그레이션 / NotebookRepository / AI prompt + schema / AiWorker matching / store + settings 영속화 / Sidebar / NotebookCreateModal / PromotionBanner / NoteCard chip / SearchBox scope / archived 제거 회귀 가드).
- typecheck 0 errors.
- 신규 npm dependency 0.
### 업그레이드
v0.3.14 인스톨러 위에 v0.4.0 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. m008 마이그레이션이 첫 launch 시 자동 실행되어 default notebook "기본" 생성 + 모든 기존 노트 배치.
## [0.3.14] — 2026-05-12 (force re-tag: 2026-05-14, 추가 dogfood fixes 7건)
### 추가 dogfood fixes (2026-05-14 force re-tag)
force re-tag 로 같은 v0.3.14 안에 묶인 후속 변경. 새 minor 안 늘리고 동일 release notes 확장.
- **fix(capture): QuickCapture blur-on-hide 제거.** 다른 창 클릭해도 ESC / Cmd+Enter 까지 창 유지. alwaysOnTop + screen-saver level 로 다른 앱 위에 떠 있음.
- **chore(ux): macOS 사용자 위해 Cmd 키 hint 안내.** Inbox empty state 와 QuickCapture 하단 hint 가 platform-aware 로 분기 — Mac 에선 `Cmd+Shift+J` / `Cmd+Enter`.
- **fix(macos): hidden autostart dock indicator + autostart mismatch false positive.** ① LoginItems `--hidden` spawn 시 NSPanel 만 떠 있어 dock 점 안 보이던 문제 — inboxWindow 를 hidden 으로 미리 create + `showInactive → hide` trick 으로 NSApp register. ② SettingsPage 의 자동실행 mismatch 경고가 macOS 13+ SMAppService 한계로 false positive 떠 있던 문제 — willLaunch 신호는 win32 에서만 mismatch 판정에 사용.
- **feat(notes): 원문 편집/이력 복원 시 AI 재처리.** `NoteRepository.markAiPendingForReprocess` 신설 — done/failed/pending 노트를 pending reset + pending_jobs 재투입. disabled 는 사용자 의도 존중 no-op. NoteCard.saveRaw 가 optimistic 으로 `aiStatus='pending'` 표시. updateAiResult 의 user-edit 가드로 사용자가 직접 편집한 필드는 보존.
- **feat(expiry): 마감 알림 inbox 제한 + 오늘 당일 포함 + 헤딩/라벨/메모 바로가기.** ① `findExpiredCandidates``due_date <= today` + `status='active'` 로 변경, 완료/보관 노트는 제외. 정렬도 `due_date DESC` 로 오늘 → 어제 순. ② ExpiryBanner 헤딩 분리 카운트 "오늘 마감 X · 지난 Y", 노트 라벨 [오늘] / [N일 지남]. ③ 노트 제목 클릭 → `note-{id}` smooth scroll.
- **fix(sync): manifest.exported_at 제거 — no-op push 회피.** 노트 변경 0건이어도 매 sync 마다 timestamp 1줄 commit + push 가 쌓이던 문제. `composeManifest` 에서 cosmetic 필드 제거. 이제 진짜 변경 있을 때만 commit.
- **feat(settings): SectionIntro + SyncHelpModal 풀어쓰기.** 설정 페이지 6 section 상단에 1-2 문장 안내. SyncHelpModal 의 기술 용어 (rebase, fast-forward, NTP) 를 사용자 언어로 풀어쓰기.
### 게이트 (추가 fix 후)
- 단위 763 PASS
- typecheck 0 errors
---
### 원본 release (2026-05-12)
AI 처리 fail 원인 가시화. 이전엔 ai_error 가 NoteCard tooltip (title attribute) 에만 있어 사용자가 마우스 오버해야 보이는 데다 raw 메시지만 노출 → 무엇이 fail 했는지 불명.
### 수정
- **NoteCard failed 노트에 "원인 보기" 접힘 섹션 (P1).** `<details>` summary 클릭하면 `<pre>``ai_error` 전체 노출. wrap + word-break 적용. 사용자가 직접 메시지를 보고 모델/네트워크/JSON 등 fail 카테고리 진단 가능.
- **`ai_error` 에 reason + provider name prefix 추가.** AiWorker 의 markAiFailed 시 `[schema|other] local-ollama/gemma4:26b\n<원본 message>` 형식. 사용자가 어느 카테고리에서, 어느 모델로 실패했는지 즉시 식별. log 의 ai.failed 에도 reason/provider 필드 함께 출력.
### 게이트
- 단위 752 PASS (ai_error 포맷 변경은 test 영향 없음 — 기존 test 가 정확한 prefix 매칭 안 함)
- typecheck 0 errors
- 신규 npm dependency 0
### 사용자 안내
이미지 AI 처리가 fail 한다면 NoteCard 의 "정리 보류" 옆 "원인 보기" 클릭 → 표시되는 메시지로:
- `[timeout] ...` → vision 모델 cold-start 가 5분 초과. `ollama run gemma4:26b` 으로 한번 warm-up 후 재시도
- `[schema] title must contain Korean characters` → vision 모델이 영어 title 반환. prompt 가 한국어 강조했지만 일부 모델은 여전히 영어. `gemma3:27b` 등 다른 vision 모델로 대체 고려
- `[schema] unparseable response: ...` → vision 모델 JSON 출력 안 따름. v0.3.12 의 loose parse 가 실패한 경우
- `[other] missing response field` → Ollama 가 빈 응답 반환. 모델 자체 문제
### 업그레이드
v0.3.13 인스톨러 위에 v0.3.14 인스톨러를 같은 위치에 실행하면 in-place 업그레이드.
## [0.3.13] — 2026-05-12
대형 vision 모델 (gemma4:26b 등) 의 cold-start timeout 으로 인한 AI 처리 실패 fix.
### 수정
- **Vision generate 의 timeout 확장 120s → 300s (P1).** `gemma4:26b` (25B MoE 가중치 + vision encoder 550M) 같은 대형 vision 모델은 첫 generate 시 모델 load + 이미지 encoding 으로 60-180s 소요. 기본 120s timeout 으로 첫 호출 시 abort → fail 빈번. vision path 에 한해 `Math.max(timeoutMs, 300_000)` 적용 (text-only path 영향 없음).
확인: gemma4 family 는 [공식 release](https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/) — 26B variant 가 Text+Image 양 modality 지원 ([ollama library](https://ollama.com/library/gemma4:26b)). 본 코드의 `VisionDetect` 가 'gemma4' family 인식하므로 사용자가 settings → Vision 섹션에서 선택 가능.
### 사용자 안내
이미지 AI 처리가 여전히 실패한다면:
1. 설정 → AI 제공자 → Vision 섹션에서 `gemma4:26b` (또는 vision-capable 모델) 가 선택돼있는지 확인
2. `ollama list` 로 모델 실제 설치 여부 확인 (`ollama pull gemma4:26b` 필요)
3. NoteCard 의 failed 노트 텍스트 위에 마우스 오버 → tooltip 의 `ai_error` 확인 (구체 fail mode 진단)
### 게이트
- 단위 752 PASS (timeout 상수만 변경 — 회귀 없음)
- typecheck 0 errors
- 신규 npm dependency 0
### 업그레이드
v0.3.12 인스톨러 위에 v0.3.13 인스톨러를 같은 위치에 실행하면 in-place 업그레이드.
## [0.3.12] — 2026-05-12
이미지 AI 처리 실패 fix. vision model 의 응답이 strict JSON 이 아닌 경우 (markdown fence / prose 섞임) 가 흔해 schema parse 단계에서 throw → `ai_status='failed'` 도달.
### 수정
- **Vision model 응답 JSON loose parse (P1).** `LocalOllamaProvider.generate``JSON.parse` 가 strict 라 vision-tuned 모델의 markdown 코드 펜스 / 앞뒤 prose 응답에서 throw. `parseJsonLoose` 헬퍼 추가 — 첫 `{` ~ 마지막 `}` substring 추출 fallback. 실패 시 raw response 200자 snippet 포함한 에러 throw (디버깅 가시화).
- **Vision prompt 강화 (P1).** `buildVisionPrompt` 가 "title 한국어로 요약" 만 명시 → schema 의 KOREAN_REGEX/KEBAB_REGEX 강제와 불일치. 규칙 4건 명시: title 한국어 60자, summary 3줄, tags 영문 kebab-case 3개, due_date ISO 또는 null. "markdown 코드 펜스 금지" 명시로 JSON-only 출력 강제.
알려진 한계:
- `gemma4:26b` 같이 Ollama 에 실제로 release 안 된 모델명 사용 시 healthCheck 통과해도 generate 가 unknown model 로 throw. 모델 설치 여부는 사용자가 `ollama list` 확인 필요.
### 게이트
- 단위 750 → **752 PASS** (+2: markdown fence 추출 + prose 추출)
- typecheck 0 errors
- 신규 npm dependency 0
### 업그레이드
v0.3.11 인스톨러 위에 v0.3.12 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.11] — 2026-05-12
붙여넣은 이미지 / 저장된 이미지가 양쪽 창에서 표시 안 되던 CSP 누락 hotfix.
### 수정
- **QuickCapture: paste 이미지 thumbnail 렌더 실패.** `quickcapture/index.html` 의 CSP 가 `img-src` 미지정 → `default-src 'self'` fallback → `URL.createObjectURL``blob:` URL 차단. `img-src 'self' data: blob:` 추가.
- **Inbox: 저장된 노트 이미지 렌더 실패.** `inbox/index.html` 의 CSP `img-src 'self' data: blob: file:``inkling-media:` 미허용 → `NoteCard``<img src="inkling-media://media/..." />` 차단 (custom protocol 자체는 main 에서 등록됐지만 renderer CSP 별도). `inkling-media:` 추가.
v0.3.0 (Cut A 이미지 첨부) 이후 양쪽 창에서 paste/render 가 잠재적으로 깨져있던 회귀. 사용자가 dogfood 중 발견.
### 게이트
- 단위 750 PASS (CSP meta tag 만 변경 — 코드 path 영향 없음)
- typecheck 0 errors
- 신규 npm dependency 0
### 업그레이드
v0.3.10 인스톨러 위에 v0.3.11 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.10] — 2026-05-12
macOS fullscreen 환경에서 QuickCapture 핫키 (Cmd+Shift+J) 가 작동하지만 강제로 홈 데스크탑으로 Space 전환 후 표시되던 버그 fix.
### 수정
- **macOS fullscreen Space 위에 QuickCapture 표시 (P1).** 기본 BrowserWindow 는 첫 생성된 Space (홈 데스크탑) 에만 표시 → 사용자가 다른 앱 fullscreen 중에 핫키 누르면 macOS 가 강제로 Space 전환 → 사용자 흐름 단절. `quickCaptureWindow.ts` 에 darwin 분기 추가:
- `type: 'panel'` — fullscreen Space 위에 floating 가능한 macOS native panel
- `setAlwaysOnTop(true, 'screen-saver')` — fullscreen app 위에 띄울 수 있는 가장 높은 level
- `setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })` — 현재 Space (fullscreen 포함) 에 함께 표시, Space 전환 차단
Windows / Linux 동작은 변경 없음 (darwin 분기만).
### 게이트
- 단위 750 PASS (변경 없음 — main window 코드는 단위 테스트 대상 아님)
- typecheck 0 errors (src)
- 신규 npm dependency 0
- macOS 사용자 수동 검증 완료
### 업그레이드
v0.3.9 인스톨러 위에 v0.3.10 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.9] — 2026-05-11
v0.3.8 audit 의 미수정 edge case 3건 완료. AI 처리 흐름의 사용자 unblock path + FTS5 query 안전성.
### 수정
- **`ai_status='pending'` 노트 cancel UI 부재 (P1).** Ollama 끊김 / 무한 pending 상태에서 사용자가 빠져나오는 path 가 없었음. NoteCard 의 pending 표시 옆에 "건너뛰기" 버튼 추가 → `inboxApi.cancelPending(id)``repo.cancelPending`: `ai_status='disabled'` + `pending_jobs` DELETE. raw_text 는 보존. pushNoteUpdated emit 으로 renderer 자동 sync.
- **`ai_status='failed'` 노트 per-note 재시도 UI 부재 (P2).** 이전엔 FailedBanner 의 일괄 재시도만 가능. NoteCard 의 failed 표시 옆에 "재시도" 버튼 추가 → `inboxApi.retryOneFailed(id)``repo.retryOneFailed`: failed → pending + `pending_jobs` INSERT + worker enqueue. pushNoteUpdated emit.
- **FTS5 query escape 불완전 (P2).** `sanitizeFtsQuery` 의 special chars regex 가 `["*():]` 만 처리 → backtick/dash/caret 미escape 로 일부 입력이 FTS5 parser throw 야기. `["*():`^\-]` 로 확장. 한국어 사용자가 의도 없이 입력할 가능성 높은 punctuation 까지 안전 처리.
### 미수정 (의도)
- **동시 편집 race (P2).** EditableField 가 이미 `editing=true` 중 value prop 변경을 무시하는 guard 보유. 사용자 입력은 보존됨 (last-write-wins). 추가 코드 불필요.
### 게이트
- 단위 745 → **750 PASS** (+5: repo retryOneFailed 2 + cancelPending 2 + FTS sanitize 1)
- typecheck 0 errors (src)
- 신규 npm dependency 0
### 업그레이드
v0.3.8 인스톨러 위에 v0.3.9 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.8] — 2026-05-11
전수 audit 후 발견된 사용자 상호작용 hole 8건 일괄 hotfix. 핵심은 (1) push-based status 동기화 root fix, (2) modal Escape affordance 통일, (3) IPC 실패 resilience.
### 수정
- **`inbox:set-status` IPC 가 `pushNoteUpdated` emit 안 함 (root fix).** 이전엔 setStatus 후 counts/list/search 가 모두 stale → 호출처마다 `refreshMeta()` 명시 호출이 필요했음 (v0.3.5 워크어라운드). 이제 IPC 핸들러가 `repo.findById()``pushNoteUpdated` 호출. renderer 의 `onNoteUpdated` 콜백 1개 path 로 모든 status 전이가 일관 갱신.
- **`upsertNote` 가 current view 무시 → 잘못된 status 노트 잔류.** 이전엔 trashed 외 모든 status 를 `notes` 에 누적 → 사용자가 inbox 에서 완료로 옮긴 노트가 list 에 잔류. v0.3.8 부터 `viewStatus` (inbox→active, completed→completed, archived→archived) 와 매칭되는 status 만 유지. `searchResults` 도 동일 패턴.
- **`upsertNote` 의 trash 판정을 `deletedAt``status='trashed'` 로 전환.** m004 이후 status 가 single source of truth, deletedAt 은 backward-compat mirror. sync conflict 후 두 컬럼 불일치 가능성 대비.
- **`restoreNote` 가 status 도 'active' 로 갱신.** 이전엔 `deletedAt: null` 만 clear → upsertNote 가 status='trashed' 그대로 라 여전히 trashNotes 에 잔류.
- **OnboardingWizard close path 부재.** IPC 실패 시 무한 wizard 잠금 → `try/catch + setBusy/error state + "지금 건너뛰기" 버튼 + Escape` 추가. 첫 launch 사용자 막힘 회피.
- **Modal Escape key dismiss 통일.** `MoveStatusModal` / `RevisionHistoryModal` / `ConflictModal` / `SyncHelpModal` / `OnboardingWizard` 모두 `keydown` listener 추가. MoveStatusModal 은 overlay 클릭 close 도 추가 (다른 modal 들은 이미 외부 클릭 지원).
- **`store.ts` async 함수 error-resilient.** `loadInitial` / `loadByView` / `searchNotes` / `loadReview` / `refreshMeta` 가 IPC throw 시 try/catch 로 감싸 무한 loading / stale data 회피. loadInitial 은 catch 시 `loading: false`, loadByView 는 빈 list, searchNotes 는 빈 결과, loadReview 는 빈 aggregate 로 graceful fallback.
### 갱신
- **NoteCard 의 명시적 `refreshMeta` 호출 보존** — onNoteUpdated path 가 이미 refreshMeta 호출하므로 redundant 지만 backup 으로 유지 (2번 fetch 만 발생, 무해).
### 게이트
- 단위 739 → **745 PASS** (+6: view-aware upsertNote 3 + setStatus push emit 1 + Modal Escape 1 + Modal overlay 클릭 1)
- typecheck 0 errors (src)
- 신규 npm dependency 0
### 업그레이드
v0.3.7 인스톨러 위에 v0.3.8 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
### 미수정 (낮은 우선순위 / 별도 작업)
- ai_status='pending' 노트 cancel UI (P1, 별도 spec 필요)
- ai_status='failed' 노트 per-note 재시도 UI (P2)
- FTS5 query escape (P2, 확인 필요)
- 동시 편집 race condition (P2)
## [0.3.7] — 2026-05-11
`MoveStatusModal` 의 button hardcode 로 인해 완료/보관/휴지통 노트가 inbox 로 돌아올 수 없던 버그 fix. v0.2.9 Cut B 부터 존재한 잠재 결함 (dropdown 의 `possibleTargets` 필터가 modal 까지 흐르지 못함).
### 수정
- **완료/보관/휴지통 노트의 Inbox 복원 path 부재.** `MoveStatusModal``완료/보관/휴지통` 3 button hardcode 라 currentStatus 외 3 status 만 동적으로 노출해야 한다는 의도가 누락. `currentStatus: NoteStatus` prop 추가 + 4 status 중 current 제외 동적 render. NoteCard 가 `local.status` 전달.
- **status label 일관성** — `statusLabel('active')` 가 '활성' 이었으나 헤더 탭 표기는 'Inbox'. modal button + AI 추천 텍스트 양쪽 모두 'Inbox' 로 통일.
### 게이트
- 단위 736 → **739 PASS** (+3: completed/archived/trashed currentStatus button list 검증)
- typecheck 0 errors (src)
- 신규 npm dependency 0
### 업그레이드
v0.3.6 인스톨러 위에 v0.3.7 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.6] — 2026-05-11
v0.3.5 의 이동 dropdown 단순화가 사용자 의도와 어긋난 점 정정. 이동 modal (사유 + AI 자동 분류 + 수동 status 선택) 은 보존해야 하는 핵심 UX 였음.
### 수정
- **이동 dropdown → 단일 "이동" 버튼 + `MoveStatusModal` 복원.** v0.3.5 에서 dropdown 항목 클릭 = 즉시 setStatus 로 단순화한 path 를 되돌림. 사용자 의도는 dropdown 의 목적지 중복 (modal 도 목적지 묻기) 제거였지, modal 자체 제거가 아니었음. 단일 "이동" 버튼 → modal → 사유 입력 + AI 자동 분류 + 수동 status 선택 path 로 통일.
- **`MoveStatusModal.tsx` + 테스트 6 case 복원** — v0.3.5 에서 dead code 로 판단해 삭제했으나 다시 mount 됨. statusLabel 헬퍼 위치는 modal 내부로 회귀 (orphan `statusLabel.ts` 제거).
- **이동 후 `refreshMeta()` 호출 유지** — v0.3.5 D1 fix (setStatus IPC 가 pushNoteUpdated emit 안 함 → 헤더 탭 count stale) 는 modal `onMoved` callback path 에서도 동일하게 트리거.
### 게이트
- 단위 734 → **736 PASS** (NoteCard 이동 case 3 → 2 + MoveStatusModal 6 복원)
- typecheck 0 errors (src)
- 신규 npm dependency 0
### 업그레이드
v0.3.5 인스톨러 위에 v0.3.6 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.5] — 2026-05-11
v0.3.4 까지 누적된 dogfood UX 결함 7건 hotfix. 사용자가 막혔던 inbox/회고/이동 3건 + 그 부류의 동반 갭 4건. 데이터/마이그레이션 변경 없음 (스키마 v8 그대로).
### 수정
- **Inbox 탭 진입 실패: 다른 보관함에서 inbox 로 못 돌아옴.** `setView('inbox')` 가 reload 를 호출 안 해서 `notes` state 가 이전 view 의 status 로 stale. `loadByView` 시그니처에 `'inbox' → 'active'` 매핑 추가 + setView 의 reload 분기에 inbox 포함.
- **회고 view 탈출 불가.** `ReviewView` 가 App 의 헤더를 우회 (early return) 해서 사용자가 뒤로 갈 길이 없던 문제. `← 돌아가기` 버튼 추가 — 클릭 시 `setView('inbox')`.
- **이동 dropdown 의 modal 중복 질문.** dropdown 에서 "완료로 이동" 선택해도 `MoveStatusModal` 이 떠서 목적지를 재확인. dropdown 클릭 = `inboxApi.setStatus(id, target, null)` 즉시 호출로 단순화. modal path 자체 제거 (`MoveStatusModal.tsx` + 동반 테스트 삭제, `statusLabel` 헬퍼는 별도 `statusLabel.ts` 로 분리).
- **이동 후 헤더 탭 count stale.** `setStatus` IPC 가 `pushNoteUpdated` emit 을 안 해서 `refreshMeta` 가 트리거되지 않던 잠재 버그. dropdown 이동 path 끝에 `refreshMeta()` 명시 호출 추가.
- **View 전환 시 검색/태그 필터 잔류.** `setView``searchResults`/`searchQuery`/`tagFilter` 를 reset 안 해서 이전 view 의 필터가 완료/보관/휴지통/회고에 잘못 적용. 한 번에 reset.
- **Inbox 첫 로드와 탭 reload 결과 불일치.** `loadInitial``listNotes()` (= `deleted_at IS NULL` = active+completed+archived 혼재) 사용 → 헤더 inbox count (active 만) 와 list 불일치. `listByStatus('active', limit:50)` 로 통일.
- **AI 배너가 completed/archived 탭에서도 노출.** OllamaBanner/PendingBanner/FailedBanner/ExpiryBanner/RecallBanner/RecoveryToast 가 `!showTrash` 만 체크해서 active 무관 컨텍스트에서도 그림. `view === 'inbox'` 분기로 한정.
### 갱신
- **이동 dropdown UX** — 메뉴 열린 상태에서 외부 클릭 / Escape 로 닫힘 (mousedown + keydown listener, useEffect 로 menuOpen=true 일 때만 활성).
### 게이트
- 단위 738 → **734 PASS** (MoveStatusModal 6 case 삭제 + NoteCard 메뉴 case 1 → 3 (직접 이동/외부 클릭/Escape) 로 재구성)
- typecheck 0 errors (src)
- 신규 npm dependency 0
### 업그레이드
v0.3.4 인스톨러 위에 v0.3.5 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음.
## [0.3.4] — 2026-05-11
v0.3.0 Cut E (양방향 sync) dogfood 의 결과로, 사용자가 conflict 시나리오에 막힌 순간 도움받을 곳이 부재한 갭을 메운 cut. 3 표면 (in-app modal + ConflictModal inline + README) 통합 도움말. PR #33 머지.
### 신규
- **`SyncHelpModal` (4 anchor 섹션)** — 설정 → 동기화 저장소 → "도움말" 버튼 또는 ConflictModal 의 "자세히 보기 →" 링크에서 진입. `#main-conflict` (편집/편집·삭제/편집·AI 결과 충돌 결정 트리) / `#auto` (fetch+rebase·첫 sync·push 거부·자동 주기) / `#silent` (NTP·동시 수정·자동 sync 실패 silent) / `#setup` (URL SSH/HTTPS 형식·잘못된 `git@https://` 사례·인증 helper·연결 테스트 실패 troubleshoot·URL 재설정).
### 갱신
- **`ConflictModal` inline 설명** — 각 conflict row 의 "내 것 사용" / "원격 사용" 의미를 1-2 줄 인라인 안내 + (옵션) "자세히 보기 →" 링크 (onOpenHelp callback). 기존 caller backward-compatible (optional prop).
- **`SyncSection` 도움말 버튼** — URL row 마지막에 추가. busy (저장/테스트/sync 진행) 중에도 도움말 reachable (read-only).
- **`README` 동기화 섹션 통째 재작성** — stale "원격 백업 (F6-L2)" (v0.2.1 MVP, 트레이 "지금 동기화" + 수동 `git init` 안내) → "동기화 (Git, F21 Cut E)". 일회 설정 / 일상 사용 / 충돌 해결 (3 케이스) / Silent risk / Troubleshoot. in-app SyncHelpModal 과 동일 정보 산문체.
### 게이트
- 단위 727 → **738 PASS** (+11): SyncHelpModal 7 + ConflictModal 회귀 3 + SyncSection 회귀 1
- typecheck 0 errors
- 신규 npm dependency 0
### 후속 (deferred)
- ESC key handler (현재 SyncHelpModal / ConflictModal 모두 X + overlay 만, 프로젝트 패턴 정합. 도입 시 양쪽 동시).
- 1주 dogfood soak 후 도움말 텍스트 정합성 1차 갱신 (실제 사용자 경험과 어긋난 부분 보강).
### 업그레이드
v0.3.3 인스톨러 위에 v0.3.4 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음 (스키마 v8 그대로).
## [0.3.3] — 2026-05-10
v0.3.0 Cut E (양방향 sync) dogfood 첫 시도 중 발견된 sync 설정 ENOENT 버그 hotfix.
### 수정
- **Sync 설정 첫 저장 실패 (git init ENOENT)**: 설정 → 동기화 저장소에서 URL 입력 후 "저장" 클릭 시 `git init failed: fatal: cannot change to '<profileDir>/sync': No such file or directory` 로 실패하던 문제. `settings:configure-sync` IPC 핸들러가 `git -C <syncDir> init` 호출 전에 syncDir 디렉토리를 생성하지 않아 git 이 chdir 단계에서 죽음. `SyncService.runSync()` 의 동일 패턴 (`mkdir(syncDir, { recursive: true })`) 을 핸들러에도 추가. 결과적으로 "연결 테스트" 버튼이 영영 활성화되지 않던 연쇄 증상 (저장 성공 시에만 url state 채워지고 버튼 enable) 도 자동 해소.
### 게이트
- 단위 테스트: `tests/unit/sync-ipc.test.ts` 18 (mkdir 호출 순서 회귀 1 추가)
- typecheck: 0 errors
- 신규 npm dependency: 0
### 업그레이드
v0.3.2 인스톨러 위에 v0.3.3 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음 (스키마 v8 그대로).
## [0.2.2] — 2026-04-26
v0.2.1 dogfood 중 발견된 F7 (Due Date 합성 표현) + Quick Capture 스크롤 버그를 묶은 패치.
### 수정
- **F7 — AI-primary due_date flow** (#11, 1c72b64·723dccd·579450e): "내일 모레" 가 "내일" 로 잘못 매칭되던 문제. 규칙 파서를 first-match-wins 단일 추출에서 `parseAllCandidates` 다중 후보 추출로 확장하고, 후보 리스트를 프롬프트 힌트로 주입해 AI 의 `dueDate` 를 최종값으로 채택. 모호한 합성 표현 ("내일 모레", "다음 주 이번 주") 은 AI 가 `null` 반환 → 사용자 수동 입력으로 위임. `PROMPT_VERSION` 3.
- **Quick Capture 창 스크롤 차단**: Ctrl+Shift+J 캡처 창에서 입력이 길어지면 textarea 가 flex `min-height: auto` 때문에 hint (`Ctrl+Enter 저장 · Esc 취소 · 이미지 붙여넣기`) 를 카드 밖으로 밀어내고 윈도우에 스크롤바가 생기던 문제. `textarea { min-height: 0 }` + `.card`/`html, body, #root { overflow: hidden }` 로 textarea 자체 스크롤로만 동작하도록 격리.
### 게이트
- 단위 테스트: 197 → **205** (+8, F7 `parseAllCandidates` 7 + AiWorker flow flip 1)
- e2e smoke: 1/1
- typecheck: 0 errors
- 신규 npm dependency: 0
### 업그레이드
v0.2.1 인스톨러 위에 v0.2.2 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음 (스키마 v2 그대로).
## [0.2.1] — 2026-04-26
v0.2.0 dogfood-feedback 8 항목 로드맵 (`docs/superpowers/specs/2026-04-26-feedback-roadmap-design.md`) 을 한 번에 흡수한 cut.
### 신규 기능
- **F6-L1 로컬 스냅샷** — `<profileDir>/backups/inkling-YYYY-MM-DD.sqlite` 자동 백업 (앱 시작/종료 시) + GFS 로테이션 (14 daily · 5 Mondays · 6 monthly). 트레이 → "지금 백업".
- **F5 마크다운 export** — 모든 노트를 frontmatter 마크다운 + `index.jsonl` + `manifest.json` + 미디어 트리로 사용자 지정 폴더에 내보내기. RAG 친화 형식. 트레이 → "내보내기...".
- **F6-L3 Import** — F5 export 트리를 DB 에 다시 적재. 충돌 정책: id 동일 + raw_text 동일 → skip; id 동일 + raw_text 상이 → 새 uuidv7 (`raw_text` 불변 invariant 보호); id 신규 → insert. 트레이 → "백업에서 복원...".
- **F1 Due Date 추출** — 한국어 노트 본문에서 마감일 자동 추출 (규칙 파서 14 카테고리 + AI 폴백). NoteCard 에 `📅 YYYY-MM-DD` 인라인 라벨 + 클릭 편집. 만료 시 회색 + 취소선.
- **F2 태그 클릭 동작 분리** — 칩 텍스트 클릭 = 동일 태그 노트만 필터, 별도 ✕ 버튼이 제거 (5초 undo 토스트).
- **F6-L2 Git sync (MVP)** — 트레이 → "지금 동기화" 가 F5 export 트리를 `<profileDir>/sync/` 에 쓰고 `git add/commit/push`. 앱 종료 시 자동 1회. 사용자가 일회 `git init && git remote add origin <url>` 설정 필요. README 의 "원격 백업" 섹션 참고.
- **F4-C·F cue 강화** — 트레이 tooltip + 메뉴 첫 항목 + Inbox 헤더에 오늘 KST 캡처 카운트 (`오늘 N번 잡아뒀다`).
### 카피 변경 (F3 + F4-E)
표면별 자연 한국어로 재배치 — "기억 구출" → 표면별 다른 동사:
| 표면 | 이전 | 신규 |
|------|------|------|
| 트레이: Inbox | 구출한 메모 보기 | 보관한 메모 보기 |
| 트레이: 새 메모 | 기억 구출하기 | 한 줄 적기 |
| 토스트 #2 | 나중에 찾을 수 있게 보관했습니다. | 머릿속에서 꺼내 두었습니다. |
| 토스트 #3 | 방금 하나의 업무 기억을 구출했습니다. | 방금 한 줄 잡아뒀습니다. |
| Inbox 빈 상태 | 첫 기억을 구출해보세요. | 머릿속에 떠다니는 한 줄을 적어보세요. |
| QC 힌트 | Ctrl+Enter 구출 | Ctrl+Enter 저장 |
| 제품 표어 | local-first 기억 구출 도구 | local-first 한 줄 보관 도구 |
`strategy.md` §1, §4.1, §9, §11 도 동반 갱신. F4-E (Zeigarnik priming) 효과를 빈 상태 카피에 반영.
### 데이터 / 마이그레이션
- **Migration v2**: `notes` 테이블에 `due_date TEXT`, `due_date_edited_by_user INTEGER NOT NULL DEFAULT 0` 컬럼 추가 (NULL 기본, 비파괴).
- **Pre-migration snapshot**: `openDb()` 가 마이그레이션 직전 `<dbFile>.pre-v<N>.bak` 생성 (v0.2.0 → v0.2.1 첫 실행 시 자동, 한 번만). 마이그레이션 결함 시 회수 가능 (F6-L1 후속 #4 반영).
- **`raw_text` 불변** invariant 그대로. F6-L3 import 의 충돌 정책이 이 invariant 를 보호.
### 시스템 의존
- **git CLI** — F6-L2 가 사용자 시스템의 `git` 을 호출. Windows: Git for Windows + Credential Manager 자동 설정. 이미 README 사전 요구에 포함.
### 게이트
- 단위 테스트: 52 → **197** (+145)
- e2e smoke: 1/1
- typecheck: 0 errors
- 신규 npm dependency: 0
### Deferred / 후속
다음 기능은 데이터 측정이 필요하여 v0.2.1 dogfood soak 후 별도 brainstorm 으로 이동:
- F4-A: 잠금/잠금해제 hook (variable interval prompt)
- F4-B: ambient if-then 온보딩
- F4-D: 무작위 부드러운 알림
또한 F6-L2 의 Settings UI · safeStorage 토큰 관리 · 자동 conflict resolution · 5분 debounce 등은 MVP 외이며, 데이터 손실 위협 없이 일단 사용 후 우선순위 결정.
### 업그레이드
v0.2.0 인스톨러 위에 v0.2.1 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. `%APPDATA%\Inkling\Inkling\profiles\default\` 데이터 보존. 첫 실행 시 자동 pre-migration snapshot + migration v2 + F6-L1 첫 백업 생성.
권장 절차 (안전):
1. v0.2.0 트레이 → "지금 백업" 1회
2. v0.2.0 트레이 → "내보내기..." 1회 (F5 — schema-agnostic 마크다운)
3. v0.2.0 종료
4. `Inkling Setup 0.2.1.exe` 실행 → 같은 폴더에 설치
5. 첫 실행 시 migration v2 자동 적용
복원 경로 2 가지:
- (a) `<dbFile>.pre-v2.bak` 으로 SQLite 자체 복원 (v0.2.0 인스톨러 재설치 필요)
- (b) F5 export → v0.2.1 의 F6-L3 import 로 schema-agnostic 복원 (더 빠름)
## [0.2.0] — 2026-04-25
슬라이스 v0.4 — 가장 얇은 종단 경로 (Quick Capture → SQLite → 로컬 Ollama → Inbox) + electron-builder NSIS 인스톨러 + Windows autostart hook + post-slice 마이그레이션 정리. 자세한 결정 이력은 `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` 와 git log 참조.
[0.2.2]: https://gitea.altair823.xyz/altair823-org/inkling/releases/tag/v0.2.2
[0.2.1]: https://gitea.altair823.xyz/altair823-org/inkling/releases/tag/v0.2.1
[0.2.0]: https://gitea.altair823.xyz/altair823-org/inkling/releases/tag/v0.2.0