From c7e9463adb0ac6eebd428961e64d35e03ead47d4 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 16:05:15 +0900 Subject: [PATCH 1/6] fix(e2e): rebuild better-sqlite3 per ABI + select inbox window by title - Vitest needs the node-ABI prebuilt sqlite binary; Electron 41 needs ABI 145. Add `rebuild:node` / `rebuild:electron` scripts and wire them as pre-hooks for `test`, `test:e2e`, `start`, `dev`, `test:integration`. - Smoke test was racing on `firstWindow()`, which non-deterministically returned the quickcapture window. Strip ELECTRON_RUN_AS_NODE from the launch env, wait for both windows to register, then pick the inbox by title and assert the heading via `getByRole`. Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 7 +++++++ tests/e2e/smoke.spec.ts | 25 +++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6df0088..977ce65 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,13 @@ "dev": "electron-vite dev", "build": "electron-vite build", "start": "electron-vite preview", + "rebuild:node": "cd node_modules/better-sqlite3 && prebuild-install", + "rebuild:electron": "cd node_modules/better-sqlite3 && prebuild-install --runtime=electron --target=41.3.0", + "pretest": "npm run rebuild:node", + "pretest:integration": "npm run rebuild:node", + "pretest:e2e": "npm run rebuild:electron && npm run build", + "prestart": "npm run rebuild:electron", + "predev": "npm run rebuild:electron", "test": "vitest run", "test:watch": "vitest", "test:integration": "INKLING_INTEGRATION=1 vitest run tests/integration", diff --git a/tests/e2e/smoke.spec.ts b/tests/e2e/smoke.spec.ts index f0811e4..8f5b652 100644 --- a/tests/e2e/smoke.spec.ts +++ b/tests/e2e/smoke.spec.ts @@ -2,13 +2,30 @@ import { test, expect, _electron as electron } from '@playwright/test'; import { resolve } from 'node:path'; test('inbox shell shows v0.2 empty state', async () => { + // Strip ELECTRON_RUN_AS_NODE if it leaked in from the parent process + // (some harnesses set it for native-module rebuild and forget to clear it). + // When set, Electron's main-process module hook is skipped and require('electron') + // returns only the binary path, so app.whenReady is undefined. + const env: Record = {}; + for (const [k, v] of Object.entries(process.env)) { + if (k === 'ELECTRON_RUN_AS_NODE') continue; + if (typeof v === 'string') env[k] = v; + } + env.INKLING_DEBUG = '1'; const app = await electron.launch({ args: [resolve('out/main/index.js')], - env: { ...process.env, INKLING_DEBUG: '1' } + env }); - const inbox = await app.firstWindow(); - await inbox.waitForLoadState('domcontentloaded'); - await expect(inbox.getByText('Inkling')).toBeVisible(); + const first = await app.firstWindow(); + // Both the inbox and quickcapture windows are created at startup; + // firstWindow() may pick either, so select by title. + await new Promise((r) => setTimeout(r, 500)); + let inbox = first; + for (const w of app.windows()) { + if ((await w.title()) === 'Inkling') { inbox = w; break; } + } + await inbox.waitForLoadState('load'); + await expect(inbox.getByRole('heading', { name: 'Inkling' })).toBeVisible(); await expect(inbox.getByText('첫 기억을 구출해보세요.')).toBeVisible(); await app.close(); }); From f9b0e67ec57be23333bdf29260e2ff7d37ff1e37 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 16:12:08 +0900 Subject: [PATCH 2/6] docs: add Korean README + 2-week dogfood strategy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: Korean project intro, prereqs (Volta + VS Build Tools 2022 + LAN Ollama), npm scripts, ABI/ELECTRON_RUN_AS_NODE gotchas, slice invariants, doc map. - docs/superpowers/strategy/dogfood-strategy.md: 2-week self-dogfood plan to satisfy slice exit condition (spec §1.3). Covers Day 0 checklist, daily triggers tied to strategy §3 if-then implementation intentions, weekly continuity tracking, friction-vs-backlog triage rule, and a Pass/Conditional/Fail verdict at Day 14. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 173 ++++++++++++ docs/superpowers/strategy/dogfood-strategy.md | 259 ++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 README.md create mode 100644 docs/superpowers/strategy/dogfood-strategy.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..bcc14c9 --- /dev/null +++ b/README.md @@ -0,0 +1,173 @@ +# Inkling + +기록 습관이 없는 사람도 3초 안에 던지고, 로컬 AI가 정리하고, 나중에 업무 자산으로 회수할 수 있게 해주는 데스크톱 메모 앱이다. "메모 = 잊기 전에 머리에서 꺼내는 1회 행동"으로 행동 기준을 낮추고, 정리는 Ollama가 맡는다. + +이 저장소는 슬라이스 v0.4 단계다 — 가장 얇은 종단 경로(Quick Capture → SQLite → 로컬 Ollama → Inbox)만 동작한다. 음성·검색·내보내기·온보딩·캘린더 트리거는 후속 spec의 범위. + +--- + +## 상태 + +- **버전:** Slice v0.4 +- **플랫폼:** Windows(dogfood 우선) + macOS(빌드 통과 수준) +- **테스트:** 단위 52/52, e2e smoke 1/1, typecheck/build 통과 +- **종료 조건:** 본인 2주 dogfood 완주 + AI 결과 7/10 수용 + 크래시 0회 (spec §1.3) + +--- + +## 사전 요구 + +| 항목 | 버전/메모 | +|------|----------| +| Node | 24.15.0 (`.nvmrc`) — Volta 권장, nvm-windows도 가능 | +| Visual Studio Build Tools | 2022 + "Desktop development with C++" 워크로드 (better-sqlite3 빌드용) | +| Ollama | LAN 서버에서 실행, `gemma4:e4b` 모델 pull (8.0B Q4_K_M, 효율 변형) | +| Git for Windows | 최신 | + +LAN Ollama 엔드포인트는 환경변수 `INKLING_OLLAMA_ENDPOINT` 로 주입한다. + +``` +INKLING_OLLAMA_ENDPOINT=http://192.168.0.47:11434 +``` + +미설정 시 `http://localhost:11434` 로 폴백. + +--- + +## 시작하기 + +```bash +# 1. 의존성 설치 +npm install + +# 2. 개발 모드 (electron-vite dev) +npm run dev + +# 3. 프로덕션 빌드 +npm run build + +# 4. 빌드 결과 실행 +npm start +``` + +전역 단축키: `Ctrl+Shift+J` (Windows) / `Cmd+Shift+J` (macOS). +Quick Capture 창이 화면 중앙 상단에 뜬다. 한 줄 던지고 `Ctrl+Enter` 로 저장. + +--- + +## 테스트 + +```bash +# 단위 테스트 (vitest) +npm test + +# 통합 테스트 (실제 Ollama 호출, 게이트됨) +npm run test:integration + +# e2e smoke 테스트 (Playwright + Electron) +npm run test:e2e + +# 타입 체크 +npm run typecheck +``` + +### Native ABI 함정 + +`better-sqlite3` 는 두 가지 런타임에서 호출된다 — vitest(Node ABI)와 Electron(ABI 145). 한 번에 한 ABI 로만 빌드되므로 npm 스크립트가 자동으로 prebuild-install 을 돌린다. + +- `pretest` / `pretest:integration` → `rebuild:node` +- `pretest:e2e` / `prestart` / `predev` → `rebuild:electron` + +새 native dep 을 추가하거나 Electron 메이저 버전을 올리면 `package.json` 의 `rebuild:*` 스크립트도 갱신해야 한다. + +### ELECTRON_RUN_AS_NODE 함정 + +이 환경변수가 부모 셸에 살아있으면 Electron main-process 모듈 hook 이 비활성화돼 `require('electron')` 이 path string 만 반환한다. e2e 스펙에서 launch env 단계에 명시적으로 strip 한다. 새 main-launch 스크립트를 작성할 때도 동일 strip 필요. + +--- + +## 아키텍처 + +``` +src/ + main/ Electron main process + db/ SQLite + 마이그레이션 + repository/ NoteRepository (CRUD + edited-flag overwrite guard) + services/ Capture, Continuity, Hotkey, Intent, Notification, MediaStore, MediaGc, HealthChecker + ai/ InferenceProvider 인터페이스 + LocalOllamaProvider + AiWorker (3-attempt backoff) + ipc/ contextBridge용 IPC 핸들러 + windows/ Inbox + QuickCapture BrowserWindow 팩토리 + tray.ts Tray 아이콘 + 메뉴 + paths.ts 프로필 디렉터리 해석 + logger.ts electron-log 래퍼 (raw_text 절대 미기록) + preload/ contextBridge로 typed API expose + renderer/ + inbox/ React 19 + Zustand 스토어 + quickcapture/ React 19, frameless 640x280 + shared/ + types.ts IPC 계약 타입 + intentPrompts.ts 의미 한 줄 회전 카피 4종 +tests/ + unit/ vitest, 52 cases + integration/ Ollama golden test (게이트됨) + e2e/ Playwright smoke +docs/ + superpowers/specs/ 설계 문서 + superpowers/plans/ 구현 계획 + superpowers/strategy/ 심리학·dogfooding 전략 + handoff/ 세션 간 핸드오프 +inkling.md 원본 제품 브리프 v1.4 +``` + +핵심 흐름은 Capture → Clarify → Capitalize 3단계 (Strategy §2). 슬라이스에서는 Capture + Clarify 만 다루고 Capitalize(Confluence 내보내기·주간 회고) 는 후속. + +--- + +## Load-bearing 불변 (재논의 금지) + +이 규칙들은 spec, 테스트, 코드 곳곳에 박혀 있다. 변경하려면 spec v0.4 §3.3 부터 다시 검토. + +1. **`raw_text` 는 불변.** update method 없음. 추가 금지. +2. **AI 재실행은 user-edited 필드를 덮어쓰지 않는다.** `*_edited_by_user` 컬럼 + CASE WHEN 가드. +3. **IntentBanner 는 노트당 정확히 1회.** `intent_prompted_at IS NULL` 이 게이트. setIntent · dismissIntent 둘 다 게이트 닫음. +4. **로그에 본문 금지.** `raw_text` / `ai_title` / `ai_summary` / `user_intent` 내용 절대 미기록. ID, 길이, 해시 prefix 만. +5. **"실패", "끊김", "연속 실패" 단어는 모든 UI 문자열에서 금지.** 회복 친화 톤이 강제. +6. **네이티브 알림은 submit 을 절대 막지 않는다.** 권한 거부 = silent fallback. 캡처는 계속 동작. +7. **버전은 정확 pin (`^` 금지).** `package.json` 갱신 시 spec §7.2 와 동시 업데이트. + +--- + +## Quick Capture 제약 + +- 단일 입력창, 텍스트 + 클립보드 이미지만. +- 제목·태그·폴더 선택 강요 안 함 (Strategy §2.1). +- 저장 직후 OS 네이티브 토스트 1개 (회전 카피 4종). +- AI 처리는 비동기 — 저장은 즉시, 정리는 백그라운드. + +## Inbox 제약 + +- 날짜 내림차순 리스트, 같은 ms 동기화 시 `id DESC` 보조 정렬. +- AI 필드(제목·요약) 는 인라인 편집 가능. 편집 시 `*_edited_by_user=1` 셋 → 이후 AI 재실행은 해당 필드 보존. +- 원문은 read-only. +- AI 가 생성한 필드 옆에 회색 "AI" 라벨. 사용자 편집 시 라벨 사라짐 ("내 메모" 정체성 표시). +- "의미 한 줄" 배너는 AI done 직후 1회만, skip 시 해당 카드에선 다시 안 뜸. +- Weekly Continuity 배지: 7건/주 (KST 월~일). 0/7 도 "이번 주 한 줄이면 시작입니다". + +--- + +## 문서 + +| 문서 | 경로 | 용도 | +|------|------|------| +| 제품 브리프 | `inkling.md` | 원본 v1.4, 전체 비전 | +| 심리학 전략 | `docs/superpowers/strategy/strategy.md` | 행동 변화 모델, 회복 친화 스트릭 | +| Dogfooding 전략 | `docs/superpowers/strategy/dogfood-strategy.md` | 2주 자기 dogfood 운영안 | +| 슬라이스 설계 | `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` | v0.4 spec, 부하 전달 결정의 출처 | +| 구현 계획 | `docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md` | 33-task TDD 플랜 | +| 핸드오프 | `docs/handoff/2026-04-25-linux-to-windows.md` | Linux→Windows 전환 메모 | + +--- + +## 라이선스 + +비공개. 본인 dogfood + 10인 사내 베타까지의 스코프. diff --git a/docs/superpowers/strategy/dogfood-strategy.md b/docs/superpowers/strategy/dogfood-strategy.md new file mode 100644 index 0000000..a3792da --- /dev/null +++ b/docs/superpowers/strategy/dogfood-strategy.md @@ -0,0 +1,259 @@ +# Inkling — Dogfooding 전략 + +**작성일:** 2026-04-25 +**대상:** 김태현 (저자 본인) — 슬라이스 v0.4 dogfood 단계 +**스펙 의존:** `2026-04-24-inkling-vertical-slice-design.md` v0.4 §1.3 (종료 조건) +**전략 의존:** `strategy.md` §1·§2·§5 (행동 정의, Capture→Clarify→Capitalize, 회복 친화 스트릭) + +--- + +## 0. 목적 + +이 문서는 슬라이스 v0.4 의 종료 조건을 충족시키는 **2주 자기 dogfood 운영안**이다. 외부 베타 모집 전에 본인이 먼저 매일 쓰면서 세 가설을 검증한다. + +| # | 가설 | 출처 | 측정 방법 | +|---|------|------|----------| +| H1 | 전역 단축키 → 입력 → 저장이 실제로 3초 내 마찰 없이 이뤄진다 | spec §0 | 일일 로그의 마찰 사건 수 + 주관 평점 | +| H2 | LAN Ollama(`gemma4:e4b`)가 한국어 제목·3줄 요약·태그를 실용 가능 품질로 생성한다 | spec §0 | 10건 중 7건 이상 사용자 수용 (spec §1.3) | +| H3 | Capture→Clarify→Capitalize 행동 루프와 회복 친화 스트릭이 "메모를 던지고 싶은 충동"을 만든다 | strategy §2·§5 | 7일 이내 3일 연속 기록(Aha) + 2주 dogfood 완주 | + +**가설 검증이 목적이지 기능 추가가 목적이 아니다.** dogfood 중 떠오르는 아이디어는 별도 백로그로 적고 슬라이스 코드에 손대지 않는다 (예외: 크래시·데이터 손실급 버그). + +--- + +## 1. 시작 체크리스트 (Day 0) + +dogfood 첫 날 시작 전, 환경을 한 번에 정렬한다. + +### 1.1 환경 + +- [ ] `.nvmrc` 의 Node 버전 (24.15.0) 활성화 +- [ ] `INKLING_OLLAMA_ENDPOINT` 가 LAN Ollama (`http://192.168.0.47:11434`) 를 가리킴 +- [ ] LAN Ollama 에 `gemma4:e4b` 가 pull 된 상태 확인 (`curl http://192.168.0.47:11434/api/tags`) +- [ ] `npm run build` 후 `npm start` 로 정식 실행 (dev 모드 아님 — dogfood 는 프로덕션 빌드) +- [ ] 윈도우 트레이에 Inkling 아이콘 떠 있음 +- [ ] `Ctrl+Shift+J` 가 다른 앱(Chrome, Edge DevTools 등)에 충돌 없이 잡힘 +- [ ] OS 알림 권한 허용 — 첫 토스트 후 시스템 트레이에서 확인 +- [ ] `%APPDATA%\Inkling\default\inkling.db` 가 새로 생성됨 (이전 dogfood 데이터 분리하려면 이 파일을 백업·삭제) + +### 1.2 dogfood 로그 파일 준비 + +`docs/dogfood/2026-04-25-week1.md` 를 새로 만든다. 이 문서는 매일 한 줄 이상 채우고 dogfood 종료 시 회고 자료가 된다. 슬라이스 종료 후 commit (Inkling 자체에 메모하지 않는 이유: 본 dogfood 가 깨지면 메모가 묶여 사라지므로). + +템플릿 — §6 참조. + +### 1.3 사전 마음가짐 + +- "메모를 잘 써야 한다" 가 아니라 "지금 머릿속에서 꺼낸다" (strategy §1). +- 오늘 0건이라도 죄책감 금지. 0/7 도 "이번 주 한 줄이면 시작입니다" 라는 슬라이스 카피 자체가 본인을 향한다. +- 기록의 품질은 AI 와 미래의 자신이 책임진다. 사용자의 책임은 캡처뿐. + +--- + +## 2. 일일 운영안 + +### 2.1 트리거 (실행 의도, strategy §3) + +매번 "메모할까 말까" 를 판단하면 마찰이 누적된다. 다음 if-then 두 개를 본인 캘린더·습관에 박는다. + +- **회의 종료 후 → `Ctrl+Shift+J` 1회.** 결정 / 액션 / 리스크 중 하나만. +- **퇴근 10분 전 → `Ctrl+Shift+J` 1회.** "오늘 다시 찾고 싶을 것" 한 줄. + +이 두 트리거가 dogfood 1주차의 최저 행동 기준이다. 추가 캡처는 보너스. + +### 2.2 캡처 규칙 + +- 한 줄로 충분. 20자 이하도 OK. +- 제목 고민 금지 — AI 가 생성. +- 클립보드 이미지가 있으면 paste, 없으면 텍스트만. +- 저장 후 토스트 ("이 생각은 이제 Inkling이 들고 있습니다." 등) 가 떴는지 확인. 안 떴으면 §5.1 마찰 로그 항목. + +### 2.3 Inbox 점검 (하루 1회, 5분) + +- 저녁에 Inbox 열기. +- 오늘 캡처한 노트의 AI 제목·요약·태그 검토. 사용자 수용 가능 = 손대지 않음. 부족 = 인라인 편집. +- "의미 한 줄" 배너가 뜬 노트 중 1~2개만 입력. 나머지는 skip 가능 (배너는 1회만 노출되므로 부담 없음). +- AI 가 `pending` 으로 멈춰 있으면 OllamaBanner 가 뜸 → LAN Ollama 상태 확인. + +### 2.4 주간 회고 (일요일 KST 22:00, 30분) + +- Weekly Continuity 배지 캡처: 이번 주 N/7 + 연속 K주. +- 일일 dogfood 로그를 빠르게 읽고 §3 의 지표를 채움. +- 다음 주 트리거 조정 — 너무 빈번이면 1개로 줄이고, 너무 드물면 1개 추가. + +--- + +## 3. 측정 지표 + +매일 / 매주 갱신할 숫자. 자동 추적 인프라는 슬라이스 범위 밖이라 수기로 적는다. 단, Inbox 의 노트 수와 Weekly Continuity 배지는 앱이 직접 보여준다. + +### 3.1 행동 지표 (매일 1줄) + +| 지표 | 의미 | 출처 | +|------|------|------| +| 일일 캡처 수 | 오늘 던진 노트 수 | Inbox | +| Time to Capture (체감) | 단축키 → 저장까지 체감 초 (>3초면 마찰 사건) | 본인 추정 | +| 트리거 응답률 | 회의 후·퇴근 전 트리거에 실제로 캡처한 횟수 / 트리거 발생 횟수 | 본인 카운트 | +| AI 수용률 | AI 생성 필드를 그대로 둔 노트 수 / 전체 캡처 수 | Inbox 의 "AI" 라벨 잔존율 | +| `pending` 스택 | 24시간 이상 `pending` 상태로 남은 노트 수 | Inbox PendingBanner | + +### 3.2 주간 지표 (일요일 갱신) + +| 지표 | 합격 기준 (slice §1.3 기반) | +|------|--------------------------| +| Weekly Continuity | ≥ 7/7 | +| Aha Moment | 설치 후 7일 이내 3일 연속 기록 — 1주차 안에 충족 (inkling.md §7.1) | +| AI 수용률 (주 평균) | ≥ 70% | +| 앱 크래시 | 0 회 | +| 데이터 손실 | 0 건 (재시작 후 노트 사라짐 0) | + +### 3.3 심리 지표 (주말 5문항, 1~5점) + +strategy §10 의 정성 설문을 슬라이스 규모로 축약. 매주 일요일에 자기 평가. + +1. Inkling 덕분에 업무 기억 부담이 줄었다 _(인지적 오프로딩)_ +2. 나는 업무 중 중요한 것을 놓칠 가능성이 줄었다 _(자기효능감)_ +3. 기록이 귀찮은 일이 아니라 도움이 되는 일로 느껴진다 _(내재화)_ +4. 나는 이제 "기록하는 사람" 에 가까워졌다 _(정체성 변화)_ +5. 0/7 → N/7 회복 카피가 죄책감 대신 안도감을 줬다 _(strategy §5 회복 친화 검증)_ + +평균이 3.0 미만이면 슬라이스 가설 H3 (행동 루프 흡인력) 가 약하다는 신호 — 다음 spec 우선순위에 반영. + +--- + +## 4. 운영 모드별 시나리오 + +### 4.1 LAN Ollama 다운 + +- OllamaBanner 가 노란색으로 뜸. 캡처는 계속 동작, AI 처리만 멈춤. +- LAN 서버 살아나면 AiWorker 가 자동 재처리 (3-attempt backoff: immediate / 30s / 120s). +- dogfood 영향: AI 수용률 측정 1일 누락. 다음 날 다시 측정. + +### 4.2 단축키 충돌 + +- DevTools 열린 Chrome/Edge 가 우선권. Quick Capture 안 뜸. +- 임시 우회: 트레이 아이콘 → "기억 구출" 메뉴. +- 마찰 사건으로 로그하고, 충돌 빈도가 주 3회 넘으면 후속 spec 에 단축키 변경 안건. + +### 4.3 0/7 주 + +- 회복 카피 ("이번 주 한 줄이면 시작입니다") 가 떴는지 확인. 안 떴으면 ContinuityService 버그. +- 회복 카피가 정상 노출됐다면 dogfood 가 진행 가능 — 죄책감 금지가 슬라이스 자체의 가설. +- 2주 연속 0/7 이면 H3 명확히 미달. spec §0 가설 재검토. + +### 4.4 크래시·데이터 손실 + +- 즉시 dogfood 중단. `%APPDATA%\Inkling\default\inkling.db` 와 로그 (`%APPDATA%\Inkling\logs\`) 백업. +- 슬라이스 invariant 위반 — 후속 작업 최우선. 본 dogfood 는 crash fix + 검증 후 재개. + +--- + +## 5. dogfood 중 떠오른 아이디어 처리 + +### 5.1 마찰 로그 vs 백로그 vs 슬라이스 패치 + +dogfood 중 발견하는 사항은 세 묶음으로 분류한다. + +| 유형 | 예시 | 처리 | +|------|------|------| +| 슬라이스 invariant 위반 | raw_text 가 변형됨, "실패" 문구가 UI 에 노출됨, 노트 손실 | 즉시 슬라이스 패치 (spec §3.3) | +| 마찰 사건 | 단축키 충돌, 토스트 안 뜸, 캡처 → 저장 5초 이상 | 일일 로그 §6 항목에 기록, 주말 회고에서 분류 | +| 신기능 아이디어 | "주간 자동 요약 보고서", "노트 검색", "캘린더 트리거" | `docs/superpowers/specs/` 에 후속 spec 초안. 슬라이스 코드 미수정 | + +**dogfood 중 슬라이스 코드에 신기능을 추가하는 것은 금지.** 본 dogfood 의 가설 검증 자체가 무효화된다. + +--- + +## 6. 일일 로그 템플릿 + +`docs/dogfood/2026-MM-DD-weekN.md` 에 매일 추가. 한 줄도 OK. + +```markdown +# Week N · YYYY-MM-DD (요일) + +## 캡처 +- 일일 캡처 수: __ +- 트리거 응답: 회의후 _/_ · 퇴근전 _/_ + +## AI +- 수용률 체감: __% +- AI 가 거의 손대지 않은 노트: __ +- AI 결과가 부족해 편집한 노트: __ + +## 마찰 사건 (있으면 1줄씩) +- + +## 오늘 가장 잘 구출한 메모 1개 +- (제목만, 본문은 적지 않음 — 로그는 git 에 들어가므로) + +## 자유 메모 +- +``` + +--- + +## 7. 종료 판정 + +2주차 마지막 날 (= 14일째) 에 다음을 평가한다. + +### 7.1 Pass — 다음 spec 으로 + +다음 모두 충족 시 슬라이스 종료 + `v0.2.0-slice` git tag + 다음 spec 작성. + +- [ ] 2주 연속 dogfood 완주 (1주차 최소 5일, 2주차 최소 5일 캡처) +- [ ] AI 수용률 주 평균 ≥ 70% +- [ ] Weekly Continuity 2주 모두 ≥ 7/7 +- [ ] 크래시 0회 + 데이터 손실 0건 +- [ ] 심리 지표 주 평균 ≥ 3.0 + +### 7.2 Conditional Pass — 슬라이스 패치 후 1주 추가 + +H1 (3초 마찰) 또는 H2 (AI 품질) 만 미달, 그 외는 충족. + +- 마찰 사건 패치 또는 프롬프트 튜닝 → 슬라이스 패치 PR 1회 +- 추가 1주 dogfood 후 재평가 + +### 7.3 Fail — 가설 재검토 + +H3 (행동 루프) 미달 또는 다중 가설 미달. + +- spec §0 의 가설 수정 또는 슬라이스 범위 조정 +- 본 dogfood 종료, 외부 베타 보류 +- strategy §11 의 A/B 테스트 항목으로 직진 (실험 4 = 매일 스트릭 vs 주 3회 연속성 → 본 슬라이스가 후자 채택, 그 가설을 다시 의심) + +--- + +## 8. 외부 베타 직전 점검 (slice 종료 후) + +본 dogfood 가 Pass 여도, 외부 10인 베타 전에 다음을 추가로 확인한다 — 본 문서 범위 밖이지만 메모 차원으로 적어둠. + +- 다중 프로필·잠금 (privacy) +- 코드 서명·SmartScreen +- 자동 업데이트 +- 충돌 리포팅 + +이 항목들은 slice 후속 spec 에서 다룬다. + +--- + +## 부록 A. dogfood 와 슬라이스 자체의 차이 + +이 dogfood 는 **슬라이스 v0.4 의 종료 조건**을 충족시키는 활동이다. 그러므로: + +- 일정: 14일 고정 +- 산출물: dogfood 로그 (`docs/dogfood/`) + Pass/Fail 판정 메모 +- 책임자: 본인 (외부 참여 없음) +- 평가 시점: Day 14 의 회고 + +이는 inkling.md §7.1 의 Aha Moment (사용자 일반론) 와 다르다. Aha 는 슬라이스가 충족시켜야 할 부분 조건이고, 본 dogfood 종료는 슬라이스 자체의 졸업 시험이다. + +## 부록 B. 카피 회수 검증 항목 + +본 dogfood 는 strategy 문서의 카피 정책이 실제 본인 감정에 어떻게 닿는지 검증할 수 있는 마지막 단계다. 다음 카피가 "본인을 향했을 때 적절한가" 를 매주 1점만 추가 평가: + +- "이 생각은 이제 Inkling이 들고 있습니다." +- "🌱 흐름을 다시 이어갑니다" +- "이번 주 한 줄이면 시작입니다" +- "내일의 내가 알아야 할 것은?" + +이 평가가 낮으면 다음 spec 의 카피 카탈로그 갱신 안건. From df56661f4f70478957ae06b2f9dc8d3fe49b2493 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 16:20:57 +0900 Subject: [PATCH 3/6] feat(diag): log resolved Ollama endpoint + surface health reason in banner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the OllamaBanner appears, it was generic enough that the user couldn't tell whether the env var was missing, the LAN host was unreachable, the model was uninstalled, or DNS was failing. Log the resolved endpoint at startup with a `fromEnv` flag so we can confirm INKLING_OLLAMA_ENDPOINT was actually read, and render the underlying health-check reason as a small subtitle under the banner copy. The user-facing primary message still avoids the forbidden tone words ("실패"/"끊김"/"연속 실패"); the diagnostic line is technical and only appears when status.reason is set. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/index.ts | 7 +++++-- src/renderer/inbox/components/OllamaBanner.tsx | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 2f1851d..ee042f7 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -34,9 +34,12 @@ app.whenReady().then(async () => { const continuity = new ContinuityService(db); const intent = new IntentService(repo); - const provider = new LocalOllamaProvider({ - endpoint: process.env.INKLING_OLLAMA_ENDPOINT + const resolvedEndpoint = process.env.INKLING_OLLAMA_ENDPOINT ?? 'http://localhost:11434'; + logger.info('ai.endpoint', { + endpoint: resolvedEndpoint, + fromEnv: process.env.INKLING_OLLAMA_ENDPOINT !== undefined }); + const provider = new LocalOllamaProvider({ endpoint: resolvedEndpoint }); const health = new HealthChecker(provider); void health.runOnce().then((h) => logger.info('ai.health', { ...h } as Record)); diff --git a/src/renderer/inbox/components/OllamaBanner.tsx b/src/renderer/inbox/components/OllamaBanner.tsx index 066f9cc..ff2c87f 100644 --- a/src/renderer/inbox/components/OllamaBanner.tsx +++ b/src/renderer/inbox/components/OllamaBanner.tsx @@ -9,8 +9,13 @@ export function OllamaBanner(): React.ReactElement | null { ? '`ollama pull gemma4:e4b` 실행 후 앱을 재시작해주세요.' : 'Inkling 정리가 잠시 멈췄습니다. Ollama를 실행해주세요.'; return ( -
+
⚠ {message} + {status.reason ? ( + + 진단: {status.reason} + + ) : null}
); } From c5e5975a12726bd963017166082dc3a2ac3909e4 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 17:07:20 +0900 Subject: [PATCH 4/6] docs(spec): stub due-date extraction spec for post-slice work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captures the design skeleton, hypotheses, and open questions for the hybrid (rule-based first → AI fallback) Korean date-phrase extractor. Schema impact, prompt change, UX copy, and library tradeoffs are sketched but deferred — formalization waits for slice v0.4 dogfood Pass and the H1 (time-phrase frequency ≥ 30%) check in week-1 retro. Cross-link added to dogfood-strategy daily-metric table so the H1 data is collected during dogfood, and to README docs map. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 1 + .../2026-04-25-due-date-extraction-design.md | 189 ++++++++++++++++++ docs/superpowers/strategy/dogfood-strategy.md | 1 + 3 files changed, 191 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-25-due-date-extraction-design.md diff --git a/README.md b/README.md index bcc14c9..0990c40 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ inkling.md 원본 제품 브리프 v1.4 | 심리학 전략 | `docs/superpowers/strategy/strategy.md` | 행동 변화 모델, 회복 친화 스트릭 | | Dogfooding 전략 | `docs/superpowers/strategy/dogfood-strategy.md` | 2주 자기 dogfood 운영안 | | 슬라이스 설계 | `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` | v0.4 spec, 부하 전달 결정의 출처 | +| Due Date 추출 (stub) | `docs/superpowers/specs/2026-04-25-due-date-extraction-design.md` | 슬라이스 dogfood 후 정식화할 후속 spec 골격 | | 구현 계획 | `docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md` | 33-task TDD 플랜 | | 핸드오프 | `docs/handoff/2026-04-25-linux-to-windows.md` | Linux→Windows 전환 메모 | diff --git a/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md b/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md new file mode 100644 index 0000000..a1f970c --- /dev/null +++ b/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md @@ -0,0 +1,189 @@ +# Inkling — Due Date 추출 설계 문서 (Stub) + +**작성일:** 2026-04-25 (v0.1, stub) +**저자:** 김태현 (dlsrks0734@gmail.com) +**상태:** ⚠️ Stub — 슬라이스 v0.4 dogfood 종료 후 정식 작성. 본 문서는 후속 spec 의 골격과 결정 대기 항목만 담는다. +**선행 문서:** +- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` v0.4 (슬라이스 종료 후 본 spec 시작) +- `docs/superpowers/strategy/strategy.md` §2.2 (Clarify 단계의 의미 부여), §7-원칙4 (AI 확신도 낮게) +- `docs/superpowers/strategy/dogfood-strategy.md` (1주차 회고에서 due_date 필요성 검증 항목) + +--- + +## 0. 목적 + +캡처된 노트 본문에 시간 표현이 들어가면 (`오늘`, `내일`, `이번 주`, `다음 주`, `다음 달`, `N일 뒤`, `N월 N일`, `금요일까지` 등) 로컬 KST 기준으로 ISO 날짜를 추출해 `due_date` 후보로 제안한다. AI 가 단정하지 않고 사용자가 수락·편집·무시할 수 있는 **제안형 라벨**로 노출한다 (Strategy §7-원칙4). + +--- + +## 1. 가설 + +| # | 가설 | 측정 | +|---|------|------| +| H1 | 슬라이스 dogfood 노트 중 시간 표현 포함 비율이 ≥ 30% | 1주차 dogfood 로그 회고에서 본인 노트 분류 | +| H2 | 규칙 기반 파서가 일상 한국어 시간 표현의 ≥ 80% 를 정확히 변환 | 골든 픽스처 50건 + 단위 테스트 | +| H3 | due_date 후보 수락률 ≥ 60% (제안 중 사용자가 그대로 수락한 비율) | post-launch 측정 | + +H1 이 미달이면 본 spec 자체가 불필요 — dogfood 회고에서 먼저 H1 부터 판정한다. + +--- + +## 2. 범위 + +### 2.1 In-scope + +- **하이브리드 추출**: 규칙 기반 파서가 1차 시도 → 매칭 없을 시에만 AI 프롬프트에 위임. 슬라이스의 비동기 AI 단계와 동일 큐에서 동작. +- **상대 표현**: `오늘`, `내일`, `모레`, `이번 주 *요일`, `다음 주`, `다음 달`, `N일/N주/N개월 뒤`. +- **절대 표현**: `N월 N일`, `YYYY-MM-DD`, `MM/DD`. 연도 미명시 시 가까운 미래로 해석. +- **기준 시점**: KST 자정 (`Date.now()` + `Asia/Seoul` 시간대). ContinuityService 의 KST 주 경계 정의와 동일 라이브러리 재사용. +- **UI**: NoteCard 에 `📅 YYYY-MM-DD` 작은 회색 라벨. AI 라벨 정책과 동일하게 사용자 편집 시 라벨 사라짐. 인라인 편집/삭제 가능. +- **수정 추적**: `due_date_edited_by_user` 컬럼 추가, AI 재실행이 사용자 편집을 덮어쓰지 않음 (slice §3.3 invariant 2 와 동일 패턴). + +### 2.2 Out-of-scope + +- 캘린더 알림·스케줄러 (별도 spec) +- 시각 단위 추출 (`오후 3시`, `15:30`) — 본 spec 은 날짜만, 시각은 후속 +- 음력 / 절기 / "월말", "주말 안에" 같은 모호 표현 — AI 위임으로만 처리, 정확도 보장 안 함 +- 반복 일정 (`매주 월요일`) +- 외부 캘린더 연동 (Google, Outlook) + +--- + +## 3. 파서 vs AI 분기 정책 + +``` +입력 텍스트 + ↓ +규칙 파서 (정규식 + chrono-style) + ↓ +매칭 있음? ──→ ISO 날짜 + confidence: high + │ + └─ 매칭 없음 + 시간 후보 어휘 감지 ──→ AI 프롬프트 확장 (due_date 필드) + ↓ + AI 응답에 ISO 있음? ──→ confidence: medium + AI 응답 비었음? ──→ due_date null +``` + +규칙 파서는 동기·결정론적·테스트 가능. AI 분기는 슬라이스의 AiWorker 큐를 그대로 사용 (별도 호출 추가 안 함, 같은 generate 응답에 필드 추가). + +`confidence` 메타는 DB 에 저장하지 않고 UI 라벨 색조로만 표현 (high = 진한 회색, medium = 옅은 회색). + +--- + +## 4. 데이터 변경 + +### 4.1 Migration v2 (forward-only) + +```sql +ALTER TABLE notes ADD COLUMN due_date TEXT; +ALTER TABLE notes ADD COLUMN due_date_edited_by_user INTEGER NOT NULL DEFAULT 0; +``` + +`due_date` 형식: `YYYY-MM-DD` (KST 자정 기준). null = 추출 안 됨. + +### 4.2 NoteRepository 변경 + +- `updateAiResult` 시그니처에 `dueDate?: string | null` 추가, edited-flag CASE WHEN 가드 적용 +- `setDueDate(noteId, date)` 사용자 수정 메서드 신설, `due_date_edited_by_user = 1` 셋 + +### 4.3 zod 스키마 확장 + +`AiResponse` 에 `due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()` 추가. AI 가 빈 응답 또는 잘못된 형식 반환 시 정상 누락 처리 (스키마 검증 실패로 전체 응답 reject 안 함 → strict → loose 변환). + +--- + +## 5. 프롬프트 변경 (AI 분기 시에만) + +기존 프롬프트 끝에 추가: + +``` +입력 텍스트에서 마감일 또는 일정 날짜를 의미하는 표현이 있으면 ISO 형식 (YYYY-MM-DD) 으로 추출. KST 기준 오늘은 {{TODAY_KST}}. 추출 불가능하면 null. +``` + +`{{TODAY_KST}}` 는 `LocalOllamaProvider.generate` 시점에 주입. 시간대 처리는 main process 에서. + +--- + +## 6. UX 카피 (Strategy 준수) + +| 상황 | 카피 | +|------|------| +| 추출 성공 | `📅 {YYYY-MM-DD}` (작은 회색 라벨) | +| 사용자 편집 후 | `📅 {YYYY-MM-DD}` (회색 → 검정, AI 라벨 제거) | +| 만료 임박 (≤ 1일) | `📅 {YYYY-MM-DD}` 노란 배경 | +| 만료됨 | `📅 {YYYY-MM-DD}` 회색 + 취소선. **"실패", "지각", "놓침" 같은 단어 금지** (slice §1.1 카피 정책 상속) | + +만료 노트 처리 정책은 본 spec 에서 결정 — 자동 삭제 ❌, 자동 done ❌, 단지 시각적 표시만. + +--- + +## 7. 결정 대기 항목 (dogfood 회고 후 결론) + +본 spec 을 정식화하기 전 1주차 dogfood 로그에서 답해야 할 질문: + +1. **얼마나 자주 시간 표현이 들어가나?** (H1 검증) +2. **"오늘 PR 리뷰"** 같은 노트는 due 가 정말 오늘인가, 아니면 단순 맥락어인가? — false positive 비율 추정 +3. **due_date 가 있는 노트를 다시 보고 싶은 트리거는 무엇인가?** (Inbox 자체 / 별도 뷰 / 알림) +4. **`📅` 라벨이 NoteCard 의 어느 슬롯에 붙어야 자연스러운가?** (제목 옆, 태그 옆, 별도 줄) +5. **AI 가 추출한 due_date 가 틀렸을 때 사용자가 어떻게 알아채나?** — 카피·색조로 confidence 차이를 충분히 전달할 수 있나 +6. **만료된 노트를 별도 필터로 보고 싶은가, 아니면 인라인 처리만으로 충분한가?** + +--- + +## 8. 단계별 산출물 + +본 spec 정식화 후 plan 작성 시 예상 task 묶음 (본 stub 시점에선 추정만): + +- **데이터:** migration v2, NoteRepository 시그니처 확장 (단위 테스트 포함) +- **추출:** `DateExtractor` 모듈 (정규식 + 자체 KST 변환). 골든 픽스처 50건. AI 위임 분기 라우팅 +- **AI:** schema/prompt 확장, AiWorker 응답 매핑 변경 +- **UI:** NoteCard 라벨 슬롯, EditableField 재사용, 만료 색조 +- **마이그레이션 안전성:** 기존 노트의 `due_date_edited_by_user = 0` 디폴트, `due_date NULL` 처리 검증 + +--- + +## 9. Slice 와의 관계 + +본 spec 은 **슬라이스 v0.4 dogfood Pass 판정 후에만 시작.** 이유: + +- 슬라이스의 3 가설 (3초 마찰 / AI 품질 / 행동 루프) 중 어느 것도 due_date 없이 검증 가능 +- migration v2 + 컬럼 2개 + UI 슬롯 = 작지 않은 변화. 슬라이스 invariant 6 ("AI 재실행은 user-edited 필드 덮어쓰기 금지") 의 적용 표면을 확장하므로 dogfood 안정성 검증 후 진입이 안전 +- dogfood 1주차 회고에서 H1 (시간 표현 빈도 ≥ 30%) 이 미달이면 본 spec 자체가 폐기되고, 30% 이상이면 본 stub 을 v0.2 로 정식화 + +--- + +## 10. 라이브러리 후보 + +| 라이브러리 | 평가 | +|-----------|------| +| `chrono-node` | 영어·한국어 일부 지원, 활성. 한국어 커버리지가 핵심 표현 외에는 약함 | +| 자체 정규식 | 핵심 표현 (`오늘`, `내일`, `N일 뒤`) 만 6~8개 패턴으로 충분. 결정론적·테스트 쉬움. 권장 | +| `dayjs` + 사용자 정의 파서 | 한국어 표현은 직접 처리, 시간대 변환은 dayjs 의 `tz` 플러그인. 의존성 증가 | + +권장: **자체 정규식 + Intl.DateTimeFormat + KST 변환 헬퍼.** 외부 의존성 추가 없음, 슬라이스의 ContinuityService 가 이미 KST 변환 로직 보유 → 재사용. + +--- + +## 부록 A. 골든 픽스처 후보 (정식화 시 확장) + +``` +"오늘 PR 리뷰" → null 또는 today (양가) +"내일 회의 준비" → tomorrow +"다음 주 월요일까지 슬라이드" → next-mon +"3일 뒤 데모" → today+3 +"5월 1일 휴가 신청" → 2026-05-01 +"이번 주 안에 리팩터링" → 이번 주 일요일 +"월말 마감" → 해당 월 말일 (AI 위임 후보) +"주말까지" → 이번 주 일요일 (AI 위임 후보) +"퇴근 전" → today (시각 단위는 본 spec 범위 밖이지만 fallback) +``` + +각 케이스의 confidence 등급 + 사용자 가설 행동(수락 / 편집 / 무시)을 dogfood 회고에서 본인이 직접 라벨링한다. 이 라벨링이 H2 / H3 측정의 근거. + +--- + +## 부록 B. 미정 정책 + +- "오늘" 의 시간대 — 자정 기준인가, 24시간 후인가? (KST 자정으로 잠정 결정, dogfood 후 검증) +- 사용자 명시적 due_date 와 AI 추출 due_date 의 시각적 구분 (현재 안: AI 라벨 색조) +- due_date 검색·필터 UI — 본 spec 범위 vs 별도 spec diff --git a/docs/superpowers/strategy/dogfood-strategy.md b/docs/superpowers/strategy/dogfood-strategy.md index a3792da..c68c0b9 100644 --- a/docs/superpowers/strategy/dogfood-strategy.md +++ b/docs/superpowers/strategy/dogfood-strategy.md @@ -96,6 +96,7 @@ dogfood 첫 날 시작 전, 환경을 한 번에 정렬한다. | 트리거 응답률 | 회의 후·퇴근 전 트리거에 실제로 캡처한 횟수 / 트리거 발생 횟수 | 본인 카운트 | | AI 수용률 | AI 생성 필드를 그대로 둔 노트 수 / 전체 캡처 수 | Inbox 의 "AI" 라벨 잔존율 | | `pending` 스택 | 24시간 이상 `pending` 상태로 남은 노트 수 | Inbox PendingBanner | +| 시간 표현 포함 노트 수 | `오늘`/`내일`/`N일 뒤`/`N월 N일` 등 시간 어휘가 들어간 노트 수 — due-date-extraction spec H1 (≥ 30%) 검증용 | 본인 라벨링 (로그 §6 자유 메모) | ### 3.2 주간 지표 (일요일 갱신) From 1fb6670e6741740238bd70b74552bf9b5393a4c8 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 17:10:04 +0900 Subject: [PATCH 5/6] docs: decouple due-date spec from dogfood strategy doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The due-date extraction work originated as feedback during dogfood, but the dogfood strategy doc itself should stay feature-agnostic — it's the generic operating manual for the 2-week dogfood, not a feedback log. - Remove the "시간 표현 포함 노트 수" row from dogfood-strategy §3.1. - Rephrase the due-date spec stub so H1 / §7 / §9 reference the spec's own sample-collection plan instead of relying on the dogfood retro. - Spec is now framed as "independent of slice exit"; entry timing is a separate decision once an accumulated sample meets H1. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-04-25-due-date-extraction-design.md | 27 +++++++++---------- docs/superpowers/strategy/dogfood-strategy.md | 1 - 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md b/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md index a1f970c..6e9756f 100644 --- a/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md +++ b/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md @@ -2,11 +2,10 @@ **작성일:** 2026-04-25 (v0.1, stub) **저자:** 김태현 (dlsrks0734@gmail.com) -**상태:** ⚠️ Stub — 슬라이스 v0.4 dogfood 종료 후 정식 작성. 본 문서는 후속 spec 의 골격과 결정 대기 항목만 담는다. +**상태:** ⚠️ Stub — 슬라이스 v0.4 dogfood 중 발견된 피드백에서 출발. 본 문서는 후속 spec 의 골격과 결정 대기 항목만 담으며, 정식화 전에 §7 의 미정 항목들이 답해져야 한다. **선행 문서:** -- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` v0.4 (슬라이스 종료 후 본 spec 시작) +- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` v0.4 (슬라이스 본문) - `docs/superpowers/strategy/strategy.md` §2.2 (Clarify 단계의 의미 부여), §7-원칙4 (AI 확신도 낮게) -- `docs/superpowers/strategy/dogfood-strategy.md` (1주차 회고에서 due_date 필요성 검증 항목) --- @@ -20,11 +19,11 @@ | # | 가설 | 측정 | |---|------|------| -| H1 | 슬라이스 dogfood 노트 중 시간 표현 포함 비율이 ≥ 30% | 1주차 dogfood 로그 회고에서 본인 노트 분류 | +| H1 | 누적 캡처 노트 중 시간 표현 포함 비율이 ≥ 30% | 본 spec 자체 표본 — 누적 50건 노트의 본문을 정규식 + 본인 라벨링으로 분류 | | H2 | 규칙 기반 파서가 일상 한국어 시간 표현의 ≥ 80% 를 정확히 변환 | 골든 픽스처 50건 + 단위 테스트 | -| H3 | due_date 후보 수락률 ≥ 60% (제안 중 사용자가 그대로 수락한 비율) | post-launch 측정 | +| H3 | due_date 후보 수락률 ≥ 60% (제안 중 사용자가 그대로 수락한 비율) | 정식 출시 후 측정 | -H1 이 미달이면 본 spec 자체가 불필요 — dogfood 회고에서 먼저 H1 부터 판정한다. +H1 이 미달이면 본 spec 자체가 불필요 — 정식화 전 H1 표본 평가가 먼저다. --- @@ -117,11 +116,11 @@ ALTER TABLE notes ADD COLUMN due_date_edited_by_user INTEGER NOT NULL DEFAULT 0; --- -## 7. 결정 대기 항목 (dogfood 회고 후 결론) +## 7. 결정 대기 항목 (정식화 전 답할 질문) -본 spec 을 정식화하기 전 1주차 dogfood 로그에서 답해야 할 질문: +본 spec 을 정식화하기 전 답해야 할 항목 — 누적 캡처 표본·자체 사고 실험·라이브러리 비교 등으로 결론 도달: -1. **얼마나 자주 시간 표현이 들어가나?** (H1 검증) +1. **얼마나 자주 시간 표현이 들어가나?** (H1 검증, 표본 50건 분류) 2. **"오늘 PR 리뷰"** 같은 노트는 due 가 정말 오늘인가, 아니면 단순 맥락어인가? — false positive 비율 추정 3. **due_date 가 있는 노트를 다시 보고 싶은 트리거는 무엇인가?** (Inbox 자체 / 별도 뷰 / 알림) 4. **`📅` 라벨이 NoteCard 의 어느 슬롯에 붙어야 자연스러운가?** (제목 옆, 태그 옆, 별도 줄) @@ -144,11 +143,11 @@ ALTER TABLE notes ADD COLUMN due_date_edited_by_user INTEGER NOT NULL DEFAULT 0; ## 9. Slice 와의 관계 -본 spec 은 **슬라이스 v0.4 dogfood Pass 판정 후에만 시작.** 이유: +본 spec 은 **슬라이스 v0.4 와 독립**이다. 슬라이스 dogfood 중 발견된 피드백이 출처지만, 슬라이스 종료 조건에는 포함되지 않는다. - 슬라이스의 3 가설 (3초 마찰 / AI 품질 / 행동 루프) 중 어느 것도 due_date 없이 검증 가능 -- migration v2 + 컬럼 2개 + UI 슬롯 = 작지 않은 변화. 슬라이스 invariant 6 ("AI 재실행은 user-edited 필드 덮어쓰기 금지") 의 적용 표면을 확장하므로 dogfood 안정성 검증 후 진입이 안전 -- dogfood 1주차 회고에서 H1 (시간 표현 빈도 ≥ 30%) 이 미달이면 본 spec 자체가 폐기되고, 30% 이상이면 본 stub 을 v0.2 로 정식화 +- migration v2 + 컬럼 2개 + UI 슬롯 = 작지 않은 변화. 슬라이스 invariant 2 ("AI 재실행은 user-edited 필드 덮어쓰기 금지") 의 적용 표면을 확장하므로 슬라이스 안정성이 먼저 검증된 상태에서 진입하는 게 안전 +- 본 spec 의 정식화·실행 시점은 별개 결정 — 누적 표본으로 H1 충족이 확인되면 진입 --- @@ -178,12 +177,12 @@ ALTER TABLE notes ADD COLUMN due_date_edited_by_user INTEGER NOT NULL DEFAULT 0; "퇴근 전" → today (시각 단위는 본 spec 범위 밖이지만 fallback) ``` -각 케이스의 confidence 등급 + 사용자 가설 행동(수락 / 편집 / 무시)을 dogfood 회고에서 본인이 직접 라벨링한다. 이 라벨링이 H2 / H3 측정의 근거. +각 케이스의 confidence 등급 + 사용자 가설 행동(수락 / 편집 / 무시)을 본 spec 정식화 시 본인이 직접 라벨링한다. 이 라벨링이 H2 / H3 측정의 근거. --- ## 부록 B. 미정 정책 -- "오늘" 의 시간대 — 자정 기준인가, 24시간 후인가? (KST 자정으로 잠정 결정, dogfood 후 검증) +- "오늘" 의 시간대 — 자정 기준인가, 24시간 후인가? (KST 자정으로 잠정 결정, 정식화 시 검증) - 사용자 명시적 due_date 와 AI 추출 due_date 의 시각적 구분 (현재 안: AI 라벨 색조) - due_date 검색·필터 UI — 본 spec 범위 vs 별도 spec diff --git a/docs/superpowers/strategy/dogfood-strategy.md b/docs/superpowers/strategy/dogfood-strategy.md index c68c0b9..a3792da 100644 --- a/docs/superpowers/strategy/dogfood-strategy.md +++ b/docs/superpowers/strategy/dogfood-strategy.md @@ -96,7 +96,6 @@ dogfood 첫 날 시작 전, 환경을 한 번에 정렬한다. | 트리거 응답률 | 회의 후·퇴근 전 트리거에 실제로 캡처한 횟수 / 트리거 발생 횟수 | 본인 카운트 | | AI 수용률 | AI 생성 필드를 그대로 둔 노트 수 / 전체 캡처 수 | Inbox 의 "AI" 라벨 잔존율 | | `pending` 스택 | 24시간 이상 `pending` 상태로 남은 노트 수 | Inbox PendingBanner | -| 시간 표현 포함 노트 수 | `오늘`/`내일`/`N일 뒤`/`N월 N일` 등 시간 어휘가 들어간 노트 수 — due-date-extraction spec H1 (≥ 30%) 검증용 | 본인 라벨링 (로그 §6 자유 메모) | ### 3.2 주간 지표 (일요일 갱신) From fac019e8a05d94f55cb60b8aab056353f52b2d8e Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 17:14:38 +0900 Subject: [PATCH 6/6] docs(spec): convert due-date stub into dogfood-feedback collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframe the file as a living register for in-flight dogfood feedback rather than a single-feature stub. Each item carries a status label (raw / drafting / ready-for-spec / promoted / rejected) and a fixed six-slot template (관찰 / 제안 방향 / 결정 대기 / 가설·측정 / 범위 / 영향). Items graduate to their own spec file once mature; the entry here then collapses to a one-line link. - F1: Due-date 추출 (drafting) — content from the previous stub normalized to the new template. - F2: 태그 클릭 = 즉시 삭제 + undo 부재 (raw) — NoteCard.tsx:110 binds chip onClick to removeTag, no confirm or undo, and there is no "filter by tag" affordance to match the user's mental model. Direction: split click=filter, ✕-icon=remove with 5s undo toast. README docs map updated to point at the new path. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 2 +- .../specs/2026-04-25-dogfood-feedback.md | 164 +++++++++++++++ .../2026-04-25-due-date-extraction-design.md | 188 ------------------ 3 files changed, 165 insertions(+), 189 deletions(-) create mode 100644 docs/superpowers/specs/2026-04-25-dogfood-feedback.md delete mode 100644 docs/superpowers/specs/2026-04-25-due-date-extraction-design.md diff --git a/README.md b/README.md index 0990c40..8cd9243 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ inkling.md 원본 제품 브리프 v1.4 | 심리학 전략 | `docs/superpowers/strategy/strategy.md` | 행동 변화 모델, 회복 친화 스트릭 | | Dogfooding 전략 | `docs/superpowers/strategy/dogfood-strategy.md` | 2주 자기 dogfood 운영안 | | 슬라이스 설계 | `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` | v0.4 spec, 부하 전달 결정의 출처 | -| Due Date 추출 (stub) | `docs/superpowers/specs/2026-04-25-due-date-extraction-design.md` | 슬라이스 dogfood 후 정식화할 후속 spec 골격 | +| Dogfood 피드백 | `docs/superpowers/specs/2026-04-25-dogfood-feedback.md` | 슬라이스 dogfood 중 발견된 본인 피드백 수집 (living document) | | 구현 계획 | `docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md` | 33-task TDD 플랜 | | 핸드오프 | `docs/handoff/2026-04-25-linux-to-windows.md` | Linux→Windows 전환 메모 | diff --git a/docs/superpowers/specs/2026-04-25-dogfood-feedback.md b/docs/superpowers/specs/2026-04-25-dogfood-feedback.md new file mode 100644 index 0000000..cb2cf2e --- /dev/null +++ b/docs/superpowers/specs/2026-04-25-dogfood-feedback.md @@ -0,0 +1,164 @@ +# 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.md` v0.4 (슬라이스 본문) +- `docs/superpowers/strategy/strategy.md` (심리학 전략) +- `docs/superpowers/strategy/dogfood-strategy.md` (dogfood 운영안 — 본 문서와 의존 없음) + +--- + +## 0. 사용 규칙 + +- 새 피드백은 다음 번호 (`F`) 로 추가. 절대 기존 번호 재사용 금지 (외부 링크 안전성). +- 각 항목 상단에 상태 라벨 한 줄: + - 🌱 **raw** — 막 적은 관찰. 가공 안 됨. + - 🔬 **drafting** — 범위·가설·결정 대기 항목 분석 중. + - 📝 **ready-for-spec** — 정식 spec 으로 승격 가능. 다음 단계는 별도 파일 추출. + - 🚀 **promoted** — 별도 spec 파일로 분기 완료. 본 문서엔 요약 + 링크만. + - ❌ **rejected** — 가설 미달 또는 우선순위 외, 닫음. +- 각 항목의 표준 슬롯: **관찰 / 제안 방향 / 결정 대기 / 가설·측정 / 범위 / 영향**. 슬롯이 비어 있으면 비워두고 채워질 때 갱신. + +--- + +## F1. Due Date 추출 (🔬 drafting) + +**발견:** 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 부재 (🌱 raw) + +**발견:** 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. 짧은 제목 (🌱 raw)` 헤더로 시작. 표준 슬롯 6개 채우거나 비워둔 채 시작 가능. diff --git a/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md b/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md deleted file mode 100644 index 6e9756f..0000000 --- a/docs/superpowers/specs/2026-04-25-due-date-extraction-design.md +++ /dev/null @@ -1,188 +0,0 @@ -# Inkling — Due Date 추출 설계 문서 (Stub) - -**작성일:** 2026-04-25 (v0.1, stub) -**저자:** 김태현 (dlsrks0734@gmail.com) -**상태:** ⚠️ Stub — 슬라이스 v0.4 dogfood 중 발견된 피드백에서 출발. 본 문서는 후속 spec 의 골격과 결정 대기 항목만 담으며, 정식화 전에 §7 의 미정 항목들이 답해져야 한다. -**선행 문서:** -- `docs/superpowers/specs/2026-04-24-inkling-vertical-slice-design.md` v0.4 (슬라이스 본문) -- `docs/superpowers/strategy/strategy.md` §2.2 (Clarify 단계의 의미 부여), §7-원칙4 (AI 확신도 낮게) - ---- - -## 0. 목적 - -캡처된 노트 본문에 시간 표현이 들어가면 (`오늘`, `내일`, `이번 주`, `다음 주`, `다음 달`, `N일 뒤`, `N월 N일`, `금요일까지` 등) 로컬 KST 기준으로 ISO 날짜를 추출해 `due_date` 후보로 제안한다. AI 가 단정하지 않고 사용자가 수락·편집·무시할 수 있는 **제안형 라벨**로 노출한다 (Strategy §7-원칙4). - ---- - -## 1. 가설 - -| # | 가설 | 측정 | -|---|------|------| -| H1 | 누적 캡처 노트 중 시간 표현 포함 비율이 ≥ 30% | 본 spec 자체 표본 — 누적 50건 노트의 본문을 정규식 + 본인 라벨링으로 분류 | -| H2 | 규칙 기반 파서가 일상 한국어 시간 표현의 ≥ 80% 를 정확히 변환 | 골든 픽스처 50건 + 단위 테스트 | -| H3 | due_date 후보 수락률 ≥ 60% (제안 중 사용자가 그대로 수락한 비율) | 정식 출시 후 측정 | - -H1 이 미달이면 본 spec 자체가 불필요 — 정식화 전 H1 표본 평가가 먼저다. - ---- - -## 2. 범위 - -### 2.1 In-scope - -- **하이브리드 추출**: 규칙 기반 파서가 1차 시도 → 매칭 없을 시에만 AI 프롬프트에 위임. 슬라이스의 비동기 AI 단계와 동일 큐에서 동작. -- **상대 표현**: `오늘`, `내일`, `모레`, `이번 주 *요일`, `다음 주`, `다음 달`, `N일/N주/N개월 뒤`. -- **절대 표현**: `N월 N일`, `YYYY-MM-DD`, `MM/DD`. 연도 미명시 시 가까운 미래로 해석. -- **기준 시점**: KST 자정 (`Date.now()` + `Asia/Seoul` 시간대). ContinuityService 의 KST 주 경계 정의와 동일 라이브러리 재사용. -- **UI**: NoteCard 에 `📅 YYYY-MM-DD` 작은 회색 라벨. AI 라벨 정책과 동일하게 사용자 편집 시 라벨 사라짐. 인라인 편집/삭제 가능. -- **수정 추적**: `due_date_edited_by_user` 컬럼 추가, AI 재실행이 사용자 편집을 덮어쓰지 않음 (slice §3.3 invariant 2 와 동일 패턴). - -### 2.2 Out-of-scope - -- 캘린더 알림·스케줄러 (별도 spec) -- 시각 단위 추출 (`오후 3시`, `15:30`) — 본 spec 은 날짜만, 시각은 후속 -- 음력 / 절기 / "월말", "주말 안에" 같은 모호 표현 — AI 위임으로만 처리, 정확도 보장 안 함 -- 반복 일정 (`매주 월요일`) -- 외부 캘린더 연동 (Google, Outlook) - ---- - -## 3. 파서 vs AI 분기 정책 - -``` -입력 텍스트 - ↓ -규칙 파서 (정규식 + chrono-style) - ↓ -매칭 있음? ──→ ISO 날짜 + confidence: high - │ - └─ 매칭 없음 + 시간 후보 어휘 감지 ──→ AI 프롬프트 확장 (due_date 필드) - ↓ - AI 응답에 ISO 있음? ──→ confidence: medium - AI 응답 비었음? ──→ due_date null -``` - -규칙 파서는 동기·결정론적·테스트 가능. AI 분기는 슬라이스의 AiWorker 큐를 그대로 사용 (별도 호출 추가 안 함, 같은 generate 응답에 필드 추가). - -`confidence` 메타는 DB 에 저장하지 않고 UI 라벨 색조로만 표현 (high = 진한 회색, medium = 옅은 회색). - ---- - -## 4. 데이터 변경 - -### 4.1 Migration v2 (forward-only) - -```sql -ALTER TABLE notes ADD COLUMN due_date TEXT; -ALTER TABLE notes ADD COLUMN due_date_edited_by_user INTEGER NOT NULL DEFAULT 0; -``` - -`due_date` 형식: `YYYY-MM-DD` (KST 자정 기준). null = 추출 안 됨. - -### 4.2 NoteRepository 변경 - -- `updateAiResult` 시그니처에 `dueDate?: string | null` 추가, edited-flag CASE WHEN 가드 적용 -- `setDueDate(noteId, date)` 사용자 수정 메서드 신설, `due_date_edited_by_user = 1` 셋 - -### 4.3 zod 스키마 확장 - -`AiResponse` 에 `due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()` 추가. AI 가 빈 응답 또는 잘못된 형식 반환 시 정상 누락 처리 (스키마 검증 실패로 전체 응답 reject 안 함 → strict → loose 변환). - ---- - -## 5. 프롬프트 변경 (AI 분기 시에만) - -기존 프롬프트 끝에 추가: - -``` -입력 텍스트에서 마감일 또는 일정 날짜를 의미하는 표현이 있으면 ISO 형식 (YYYY-MM-DD) 으로 추출. KST 기준 오늘은 {{TODAY_KST}}. 추출 불가능하면 null. -``` - -`{{TODAY_KST}}` 는 `LocalOllamaProvider.generate` 시점에 주입. 시간대 처리는 main process 에서. - ---- - -## 6. UX 카피 (Strategy 준수) - -| 상황 | 카피 | -|------|------| -| 추출 성공 | `📅 {YYYY-MM-DD}` (작은 회색 라벨) | -| 사용자 편집 후 | `📅 {YYYY-MM-DD}` (회색 → 검정, AI 라벨 제거) | -| 만료 임박 (≤ 1일) | `📅 {YYYY-MM-DD}` 노란 배경 | -| 만료됨 | `📅 {YYYY-MM-DD}` 회색 + 취소선. **"실패", "지각", "놓침" 같은 단어 금지** (slice §1.1 카피 정책 상속) | - -만료 노트 처리 정책은 본 spec 에서 결정 — 자동 삭제 ❌, 자동 done ❌, 단지 시각적 표시만. - ---- - -## 7. 결정 대기 항목 (정식화 전 답할 질문) - -본 spec 을 정식화하기 전 답해야 할 항목 — 누적 캡처 표본·자체 사고 실험·라이브러리 비교 등으로 결론 도달: - -1. **얼마나 자주 시간 표현이 들어가나?** (H1 검증, 표본 50건 분류) -2. **"오늘 PR 리뷰"** 같은 노트는 due 가 정말 오늘인가, 아니면 단순 맥락어인가? — false positive 비율 추정 -3. **due_date 가 있는 노트를 다시 보고 싶은 트리거는 무엇인가?** (Inbox 자체 / 별도 뷰 / 알림) -4. **`📅` 라벨이 NoteCard 의 어느 슬롯에 붙어야 자연스러운가?** (제목 옆, 태그 옆, 별도 줄) -5. **AI 가 추출한 due_date 가 틀렸을 때 사용자가 어떻게 알아채나?** — 카피·색조로 confidence 차이를 충분히 전달할 수 있나 -6. **만료된 노트를 별도 필터로 보고 싶은가, 아니면 인라인 처리만으로 충분한가?** - ---- - -## 8. 단계별 산출물 - -본 spec 정식화 후 plan 작성 시 예상 task 묶음 (본 stub 시점에선 추정만): - -- **데이터:** migration v2, NoteRepository 시그니처 확장 (단위 테스트 포함) -- **추출:** `DateExtractor` 모듈 (정규식 + 자체 KST 변환). 골든 픽스처 50건. AI 위임 분기 라우팅 -- **AI:** schema/prompt 확장, AiWorker 응답 매핑 변경 -- **UI:** NoteCard 라벨 슬롯, EditableField 재사용, 만료 색조 -- **마이그레이션 안전성:** 기존 노트의 `due_date_edited_by_user = 0` 디폴트, `due_date NULL` 처리 검증 - ---- - -## 9. Slice 와의 관계 - -본 spec 은 **슬라이스 v0.4 와 독립**이다. 슬라이스 dogfood 중 발견된 피드백이 출처지만, 슬라이스 종료 조건에는 포함되지 않는다. - -- 슬라이스의 3 가설 (3초 마찰 / AI 품질 / 행동 루프) 중 어느 것도 due_date 없이 검증 가능 -- migration v2 + 컬럼 2개 + UI 슬롯 = 작지 않은 변화. 슬라이스 invariant 2 ("AI 재실행은 user-edited 필드 덮어쓰기 금지") 의 적용 표면을 확장하므로 슬라이스 안정성이 먼저 검증된 상태에서 진입하는 게 안전 -- 본 spec 의 정식화·실행 시점은 별개 결정 — 누적 표본으로 H1 충족이 확인되면 진입 - ---- - -## 10. 라이브러리 후보 - -| 라이브러리 | 평가 | -|-----------|------| -| `chrono-node` | 영어·한국어 일부 지원, 활성. 한국어 커버리지가 핵심 표현 외에는 약함 | -| 자체 정규식 | 핵심 표현 (`오늘`, `내일`, `N일 뒤`) 만 6~8개 패턴으로 충분. 결정론적·테스트 쉬움. 권장 | -| `dayjs` + 사용자 정의 파서 | 한국어 표현은 직접 처리, 시간대 변환은 dayjs 의 `tz` 플러그인. 의존성 증가 | - -권장: **자체 정규식 + Intl.DateTimeFormat + KST 변환 헬퍼.** 외부 의존성 추가 없음, 슬라이스의 ContinuityService 가 이미 KST 변환 로직 보유 → 재사용. - ---- - -## 부록 A. 골든 픽스처 후보 (정식화 시 확장) - -``` -"오늘 PR 리뷰" → null 또는 today (양가) -"내일 회의 준비" → tomorrow -"다음 주 월요일까지 슬라이드" → next-mon -"3일 뒤 데모" → today+3 -"5월 1일 휴가 신청" → 2026-05-01 -"이번 주 안에 리팩터링" → 이번 주 일요일 -"월말 마감" → 해당 월 말일 (AI 위임 후보) -"주말까지" → 이번 주 일요일 (AI 위임 후보) -"퇴근 전" → today (시각 단위는 본 spec 범위 밖이지만 fallback) -``` - -각 케이스의 confidence 등급 + 사용자 가설 행동(수락 / 편집 / 무시)을 본 spec 정식화 시 본인이 직접 라벨링한다. 이 라벨링이 H2 / H3 측정의 근거. - ---- - -## 부록 B. 미정 정책 - -- "오늘" 의 시간대 — 자정 기준인가, 24시간 후인가? (KST 자정으로 잠정 결정, 정식화 시 검증) -- 사용자 명시적 due_date 와 AI 추출 due_date 의 시각적 구분 (현재 안: AI 라벨 색조) -- due_date 검색·필터 UI — 본 spec 범위 vs 별도 spec