# Dogfood 피드백 수집

**작성일:** 2026-04-25 (open, ongoing — v0.4 slice → v0.2.x cuts 까지 누적)
**최종 갱신:** 2026-05-05 (v0.2.6 정식 cut 후, F8~F13 dogfood 발견 추가)
**저자:** 김태현 (dlsrks0734@gmail.com)
**문서 성격:** v0.4 slice → v0.2.x cuts 동안 누적된 본인 dogfood 피드백 수집·정제 living document. 각 항목은 후속 spec/cut 으로 승격될 후보다.

**선행 문서:**
- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` v0.4 (슬라이스 본문)
- `docs/superpowers/specs/2026-05-01-v023-feedback-roadmap-design.md` (v0.2.3 7항목 cut 로드맵)
- `docs/superpowers/strategy/strategy.md` (심리학 전략)
- `docs/superpowers/strategy/dogfood-strategy.md` (dogfood 운영안)
- `docs/superpowers/v024-backlog.md` (PR review deferred + dogfood 발견 누적 backlog, 잔여 24건)

---

## 진척 흐름 요약 (2026-05-05 기준)

본 문서가 작성된 v0.4 slice 시점 이후 흐름:

| 시점 | Cut | 주요 피드백 / 발견 |
|---|---|---|
| 2026-05-01 | v0.2.3 7항목 roadmap (PR #13~#19) | v0.2.2 dogfood 발견 6건 (`memory/project_v022_feedback.md`) → telemetry skeleton + 휴지통 + 만료 추천 + Ollama 회복 + AI retry + 태그 vocab + RecallBanner |
| 2026-05-04 | v0.2.3.1 (PR #21) → v0.2.4 (PR #22, semver 우회) | **F8 11434 reserved**, **F9 multi-instance**, **F10 버전 정보 부재** |
| 2026-05-05 | v0.2.5 critical hotfix (PR #23) | **F11 single-instance lock 부재** (critical, 해결됨) |
| 2026-05-05 | v0.2.6 정식 cut (PR #24) | **F12 autostart 풀림**, **F13 hidden-start race** (해결됨) |

**v0.2.7 brainstorm 트리거**: dogfood ≥1주 soak 후 telemetry export + 신규 피드백 + 잔여 backlog 24건 일괄 triage.

---

## 0. 사용 규칙

- 새 피드백은 다음 번호 (`F<N>`) 로 추가. 절대 기존 번호 재사용 금지 (외부 링크 안전성).
- 각 항목 상단에 상태 라벨 한 줄:
  - 🌱 **raw** — 막 적은 관찰. 가공 안 됨.
  - 🔬 **drafting** — 범위·가설·결정 대기 항목 분석 중.
  - 📝 **ready-for-spec** — 정식 spec 으로 승격 가능. 다음 단계는 별도 파일 추출.
  - 🚀 **promoted** — 별도 spec 파일로 분기 완료. 본 문서엔 요약 + 링크만.
  - ❌ **rejected** — 가설 미달 또는 우선순위 외, 닫음.
- 각 항목의 표준 슬롯: **관찰 / 제안 방향 / 결정 대기 / 가설·측정 / 범위 / 영향**. 슬롯이 비어 있으면 비워두고 채워질 때 갱신.

---

## F1. Due Date 추출 (🚀 promoted)

**진행 상태:** 🚀 promoted → `docs/superpowers/specs/2026-04-26-f1-due-date.md`

**발견:** 2026-04-25 dogfood 시작 직전 사고 실험.

### 관찰

캡처된 노트 본문에 `오늘`, `내일`, `이번 주`, `다음 주`, `다음 달`, `N일 뒤`, `N월 N일`, `금요일까지` 같은 시간 표현이 자주 들어간다. 현재 슬라이스는 이걸 그냥 평문 본문에 두고 끝. 사용자는 노트가 "언제까지 해야 하는 일" 인지 다시 본문을 읽어 파악해야 한다.

### 제안 방향

**하이브리드 — 규칙 파서 1차, AI 위임 2차.**

```
입력 텍스트
  ↓
규칙 파서 (정규식 + KST 변환)
  ↓
매칭 있음? ──→ ISO YYYY-MM-DD + confidence: high
  │
  └─ 시간 후보 어휘 감지 + 매칭 없음 ──→ AI 프롬프트에 due_date 필드 추가
                                          ↓
                                       AI 응답에 ISO 있음? ──→ confidence: medium
                                       AI 응답 비었음? ──→ due_date null
```

규칙 파서는 결정론적·테스트 쉬움. AI 위임은 같은 generate 응답에 필드만 추가 — 별도 호출 없음. UI 라벨은 작은 회색 `📅 YYYY-MM-DD`, AI 라벨 정책과 동일하게 사용자 편집 시 진해지고 라벨 사라짐 (Strategy §7-원칙4 "AI 확신도 낮게").

### 결정 대기

1. 시간 표현이 들어간 노트 비율 — 누적 50건 표본 분류 (가설 H1)
2. "오늘 PR 리뷰" 같은 노트의 due 가 정말 오늘인가, 단순 맥락어인가? false positive 비율
3. due_date 가 있는 노트의 재방문 트리거 — Inbox 정렬 / 별도 뷰 / 알림 중 어디
4. NoteCard 의 라벨 슬롯 위치 — 제목 옆, 태그 옆, 별도 줄
5. 만료된 노트 처리 — 자동 done ❌, 자동 삭제 ❌, 시각적 표시만? 별도 필터?
6. 음력·"월말"·"주말 안에" 등 모호 표현은 AI 위임만으로 충분한가

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | 누적 캡처 노트 중 시간 표현 포함 비율 ≥ 30% | 표본 50건 본인 라벨링 |
| H2 | 규칙 파서가 일상 한국어 시간 표현의 ≥ 80% 정확 변환 | 골든 픽스처 50건 + 단위 테스트 |
| H3 | due_date 후보 수락률 ≥ 60% | 정식 출시 후 측정 |

H1 이 미달이면 본 항목 ❌ rejected.

### 범위

- **In:** `오늘`/`내일`/`모레`, `이번/다음 주`, `다음 달`, `N일·주·개월 뒤`, `N월 N일`, `MM/DD`, `YYYY-MM-DD`, KST 자정 기준
- **Out:** 시각 단위 (`오후 3시`), 음력·절기, 반복 일정 (`매주 월요일`), 외부 캘린더 연동

### 영향

- **Schema:** migration v2 — `notes.due_date TEXT`, `notes.due_date_edited_by_user INTEGER NOT NULL DEFAULT 0`
- **NoteRepository:** `updateAiResult` 시그니처에 `dueDate?: string | null`, edited-flag CASE WHEN 가드. 신규 `setDueDate(noteId, date)`
- **AI:** zod 스키마에 `due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()`, 프롬프트 끝에 "추출 가능하면 ISO, 아니면 null" + `{{TODAY_KST}}` 주입
- **Renderer:** NoteCard 라벨 슬롯, EditableField 재사용, 만료 시 회색 + 취소선 (단어는 "실패"/"지각" 금지 — slice §1.1 카피 정책)
- **Lib:** 외부 의존성 0 권장. 자체 정규식 + Intl.DateTimeFormat. ContinuityService 의 KST 헬퍼 재사용

### 골든 픽스처 후보

```
"오늘 PR 리뷰"            → null 또는 today (양가)
"내일 회의 준비"          → tomorrow
"다음 주 월요일까지 슬라이드" → next-mon
"3일 뒤 데모"             → today+3
"5월 1일 휴가 신청"       → 2026-05-01
"이번 주 안에 리팩터링"    → 이번 주 일요일
"월말 마감"               → 해당 월 말일 (AI 위임)
"주말까지"                → 이번 주 일요일 (AI 위임)
"퇴근 전"                 → today (시각 단위 fallback)
```

각 케이스의 confidence + 가설 행동(수락/편집/무시) 라벨링이 H2/H3 측정의 근거.

---

## F2. 태그 클릭 = 즉시 삭제 + undo 부재 (🚀 promoted)

**진행 상태:** 🚀 promoted → `docs/superpowers/specs/2026-04-26-f2-tag-click.md`

**발견:** 2026-04-25 dogfood 중. 슬라이스 v0.4 동작.

### 관찰

`NoteCard.tsx:110` 에서 태그 칩의 `onClick` 이 `removeTag(t.name)` 으로 묶여 있다. 즉시 DB 에 반영되고, 확인 다이얼로그도 undo 도 없다. hover title 은 "클릭으로 제거" 로 의도된 동작이지만, 사용자는 태그 UI 의 일반적 관행 (클릭 = 동일 태그 노트 필터링) 을 기대했다.

문제 두 갈래:

1. **기능 부재:** "이 태그를 가진 노트만 보기" 가 슬라이스에 없음. 사용자 멘탈 모델과 어긋남.
2. **데이터 손실 마찰:** 평범한 클릭 한 번으로 AI 가 만든 태그가 사라지는데 되돌릴 수 없음. 슬라이스 §1.1 의 "AI 라벨 → 사용자 편집으로 전환" 정체성 흐름과도 충돌 — 삭제는 편집이 아니라 파괴.

### 제안 방향

세 동작을 분리한다.

| 동작 | 트리거 | 결과 |
|------|--------|------|
| **필터** | 짧은 클릭 | Inbox 가 동일 태그 노트만 표시 (헤더에 "필터: #tag · 해제" 칩) |
| **편집** | 칩 더블클릭 또는 칩 우측 작은 ✕ 아이콘 | 태그 이름 인라인 편집 또는 단일 태그 제거 |
| **재추가 (undo)** | 방금 제거한 직후 토스트 "태그 'foo' 를 제거했습니다 · 되돌리기" 5초 노출 | 클릭 시 즉시 복원 |

권장 1차 패치 (작음): 클릭 = 필터로 변경 + 칩에 ✕ 아이콘 추가 (✕ 클릭 시 confirm 또는 5초 undo 토스트). 필터는 zustand 상태에 `tagFilter: string | null` 추가 + Inbox 의 `notes.filter` 단계 한 줄.

권장 2차 (별 spec): 다중 태그 필터 (AND/OR), 태그 편집 (rename / merge), 태그 단위 일괄 작업.

### 결정 대기

1. 클릭 = 필터 라는 가정이 dogfood 본인에게도 성립하는가? — 본인 예상 동작 한 번 더 셀프 체크
2. AI 태그 vs 사용자 추가 태그 클릭 동작이 같아야 하는가, 달라야 하는가?
3. undo 토스트의 dismissal 정책 — 5초 자동 사라짐 vs 다음 작업까지 유지
4. 필터링 시 Continuity 배지·Pending 배너의 카운트는 전체 기준인가, 필터된 부분 기준인가?
5. 태그 단일 제거 외에 "이 태그가 붙은 모든 노트에서 제거" 같은 일괄 작업도 묶을지 — 별 spec 으로 분리 권장

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | 슬라이스의 현재 "클릭 = 삭제" 가 본인 dogfood 에서 ≥ 1회 실수 유발 | 일일 로그의 마찰 사건 |
| H2 | 클릭 = 필터로 바꾸면 본인이 태그를 통해 과거 노트를 재방문하는 빈도 ≥ 주 1회 | 사용 로그 |
| H3 | undo 토스트가 있으면 의도된 태그 제거의 인지 부담 감소 | 정성 평가 |

### 범위

- **In:** 단일 태그 클릭 동작 변경, 단일 태그 제거 시 undo, Inbox 의 단일 태그 필터
- **Out:** 다중 태그 필터, 태그 rename / merge, 태그 단위 일괄 작업, 태그 자동완성

### 영향

- **Schema:** 없음 (이 패치 자체는 DB 변경 없음. 일괄 작업이 들어오면 별 spec)
- **Renderer:** `NoteCard.tsx` 의 칩 `onClick` 변경, 칩 우측 ✕ 아이콘 추가, Inbox `App.tsx` 에 필터 상태 + 헤더 칩, undo 토스트 컴포넌트 (RecoveryToast 패턴 재사용 가능)
- **Store:** zustand `tagFilter` 상태 + `setTagFilter`, `notes` 셀렉터에 필터 적용
- **API:** 변경 없음 (필터는 클라이언트 사이드만)
- **카피:** 슬라이스 §1.1 "실패"/"끊김"/"연속 실패" 금지 정책 그대로. undo 토스트 문구는 "되돌리기" / "방금 제거" 같은 중립 표현

---

## F3. "구출" 카피가 한국어로 어색함 (🚀 promoted)

**Promoted →** `docs/superpowers/specs/2026-04-26-f3-f4e-copy.md` (F4-E 와 함께 한 PR)

**발견:** 2026-04-26 dogfood 첫날, 메모 1건 캡처 후 OS 토스트 알림에서 "방금 하나의 업무 기억을 구출했습니다" 문구가 떴을 때.

### 관찰

`구출(rescue)` 은 한국어 일상어로 거의 쓰이지 않는다. 인질·재난 맥락에서 주로 쓰이는 어휘라서 "메모 한 줄 적었더니 구출됐다" 라는 표현이 어색하게 들린다. 영어 원문 의도(rescue a thought before you lose it) 를 직역한 결과로 추정.

현재 "구출" 등장 표면 (코드 경로 기준):

| # | 표면 | 위치 | 문구 |
|---|------|------|------|
| 1 | OS 토스트 (회전 카피 4종 중 3번째) | `src/main/services/NotificationService.ts:6` | `방금 하나의 업무 기억을 구출했습니다.` |
| 2 | 트레이 메뉴 | `src/main/tray.ts:13` | `기억 구출하기` |
| 3 | 트레이 메뉴 | `src/main/tray.ts:12` | `구출한 메모 보기` |
| 4 | Inbox 빈 상태 | `src/renderer/inbox/App.tsx:44` | `첫 기억을 구출해보세요.` |
| 5 | QuickCapture 힌트 | `src/renderer/quickcapture/App.tsx:68` | `Ctrl+Enter 구출 · Esc 취소 · 이미지 붙여넣기` |

추가 영향 표면 (코드 외):
- `package.json` `description: "Inkling — local-first 기억 구출 도구"`
- `tests/e2e/smoke.spec.ts:29` 의 단언 `await expect(inbox.getByText('첫 기억을 구출해보세요.')).toBeVisible()`
- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` §5.5 의 카피 테이블 (519~523행)
- `docs/superpowers/strategy/strategy.md` 의 §1, §3 — "메모 작성 → 기억 구출" 으로 핵심 행동을 재정의한 **전략적 선언** 그 자체

### 제안 방향

**카피 변경 + strategy 문서의 어휘 결정 재검토를 한 항목으로 묶음.**

대체 후보 (전략 의도 보존도 vs 자연스러움 트레이드오프):

| 후보 | 의도 보존 | 자연스러움 | 비고 |
|------|----------|-----------|------|
| **꺼내 놓기** | 중 | 상 | "머릿속에서 꺼낸다" 직관, 기존 strategy "꺼내는 1회 행동" 표현과 일치 |
| **잡아두기** | 중 | 상 | "잊기 전에 잡아둔다" 어감, 가벼움 |
| **한 줄 던지기** | 상 | 상 | 슬라이스 §1.1 "3초 안에 던지고" 표현 그대로. 캡처 행위에 적합 |
| **남겨두기** | 하 | 상 | 평이함, 의도성 약함 |
| **적어두기/메모하기** | 하 | 상 | 전략 의도 상실 ("메모 작성" 회귀) |
| **챙겨두기** | 중 | 상 | 알림 카피에 자연 ("하나 챙겨뒀습니다") |
| **(현행) 구출** | 상 | 하 | 어색함 |

권장 1차안 — **표면별 다른 동사 허용** (한 단어로 통일 강제 안 함):

| 표면 | 권장 카피 |
|------|----------|
| 토스트 회전 카피 #3 | `방금 한 줄 잡아뒀습니다.` 또는 `머릿속에서 한 줄 꺼내뒀습니다.` |
| 트레이: 새 메모 | `한 줄 적기` 또는 `빠르게 한 줄` |
| 트레이: Inbox 열기 | `보관한 메모 보기` 또는 `Inkling 열기` |
| 빈 상태 | `첫 한 줄을 던져보세요.` 또는 `머릿속에 떠다니는 한 줄을 적어보세요.` |
| QC 힌트 | `Ctrl+Enter 저장 · Esc 취소 · 이미지 붙여넣기` |

핵심: "구출" 의 행동적 의미("정리·작성이 아니라 머릿속에서 꺼내기만") 는 보존하되, 직역체 한 단어로 강제하지 않고 표면별 자연 문맥에 맞춰 동사를 분배.

### 결정 대기

1. **Strategy 문서 §1, §3 의 "기억 구출" 선언을 함께 수정할 것인가?** — "구출" 은 단순 UI 카피가 아니라 전략 어휘다. 카피만 바꾸면 strategy 와 코드가 어긋난다. 같이 바꾸려면 strategy.md §1·§3·§7 갱신 + "꺼내기" / "한 줄" 같은 새 키워드 정의.
2. 단일 동사로 통일할 것인가, 표면별 자연어로 분배할 것인가? — 통일은 브랜딩 자산, 분배는 자연스러움.
3. 제품명 표어 ("local-first 기억 구출 도구") 는 어디까지 따라가는가? — `package.json description`, GitHub README 첫 문단, 향후 onboarding 문구 모두 영향.
4. e2e smoke 의 단언 문구 변경 시 카피 freeze 관행을 도입할지 — 카피 변경마다 e2e fix 가 따라오면 마찰 큼. 단언을 더 약하게 (`getByRole('main')` 등) 잡거나 카피 상수 import 가 대안.
5. 캡처-보상 카피 4종 중 #1, #2, #4 (`이 생각은 이제 Inkling이 들고 있습니다.` / `나중에 찾을 수 있게 보관했습니다.` / `기록 완료. 이제 잊어도 됩니다.`) 는 그대로 유지해도 자연스러우니 #3 만 교체로 충분한가? 아니면 전체 톤 재정렬?

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | 본인 dogfood 1주 누적 시 "구출" 표현에 대한 위화감 토스트/메뉴 노출당 ≥ 1회 보고 | 일일 dogfood 로그의 카피 마찰 항목 |
| H2 | 권장안 적용 시 카피 위화감 보고 0건/주로 감소 | 동일 로그 |
| H3 | 외부인(가족·동료) 1명에게 권장안 vs 현행 블라인드 비교 시 권장안이 자연스럽다고 답함 | 정성 1회 |

### 범위

- **In:**
  - `NotificationService.REWARD_COPIES` 의 #3 교체
  - 트레이 메뉴 라벨 2개
  - Inbox 빈 상태 + QuickCapture 힌트
  - `package.json description`
  - smoke spec 단언 문구 동기화
  - 카피 테이블 (`2026-04-24-inkling-vertical-slice-design.md` §5.5)
  - **strategy.md §1·§3·§7 의 "기억 구출" 어휘 결정** 동시 갱신 여부 결정
- **Out:**
  - 회전 카피 4종 전면 재작성 (필요하면 별 항목으로 분리)
  - onboarding 흐름 (슬라이스 외)
  - 로고·앱 아이콘 텍스트
  - 다국어 (영어 카피)

### 영향

- **Schema:** 없음
- **코드:**
  - `src/main/services/NotificationService.ts` — 회전 카피 한 줄
  - `src/main/tray.ts` — 메뉴 라벨 2곳
  - `src/renderer/inbox/App.tsx` — 빈 상태 문구
  - `src/renderer/quickcapture/App.tsx` — 힌트
  - `package.json` — description
  - `tests/e2e/smoke.spec.ts` — 단언 문구 (또는 셀렉터 약화)
- **문서:**
  - `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` 카피 테이블
  - `docs/superpowers/strategy/strategy.md` — 결정 대기 #1 결과에 따라 부분 또는 전면
  - 본 문서 — promoted 시 별 spec 으로 추출

### 메모

이 항목은 strategy 문서와의 어휘 일관성 결정이 함께 묶여 있어서, 단순 카피 PR 로 끝낼지 / strategy 재검토 미니 spec 으로 승격할지 결정 대기 #1 의 답이 promoted 경로를 좌우함. drafting 단계에서 결정 대기 #1 부터 답하고 ready-for-spec 으로 넘기는 순서 권장.

---

## F4. 떠오른 순간 → "Inkling!" 자동 연상 만들기 (🔬 drafting — C·E·F promoted)

**발견:** 2026-04-26 dogfood 시작 직전 사고 실험. 슬라이스 v0.4 와 strategy.md §3 가 **이미 알고 있는 contextual cue** (회의 후, 퇴근 전, 디버깅 후) 의 if-then 만 다루고, **ambient/spontaneous 떠오름** (샤워, 산책, 대화 중, 자기 전) 은 사각지대.

**진행 상태:**
- E (Zeigarnik priming) — 🚀 promoted → `docs/superpowers/specs/2026-04-26-f3-f4e-copy.md`
- C (환경 앵커) — 🚀 promoted → `docs/superpowers/specs/2026-04-26-f4-cf-cue.md`
- F (정체성 고리) — 🚀 promoted → `docs/superpowers/specs/2026-04-26-f4-cf-cue.md`
- A (잠금 hook), D (variable interval prompt), B (ambient if-then) — 🌱 raw, 측정 후 결정

### 관찰

캡처 자체의 마찰은 거의 0 — `Ctrl+Shift+J` + 한 줄 + Enter = 3초. 하지만 dogfood 의 진짜 실패 모드는 **"3초가 너무 길다"** 가 아니라 **"그 3초가 머릿속에 안 떠오른다"** 다. 떠오름 → Inkling 연상 사이의 인지적 다리가 약하면, 메모 자체가 시도되지 않는다.

strategy.md 가 다루는 cue 는:

| 위치 | 내용 | 강한 cue 여부 |
|------|------|--------------|
| §3 회의형 | "회의가 끝나면" | ✅ 외부 신호 (Zoom 종료, 자리 일어남) |
| §3 퇴근 회고형 | "업무 종료 10분 전" | ✅ 시계·전등·동료 |
| §3 디버깅형 | "오류를 해결하면" | ✅ 테스트 PASS 화면 |
| §3 학습형 | "새로 배운 것이 있으면" | ⚠ 내부 인식, 약함 |
| §3 요청 관리형 | "누군가 업무를 요청하면" | ✅ 메시지/대화 |

남은 사각지대는 **외부 신호 없이 머릿속에서만 떠오르는 생각** — 샤워, 운전, 산책, 자기 직전, 책 읽다가, 잡담 중. 본인 dogfood 에서 가장 풍부한 메모 후보가 이 영역에서 발생할 가능성이 큰데 (RTX 4070 owner profile, 퇴근 후 사이드 프로젝트형 사고 패턴), 슬라이스가 cue 를 안 만들어주면 그냥 잊혀짐.

### 제안 방향

**6개 메커니즘 후보** — 각각 Inkling 의 좁은 실행 가능 단위로 변환.

#### A. 습관 쌓기 (Habit Stacking · BJ Fogg, James Clear)

기존 강한 자동 행동 직후에 새 행동을 붙임. "X 하면 Y 한다" — Y 가 X 의 부산물처럼 느껴지게.

| 강한 기존 자동 행동 | 붙일 Inkling 행동 |
|--------------------|-------------------|
| 노트북 덮음 / 잠금 (Win+L) | 30초 짧은 캡처 윈도우 자동 호출 |
| 휴대폰을 책상에 엎어놓음 | (휴대폰 앱이 생기면) 트리거 |
| 커피·물 가지러 일어남 | 타임랩스 카운트로 "자리 떴음 → 돌아왔을 때 한 줄" 프롬프트 |
| 메신저 알림 dismiss | 트레이 미니 입력 — Quick Capture 단축 |

슬라이스 가능: **잠금 화면 진입 직후 3초 캡처 윈도우**. Windows API `SystemEvents.SessionSwitch` (lock/unlock) 로 unlock 시 한 번만 가벼운 토스트 ("머릿속에 남은 한 줄?"). 토스트 클릭 = QC. 무시 = 사라짐.

#### B. 실행 의도의 확장 (Gollwitzer, ambient 영역)

§3 의 if-then 을 "내적 인식" 트리거로 확장.

```
"무언가 해야 한다는 생각이 들면 → 그 자리에서 Ctrl+Shift+J"
"어제 그거 어떻게 됐지? 라는 생각이 들면 → 그 자리에서 Ctrl+Shift+J"
"이거 까먹을 것 같다는 느낌이 들면 → 그 자리에서 Ctrl+Shift+J"
```

내적 cue 의 약점은 인식 자체가 안 일어난다는 점이지만, 1~2주 의식적 반복 후 자동화 가능 (Lally 18~254일 범위, 본 사례는 행동이 단순해서 짧은 쪽). 온보딩에서 사용자에게 **본인의 ambient 떠오름 패턴** 을 1개 직접 적게 하고 그걸 if-then 으로 변환.

#### C. 환경 앵커 (Cue Salience · 시각·물리적 단서)

생각이 떠오를 때 시야에 Inkling 이 있으면 연상 강화.

| 앵커 | 비용 | 슬라이스 적합도 |
|------|------|---------------|
| 잠금화면 배경에 Ctrl+Shift+J 안내 | 0 (사용자 셀프) | 외부 |
| 책상 모니터에 작은 펜 그림 스티커 | 0 (사용자 셀프) | 외부 |
| 작업 표시줄 트레이 아이콘 (이미 있음) | 0 | 슬라이스 ✅ |
| 트레이 아이콘 색을 "오늘 미캡처 시간" 에 따라 변화 | 작음 | 슬라이스 후속 |
| 윈도우 위젯 (Windows 11 위젯 보드) | 큼 | 슬라이스 외 |

가장 가벼운 즉시 적용: **트레이 아이콘에 색·뱃지** — 오늘 캡처 0건이면 점선 동그라미, 1건 이상이면 실선. 사용자가 무의식 중에 시야 끝에서 "비어있다" 신호를 받음.

#### D. 무작위 부드러운 알림 (Spaced retrieval · Variable interval)

랜덤 간격으로 "지금 머릿속에 떠다니는 한 줄?" 토스트. variable interval reinforcement 가 fixed interval 보다 행동 강화에 강하다는 행동주의 결과 응용. 단 **거슬리면 즉시 망함** — 회복 친화 톤 강제.

| 변수 | 권장 1차 |
|------|---------|
| 빈도 | 평일 09~18 KST 사이 2~3회, 분포는 90~180분 사이 무작위 |
| 작업 컨텍스트 가드 | full-screen 앱 감지 시 skip (회의·발표 중 보호) |
| 카피 톤 | "지금 한 줄 던져두면 좋을 게 있나요?" (강요 없음) |
| Off-toggle | 트레이 메뉴 1클릭 |
| 안티패턴 | 캡처 0/일 자체에 대한 죄책감 유발 카피 — 슬라이스 §1.1 위반 |

슬라이스 적합도: **중**. Hotkey + Tray 만으로도 충분하다는 §3 결정과 충돌 가능. dogfood 1주 후 본인 캡처 빈도 데이터 본 뒤 결정.

#### E. Zeigarnik 효과 활용 (미완 텐션 → 외재화)

미완 과제는 인지 자원을 점유하며 회상이 더 잘 된다는 Zeigarnik 1927. Inkling 의 "이제 잊어도 됩니다" 보상이 이미 이 방향이지만, **떠올림 → 캡처** 다리에서는 활용 안 됨. 강화 방향:

- 메인 토스트 카피 1개를 **사용 전 priming** 으로 재배치: 앱 첫 설치 직후 / 매일 첫 캡처 직후 짧게 "머릿속에 떠다니면 잡아두세요" 주입. 반복 노출이 ambient 떠오름 시 자동 reactivation.
- inbox 빈 상태 카피를 **유발 어휘** 로 변경: "오늘 머릿속에서 그냥 흘러간 생각 1개만 적어보세요" — 사용자에게 "흘러갔던 게 분명 있었지" 회상을 자극.

비용 0, 슬라이스 내 적용 가능. F3 (구출 카피 재검토) 와 자연스럽게 묶임.

#### F. 정체성 고리 (Identity-based habit · James Clear)

"메모 잘하는 사람이 되자" 가 아니라 "나는 머릿속을 비워두는 사람" 이라는 자기개념. 매 캡처마다 정체성 강화 카피 1개:

```
"오늘 7번째 비웠습니다." (count visible)
"한 주 누적 23개. 잊을 자유 23번."
"이번 주 다시 본 메모 4건 — 외부 기억이 일하고 있음."
```

이미 §4.2 능력감 보상에 일부 있음. 차이는 "기억력" 이 아니라 "비움/외재화" 정체성으로 frame. 트레이 메뉴 또는 inbox 헤더에 작은 카운터.

### 결정 대기

1. **슬라이스 외부 vs 후속 미니 spec**: A·D 는 새 IPC + Windows API + 알림 스케줄링이 필요해 슬라이스 §3 ("Hotkey + Tray + Notification 만") 와 충돌. C·E·F 는 슬라이스 내 적용 가능. 어디까지 슬라이스에서 시도하고 어디서부터 별 spec 으로 분기?
2. **무작위 알림 (D) 의 거슬림 임계점**: 본인 dogfood 1주 누적 데이터 (캡처 빈도 중간값) 를 보기 전에 결정 미루기. 데이터 없이 도입하면 anti-pattern 될 위험.
3. **메커니즘 결합 vs 격리 시험**: 6개 동시 적용 시 어느 게 효과 있었는지 분리 불가. dogfood 단계에서 격리 A/B 가능한가? 본인 단일 사용자라 통계 의미 약함 — 정성 라벨링으로 대체.
4. **strategy.md 와의 관계**: 본 항목이 strategy.md §3 ambient cue 절을 신규 §3.6 으로 추가할 만큼 무거운가, 아니면 specs/ 만으로 충분한가? F3 와 동일한 "strategy 동반 갱신 여부" 결정 패턴.
5. **외부 의존 (Win API) 도입의 슬라이스 의의**: A 의 SessionSwitch hook 은 슬라이스에서 한 번도 열어본 적 없는 native API. 도입 시 §7 dependency invariant 에 추가, 그 비용 vs cue 효과 가치 비교 필요.

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | E (Zeigarnik 카피 재배치) 만 적용해도 본인 dogfood 1주 캡처 빈도 ≥ 30% 증가 | 캡처/일 카운트, 적용 전후 1주씩 |
| H2 | C (트레이 뱃지 0/N 표시) 가 시각적 cue 로 작동하여 12~18시 사이 "지금 비어있네" 자각 사건 ≥ 2회/주 | 본인 라벨링 |
| H3 | D (무작위 알림) 가 캡처 빈도를 늘리지만 거슬림 점수 (1~5) 가 평균 ≤ 2 | 정성 점수 |
| H4 | F (정체성 카피) 가 dogfood 종료 시 "Inkling 을 계속 쓰고 싶다" 점수에 양의 영향 | exit 인터뷰 |
| H5 | B (ambient if-then 온보딩) 의 사용자 작성 if-then 1~2개가 1주 후에도 회상 가능 | 본인 self-report |

### 범위

- **In (슬라이스 가능 — 가벼운 적용):**
  - C 의 일부 — 트레이 아이콘 색·뱃지 (오늘 캡처 0/≥1)
  - E — Zeigarnik priming 카피 1줄 + 빈 상태 카피 재작성 (F3 와 묶기)
  - F — 트레이/Inbox 헤더의 정체성 카피 1줄 (count 표시)
- **In (슬라이스 후속 미니 spec):**
  - A — 잠금/잠금해제 시 부드러운 캡처 프롬프트
  - B — 온보딩에 ambient if-then 1~2개 작성 단계 추가
  - D — 무작위 알림 + 가드 (full-screen, off-toggle)
- **Out:**
  - 음성 ("Hey Inkling")
  - 휴대폰 앱
  - 위젯 보드 통합
  - 외부 캘린더 cue 연동

### 영향

- **Schema:** 없음 (모두 클라이언트 사이드 또는 카피)
- **코드:**
  - **C 트레이 뱃지:** `tray.ts` — 이미지 동적 갱신, `repo.countToday(): number` 또는 `getInbox` 필터로 오늘 카운트 조회. 10분 간격 또는 새 노트 생성 IPC 신호로 갱신.
  - **E priming 카피:** `NotificationService.REWARD_COPIES`, `inbox/App.tsx` 빈 상태, `quickcapture/App.tsx` 힌트
  - **F 정체성 카피:** `inbox/App.tsx` 헤더 영역 (count + 한 줄), 또는 트레이 tooltip
  - **A 후속:** `electron.powerMonitor` 의 `lock-screen`/`unlock-screen` 이벤트, 새 `LockHookService`
  - **D 후속:** 새 `RandomPromptService` — setInterval + jitter, full-screen 감지는 `screen.getDisplayNearestPoint` 또는 native 호출
- **문서:**
  - 본 항목 promoted 시 `2026-04-26-cue-strengthening.md` (가칭) 으로 추출
  - strategy.md 결정 #4 결과에 따라 §3.6 신설 또는 본 spec 만으로 종결
- **테스트:**
  - C 의 단위 테스트 — 카운트 0/N 분기, 날짜 경계 KST
  - E·F 카피 변경에 따른 e2e smoke 단언 동기화 (F3 와 묶음)

### 비고

본 항목은 슬라이스 §1.3 의 종료 조건 ("본인 2주 dogfood 완주") 을 **달성하기 위한 메타-행동 설계**다. 즉 slice 의 "기능 엣지" 가 아니라 "사용자가 slice 를 안 잊고 쓰게 하는 메타 레이어". 우선순위는 H1 (E 만 1주 시도) → 데이터 확인 → C·F 추가 → A·D 검토 순서 권장. 6개 메커니즘 동시 도입은 신호 분리 불가로 안티패턴.

---

## F5. 마크다운 일괄 export (RAG 활용 가정) (🚀 promoted)

**진행 상태:** 🚀 promoted → `docs/superpowers/specs/2026-04-26-f5-export.md`

**발견:** 2026-04-26 dogfood 시작 직전 사고 실험. 슬라이스 v0.4 는 노트가 SQLite + 로컬 미디어 폴더에만 존재. 외부 도구 (Obsidian, RAG 파이프라인, 로컬 LLM 컨텍스트, 검색 엔진) 로 빼낼 통로가 0.

### 관찰

스키마에 이미 export 에 필요한 모든 정보가 있다 (`m001_initial.ts:7~57`):
- `notes` — id, raw_text, ai_{title,summary}, *_edited_by_user, user_intent, intent_prompted_at, created_at, updated_at, ai_provider, ai_generated_at
- `note_tags` (+`tags`) — 태그 + source(ai/user)
- `media` — note 첨부 이미지 메타 (`rel_path` 가 MediaStore 의 프로필 디렉터리 기준)
- (F1 promoted 시) `due_date`, `due_date_edited_by_user`

내보낼 자산은 두 종류:
1. **노트 본문 + 메타데이터** — 텍스트
2. **첨부 이미지** — 바이너리 파일 (현재는 `<profile>/media/<rel_path>`)

RAG 파이프라인 (LangChain, LlamaIndex, ChromaDB, 로컬 embedding) 의 표준 입력은 **YAML frontmatter 가 붙은 단일-노트-단일-파일 마크다운** + 안정 ID + 카테고리/태그 메타. 본 export 는 이 형식에 정렬해야 후속 의사결정 (RAG 도입, Confluence 동기화, Obsidian vault 사용) 모두 한 형식으로 흡수 가능.

### 제안 방향

**1차 권장 — 디렉터리 트리 + frontmatter 마크다운 + index.jsonl + 미디어 동봉.**

```
inkling-export-2026-04-26/
  notes/
    2026-04-25-014a3b9c-주간회고-PR-리뷰.md
    2026-04-25-02f17de8-새-디버깅-패턴.md
    ...
  media/
    014a3b9c__1.png        # MediaStore rel_path 평탄화
    02f17de8__1.jpg
  index.jsonl              # RAG 친화 1줄=1노트 메타
  manifest.json            # 스키마 버전, 내보낸 시각, 노트 수, 검증 해시
  README.md                # 형식 설명, RAG 적용 가이드
```

**노트 파일 포맷** (one file per note, RAG 친화):

```markdown
---
id: 014a3b9c-...
created_at: 2026-04-25T14:23:11+09:00
updated_at: 2026-04-25T14:24:02+09:00
title: 주간 회고 PR 리뷰
title_source: ai            # ai | user (edited 면 user)
summary: 회고 양식 통일을 위한 PR 리뷰 메모.
summary_source: ai
tags:
  - { name: pr, source: ai }
  - { name: review, source: user }
user_intent: 팀에서 회고 양식 통일
intent_prompted_at: 2026-04-25T14:24:02+09:00
due_date: 2026-05-01      # F1 promoted 시
due_date_source: ai
ai_provider: local-ollama/gemma4:e4b
ai_generated_at: 2026-04-25T14:23:34+09:00
images:
  - rel: media/014a3b9c__1.png
    mime: image/png
    bytes: 152834
inkling_export_version: 1
---

# 주간 회고 PR 리뷰

> 회고 양식 통일을 위한 PR 리뷰 메모.

내일 까지 PR 리뷰 마무리하고, 회고 양식은 팀에 공유.
오후 미팅 중에 떠올랐음.

![](media/014a3b9c__1.png)
```

**index.jsonl** (RAG 인덱싱용 1줄=1노트):

```json
{"id":"014a3b9c-...","path":"notes/2026-04-25-014a3b9c-주간회고-PR-리뷰.md","created_at":"2026-04-25T14:23:11+09:00","tags":["pr","review"],"due_date":"2026-05-01","embedding_text":"주간 회고 PR 리뷰\n\n내일 까지 PR 리뷰 마무리하고..."}
```

`embedding_text` 는 title + raw_text + tags 를 결합한 임베딩 입력 후보. 사용자가 별도 가공 없이 LangChain `JSONLoader` 또는 LlamaIndex `JSONReader` 로 바로 적재 가능.

**파일명 컨벤션:** `YYYY-MM-DD-{id8}-{slugified-title}.md`. 충돌 회피 + 인간 가독 + 디렉터리 정렬 친화.
- `id8` = UUIDv7 의 처음 8자리. 시간 정렬 + 충돌 0.
- `slugified-title` = title 의 한글 보존, 공백→하이픈, 파일시스템 금지 문자 제거 (`/\\:*?"<>|`), 32자 제한.
- title 비어있으면 `untitled` 폴백.

**트리거 (1차 권장):** 트레이 메뉴 → "마크다운으로 내보내기..." → Electron `dialog.showOpenDialog({ properties: ['openDirectory'] })` 로 사용자가 폴더 선택 → 진행 토스트 → 완료 토스트 (성공 시 노트 수 + "폴더 열기" 버튼).

**증분 vs 전체:** 1차는 전체 덮어쓰기만. 증분(변경된 노트만 갱신)은 후속.

### 결정 대기

1. **포맷 1차안 확정**: one-file-per-note + frontmatter + index.jsonl 트리플 vs 단일 monolithic .md vs 두 형식 동시 출력? → RAG 우선이면 트리플이 압도적이지만 사용자 선호 확인 필요.
2. **미디어 포함 기본값**: 항상 동봉 vs 사용자 선택 (체크박스). 슬라이스 §1.1 의 "raw_text 본문에 민감정보 가능" 정책 — 이미지가 스크린샷인 경우 export 가 의도치 않은 노출 통로가 될 수 있음. **기본 동봉 + export 시 다이얼로그에 "이미지 N개 포함됩니다" 명시** 가 안전.
3. **삭제된 노트 처리**: SQLite 에 soft-delete 컬럼이 없음. 현재는 hard delete. export 결과는 *현 시점* 스냅샷만 — 삭제 이력 없음. 충분한가, 별 issue 인가?
4. **필드 정책 — provenance 표현**: `title_source: ai|user` 같은 단일 enum vs `title: { value, source, edited_at }` 객체. RAG 파이프라인의 frontmatter parser 마다 다름 — 평탄한 enum 이 호환성 좋음.
5. **embedding_text 합성 규칙**: title + raw_text + tags 단순 결합 vs raw_text 만 (가장 untouched) vs title + summary + raw_text. 본인 RAG 사용 패턴 미정 — 1차는 raw_text 단독으로 시작 + 옵션화.
6. **파일명에서 raw_text vs ai_title 사용**: ai_title 사용이 가독성 좋지만 AI 변경 시 파일명도 변하는 안티패턴. **ai_title 사용 + 사용자가 수동 export 트리거 시점 기준** 으로 동결 (재 export 시 새 파일명). 파일명 안정성 vs 가독성 트레이드오프 명시.
7. **트리거 표면**: 트레이 메뉴만 vs Inbox 헤더 버튼도 추가 vs CLI 플래그 (`--export <dir>`). 자동화 사용자라면 CLI 가 매력. 슬라이스 후속 미니 spec 으로 분리 가능.
8. **export 형식 버전 정책**: `inkling_export_version: 1` 박아두고 후속 변경 시 마이그레이션 가이드 동봉. 처음부터 박는 게 깔끔.
9. **민감정보 표시 경고**: 본 사용자는 `dlsrks0734@gmail.com` 계정 본인 단일 사용자라 위험 낮지만, export 후 폴더가 어디 가는지에 따라 위험 발생. 트레이 export 다이얼로그에 "이 export 는 평문이며 raw_text 가 그대로 포함됩니다" 명시 필요.

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | dogfood 1주 후 본인이 export 한 마크다운 더미를 LlamaIndex 기본 markdown loader 로 직접 적재 가능 (사이즈 변환 0) | 실측 — 1회 시도 |
| H2 | export 결과의 frontmatter 가 Obsidian 의 frontmatter renderer 에도 호환 | 실측 — Obsidian 에 폴더 import 후 메타 표시 확인 |
| H3 | 노트당 평균 raw_text 길이 ≤ 200 토큰 → RAG chunking 불필요 | 표본 50건 토크나이저 통계 |
| H4 | export 누적 사이즈가 1MB / 100 노트 이하 (미디어 제외) | 측정 |
| H5 | 본인이 export → 외부 도구 적재 → 적어도 1번 의미 있는 회수 (검색·RAG·재방문) 발생, dogfood 2주 내 | 본인 라벨링 |

### 범위

- **In:**
  - `ExportService` 신규 — DB 쿼리 + 파일 쓰기 + 미디어 복사
  - 트레이 메뉴 항목 1개 추가 ("마크다운으로 내보내기...")
  - Electron `dialog` 디렉터리 선택
  - frontmatter 합성 + 파일명 슬러그
  - `index.jsonl` + `manifest.json` + `README.md` 동시 생성
  - 미디어 평탄화 복사 (rel_path → `media/{id8}__{n}.{ext}`)
  - 진행 상태 토스트 (노트 수 ≥ 100 시 진행률)
  - 단위 테스트 — frontmatter 합성, 슬러그, JSON 직렬화
- **Out (후속 미니 spec):**
  - 증분 export
  - 자동 export (cron / watch)
  - CLI 플래그
  - import (역방향)
  - 다중 형식 (CSV, JSON 단일 파일, OPML)
  - 외부 SaaS 동기화 (Confluence, Notion)
  - export 시 raw_text 마스킹·익명화

### 영향

- **Schema:** 없음 — 현 스키마로 충분
- **신규 파일:**
  - `src/main/services/ExportService.ts`
  - `src/main/ipc/exportApi.ts`
  - 테스트 `tests/unit/ExportService.spec.ts`
- **변경 파일:**
  - `src/main/index.ts` — 등록
  - `src/main/tray.ts` — 메뉴 항목 추가
  - `src/preload/index.ts` — IPC expose
- **외부 의존:**
  - 없음 — Node `fs/promises` + `path` + `node:crypto` (해시) 만 사용
  - YAML 직렬화는 frontmatter 가 단순 하므로 자체 구현 (외부 dep 추가 불필요)
- **로깅:**
  - export 시작·완료·노트 수만 기록. raw_text·title·summary 미기록 (slice §1.1 invariant 4 그대로)
- **문서:**
  - 본 항목 promoted 시 `2026-04-26-markdown-export.md` (가칭) 으로 추출
  - 추출 후 README 의 doc map 갱신
  - export 폴더 안의 `README.md` — RAG 적재 예시 코드 포함

### 비고

본 항목은 **읽기 전용** 이라 dogfood 안전성 영향 0 (raw_text 변경 없음, AI 호출 없음, 네트워크 0). 우선순위 측면에선 F1·F2·F3 보다 후순위지만 **F4 의 H5 (외부 도구로 회수) 평가 자체가 export 없이는 측정 불가** — 즉 F4-H5 = F5 dependency. F4 의 데이터 수집을 위해 F5 가 먼저 promoted 되는 경로도 있음.

---

## F6. 메모 데이터 백업 + 복원 (3-layer 권장) (🔬 drafting — L1 promoted)

**진행 상태:**
- L1 (로컬 스냅샷) — 🚀 promoted → `docs/superpowers/specs/2026-04-26-f6-l1-local-snapshot.md`
- L2 (git sync) — 🚀 promoted (MVP) → `docs/superpowers/specs/2026-04-26-f6-l2-git-sync.md`
- L3 (import) — 🚀 promoted → `docs/superpowers/specs/2026-04-26-f6-l3-import.md`

**발견:** 2026-04-26 dogfood 시작 직전 사고 실험. 슬라이스 v0.4 의 메모 데이터는 `%APPDATA%\Inkling\Inkling\profiles\default\` 단 한 위치에만 존재. 디스크 고장·실수 삭제·DB 손상·OS 재설치 = 총 손실. Strategy.md §1 의 "이제 잊어도 됩니다" 보상이 **데이터 영속성 신뢰** 위에 서 있어서, 이 신뢰가 깨지면 슬라이스 §1.3 의 종료 조건 ("본인 2주 dogfood 완주") 자체가 위협받음.

### 관찰

현재 단일 실패 지점 (SPOF):
- `inkling.sqlite` (WAL 두 파일 포함) — 노트·태그·AI 메타·intent 전부
- `media/` — 클립보드 이미지 바이너리 (DB 의 `rel_path` 와 짝)
- 부팅 시 `MediaGc` 가 DB 미참조 미디어를 정리 — DB 가 손상되면 미디어도 GC 사이클에서 사라질 수 있음 (위험 증폭)

기존 부분 완화는 0:
- 자동 백업 0
- 외부 동기화 0
- import 경로 0
- F5 (export) 가 promoted 되어도 단방향 + 수동

본인 dogfood 운영 환경 신호:
- 이미 `gitea.altair823.xyz` 자체 호스팅 중 — 사적 git remote 인프라가 있음
- 프로젝트 메모리: Mac=업무 / Windows=개인+dogfood — 디바이스 전환 가능성 (단일 활성, 동시 X)
- RTX 4070 Windows = 메인 dogfood 머신, 디스크 1대 SSD 가정 → 디스크 고장 1회 = 전체 손실

### 제안 방향

**3-layer 다층 백업.** 각 layer 가 다른 위협 모델을 커버.

| Layer | 위협 모델 | 비용 | 슬라이스 적합 |
|-------|----------|------|--------------|
| **L1 로컬 원자 스냅샷** | 실수 삭제, DB 손상, AI 마이그레이션 실패 | 작음 | ✅ 슬라이스 후속 가벼움 |
| **L2 git remote 마크다운 동기화** | 디스크 고장, 디바이스 이동, 버전 이력 필요 | 중 | 🔬 별도 미니 spec |
| **L3 전체 export/import** | OS 재설치, 디바이스 이주, 사용자 통제 백업 | 작음 (L1 + F5 위) | ✅ F5 위에 import 만 추가 |

#### L1 — 로컬 원자 스냅샷

`better-sqlite3` 의 `db.backup(path)` API 사용. WAL 활성 상태에서도 안전한 원자적 복제 (파일 단순 cp 와 다름 — WAL 미반영분 누락 위험 없음).

```ts
// 의사코드
async function snapshot(): Promise<void> {
  const ts = format(new Date(), 'yyyy-MM-dd');
  const dest = join(profileDir, 'backups', `inkling-${ts}.sqlite`);
  await db.backup(dest);
  await rotate({ daily: 14, weekly: 4, monthly: 6 });
}
```

**스케줄**:
- 앱 종료 직전 1회 (`before-quit`)
- 매일 첫 캡처 시 (`<profileDir>/backups/.last-snapshot` mtime 비교)
- 명시 트리거: 트레이 메뉴 "지금 백업"

**저장 정책 — Grandfather-Father-Son**:
- 일일 14개 → 주간 4개 → 월간 6개. 누적 24개 안팎, 평균 사이즈 가정 시 < 50MB.
- `backups/` 는 미디어 미포함 (DB 만). 미디어는 L2 또는 L3 책임.

**위협 미커버**: 디스크 자체 고장. SSD 가 죽으면 backups/ 도 같이 죽음. L2 가 이 위협 담당.

#### L2 — git remote 동기화 (RECOMMENDED 핵심 layer)

**핵심 결정: SQLite 바이너리를 push 하지 말고, F5 마크다운 트리를 push 한다.**

| | SQLite 바이너리 | F5 마크다운 트리 |
|--|----------------|-----------------|
| diff 의미성 | 0 (전체 blob 변경) | ✅ 노트별 라인 diff |
| repo 사이즈 | 매 push 마다 풀 DB | 변경 노트만 |
| 멀티 디바이스 머지 | 불가 (binary conflict) | 가능 (텍스트 merge) |
| 외부 도구 호환 | 0 | RAG / Obsidian / grep 즉시 |
| F5 와 의 시너지 | 0 | F5 그대로 재사용 |

→ **F5 의 export 형식을 git 추적 대상으로 그대로 사용**. F5 가 promoted 되면 F6-L2 는 그 위에 자동화 layer 만 얹는 구조.

**아키텍처**:

```
[CaptureService / NoteRepository]
        │ (write)
        ▼
   inkling.sqlite       ← Layer 0 (primary)
        │
        │ (DB write 후 dirty 마크)
        ▼
   <profileDir>/sync/   ← Git working tree (L2)
        ├── notes/      ← F5 형식 마크다운
        ├── media/
        ├── index.jsonl
        └── manifest.json
        │
        ▼ (BackgroundSyncWorker, 5분 주기 또는 dirty=true 후 30초 debounce)
   git add . && git commit -m "..." && git push
```

**커밋 메시지 컨벤션** (자동 생성):

```
chore(notes): +3 ~1 -0 (2026-04-26T14:23+09:00)

added:    01H89aab... 주간 회고 PR 리뷰
added:    01H89bcd... ...
modified: 01H78xyz... 어제 회의 메모
```

기존 inkling 본 저장소 commit 스타일과 분리되며, "automated note sync" 임이 명확.

**Auth & 보안**:
- Personal Access Token 또는 SSH key. Electron `safeStorage` API (OS keychain 백엔드 — Windows 는 DPAPI) 로 평문 미저장.
- 토큰은 절대 로그/오류 메시지에 노출 금지 (slice §1.1 invariant 4 확장).
- repo 는 **반드시 private** — 평문 raw_text 노출 위험. 처음 설정 시 다이얼로그에 굵은 경고.

**Conflict 정책 — single-active-device 가정**:
- push 가 거부되면 (다른 디바이스가 먼저 push) → `git pull --rebase` → 자동 머지 시도
- 머지 실패 (같은 노트 양쪽 수정) → 트레이 알림 + 수동 해결 다이얼로그. 노트별 "내 버전 / 원격 버전 / 둘 다 보존" 3-way 선택
- 본인 dogfood = 단일 활성 디바이스라 거의 발생 안 함 — 멀티 디바이스 시나리오 정식 지원은 L2 의 v2

**Repo 초기화**:
- 첫 설정 시 사용자가 빈 remote URL 입력 → 앱이 `git init` + 초기 export + 첫 커밋 + push
- 또는 기존 repo URL 입력 → clone → 검증 (이전 manifest 호환성) → 동기화 시작

**미디어 정책**:
- 평문 push 가 default — 텍스트 노트와 함께 미디어도 git 에 올라감
- repo 사이즈 폭발 위험 → 토큰 옵션: "이미지 제외" 토글 또는 Git LFS (선택). 1차는 옵션 X, 단순 push, 사이즈 모니터링만.
- 이미지 제외 시 frontmatter 의 `images` 항목은 보존하되 파일은 미포함 → 복원 시 placeholder 표시

#### L3 — 수동 전체 export / import

- **export**: F5 가 그대로 담당. 변경 없음.
- **import**: 신규. F5 형식 폴더를 읽고 DB 에 upsert. 충돌 정책:
  - id 충돌 + 본문 동일 → skip
  - id 충돌 + 본문 상이 → 사용자 선택 (덮어쓰기 / skip / 양쪽 보존하며 새 id 생성)
  - id 신규 → insert
  - 미디어 → MediaStore 에 복사
- 트레이 메뉴 "백업에서 복원..." → 폴더 선택 → 미리보기 (n개 신규, m개 변경, k개 충돌) → 확인 → 적용

### 결정 대기

1. **3-layer 동시 도입 vs 단계적**: L1 → L3 → L2 순서가 비용·위험 단조 증가라 권장. L1 만으로도 SPOF 완화의 80% 커버.
2. **L2 sync 단위**: 매 변경 vs 5분 debounce vs 종료 시 1회 vs 명시 동기화만. 실시간일수록 데이터 손실 윈도우 작지만 git push 빈도 폭발 + 네트워크 마찰. **5분 debounce + 종료 시 즉시 push** 가 1차 권장.
3. **L2 repo 분리**: 기존 `gitea.altair823.xyz/altair823-org/inkling` (소스 코드) 와 분리된 별 repo (예: `altair823-org/inkling-data`) — **반드시 분리**. 데이터·코드 라이프사이클 다름, 외부 협업자에게 데이터 노출 위험.
4. **L2 충돌 시 정책 — slice §1.1 vs 사용자 선택**: 자동 "내 디바이스 우선" 가속 vs 매번 묻기. dogfood 단일 디바이스 가정으론 자동 OK, but defensive 차원에서 충돌 발생 시 1회 확인이 안전.
5. **media 의 git 추적**: 포함 vs 제외 vs LFS. 1차는 포함 + 사이즈 < 100MB 경고. 누적 시점에 후속 결정.
6. **L1 백업 위치**: `<profileDir>/backups/` (현 프로필 안) vs 별 디렉터리 (`%APPDATA%\Inkling\backups\`) vs 사용자 지정 외부 경로. 외부 경로 옵션이 OneDrive 등 클라우드 sync 폴더 이용 가능 — 거의 공짜 cloud backup.
7. **import 시 raw_text invariant 보호**: slice §1.1 "raw_text 불변" 은 *동일 id 내* 의미. import 가 같은 id 의 raw_text 를 다른 값으로 덮어쓰면 invariant 위반. 충돌 시 raw_text 다르면 **새 id 강제** 정책이 안전.
8. **L2 첫 설정의 UX 부담**: token 입력 + remote 검증 + 초기 push 가 dogfood 1일차 첫 인상에 마찰. 첫 설치 후 N 일 (예: 7일) 까지는 L1 만 켜두고 L2 는 트레이 메뉴 "원격 백업 설정" 으로 opt-in 권장.
9. **암호화 — local-first 라도 token 외 추가 보호 필요한가**: SQLite·미디어·git 모두 평문. 디스크 도난 시 노출. 1차는 평문 (slice §1.1 미적용 영역), 후속에 SQLCipher / age 암호화 검토.
10. **slice §7 strict-pin invariant 영향**: L1 은 `better-sqlite3.backup()` 만 사용 — 추가 dep 0. L2 는 `simple-git` 또는 `nodegit` 같은 git 바인딩 또는 child_process 로 git CLI 호출. CLI 호출이 dep 0 + 사용자 git 환경 재사용. **CLI 호출 권장**.

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | dogfood 2주 누적 동안 디스크 측 사건 (실수 삭제, DB 손상, 디스크 고장) ≥ 1회 발생할 정성 가능성 | 발생 시 라벨링 |
| H2 | L1 단독 만 도입해도 SPOF 발생 시 회복 가능 (백업으로 ≥ 95% 데이터 복원) | 복원 시뮬레이션 1회 (의도적 DB 삭제 후 복원) |
| H3 | L2 5분 debounce push 가 일평균 ≤ 30 commit. repo 사이즈 누적 < 100MB / 1년 | 로그 측정 |
| H4 | L2 commit 메시지 통계 (added·modified·deleted) 가 dogfood 활동 회고 자료로 가치 발생 | 정성 평가 |
| H5 | "이제 잊어도 됩니다" 보상의 신뢰도 — 백업이 있다는 인지가 capture 빈도 또는 심리적 부담 감소에 영향 | 본인 self-report |

### 범위

- **In (L1 — 슬라이스 후속 가벼움):**
  - `BackupService` 신규 — `db.backup()` 래핑 + 로테이션
  - 트레이 메뉴 "지금 백업" + 타임스탬프 표시
  - 종료·일일 1회 자동 트리거
  - `backups/` 디렉터리 — `.gitignore` 와 같은 .ignored 마커 고려
  - 단위 테스트 — 로테이션 GFS 정책
- **In (L3 — F5 위에 import 만):**
  - `ImportService` 신규
  - 충돌 미리보기 다이얼로그
  - 트레이 메뉴 "백업에서 복원..."
- **In (L2 — 별 spec, 가장 큼):**
  - `SyncService` (BackgroundSyncWorker)
  - F5 ExportService 의 incremental 모드 (변경 노트만)
  - git CLI 래퍼 + safeStorage 토큰 관리
  - 설정 UI — remote URL, 토큰, 동기화 주기, 미디어 포함 여부, 충돌 정책
  - 충돌 해결 다이얼로그
  - 상태 표시 (트레이 아이콘 색·tooltip)
- **Out:**
  - SQLCipher 암호화
  - 다중 활성 디바이스 실시간 sync
  - 외부 SaaS (Dropbox API, Google Drive API) 직접 연동
  - Rsync 전송
  - SQLite WAL 의 logical replication

### 영향

- **Schema:** 없음
- **신규 파일 (L1 + L3):**
  - `src/main/services/BackupService.ts`
  - `src/main/services/ImportService.ts`
  - `src/main/ipc/backupApi.ts`
  - 테스트 `tests/unit/BackupService.spec.ts`, `ImportService.spec.ts`
- **신규 파일 (L2 별 spec):**
  - `src/main/services/SyncService.ts`
  - `src/main/services/GitClient.ts` (git CLI 래퍼)
  - `src/main/services/CredentialStore.ts` (safeStorage 래퍼)
  - 설정 UI (Settings 창 신설 — 슬라이스 §5 의 "Settings 창 없음" 결정 재검토 필요)
- **외부 의존:**
  - L1: 0
  - L3: 0
  - L2: 사용자 머신의 git CLI 필요. README 사전 요구 항목 추가
- **로깅:**
  - 백업 시작·완료·사이즈만. 본문·파일명 미기록
  - 동기화 push 결과·conflict 발생만. 토큰·URL 일부 마스킹
- **문서:**
  - 본 항목 promoted 시 분리 권장:
    - `2026-04-26-local-snapshot.md` (L1)
    - `2026-04-26-import.md` (L3, F5 와 자매)
    - `2026-04-26-git-sync.md` (L2)
  - 또는 단일 `2026-04-26-backup-strategy.md` 로 통합 후 §A·§B·§C 로 분리

### 비고

본 항목과 F5 는 **완벽한 데이터 라이프사이클 그림** 의 두 절반:
- F5 = 외부 회수 (read 방향)
- F6 = 외부 백업 + 내부 복원 (write·sync 방향)

L2 (git sync) 가 dogfood 본인의 기존 인프라 (gitea 자체 호스팅) 와 자연스럽게 맞물리는 점은 본 사용자에게 특히 강한 가치. 다른 사용자였다면 GitHub Actions 등 외부 서비스 의존이라 우선순위 낮을 수 있음.

slice §1.3 종료 조건 ("크래시 0회") 와 별개로, **"데이터 손실 0회"** 가 silent invariant 로 추가되어야 함. 본 항목 → `slice spec §1.3` 추가 갱신 후보.

---

## F7. Due Date 규칙 파서가 합성 표현을 first-match-wins 로 오인 (🚀 promoted)

**진행 상태:** 🚀 promoted (D AI-primary 채택) → `docs/superpowers/specs/2026-04-26-f7-ai-primary-due-date.md`

**발견:** 2026-04-26 v0.2.1 dogfood 첫 사이클. 본문에 `"내일 모레"` 가 들어간 노트가 due_date = 내일 (today + 1) 로 추출됨. 사용자 의도는 "모레" 또는 "이틀 안" 정도의 모호 표현.

### 관찰

`src/main/services/dueDateParser.ts` 의 매칭 로직:
- 14 high-confidence 규칙을 순회 (모레가 내일보다 우선순위 위)
- 같은 텍스트에 여러 토큰 발견 시 `text.indexOf` 로 **가장 먼저 등장한 위치** 의 토큰 채택 (`내일도 모레도 바쁨` → 내일이라는 기존 fixture 가 이 동작을 lock-in)

문제 케이스:
- `"내일 모레"` — 합성 표현 (내일+모레 ≈ "곧" / "1-2일 안") 인데 첫 토큰 "내일" 만 잡힘
- `"오늘 내일 안에"` — 동일 패턴
- `"이번 주 다음 주"` — 양가 표현
- `"3일 4일 뒤"` — 범위 표현

규칙 파서의 한계 3가지:
1. **합성/양가 표현**: 두 시간 토큰이 인접하면 단일 의미로 해석되는 한국어 관용 (사용자 의도 ≠ 첫 토큰)
2. **범위 표현**: "2~3일 뒤", "이번 주 안에", "다음 주 중", "월말 즈음" — 명확한 ISO 가 없는 본질
3. **모호 표현**: "곧", "조만간", "여건 되면" — 사람도 ISO 변환 못함

현재 동작은 첫 토큰 기반이라 **확신도 낮은 매치도 high confidence 로 표기되어 NoteCard 에 그대로 박힘**. 사용자가 매번 editing 해야 하는 마찰.

### 제안 방향

**5가지 후보, 짧은 시간 / 긴 시간 layered:**

| ID | 방향 | 비용 | 효과 | 슬라이스 적합 |
|----|------|------|------|--------------|
| **A** | "내일 모레" / "오늘 내일" / "이번 주 다음 주" 등 **합성 패턴 화이트리스트** 추가 — 인접한 두 시간 토큰 발견 시 **확신도 낮춰서 AI 위임** | 작음 | 한정적 (알려진 패턴만) | ✅ 작은 PR |
| **B** | 한 노트에 **여러 high-confidence 매치 발견 시 충돌 감지** → 모두 medium 으로 강등 → AI 위임 | 작음 | 중 — 단일 토큰은 그대로, 다중만 영향 | ✅ 작은 PR |
| **C** | 규칙 매치 결과에 **항상 confidence 정보 + matched span 첨부** + UI 에 `?` 마크 (낮은 확신도) 노출 + 클릭 1번에 빠른 dismiss | 중 | 중-상 — 사용자가 인지 + 빠른 수정 가능 | 중 |
| **D** | **AI 우선 + 규칙은 후위 검증** 으로 흐름 반전 — AI 가 due_date 응답하면 규칙으로 sanity check (확실한 매치만 그대로, 충돌 시 AI 우선) | 큼 | 가장 정확 | 후속 |
| **E** | 규칙 파서 폐기, **AI 단독** | 작음 (코드 삭제) | AI 모델 의존도 ↑, latency 의존, 결정론 손실 | 후속 |

**권장 조합 (단계적)**:

1. **즉시 (slice MVP +)**: B + 일부 A — 첫 발견 시 충돌 / 화이트리스트 매치는 medium 으로 강등 (AI 위임). 골든 픽스처에 `"내일 모레" → null` 추가.
2. **다음 cycle**: C — UI 신호 (확신도 표시 + 빠른 편집)
3. **누적 데이터 후**: D 또는 E — AI 우선/단독 흐름 반전 결정. dogfood 1주 데이터 (수용률 / 편집률 / 충돌 발생률) 가 의사결정 기반.

### 결정 대기

1. **합성 패턴 화이트리스트 (A) 의 크기**: 2 토큰 (`내일 모레`, `오늘 내일`, `이번 주 다음 주`, `오늘 내일 모레` 같은 3-token) 까지 — 어디까지가 합리적 범위인가?
2. **충돌 감지 (B) 의 임계값**: "high-confidence 토큰이 동일 텍스트에 N개 이상 등장 시 medium 강등" — N = 2 OK? 또는 토큰 간 거리 (한 줄 안 vs 떨어진 문장) 로 다르게?
3. **fixture 유지**: 기존 `'내일도 모레도 바쁨' → 내일` 테스트는 **현재 동작을 lock-in** 하는 케이스. B 도입 시 이걸 → null 로 바꿔야 일관됨. 의도된 변경?
4. **C 의 UI 위치**: 확신도 `?` 를 어디에 표시? `📅 YYYY-MM-DD ?` (라벨 옆) vs hover tooltip vs 별 색 (회색 = 확신 낮음).
5. **D vs E**: 결정론적 fallback 가치 vs AI 단순화. dogfood 누적 데이터 본 뒤 결정.
6. **F1 spec 의 후속 리스트** 에 본 항목을 추가할지, 별 항목으로 분리할지. **별 항목 권장** (영향 큼, 수정 복잡도 다름).

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H1 | dogfood 1주 동안 합성 표현 ("내일 모레", "이번 주 다음 주" 등) 등장 ≥ 3회 | 본인 라벨링 + grep |
| H2 | A+B 적용 시 false positive 매치율 > 50% 감소 | 적용 전후 1주씩 비교 |
| H3 | C 의 `?` 표시가 사용자 편집 빈도를 ≥ 30% 증가시킴 (자각 → 행동) | 편집 이벤트 카운트 |
| H4 | 본인 dogfood 에서 due_date 가 한 번이라도 잘못 박혀서 무시되는 노트 비율 < 10% (현재 측정 없으나 첫 케이스가 발생함) | 정성 라벨링 |
| H5 | D (AI 우선) 적용 시 latency 영향 ≤ 200ms / 노트 (현재 ~1-3초 대비 무시 가능) | LocalOllamaProvider 응답 시간 측정 |

### 범위

- **In (1차 — A + B):**
  - `dueDateParser.ts` — 합성 패턴 화이트리스트 (2-token 인접 시 medium 강등) + 충돌 감지 (≥2 high-confidence 매치 시 모두 medium)
  - `tests/unit/dueDateParser.test.ts` — 새 fixture (`"내일 모레" → null`, `"오늘 내일" → null`, `"이번 주 다음 주" → null`)
  - 기존 `'내일도 모레도 바쁨' → 내일` fixture 결정 — 유지 vs 변경
  - AiWorker 의 머지 로직 그대로 (`ruleResult.iso ?? aiResponse.dueDate ?? null`) — medium 매치는 iso=null 이므로 자동으로 AI 위임
- **In (2차 — C):**
  - `ParseResult` 에 `matchedSpan: { start, end }` 추가
  - `dueDateParser` 가 confidence 와 함께 매치한 토큰 span 반환
  - AiWorker 가 final result 에 `dueDateConfidence: 'high' | 'medium' | 'low'` 추가
  - DB 컬럼 `due_date_confidence` 신규 (migration v3) — 또는 transient 만 (UI 만 표시, DB 미저장)
  - `NoteCard.DueDateBadge` — 확신도 낮음 시 `?` 표시 + tooltip
- **In (후속 — D):**
  - AiWorker 흐름 반전: AI 먼저 호출 → 응답에 due_date 있으면 채택 → 규칙은 sanity (AI 응답이 명백히 틀린 경우 — 예: AI 가 과거 날짜 반환) 검증
  - 또는 E: 규칙 파서 deprecate
- **Out:**
  - "월말", "주말" 같은 모호 표현 (이미 medium 처리 중)
  - 시각 단위 (오후 3시 등)
  - 음력 / 절기

### 영향

- **Schema (1차):** 없음
- **Schema (2차 C):** migration v3 후보 — `due_date_confidence` 컬럼. 또는 transient 만 (DB 미저장).
- **코드 (1차):**
  - `src/main/services/dueDateParser.ts` — 충돌 감지 로직 + 화이트리스트 추가 (~30 줄)
  - `tests/unit/dueDateParser.test.ts` — 새 fixture 5+ 케이스
- **코드 (2차 C):**
  - `dueDateParser` 의 `ParseResult` interface 확장
  - `NoteCard.DueDateBadge` 의 props 확장 + 시각 표시
  - IPC / preload / store 의 due_date 타입 동반 갱신 (confidence 도 expose 시)
- **로깅:**
  - `AiWorker` 의 `ai.done` 메타에 `dueDateConfidence` 추가 (기존 `dueDateSource: rule|ai|none` 와 함께)
- **문서:**
  - 본 항목 promoted 시 `2026-04-MM-due-date-rule-limits.md` (가칭) 으로 추출
  - F1 spec (`2026-04-26-f1-due-date.md`) 의 §"후속" 에 본 항목 링크 추가
- **slice spec §1.3 영향**: dogfood 종료 조건 "AI 결과 7/10 수용" 의 측정에 due_date confidence 가 들어가야 의미 있는 데이터 됨 — 본 항목이 그 측정 인프라 제공.

### 비고

본 피드백은 **F1 promoted 후 첫 실사용 신호**다. v0.2.1 dogfood 의 의도된 측정 사이클 (≥1주 soak → F4-A·D 결정) 와 같은 흐름에 자연스럽게 합류하는 항목이고, 다음 cut (v0.2.2 또는 v0.3.0) 의 후보. 단계 1 (A+B) 만 작은 PR 로 분리 가능 — 즉시 시도해도 위험 0.

규칙 파서 폐기 (E) 는 매력적이지만 **AI 가 항상 떠 있는 dogfood 환경 가정** 이 중요. `OllamaBanner` 가 자주 뜨는 환경 (LAN 박스 다운 등) 에선 결정론적 매치가 가치 있음. 이 트레이드오프는 dogfood 1주 데이터 (Ollama uptime · health success rate) 보고 결정.

---

## F8. Windows 11434 포트 OS-level reserved (🚀 promoted → v0.2.3.1/v0.2.4)

**진행 상태:** 🚀 promoted → PR #21 (in-app Ollama 설정), v0.2.4 release.

**발견:** 2026-05-04 dogfood 시작 시도 직후, Windows 머신.

### 관찰

v0.2.3 binary 설치 후 첫 실행 시 OllamaBanner 가 "unreachable" 상태 지속. `curl http://localhost:11434/api/tags` 응답 없음. 하지만 `ollama app.exe` 프로세스는 떠있음. 진단 결과:
- Ollama server 가 11434 bind 무한 실패: `Error: listen tcp 127.0.0.1:11434: bind: An attempt was made to access a socket in a way forbidden by its access permissions.`
- `netsh int ipv4 show excludedportrange protocol=tcp` → 11242–11941 범위 (8개 100-port 블록) reserved
- 원인: WSL2 (Ubuntu Running) + Hyper-V `hns` (Host Network Service) 가 부팅 시 동적 NAT 용 포트 블록 할당 — 11434 가 포함됨

### 제안 방향

**v0.2.3.1 → v0.2.4 cut**: env var 의존 제거, in-app endpoint/model 설정 UI 추가. 트레이 메뉴 + OllamaBanner "설정" 링크 → modal → JSON 영속화 (`<profileDir>/settings.json`).

추가: 사용자가 빈 포트 (11942 등) 로 Ollama 재시작 후 in-app 에서 endpoint 변경 — 시스템 설정 변경 (excludedportrange 등) 없이 dogfood 즉시 unblock.

### 결정 대기

- (해결됨) endpoint + model 둘 다 in-app 설정 가능해야 하는가 → Q1=B 결정 (v0.2.3.1 spec)
- (해결됨) JSON 영속화 vs SQLite 새 테이블 → Q3=B JSON (migration 회피)
- (해결됨) Settings precedence → settings > env > default

### 가설·측정

| # | 가설 | 측정 |
|---|------|------|
| H8.1 | dogfood 사용자 의 ≥30% 가 비기본 endpoint 사용 (LAN 또는 다른 포트) | telemetry `ollama_settings_changed` (count-only, v0.2.7) |
| H8.2 | 11434 reserved 가 다른 머신에도 발생 | LAN 머신 / 동료 dogfood |

### 범위

- **In:** SettingsService + ProviderHolder + OllamaSettingsModal + IPC + 트레이 메뉴 항목
- **Out:** Multi-provider abstraction (OpenAI 등), Settings dropdown UI (v0.2.4 freetext)

### 영향

- **Spec:** `docs/superpowers/specs/2026-05-04-v0231-ollama-settings-design.md`
- **Backlog 잔여:** #39 (`ollama_unreachable.reason` 의 endpoint URL PII 우회 노출 — telemetry 하드닝 v0.2.7)

---

## F9. 앱 다중 인스턴스 spawn — SQLite race 위험 (🚀 promoted → v0.2.5 critical hotfix)

**진행 상태:** 🚀 promoted → PR #23 critical hotfix, v0.2.5 release.

**발견:** 2026-05-05 dogfood 도중. v0.2.4 설치 후 단축키 / 트레이 / 시작메뉴 클릭 반복 시.

### 관찰

앱 아이콘 클릭 시마다 새 process spawn. 트레이 아이콘 여러 개 누적. 작업관리자 에서 `Inkling.exe` process 다중 확인. 잠재 영향:
- SQLite WAL 동시 write → DB 손상 가능
- AiWorker 중복 polling → 같은 pending_jobs 두 process race
- HealthChecker 중복 polling → Ollama 부하 + telemetry 이중 emit
- `settings.json` atomic write 의 temp/rename race

### 제안 방향

**Critical hotfix** — `app.requestSingleInstanceLock()` 호출 + `second-instance` event handler 로 기존 inbox 창 restore + show + focus.

### 결정 대기

(해결됨) — Electron 표준 패턴 직접 적용.

### 범위

- **In:** `src/main/index.ts` 진입점 (whenReady 전 lock 획득). 단일 21줄 변경.
- **Out:** None — cross-platform 자동 동작.

### 영향

- v0.2.5 critical hotfix release (`Inkling Setup 0.2.5.exe`)
- **Backlog 잔여:** #46 hidden-start race (NSIS 직후 사용자 클릭 + autostart `--hidden` 동시 시도) — v0.2.6 (PR #24) 에서 `additionalData` 패턴으로 해결 완료.

### 비고

dogfood 발견 → 4시간 안에 hotfix release. 잠재 데이터 손실급 버그라 patch increment (v0.2.5) 사용. v0.2.4 사용자 즉시 업그레이드 권장.

---

## F10. 버전 / 빌드 정보 표시 surface 부재 (🚀 promoted → v0.2.4)

**진행 상태:** 🚀 promoted → PR #22, v0.2.4 release.

**발견:** 2026-05-04 dogfood 도중. 머신 간 버전 일치 검증 시도 시.

### 관찰

설치된 Inkling 의 버전을 UI 어디서도 확인 못함. 트레이 메뉴 / Inbox 푸터 / About 모달 모두 부재. 핸드오프 후 다른 머신 (Mac vs Windows) 에서 같은 버전인지 검증 path 없음. issue report 시 첨부할 디버그 정보 (Electron / Node / OS / profileDir) 도 노출 안 됨.

### 제안 방향

트레이 메뉴 마지막 항목 (종료 직전) "Inkling 정보..." → native `dialog.showMessageBox`. 표시:
- 버전 (`app.getVersion()`)
- Electron / Node 버전
- OS (`platform()` + `release()`)
- 데이터 위치 (`app.getPath('userData')`)

버튼: 확인 / 데이터 위치 열기 (`shell.openPath`) / 정보 복사 (`clipboard.writeText`).

### 영향

- v0.2.4 cut 동봉. 별도 spec 없음 (단순 추가).
- **Backlog 잔여:** Inbox footer 형태 검토 (v0.2.7 — 항상 보이는 작은 버전 라벨)

---

## F11. Single-instance lock 부재 (🚀 promoted)

**진행 상태:** 🚀 promoted → F9 와 동일 cut (v0.2.5 hotfix).

F9 와 root cause 동일. 원래 별개 발견이었지만 fix 가 같음 — 본 항목은 F9 의 부분으로 흡수.

---

## F12. 윈도우 자동 실행 옵션이 재시작 후 풀려있는 버그 (🚀 promoted — v0.2.7 deeper fix)

**진행 상태:** 🚀 promoted → docs/superpowers/specs/2026-05-06-v027-cross-platform-design.md §9. v0.2.7 진단 노출 (withArgs/noArgs/registry/execPath + mismatch 경고 + 재등록 버튼) 적용 — 설정 페이지 "자동 실행" 섹션에서 사용자가 진단 정보 직접 확인 + 1-클릭 재등록 가능.

**발견:** 2026-05-05 dogfood 도중. autostart 토글 후 재시작.

### 관찰

트레이 메뉴 "윈도우 시작 시 자동 실행" 체크 → 종료 → 재실행 → 체크박스 풀려있음. `app.setLoginItemSettings({ openAtLogin, args: ['--hidden'] })` 호출 후 다음 부팅 시 `app.getLoginItemSettings()` 가 `openAtLogin=false` 반환.

### 제안 방향 (현 cut: 진단 fallback)

추정 원인:
- (a) Windows registry path mismatch (NSIS 설치 위치 변경 / 버전 업데이트 시 새 디렉터리)
- (b) Electron `setLoginItemSettings` Windows 구현 의 path canonicalization
- (c) `args: ['--hidden']` 와 actual launch args 비교 mismatch — `getLoginItemSettings()` 가 args 없이 호출되면 mismatch

**v0.2.6 적용**:
- `getLoginItemSettings({ args: ['--hidden'] })` 명시 → 트레이 checkbox checked 상태 가 실제 args 와 정합 비교
- `autostart.state` 진단 로그 (withArgs vs noArgs 비교 + executableWillLaunchAtLogin) — dogfood 에서 실제 동작 로그 수집

### 결정 대기

dogfood 후 `autostart.state` 로그 분석:
- args 명시 fix 만으로 충분 → close
- 여전히 mismatch → registry 직접 inspect (HKCU\Software\Microsoft\Windows\CurrentVersion\Run\inkling) → exe path 확인 → executable path canonicalization 검토

### 영향

- dogfood UX 핵심 마찰 — autostart 가 핸드오프 시 매번 수동 재설정 필요. 자동 실행 의도 자체가 dogfood "잊지 않고 매일 사용" 목적인데 깨짐.
- v0.2.6 진단 fallback 로 일단 시도, **v0.2.7 deeper fix 영역**.

---

## F13. PR review 발견: restoreNote production path dead code (🚀 promoted → v0.2.6 round 1 Critical fix)

**진행 상태:** 🚀 promoted → PR #24 round 1 Critical fix, v0.2.6 release.

**발견:** 2026-05-05 PR #24 round 1 reviewer (Claude code-reviewer subagent).

### 관찰

v0.2.6 cut 의 B1 (#10 restoreNote) 작업이 새 메서드 `NoteRepository.restoreNote(id)` 추가했지만 — **production path 가 옛 메서드 `repo.restore()` 호출 중**. CaptureService.restoreNote (line 93) 가 `this.repo.restore(noteId)` 호출 → `deleted_at = NULL` 만 set, `ai_status='failed'` 그대로 + `pending_jobs` 미재생성.

즉 새 메서드는 unit test 만 검증, 실제 사용자가 trash → AI fail → restore 흐름 시 영구 fail 상태 그대로.

### Fix

**Round 1 Critical fix (commit `a991008`)**:
- `CaptureService.restoreNote` 가 `repo.restoreNote` 호출 (production path 활성화)
- `before` 의 `ai_status` 가 `failed` 또는 `pending` 이면 `worker.enqueue(noteId)` 추가 호출 — in-memory AiWorker queue 갱신 (다음 app start 까지 대기 X)

테스트 +2 (CaptureService 의 enqueue 호출 검증).

### 비고

Round 1 reviewer 의 발견 가치 = **production path 와 unit test 가 갈라진 dead code 패턴**. 비슷한 패턴 다른 곳도 점검 가치. v0.2.7 brainstorm 시 grep 으로 "신 메서드 vs 옛 메서드 둘 다 존재 + 호출자 옛 사용" 패턴 검사.

---

## F14. macOS dock 클릭 시 hidden 창 재현 안 됨 (🚀 promoted → v0.2.7)

**진행 상태:** 🚀 promoted → docs/superpowers/specs/2026-05-06-v027-cross-platform-design.md §8. 2026-05-05 v0.2.6 dogfood 발견.

**발견:** 2026-05-05 김태현 macOS dogfood 도중.

### 관찰

- macOS 에서 inbox 창 닫음 (빨간 신호등) → 창 사라짐.
- Dock 에는 Inkling 아이콘이 "켜져 있음" 표시 (점) 으로 그대로.
- Dock 아이콘 클릭 → **창이 안 뜸**. 클릭이 그냥 무시됨.
- 트레이 메뉴 "보관한 메모 보기" 로는 정상 호출 가능 (별 이슈 없음).
- 앱을 완전히 종료 후 재실행 해야만 창이 다시 뜸.

### 제안 방향

추정 원인 (코드 확인 결과):

- [src/main/windows/inboxWindow.ts:39-44](src/main/windows/inboxWindow.ts#L39-L44) — close 이벤트에서 `app.isQuitting === false` 면 `e.preventDefault()` + `inboxWindow.hide()`. 즉 창은 살아있고 단지 hidden.
- [src/main/index.ts:411-413](src/main/index.ts#L411-L413) — `app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createInboxWindow(); })`. 하지만 hidden 창은 destroy 되지 않았으므로 `getAllWindows().length === 1`. 분기 안 타고 no-op.

따라서 fix 방향은 단순:

```ts
app.on('activate', () => {
  const win = getInboxWindow();
  if (win && !win.isDestroyed()) {
    win.show();
    win.focus();
  } else {
    createInboxWindow();
  }
});
```

(B4 #46 second-instance 핸들러의 windows-restore 로직과 동일 패턴 — 거기서는 macOS dock 이 아닌 Windows multi-launch 경로지만 의도가 같음.)

### 결정 대기

- macOS 에서 트레이 (menubar extra) 도 사용 중인가? Windows 에서 트레이가 핵심 surface 인 반면 macOS 에서는 dock 클릭이 자연스러운 1차 surface.
- 현재 macOS 빌드는 "기본 동작 유지" 수준 (메모리 우선순위 정책). dogfood 1차 타깃이 Windows 라 macOS 마찰을 어디까지 잡을지 cost-benefit 결정 필요.

### 가설·측정

- Fix 후 macOS dogfood 시 dock click → 창 즉시 등장 (1 클릭, < 100ms 체감).
- regression 측정: Windows 에서 close→tray 메뉴 경로 영향 없음 (activate 이벤트는 macOS 전용 흐름이라 Windows 무관해야 함).

### 범위

- 1-file edit (`src/main/index.ts` activate 핸들러 5줄).
- 단위 테스트 추가 어려움 (BrowserWindow + activate 이벤트 mocking 비용 ↑) — manual dogfood 검증 으로 충분.
- v0.2.7 telemetry export 와 함께 묶어 cut.

### 영향

- macOS dogfood UX 마찰. "앱이 떠 있는데 안 보임 + 클릭 무반응" 은 사용자가 "고장났나?" 의심하게 만드는 첫 신호 — 신뢰 손상 큼.
- Windows 는 트레이 중심이라 동일 증상 없음. macOS 단독 이슈.
- v0.2.7 우선순위: F12 autostart 풀림 (Windows) 와 동급의 "잊지 않고 매일 쓰는" 흐름 마찰.

---

## F15. Linux 앱 빌드 (🚀 promoted → v0.2.7 / CLI 부분 거부)

**진행 상태:** 🚀 promoted (Linux 빌드만) → `docs/superpowers/specs/2026-05-06-v027-cross-platform-design.md` §5. CLI 부분은 ❌ rejected (DB/Ollama 동시접근 race + monorepo 부담 대비 본인 dogfood metric 직접 기여 적음).

**발견:** 2026-05-05 dogfood 외부 피드백 + 본인 후속 결정.

### 관찰

- Linux 터미널 사용자가 "Inkling 을 터미널에서 쓰고 싶다" 요청.
- 본인 macOS 흐름에서도 GUI quick-capture 띄우는 비용 > `ink "한 줄"` 한 방 비용.
- 현재 빌드 타겟: Windows NSIS x64 + macOS DMG arm64. Linux 타겟 자체 없음 → Linux 사용자에게 입구 자체 부재.

### 결정된 방향 (narrow scope)

**범위 (v0.2.7 narrow):**
- CLI **capture-only**: `inkling capture "한 줄"` 만 노출. search/recall/restore/edit 등 v0.3+ 로 deferred.
- 플랫폼: **Linux + macOS** (Windows CLI 제외 — 본인 Windows 는 GUI 흐름).
- Linux 앱 빌드 추가: **AppImage + deb** 1차. rpm 은 외부 demand 후.

**아키텍처 — race-free 설계:**
- CLI = SQLite WAL writer 로 `notes` INSERT (raw_text) + `pending_jobs` INSERT (note_id) 만 수행.
- AI 워커는 **Electron 메인 단일 보유**. CLI 는 워커 안 띄움.
- Electron 떠있으면 → 워커가 즉시 pending_jobs poll → AI 처리.
- Electron 안 떠있으면 → 다음 launch 시 처리 (기존 pending_jobs 큐 모델 그대로).
- 동일 DB 파일 동시 접근은 SQLite WAL 로 안전 (1 writer + N readers). Single-instance lock 은 **Electron 인스턴스 끼리만** 적용 — CLI 는 짧은 transaction 만 수행 후 종료, lock 우회 무관.

**플랫폼별 build 비용:**

| 타겟 | 빌드 비용 | 커버 |
|---|---|---|
| AppImage | 매우 낮음 (Mac/Linux cross-build 가능) | 모든 desktop linux 배포판 (1-file portable) |
| deb | 낮음 (Mac brew `dpkg` `fakeroot`) | Debian/Ubuntu/Mint |
| rpm | 중간 (Linux 호스트 또는 Docker) — **defer** | Fedora/RHEL/openSUSE |

**CLI 패키징:**
- pkg / nexe 로 Node CLI → 단일 바이너리. Linux + macOS 각각 빌드.
- 또는 Electron 앱 안에 `inkling` 심볼릭 링크 노출 (Mac: `/Applications/Inkling.app/Contents/MacOS/inkling`, Linux: AppImage 내 path 노출 / deb postinst hook).
- 결정 대기 — v0.2.7 brainstorm 때 spike.

### 결정 대기 (v0.2.7 brainstorm)

- CLI 패키징: 별도 단일 바이너리 vs Electron 앱 번들 내 노출 — install UX 영향 큼.
- AI 처리 시점 표기: capture 시 즉시 stdout 으로 "queued" 만 회신할지, `--wait` 플래그로 처리 완료 대기할지.
- AppImage + deb 동시 vs 단계적.

### 가설·측정

- 본인 macOS 흐름에서 quick-capture window 호출 vs `ink "..."` time-to-capture 비교. 후자가 의미 있게 빠르면 본인 Aha metric (7일/3일) 에도 기여 가능.
- 외부 Linux 사용자 1주 soak 후 capture 발생 빈도 (telemetry) — 채널 살아있는지 확인.

### 범위

- Linux 앱 빌드 + AppImage + deb: 1~2일.
- CLI capture-only (Mac/Linux 양쪽): 2~3일.
- better-sqlite3 prebuild 매트릭스 확장 (linux-x64): 부수 작업, 0.5일.
- 합 약 4~5일 spike — v0.2.7 cut 1개로 가능.

### 영향

- 본인 macOS dogfood capture 가속 (직접 효용).
- Linux 사용자 입구 제공 (외부 확장 첫걸음).
- v0.4 slice 자기 종료 조건 (본인 2주 완주) 와 병행 가능 — capture 만 노출하므로 v0.4 핵심 흐름 (recall, AI tag, due) 변경 없음.
- Risk: Linux native ABI 첫 진입 — better-sqlite3 prebuild 가 linux-x64 에서 깔끔히 떨어질지 dogfood 검증 필요.

---

## F16. 트레이 의존도 ↓ + 별도 설정 페이지 (🚀 promoted → v0.2.7)

**진행 상태:** 🚀 promoted → docs/superpowers/specs/2026-05-06-v027-cross-platform-design.md §6, §7. 트레이 = quick-capture / 보관함 / 설정 / 종료 4-항목 minimum 으로 강등 + inbox window 안 SettingsPage (AI provider · 자동 실행 · 백업/복원 · 내보내기/가져오기 · 텔레메트리 · 정보 6 섹션) 신설.

**발견:** 2026-05-05 dogfood 후속 결정.

### 관찰

- 현재 [src/main/tray.ts](src/main/tray.ts) 가 **13개 항목** 보유: 보관함 열기, 한 줄 적기, 백업, 내보내기, 복원, 동기화, telemetry export, Ollama 재확인, AI 재처리, **Ollama 설정...**, **자동 실행 토글**, 정보, 종료.
- Windows: SysTray 잘 보임 → 핵심 surface 로 기능했음.
- **macOS**: menubar extra 로 뜨지만 사용자가 메뉴바 가득찼을 때 가려짐. dock 사용자 흐름과 분리되어 발견성 ↓.
- **Linux**: 모던 GNOME 등 일부 DE 는 system tray 자체 없음 (extension 필요). KDE/Cinnamon 은 보임.
- 결과: macOS / Linux 사용자가 **Ollama 설정 / 자동 실행 토글 등 핵심 설정에 접근 불가**.

### 결정된 방향 (정책 변화)

**1. 트레이 = 최소 surface 로 강등.**
- 잔류: 한 줄 적기 (quick capture), 보관함 열기, 종료.
- 이동: Ollama 설정, 자동 실행, 백업/복원/내보내기, telemetry export, AI 재처리, Ollama 재확인, 정보 → **설정 페이지** 또는 inbox window 안 메뉴.

**2. 별도 설정 페이지 신설 (inbox window 안 또는 별도 윈도우).**
- 예상 섹션: AI provider (Ollama endpoint/model — 기존 OllamaSettingsModal 흡수), 자동 실행, 백업/복원, 내보내기/가져오기, 텔레메트리, 정보.
- 현재 OllamaSettingsModal 은 inbox 안 modal — 그대로 활용하거나 페이지화 검토.

**3. 1차 액션 진입점 재설계.**
- macOS: dock 클릭 (F14 fix 와 동시) → inbox 창 → 메뉴 또는 톱니바퀴 → 설정.
- Linux: 앱 launcher 또는 CLI (`inkling settings` 후보) → inbox 창 → 설정.
- Windows: 트레이 우클릭 "한 줄 적기" + "보관함 열기" 만 잔류 + 트레이에서 "설정..." 한 항목 추가 (inbox 안 설정 페이지로 라우팅).

### 결정 대기 (v0.2.7 brainstorm)

- 설정 surface 형태:
  - (a) inbox 안 별도 라우트 (e.g., `/settings`) — SPA 내부 페이지
  - (b) 별도 BrowserWindow — 독립 윈도우
  - (c) 메뉴바 (Application Menu) 사용 — macOS 표준이지만 Win/Linux 와 일관성 깨짐
  - 추천 잠정: (a) — 최소 비용, 기존 inbox UI 연속성, OllamaSettingsModal 흡수 자연스러움.
- 트레이 잔류 항목: "설정..." 1줄 잔류 vs 완전 제거 (Windows 사용자 경험 시 trade-off).
- 자동 실행 토글의 실제 동작 — F12 deeper fix 가 설정 페이지 안에서 더 명확한 진단 노출 가능 (registry path 확인, args 미스매치 표시 등).

### 가설·측정

- 새 사용자 (macOS) 가 Ollama endpoint 변경 작업 완료까지 클릭 수: 현재 (트레이 메뉴에 가려짐) vs 설정 페이지 (inbox → 설정) — 후자가 의미 있게 짧으면 채택.
- 트레이 메뉴 항목별 사용 빈도 telemetry — v0.2.7 export 후 실제 어떤 항목이 자주 클릭되는지 보고 잔류 우선순위 결정.

### 범위

- 1~2일: OllamaSettingsModal 을 설정 페이지 섹션으로 흡수.
- 1~2일: 자동 실행 / 백업 / 복원 / 내보내기 등 트레이 click 핸들러를 설정 페이지 버튼으로 이동 (메인 IPC 는 그대로).
- 0.5일: 트레이 메뉴 슬림화 (3~4 항목으로).
- 합 3~5일. CLI (F15) 와 묶으면 v0.2.7 한 cut 가능.

### 영향

- **macOS / Linux 입구 정상화** — F14 (dock 클릭) + F15 (CLI 입구) 와 정합. 외부 Linux 사용자 + 본인 macOS 모두 설정 접근 가능.
- **F12 autostart 영향**: 자동 실행 토글이 설정 페이지로 이동하면 진단 정보 (registry path, args 비교 결과) 노출 가능 → deeper fix 자연스럽게 결합.
- **트레이 코드 단순화**: 13 항목 → 3~4 항목. v0.2.6 의 TrayCallbacks 객체화 (C2) 효과 가시화.
- Risk: Windows 사용자 흐름 변경 — 트레이 한 클릭으로 끝나던 동작이 inbox 열기 → 설정 → 항목 클릭 으로 늘어남. 단, 빈도 낮은 동작 (Ollama 설정 변경, 백업 등) 만 이동하고 자주 쓰는 캡처/보관함 은 트레이 잔류 → 체감 마찰 ↓ 예상.

---
## F17. 휴지통의 의미 혼재 — 완료/보관과 버림 구분 (🚀 promoted → docs/superpowers/specs/2026-05-09-v029-cut-b-design.md)

**진행 상태:** 🚀 promoted → v0.2.9 Cut B. status 4분기 (active/completed/archived/trashed) + AI 자동 분류 버튼 + 자유 텍스트 사유.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood.

### 관찰

- 현재 메모 destination = active(`deleted_at IS NULL`) 또는 휴지통(`deleted_at != NULL`) 단일 분기.
- 사용자 의도가 갈리는 case:
  - (a) **불필요해서 버림** — 잘못 적은 캡처, 더 이상 의미 없음. 휴지통 자연스러움.
  - (b) **완료해서 더 이상 안 띄움** — 작업 끝났지만 기록은 보존하고 싶음. 휴지통은 의미 부적합 (회수 대상이 아니라 정리 대상).
  - (c) **급하지 않아 미루기** — 지금은 안 띄우지만 나중에 다시 보고 싶음. 휴지통도 active 도 어색.
- 모두 휴지통으로 보내면 의미 혼재 + 30일 후 영구 삭제 정책 (현재) 이 (b)/(c) 의도 깨뜨림.

### 제안 방향 (옵션 분석)

**A. status 컬럼 분기** (notes 테이블에 `status`: `'active' | 'completed' | 'archived' | 'trashed'`):
- `completed`: 작업 끝, 영구 보관 (회수 대상 아님, 검색 가능)
- `archived`: 장기 보관 (회수 가능, 별도 view)
- `trashed`: 30일 후 영구 삭제 (현재 휴지통)
- inbox 탭 + 휴지통 탭 외에 "완료" / "보관함" 탭 추가
- 마이그레이션 비용 + UI 변경 (NoteCard 액션 메뉴 + 헤더 탭)

**B. AI 자동 분류** (옵션 A 의 extension):
- "완료" 키워드 / 패턴 (예: "X 끝남", "처리됨", "결재됨") AI 가 감지 → 자동 `status='completed'` 제안
- 사용자가 confirm/dismiss
- F19 의 recall 강화와 결합 시 효율 ↑

**C. 휴지통 의미 유지 + 보관함만 별도** (간단):
- 휴지통은 그대로 (불필요 — 30일 영구 삭제)
- 보관함만 신규 — `status='archived'` (회수 가능, 영구 보존)
- 완료 / 미루기 모두 보관함 사용
- 변경 작음, 의미 명확

### 결정 대기 (v0.2.8 brainstorm)

- 보관함 detail UI: 별도 탭 vs 별도 라우트 vs filter 토글
- "완료" 와 "보관" 을 같은 destination 으로 둘지 (옵션 C) vs 분리 (옵션 A)
- AI 자동 분류 (옵션 B) — 정확도 + false-positive risk 측정 필요. v0.2.8 본 cut 보다 나중 가능.

### 가설·측정

- 본인 dogfood: 2주간 휴지통 이동한 메모 중 (a)/(b)/(c) 비율 — telemetry 또는 자기 회상 으로 측정. (b)/(c) 비율 ≥30% 면 의미 분기 가치 큼.
- 옵션 C 후 보관함 사용 빈도 — active 메모 대비 비율.

### 범위

- 옵션 A: 큰 작업 (DB 마이그레이션 + UI 탭 + IPC + telemetry kind 추가). 별도 spec 가치.
- 옵션 C: 1주 spike 가능 (status 컬럼 또는 별도 `archived_at` 추가 + UI 탭).
- 옵션 B: 추가 cut — 옵션 A/C 안정 후.

### 영향

- 메모 의미가 명확해짐 — "버림" vs "정리" 구분.
- 휴지통의 30일 영구 삭제 정책 유지 가능 (의미 혼재 X).
- F18 (사유 입력) 와 결합 시 사용자 의도 데이터 누적 → recall 알고리즘 (F19) 기여.

---

## F18. 메모 휴지통/보관 이동 시 사유 입력 (🚀 promoted → docs/superpowers/specs/2026-05-09-v029-cut-b-design.md)

**진행 상태:** 🚀 promoted → v0.2.9 Cut B. notes.move_reason 자유 텍스트 컬럼 + MoveStatusModal 사유 입력.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood.

### 관찰

- 휴지통 이동 = silent (단순 `deleted_at` 셋팅).
- 사용자 의도 다양 ("완료", "급하지 않음", "더 이상 필요 없음", "잘못 적음" 등) — 데이터로 보존 안 됨.
- 사유 데이터가 있으면:
  - 자기 회상 (왜 버렸는지 다시 보기)
  - recall 알고리즘 (F19) 학습 입력 — "급하지 않음" 메모는 N일 후 재surface
  - telemetry 분석 (어떤 종류 메모가 빠르게 trash 되는가)

### 제안 방향

**A. 자유 텍스트 reason 필드** (notes.deleted_reason VARCHAR 또는 별도 trash_log 테이블):
- 휴지통 이동 시 prompt: "왜 버려? (선택사항)" 한 줄 입력
- 빈 값 허용
- 검색/필터 가능

**B. preset 사유 + 자유 텍스트 옵션**:
- 빠른 선택: "완료" / "급하지 않음" / "잘못 적음" / "기타"
- "기타" 선택 시 자유 텍스트
- 통계 가능 (preset 분류)

**C. F17 의 status 분기 + 사유 결합**:
- status='completed' 이면 사유 = "완료" 자동 inferred
- status='archived' 이면 사유 입력 prompt
- status='trashed' 이면 사유 선택사항

### 결정 대기

- 사유 입력 friction vs 데이터 가치 — 매번 묻으면 capture 흐름 깨짐 risk
- preset 만 vs 자유 텍스트 — preset 만이 friction 최소
- 위치: NoteCard 인라인 dropdown vs 별도 modal

### 가설·측정

- 본인 dogfood 1주 — 사유 입력 비율 (skip 없이 입력) ≥50% 면 데이터 가치 충분.
- 사유 distribution — preset N개 / "기타" 비율 — preset 명세 검증.

### 범위

- A: 1일 (DB column + UI prompt + IPC). 가장 작음.
- B: 1.5일 (+ preset UI).
- C: F17 의 옵션 A/C 안에 포함 가능 (한 cut).

### 영향

- 사용자 의도 데이터 누적 → F19 recall 알고리즘 입력 + 자기 회상 surface.
- F17 의 status 분기 가치 ↑ (사유 + status 의 의미 결합).
- friction 우려 — preset 또는 skip 가능 으로 완화.

---

## F19. 획기적 recall 메커니즘 (✅ promoted v0.2.11 Cut D — A+D 옵션)

**진행 상태:** ✅ promoted v0.2.11 Cut D — A (FTS5 search) + D (일/주/월 회고 view) 적용. m007 마이그레이션 + `NoteRepository.search` + `reviewAggregate` + SearchBox + ReviewView. B/C/E/F 옵션은 v0.3+ deferred.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. "메모의 빠른 기록도 중요하지만 적절한 recall 도 훨씬 중요" — 본인 표현.

### 관찰

- 현재 recall surface:
  - **RecallBanner** (v0.2.3 #6) — 1일 1회, 14~21일 전 candidate 1건. 한 번에 1건 + dismiss 후 다음 날까지 안 뜸.
  - **RecoveryToast** — 주간 회복 ping (1주 capture 끊김 시 한정).
  - **search/filter** — tag 필터링만, free text search 없음.
- 빠른 기록 (capture) 는 잘 됨 — 한 줄 적기 + 트레이 hotkey 가 ≤1초 완성.
- recall 은 약함:
  - 적극적 recall 안 됨 (메모를 다시 봐야 의미 있는데 surface 가 너무 약함)
  - search 부재 — "뭐였더라" 회상 시 인덱스 없음
  - context-based recall 없음 (시간/태그/장소 기반)
  - AI 가 capture 단계에서만 활용, recall 단계 X
- 사용자 표현 "획기적 방법" — 현 RecallBanner 의 점진 개선이 아니라 질적 변화 요구.

### 제안 방향 (브레인스토밍 후보 — v0.2.8 spec 단계에서 압축)

**A. Free text search**:
- inbox 헤더 search box → raw_text + summary + tags 인덱스 검색
- SQLite FTS5 (Full-Text Search) 활용
- 가장 작은 단위 첫걸음. 다른 옵션의 prerequisite.

**B. Context-based recall**:
- 시간: "이 시간대 (오전/오후)" 메모 추천
- 요일: "이전 월요일" 메모
- 태그 클러스터링 — 현재 active tag 와 연관 메모 surface
- 사용자가 inbox 보고 있을 때 sidebar 또는 banner 로 노출

**C. AI-driven 연관 메모 추천**:
- 현재 보고 있는 메모와 의미 유사한 옛 메모 surface (embedding-based or LLM-judged)
- "이거랑 비슷한 옛날 메모 N건" UI
- Ollama embedding API 활용 가능 (모델 추가 부담)

**D. 회고 view (일/주/월)**:
- "지난 주 기록" 한 페이지 — N건 메모 + tag distribution + due date 진행
- 정해진 시점 (월요일 아침 등) banner 또는 별도 라우트

**E. Spaced repetition recall**:
- Anki 같은 SM-2 알고리즘 — "잊을 만한 시점" 에 surface
- 사용자가 confirm/dismiss → 다음 surface 시점 학습
- 현 RecallBanner 의 발전형

**F. 검색 + AI 자연어 query**:
- "지난 달 회의 메모 보여줘" 자연어 → SQL or filter 자동 변환
- Ollama function-calling 또는 prompt template
- C/D 의 통합 진입점.

### 결정 대기 (v0.2.8 brainstorm)

- "획기적" 의 정의: 현 RecallBanner 점진 vs 새 핵심 surface
- 1차 cut scope — A (search) 만으로 충분한 가치인가, B/C 와 묶어야 가치인가
- AI 의존 (C/F) 의 latency / 정확도 trade-off — Ollama 추론 비용
- F17/F18 의 status + 사유 데이터 가 recall 입력으로 활용 가능한가

### 가설·측정

- 본인 dogfood: 옛 메모 다시 본 횟수 / 캡처 수 비율 — 현재 < 5% 추정. ≥20% 까지 끌어올리는 것이 "획기적" 기준.
- search 사용 빈도 (A 만 도입 시) — 일 1회 미만이면 의미 약함.

### 범위

- A: 3-4일 (FTS5 + UI search box).
- B: 1주 + (시간/태그/요일 로직 + UI).
- C: 2주 (embedding 인프라 추가 — Ollama embedding 모델 + 벡터 저장).
- D: 1주 (회고 라우트 + aggregate query).
- E: 2주 (SM-2 + UI + 사용자 feedback loop).
- F: C 위에 추가 1주.

→ 한 cut 에 다 넣기 무리. v0.2.8 = A + D 또는 A + B 권장. C/E/F 는 v0.3+.

### 영향

- 핵심 가치 (capture → 의미 있는 보존 → 다시 보기) 의 후반 절반 완성.
- F1 (Due Date), F4 (Aha Moment), F17 (status 분기) 모두 recall 강화로 가치 ↑.
- v0.4 slice 종료 조건 (본인 2주 dogfood 완주) 의 1주차 검증 항목 = recall 효과 측정.
- 큰 cut — separate spec 이 자연스러움.

---

## F20. 기존 메모 본문 (raw_text) 수정 가능성 (✅ promoted v0.2.10 Cut C — invariant 폐기)

**진행 상태:** ✅ promoted v0.2.10 Cut C — `raw_text 불변` invariant 폐기, `note_revisions` 테이블로 변경 이력 보존. m006 마이그레이션 + `updateRawText`/`listRevisions`/`restoreRevision` repo API + RevisionHistoryModal UI. AI 재실행 input = current latest raw_text (옵션 B).

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood.

### 관찰

- 현재 NoteCard 의 EditableField 는 AI 결과 필드 (title / summary / tags) 만 수정 가능.
- raw_text (capture 시점 원본 본문) 는 read-only — 메모리 정책상 **load-bearing invariant**.
- dogfood 시 빈도 높은 use case:
  - **오타 정정** — 빠른 capture 중 잘못 입력 (예: "회으" → "회의")
  - **의미 보강** — 나중에 보니 정보 부족 ("회의" → "월요일 분기 회의 안건")
  - **잘못된 캡처 정정** — 음성/clipboard 자동 입력 시 오인식

### 제안 방향 (옵션 분석 — invariant trade-off)

**A. raw_text 수정 허용** (invariant 폐기):
- 가장 단순 — EditableField 가 raw_text 도 수정 가능.
- 비용: capture 시점 원본 lost. AI 재실행 시 input 이 user-edited 본문 — 그 시점 의도와 다를 수 있음.
- 영향: 다른 spec (F1 Due Date, F4 Aha Moment 등) 의 raw_text 불변 가정 재검토 필요.

**B. raw_text 불변 유지 + `user_edited_text` 필드 추가**:
- 원본 보존 + 사용자 정정 별도 컬럼.
- NoteCard 가 user_edited_text 우선 표시 (없으면 raw_text fallback).
- AI 재실행 시 어느 입력을 사용할지 결정 — 원본 (안정성) 또는 사용자 정정 (의도 정확성).
- 마이그레이션 + UI 분기 비용.

**C. raw_text 수정 허용 + revision history**:
- `note_revisions` 테이블 — 변경 이력 보존.
- 사용자가 옛 버전 회수 가능.
- 비용 가장 큼 (스키마 + UI + 회수 흐름).

**D. invariant 유지 (현 동작)** — 이 피드백 reject:
- 정책 사유: capture = "기록", 수정 시 의미 본질 변경.
- 그러나 dogfood 실용 마찰 클 가능성.

### 결정 대기 (v0.2.8 brainstorm — 핵심 결정)

- **invariant 재검토**: 본인 dogfood 1주 누적 시 raw_text 정정 욕구 빈도 측정. 주 ≥3건 면 옵션 A/B/C 검토 가치.
- AI 재실행 input: raw_text vs user_edited — 메모리 정책 `AI 재실행은 user-edited 필드 덮어쓰기 금지` 와 정합 검토.
- F1 (Due Date 파서), F4 (Aha Moment) 의 raw_text 가정 영향 분석.

### 가설·측정

- 본인 dogfood 1주: 메모 정정 욕구 발생 횟수 / 캡처 수. ≥10% 면 옵션 A 또는 B 강한 motivation.
- 옵션 B 시 user_edited 사용 비율 — ≥30% 면 분기 가치.

### 범위

- A: 1일 (EditableField 가 raw_text 도 처리 + IPC 수정).
- B: 3-4일 (스키마 마이그레이션 + UI 분기 + AI 재실행 정책 결정).
- C: 1주 + (revisions 테이블 + UI 회수 흐름).

### 영향

- **load-bearing invariant 재검토** — 메모리 정책 갱신 가치.
- F1 / F4 / F17 / F19 모두 raw_text 가정 재검토 영향.
- 사용자 마찰 ↓ (오타/오인식 정정 가능) vs 기록 본질 약화 trade-off.
- 옵션 B 가 가장 균형 — 원본 보존 + 사용자 정정 모두 가능.

---

## F21. 다기기 git-based 동기화 (✅ promoted v0.3.0 Cut E — 양방향 + Configure UI + Conflict)

**진행 상태:** ✅ promoted v0.3.0 Cut E — 옵션 A (자동 rebase) + B (Configure UI) + C (conflict UI). SyncService 양방향 6단계 (export → commit → fetch → rebase → re-import → push), `NoteRepository.upsertFromSync` (sync 전용 3 분기), `SettingsService.{getSyncRepoUrl,isAutoSyncEnabled,getSyncIntervalMin}` + `SyncTimer` (자동 주기 + reconfigure), `SyncSection` UI + `ConflictModal` (local/remote 2 choice, both deferred v0.3.1+). 단위 608 → 679. dogfood 1주 soak 후 Cut F (F24 vision) 진입.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. 사용자 표현: "그 중심에 git repo 를 쓸 수 있으면 좋겠어".

### 관찰 (현재 동작)

[src/main/services/SyncService.ts](src/main/services/SyncService.ts) 의 `sync()`:

1. SQLite → markdown export (ExportService) 를 `<profileDir>/sync/` 에 산출
2. `git add -A && git commit -m "chore(notes): sync <ts>" && git push`
3. `not_configured` 시 skip (`<profileDir>/sync/` 가 git repo + origin remote 가져야 함)

즉 **outbound only** — 다른 기기로 보내는 흐름은 있음.

### 누락 부분 (다기기 동기화 충족 X)

- **Pull**: 다른 기기에서 push 한 변경 가져오기 — `git fetch && git pull` 흐름 부재
- **Re-import**: pull 한 markdown 을 SQLite 로 다시 적재 (`ImportService` 가 활용 가능 — F6 백업 복원 흐름과 유사)
- **Conflict resolution**: 같은 노트를 두 기기에서 동시 수정 시 우선순위 + merge 정책
- **Configure UI**: 사용자가 `<profileDir>/sync/` 에 git init + `git remote add origin <url>` 수동 — GUI 부재
- **raw_text 불변 + user-edited 덮어쓰기 금지** (메모리 정책) 다기기 환경에서 어느 기기 user-edited 가 정답인가 결정 필요 — F20 (raw_text 수정 옵션 B `user_edited_text`) 와 강하게 연관

### 제안 방향

**A. SyncService 양방향화** (가장 작은 첫걸음):
- `sync()` 가 push 전 pull 먼저 — `git fetch && git rebase origin/main` 또는 `merge`
- pull 후 변경된 markdown → re-import 하여 SQLite 갱신
- conflict 시 user prompt (또는 일단 fail + 수동 resolve 안내)

**B. Configure UI** (설정 페이지 안 신규 sub-section 또는 별도 섹션):
- "동기화 저장소 URL" 입력 → SyncService 가 `<profileDir>/sync/` 에 git init + remote add origin 자동
- 인증 안내 (SSH key / token) — Gitea/GitHub 양쪽 호환
- 마지막 sync 결과 + 시간 표시

**C. Conflict resolution UX**:
- 옵션 1: `git merge` 시도 → 실패하면 "양쪽 비교" UI (note id 단위, 각 기기 본문 + AI 결과 비교)
- 옵션 2: timestamp 기반 자동 (마지막 수정 우선) — 데이터 lost risk
- 옵션 3: "내 기기 우선" / "원격 우선" / "수동 merge" 사용자 선택

**D. F20 (user_edited_text) 옵션 B 와 결합**:
- raw_text = 캡처 시점 원본 (절대 충돌 X — capture 는 한 기기에서만)
- user_edited_text = 다기기 sync 대상. timestamp + conflict resolution 적용
- AI 결과 (title/summary/tags) = 어느 기기 가장 최근 결과 사용

### 결정 대기 (v0.2.8 brainstorm)

- 충돌 처리 정책 — A 옵션의 default (rebase / merge / fail) 결정
- F20 invariant 결정 후 결합 — F20 옵션 B (`user_edited_text`) 가 채택되면 sync 가치 ↑
- pull 후 re-import 시점 — manual ("지금 동기화" 클릭) vs 주기적 (5분/30분 자동)
- 다기기 운영 시 Aha Moment metric (7일/3일) 측정 — sync lag 영향

### 가설·측정

- 본인 dogfood: Mac + Windows 두 기기 사용 시 (메모: 본인 Mac=업무, Windows=개인+dogfood) — 현재 single-device. 양방향 sync 후 Mac dogfood 가능성 측정.
- conflict 발생 빈도 — 양 기기에서 동일 노트 수정 케이스 (낮을 것 추정).

### 범위

- A (양방향 sync) + B (Configure UI): 1주 spike 가능.
- A + B + C (conflict UI): 2주.
- D (F20 결합): F20 채택 후 추가 1주.

### 영향

- 다기기 운영 → Aha Moment metric 직접 기여 (Mac 업무 시간에도 capture 가능).
- F20 (raw_text 수정) + F19 (recall) 모두 sync 데이터 일관성 의존.
- v0.4 slice 종료 조건 (본인 2주 dogfood 완주) 의 핵심 인프라 — 단일 기기 dogfood 의 한계 극복.

---

## F22. NoteCard 이미지가 회색 placeholder 만 표시 (🚀 promoted → docs/superpowers/specs/2026-05-09-v028-cut-a-design.md)

**진행 상태:** 🚀 promoted → v0.2.8 Cut A. inkling-media:// custom protocol + NoteCard `<img>` + IPC inbox:open-media + OS viewer 클릭. (commit 470384b + f6bea62 + 9cdea15)

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. 사용자 표현: "이미지 렌더링이 제대로 되지 않는 것 같아".

### 관찰

[src/renderer/inbox/components/NoteCard.tsx:334-340](src/renderer/inbox/components/NoteCard.tsx#L334-L340):

```tsx
{local.media.length > 0 && (
  <div style={{ marginTop: 10, display: 'flex', gap: 6 }}>
    {local.media.map((m) => (
      <div key={m.id} style={{ width: 48, height: 48, background: '#eee', borderRadius: 4 }} title={m.relPath} />
    ))}
  </div>
)}
```

**`<img>` 가 아니라 회색 `<div>`**. 즉 capture 시 첨부한 이미지가 보관함에서 회색 48x48 사각형만 표시 — title attribute (relPath) 만 hover tooltip 으로 보임. 실제 이미지 렌더링 자체 부재.

`MediaStore` 는 `<profileDir>/media/<noteId>/<filename>` 절대 경로로 파일 보존. relPath = `media/<noteId>/<filename>` 형태. Electron renderer 에서 직접 `file://` 또는 custom protocol 로 src 매핑 필요.

### 추정 원인 (placeholder 인 이유)

- 초기 v0.4 slice 단계에 thumbnail 렌더는 후순위로 미루고 placeholder 로 둔 채 그대로 잔류.
- Electron renderer 가 raw `file://` 경로 보안 정책상 직접 접근 어려움 — custom protocol (`inkling-media://`) 또는 IPC handle 로 base64 변환 필요.

### 제안 방향

**A. Custom protocol 등록** (권장):
- main process 에서 `protocol.registerFileProtocol('inkling-media', ...)` 등록 — `<profileDir>/media/` 하위 경로를 `inkling-media://<noteId>/<filename>` 으로 매핑
- NoteCard: `<img src={`inkling-media://${m.relPath.slice(6)}`} alt="" />`
- 보안: scheme 별 allowlist + protocol handler 가 path traversal 검사

**B. IPC 로 base64 변환** (작은 이미지에 한정):
- `inboxApi.getMediaDataUrl(relPath)` → main 이 file 읽고 `data:image/png;base64,...` 반환
- renderer 에 `<img src={dataUrl} />`
- 큰 이미지 (수 MB) 시 메모리 부담

**C. file:// 직접** (Electron 특수 설정 필요):
- `webPreferences.webSecurity: false` — 보안 약화 risk. **Reject**.

### 결정 대기

- thumbnail 표시 vs 클릭 시 full-size modal — UX 선택
- 다중 이미지 (현재 capture 가 N개 첨부 가능) 의 grid layout
- 이미지 alt text — capture 시 입력 또는 AI 자동 생성 (옵션)

### 가설·측정

- 본인 dogfood: capture 시 이미지 첨부 빈도 — 현재 추정치 < 일 1건. ≥ 일 1건이면 이미지 흐름 가치 큼.
- 옵션 A 도입 후 NoteCard 클릭 시 modal full-size 사용 빈도 — UX 선택 검증.

### 범위

- A (custom protocol + thumbnail): 1-2일.
- A + click → full-size modal: + 0.5일.
- alt text AI 생성: 별도 cut.

### 영향

- 명확한 bug 수정 — 사용자 마찰 명백.
- F19 (recall) 의 시각적 단서 — 이미지 보일 때 메모 회상 ↑.
- v0.2.8 narrow scope 에 포함 가치 (1-2일 작업).

---
## F23. 로컬 LLM 활성화 옵션 (Ollama-less 모드) (🚀 promoted → docs/superpowers/specs/2026-05-09-v029-cut-b-design.md)

**진행 상태:** 🚀 promoted → v0.2.9 Cut B. ai_status='disabled' enum + Onboarding wizard + 설정 토글 + Banner/HealthChecker 비활성 + requeueDisabled.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. 사용자 표현: "Ollama 를 쓰지 못하는 환경을 위해 로컬 llm 활성화 옵션을 만들고, Ollama 를 안 쓰는 경우 그냥 원문만 저장하고 보여주도록".

### 관찰

- 현재 capture 흐름: `CaptureService.create()` → notes INSERT (ai_status='pending') → `pending_jobs` enqueue → AiWorker → Ollama 호출 → title/summary/tags 채움 → NoteCard 표시.
- Ollama 의존 강제 — 회사 환경 / offline / low-resource device / 사용자 선호도 등 Ollama 안 쓰고 싶은 경우 옵션 부재.
- 현재 Ollama 끊긴 동안 capture 는 가능 (notes INSERT + pending_jobs enqueue), 단 ai_status 가 'pending' 또는 'failed' 로 고착 → FailedBanner / OllamaBanner 가 지속 노출 (사용자 불필요한 마찰).

### 제안 방향

**핵심 설계: "AI 활성화" 토글 (default ON, OFF 시 raw-only 모드)**

설정 페이지 → AI 제공자 섹션 → 새 토글 "AI 자동 처리 사용" (default ON).

#### A. OFF 일 때 동작

1. `CaptureService.create()` 가 notes INSERT 시 ai_status='disabled' (신규 enum value) + pending_jobs enqueue **skip**.
2. AiWorker 가 'disabled' 상태 노트를 pull 안 함.
3. NoteCard 표시:
   - title = raw_text 첫 줄 (또는 빈 값) — fallback rendering
   - summary = 빈 값 (UI 에서 hide)
   - tags = 빈 배열 (tag filter 비활성)
   - raw_text 그대로 노출 (이미 NoteCard 가 표시함)
4. OllamaBanner / FailedBanner 비활성 (AI off 면 의미 없음).
5. 트레이/banner 의 "지금 AI 처리" / "Ollama 재확인" surface 비활성.

#### B. OFF → ON 전환 시

기존 ai_status='disabled' 노트들 — 두 옵션:

- **B1**: 기존 disabled 잔류 (사용자가 "지금 처리" 버튼 1회 눌러야 재처리). 안전.
- **B2**: 자동 enqueue (모든 disabled → pending + pending_jobs INSERT). 사용자 의도 불일치 risk (옛 메모 갑자기 AI 처리되며 큐 폭증).

추천: **B1** — 사용자 명시적 trigger 만 옛 노트 처리. 새 노트는 ON 이후 capture 분만 자동 처리.

#### C. ON → OFF 전환 시

- 큐의 pending 잔재 — drain (현 실행 중) + enqueue stop.
- 옵션: pending → disabled 자동 변환 (대량 cleanup) vs 잔류 (다시 ON 시 재개).
- 추천: 잔류 (사용자 의도 보존).

### 결정 대기 (v0.2.8 brainstorm)

- ai_status 새 enum 값 'disabled' vs 별도 컬럼 (notes.ai_enabled BOOLEAN)
- B1 vs B2 default — B1 추천.
- raw-only 모드의 NoteCard title fallback — raw_text 첫 줄 / 첫 N자 / 사용자 입력 prompt.
- F19 (recall) 가 raw-only 모드에서도 동작 — tag 부재 시 시간 기반 candidate 만.
- F17 (status 분기) 의 AI 자동 분류 (옵션 B) 가 raw-only 모드에서 비활성 — 정합 검토.
- 처음 설치 시 default — 메모리 정책 "주 타깃 OS 는 Windows + 본인 dogfood 우선" 고려, default ON 유지 (LAN Ollama 가정).

### 가설·측정

- 본인 dogfood: 회사 환경 (Mac 업무) 에서 Ollama 못 쓰는 시간 — raw-only 모드로 capture 만 가능해지면 dogfood metric 영향 측정.
- 사용자 (외부) 가 raw-only 시작 후 ON 전환 비율 — onboarding 흐름 검증.

### 범위

- A 기본 (토글 + ai_status 'disabled' + capture skip): 2-3일.
- B/C 전환 정책 (B1 추천 — 가장 작음): + 0.5일.
- raw-only NoteCard fallback (title=raw_text 첫 줄): + 0.5일.
- 합 3-4일 — v0.2.8 narrow scope 가능.

### 영향

- **Ollama 의존성 옵션화** — 환경 다양성 (회사 / offline / 저사양) 대응.
- F17 (status 자동 분류 옵션 B) 무력화 — AI off 시 옵션 C (보관함만 별도) 가 default 흐름.
- F19 (recall) 가 tag 데이터 부재로 단순화 — context-based / spaced repetition 기반 가능 (search 는 raw_text 가 인덱스 source).
- F1 (Due Date 파서) 는 raw_text 정규식 기반이라 raw-only 모드에서도 동작 ✅.
- 메모리 정책 "raw_text 불변" 의 가치 ↑ — raw_text 자체가 1차 surface 가 되므로 보존 의의 강화.
- v0.4 slice 종료 조건 (본인 2주 dogfood 완주) 의 대안 경로 — Mac 업무 시간 raw-only capture + 저녁 Windows 에서 batch AI 처리 가능.

---

## F24. 이미지 멀티모달 AI 분석 (✅ promoted v0.3.1 Cut F)

**진행 상태:** ✅ promoted v0.3.1 Cut F — Ollama vision 모델 (gemma3 family default) 활용. capability detection (app launch + manual refresh) + Configure UI dropdown + AiWorker vision integration (5MB cap + base64 변환). 자동 fallback (caption → text) + 'skipped' enum deferred v0.3.2+. 단위 679 → 710. dogfood: vision 결과 정확도 + 한국어 token 정확도 검증.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. F22 (이미지 렌더링) + F23 (Ollama-less 모드) 와 강하게 연관.

### 관찰

[src/main/ai/LocalOllamaProvider.ts:33-42](src/main/ai/LocalOllamaProvider.ts#L33-L42) — 현재 `/api/generate` 호출 시 text-only prompt:

```ts
const res = await request(`${this.endpoint}/api/generate`, {
  prompt: buildPrompt(input.text, input.todayKst, input.dueDateCandidates, input.vocab ?? []),
  ...
});
```

`InferenceProvider.GenerateInput` 인터페이스에 `images` 필드 부재. Ollama API 자체는 `images: string[]` (base64) 지원하나 모델이 vision 지원해야 함.

이미지가 있는 capture (paste 또는 첨부) 의 경우:
- 현재: title/summary/tags 모두 raw_text 기반만 — 이미지 내용 ignore
- 이미지만 있는 capture (raw_text 빈 값): AI 처리 무의미 → 사용자가 빈 메모 받음

### 제안 방향

**A. Vision capability detection + opt-in 모델 선택** (권장):
1. 설정 페이지 → AI 제공자 섹션 → "이미지 분석 모델" 입력 (별도 필드, default 빈 값 = 비활성).
2. main process 가 startup / 사용자 트리거 시 `GET /api/tags` 로 사용자 Ollama 의 모델 목록 조회 + vision capable 모델 (llava / llama3.2-vision / gemma3 family 등) 자동 감지 → 추천 표시.
3. 사용자가 vision 모델 명시 + 본인 cluster 에 해당 모델 pull 되어 있어야 enable.
4. capability 부재 (모델 없음 / endpoint 끊김 / 빈 값) 면 vision 분석 skip — 텍스트만 + raw_text 처리.

**B. 분석 흐름 (vision enabled 시)**:

각 capture 의 이미지 + raw_text 결합 prompt 전송:

- raw_text 있음 + 이미지 있음 → "다음 텍스트 + 이미지를 종합 요약" prompt
- raw_text 빈 값 + 이미지만 → "이미지 내용 요약 + 한국어 태그" prompt
- AI 응답 형식은 기존 (`title`/`summary`/`tags`) 그대로 — vision 결과가 raw_text 자리에 들어가 자연스럽게 채워짐

**C. InferenceProvider 인터페이스 확장**:

```ts
interface GenerateInput {
  text: string;
  images?: Array<{ base64: string; mime: string }>;
  ...
}
```

`LocalOllamaProvider` 가 `images` 비어 있지 않으면 Ollama `/api/generate` 의 `images: [base64...]` 필드 추가 + vision 모델로 호출. provider 가 vision capability 부재 시 images ignore (graceful degrade).

### 결정 대기 (v0.2.8/v0.3 brainstorm)

- vision 모델 default 추천 — 한국어 + 이미지 동시 잘하는 모델 (llama3.2-vision / gemma3 family 등 — dogfood 검증 후 결정)
- 이미지 base64 변환 위치 — main process (MediaStore 가 이미 file path 보유) 가 자연스러움
- 처리 비용 — vision 모델 추론 시간 (수 초~수십 초) + 메모리 부담. capture 흐름 backend 처리 라 사용자 대기 X.
- raw-only 모드 (F23 OFF) 와 정합 — F23 토글 OFF 시 vision 도 OFF (자명).
- 이미지 alt text 자동 생성 (F22 가설 슬롯) 가능 — vision 모델이 alt 도 같이 출력하도록 prompt 설계.

### 가설·측정

- 본인 dogfood: capture 시 이미지 첨부 비율 — 현재 추정 < 일 1건. 일 ≥ 1건 누적 + vision 분석 결과 정확도 측정.
- vision 결과의 사용자 수정 비율 — 높으면 모델 부적합 (다른 모델 / prompt 튜닝).
- "이미지만 있는 capture" 의 처리 가능성 — vision 으로 의미 있는 title/summary 생성 가능한지 검증.

### 범위

- A (capability detection + 설정) + B (vision prompt) + C (InferenceProvider 확장): 1주.
- 이미지 alt text 자동 생성: + 0.5일.
- F22 (이미지 렌더링) 가 선행 prerequisite — 같이 묶으면 1.5주.

### 영향

- 멀티모달 capture — "사진만 찍고 끝" 흐름 가능 (회의 화이트보드 / 영수증 / 메뉴판 등).
- F22 (이미지 렌더링) 의 가치 ↑ — 보이는 이미지 + AI 가 의미 추출.
- F19 (recall) 강화 — vision-derived tags + summary 가 이미지 내용 기반 검색 가능하게 함.
- F23 (Ollama-less 모드) 와 trade-off — AI 토글 OFF 시 vision 도 자동 OFF (자명, 추가 분기 X).
- 메모리 정책 "raw_text 불변" 그대로 — vision 결과는 summary/tags 에만 반영.
- Ollama 의존성 ↑ (vision 모델 추가 pull 부담) — F23 OFF 시 회피 가능.

---

## F25. 사이드바 — 메모 리스트 + 메모 저장소 리스트 (🌱 raw — v0.2.8/v0.3 후보, layout 큰 변화)

**진행 상태:** 🌱 raw — layout 재구성 + "저장소" 정의 필요. v0.2.8 brainstorm 시 F17/F21 와 함께 triage.

**발견:** 2026-05-09 v0.2.7 release 후 본인 dogfood. 사용자 표현: "사이드에 메모 리스트, 메모 저장소 리스트도 보여줬으면".

### 관찰

- 현재 inbox layout = single-pane (header + NoteCard list). 사이드바 부재.
- "메모 저장소" 의미 모호 — 추정 옵션:
  - (a) **다중 profile/database** — "회사 메모 / 개인 / 학습" 등 분리. 현재 single profile (`<userData>/default/`).
  - (b) **카테고리/폴더** — 단일 DB 안 그룹화. 현재 tag 만 존재.
  - (c) **다중 sync repo** — F21 의 git sync 가 여러 remote 가능. 본인 + 외부 collaborator.
- 사용자 의도 = (a) 가 가장 자연스러움 ("저장소" 표현). 본인이 dogfood 중 분리 욕구 발생 추정.

### 제안 방향 (사이드바 + 저장소 모델 분리 결정)

#### 1. 사이드바 layout

좌측 또는 우측 column. 폭 240-320px. 상단: 저장소 selector. 하단: 메모 list (현 inbox compact view).

```
┌───────────┬────────────────────────────┐
│ 저장소     │ inbox / 휴지통 (탭)         │
│  • 기본    ├────────────────────────────┤
│  • 회사    │                            │
│  • 학습    │  NoteCard detail or grid   │
├───────────┤                            │
│ 메모 리스트│                            │
│  - title 1│                            │
│  - title 2│                            │
│  - title 3│                            │
└───────────┴────────────────────────────┘
```

#### 2. "저장소" 정의 (큰 결정)

**A. 다중 profile** (`<userData>/<profile-name>/` 별도 DB):
- 가장 정의 명확 — sqlite db / media / sync 모두 저장소 단위 분리
- 사용자가 새 저장소 생성 → migrations 새로 적용 → 빈 DB
- 메모 / 태그 / due / pending_jobs 모두 저장소 안 격리
- 마이그레이션 부담 — `resolveProfilePaths` 가 'default' fixed 인 부분 다중 지원
- AiWorker / HealthChecker / SyncService 등 모두 active profile 기반 재초기화 필요 (큰 refactor)

**B. 카테고리/폴더** (단일 DB 안 `notebook_id` 컬럼):
- 가벼움 — schema 추가만, services 영향 적음
- 단점: "회사 메모 따로 백업/sync" 불가 (단일 DB 백업)
- F17 (status 분기) 와 비슷한 의미 layer (status + notebook 두 분기 가치 충돌 가능)

**C. 단일 profile + 다중 git remote** (F21 sync 관련):
- 한 DB → 여러 sync 대상 (회사 git + 개인 git)
- "저장소" 가 sync target 의미라면 자연스러움
- 데이터 자체는 분리 안 됨 — "메모 저장소" 이름과 의미 불일치 risk

추천: **A** — "저장소" 의 사용자 의도가 데이터 분리 (회사/개인) 라면 명확. 그러나 큰 refactor.
대안: **B** — 빠른 구현, 사용자 의도 (a) 일부 충족.

#### 3. 메모 리스트 (사이드바 안)

현 inbox NoteCard list 의 compact 버전 — title + tag chip 만, raw_text/summary hide. 클릭 시 main 에서 NoteCard expand.

main pane 의 view 옵션:
- (i) 단일 detail (사이드 클릭으로 전환)
- (ii) 현재처럼 NoteCard grid (사이드는 scroll/jump 용)

추천: (ii) — 현 흐름 보존, 사이드바는 navigation 보조.

### 결정 대기 (v0.2.8 brainstorm)

- "저장소" 정의 — A/B/C 중 사용자 의도 확인 (직접 묻거나, dogfood 중 욕구 정확히 파악)
- 사이드바 토글 — default visible vs hide-by-default (좁은 화면 + 단일 저장소 시 noise)
- 메모 리스트 sort 기준 — 최신순 / tag / due date
- F17 (status 분기), F19 (recall search) 와 layout 조화 — 검색 박스 위치 (사이드 vs header)

### 가설·측정

- 본인 dogfood: "회사" / "개인" 메모 분리 욕구 빈도 — 1주 soak 후 측정. 빈도 낮으면 옵션 B 또는 C 충분.
- 사이드바 사용 빈도 — main pane 만으로 동작 가능하면 사이드바 noise.

### 범위

- A (다중 profile + 사이드바 + 저장소 selector): 2-3주. AiWorker / HealthChecker / SyncService / 마이그레이션 모두 영향.
- B (notebook_id + 사이드바): 1주. schema + repo 메서드 + UI.
- C (다중 sync remote + 사이드바 navigation 만): 0.5주 (F21 의 일부).
- 사이드바 자체 (저장소 결정 무관, navigation 기능만): 2-3일.

### 영향

- **layout 큰 변화** — 현재 single-pane → two-pane. 좁은 화면 (1280×720 dev) 영향 검증 필요.
- F17 (status 분기) 와 conceptual overlap — "저장소" + "status" + "tag" 세 분기 layer 가 사용자 정신 부담.
- F19 (recall search) — 사이드바에 search box 둘지 결정.
- F21 (git sync) — 옵션 A 시 저장소별 별도 sync repo 자연스러움.
- 본인 dogfood metric — Mac 업무 (회사 메모) + Windows 개인 (dogfood) 분리 시 가치 ↑.

---


## (다음 항목 자리)

새 피드백 추가 시 `## F26. 짧은 제목 (🌱 raw)` 헤더로 시작. 표준 슬롯 6개 채우거나 비워둔 채 시작 가능.

v0.2.8 release 후 dogfood ≥1주 soak 동안 새 발견 항목들 여기 누적 → v0.2.9 brainstorm 트리거.
