v0.3.1 Cut F — 멀티모달 vision AI (F24) #31

Merged
altair823 merged 13 commits from worktree-v031-cut-f-vision into main 2026-05-10 03:10:38 +00:00
Owner

Summary

v0.3.1 Cut F — F24 (이미지 멀티모달 AI 분석). semver patch — 새 vision 기능 추가, 기존 text-only 흐름 영향 X (GenerateOptions? optional 로 backward compat). Ollama vision 모델 (gemma3 family default) 활용 + capability detection (app launch + manual refresh) + Configure UI + AiWorker integration (5MB cap + base64 변환). 'both' 자동 fallback 과 'skipped' enum 은 v0.3.2+ deferred.

  • F24-A capability detection: VisionDetect.isVisionCapable(model) (family/families/name 3-layer) + refreshVisionCache(deps) (Ollama /api/tags 결과 cache). app launch 시 fire-and-forget + 설정 페이지 manual refresh 버튼.
  • F24-B Configure UI: AiProviderSection 안 VisionSection 추가 — cache 기반 dropdown + 다시 감지 버튼 + 마지막 감지 시각. 빈 cache 시 "감지된 모델 없음" 안내.
  • F24-C InferenceProvider 확장: GenerateInput.images?: Array<{ base64; mime }> + GenerateOptions.visionModel? (둘 다 optional — 기존 호출자 무영향). LocalOllamaProvider.generate(input, opts?)useVision = !!visionModel && images.length > 0 분기 — vision 시 model = visionModel, prompt = buildVisionPrompt, body.images = base64[].
  • F24-D AiWorker integration: note.media + visionModel 둘 다 충족 시 vision path. 이미지당 5MB cap (final review fix: note.media[].bytes fast-fail — readFile/base64 전 차단). visionModel null 시 기존 text-only 회귀 검증.
  • single write path 강제 유지 (Cut C/D/E 정책): vision 결과는 기존 repo.updateAiResult() 경유 — notes_fts / note_revisions / note_tags 직접 mutation 없음. 4-path invariant 보존.
  • Cut E gap fix 포함: ImportService.test buildExportNote helper 가 frontmatter 5 필드 (Cut E 추가) 누락 → composeFrontmatter 가 undefined moveReason 받아 throw. helper 에 default 추가.

변경 내역 (12 commits)

Phase 0: 문서

  • 7a56184 docs(plan) — Cut F plan + spec 정정 (단위 679, SettingsService 개별 메서드, 'skipped' enum 미도입, fallback 미구현)

Phase 1: settings + detection

  • 463be7c Task 2 — SettingsService.{getVisionModel,setVisionModel,getVisionCapableCache,setVisionCapableCache}
  • 3eb0ef1 Task 1 — VisionDetect.{isVisionCapable,refreshVisionCache} (fetch 주입)

Phase 2: provider/prompt

  • e2e8b9b Task 3 — buildVisionPrompt + GenerateInput.images? + GenerateOptions.visionModel?
  • 369d418 Cut E gap fix — ImportService.test buildExportNote 5 필드 default
  • 5012b40 Task 4 — LocalOllamaProvider vision path

Phase 3: AiWorker + IPC

  • 2179cfb Task 5 — AiWorker vision integration (note.media + 5MB cap)
  • d03098c Task 6 — vision IPC + preload (3 채널)

Phase 4: UI + 자동 호출

  • 72e9b68 Task 7 — VisionSection UI (dropdown + 다시 감지)
  • 7468217 Task 8 — main refreshVisionCache whenReady fire-and-forget

Phase 5: release + final review fix

  • 7b53640 chore(release) — v0.3.1
  • 81fae12 fix(v031) — endpoint resolution helper + 5MB fast-fail (final review)

테스트 / 빌드

  • 단위: 679 → 710 pass (+31):
    • VisionDetect 9 (5 isVisionCapable + 4 refreshVisionCache)
    • SettingsService vision 4
    • visionPrompt 2
    • LocalOllamaProvider vision 3
    • AiWorker vision 3
    • vision IPC 5
    • VisionSection 4
    • ImportService helper fix 5 (Cut E gap 회복)
    • 기존 LocalOllamaProvider/AiWorker 회귀 PASS
  • typecheck: 0 errors
  • e2e: 세션 내 미수행 — UI 변경 = SettingsPage AiProviderSection 안 VisionSection — capture/onboarding/banner flow 무관. 머지 후 main 에서 검증 권장
  • 산출물: 후속 release 단계에서 Windows exe + macOS dmg + Linux AppImage/deb

Schema 변경

m007 (Cut D) 이후 schema 변경 없음 — Cut F 는 settings.json 의 zod schema 만 확장 (vision_model / vision_capable_cache / vision_cache_at). DB schema 무영향.

메모리 정책 갱신 (Cut F 머지 후 적용)

  • Vision capability detection 패턴: family/families/name 3-layer + Ollama /api/tags cache. dogfood 시 false-positive 발견 시 family set 보강
  • 5MB image cap: vision 모델 base64 메모리 폭주 방지. fast-fail (note.media[].bytes) 로 readFile 전 차단
  • endpoint resolution unification: settingsApi 도 index.ts 의 resolvedEndpoint 패턴 (settings → env → default) 사용 — dev 환경 manual refresh 호환
  • 'sync' / 'skipped' enum 미도입 일관: Cut C 의 'sync' (raw_text 변경 source) + Cut F 의 'skipped' (vision unavailable) 모두 m마이그레이션 회피. 기존 enum 재활용 ('user' / 'failed')
  • Single write path 강제 유지: vision 결과 = updateAiResult 경유. 새 entry path 추가 없음

Risk 잔재 (final review)

  • vision 모델 한국어 정확도: gemma3 family 가 한국어 약하면 다른 family 추천 갱신 (메모리 정책). dogfood 검증 필수
  • Ollama 가 vision images 무시 (capability detection false-positive): 사용자가 dropdown 에서 다른 모델 선택해 우회. 자동 fallback 미구현
  • 5MB cap 도달 시 fail/retry: fast-fail 적용했지만 retry 흐름은 동일 path 반복. 그래도 retry 마다 readFile/base64 미수행 — 비용 0
  • whenReady silent failure: Ollama offline 시 cache 빈 상태 유지 — 사용자가 "다시 감지" 직접 trigger 가능
  • e2e: 머지 후 main 에서 검증 권장 — 본 cut 의 3 IPC 채널 + VisionSection mount 만 packaged build 에서 smoke

Test Plan

  • 첫 launch — Ollama 가 동작 중이면 capability cache 자동 채워짐 (whenReady fire-and-forget)
  • 설정 페이지 → AI 제공자 → 이미지 분석 모델 dropdown — capable 모델 표시
  • dropdown 에서 vision 모델 선택 → settings 저장 + 새 capture (이미지 첨부) 시 vision path 동작
  • vision 모델 선택 + 5MB 초과 이미지 capture → fast-fail (readFile 안 됨) → ai_status='failed' 도달
  • vision 모델 선택 + 텍스트만 capture → 기존 text-only 응답 (images undefined)
  • vision 모델 비선택 + 이미지 capture → 기존 text-only 흐름 (회귀)
  • "다시 감지" 버튼 → /api/tags 새 호출 + dropdown 갱신 + 마지막 감지 시각 갱신
  • dev 환경 (settings.ollama 미설정 + INKLING_OLLAMA_ENDPOINT 만 있음) — manual 다시 감지 정상 동작 (fallback chain)
  • Ollama offline 시 자동 + manual 모두 silent / "감지 실패" 표시
  • F23 OFF (ai_enabled=false) → refreshVisionCache 가 ai_disabled 분기 (cache 유지)
  • macOS dmg / Linux AppImage/deb 빌드 + Linux VM smoke

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

## Summary v0.3.1 Cut F — F24 (이미지 멀티모달 AI 분석). semver patch — 새 vision 기능 추가, 기존 text-only 흐름 영향 X (`GenerateOptions?` optional 로 backward compat). Ollama vision 모델 (gemma3 family default) 활용 + capability detection (app launch + manual refresh) + Configure UI + AiWorker integration (5MB cap + base64 변환). 'both' 자동 fallback 과 'skipped' enum 은 v0.3.2+ deferred. - **F24-A capability detection**: `VisionDetect.isVisionCapable(model)` (family/families/name 3-layer) + `refreshVisionCache(deps)` (Ollama `/api/tags` 결과 cache). app launch 시 fire-and-forget + 설정 페이지 manual refresh 버튼. - **F24-B Configure UI**: AiProviderSection 안 `VisionSection` 추가 — cache 기반 dropdown + 다시 감지 버튼 + 마지막 감지 시각. 빈 cache 시 "감지된 모델 없음" 안내. - **F24-C InferenceProvider 확장**: `GenerateInput.images?: Array<{ base64; mime }>` + `GenerateOptions.visionModel?` (둘 다 optional — 기존 호출자 무영향). `LocalOllamaProvider.generate(input, opts?)` 가 `useVision = !!visionModel && images.length > 0` 분기 — vision 시 `model = visionModel`, `prompt = buildVisionPrompt`, `body.images = base64[]`. - **F24-D AiWorker integration**: `note.media + visionModel` 둘 다 충족 시 vision path. 이미지당 5MB cap (final review fix: `note.media[].bytes` fast-fail — readFile/base64 전 차단). visionModel null 시 기존 text-only 회귀 검증. - **single write path 강제 유지 (Cut C/D/E 정책)**: vision 결과는 기존 `repo.updateAiResult()` 경유 — `notes_fts` / `note_revisions` / `note_tags` 직접 mutation 없음. 4-path invariant 보존. - **Cut E gap fix 포함**: `ImportService.test buildExportNote` helper 가 frontmatter 5 필드 (Cut E 추가) 누락 → `composeFrontmatter` 가 undefined moveReason 받아 throw. helper 에 default 추가. ## 변경 내역 (12 commits) ### Phase 0: 문서 - `7a56184` docs(plan) — Cut F plan + spec 정정 (단위 679, SettingsService 개별 메서드, 'skipped' enum 미도입, fallback 미구현) ### Phase 1: settings + detection - `463be7c` Task 2 — `SettingsService.{getVisionModel,setVisionModel,getVisionCapableCache,setVisionCapableCache}` - `3eb0ef1` Task 1 — `VisionDetect.{isVisionCapable,refreshVisionCache}` (fetch 주입) ### Phase 2: provider/prompt - `e2e8b9b` Task 3 — `buildVisionPrompt` + `GenerateInput.images?` + `GenerateOptions.visionModel?` - `369d418` Cut E gap fix — `ImportService.test buildExportNote` 5 필드 default - `5012b40` Task 4 — `LocalOllamaProvider` vision path ### Phase 3: AiWorker + IPC - `2179cfb` Task 5 — `AiWorker` vision integration (note.media + 5MB cap) - `d03098c` Task 6 — vision IPC + preload (3 채널) ### Phase 4: UI + 자동 호출 - `72e9b68` Task 7 — `VisionSection` UI (dropdown + 다시 감지) - `7468217` Task 8 — main `refreshVisionCache` whenReady fire-and-forget ### Phase 5: release + final review fix - `7b53640` chore(release) — v0.3.1 - `81fae12` fix(v031) — endpoint resolution helper + 5MB fast-fail (final review) ## 테스트 / 빌드 - 단위: 679 → **710 pass** (+31): - VisionDetect 9 (5 isVisionCapable + 4 refreshVisionCache) - SettingsService vision 4 - visionPrompt 2 - LocalOllamaProvider vision 3 - AiWorker vision 3 - vision IPC 5 - VisionSection 4 - ImportService helper fix 5 (Cut E gap 회복) - 기존 LocalOllamaProvider/AiWorker 회귀 PASS - typecheck: **0 errors** - e2e: **세션 내 미수행** — UI 변경 = SettingsPage AiProviderSection 안 VisionSection — capture/onboarding/banner flow 무관. 머지 후 main 에서 검증 권장 - 산출물: 후속 release 단계에서 Windows exe + macOS dmg + Linux AppImage/deb ## Schema 변경 m007 (Cut D) 이후 schema 변경 없음 — Cut F 는 settings.json 의 zod schema 만 확장 (vision_model / vision_capable_cache / vision_cache_at). DB schema 무영향. ## 메모리 정책 갱신 (Cut F 머지 후 적용) - **Vision capability detection 패턴**: family/families/name 3-layer + Ollama `/api/tags` cache. dogfood 시 false-positive 발견 시 family set 보강 - **5MB image cap**: vision 모델 base64 메모리 폭주 방지. fast-fail (`note.media[].bytes`) 로 readFile 전 차단 - **endpoint resolution unification**: settingsApi 도 index.ts 의 resolvedEndpoint 패턴 (settings → env → default) 사용 — dev 환경 manual refresh 호환 - **'sync' / 'skipped' enum 미도입 일관**: Cut C 의 'sync' (raw_text 변경 source) + Cut F 의 'skipped' (vision unavailable) 모두 m마이그레이션 회피. 기존 enum 재활용 ('user' / 'failed') - **Single write path 강제 유지**: vision 결과 = updateAiResult 경유. 새 entry path 추가 없음 ## Risk 잔재 (final review) - **vision 모델 한국어 정확도**: gemma3 family 가 한국어 약하면 다른 family 추천 갱신 (메모리 정책). dogfood 검증 필수 - **Ollama 가 vision images 무시 (capability detection false-positive)**: 사용자가 dropdown 에서 다른 모델 선택해 우회. 자동 fallback 미구현 - **5MB cap 도달 시 fail/retry**: fast-fail 적용했지만 retry 흐름은 동일 path 반복. 그래도 retry 마다 readFile/base64 미수행 — 비용 0 - **whenReady silent failure**: Ollama offline 시 cache 빈 상태 유지 — 사용자가 "다시 감지" 직접 trigger 가능 - **e2e**: 머지 후 main 에서 검증 권장 — 본 cut 의 3 IPC 채널 + VisionSection mount 만 packaged build 에서 smoke ## Test Plan - [ ] 첫 launch — Ollama 가 동작 중이면 capability cache 자동 채워짐 (whenReady fire-and-forget) - [ ] 설정 페이지 → AI 제공자 → 이미지 분석 모델 dropdown — capable 모델 표시 - [ ] dropdown 에서 vision 모델 선택 → settings 저장 + 새 capture (이미지 첨부) 시 vision path 동작 - [ ] vision 모델 선택 + 5MB 초과 이미지 capture → fast-fail (readFile 안 됨) → ai_status='failed' 도달 - [ ] vision 모델 선택 + 텍스트만 capture → 기존 text-only 응답 (images undefined) - [ ] vision 모델 비선택 + 이미지 capture → 기존 text-only 흐름 (회귀) - [ ] "다시 감지" 버튼 → /api/tags 새 호출 + dropdown 갱신 + 마지막 감지 시각 갱신 - [ ] dev 환경 (settings.ollama 미설정 + INKLING_OLLAMA_ENDPOINT 만 있음) — manual 다시 감지 정상 동작 (fallback chain) - [ ] Ollama offline 시 자동 + manual 모두 silent / "감지 실패" 표시 - [ ] F23 OFF (ai_enabled=false) → refreshVisionCache 가 ai_disabled 분기 (cache 유지) - [ ] macOS dmg / Linux AppImage/deb 빌드 + Linux VM smoke 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
altair823 added 12 commits 2026-05-09 20:09:26 +00:00
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cut E v0.3.0 에서 ExportNote interface 에 status / statusChangedAt / moveReason /
dueDate / dueDateEditedByUser 필드 추가했지만 ImportService.test 의 buildExportNote
helper 갱신 누락 → composeFrontmatter 가 undefined moveReason 로 formatScalar 호출
시 null !== undefined 분기 통과 후 .includes throw.

helper 에 5 필드 default (active / null / null / null / false) 추가. 회귀 fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- F24 promoted ( v0.3.1 Cut F — Ollama vision 모델 capability detection + AiWorker integration)
- version 0.3.0 → 0.3.1 (semver patch — 새 기능, 기존 영향 X)
- 단위 679 → 710 (+31): VisionDetect 9 + SettingsService 4 + visionPrompt 2 + LocalOllamaProvider vision 3 + AiWorker vision 3 + IPC 5 + UI 4 + ImportService helper fix 5 (Cut E gap)
- typecheck 0 errors
- 자동 fallback (caption→text) + 'skipped' enum deferred v0.3.2+
final code review (Opus) 발견 minor issues 중 valuable 2건:

1. settings:refresh-vision-cache 가 settings.ollama.endpoint 만 체크 — env / default
   fallback 누락. dev 환경 (env var only) 사용자가 manual 다시 감지 시 'no_endpoint'
   silent fail. → index.ts 의 resolvedEndpoint 와 동일 fallback 체인 (settings → env →
   DEFAULT_OLLAMA_ENDPOINT).

2. AiWorker 의 5MB cap 이 readFile + base64 변환 후 throw — retry 마다 동일 비용 반복.
   note.media[].bytes 가 DB 에 이미 있으니 readFile 전 fast-fail. 비용 절감 + 동일 회로
   (markAiFailed 도달).

회귀 test 영향 없음 (기존 5MB throw 시나리오 그대로 — fast-fail 도 throw 분기 동일).
claude-reviewer-01 approved these changes 2026-05-09 20:10:38 +00:00
claude-reviewer-01 left a comment
Member

코드 리뷰 — v0.3.1 Cut F

Scope: 12 commits (7a56184..81fae12) — semver patch (vision 추가, backward compat)

Spec coverage 100%

Cut F design 의 모든 섹션이 task 매핑됨. F24 옵션 모두 적용 — 'skipped' enum + automatic fallback 만 v0.3.2+ deferred (spec 정정 반영).

spec § 구현
§3-1 capability detection VisionDetect.isVisionCapable (family/families/name 3-layer) + 9 unit
§3-2 SettingsService 4 메서드 getVisionModel/setVisionModel/getVisionCapableCache/setVisionCapableCache (Cut B/E 패턴)
§3-3 main wiring void refreshVisionCache(...) whenReady fire-and-forget
§3-4 Configure UI VisionSection (dropdown + 다시 감지 + 마지막 감지 시각 + 안내 텍스트)
§4 Provider 인터페이스 GenerateInput.images? + GenerateOptions.visionModel? (둘 다 optional → backward compat)
§4-2 LocalOllamaProvider useVision = !!visionModel && images.length > 0 분기. body.images base64
§5 AiWorker note.media + visionModel 둘 다 충족 시 vision path. 5MB cap fast-fail (final review fix)
§6 image-only fallback 'skipped' enum 미도입 (YAGNI) — 기존 'failed' 분기 활용
§7 IPC 3 채널 + 5 unit
§8 테스트 +31 신규 (목표 22 초과 — Cut E gap fix 포함)

코드 품질

Strengths

  • Pure functions + dependency injection: VisionDetect 의 fetch/now 주입 → 단위 테스트 결정적. visionPrompt 도 pure builder.
  • Backward compatibility 보장: GenerateInput.images? + GenerateOptions? 모두 optional — 기존 LocalOllamaProvider/AiWorker 호출자 무영향. 회귀 test 3건 (LocalOllamaProvider) 명시 검증.
  • AiWorker 생성자 변경 최소화: 새 deps (settings, mediaStore) 를 기존 opts 객체-bag 안에 추가 → 모든 기존 test 의 new AiWorker(repo, holder, {...}) 사이트 무수정. typecheck 0.
  • Single write path 강제 유지 (Cut C/D/E 정책): vision 결과 = updateAiResult() 경유. notes_fts / note_revisions / note_tags 직접 mutation path 추가 없음. 4-path invariant 보존.
  • 5MB cap fast-fail: note.media[].bytes (이미 DB 에 있음) 로 readFile/base64 전 차단. retry 마다 동일 비용 반복 회피.
  • endpoint resolution unification: settingsApi 의 manual refresh 도 index.ts 의 resolvedEndpoint 패턴 (settings → env → default) 사용 — dev 환경 호환.
  • Cut E gap fix 포함: ImportService.test buildExportNote 의 frontmatter 5 필드 누락 → composeFrontmatter(undefined moveReason) throw. helper 에 default 추가하여 회귀 회복.

Final review follow-up 적용 (81fae12)

initial final review (Opus, 1차) 에서 발견된 minor 2건:

  • endpoint resolution divergence: settings:refresh-vision-cachesettings.ollama?.endpoint 만 체크 — env / default fallback 없음. dev 환경 (env var only) 사용자가 manual 다시 감지 시 silent 'no_endpoint' 반환
  • 5MB cap waste: readFile + base64 변환 후 throw → 3 retry 마다 같은 작업 반복

→ Fix:

  • settings:refresh-vision-cachesettings.ollama?.endpoint ?? process.env.INKLING_OLLAMA_ENDPOINT ?? DEFAULT_OLLAMA_ENDPOINT 동일 fallback chain 사용
  • AiWorker 의 cap 검사를 note.media[].bytes (DB 값) 으로 fast-fail — readFile 전 throw

re-verification 결과:

  • 710/710 unit PASS, typecheck 0
  • 회귀 test 영향 없음 (5MB throw 시나리오 동일 분기 도달)
  • bounded scope (settingsApi + AiWorker 2 file 만 변경)

Architecture

  • 단위 분해 명확 (helper / service / provider / worker / ipc / preload / store / component 8계층 분리)
  • 책임 명확성 — VisionDetect = pure detection / LocalOllamaProvider = transport + 분기 / AiWorker = orchestration / VisionSection = view
  • 일관성 — 기존 settings section pattern (AiProviderSection, SyncSection) 와 VisionSection 동일 layout

Risk 잔재 (final review 분석)

  • vision 모델 한국어 정확도: gemma3 family 가 약하면 family set 보강 (메모리 정책). dogfood 검증
  • capability detection false-positive: 사용자가 dropdown 에서 다른 모델 선택해 우회. 자동 fallback 미구현 (YAGNI)
  • whenReady silent failure: Ollama offline 시 cache 빈 채로 진행. 사용자 "다시 감지" 우회
  • 5MB cap retry 반복: fast-fail 적용해서 비용 0 — DB 값 (note.media[].bytes) 사용. retry 자체는 동일 path 반복하나 readFile 안 함
  • e2e 본 세션 미수행: 머지 후 main 에서 검증 — 본 cut 의 3 IPC + VisionSection mount 만 영향, capture/onboarding flow 무관

Sub-review 트레일

  • Tasks 1-3 (settings + detect + prompt) — Sonnet batch + 15 신규 test
  • ImportService.test gap fix (Cut E 보강) — direct edit + 5 회귀 PASS
  • Tasks 4-5 (provider + worker) — Sonnet batch + 6 신규 test (vision path 3 + LocalOllamaProvider vision 3)
  • Tasks 6-7 (IPC + UI) — Sonnet batch + 9 신규 test (5 IPC + 4 UI)
  • Task 8 (main wiring) — direct edit
  • Task 9 (release commit) — direct
  • Final code review (Opus, 1차) — ⚠ Approved with 2 minor follow-ups
  • Final fix re-verification 710/710

머지 권장

  1. PR #31 머지
  2. tag v0.3.1 + npm run dist:win → Windows exe 빌드
  3. Gitea release v0.3.1 + exe attach
  4. macOS host: dist:mac + dist:linux 후 dmg/AppImage/deb 추가 attach
  5. 메모리 정책 갱신 — vision capability detection 패턴 / 5MB fast-fail / endpoint resolution unification / Single write path 4-path 유지
  6. Vision 모델 dogfood ≥1주 soak — 한국어 token 정확도 + capability false-positive / negative + 사용자 수정 비율 (vision 결과 정확도) → v0.3.2 Cut G plan + 2단계 fallback / 'skipped' enum 도입 여부 결정

Overall

Ready to merge. Spec 100% coverage, 679 → 710 unit (+31) + typecheck 0, schema 변경 없음 (settings.json zod 만), backward compat 보장 (optional opts), single write path invariant 4-path 유지 (vision 결과 = updateAiResult 경유), Cut E gap 회복 (ImportService helper). final review 2 minor 모두 follow-up commit 으로 처리. Cut G (v0.3.2 — F25 사이드바) 진입 가능.

🤖 Reviewed by Claude Opus 4.7 (1M context) with Sonnet sub-agents per subagent-driven-development skill

## 코드 리뷰 — v0.3.1 Cut F **Scope**: 12 commits (7a56184..81fae12) — semver patch (vision 추가, backward compat) ### Spec coverage ✅ 100% [Cut F design](docs/superpowers/specs/2026-05-09-v031-cut-f-design.md) 의 모든 섹션이 task 매핑됨. F24 옵션 모두 적용 — 'skipped' enum + automatic fallback 만 v0.3.2+ deferred (spec 정정 반영). | spec § | 구현 | |---|---| | §3-1 capability detection | `VisionDetect.isVisionCapable` (family/families/name 3-layer) + 9 unit | | §3-2 SettingsService 4 메서드 | `getVisionModel/setVisionModel/getVisionCapableCache/setVisionCapableCache` (Cut B/E 패턴) | | §3-3 main wiring | `void refreshVisionCache(...)` whenReady fire-and-forget | | §3-4 Configure UI | `VisionSection` (dropdown + 다시 감지 + 마지막 감지 시각 + 안내 텍스트) | | §4 Provider 인터페이스 | `GenerateInput.images?` + `GenerateOptions.visionModel?` (둘 다 optional → backward compat) | | §4-2 LocalOllamaProvider | useVision = `!!visionModel && images.length > 0` 분기. body.images base64 | | §5 AiWorker | note.media + visionModel 둘 다 충족 시 vision path. 5MB cap fast-fail (final review fix) | | §6 image-only fallback | 'skipped' enum 미도입 (YAGNI) — 기존 'failed' 분기 활용 | | §7 IPC | 3 채널 + 5 unit | | §8 테스트 | +31 신규 (목표 22 초과 — Cut E gap fix 포함) | ### 코드 품질 **Strengths** - **Pure functions + dependency injection**: `VisionDetect` 의 fetch/now 주입 → 단위 테스트 결정적. `visionPrompt` 도 pure builder. - **Backward compatibility 보장**: `GenerateInput.images?` + `GenerateOptions?` 모두 optional — 기존 LocalOllamaProvider/AiWorker 호출자 무영향. 회귀 test 3건 (LocalOllamaProvider) 명시 검증. - **AiWorker 생성자 변경 최소화**: 새 deps (`settings`, `mediaStore`) 를 기존 `opts` 객체-bag 안에 추가 → 모든 기존 test 의 `new AiWorker(repo, holder, {...})` 사이트 무수정. typecheck 0. - **Single write path 강제 유지 (Cut C/D/E 정책)**: vision 결과 = `updateAiResult()` 경유. `notes_fts` / `note_revisions` / `note_tags` 직접 mutation path 추가 없음. 4-path invariant 보존. - **5MB cap fast-fail**: `note.media[].bytes` (이미 DB 에 있음) 로 readFile/base64 전 차단. retry 마다 동일 비용 반복 회피. - **endpoint resolution unification**: settingsApi 의 manual refresh 도 index.ts 의 resolvedEndpoint 패턴 (settings → env → default) 사용 — dev 환경 호환. - **Cut E gap fix 포함**: `ImportService.test buildExportNote` 의 frontmatter 5 필드 누락 → `composeFrontmatter(undefined moveReason)` throw. helper 에 default 추가하여 회귀 회복. **Final review follow-up 적용** (`81fae12`) initial final review (Opus, 1차) 에서 발견된 minor 2건: - ⚠ **endpoint resolution divergence**: `settings:refresh-vision-cache` 가 `settings.ollama?.endpoint` 만 체크 — env / default fallback 없음. dev 환경 (env var only) 사용자가 manual 다시 감지 시 silent 'no_endpoint' 반환 - ⚠ **5MB cap waste**: `readFile` + `base64` 변환 후 throw → 3 retry 마다 같은 작업 반복 → Fix: - ✅ `settings:refresh-vision-cache` 도 `settings.ollama?.endpoint ?? process.env.INKLING_OLLAMA_ENDPOINT ?? DEFAULT_OLLAMA_ENDPOINT` 동일 fallback chain 사용 - ✅ AiWorker 의 cap 검사를 `note.media[].bytes` (DB 값) 으로 fast-fail — readFile 전 throw re-verification 결과: - 710/710 unit PASS, typecheck 0 - 회귀 test 영향 없음 (5MB throw 시나리오 동일 분기 도달) - bounded scope (settingsApi + AiWorker 2 file 만 변경) ### Architecture - 단위 분해 명확 (helper / service / provider / worker / ipc / preload / store / component 8계층 분리) - 책임 명확성 — `VisionDetect` = pure detection / `LocalOllamaProvider` = transport + 분기 / `AiWorker` = orchestration / `VisionSection` = view - 일관성 — 기존 settings section pattern (`AiProviderSection`, `SyncSection`) 와 `VisionSection` 동일 layout ### Risk 잔재 (final review 분석) - **vision 모델 한국어 정확도**: gemma3 family 가 약하면 family set 보강 (메모리 정책). dogfood 검증 - **capability detection false-positive**: 사용자가 dropdown 에서 다른 모델 선택해 우회. 자동 fallback 미구현 (YAGNI) - **whenReady silent failure**: Ollama offline 시 cache 빈 채로 진행. 사용자 "다시 감지" 우회 - **5MB cap retry 반복**: fast-fail 적용해서 비용 0 — DB 값 (note.media[].bytes) 사용. retry 자체는 동일 path 반복하나 readFile 안 함 - **e2e 본 세션 미수행**: 머지 후 main 에서 검증 — 본 cut 의 3 IPC + VisionSection mount 만 영향, capture/onboarding flow 무관 ### Sub-review 트레일 - **Tasks 1-3 (settings + detect + prompt)** — Sonnet batch + 15 신규 test ✅ - **ImportService.test gap fix** (Cut E 보강) — direct edit + 5 회귀 PASS ✅ - **Tasks 4-5 (provider + worker)** — Sonnet batch + 6 신규 test (vision path 3 + LocalOllamaProvider vision 3) ✅ - **Tasks 6-7 (IPC + UI)** — Sonnet batch + 9 신규 test (5 IPC + 4 UI) ✅ - **Task 8 (main wiring)** — direct edit ✅ - **Task 9 (release commit)** — direct - **Final code review (Opus, 1차)** — ⚠ Approved with 2 minor follow-ups - **Final fix re-verification** — ✅ 710/710 ### 머지 권장 1. PR #31 머지 2. tag `v0.3.1` + `npm run dist:win` → Windows exe 빌드 3. Gitea release v0.3.1 + exe attach 4. macOS host: `dist:mac` + `dist:linux` 후 dmg/AppImage/deb 추가 attach 5. 메모리 정책 갱신 — vision capability detection 패턴 / 5MB fast-fail / endpoint resolution unification / Single write path 4-path 유지 6. **Vision 모델 dogfood ≥1주 soak** — 한국어 token 정확도 + capability false-positive / negative + 사용자 수정 비율 (vision 결과 정확도) → v0.3.2 Cut G plan + 2단계 fallback / 'skipped' enum 도입 여부 결정 ### Overall **Ready to merge.** Spec 100% coverage, 679 → 710 unit (+31) + typecheck 0, schema 변경 없음 (settings.json zod 만), backward compat 보장 (optional opts), single write path invariant 4-path 유지 (vision 결과 = updateAiResult 경유), Cut E gap 회복 (ImportService helper). final review 2 minor 모두 follow-up commit 으로 처리. Cut G (v0.3.2 — F25 사이드바) 진입 가능. 🤖 Reviewed by Claude Opus 4.7 (1M context) with Sonnet sub-agents per subagent-driven-development skill
altair823 added 1 commit 2026-05-10 02:12:18 +00:00
본인 dogfood 환경 = gemma4:e4b (텍스트). vision 변종은 현재 gemma3 (vision-capable)
또는 향후 gemma4 출시 시. 양 family 모두 hint 에 포함 — capability detection 이
future-proof.

- VisionDetect.VISION_FAMILIES + VISION_NAME_HINTS 에 'gemma4' 추가
- isVisionCapable test 2건 추가 (gemma4 family / gemma4 name hint detection)
- spec §1 + §2 의 'gemma3 family default' → 'gemma family — gemma3 / gemma4'

영향: 기존 detection 정확도 무영향 (set 추가만), 사용자가 gemma4 vision 변종을
설치하면 자동 인식.
altair823 merged commit 0d2896e0cc into main 2026-05-10 03:10:38 +00:00
altair823 deleted branch worktree-v031-cut-f-vision 2026-05-10 03:10:39 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/inkling#31