v0.2.1 dogfood 첫 실증 피드백. '내일 모레' → 내일로 잘못 잡힘. 규칙 파서 한계 3 (합성/양가 / 범위 / 모호) + 5 후보 방향 (A 화이트 리스트 / B 충돌 감지 / C UI 신호 / D AI 우선 / E 규칙 폐기). 1차 A+B 작은 PR 즉시 시도 가능, 2차 C UI 신호, 후속 D/E 결정. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 KiB
Dogfood 피드백 수집
작성일: 2026-04-25 (open) 저자: 김태현 (dlsrks0734@gmail.com) 문서 성격: 슬라이스 v0.4 dogfood 중 발견된 본인 피드백을 수집·정제하는 living document. 각 항목은 후속 spec 으로 승격될 후보다. 정식 spec 이 분기될 만큼 성숙하면 별도 파일로 추출하고 여기엔 링크만 남긴다.
선행 문서:
docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.mdv0.4 (슬라이스 본문)docs/superpowers/strategy/strategy.md(심리학 전략)docs/superpowers/strategy/dogfood-strategy.md(dogfood 운영안 — 본 문서와 의존 없음)
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 확신도 낮게").
결정 대기
- 시간 표현이 들어간 노트 비율 — 누적 50건 표본 분류 (가설 H1)
- "오늘 PR 리뷰" 같은 노트의 due 가 정말 오늘인가, 단순 맥락어인가? false positive 비율
- due_date 가 있는 노트의 재방문 트리거 — Inbox 정렬 / 별도 뷰 / 알림 중 어디
- NoteCard 의 라벨 슬롯 위치 — 제목 옆, 태그 옆, 별도 줄
- 만료된 노트 처리 — 자동 done ❌, 자동 삭제 ❌, 시각적 표시만? 별도 필터?
- 음력·"월말"·"주말 안에" 등 모호 표현은 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 의 일반적 관행 (클릭 = 동일 태그 노트 필터링) 을 기대했다.
문제 두 갈래:
- 기능 부재: "이 태그를 가진 노트만 보기" 가 슬라이스에 없음. 사용자 멘탈 모델과 어긋남.
- 데이터 손실 마찰: 평범한 클릭 한 번으로 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), 태그 단위 일괄 작업.
결정 대기
- 클릭 = 필터 라는 가정이 dogfood 본인에게도 성립하는가? — 본인 예상 동작 한 번 더 셀프 체크
- AI 태그 vs 사용자 추가 태그 클릭 동작이 같아야 하는가, 달라야 하는가?
- undo 토스트의 dismissal 정책 — 5초 자동 사라짐 vs 다음 작업까지 유지
- 필터링 시 Continuity 배지·Pending 배너의 카운트는 전체 기준인가, 필터된 부분 기준인가?
- 태그 단일 제거 외에 "이 태그가 붙은 모든 노트에서 제거" 같은 일괄 작업도 묶을지 — 별 spec 으로 분리 권장
가설·측정
| # | 가설 | 측정 |
|---|---|---|
| H1 | 슬라이스의 현재 "클릭 = 삭제" 가 본인 dogfood 에서 ≥ 1회 실수 유발 | 일일 로그의 마찰 사건 |
| H2 | 클릭 = 필터로 바꾸면 본인이 태그를 통해 과거 노트를 재방문하는 빈도 ≥ 주 1회 | 사용 로그 |
| H3 | undo 토스트가 있으면 의도된 태그 제거의 인지 부담 감소 | 정성 평가 |
범위
- In: 단일 태그 클릭 동작 변경, 단일 태그 제거 시 undo, Inbox 의 단일 태그 필터
- Out: 다중 태그 필터, 태그 rename / merge, 태그 단위 일괄 작업, 태그 자동완성
영향
- Schema: 없음 (이 패치 자체는 DB 변경 없음. 일괄 작업이 들어오면 별 spec)
- Renderer:
NoteCard.tsx의 칩onClick변경, 칩 우측 ✕ 아이콘 추가, InboxApp.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.jsondescription: "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 취소 · 이미지 붙여넣기 |
핵심: "구출" 의 행동적 의미("정리·작성이 아니라 머릿속에서 꺼내기만") 는 보존하되, 직역체 한 단어로 강제하지 않고 표면별 자연 문맥에 맞춰 동사를 분배.
결정 대기
- Strategy 문서 §1, §3 의 "기억 구출" 선언을 함께 수정할 것인가? — "구출" 은 단순 UI 카피가 아니라 전략 어휘다. 카피만 바꾸면 strategy 와 코드가 어긋난다. 같이 바꾸려면 strategy.md §1·§3·§7 갱신 + "꺼내기" / "한 줄" 같은 새 키워드 정의.
- 단일 동사로 통일할 것인가, 표면별 자연어로 분배할 것인가? — 통일은 브랜딩 자산, 분배는 자연스러움.
- 제품명 표어 ("local-first 기억 구출 도구") 는 어디까지 따라가는가? —
package.json description, GitHub README 첫 문단, 향후 onboarding 문구 모두 영향. - e2e smoke 의 단언 문구 변경 시 카피 freeze 관행을 도입할지 — 카피 변경마다 e2e fix 가 따라오면 마찰 큼. 단언을 더 약하게 (
getByRole('main')등) 잡거나 카피 상수 import 가 대안. - 캡처-보상 카피 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— descriptiontests/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 의 약점은 인식 자체가 안 일어난다는 점이지만, 12주 의식적 반복 후 자동화 가능 (Lally 18254일 범위, 본 사례는 행동이 단순해서 짧은 쪽). 온보딩에서 사용자에게 본인의 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 |
| 작업 컨텍스트 가드 | 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 헤더에 작은 카운터.
결정 대기
- 슬라이스 외부 vs 후속 미니 spec: A·D 는 새 IPC + Windows API + 알림 스케줄링이 필요해 슬라이스 §3 ("Hotkey + Tray + Notification 만") 와 충돌. C·E·F 는 슬라이스 내 적용 가능. 어디까지 슬라이스에서 시도하고 어디서부터 별 spec 으로 분기?
- 무작위 알림 (D) 의 거슬림 임계점: 본인 dogfood 1주 누적 데이터 (캡처 빈도 중간값) 를 보기 전에 결정 미루기. 데이터 없이 도입하면 anti-pattern 될 위험.
- 메커니즘 결합 vs 격리 시험: 6개 동시 적용 시 어느 게 효과 있었는지 분리 불가. dogfood 단계에서 격리 A/B 가능한가? 본인 단일 사용자라 통계 의미 약함 — 정성 라벨링으로 대체.
- strategy.md 와의 관계: 본 항목이 strategy.md §3 ambient cue 절을 신규 §3.6 으로 추가할 만큼 무거운가, 아니면 specs/ 만으로 충분한가? F3 와 동일한 "strategy 동반 갱신 여부" 결정 패턴.
- 외부 의존 (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 호출
- C 트레이 뱃지:
- 문서:
- 본 항목 promoted 시
2026-04-26-cue-strengthening.md(가칭) 으로 추출 - strategy.md 결정 #4 결과에 따라 §3.6 신설 또는 본 spec 만으로 종결
- 본 항목 promoted 시
- 테스트:
- 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_atnote_tags(+tags) — 태그 + source(ai/user)media— note 첨부 이미지 메타 (rel_path가 MediaStore 의 프로필 디렉터리 기준)- (F1 promoted 시)
due_date,due_date_edited_by_user
내보낼 자산은 두 종류:
- 노트 본문 + 메타데이터 — 텍스트
- 첨부 이미지 — 바이너리 파일 (현재는
<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 친화):
---
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 리뷰 마무리하고, 회고 양식은 팀에 공유.
오후 미팅 중에 떠올랐음.

index.jsonl (RAG 인덱싱용 1줄=1노트):
{"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차안 확정: one-file-per-note + frontmatter + index.jsonl 트리플 vs 단일 monolithic .md vs 두 형식 동시 출력? → RAG 우선이면 트리플이 압도적이지만 사용자 선호 확인 필요.
- 미디어 포함 기본값: 항상 동봉 vs 사용자 선택 (체크박스). 슬라이스 §1.1 의 "raw_text 본문에 민감정보 가능" 정책 — 이미지가 스크린샷인 경우 export 가 의도치 않은 노출 통로가 될 수 있음. 기본 동봉 + export 시 다이얼로그에 "이미지 N개 포함됩니다" 명시 가 안전.
- 삭제된 노트 처리: SQLite 에 soft-delete 컬럼이 없음. 현재는 hard delete. export 결과는 현 시점 스냅샷만 — 삭제 이력 없음. 충분한가, 별 issue 인가?
- 필드 정책 — provenance 표현:
title_source: ai|user같은 단일 enum vstitle: { value, source, edited_at }객체. RAG 파이프라인의 frontmatter parser 마다 다름 — 평탄한 enum 이 호환성 좋음. - embedding_text 합성 규칙: title + raw_text + tags 단순 결합 vs raw_text 만 (가장 untouched) vs title + summary + raw_text. 본인 RAG 사용 패턴 미정 — 1차는 raw_text 단독으로 시작 + 옵션화.
- 파일명에서 raw_text vs ai_title 사용: ai_title 사용이 가독성 좋지만 AI 변경 시 파일명도 변하는 안티패턴. ai_title 사용 + 사용자가 수동 export 트리거 시점 기준 으로 동결 (재 export 시 새 파일명). 파일명 안정성 vs 가독성 트레이드오프 명시.
- 트리거 표면: 트레이 메뉴만 vs Inbox 헤더 버튼도 추가 vs CLI 플래그 (
--export <dir>). 자동화 사용자라면 CLI 가 매력. 슬라이스 후속 미니 spec 으로 분리 가능. - export 형식 버전 정책:
inkling_export_version: 1박아두고 후속 변경 시 마이그레이션 가이드 동봉. 처음부터 박는 게 깔끔. - 민감정보 표시 경고: 본 사용자는
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.tssrc/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 추가 불필요)
- 없음 — Node
- 로깅:
- export 시작·완료·노트 수만 기록. raw_text·title·summary 미기록 (slice §1.1 invariant 4 그대로)
- 문서:
- 본 항목 promoted 시
2026-04-26-markdown-export.md(가칭) 으로 추출 - 추출 후 README 의 doc map 갱신
- export 폴더 안의
README.md— RAG 적재 예시 코드 포함
- 본 항목 promoted 시
비고
본 항목은 읽기 전용 이라 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 미반영분 누락 위험 없음).
// 의사코드
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-snapshotmtime 비교) - 명시 트리거: 트레이 메뉴 "지금 백업"
저장 정책 — 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
safeStorageAPI (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개 충돌) → 확인 → 적용
결정 대기
- 3-layer 동시 도입 vs 단계적: L1 → L3 → L2 순서가 비용·위험 단조 증가라 권장. L1 만으로도 SPOF 완화의 80% 커버.
- L2 sync 단위: 매 변경 vs 5분 debounce vs 종료 시 1회 vs 명시 동기화만. 실시간일수록 데이터 손실 윈도우 작지만 git push 빈도 폭발 + 네트워크 마찰. 5분 debounce + 종료 시 즉시 push 가 1차 권장.
- L2 repo 분리: 기존
gitea.altair823.xyz/altair823-org/inkling(소스 코드) 와 분리된 별 repo (예:altair823-org/inkling-data) — 반드시 분리. 데이터·코드 라이프사이클 다름, 외부 협업자에게 데이터 노출 위험. - L2 충돌 시 정책 — slice §1.1 vs 사용자 선택: 자동 "내 디바이스 우선" 가속 vs 매번 묻기. dogfood 단일 디바이스 가정으론 자동 OK, but defensive 차원에서 충돌 발생 시 1회 확인이 안전.
- media 의 git 추적: 포함 vs 제외 vs LFS. 1차는 포함 + 사이즈 < 100MB 경고. 누적 시점에 후속 결정.
- L1 백업 위치:
<profileDir>/backups/(현 프로필 안) vs 별 디렉터리 (%APPDATA%\Inkling\backups\) vs 사용자 지정 외부 경로. 외부 경로 옵션이 OneDrive 등 클라우드 sync 폴더 이용 가능 — 거의 공짜 cloud backup. - import 시 raw_text invariant 보호: slice §1.1 "raw_text 불변" 은 동일 id 내 의미. import 가 같은 id 의 raw_text 를 다른 값으로 덮어쓰면 invariant 위반. 충돌 시 raw_text 다르면 새 id 강제 정책이 안전.
- L2 첫 설정의 UX 부담: token 입력 + remote 검증 + 초기 push 가 dogfood 1일차 첫 인상에 마찰. 첫 설치 후 N 일 (예: 7일) 까지는 L1 만 켜두고 L2 는 트레이 메뉴 "원격 백업 설정" 으로 opt-in 권장.
- 암호화 — local-first 라도 token 외 추가 보호 필요한가: SQLite·미디어·git 모두 평문. 디스크 도난 시 노출. 1차는 평문 (slice §1.1 미적용 영역), 후속에 SQLCipher / age 암호화 검토.
- 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.tssrc/main/services/ImportService.tssrc/main/ipc/backupApi.ts- 테스트
tests/unit/BackupService.spec.ts,ImportService.spec.ts
- 신규 파일 (L2 별 spec):
src/main/services/SyncService.tssrc/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 로 분리
- 본 항목 promoted 시 분리 권장:
비고
본 항목과 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 로 오인 (🔬 drafting)
발견: 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가지:
- 합성/양가 표현: 두 시간 토큰이 인접하면 단일 의미로 해석되는 한국어 관용 (사용자 의도 ≠ 첫 토큰)
- 범위 표현: "2~3일 뒤", "이번 주 안에", "다음 주 중", "월말 즈음" — 명확한 ISO 가 없는 본질
- 모호 표현: "곧", "조만간", "여건 되면" — 사람도 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 의존, 결정론 손실 | 후속 |
권장 조합 (단계적):
- 즉시 (slice MVP +): B + 일부 A — 첫 발견 시 충돌 / 화이트리스트 매치는 medium 으로 강등 (AI 위임). 골든 픽스처에
"내일 모레" → null추가. - 다음 cycle: C — UI 신호 (확신도 표시 + 빠른 편집)
- 누적 데이터 후: D 또는 E — AI 우선/단독 흐름 반전 결정. dogfood 1주 데이터 (수용률 / 편집률 / 충돌 발생률) 가 의사결정 기반.
결정 대기
- 합성 패턴 화이트리스트 (A) 의 크기: 2 토큰 (
내일 모레,오늘 내일,이번 주 다음 주,오늘 내일 모레같은 3-token) 까지 — 어디까지가 합리적 범위인가? - 충돌 감지 (B) 의 임계값: "high-confidence 토큰이 동일 텍스트에 N개 이상 등장 시 medium 강등" — N = 2 OK? 또는 토큰 간 거리 (한 줄 안 vs 떨어진 문장) 로 다르게?
- fixture 유지: 기존
'내일도 모레도 바쁨' → 내일테스트는 현재 동작을 lock-in 하는 케이스. B 도입 시 이걸 → null 로 바꿔야 일관됨. 의도된 변경? - C 의 UI 위치: 확신도
?를 어디에 표시?📅 YYYY-MM-DD ?(라벨 옆) vs hover tooltip vs 별 색 (회색 = 확신 낮음). - D vs E: 결정론적 fallback 가치 vs AI 단순화. dogfood 누적 데이터 본 뒤 결정.
- 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의ParseResultinterface 확장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) 의 §"후속" 에 본 항목 링크 추가
- 본 항목 promoted 시
- 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. 짧은 제목 (🌱 raw) 헤더로 시작. 표준 슬롯 6개 채우거나 비워둔 채 시작 가능.