Update vertical slice spec to v0.2 with Strategy integration
Integrates psychology-based habit formation strategy from docs/superpowers/strategy/strategy.md into the slice scope: - §0.1 5 product principles (memo existence > completeness, capture is user's only responsibility, AI re-evokes thought, streak for recovery, personal notes as team knowledge seed). - §1.1 Adds in-scope: post-submit native notification toast (4 rotating copies), AI proposal labels with edited-by-user flags, one-line intent banner after AI done (4 rotating prompts), Weekly Continuity streak (7 notes/week target), recovery-friendly copy policy. - §1.2 Defers if-then onboarding, situational templates, 66-day program, psychology KPIs, A/B infra, Confluence candidate recommendation, triggered capture, streak freeze. - §2.2 Adds NotificationService and IntentService modules. - §2.3 IPC adds setIntent/dismissIntent/getContinuity, drops getStreak in favor of getContinuity. - §3.1 Schema adds user_intent, intent_prompted_at, title_edited_by_user, summary_edited_by_user columns. - §3.3 Invariants added for new columns. - §5.1 QuickCapture copy + post-submit native notification flow. - §5.2 Inbox redesigned: ContinuityBadge, recovery toast, IntentBanner, AI proposal labels per field. - §5.4 Full copy catalog with recovery-friendly tone. - §6.1 Adds failure modes for OS notification denial and concurrent intent updates. - §8 Lists Strategy items deferred to future specs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,20 +1,34 @@
|
||||
# Inkling — Vertical Slice v0.1 설계 문서
|
||||
# Inkling — Vertical Slice v0.2 설계 문서
|
||||
|
||||
**작성일:** 2026-04-24
|
||||
**작성일:** 2026-04-24 (v0.1) · **개정:** 2026-04-25 (v0.2, Strategy 통합)
|
||||
**저자:** 김태현 (dlsrks0734@gmail.com)
|
||||
**대상 기획서:** `inkling.md` v1.4
|
||||
**참조 문서:** `docs/superpowers/strategy/strategy.md` (심리학 기반 습관화 전략)
|
||||
**문서 성격:** Phase 1 MVP의 첫 서브프로젝트 설계 (vertical slice)
|
||||
|
||||
---
|
||||
|
||||
## 0. 개요
|
||||
|
||||
이 문서는 `inkling.md` 기획서의 Phase 1 MVP(§8.1) 중 **종단 end-to-end 경로** 한 줄만 최소 구현하기 위한 설계다. 목적은 두 가지 핵심 가설을 가장 얇은 경로로 검증하는 것이다.
|
||||
이 문서는 `inkling.md` 기획서의 Phase 1 MVP(§8.1) 중 **종단 end-to-end 경로** 한 줄만 최소 구현하기 위한 설계다. 목적은 세 가지 핵심 가설을 가장 얇은 경로로 검증하는 것이다.
|
||||
|
||||
1. "전역 단축키 → 입력 → 저장"이 실제로 3초 내 마찰 없이 이루어지는가. (§3.2-3)
|
||||
2. 로컬 Ollama(Gemma 4 9B)가 제목·요약·태그를 실용 가능한 품질로 생성하는가. (§4.2, §6.5)
|
||||
1. "전역 단축키 → 입력 → 저장"이 실제로 3초 내 마찰 없이 이루어지는가. (기획서 §3.2-3)
|
||||
2. 로컬 Ollama(Gemma 4 9B)가 제목·요약·태그를 실용 가능한 품질로 생성하는가. (기획서 §4.2, §6.5)
|
||||
3. Strategy 문서의 핵심 행동 루프(Capture → Clarify → Capitalize)와 회복 친화 스트릭이 실제 dogfood에서 "메모를 던지고 싶은 충동"을 만드는가.
|
||||
|
||||
Slice는 dogfood만을 목적으로 하며, 패키징·서명·배포·알림·검색·내보내기는 전부 후속 spec의 범위다.
|
||||
Slice는 dogfood만을 목적으로 하며, 패키징·서명·배포·캘린더 트리거·검색·내보내기는 전부 후속 spec의 범위다.
|
||||
|
||||
### 0.1 제품 원칙 (Strategy §12)
|
||||
|
||||
이 원칙들은 모든 UX 결정·카피·기능 우선순위의 상위 기준이다. 충돌 시 원칙이 이긴다.
|
||||
|
||||
1. **메모의 기준은 완성도가 아니라 존재 여부다.**
|
||||
2. **사용자의 첫 책임은 캡처, 정리는 시스템의 책임이다.**
|
||||
3. **AI는 사고를 대체하지 않고 사고를 다시 꺼내게 한다.**
|
||||
4. **스트릭은 죄책감이 아니라 회복을 위한 장치다.**
|
||||
5. **개인 메모는 팀 지식의 씨앗이다.**
|
||||
|
||||
원칙 1·2·3은 본 slice에 직접 반영된다. 원칙 4는 Weekly Continuity와 회복 친화 카피로 반영. 원칙 5는 Confluence 내보내기 spec(후속)에서 본격 다룸.
|
||||
|
||||
---
|
||||
|
||||
@@ -27,13 +41,18 @@ Slice는 dogfood만을 목적으로 하며, 패키징·서명·배포·알림·
|
||||
- **저장:** 로컬 SQLite 단일 프로필(`default`). 미디어는 프로필 디렉터리 `media/` 하위 파일로 저장.
|
||||
- **AI 파이프라인:** 비동기 처리. `LocalOllamaProvider` 1개 구현체. `gemma4:9b` 표준 티어 고정. 출력은 제목 + 3줄 요약 + 태그 최대 3개.
|
||||
- **Inbox:** 날짜 내림차순 리스트. 카드에 제목·요약·태그·원문·미디어 썸네일. AI 필드 인라인 편집 및 노트 삭제 지원. 원문은 읽기 전용.
|
||||
- **스트릭:** 연속 기록 일수를 Inbox 상단 배지에 표시. SQL 집계 기반.
|
||||
- **즉시 보상 토스트 (Strategy §4.1):** Quick Capture 저장 직후 OS 네이티브 알림으로 회전 카피 1개 표시 ("이 생각은 이제 Inkling이 들고 있습니다." 등 4종).
|
||||
- **AI 제안형 라벨 (Strategy §7-원칙4):** AI가 생성한 제목·태그 옆에 작은 "AI" 회색 라벨. 사용자가 편집한 필드에서는 라벨이 사라져 "내 메모" 정체성 식별 가능.
|
||||
- **"의미 한 줄" 배너 (Strategy §2.2, §7-원칙2):** AI가 `done` 전환 시 카드 상단에 1회 노출. 회전 4종 중 1개 ("내일의 내가 알아야 할 것은?" 등). 입력 시 카드에 💡 배지로 영구 노출, 건너뛰기 시 해당 카드에서는 다시 안 띄움. `notes.user_intent`·`notes.intent_prompted_at` 컬럼 사용.
|
||||
- **Weekly Continuity 스트릭 (Strategy §5):** Daily streak 폐기. KST 월~일 주 단위, 목표 7건/주(요일 무관, 같은 날 누적 OK). Inbox 상단 배지: 미달성 "이번 주 N/7", 달성 "이번 주 7/7 ✓ · 연속 K주 완성". 마지막 기록 후 7일 이상 갭 후 첫 기록 시 24시간 동안 "🌱 흐름을 다시 이어갑니다" 토스트.
|
||||
- **회복 친화 카피 정책 (Strategy §5, §12-원칙4):** "실패"·"끊김"·"연속 실패" 단어 금지. 0건 상태도 "이번 주 한 줄이면 시작입니다". 모든 사용자 대면 문구는 §5의 카피 카탈로그 따름.
|
||||
|
||||
### 1.2 Out-of-scope (후속 spec에서 다룸)
|
||||
|
||||
기존 항목:
|
||||
- 음성 입력 / STT
|
||||
- 관련 메모 링크 / 프로젝트 자동 분류 / 벡터 검색
|
||||
- 알림·푸시·스케줄러·캘린더 어댑터·모닝/이브닝 프롬프트
|
||||
- 푸시·스케줄러·캘린더 어댑터·모닝/이브닝 프롬프트
|
||||
- 다중 프로필 / 비밀번호 잠금 / 마스킹 레이어
|
||||
- LAN Ollama Provider / 외부 API Provider / BYOK 키 관리
|
||||
- Confluence 내보내기 / 프로필 zip 내보내기·재가져오기
|
||||
@@ -41,6 +60,15 @@ Slice는 dogfood만을 목적으로 하며, 패키징·서명·배포·알림·
|
||||
- 코드 서명·공증·자동 업데이트·배포 파이프라인
|
||||
- Android 보조 앱
|
||||
|
||||
Strategy 문서에서 추가로 명시 이관 (Strategy 절 번호와 함께):
|
||||
- **실행 의도(if-then) 온보딩** (§3) — 별도 온보딩 위자드 spec
|
||||
- **상황별 최소 템플릿** (회의/트러블슈팅/요청/의사결정/인수인계, §6) — QuickCapture v2 spec
|
||||
- **66일 단계별 습관 프로그램** (§9) — 알림 빈도·프롬프트 변화 spec
|
||||
- **심리학 기반 KPI 측정 + 월 1회 정성 설문** (§10) — 분석 인프라 spec
|
||||
- **A/B 테스트 인프라** (§11) — 실험 플랫폼 spec
|
||||
- **Confluence 공유 후보 추천 + 실패→주의 카피 변환** (§8) — Confluence export spec
|
||||
- **Triggered Capture** (회의 종료·퇴근 직전, §3) — 캘린더 어댑터 spec
|
||||
|
||||
### 1.3 종료 조건
|
||||
|
||||
- Windows에서 저자 본인이 하루 5건 이상 실제 dogfood를 2주 연속 수행.
|
||||
@@ -87,12 +115,14 @@ Slice는 dogfood만을 목적으로 하며, 패키징·서명·배포·알림·
|
||||
### 2.2 모듈 책임과 경계
|
||||
|
||||
- **HotkeyService** — `Ctrl/Cmd+Shift+J` 등록, 중복·충돌 감지, 이벤트를 `QuickCaptureWindow.show()`로 연결. OS별 분기는 이 모듈 안에 격리한다.
|
||||
- **CaptureService** — QuickCapture에서 들어온 페이로드(`{text, images[]}`)를 받아 단일 트랜잭션으로 `NoteRepository.create` + `MediaStore.save`를 실행하고, 이어서 `AiWorker.enqueue(noteId)`를 호출한다. 저장 성공 시점에 창 닫힘 응답을 반환한다.
|
||||
- **NoteRepository** — `notes`·`note_tags`·`tags` 테이블 접근을 전담. SQL 문자열이 이 모듈 밖으로 새지 않는다. `better-sqlite3`(동기 드라이버, 단일 프로세스에 적합)를 사용한다.
|
||||
- **CaptureService** — QuickCapture에서 들어온 페이로드(`{text, images[]}`)를 받아 단일 트랜잭션으로 `NoteRepository.create` + `MediaStore.save`를 실행하고, 이어서 `AiWorker.enqueue(noteId)`를 호출한 뒤 `NotificationService.celebrate(noteId)`를 호출한다. 저장 성공 시점에 창 닫힘 응답을 반환한다.
|
||||
- **NoteRepository** — `notes`·`note_tags`·`tags` 테이블 접근을 전담. `user_intent`, `intent_prompted_at`, `title_edited_by_user`, `summary_edited_by_user` 컬럼 관리도 여기 포함. SQL 문자열이 이 모듈 밖으로 새지 않는다. `better-sqlite3`(동기 드라이버, 단일 프로세스에 적합)를 사용한다.
|
||||
- **MediaStore** — 클립보드 PNG 바이트를 `{profileDir}/media/{noteId}/{uuid}.png` 형태로 파일 저장하고, 상대 경로만 DB에 기록한다.
|
||||
- **AiWorker** — in-memory FIFO 큐 + SQLite `pending_jobs` 테이블(앱 재시작 시 재적재용). 동시 처리 1개. Ollama 호출 실패/타임아웃을 지수 백오프로 3회 재시도 후 노트를 `failed`로 전이. `pending` 상태의 작업만 재시작 시 자동 재적재되며, 이미 `failed`로 전이된 노트는 slice 범위에서는 수동·자동 어느 쪽으로도 다시 처리되지 않는다.
|
||||
- **InferenceProvider 인터페이스** — §4 참조. 본 slice의 유일한 구현체는 `LocalOllamaProvider`.
|
||||
- **StreakService** — `SELECT DISTINCT DATE(created_at, 'localtime') FROM notes` 결과로 연속 일수를 계산한다. 순수 읽기 작업.
|
||||
- **StreakService** — Weekly Continuity 계산 전담. `notes.created_at`을 KST 기준으로 그룹화해 이번 주 건수, 연속 완성 주(consecutive complete weeks), 회복 토스트 노출 여부(`showRecoveryToast`)를 산출. 순수 읽기 작업.
|
||||
- **NotificationService** — OS 네이티브 알림(`new Notification(...)`) 발사 전담. 회전 카피 4종 중 1개 선택 (noteId 해시 mod 4). 권한 거부 시 무음 폴백. Strategy §4.1 즉시 보상 책임.
|
||||
- **IntentService** — `setIntent(noteId, text)` / `dismissIntent(noteId)` 두 메서드만. `notes.user_intent`와 `notes.intent_prompted_at` 갱신 책임을 격리. UI(IntentBanner)에서 IPC를 통해 호출. 회전 프롬프트 4종 중 noteId 해시 mod 4로 결정론적 선택.
|
||||
|
||||
### 2.3 IPC 계약 (typed preload)
|
||||
|
||||
@@ -108,13 +138,27 @@ inboxApi: {
|
||||
fields: {title?: string; summary?: string; tags?: string[]}
|
||||
): Promise<void>;
|
||||
deleteNote(noteId: string): Promise<void>;
|
||||
getStreak(): Promise<{current: number; longest: number}>;
|
||||
getContinuity(): Promise<WeeklyContinuity>;
|
||||
getPendingCount(): Promise<number>;
|
||||
getOllamaStatus(): Promise<{ok: boolean; reason?: string}>;
|
||||
setIntent(noteId: string, text: string): Promise<void>;
|
||||
dismissIntent(noteId: string): Promise<void>;
|
||||
onNoteUpdated(cb: (note: Note) => void): () => void;
|
||||
}
|
||||
|
||||
// Continuity / intent types
|
||||
interface WeeklyContinuity {
|
||||
weekStart: string; // ISO date, KST 월요일
|
||||
weekCount: number; // 이번 주 메모 건수
|
||||
weekTarget: number; // 7
|
||||
consecutiveCompleteWeeks: number; // 7건 채운 주 연속 횟수
|
||||
showRecoveryToast: boolean; // 갭≥7일 후 첫 기록인지
|
||||
}
|
||||
```
|
||||
|
||||
`onNoteUpdated`는 AiWorker가 노트 상태를 전이할 때마다 푸시한다. 낙관적 업데이트는 하지 않으며, pending 카드는 "처리 중…" 상태로 두었다가 완료 이벤트 수신 시 부분 리렌더로 교체한다.
|
||||
`onNoteUpdated`는 AiWorker가 노트 상태를 전이할 때마다 푸시한다. 낙관적 업데이트는 하지 않으며, pending 카드는 "Inkling이 정리하는 중…" 상태로 두었다가 완료 이벤트 수신 시 부분 리렌더로 교체한다. `done` 전환된 카드 중 `intent_prompted_at IS NULL`인 것은 IntentBanner를 1회 노출하고, 사용자가 setIntent 또는 dismissIntent 호출 시 `intent_prompted_at`이 기록되어 다시 노출되지 않는다.
|
||||
|
||||
IntentBanner의 회전 4종 프롬프트는 renderer 측에 상수 배열로 보유하고 noteId 해시 mod 4로 결정론적으로 선택한다. IPC 라운드트립이 필요 없는 순수 클라이언트 계산이므로 contract에 노출하지 않는다.
|
||||
|
||||
### 2.4 데이터 디렉터리
|
||||
|
||||
@@ -132,17 +176,23 @@ Electron의 `app.getPath('userData')`를 기반으로 경로를 구성한다.
|
||||
```sql
|
||||
-- 메모 본문
|
||||
CREATE TABLE notes (
|
||||
id TEXT PRIMARY KEY, -- UUID v7
|
||||
raw_text TEXT NOT NULL, -- 원본. 불변 (기획서 §4.2)
|
||||
ai_title TEXT, -- AI 생성, 사용자 편집 가능
|
||||
ai_summary TEXT, -- AI 생성, 사용자 편집 가능
|
||||
ai_status TEXT NOT NULL
|
||||
CHECK (ai_status IN ('pending','done','failed')),
|
||||
ai_error TEXT, -- 실패 사유 (디버그용)
|
||||
ai_provider TEXT, -- 'local-ollama/gemma4:9b'
|
||||
ai_generated_at TEXT, -- ISO8601
|
||||
created_at TEXT NOT NULL, -- ISO8601 UTC
|
||||
updated_at TEXT NOT NULL
|
||||
id TEXT PRIMARY KEY, -- UUID v7
|
||||
raw_text TEXT NOT NULL, -- 원본. 불변 (기획서 §4.2)
|
||||
ai_title TEXT, -- AI 생성, 사용자 편집 가능
|
||||
ai_summary TEXT, -- AI 생성, 사용자 편집 가능
|
||||
ai_status TEXT NOT NULL
|
||||
CHECK (ai_status IN ('pending','done','failed')),
|
||||
ai_error TEXT, -- 실패 사유 (디버그용)
|
||||
ai_provider TEXT, -- 'local-ollama/gemma4:9b'
|
||||
ai_generated_at TEXT, -- ISO8601
|
||||
title_edited_by_user INTEGER NOT NULL DEFAULT 0 -- 0/1, AI 라벨 표시 토글
|
||||
CHECK (title_edited_by_user IN (0,1)),
|
||||
summary_edited_by_user INTEGER NOT NULL DEFAULT 0
|
||||
CHECK (summary_edited_by_user IN (0,1)),
|
||||
user_intent TEXT, -- "의미 한 줄" (Strategy §2.2)
|
||||
intent_prompted_at TEXT, -- 배너 노출/처리된 시점 (NULL=아직 미노출)
|
||||
created_at TEXT NOT NULL, -- ISO8601 UTC
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_notes_created_at ON notes(created_at DESC);
|
||||
CREATE INDEX idx_notes_ai_status ON notes(ai_status);
|
||||
@@ -204,6 +254,8 @@ CREATE TABLE schema_migrations (
|
||||
- `ai_status='pending'` 행은 반드시 `pending_jobs`에 대응 엔트리가 존재한다. `CaptureService.create`는 두 insert를 동일 트랜잭션 안에서 수행한다.
|
||||
- 노트 삭제는 DB 트랜잭션 수행 후 미디어 디렉터리 제거 순서로 진행한다. 미디어 삭제 실패로 생긴 고아 파일은 기동 시 GC가 청소한다.
|
||||
- 태그는 `source='ai'`와 `source='user'`를 구분해 저장한다. 사용자가 AI 태그를 제거해도 `tags` 테이블에서 지우지 않고 `note_tags` 행만 삭제한다(태그 이름 재사용 가능).
|
||||
- `title_edited_by_user`/`summary_edited_by_user`는 사용자가 해당 필드를 한 번이라도 편집하면 `1`로 전이되며, 이후 절대 `0`으로 되돌아가지 않는다. AI가 같은 필드를 재생성하지 않으므로 (slice에는 재처리 기능 없음) 안전하다.
|
||||
- `intent_prompted_at`은 `setIntent` 또는 `dismissIntent` 호출 시 1회 기록되며, 이후 갱신되지 않는다. NULL 여부가 IntentBanner 노출 게이트.
|
||||
|
||||
### 3.4 마이그레이션
|
||||
|
||||
@@ -212,7 +264,7 @@ CREATE TABLE schema_migrations (
|
||||
### 3.5 쿼리 핫스팟
|
||||
|
||||
- Inbox 리스트: `SELECT ... FROM notes ORDER BY created_at DESC LIMIT 50 OFFSET ?` + 태그·미디어를 별도 fetch(N+1 허용, 페이지당 50행 기준 실용 문제 없음).
|
||||
- 스트릭: `SELECT DISTINCT DATE(created_at, 'localtime') FROM notes`를 로드해 애플리케이션 레이어에서 연속 일수를 계산한다.
|
||||
- Weekly Continuity: `SELECT created_at FROM notes ORDER BY created_at ASC` 결과를 KST 월~일 주 단위로 그룹화. 이번 주 카운트와 연속 완성 주(consecutive complete weeks) 계산. 회복 토스트는 `MAX(created_at)` 한 행 추가 조회.
|
||||
|
||||
---
|
||||
|
||||
@@ -320,7 +372,8 @@ Rules:
|
||||
- frameless, always-on-top, 640x280
|
||||
- 화면 중앙, 포커스 자동 획득
|
||||
- 단일 textarea + 썸네일 스트립 (이미지 있을 때만)
|
||||
- 하단 힌트: "Ctrl+Enter 저장 · Esc 취소 · 이미지 붙여넣기 가능"
|
||||
- placeholder: "지금 머릿속에 있는 것 한 줄. 정리는 나중입니다."
|
||||
- 하단 힌트: "Ctrl+Enter 구출 · Esc 취소 · 이미지 붙여넣기"
|
||||
│
|
||||
├─ 사용자 타이핑 / Ctrl+V (이미지)
|
||||
│ └─ 클립보드 이미지 감지 → 썸네일 추가 (메모리 버퍼)
|
||||
@@ -329,58 +382,117 @@ Rules:
|
||||
│ - CaptureService.submit({text, images})
|
||||
│ - 트랜잭션: notes insert + media 저장 + pending_jobs insert
|
||||
│ - 성공 시 창 즉시 닫힘 (AI 완료 대기 없음)
|
||||
│ - 닫힘 직후 NotificationService.celebrate(noteId) 발사 (OS 네이티브 알림)
|
||||
│ - 실패 시 창 내 빨간 토스트, 내용 유지
|
||||
│
|
||||
└─ Esc → 취소 (5자 이상이면 "정말 버릴까요?" 1-step 확인)
|
||||
└─ Esc → 취소 (5자 이상이면 "이 한 줄을 흘려보낼까요?" 1-step 확인)
|
||||
```
|
||||
|
||||
**타이밍 목표**
|
||||
|
||||
- 단축키 → 창 표시: p95 < 100ms (숨겨진 창을 `show/hide` 토글, 매번 생성하지 않음).
|
||||
- Submit → 창 닫힘: p95 < 300ms (DB 트랜잭션만 동기 대기, AI는 비동기).
|
||||
- Submit → 창 닫힘: p95 < 300ms (DB 트랜잭션만 동기 대기, AI·알림은 비동기).
|
||||
|
||||
**저장 직후 즉시 보상 토스트 (Strategy §4.1)**
|
||||
|
||||
- 발사 시점: `CaptureService.submit` 트랜잭션 성공 직후.
|
||||
- 채널: OS 네이티브 알림 (`new Notification(title, {body, silent})`). Inbox in-app 토스트 아님 — 사용자가 작업 중인 다른 앱 위에 즉시 떠야 보상 즉시성을 살림.
|
||||
- 회전 카피 4종 (noteId 해시 mod 4로 결정론적 선택):
|
||||
1. "이 생각은 이제 Inkling이 들고 있습니다."
|
||||
2. "나중에 찾을 수 있게 보관했습니다."
|
||||
3. "방금 하나의 업무 기억을 구출했습니다."
|
||||
4. "기록 완료. 이제 잊어도 됩니다."
|
||||
- 권한: 첫 submit 시 OS 권한 다이얼로그. 거부 시 무음 폴백 (캡처는 정상 동작).
|
||||
- 알림 본문에는 `raw_text`나 `ai_title`을 포함하지 않는다. 오직 회전 카피만 표시 (PII 보호와 일관성).
|
||||
|
||||
### 5.2 Inbox
|
||||
|
||||
```
|
||||
[InboxWindow] — 트레이 아이콘 클릭으로 열림, 상시 실행
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Inkling 🔥 연속 4일 │ ← StreakBadge
|
||||
├─────────────────────────────────────────┤
|
||||
│ [🟡 Ollama 처리 중: 3건] │ ← pending count (0이면 숨김)
|
||||
│ [Ollama 상태 경고가 있으면 여기 배너] │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ┌ 2026-04-24 18:32 ─────────────────┐ │
|
||||
│ │ [제목 AI] 회의 A프로젝트 이슈 정리 │ │ ← 인라인 편집 가능
|
||||
│ │ [요약] 3줄... │ │
|
||||
│ │ [태그] api-timeout meeting │ │ ← 클릭 제거
|
||||
│ │ [썸네일] 🖼 🖼 │ │
|
||||
│ │ ▸ 원문 보기 (토글, 기본 접힘) │ │
|
||||
│ │ [🗑 삭제] │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ ┌ 2026-04-24 17:10 ─────────────────┐ │
|
||||
│ │ [제목] 처리 중… │ │ ← ai_status='pending'
|
||||
│ │ (원문 자동 펼침) │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ [더 보기] (50개씩 페이지네이션) │
|
||||
└─────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Inkling 이번 주 4/7 · 연속 2주 완성 │ ← ContinuityBadge
|
||||
├──────────────────────────────────────────────────┤
|
||||
│ [🌱 흐름을 다시 이어갑니다] │ ← 회복 토스트 (24h, 갭≥7일 후만)
|
||||
│ [🟡 Inkling이 정리하는 중: 3건] │ ← pending count (0이면 숨김)
|
||||
│ [Ollama 상태 경고가 있으면 여기 배너] │
|
||||
├──────────────────────────────────────────────────┤
|
||||
│ ┌ 2026-04-24 18:32 ────────────────────────────┐ │
|
||||
│ │ 회의 A프로젝트 이슈 정리 [AI] │ │ ← 제목 + AI 라벨 (편집 안 됨)
|
||||
│ │ 첫 줄 요약 │ │
|
||||
│ │ 둘째 줄 요약 │ │
|
||||
│ │ 셋째 줄 요약 [AI] │ │
|
||||
│ │ [api-timeout AI] [meeting AI] │ │ ← 태그 + AI 표시
|
||||
│ │ 💡 다시 만나면 trace 먼저 확인 │ │ ← user_intent (입력된 경우)
|
||||
│ │ [썸네일] 🖼 🖼 │ │
|
||||
│ │ ▸ 원문 보기 (토글, 기본 접힘) │ │
|
||||
│ │ [🗑 삭제] │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ ┌ 2026-04-24 18:10 ────────────────────────────┐ │
|
||||
│ │ ┌ IntentBanner (1회 노출) ─────────────────┐ │ │ ← intent_prompted_at IS NULL
|
||||
│ │ │ 💭 내일의 내가 알아야 할 것 한 줄? │ │ │
|
||||
│ │ │ [한 줄 입력...] [건너뛰기] │ │ │
|
||||
│ │ └──────────────────────────────────────────┘ │ │
|
||||
│ │ 트러블슈팅 timeout │ │
|
||||
│ │ ... │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ ┌ 2026-04-24 17:10 ────────────────────────────┐ │
|
||||
│ │ Inkling이 정리하는 중… │ │ ← ai_status='pending'
|
||||
│ │ (원문 자동 펼침) │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
├──────────────────────────────────────────────────┤
|
||||
│ [더 보기] (50개씩 페이지네이션) │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**상태별 카드 렌더링**
|
||||
|
||||
- `pending` — 제목 자리에 스피너와 "처리 중…". 요약·태그 숨김, 원문 자동 펼침.
|
||||
- `done` — 제목·요약·태그 전부 표시, 원문 접힘.
|
||||
- `failed` — 제목 자리에 "AI 처리 실패" + 사유 hover tooltip. 원문 자동 펼침. 재시도 버튼은 slice 외.
|
||||
- `pending` — 제목 자리에 "Inkling이 정리하는 중…" 스피너. 요약·태그 숨김, 원문 자동 펼침. IntentBanner 미노출.
|
||||
- `done` — 제목·요약·태그 표시, 원문 접힘. AI 라벨은 사용자가 편집하지 않은 필드에만 노출. `intent_prompted_at IS NULL`이면 카드 상단에 IntentBanner 1회 노출.
|
||||
- `failed` — 제목 자리에 "정리 보류 — 원문은 안전합니다" + 사유 hover tooltip. 원문 자동 펼침. 재시도 버튼은 slice 외. IntentBanner 미노출.
|
||||
|
||||
**AI 제안형 라벨 (Strategy §7-원칙4)**
|
||||
|
||||
- 제목 옆 작은 회색 "AI" 배지: `title_edited_by_user=0`일 때만 표시.
|
||||
- 요약 카드 우측 하단 "AI" 배지: `summary_edited_by_user=0`일 때만 표시.
|
||||
- 태그 옆 "AI" 첨자: `note_tags.source='ai'`인 태그만. 사용자 편집/추가 태그는 라벨 없음.
|
||||
- 라벨은 클릭 불가 (장식). hover tooltip: "AI 제안 — 클릭하여 편집".
|
||||
|
||||
**IntentBanner (Strategy §2.2)**
|
||||
|
||||
- 노출 조건: `note.aiStatus === 'done' && note.intentPromptedAt === null`.
|
||||
- 프롬프트 회전 4종 (noteId 해시 mod 4로 결정론적):
|
||||
1. "내일의 내가 이 메모에서 꼭 알아야 할 것은?"
|
||||
2. "이 메모가 중요한 이유를 한 줄로?"
|
||||
3. "이 문제를 다시 만나면 무엇을 먼저 확인할까요?"
|
||||
4. "동료에게 공유한다면 제목을 뭐라고?"
|
||||
- 입력 → `setIntent(noteId, text)` IPC. 성공 시 배너 사라지고 카드에 "💡 {intent}" 영구 배지로 노출.
|
||||
- 건너뛰기 → `dismissIntent(noteId)`. 배너 사라지고 다시 안 보임.
|
||||
- 두 경로 모두 `intent_prompted_at`을 현재 시각으로 기록.
|
||||
- 입력 시 한 줄 ≤ 200자 강제 (오버는 잘림).
|
||||
- 💡 배지 클릭 시 인라인 편집 가능 (EditableField 재사용).
|
||||
|
||||
**편집 동작**
|
||||
|
||||
- 제목·요약 필드는 클릭 시 `contentEditable`로 전환, blur 시점에 저장(`updateAiFields` IPC).
|
||||
- 제목·요약 필드는 클릭 시 `contentEditable`로 전환, blur 시점에 저장(`updateAiFields` IPC). 저장 시 해당 필드의 `*_edited_by_user`가 1로 전이.
|
||||
- 태그 클릭 시 제거(source 무관). 태그 추가 UI는 slice 외.
|
||||
- 저장 실패 시 이전 값 복구 + 빨간 테두리 1회 플래시.
|
||||
|
||||
**Continuity 배지 (Strategy §5)**
|
||||
|
||||
- 0건: "이번 주 한 줄이면 시작입니다"
|
||||
- 1~6건: "이번 주 N/7"
|
||||
- 7건 이상: "이번 주 7/7 ✓ · 연속 K주 완성" (K=consecutiveCompleteWeeks)
|
||||
- 모든 카피에서 "실패", "끊김", "연속 실패" 단어 금지 (회복 친화 카피 정책).
|
||||
|
||||
**회복 토스트**
|
||||
|
||||
- 조건: 마지막 메모로부터 ≥7일 갭 후 첫 기록 시.
|
||||
- 노출: Inbox 상단 in-app 배너 (Continuity 배지와 별도). "🌱 흐름을 다시 이어갑니다".
|
||||
- 표시 기간: 24시간 또는 사용자가 닫기까지. 닫기 후 같은 회복 사건으로 재노출 안 됨 (`localStorage`에 dismiss 시점 저장).
|
||||
|
||||
**삭제**
|
||||
|
||||
- 🗑 클릭 → 네이티브 confirm 다이얼로그 1회. 확인 시 `deleteNote` 호출 후 카드 제거 및 미디어 파일 삭제.
|
||||
- 🗑 클릭 → 네이티브 confirm 다이얼로그 ("이 기억을 버릴까요? 되돌릴 수 없습니다."). 확인 시 `deleteNote` 호출 후 카드 제거 및 미디어 파일 삭제.
|
||||
|
||||
**실시간 업데이트**
|
||||
|
||||
@@ -391,13 +503,41 @@ Rules:
|
||||
Slice는 온보딩 위자드를 제공하지 않는다.
|
||||
|
||||
1. 최초 기동 시 `{userData}/Inkling/profiles/default/`가 자동 생성된다.
|
||||
2. Inbox 창이 열리고 빈 상태 메시지("`Ctrl+Shift+J`로 첫 메모를 남겨보세요.")가 노출된다.
|
||||
2. Inbox 창이 열리고 빈 상태 메시지("첫 기억을 구출해보세요. `Ctrl+Shift+J`")가 노출된다.
|
||||
3. Ollama 헬스 체크가 실행되고 결과에 따라 배너가 표시된다.
|
||||
|
||||
### 5.4 앱 종료·재시작
|
||||
### 5.4 카피 카탈로그
|
||||
|
||||
- 창 닫기(X)는 트레이로 숨기는 동작이며 앱은 계속 실행된다. 실제 종료는 트레이 메뉴의 "Quit" 항목으로 수행한다.
|
||||
본 slice의 모든 사용자 대면 문구는 다음 카탈로그를 따른다. 추가 문구가 필요하면 동일 톤(회복 친화·인지 부담 감소·정체성 강화)을 유지한다.
|
||||
|
||||
| 위치 | 문구 |
|
||||
|------|------|
|
||||
| QuickCapture placeholder | 지금 머릿속에 있는 것 한 줄. 정리는 나중입니다. |
|
||||
| QuickCapture hint | Ctrl+Enter 구출 · Esc 취소 · 이미지 붙여넣기 |
|
||||
| QuickCapture 제출 실패 | 저장에 실패했습니다. 다시 시도해주세요. |
|
||||
| QuickCapture 취소 confirm (5자+) | 이 한 줄을 흘려보낼까요? |
|
||||
| Inbox 빈 상태 | 첫 기억을 구출해보세요. `Ctrl+Shift+J` |
|
||||
| Inbox 처리 중 카드 | Inkling이 정리하는 중… |
|
||||
| Inbox AI 실패 카드 | 정리 보류 — 원문은 안전합니다 |
|
||||
| 트레이 메뉴: Quick Capture | 기억 구출하기 |
|
||||
| 트레이 메뉴: Inbox 열기 | 구출한 메모 보기 |
|
||||
| 트레이 메뉴: Quit | 종료 |
|
||||
| 삭제 confirm | 이 기억을 버릴까요? 되돌릴 수 없습니다. |
|
||||
| Continuity 0건 | 이번 주 한 줄이면 시작입니다 |
|
||||
| Continuity 1~6건 | 이번 주 N/7 |
|
||||
| Continuity 7건+ | 이번 주 7/7 ✓ · 연속 K주 완성 |
|
||||
| 회복 토스트 | 🌱 흐름을 다시 이어갑니다 |
|
||||
| 즉시 보상 토스트 (회전 4종) | (§5.1 참조) |
|
||||
| IntentBanner 프롬프트 (회전 4종) | (§5.2 참조) |
|
||||
| Ollama 미실행 배너 | Inkling 정리가 잠시 멈췄습니다. Ollama를 실행해주세요. |
|
||||
| Ollama 모델 미설치 배너 | `ollama pull gemma4:9b` 실행 후 앱을 재시작해주세요. |
|
||||
| 단축키 등록 실패 배너 | 단축키 `Ctrl+Shift+J`를 다른 앱이 사용 중입니다. 충돌 앱을 닫거나 FAQ를 확인하세요. |
|
||||
|
||||
### 5.5 앱 종료·재시작
|
||||
|
||||
- 창 닫기(X)는 트레이로 숨기는 동작이며 앱은 계속 실행된다. 실제 종료는 트레이 메뉴의 "종료" 항목으로 수행한다.
|
||||
- 재시작 시 `pending_jobs`를 큐에 재적재하여 AiWorker 처리가 이어진다.
|
||||
- 회복 토스트 dismiss 상태는 renderer `localStorage`에 저장되어 재시작 후에도 유지된다.
|
||||
|
||||
---
|
||||
|
||||
@@ -416,6 +556,8 @@ Slice는 온보딩 위자드를 제공하지 않는다.
|
||||
| 7 | 앱 비정상 종료 | WAL 미체크포인트 | 재기동 시 자동 복구 | `PRAGMA journal_mode=WAL` + SQLite 자체 복구. |
|
||||
| 8 | 미디어 고아 파일 | DB에는 기록 없으나 파일 잔존 | 디스크 낭비 | 앱 기동 시 GC가 `media/{note_id}/` 중 미등록 디렉터리 제거. |
|
||||
| 9 | 마이그레이션 실패 | 스키마 충돌 | 앱 기동 불가 | DB 백업 복사본 자동 생성 후 기동 중단, 에러 다이얼로그 표시. |
|
||||
| 10 | OS 알림 권한 거부 | 즉시 보상 토스트 미발사 | 보상 신호 없음 | 캡처는 정상 동작. 로그에 1회 기록 후 무음 폴백. 재요청 안 함. |
|
||||
| 11 | IntentBanner 동시 setIntent | 두 탭/창에서 동시 호출 | (slice는 단일 창이라 미발생) | `intent_prompted_at` 컬럼이 NOT NULL이면 후속 setIntent는 user_intent만 갱신, 첫 시각 보존. |
|
||||
|
||||
### 6.2 로깅·진단
|
||||
|
||||
@@ -508,12 +650,24 @@ Slice는 온보딩 위자드를 제공하지 않는다.
|
||||
|
||||
## 8. 오픈 이슈 / 차기 spec으로 이관
|
||||
|
||||
기존 항목:
|
||||
- **단축키 충돌 시 변경 UI**: 설정 화면이 slice에 없으므로 FAQ/환경 변수로 임시 대응. 후속 spec에서 정식 설정 UI로 해결.
|
||||
- **AI 결과 수동 재처리 버튼**: dogfood 중 프롬프트 튜닝 편의가 필요하면 개발자 전용 debug 메뉴로 먼저 도입 검토.
|
||||
- **Gemma 4 모델 가용성**: 설계 시점(2026-04) 공식 체크포인트 상태를 별도 확인. 미출시면 `gemma3:9b` 등 동세대 대체 모델로 임시 운용하고 스펙을 업데이트.
|
||||
- **Electron 패키징 옵션 결정**: `electron-builder` vs `electron-forge`. Slice 바이너리는 dev 빌드로 충분하므로 후속 배포 spec에서 결정.
|
||||
- **Node 24 LTS → 이후 LTS 전환**: Node 24 Maintenance 진입 시점(로드맵상 2027년대)에 다음 짝수 메이저로 이관. 시점 도래 시 별도 spec.
|
||||
|
||||
Strategy v0.2 추가 이관 (구현·실험 시점이 slice 이후로 미뤄진 항목):
|
||||
- **실행 의도(if-then) 온보딩 위자드** (Strategy §3) — 첫 실행 3분 위자드, 2개 트리거 선택, Capture → Clarify → Capitalize 행동 모델 노출.
|
||||
- **상황별 최소 템플릿** (Strategy §6) — 회의/트러블슈팅/요청/의사결정/인수인계 5종, 2~4칸 입력 카드. QuickCapture v2.
|
||||
- **66일 단계별 습관 프로그램** (Strategy §9) — Week 0/1-2/3-4/5-8/9+ 단계별 알림 빈도·프롬프트 자동 변화. 사용자별 진행 상태 저장 필요.
|
||||
- **심리학 기반 KPI 측정** (Strategy §10) — Time to First Note, MVN Rate, Triggered Capture Rate, Reuse Rate, Confluence Candidate Acceptance + 월 1회 정성 설문 (인지 오프로딩, 자기효능감, 정체성 변화).
|
||||
- **A/B 테스트 인프라** (Strategy §11) — 4종 실험 (빈창 vs 템플릿 / 스트릭형 vs 가치형 피드백 / 의미 질문 유무 / 매일 vs 주 7회).
|
||||
- **Confluence 공유 후보 추천** (Strategy §8) — AI가 문제·해결·근거·재발 방지 패턴을 가진 메모를 후보로 분류. 실패 단어 → "주의할 점" 카피 변환.
|
||||
- **Triggered Capture** (Strategy §3, 회의형/디버깅형/요청 관리형/퇴근 회고형/학습형) — 캘린더 어댑터 + 시간 트리거 필요.
|
||||
- **Streak Freeze 자동 보호** (Strategy §5) — 휴가·주말·공휴일 자동 감지. 캘린더 권한 필요.
|
||||
- **알림 강도 사용자 조절 + 빈도 자동 감소** (Strategy §9 Week 9+) — 사용자 행동 분석 기반 알림 개인화.
|
||||
|
||||
---
|
||||
|
||||
*본 문서는 dogfood 결과와 후속 spec 작성 과정에서 조정될 수 있다.*
|
||||
|
||||
Reference in New Issue
Block a user