Commit Graph

26 Commits

Author SHA1 Message Date
altair823
c9ccf6433f feat(notify): NotificationService with 4 rotating reward copies
Task 15 of the slice plan. Strategy §4.1 immediate-reward
toast. celebrate(noteId) deterministically picks one of the
4 reward copies via SHA-256(noteId)[0] % 4, then forwards to
the injected send() callback (which Task 30 wires to a real
electron Notification). Skips silently when isSupported() is
false (denied OS permission), and swallows send() errors so
that capture path never fails because of a notification quirk.

Verification: `npx vitest run tests/unit/NotificationService.test.ts`
3 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:11:44 +09:00
altair823
38a54a83b8 feat(capture): CaptureService with enqueue + celebrate hooks
Task 14 of the slice plan. Orchestrates a Quick Capture submit:
trims/validates input, creates the note row + pending_jobs row
in one repo.create transaction, persists each pasted PNG via
MediaStore (relPath stored in media table), then awaits the
injected enqueue() (AiWorker.enqueue at runtime) and fires
celebrate() (NotificationService.celebrate). deleteNote drops
the db row (cascading note_tags / media / pending_jobs) and
removes the on-disk media directory afterwards.

Verification: `npx vitest run tests/unit/CaptureService.test.ts`
4 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:11:43 +09:00
altair823
0c38fcaf85 feat(ai): AiWorker with sequential queue, 3-attempt backoff
Task 13 of the slice plan. Drives the pending → done/failed
transitions:
- enqueue() pushes a Job and kicks the loop; loadFromDb()
  rehydrates pending_jobs at startup so app restart resumes
  in-flight work.
- drain() exposes a Promise for tests + graceful shutdown.
- Concurrency 1: a single async loop awaits each provider call
  before the next, matching spec §2.2.
- 3-attempt backoff (default [0, 30s, 120s]; tests inject [0,0,0]).
  Each failure logs ai.retry, increments pending_jobs.attempts,
  and on the final attempt calls markAiFailed and emits onUpdate.
- emit() pushes the freshly-hydrated note to onUpdate (used by
  Task 30 to fan out IPC note:updated events).

Verification: `npx vitest run tests/unit/AiWorker.test.ts`
4 passed. Suite total 41 / 41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:10:15 +09:00
altair823
095413ed92 feat(ai): LocalOllamaProvider with 120s timeout + integration harness
Task 12 of the slice plan. Implements the slice's only provider:
- generate(): POST {endpoint}/api/generate with Korean-first
  prompt; AbortController-driven 120s timeout; parses
  body.response as JSON and runs it through parseAiResponse.
- healthCheck(): GET /api/tags; returns ok when configured model
  is in the listing, otherwise reports the missing-model reason.
- Constructor takes opts.endpoint / opts.model so Task 30 main
  entry can inject INKLING_OLLAMA_ENDPOINT for LAN dogfood;
  defaults are http://localhost:11434 and gemma4:e4b.

Tests: 6 unit cases via undici MockAgent (parse, non-JSON,
timeout abort, healthCheck ok / missing / connection error).
Integration test gated by INKLING_INTEGRATION=1 hits real
Ollama for Korean / English-stack / mixed input cases with a
180s per-test budget — also reads INKLING_OLLAMA_ENDPOINT so
LAN dogfood can be exercised end-to-end.

Plan deviation: undici@8 types treat MockAgent's `.reply()`
callback as sync-return-only, so the 500ms-delayed reply used
in the timeout test is cast `as never` to bypass the overload
mismatch. Behavior is correct at runtime; the cast is local to
the test.

Verification: `npx vitest run tests/unit/LocalOllamaProvider.test.ts`
6 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:10:15 +09:00
altair823
e7f1d8fd75 feat(ai): InferenceProvider interface
Task 11 of the slice plan. Single-method provider contract
(name + generate + healthCheck) so future LAN / external API
implementations can drop in without touching AiWorker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:10:15 +09:00
altair823
e0713c94e6 feat(ai): zod schema validator + Korean-first prompt
Task 10 of the slice plan. parseAiResponse runs the model JSON
through a zod RawResponseSchema, then enforces slice rules:
- title MUST contain Korean characters; throw otherwise.
- summary normalized to exactly 3 lines (pad with empty when too
  few; collapse trailing lines into line 3 when too many).
- tags filtered to /^[a-z0-9]+(-[a-z0-9]+)*$/ kebab-case and
  capped at 3.
buildPrompt assembles the user-facing prompt with explicit
Korean-output / kebab-tag / no-fence rules (PROMPT_VERSION=1).

Verification: `npx vitest run tests/unit/ai-schema.test.ts`
7 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:10:14 +09:00
altair823
c0ee8c0981 feat(continuity): WeeklyContinuity service (7 notes/week, recovery detection)
Task 9 of the slice plan. Computes the data behind the Inbox
ContinuityBadge and recovery toast (Strategy §5):
- KST Mon-Sun grouping via toKstDateKey/kstMondayOf helpers
  (UTC + 9h shift, then bucket to local Monday).
- weekCount + weekTarget=7 + consecutiveCompleteWeeks (walk
  backward from current week, or previous if current incomplete,
  counting only weeks with >=7 notes).
- showRecoveryToast=true when the latest note lands on today
  (KST) and the prior note is at least 7 days earlier — this is
  the "흐름을 다시 이어갑니다" trigger.
- now() injected for deterministic tests.

Verification: `npx vitest run tests/unit/ContinuityService.test.ts`
6 passed. All 24 unit tests across migrations / NoteRepository /
MediaStore / ContinuityService green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:06:45 +09:00
altair823
9c47ff659f feat(media): MediaStore for image persistence and cleanup
Task 8 of the slice plan. Saves clipboard PNG/JPEG bytes under
{profileDir}/media/{noteId}/{uuid}.{ext} with mkdir -p semantics,
returns relPath/mime/bytes for the repository row, supports
deleteNoteDirectory (used by note delete + GC) and listNoteDirs
(used by media GC to find orphans).

Verification: `npx vitest run tests/unit/MediaStore.test.ts`
4 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:06:45 +09:00
altair823
797d97c392 feat(repo): NoteRepository with intent, edited flags, AI overwrite guard
Task 7 of the slice plan. Implements the full repository surface
backing every IPC inbox/capture path: create (UUID v7 + atomic
notes + pending_jobs insert), insertMedia, findById/list,
updateAiResult (CASE WHEN guard against title/summary
overwrite when *_edited_by_user flips), markAiFailed (truncates
ai_error to 500 chars + clears pending job), updateUserAiFields
(sets edited flags as a side effect, replaces user-source tags),
setIntent + dismissIntent (intent_prompted_at uses COALESCE so
the first stamp wins), delete, getPendingCount,
getAllPendingJobs, incrementJobAttempt, and a private hydrate
that joins notes with note_tags + media.

Plan deviation: list/list-with-cursor query gets a secondary
"id DESC" tiebreaker. Two notes created in the same millisecond
shared created_at and reordered nondeterministically; UUID v7
sorts monotonically with creation order, so id DESC restores
"newest first" within ties.

Verification: `npx vitest run tests/unit/NoteRepository.test.ts`
12 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:06:45 +09:00
altair823
114e971518 feat(db): v1 schema with user_intent + edited flags
Task 6 of the slice plan. Adds the initial database surface:
- m001_initial: notes (with v0.2 columns user_intent,
  intent_prompted_at, title_edited_by_user,
  summary_edited_by_user), tags, note_tags (with source ai/user),
  media, pending_jobs.
- migrations/index: forward-only PRAGMA user_version runner that
  applies pending migrations inside a single transaction.
- db/index: openDb() that opens better-sqlite3, enables WAL +
  foreign_keys, then runs migrations.
- migrations.test: schema columns are present at v1; runMigrations
  is idempotent.

Verification: `npx vitest run tests/unit/migrations.test.ts`
2 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:02:04 +09:00
altair823
8f174cb569 feat(paths): per-profile directory resolver
Task 5 of the slice plan. resolveProfilePaths(profile='default')
returns { profileDir, dbFile, mediaDir } under
{userData}/Inkling/profiles/<profile>/ and ensures mediaDir exists
via recursive mkdirSync. Slice scope is single profile 'default'
(spec §1.1, §2.4).

Verification: `npm run typecheck` exits 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:00:55 +09:00
altair823
71eff35d68 feat(logger): add electron-log with PII-safe helpers
Task 4 of the slice plan. Adds initLogger() (rotates daily logs
under {userData}/Inkling/logs at 5MB cap, info level by default
and debug when INKLING_DEBUG=1), a hashPrefix() helper for
safely referencing note IDs in logs without exposing content,
and a typed logger facade with info/warn/error/debug methods.
Wires initLogger() + an app.start info entry into the
whenReady hook.

Verification: `npm run typecheck` exits 0. Runtime log file
creation deferred to live `npm run dev` once Tasks 3+19 land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:00:55 +09:00
altair823
c480cc0ace feat(preload): expose typed InklingApi (v0.2: intent + continuity)
Task 3 of the slice plan. Replaces the minimum @shared/types
augmentation with the full domain model: Note (with v0.2
intent + edited-flag fields), NoteTag, NoteMedia, AiStatus,
WeeklyContinuity, CaptureApi, InboxApi, and the InklingApi
aggregate exposed on window.inkling. Adds the preload bridge
that wires every InklingApi method to its IPC channel via
contextBridge.exposeInMainWorld.

Verification: `npm run typecheck` exits 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:59:39 +09:00
altair823
4b16b873c6 feat(main): add Electron entry + Inbox window shell
Task 2 of the slice plan. Creates the minimal Electron main
process: app entry that opens an Inbox BrowserWindow on
whenReady, an inboxWindow module that handles show/hide/close-
to-tray semantics, an HTML placeholder renderer ("Inkling Inbox
(renderer pending)"), and the minimum @shared/types augmentation
for app.isQuitting (Task 3 expands this file).

Plan tsconfig template adjustment: TypeScript 6 deprecates
baseUrl. Dropped it and made paths entries explicitly relative
("./src/...") so they resolve from tsconfig.json's directory.
Updated both the in-repo tsconfig.json and Task 1 Step 3 in the
plan to match.

Verification: `npm run typecheck` exits 0. Full `npm run dev`
sanity check is deferred until Task 3 (preload) and Task 19
(quickcapture HTML) land — electron-vite.config.ts wires both
entries that don't yet exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:58:59 +09:00
altair823
5abec8e7d9 chore: bootstrap inkling project (Task 1)
Adds package.json, tsconfig.json, tsconfig.node.json,
electron.vite.config.ts, vitest.config.ts, .gitignore, and
package-lock.json. Verified Node 24.15.0 + npm 11.12.1 via Volta.
better-sqlite3 12.9.0 native build succeeded against VS 2022 +
Python 3.12.

Plan v0.4 had two install pin issues; adjusted live and logged
in plan Task 1 Step 2:
- electron-vite@2.3.0 + vite@6.0.3 ERESOLVE (peer ^4||^5).
  Promoted build-tool chain: electron-vite 5.0.0, vite 7.3.2,
  @vitejs/plugin-react 5.1.4 (vite@7 is the compatible overlap;
  vitest@4.1.5 also accepts vite@7).
- @types/uuid@11.0.0 deprecated stub (uuid@11 ships own types);
  removed from devDependencies.

Other deps installed at the planned exact pins. Step 8 sanity:
TypeScript 6.0.3, Vitest 4.1.5, Electron 41.3.0 (npm ls).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:52:57 +09:00
altair823
82137a5ce1 Bump spec + plan to v0.4: downsize gemma4:26b → gemma4:e4b
User reassessed model needs: 26b is overkill for slice-scope tasks
(short Korean title + 3-line summary + ≤3 kebab-case tags). e4b
(8.0B Q4_K_M, efficiency variant) sits closest to the original
"표준 9b" tier in spec §6.5 and gives faster responses with
adequate margin for the slice's simple structuring work.

Spec changes:
- Header bumped to v0.4 with new revision line.
- §1.1 retiers AI 파이프라인 from "고품질 26B" to "표준 e4b".
- §2.1 diagram, §3.1 ai_provider example, §4.1 provider name,
  §4.2 request body, §4.6 health check, §5.4 copy catalog: model
  string swapped 26b → e4b.
- §6.4 perf target restored to <30s provisional (e4b is similar
  scale to original 9b reference, 60s margin no longer needed).
- §8 open issue rewritten: e4b is the standard-tier substitute,
  with 26b/e2b documented as quality-up / speed-up fallback paths
  if dogfood reveals issues with Korean output or tag formatting.

Plan changes:
- Header bumped to v0.4 with restated Goal.
- Architecture summary, Task 12 unit test mocks/expectations,
  LocalOllamaProvider default model, copy banner, Task 33 dogfood
  prerequisite: 26b → e4b throughout.

Tier ladder for dogfood escalation (from §8):
  e2b (5.1B, speed) ← e4b (8.0B, default) → 26b (25.8B, quality)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:46:27 +09:00
altair823
0541d44b7e Bump spec + plan to v0.3: gemma4:26b + LAN Ollama endpoint
Model swap: gemma4:9b has no official checkpoint; LAN server
(192.168.0.47:11434) ships gemma4:26b (25.8B Q4_K_M), which fits the
§6.5 26B cap and the "high-quality" tier. Updates §1.1, §2.1 diagram,
§3.1 ai_provider example, §4.1 provider name, §4.2 request body, §4.6
health check, §5.4 copy catalog, plan Task 12 (mock + default model),
Task 30 main entry, Task 33 dogfood intro.

Endpoint: LocalOllamaProvider now configurable via constructor opts.
Task 30 main entry and integration test read INKLING_OLLAMA_ENDPOINT
from env; unset falls back to localhost. LAN Ollama moves from
out-of-scope to in-scope via endpoint injection; only a separate
LanOllamaProvider class remains deferred.

Perf target §6.4: RTX 3060-referenced <30s target no longer valid
under 26B Q4_K_M + LAN. Set to <60s provisional; §8 adds a new open
issue to measure real p95 during first dogfood week and confirm.

§7.4 adds INKLING_OLLAMA_ENDPOINT env setup and recommends Volta over
nvm-windows for Windows Node 24.15.0 install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:39:56 +09:00
ccc166917a Record native-Windows-vs-WSL decision and LAN Ollama implication
Adds to handoff: rationale for choosing native Windows over WSL2
(global hotkey, OS notifications, clipboard image, tray, data path,
performance measurements all break under WSLg). Pre-flight
checklist updated so Ollama runs on a LAN server, not on the
Windows machine. Notes the one-line env-var change needed in Plan
Task 30 to wire LocalOllamaProvider to a LAN endpoint, and clarifies
that this is configuration (not a new Provider class).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 02:15:10 +00:00
ea8791848c Add Linux→Windows session handoff doc
Captures decisions made across both /brainstorming rounds (initial
slice scope + Strategy integration), user profile, project state,
Windows-specific gotchas (build tools, hotkey conflicts, Ollama,
file paths), load-bearing invariants, and suggested first messages
for the new Claude Code session on the dogfood machine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 02:08:39 +00:00
a5afdc5861 📝 docs(strategy): Inkling 심리학 기반 습관화 전략 문서 추가
- Inkling의 습관화 전략을 설명하는 기획서 추가
- 메모 습관 형성을 위한 심리학적 접근 방안 제시
- 사용자 경험을 향상시키기 위한 다양한 전략과 템플릿 제공
- AI를 활용한 메모 관리 및 팀 지식 공유 방안 포함
2026-04-25 01:50:15 +00:00
35f900b65d Rewrite implementation plan to v0.2 with Strategy integration
Adds 5 new tasks and updates existing ones to cover:
- ContinuityService (was StreakService) for Weekly Continuity (7
  notes/week, KST Mon-Sun, recovery detection >=7 day gap).
- NotificationService for OS native post-submit reward toast (4
  rotating copies, deterministic by noteId hash).
- IntentService + IntentBanner component for "의미 한 줄" prompt
  after AI done (4 rotating prompts).
- RecoveryToast component with localStorage dismissal.
- AI proposal labels (gray "AI" badges on un-edited fields), tag
  source subscript, edited-by-user flags guarding AI overwrites.
- Updated copy throughout (recovery-friendly, no failure language).
- New schema columns: user_intent, intent_prompted_at,
  title_edited_by_user, summary_edited_by_user.
- Updated IPC: setIntent/dismissIntent/getContinuity.
- Tray copy ("기억 구출하기" / "구출한 메모 보기").
- Updated dogfood checklist with all v0.2 features.

Plan now has 33 tasks (was 28). All tasks remain bite-sized with
TDD discipline (red → green → commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 01:46:20 +00:00
c7bfd15aee 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>
2026-04-25 01:35:54 +00:00
4b25ba3b11 Add vertical slice implementation plan
28-task TDD plan covering project bootstrap, data layer, AI pipeline,
Quick Capture + Inbox UI, tray, media GC, and E2E smoke test. Each
task is bite-sized with red/green/commit discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:26:00 +00:00
cdb329e789 Pin Node 24.15.0 LTS via .nvmrc
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:15:52 +00:00
e22194bf39 Add tech stack and version policy section to vertical slice spec
Pin Node 24 LTS (Krypton) as runtime. Document latest-stable-major
policy for dependencies with initial pin candidates as of 2026-04-24,
and .nvmrc-based version synchronization for dev/CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:15:32 +00:00
bbbe292fc6 Add vertical slice design spec for Inkling MVP
Initial design document covering the first end-to-end slice of Inkling:
Quick Capture, SQLite storage, Local Ollama AI pipeline (gemma4:9b),
and Inbox with inline editing. Scoped for dogfood on Windows/macOS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:12:21 +00:00