v0.2.7 release 후 dogfood 9건 누적 (F17~F25) 정리: - F17 휴지통 의미 분기 / F18 사유 입력 / F19 recall / F20 raw_text 가변 - F21 다기기 sync / F22 이미지 렌더링 (이미 v0.2.8 promoted) / F23 Ollama-less - F24 멀티모달 vision / F25 사이드바 + 저장소 추가: - v0.2.8+ roadmap: 7 cut 분할 (A~G), 12주 시간선, dependency graph - Cut A~G design specs (각 cut 별 design 결정 + schema + UI + 테스트 전략) - Cut A implementation plan (이미 v0.2.8 머지로 실행 완료, 참고 보존) PR #26 머지 후 main 에 doc commits rebase 안 되어 manual merge 진행: - F22 entry 는 origin/main 의 promoted 형태 우선 - 신규 9 파일 (specs/plan/roadmap) 은 origin/main 에 없는 파일 - "다음 항목 자리" 안내 F23 → F26 갱신 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.6 KiB
v0.2.9 — Cut B Design (status 4분기 + 사유 + Ollama-less)
작성일: 2026-05-09 선행 문서:
docs/superpowers/specs/2026-04-25-dogfood-feedback.md(F17, F18, F23)docs/superpowers/strategy/v028plus-roadmap.mdCut B
Cut 라벨: v0.2.9 — semver 엄밀히 minor (새 status enum + onboarding wizard) 이지만 v0.2.x feature lane 관습 유지.
1. Cut 정체성
데이터 모델 정비 cut. 메모의 의미 분기 (active / completed / archived / trashed) + 이동 시 사유 입력 + Ollama-less 모드 onboarding. 세 항목이 같은 schema 영역 (notes 테이블 + ai_status enum) 영향이라 한 cut.
2. 범위
| 항목 | 결정 |
|---|---|
| F17 | status 4분기 (active/completed/archived/trashed) + AI 자동 분류 버튼 (사유 입력 후 클릭 → AI 가 reason+raw_text 분석 → status 추천 → 사용자 confirm/dismiss) |
| F18 | 자유 텍스트 사유 (preset X — friction 최소). notes.move_reason 컬럼 또는 별도 trash_log 테이블 |
| F23 | 첫 launch wizard (Y/N) + Ollama 최적화 안내 + 설치 가이드 페이지 링크. ai_status='disabled' 신규 enum + capture skip + raw-only NoteCard fallback |
3. F17 디테일
3-1. Schema 마이그레이션 (m004)
ALTER TABLE notes ADD COLUMN status TEXT NOT NULL DEFAULT 'active'
CHECK (status IN ('active', 'completed', 'archived', 'trashed'));
ALTER TABLE notes ADD COLUMN status_changed_at TEXT;
ALTER TABLE notes ADD COLUMN move_reason TEXT;
-- 기존 deleted_at != NULL 노트 → status='trashed' migrate
UPDATE notes SET status='trashed', status_changed_at=deleted_at
WHERE deleted_at IS NOT NULL;
deleted_at 컬럼 — backward compat 위해 잔류 (status='trashed' 와 동기화). 향후 cut 에서 제거 가능.
3-2. 인터페이스
type NoteStatus = 'active' | 'completed' | 'archived' | 'trashed';
interface Note {
// ... 기존 필드
status: NoteStatus;
statusChangedAt: string | null;
moveReason: string | null;
}
NoteRepository 메서드:
setStatus(id: string, status: NoteStatus, reason: string | null): voidlistByStatus(status: NoteStatus, limit?: number): Note[]- 기존
restoreNote()→setStatus(id, 'active', null)으로 재구현
3-3. UI — inbox 헤더 탭 4개
기존 Inbox / 휴지통 2탭 → Inbox / 완료 / 보관 / 휴지통 4탭. 헤더 폭 좁아질 수 있어 short label + count badge.
<button>Inbox(N)</button> <button>완료(N)</button> <button>보관(N)</button> <button>휴지통(N)</button>
useInbox store 의 view: 'inbox' | 'completed' | 'archived' | 'trash' | 'settings' enum 확장 (기존 showTrash boolean + showSettings boolean → enum 통합 권장 — 또는 boolean 3개 유지). enum 통합이 깔끔.
3-4. NoteCard 액션 메뉴
기존 휴지통 버튼 1개 → 메뉴 (설정 페이지 내부의 dropdown 또는 inline 버튼 group):
- "완료로 이동"
- "보관함으로 이동"
- "휴지통으로 이동"
각 클릭 → 사유 입력 modal (한 줄 textarea, 빈 값 허용) → 확인 → setStatus(id, target, reason).
3-5. AI 자동 분류 버튼
사유 입력 modal 안 옵션:
사유: [____________________________]
[ AI 자동 분류 ] [ 완료 ] [ 보관 ] [ 휴지통 ] [ 취소 ]
"AI 자동 분류" 클릭 → main 의 ai:classify-status IPC → AiWorker 가 prompt:
다음 메모와 사용자 사유를 보고 어디로 이동해야 할지 판단:
- 메모 본문: <raw_text>
- 메모 요약: <summary>
- 사용자 사유: <reason>
- 가능한 status: completed (작업 끝), archived (장기 보관, 회수 가능), trashed (불필요)
JSON 출력: { "recommended": "completed|archived|trashed", "rationale": "..." }
응답 → 사용자에게 추천 + rationale 표시:
AI 추천: 완료
이유: "처리됨" 표현 + 사용자 사유 "결재 끝" 일치
[ 확정 ] [ 다른 status 선택 ]
확정 → setStatus 적용. 다른 선택 → preset 버튼 노출.
3-6. IPC
// 신규
'inbox:set-status': (id: string, status: NoteStatus, reason: string | null) => Promise<{ ok: true }>
'ai:classify-status': (id: string, reason: string) => Promise<{ recommended: NoteStatus; rationale: string }>
4. F18 디테일
자유 텍스트 사유 입력 — F17 의 modal 안에 그대로 포함. 별도 컬럼 notes.move_reason TEXT (가장 마지막 사유 보존). 변경 이력 보존 시 별도 테이블 (note_status_log) 가능 but YAGNI — 마지막 사유만으로 충분.
빈 값 허용 (preset X 정책 따라). 검색/필터 — 향후 cut (F19 search) 에서 검색 인덱스 포함 가능.
5. F23 디테일
5-1. Schema
ai_status enum 확장: pending | processing | complete | failed | disabled. 마이그레이션 m004 동일 commit:
-- ai_status 가 enum text 라 그대로 새 값 INSERT 가능. CHECK constraint 갱신:
-- (SQLite 는 CHECK ALTER 직접 안 됨 → table 재생성 또는 trigger 추가)
SQLite CHECK 갱신 어려움 — 옵션:
- (a) 기존 CHECK 제거 + 새 CHECK 추가 (table 재생성)
- (b) CHECK 부재 + application-level 검증
추천: (b) — application 검증 (zod schema). 마이그레이션 비용 ↓.
5-2. Onboarding wizard
첫 launch (settings.json 의 onboarding_completed flag 부재) 시 모달:
Inkling 사용 시작
Inkling 은 로컬 LLM (Ollama) 으로 메모를 자동 정리합니다.
Ollama 가 설치되어 있고 한국어 지원 모델 (gemma3, gemma2 등) 이 pull 되어 있어야 최적의 경험이 가능합니다.
설치 가이드: https://ollama.com/download
[ AI 자동 처리 사용 (Ollama 필요) ]
[ 원문만 저장 (AI 처리 안 함) ]
[ 나중에 설정 ]
3 옵션:
- (1) AI 사용 → settings.ai_enabled=true + onboarding_completed=true
- (2) 원문만 → ai_enabled=false + onboarding_completed=true
- (3) 나중에 → onboarding_completed=false (다음 launch 다시 prompt — 하지만 capture 가능, ai_enabled=null=undefined → default true)
추천: 3 옵션 모두 onboarding_completed=true 로 설정 + 사용자가 설정 페이지에서 언제든 변경. (3) 만 다시 prompt 면 friction.
수정: 3 옵션 모두 close 시 onboarding_completed=true. (3) 은 default ai_enabled=true (LAN Ollama 가정 본인 dogfood).
5-3. AI off 시 capture path
CaptureService.create():
const aiEnabled = await settingsService.get('ai_enabled', true);
if (!aiEnabled) {
// notes INSERT with ai_status='disabled' + skip pending_jobs enqueue
this.repo.insert({ ...input, ai_status: 'disabled' });
return { id, ... };
}
// 기존 path
5-4. NoteCard fallback
ai_status='disabled' 노트 → title fallback = raw_text 첫 60자 (또는 첫 줄).
const displayTitle = note.title?.trim() || note.rawText.split('\n')[0].slice(0, 60) || '(빈 메모)';
summary/tags hide. raw_text 그대로.
5-5. Banner 비활성
ai_enabled=false → OllamaBanner / FailedBanner 자동 비활성 (state 가 false 면 render skip). HealthChecker 도 ai_enabled=false 시 polling 중단.
5-6. 설정 페이지 토글
AI 제공자 섹션 상단 추가:
[ ] AI 자동 처리 사용
↳ AI 처리를 사용하면 메모의 제목/요약/태그가 자동 생성됩니다.
Ollama 로컬 LLM 이 필요합니다. 설치 가이드: ollama.com/download
토글 OFF → ON 전환 시: onboarding wizard 와 동일 prompt 재노출 (간소화 — 그냥 endpoint 검증 후 결과 표시).
5-7. 옛 노트 처리 (ON ↔ OFF 전환)
B1 정책 채택 (roadmap):
- ON → OFF: 기존 pending 잔재 그대로 (드레인 후 enqueue stop)
- OFF → ON: 기존 disabled 잔류 (사용자 명시 trigger 만 처리)
설정 페이지 안 "기존 disabled 메모 N건 — 지금 모두 처리" 버튼 (ON 전환 후 disabled count > 0 시 노출).
6. 테스트 전략
| 영역 | 단위 | 수동 |
|---|---|---|
| m004 마이그레이션 | mock db → status 컬럼 + 기존 deleted_at != NULL → trashed | - |
setStatus repo |
4 status 전환 + reason 저장 + statusChangedAt | - |
listByStatus |
각 status filter | - |
| 4탭 UI 렌더 | view enum 4값 분기 + count badge | - |
| 사유 입력 modal | 자유 텍스트 입력 + 빈 값 허용 + 4 status 버튼 | - |
ai:classify-status IPC |
mock provider 응답 → recommended + rationale 반환 | - |
| AI 자동 분류 UI | recommended 표시 + 확정 클릭 시 setStatus 호출 | - |
| ai_status='disabled' enum | application zod 검증 + capture path skip pending_jobs | - |
| Onboarding wizard | 첫 launch 시 표시 (settings 부재) + 3 옵션 결과 | 첫 launch 시 표시 확인 |
| AI off 시 NoteCard | title fallback (raw 첫 줄), summary/tags hide | - |
| AI off 시 Banner | render skip 회귀 | - |
목표: 단위 467 → 약 490 (+23), typecheck 0.
7. Risk
| Risk | 대응 |
|---|---|
| AI 자동 분류 정확도 낮음 | 추천만 표시, 사용자 confirm 강제. fallback = preset 4 status 버튼 |
| status 4분기 + tag + reason layer 가 사용자 정신 부담 | dogfood 1주 측정. 사용 빈도 낮은 status 는 v0.2.10+ 에서 hide 옵션 |
| Onboarding wizard 가 첫 launch 흐름 차단 | "나중에 설정" 옵션 제공 + close 가능 |
| ai_enabled false 시 회귀 (기존 pending → 영원히 잔류) | 설정 페이지의 "기존 disabled 메모 N건 처리" 버튼 |
8. v0.2.9 후
Cut C (v0.2.10) — F20 raw_text revision history. AI 재실행 input = current raw_text (latest revision).
dogfood verify:
- 4탭 사용 빈도 (active/completed/archived/trashed) — 사용 안 되는 status 발견 시 cut B+1 에서 hide
- AI 자동 분류 정확도 (사용자 confirm 비율)
- Onboarding wizard 의 3 옵션 비율