From 22a25cc62286e6ec5b3424bcb3a8b33a77de34f0 Mon Sep 17 00:00:00 2001 From: altair823 Date: Fri, 1 May 2026 13:56:16 +0900 Subject: [PATCH] docs(spec): v0.2.3 dogfood feedback roadmap (7 items, single cut) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.2.2 dogfood 7항목 (#7 telemetry 신설 + #1~#6) 단일 cut 로드맵. 데이터 안전 우선 (C 채택), schema migration v3 3컬럼 한 묶음 (B), trash↔backup/export B 정책, #6 = 1 spike 흡수. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...2026-05-01-v023-feedback-roadmap-design.md | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-01-v023-feedback-roadmap-design.md diff --git a/docs/superpowers/specs/2026-05-01-v023-feedback-roadmap-design.md b/docs/superpowers/specs/2026-05-01-v023-feedback-roadmap-design.md new file mode 100644 index 0000000..f3a1538 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-v023-feedback-roadmap-design.md @@ -0,0 +1,342 @@ +# v0.2.2 Dogfood 피드백 로드맵 (#7→#6 → v0.2.3) 설계 + +**작성일:** 2026-05-01 +**저자:** 김태현 (dlsrks0734@gmail.com) +**문서 성격:** v0.2.2 dogfood 중 발견된 7개 항목 (`memory/project_v022_feedback.md` #1~#6 + 본 brainstorm 에서 추가된 #7 telemetry) 의 순차 작업 로드맵. 본 문서는 **순서·범위·게이트** 만 정의하며, 각 항목 내부 설계는 항목별 mini-brainstorm + writing-plans 에서 결정. + +**선행 문서:** +- `memory/project_v022_feedback.md` (raw 피드백 6건) +- `docs/superpowers/specs/2026-04-26-feedback-roadmap-design.md` (v0.2.1 로드맵, 본 문서의 패턴 원형) +- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` (slice §1.1 invariant 4 — 본문 미기록) +- `docs/superpowers/strategy/strategy.md` (#6 에서 §2.3·§4.3·§8 동반 갱신 대상) + +--- + +## 1. 결정 요약 + +| 결정 | 값 | 근거 | +|------|-----|------| +| Cut 패턴 | **단일 cut v0.2.3** (7항목 한 묶음) | v0.2.1 패턴 반복. 항목 간 결합도 (특히 #7 → #4~#6 emit) 분리 시 회전 비용. | +| 우선순위 기준 | **데이터 안전 우선** (v0.2.1 패턴) | 측정 인프라 (#7) → schema migration v3 (#4) → 안전망 위에서 기능 진행. | +| 첫 항목 | **#7 Telemetry skeleton** | 다른 6 항목이 emit hook 만 추가. 측정 없는 기능 출시는 다음 cut 까지 1주 본인 라벨링으로 후퇴. | +| 항목당 게이트 | **머지 + 테스트 통과** (typecheck + 205+ 단위 + e2e smoke) | v0.2.2 기준선. | +| 다음 빌드 | **v0.2.3** (7항목 모두 머지 후 단일 cut) | slice §7 strict-pin patch 증분. | +| 신규 dependency | **0 목표** | 모두 stdlib + 기존 better-sqlite3 / electron 으로 충분. | +| Schema 변경 | **migration v3** — 3 컬럼 한 번에: `deleted_at TEXT NULL`, `last_recalled_at TEXT NULL`, `recall_dismissed_at TEXT NULL` | #4 휴지통 + #6 회상 메타 한 묶음. 별 migration 두 번 회피. | +| Trash 와 export/backup | **B 정책** — F6-L1 backup 포함 (byte-for-byte), F5 export 제외, F6-L3 import 시 `deleted_at IS NOT NULL` 우선 (삭제 보존) | 백업은 회복 용도, export 는 외부 노출 형식. | +| Decision-pending 처리 | **항목별 mini-brainstorm** | 본 문서는 순서·In/Out 만, 항목 내부는 per-item. | + +--- + +## 2. 순차 작업 순서 + +``` +v0.2.2 ────────[ dogfood 동결, 병렬 진행 ]──────── + │ +개발 트랙 (main 직접 머지 또는 PR): │ + ① #7 Telemetry skeleton [작음, 인프라 1번] │ + ② #4 휴지통 + migration v3 [중, schema + 정책] │ + ③ #5 만료 추천 [작음, #4 destination]│ + ④ #1 Ollama 회복 polling [작음, 독립] │ + ⑤ #2 AI retry / 수동 trigger [중, AiWorker 정책] │ + ⑥ #3 태그 vocab 주입 [작음, 독립] │ + ⑦ #6 리마인드 1 spike [중, strategy 갱신] │ + │ + ┌──────────┘ + ▼ + v0.2.3 cut (단일) + │ + ▼ + dogfood 재설치 + ≥ 1주 soak + │ + ▼ + telemetry export → 분석 → + v0.2.4 brainstorm +``` + +### 2.1 순서 결정 근거 + +1. **#7 (1번)** — 측정 인프라. 다른 항목이 emit hook 추가만 하도록 skeleton 먼저. Cross-cutting privacy invariant 강제도 1번에서 단위 테스트로 고정. +2. **#4 (2번)** — schema migration v3 가 #6 회상 메타 컬럼 동반. 휴지통 invariant (`deleted_at IS NULL` 모든 active 쿼리) 가 다른 항목에 영향. 회복 안전망 (pre-v3 snapshot, v0.2.1 메커니즘) 위에서 진행. +3. **#5 (3번)** — #4 휴지통 destination 직접 소비. 같은 영역 (Inbox 상단 배너). +4. **#1 (4번)** — #2 의 reliable health 의존성. polling 인프라 먼저. +5. **#2 (5번)** — #1 health 위에서 retry/manual trigger 정책 변경. AiWorker 의 unreachable infinite retry 로 #1 polling 결과 활용. +6. **#3 (6번)** — 독립 prompt 변경. PROMPT_VERSION 4. AI 영역 마지막에 묶어서 AiWorker 회귀 위험 격리. +7. **#6 (7번)** — strategy.md 갱신 + RecallBanner 1 spike. last_recalled_at / recall_dismissed_at 사용. 가장 마지막에 두는 이유: 다른 항목 telemetry hook 이 모두 박혀야 #6 측정 가치가 살아남. + +--- + +## 3. 항목당 In (PR 범위) / Out (deferred) + +각 항목 PR 범위 라인. 세부 결정 (decision-pending) 은 항목 시작 시 mini-brainstorm. + +### #7 Telemetry skeleton (1번) + +**In:** +- `TelemetryService` (`src/main/services/TelemetryService.ts`): + - `emit(kind, payload)` → 비동기 append to `/telemetry/events-YYYY-MM-DD.jsonl` + - 일자별 rotation (KST 자정), 14일 후 rolling 삭제 + - write 실패 시 silent log only (앱 동작 영향 없음) +- 이벤트 zod schema: `{ ts: ISO string, kind: enum, payload: object }`. payload shape 는 kind 별 fixed. +- **Privacy invariant** (slice §1.1 invariant 4 강화): payload 에 `raw_text` / `ai_title` / `ai_summary` / `user_intent` / 태그 name 포함 시 zod parser 거부. 단위 테스트로 고정. +- 기본 emit hook 박기: + - `capture` (CaptureService.submit 후): `{ noteId, rawTextLength, hasMedia }` + - `ai_succeeded` (AiWorker.processJob 성공): `{ noteId, durationMs, attempts }` + - `ai_failed` (AiWorker.processJob 실패): `{ noteId, reason: "unreachable"|"schema"|"timeout"|"other", attempts }` +- 트레이 메뉴 "사용 로그 내보내기...": + - 폴더 다이얼로그 → `events.jsonl` (최근 14일 concat) + `stats.md` (집계 마크다운) zip + - `stats.md` 내용: 항목별 일자별 카운트 표 + 핵심 ratio (AI 성공률, ollama uptime%, recall opened/shown, expired batched/shown 등) +- IPC: `tray:exportTelemetry` +- 단위 테스트: emit, rotation, privacy invariant 거부, stats 집계, export zip + +**Out:** 자동 업로드 / 원격 telemetry (모두 로컬), 실시간 대시보드 UI, opt-out 토글 (로컬이라 불필요), 14일 보존 기간 사용자 설정 + +### #4 휴지통 (2번) + +**In:** +- migration v3: `notes.deleted_at TEXT NULL` + `notes.last_recalled_at TEXT NULL` + `notes.recall_dismissed_at TEXT NULL` (3 컬럼 한 번) +- `NoteRepository`: `trash(id)` (`deleted_at = now()`), `restore(id)` (`deleted_at = NULL`), `emptyTrash()` (hard delete + media 정리). 기존 `delete()` 는 deprecate 후 `emptyTrash` 내부에서만 호출. +- **Active 쿼리 일괄 `WHERE deleted_at IS NULL` 추가**: `listNotes`, `countToday`, `findByTag`, search, F5 export, AiWorker `loop()` 진입 시 deleted_at 체크 +- 휴지통 UI: Inbox 상단 탭 ("Inbox · 휴지통(N)") — 정밀 위치는 mini-brainstorm +- 휴지통 비우기 confirm dialog ("N개 영구 삭제. 되돌릴 수 없음.") +- F5 export 가 `deleted_at IS NOT NULL` 제외 +- F6-L3 import 충돌 정책 추가: source 와 dest 중 `deleted_at IS NOT NULL` 우선 (삭제 보존) +- IPC: `inbox:trash` / `inbox:restore` / `inbox:emptyTrash` / `inbox:listTrash` +- Telemetry emit: `trash` / `restore` / `emptyTrash` +- 단위 테스트: trash/restore/emptyTrash, active query 제외, AiWorker skip, F5 export 제외, F6-L3 import 머지 + +**Out:** 자동 비우기 정책 (사용자 트리거만), 휴지통 검색, trash 안 노트 편집, 휴지통 UI 정밀 위치 (mini-brainstorm), per-note 영속 보호 플래그 + +### #5 만료 추천 (3번) + +**In:** +- `NoteRepository.findExpiredCandidates({today})`: + - `WHERE due_date < today AND deleted_at IS NULL AND ai_status = 'done'` + - ORDER BY `created_at DESC` +- Inbox 상단 **만료 배너** (펼침 가능): + - "오늘 기준 만료 N개" 헤더 + - 펼치면 노트 카드 리스트 + 체크박스 멀티선택 + - "선택 휴지통" 버튼 → 일괄 trash + telemetry emit + - "오늘 그만" → in-memory snooze (자정 KST 리셋) +- IPC: `inbox:listExpired`, `inbox:trashBatch` +- Telemetry emit: `expired_banner_shown` (`{ candidateCount }`), `expired_batch_trash` (`{ count }`) +- 단위 테스트: 후보 query, 멀티선택 batch trash, snooze 동작, deleted_at 제외 확인 + +**Out:** 시스템 알림 surface, 별 페이지, snooze 영속화, "안 옮김" 가중치 감소, 만료 임박 (D-7) 추천 + +### #1 Ollama 회복 (4번) + +**In:** +- HealthChecker 주기 polling (기본 60s — mini-brainstorm 에서 주기/backoff 확정): + - `runOnce()` 가 setInterval 로 자동 발화 + - 회복 시 `onUpdate` fire → 구독 (renderer OllamaBanner) 자동 갱신 + - 실패 N회 후 polling 중단 정책 — mini-brainstorm +- 수동 "재확인" 버튼: `OllamaBanner` + 트레이 컨텍스트 메뉴 +- IPC: `inbox:ollamaRecheck` +- Telemetry emit: `ollama_unreachable` (`{ reason }`), `ollama_recovered` (`{ downtimeMs }`), `ollama_recheck_manual` (`{}`) +- 단위 테스트: polling fire, manual recheck, 회복 status 전이 + telemetry emit + +**Out:** 사용자 설정 가능 polling 주기, 회복 toast 알림, 모델 정상성 (tags 외) 체크 + +### #2 AI retry / 수동 trigger (5번) + +**In:** +- `AiWorker.processJob()` 정책 변경: + - **ollama unreachable** 일 때 `attempts` 증가 안 하고 `next_run_at` 만 backoff (무한 retry while unreachable) + - schema fail / invalid response / timeout 만 `attempts` 증가 (기존 max 3 유지) + - reason 분류는 `LocalOllamaProvider` 결과 + zod 결과로 결정 +- `markAiFailed` 한 노트 수동 re-enqueue 가능 (hard fail 도 회수 경로) +- 트레이 + Inbox 메뉴 **"지금 AI 처리 (실패 N건)"** → 모든 ai_status='failed' → pending_jobs 재투입 +- `FailedBanner` (PendingBanner 형제, 실패 N건 + retry 버튼) +- IPC: `inbox:retryAllFailed`, `inbox:failedCount` +- Telemetry emit: `ai_failed` (#7 의 기본 hook 에 reason 분류 추가), `ai_retry_manual` (`{ failedCount }`) +- 단위 테스트: unreachable infinite retry, retry-all trigger, unreachable vs schema fail 구분, attempts 증가 정책 + +**Out:** per-note retry 버튼 (NoteCard), failed reason 별 차등 정책, retry progress UI, retry rate-limit + +### #3 태그 vocab (6번) + +**In:** +- `NoteRepository.getTopUsedTags(N=20)`: + - `SELECT t.name, COUNT(*) c FROM tags t JOIN note_tags nt ON nt.tag_id=t.id JOIN notes n ON n.id=nt.note_id WHERE n.deleted_at IS NULL GROUP BY t.id ORDER BY c DESC LIMIT 20` +- `buildPrompt()` 에 vocab 주입 라인: + - "기존 태그를 우선 재사용. 새 태그는 vocab 에 없는 의미일 때만 만들기:" + kebab-case 리스트 + - vocab 빈 케이스 (신규 사용자) → 라인 자체 생략 +- `PROMPT_VERSION` 3 → **4** +- AI 응답 후 vocab hit/miss 분류 → telemetry emit +- Telemetry emit: `tag_vocab_hit` (`{ tagId, vocabSize }`), `tag_vocab_miss` (`{ vocabSize }`) +- 단위 테스트: vocab 합성, 빈 vocab, 길이 cap, prompt version bump, hit/miss 분류 + +**Out:** 임베딩 유사도 dedup, 사용자 controlled vocabulary 화이트리스트, 자동 normalize ("회의" ↔ "미팅"), top-N 튜닝, vocab cache invalidation 정책 + +### #6 리마인드 1 spike (7번) + +**In:** +- `strategy.md` §2.3 / §4.3 / §8 갱신: Capitalize 본격 진입, "오늘 회상" surface 정의, F4-A/B/D deferred 항목의 측정 인프라 마련 명시 +- Inbox 상단 **`RecallBanner`** — "오늘 회상해볼 노트" 1건 추천: + - algo: `WHERE (last_recalled_at IS NULL OR last_recalled_at < date('now','-7 day')) AND (recall_dismissed_at IS NULL OR recall_dismissed_at < date('now','-30 day')) AND ai_status='done' AND deleted_at IS NULL AND (due_date IS NULL OR due_date >= today) ORDER BY created_at ASC LIMIT 1` + - 사용자 액션 3개: + - "열어보기" → 노트 카드 스크롤 + `last_recalled_at = now()` + - "다음에" → in-memory snooze 1일 (영속화 X) + - "더 이상" → `recall_dismissed_at = now()` +- IPC: `inbox:listRecallCandidate`, `inbox:markRecallOpened`, `inbox:dismissRecall` +- Telemetry emit: `recall_shown` (`{ noteId, ageDays }`), `recall_opened`, `recall_dismissed`, `recall_snoozed` +- 단위 테스트: algo selection, dismiss 만료 (30일 후 재추천), last_recalled 갱신, deleted_at 제외, 후보 0건 케이스 + +**Out:** 잠금해제 hook (F4-A), 무작위 토스트 (F4-D), ambient if-then (F4-B), 임베딩 유사도 추천 (#3 vocab 후속), spaced repetition (Leitner/SM-2), 다중 후보 추천 + +### 3.1 공통 게이트 (모든 항목) + +각 항목 머지 전 필수: + +- `npm run typecheck` 통과 (현재 0 에러) +- `npm test` 통과 (현재 205/205, 항목 신규 단위 추가) +- `npm run test:e2e` 통과 (현재 1/1) +- 항목 신규 단위 테스트 ≥ 1개 (TDD) +- main 머지 + +--- + +## 4. 항목당 작업 흐름 + Cross-cutting + +``` +[항목 N 시작] + │ + ├─ mini-brainstorm ← decision-pending 답변 + │ - 본 문서 §3 의 "Out" 후보 일부가 In 으로 승격 가능 + │ - per-item spec doc → docs/superpowers/specs/2026-MM-DD-v023-.md + │ + ├─ writing-plans ← TDD 구현 계획 + │ + ├─ 구현 (executing-plans 또는 직접) + │ - 브랜치: feat/v023- (예: feat/v023-trash, feat/v023-recall) + │ - 게이트 통과 후 main 머지 + │ + └─ 다음 항목 시작 +``` + +### 4.1 Cross-cutting 정책 + +| 영역 | 정책 | +|------|------| +| **버전 관리** | 7개 모두 머지될 때까지 `package.json` `0.2.2` 유지. v0.2.3 cut 은 7번 후 단일. | +| **CHANGELOG** | 기존 `CHANGELOG.md` 에 `[0.2.3]` section append (v0.2.2 에서 확립한 패턴). v0.2.3 cut 직전 한 번만 수정. | +| **브랜치 전략** | `feat/v023-` 단명. main 머지 후 삭제. 작은 항목 (#1, #3, #6 strategy 갱신) 은 main 직접 push 도 허용. | +| **테스트 추가 정책** | 항목당 최소 단위 1개. e2e smoke 영향 시 단언 동기화. AiWorker 변경 (#1, #2, #3) 은 integration (Ollama) 영향 시 검토. | +| **Slice invariant 위반 시** | 본 로드맵 결과로 invariant 변경 — slice §1.1 §7 도 PR 안에 동봉 수정. | +| **신규 dependency** | slice §7 strict-pin 그대로. 0 신규 dep 목표 — 위반 시 PR 안에 §7.2 갱신 + 합리화 동봉. | +| **로깅 정책** | slice §1.1 invariant 4 **강화**: telemetry payload 에 raw_text/title/summary/intent/tag name 포함 절대 금지. 위반 시 silent invariant 위반. #7 단위 테스트로 zod parser 가 거부. | +| **Strategy.md 동반 갱신** | #6 항목 (7번) 에서만. 다른 항목은 strategy.md 미수정. | +| **Schema invariant 추가** | `deleted_at IS NOT NULL` 노트는 모든 active 쿼리 (Inbox 리스트 / 카운트 / 검색 / 태그 필터 / AiWorker 처리 / F5 export) 에서 제외. F6-L1 backup 만 예외. 위반 시 dogfood-feedback 재오픈. | + +--- + +## 5. v0.2.3 Cut 단계 + +7번 항목 머지 후: + +``` +[v0.2.2 dogfood 환경에서] +1. 트레이 → "지금 백업" 1회 클릭 ← F6-L1 첫 실증 +2. 트레이 → "내보내기..." 1회 ← F5 schema-agnostic 백업 +3. 트레이 → "사용 로그 내보내기..." 1회 ← #7 의 첫 실증 (없으면 v0.2.2 raw 데이터 손실) +4. Inkling 종료 + +[빌드 머신에서] +5. package.json version: 0.2.2 → 0.2.3 +6. CHANGELOG.md 에 [0.2.3] section append +7. npm run dist +8. dist/Inkling Setup 0.2.3.exe 검증 + +[dogfood 머신에서] +9. Setup 0.2.3.exe 실행 → 같은 폴더에 설치 +10. 첫 실행 → migration v3 자동 적용 (deleted_at + last_recalled_at + recall_dismissed_at) +11. 트레이 메뉴 "사용 로그 내보내기..." 항목 존재 확인 +12. ≥ 1주 soak 시작 +``` + +### 5.1 업그레이드 안전망 + +| 위험 | 완화 | +|------|------| +| migration v3 결함으로 DB 손상 | 2가지 복원 경로 (v0.2.1 부터): (a) `.pre-v3.bak` 자동 snapshot 으로 SQLite 복원 (v0.2.2 인스톨러 재설치 필요), (b) F5 export → v0.2.3 의 F6-L3 import 로 schema-agnostic 복원 (더 빠름) | +| `deleted_at IS NULL` 누락 — 휴지통 노트가 active 쿼리에 새는 회귀 | 단위 테스트로 모든 active 쿼리 확인. 실수 시 dogfood-feedback 즉시 재오픈. | +| Telemetry payload 에 본문 누출 | `TelemetryService.emit` 의 zod parser 가 거부. CI 단위 테스트로 고정. | +| AiWorker unreachable infinite retry 가 큐 폭주 | next_run_at 의 backoff cap (15분) — mini-brainstorm 에서 확정. | +| 자동시작 토글 / 데이터 디렉터리 손실 | v0.2.1 동일 — `HKCU\...\Run` + `` 보존됨 | + +--- + +## 6. 측정 + +### 6.1 로드맵 측정 + +| 메트릭 | 임계값 | 측정 방법 | +|--------|--------|----------| +| 항목 평균 PR 사이즈 | < 800 lines diff | git log 통계 | +| 항목 평균 머지 간격 | < 5일 | git log 시간차 | +| 회귀 테스트 추가 | 항목당 ≥ 1개 단위 | `tests/unit` 카운트 | +| v0.2.3 cut 후 1주 데이터 손실 | 0회 | telemetry + 본인 라벨링 보강 | +| typecheck/test 회귀 | 0회 | CI · 로컬 | +| Telemetry 본문 누출 | 0건 | events.jsonl grep + zod parser | + +### 6.2 dogfood soak 측정 (#7 의 본격 사용처) + +`stats.md` 가 다음을 답해야 함: + +| 질문 | 데이터 | +|------|--------| +| AI 가 실제로 동작 중인가? | `ai_succeeded / (ai_succeeded + ai_failed)` ratio 일자별 | +| Ollama unreachable 빈도? | `ollama_unreachable` count + 평균 `downtimeMs` | +| 수동 trigger 가 쓰이고 있나? | `ai_retry_manual` / `ollama_recheck_manual` count | +| 휴지통이 회수 도구로 동작? | `restore / trash` ratio | +| 만료 추천이 nudging 으로 동작? | `expired_batch_trash / expired_banner_shown` ratio | +| 회상 spike 가 의미 있나? | `recall_opened / recall_shown` ratio + `recall_dismissed` count | +| Tag vocab 재사용? | `tag_vocab_hit / (hit + miss)` ratio (목표: 시간 흐름에 따라 상승) | + +### 6.3 silent invariant 후보 + +본 로드맵 결과로 slice §1.3 종료 조건에 추가 권장: + +> **"Telemetry 본문 누출 0회"** — events.jsonl 의 어떤 payload 에도 raw_text/title/summary/intent/tag name 미포함. 발생 시 즉시 silent invariant 위반. + +> **"`deleted_at IS NULL` 망각 0회"** — active 쿼리 회귀 시 즉시 dogfood-feedback 재오픈. + +이 추가는 #7 / #4 항목 머지 시 slice §1.3 동봉 수정. + +--- + +## 7. 본 로드맵의 종료 조건 + +**모두 만족해야 종결**: + +1. #7·#4·#5·#1·#2·#3·#6 7개 항목 모두 main 머지 +2. `CHANGELOG.md [0.2.3]` section + `package.json` 0.2.3 + slice §1.3 silent invariant 2개 추가 동봉 갱신 완료 +3. v0.2.3 cut → dogfood 머신 재설치 → migration v3 적용 확인 → 첫 실행 정상 + 트레이 메뉴 신규 항목 ("사용 로그 내보내기...") 동작 확인 +4. ≥ 1주 dogfood soak 완료 (데이터 손실 0회 + telemetry 본문 누출 0건 확인) +5. `events.jsonl` + `stats.md` export → 분석 → v0.2.4 brainstorm 진입 + +5 가 끝나면 본 로드맵 종결. + +--- + +## 8. 미결정 항목 (각 항목 mini-brainstorm 에서 답변) + +본 로드맵은 순서·In/Out 만 정의. 다음 결정들은 빨리 마주치게 됨: + +- **#7**: events.jsonl rotation 주기 (자정 KST 확정), stats.md 집계 ratio 의 정확한 컬럼 list, payload schema 별 zod 파서 합성 정책, write 실패 시 백오프 +- **#4**: 휴지통 UI 정밀 위치 (Inbox 탭 vs 트레이 별 윈도우 vs 별 페이지), 휴지통 비우기 confirm 카피, F5 export 의 trash 옵션 (제외 강제 vs 사용자 토글) +- **#5**: 후보 `due_date_edited_by_user` 필터 여부 (수동 입력만 vs AI 자동 추출 포함), 만료 임박 (D-7) 포함 여부, 멀티선택 default 상태 (전체 선택 vs 비선택) +- **#1**: polling 주기 (10/30/60s), 실패 N회 후 polling 중단, exponential backoff 적용 +- **#2**: unreachable backoff cap (15분 후보), reason 분류 정밀도 (timeout vs unreachable 구분), per-note retry 승격 여부 +- **#3**: top-N 값 (20 후보), vocab cache invalidation 정책 (write-through vs 매 prompt 시 fresh), 빈 vocab 임계값 +- **#6**: dismiss 만료 30일 vs 14일 vs 60일, 후보 0건 시 RecallBanner 숨김 vs 빈 상태 카피 +- **#5+#6 coexistence**: 둘 다 Inbox 상단 배너 noting. stack 순서 (만료 위 → 회상 아래 가 자연 — 시간 민감도 우선), 동시 N건 시 우선 표시 정책, 빈 상태 시 영역 collapse 여부. #5 → #6 순서 머지라 #6 mini-brainstorm 에서 #5 와 통합 layout 결정. + +--- + +## 9. 변경 이력 + +| 일자 | 변경 | +|------|------| +| 2026-05-01 | 초안 — v0.2.2 dogfood 7항목 (#7 telemetry 신설 포함) 단일 cut 로드맵, 데이터 안전 우선 (C 채택), schema migration v3 3컬럼 한 묶음 (B 채택), trash↔backup/export B 정책, #6 = 1 spike 흡수 (B 채택). |