신규 spec 파일 추가 (구현 결과 반영). dogfood-feedback.md 의 F7
헤더 🔬 drafting → 🚀 promoted 로 갱신. F1 spec 의 후속 리스트에
F7 링크 추가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flow 반전 (F7-D 채택):
- 기존: rule.iso ?? ai.dueDate (rule 우선)
- 신규: ai.dueDate ?? null (AI 우선)
규칙은 parseAllCandidates 로 모든 매치를 추출 → prompt 에 후보
힌트로 주입. AI 가 종합 판단. AI 실패 시 due_date null (별 fallback 없음).
해결되는 케이스: '내일 모레' → AI 가 ambiguous 인지 → null.
PROMPT_VERSION → 3. GenerateInput.dueDateCandidates 신규.
buildPrompt(rawText, todayKst, candidates) — 빈 배열일 때 hint 섹션 생략.
Tests:
- AiWorker.test.ts — 'rule priority' 테스트 → 'AI dueDate wins' flip
- AiWorker.test.ts — passes todayKst 테스트 확장 (dueDateCandidates 도 검증)
- AiWorker.test.ts — 신규 'passes parseAllCandidates result as dueDateCandidates'
- LocalOllamaProvider.test.ts / ollama-golden.test.ts — generate 호출에 dueDateCandidates: [] 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
기존 parseDueDate (first-match-wins) 는 backward compat 로 보존.
parseAllCandidates 가 모든 high/medium 매치를 text 순서로 반환 — F7
AI-primary flow 의 prompt 후보 주입 입력으로 사용.
Overlapping-span suppression: 다음 주 월요일 같은 케이스에서 rule 10
(전체) 이 rule 13 (다음 주 alone) 을 포함하면 후자 매치 제거.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
8 항목 dogfood-feedback 로드맵 (F1·F2·F3·F4-E·F5·F6-L1·F6-L2·F6-L3·F4-C·F)
한 번에 흡수. migration v2 (due_date) + pre-migration snapshot.
단위 테스트 52 → 197. 신규 npm dep 0, 시스템 dep 추가는 git CLI (이미 사전 요구).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F4 의 6개 cue 메커니즘 중 외부 신호 없이 즉시 구현 가능한 두 가지
(C 환경 앵커, F 정체성 고리) 를 묶어 promoted spec 으로 추출. A (잠금
hook), D (variable interval prompt), B (ambient if-then) 는 dogfood
soak 측정 결과를 본 뒤 결정.
F4 헤더를 🌱 raw → 🔬 drafting (C·E·F promoted) 로 갱신하고, F4 진행
상태에 두 promoted 경로를 명시.
F4-C·F (cue 강화) — Inkling 의 두 표면에 정체성 신호를 추가.
F4-C 환경 앵커 (tray):
- nativeImage 색 변경은 미지원이므로 색 뱃지 대신 tooltip + 메뉴 첫
비활성 라벨로 대체. tooltip 은 항상 `Inkling — 오늘 N`,
메뉴 첫 항목은 N>0 일 때 `오늘 N번 잡아둠` (비활성).
- `tray.ts` 가 `refreshTray(todayCount)` 를 export 하여 main 이
60s interval + AiWorker.onUpdate hook 에서 갱신을 트리거.
- N=0 일 때는 라벨을 띄우지 않아 메뉴가 자연스럽게 시작.
F4-F 정체성 고리 (Inbox 헤더):
- ContinuityBadge 옆에 새 IdentityCounter 컴포넌트.
- N>0 → `오늘 N번 잡아뒀다` (정체성 강화 카피).
- N=0 → `오늘은 처음 한 줄?` (priming 카피로 첫 캡처 유도).
- 갱신은 `loadInitial` / `refreshMeta` (focus + note:updated) 경로
공유 — 별도 IPC subscription 없음.
Wiring:
- `NoteRepository.countToday()` 를 `inbox:todayCount` IPC 로 노출.
- preload bridge `getTodayCount`, `InboxApi.getTodayCount()` 타입.
- 스토어에 `todayCount: number` 필드 추가, 두 메타 fetch 경로 모두에서 갱신.
스키마 변경 없음. 197/197 unit pass, 1/1 e2e pass.
For the F4-C·F cue strengthening surfaces (tray tooltip + Inbox identity
counter), main + renderer need a single source of truth for "오늘 N번
잡아뒀다". Implements `NoteRepository.countToday(now?)` that computes the
half-open UTC interval covering the KST calendar date of `now` and counts
rows whose `created_at` falls inside.
`now` is injectable for deterministic tests across the KST/UTC boundary
(02:00 KST and 23:00 KST land on different UTC dates yet the same / a
different KST day). Four new cases cover empty DB, KST-day filtering,
KST-midnight crossover, and the default-arg branch.
- 신규 spec docs/superpowers/specs/2026-04-26-f6-l2-git-sync.md
결정 표 + 범위 + 시스템 의존 + 동작 요약 + 후속 후보
- 2026-04-25-dogfood-feedback.md F6 진행 상태 line 갱신:
L2 — 🌱 raw → 🚀 promoted (MVP)
- README.md '원격 백업 (선택, F6-L2)' 섹션:
일회 설정 (git init + remote add + 자격증명) + 사용법
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F6-L2 MVP 의 오케스트레이터.
- isConfigured(): syncDir 가 git repo + origin remote 있을 때만 true
- sync():
1) ExportService.export(<syncDir>, includeMedia: true) — F5 트리 그대로 덮어쓰기
2) git add -A
3) git commit -m "chore(notes): sync <ts>"
4) "nothing to commit" 이면 changed=false 로 정상 반환
5) git push (upstream 미설정이면 -u origin <branch> 자동)
- GitClient.push() 에 hasUpstream() + 자동 -u 추가 (첫 push 케이스)
- 5 vitest cases — bare local remote 로 push 검증, 두 번째 sync 는 변경 없음 확인
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
얇은 git CLI 래퍼. F6-L2 sync MVP 의 빌딩 블록.
- run/isRepo/hasRemote/addAll/commit/push/currentBranch
- commit() 은 "nothing to commit" 을 changed=false 로 구분 (정상 path)
- 그 외 실패는 throw, exitCode + stderr 보존
- 8 vitest cases — empty file 로 GIT_CONFIG_GLOBAL/SYSTEM 격리
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
strategy.md §1 heading and core sentence reframed to '머릿속에서
꺼내 두기' as the load-bearing user action (was '기억 구출').
§4.1 보상 카피 list updated to match shipped copy. Brief Zeigarnik-
effect rationale added per F4-E. §7 example updated.
Slice spec §5.5 카피 테이블 4개 항목 갱신 — code 와 spec 의 카피
드리프트 차단.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Marks F2 in dogfood-feedback as 🚀 promoted and adds the standalone
spec at docs/superpowers/specs/2026-04-26-f2-tag-click.md capturing
the mini-brainstorm decisions, scope, and follow-ups (multi-tag
filter, rename/merge, source preservation on undo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits the tag chip into two actions per F2 dogfood feedback:
- short click on chip text → applies the tag to the inbox filter
(Inbox header shows "🔎 필터: #tag (n개)" banner with ✕ 해제 button)
- × button on chip → immediately removes the tag and surfaces a
module-level pub/sub undo toast for 5 seconds; clicking the toast
restores the tag
`TagUndoToast` is a tiny self-contained component: `pushTagUndo()` from
NoteCard publishes an entry, the mounted `<TagUndoToast />` near the
end of `<App>` subscribes and renders it. Auto-dismiss after 5 s,
click-to-undo cancels the timer and runs the restore callback.
AI vs user tags share the same behaviour — only the chip background
colour distinguishes them, matching the F2 decision table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `tagFilter: string | null` and `setTagFilter` to the inbox store, plus
an extracted pure `selectFilteredNotes` selector so unit tests can import it
under vitest's `node` environment without dragging `api.ts` (which touches
`window.inkling` at module load).
Tests cover four cases: null filter passes through, single-tag match,
no-match empty result, and any-tag-matches semantics.
F2 dogfood feedback step 1/3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracted to own spec with mini-brainstorm decisions captured.
F1 in dogfood-feedback.md marked 🚀 promoted with link.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline 📅 YYYY-MM-DD badge appears in NoteCard between summary and
tags. Click to edit (HTML date input). Past dates: gray + line-through.
AI label shown when not user-edited (mirrors title/summary AI badge
policy). Empty state shows '📅 마감일 추가' link in gray.
New IPC inbox:setDueDate routes to NoteRepository.setDueDate which
sets due_date_edited_by_user=1 (per slice §1.1 invariant 2 — user
edit blocks future AI overwrite). Preload bridge + InboxApi type
extended.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GenerateInput gains todayKst field. AiWorker computes KST-aligned
date once per job, runs parseDueDate on rawText, calls provider.generate
with todayKst, then merges: rule.iso wins if matched (deterministic),
else AI's due_date, else null. Logs dueDateSource (rule|ai|none) for
debugging. now() injection for testability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AiResponse extends with dueDate: string|null. zod regex
^\d{4}-\d{2}-\d{2}$, follow-up roundtrip check coerces invalid
dates (2026-13-99 etc.) to null. PROMPT_VERSION → 2: prompt now
takes todayKst arg, asks model to extract due_date as ISO or null.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regex + KST math, returns ISO YYYY-MM-DD or null. 14 high-confidence
rules (literal date, N월 N일, MM/DD, N일/주/개월 뒤, 모레/내일/글피/오늘,
다음/이번 주 X요일, 다음 달). Ambiguous tokens (월말, 주말, 퇴근 전,
시각) return iso=null + confidence='medium' so caller (AiWorker)
can defer to AI. 26+ golden fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-state outcome per note: inserted (new id), skipped (id+rawText
match), forked (id match but rawText differs → new uuidv7 to preserve
raw_text invariant from slice §1.1). Media files copied into
MediaStore convention <profileDir>/media/{noteId}/{n}.{ext} with
new media DB rows.
NoteRepository.importNote handles full provenance: ai_status='done',
ai_provider, ai_generated_at, edited flags, intent fields, tags
with source preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracted to own spec with mini-brainstorm decisions captured.
F5 in dogfood-feedback.md marked 🚀 promoted with link.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tray now has 4th callback that opens directory chooser, exports all
notes via ExportService with includeMedia=true default. Dialog
message warns about raw_text plain-text + recommends private location.
Native toast on success/failure with note + media counts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ExportService composes pure exportFormat layer + reads from
NoteRepository.listAll (new, asc-ordered) + MediaStore.absolutePath
(new helper). Writes notes/{date-id8-slug.md}, media/{id8__n.ext},
index.jsonl, manifest.json, README.md to user-picked dir.
6 unit tests against tmp dirs + :memory: DB.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
final reviewer 가 식별한 forward-looking polish 4건을 후속 리스트에
명시. 4번째는 F1 (Due Date 마이그레이션 v2) PR 시 즉시 반영 권장:
openDb() 가 마이그레이션을 BackupService 인스턴스화 전 호출하므로
마이그레이션 결함 시 첫 실행 직전 상태 회수 불가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracted to its own spec, dogfood-feedback.md F6 header reflects
L1 promoted status while L2/L3 remain raw.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Instantiate BackupService at app.whenReady, run daily snapshot then
again before quit (synchronous-blocking via preventDefault). Tray menu
gets '지금 백업' entry that triggers manual runDaily with native
toast feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skips when marker matches today's KST date. Marker written after
successful snapshot, before rotation. lastSnapshotAt() exposed for UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review I1: wrap snapshot's backup+rename in try/catch, unlink
orphan tmp file on failure so the next run does not encounter a
confusing 'existing file is not a database' error from sqlite.
Code review I2: JSDoc note that snapshot() is not safe for concurrent
calls — callers should serialize via runDaily()'s marker.
New unit test injects a fake db whose backup() rejects after a partial
write, asserts no .tmp / .sqlite remains.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code reviewer minor nitpicks:
- Add test for inkling-2026-02-30.sqlite (locks roundtrip-validation contract)
- Add test for weekly window inclusive at oldest boundary
- Precompute today=startOfDayUtc(now) once outside the loop, pass to helpers
No behavior change; tests added cover existing semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec reviewer flagged the weekly window keeps 5 Mondays (anchor + 4
prior), not 4 as the plan prose said. Code preserved (tests pass);
constant renamed and comment made honest about the actual semantic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 1: 순수 GFS retention 함수 + 7 단위 테스트
Task 2: BackupService.snapshot() — KST 날짜·tmp+rename 원자성 + 6 단위 테스트
Task 3: runDaily() — .last-snapshot 마커 + lastSnapshotAt + 7 단위 테스트
Task 4: main/index.ts wiring (whenReady + before-quit) + tray '지금 백업'
Task 5: F6-L1 promotion (별 spec 분기 + dogfood-feedback.md 상태 갱신)
backup 위치: <profileDir>/backups/ (mini-brainstorm 결과 A 채택).
스키마 변경 0, 외부 dep 0. better-sqlite3.backup() API 가정.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8개 항목 순차 작업 (F6-L1 → F5 → F6-L3 → F1 → F2 → F3+F4-E →
F6-L2 → F4-C·F) + 데이터 안전 우선 + 머지+테스트 게이트 + 단일
v0.2.1 cut 후 dogfood 재설치 + 1주 soak. F4-A·D 는 측정 후
별 brainstorm 으로 deferred. 항목별 mini-brainstorm 에서
decision-pending 답변하는 라이프사이클.
본 spec 은 순서·범위·게이트만 정의, 항목 내부 설계는 per-item.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>