diff --git a/memory/handoff-archive/2026-04-12-1830-phase32-premium-llm.md b/memory/handoff-archive/2026-04-12-1830-phase32-premium-llm.md
new file mode 100644
index 0000000..9c6f24a
--- /dev/null
+++ b/memory/handoff-archive/2026-04-12-1830-phase32-premium-llm.md
@@ -0,0 +1,198 @@
+# Session Handoff — 2026-04-11 23:30 (빅뱅 Phase 5 Part 6 — Voice Conversation 몰입 UX + 남은 이슈 전체 정리)
+
+> From: macOS (yunchan-ui-MacBookPro)
+> Last commit: `dc7f933` — fix(desktop): CloudSync Realtime 자동 재구독 + Refresh token 실패 재인증 알림 (U1+U3)
+> Branch: `main`
+> Remote: `https://gitlab.twentyoz.kr:8443/d3r0/voice.git`
+> Working tree: clean, push 완료
+
+## ✅ 이번 세션 요약 — 빅뱅 Phase 5 Part 6 (commit 3개)
+
+Part 5-C 설계만 완료 상태로 이월된 Voice Conversation UX 몰입 패널을 구현하고, Mac 플랫폼 감사에서 남은 작은 이슈들(U1/U3/U4/U7)을 **한 세션에 모두 털음**.
+
+### `412a2e7` — Voice Conversation 몰입 UX + 사운드 피드백 + Bug 13 빈 STT (U6 + U8)
+
+**신규 컴포넌트**: `apps/desktop/src/renderer/components/voice-conversation/VoiceRecordingPanel.tsx`
+- `recording-tip/script.js`의 9바 cos-분포 waveform(BAR_COUNT=9, SMOOTHING=0.5, RANDOM_FACTOR=0.35, UPDATE_INTERVAL=100, MIN=2, MAX=28)을 React로 포팅
+- `useRef`로 `audioLevelRef` 유지, `useEffect`에서 `setInterval` 100ms. 렌더링은 `setHeights(next)`로 반영.
+- REC LED(pulse) + elapsed 타이머 + "SPEAK NOW · PRESS ⏹ TO SEND" 힌트(i18n)
+- 색상은 전부 `d3roPalette.accent.amber` + `amberGlow` (hex 하드코딩 없음)
+
+**서비스 변경**: `VoiceConversationService.ts`
+- `_onAudioLevel` 추가 — `AudioCaptureService`의 `audio-level` 이벤트를 listening 상태일 때만 `VOICE_CONVERSATION.AUDIO_LEVEL` 채널로 렌더러 forwarding
+- 사운드 훅 4개: `_startListening()`에 `recording-start`, `finishListening`에 `recording-stop`, TTS finished 직후 `chime`, `_emitError()`에 `error`
+- **Bug 13(U8) 동반 해소**: minBytes 미달(`< 16000 bytes`) 또는 `transcribe` 결과 빈 텍스트일 때 조용히 listening으로 복귀하던 것을 `_emitError('stt', 'No speech detected...')`로 사용자 피드백 추가
+
+**SoundEffectService**: `SoundName`에 `'chime'` 추가, 파일은 `recording-stop.wav` 재사용
+
+**VoiceConversationPage 레이아웃 분기**:
+- `state === 'listening'`: `` 풀 몰입 모드 (메시지 리스트/TextField 모두 숨김)
+- 그 외: 기존 메시지 리스트 + `state === 'thinking' && !streamingText` 일 때 점 3개 typing indicator 버블 추가
+- TextField: `state !== 'listening'` 조건부 렌더 + `thinking/speaking`에선 `disabled` + placeholder 교체
+
+**IPC**: `packages/core/src/ipc-channels.ts`에 `VOICE_CONVERSATION.AUDIO_LEVEL` 채널 추가. preload에 `voiceConversation.onAudioLevel` 구독 API.
+
+**i18n**: `ko.json` / `en.json`에 3개 키 추가 (나머지 10개 locale은 conversation 네임스페이스 자체가 없어 en fallback):
+- `conversation.recording.hint` — "말씀하세요 · ⏹로 전송"
+- `conversation.thinking.placeholder` — "응답 대기 중…"
+- `conversation.state.recording` — "녹음 중"
+
+**실측**: 사용자 "잘됩니당!" 보고 + 로그에서 5.1초 한국어 오디오 전사 성공(`"에이에이에이에이 또 검중이래요"`).
+
+### `dd591d0` — AudioCaptureService.getDevices Mac 분기 (U7)
+
+`getDevices()`가 플랫폼 불문 `powershell -NoProfile`을 호출해 Mac에서 "Failed to enumerate audio devices via PowerShell" 경고 + 1 device fallback이 발생하던 문제. `process.platform` 분기로 `_getDevicesWindows` / `_getDevicesMac` helper 분리. Mac은 `system_profiler SPAudioDataType -json`으로 `coreaudio_device_input > 0` 인 항목만 추출.
+
+macOS SoX는 실제 캡처 경로에서 `-d`(default) 고정이므로 `deviceId='default'`로 통일하고 label만 사용자 구분용 노출. Linux는 default 1개만 반환.
+
+**실측 로그**: `Found 2 audio device(s) on darwin` (default + MacBook Pro 마이크).
+
+### `dc7f933` — CloudSync Realtime 자동 재구독 + Refresh token 재인증 (U1 + U3 + U4)
+
+**U1 — Realtime TIMED_OUT 지수 백오프 재구독**
+- `CloudSyncService.startRealtime()`의 `subscribe()` 콜백이 `TIMED_OUT` 한 줄만 로깅하고 끝나던 것을 재구독 로직으로 교체
+- `SUBSCRIBED` → retry count reset + timer clear
+- `TIMED_OUT / CHANNEL_ERROR / CLOSED` → `_scheduleRealtimeRetry()` (중복 예약 가드 있음)
+- 백오프: 1s → 3s → 10s (MAX 3회), 초과 시 경고 로그 후 포기
+- `stopRealtime()`에서 타이머/카운터 cleanup
+- **실측 확인**: `(1/3): 1000ms → (2/3): 3000ms → (3/3): 10000ms` 순서대로 로그 찍힘. 근본 원인은 Supabase 서버 transient 의심(이 패치로 완전 해결은 아님), 네트워크 jitter 복구 윈도우 확보가 목표.
+
+**U3 — Refresh token 실패 시 재인증 알림**
+- `init()`의 `refreshSession` 실패 브랜치에서 `warn` 로깅 + 토큰 삭제만 하던 것을 `error` 레벨 승격 + `sync-error` 이벤트 + `auth-changed { user: null }` emit으로 교체
+- `catch` 브랜치도 동일 처리 (기존엔 로깅만)
+- 재현이 쉽지 않아 예방적 개선. 사용자가 "왜 로그아웃됐지?" 상태 방치 방지.
+
+**U4 — package-lock optional dep (close)**
+- lock에 rollup 전 플랫폼 optional deps가 기록돼 있지만 Windows 빌드자 사전 fetch 효율 + 크로스플랫폼 CI 고려해 **유지**. `apps/desktop/package.json:56`에 `@rollup/rollup-win32-x64-msvc` 명시 의존. 이 이슈는 확인 후 close.
+
+## 🚧 미해결 이슈 (이월)
+
+### U1 근본 원인 (부분 개선)
+재구독 로직 추가로 jitter 복구는 가능하지만 매 기동 시 초기 구독에서 `TIMED_OUT`이 나오는 현상 자체의 원인은 여전히 불명. Publication/RLS/setAuth 모두 정상이라 Supabase 서버 측 transient 의심. **블로커 아님**(Phase 3.3 Auto push fallback 완벽).
+
+### Phase 3.2 PREMIUM_LLM (빅뱅 8번 기준 중 8/8 마지막)
+**선행 조건**: `supabase secrets set` 으로 `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` (선택: `STRIPE_*`, `RESEND_*`, `GOOGLE_CLOUD_STT_KEY`) 주입 필요. 키는 사용자 소유. 주입 안 된 상태에서는 `llm-proxy` Edge Function 400/500 반환.
+
+**다음 세션 액션**:
+1. 사용자가 `cd server && SUPABASE_ACCESS_TOKEN=... supabase secrets set ANTHROPIC_API_KEY=sk-ant-... OPENAI_API_KEY=sk-proj-...`
+2. `web/api/llm-proxy` 경로 실측 (Pro 이상 티어 가드 통과 여부 확인)
+3. desktop에서 PREMIUM_LLM feature 호출 경로 검증 — 빅뱅 성공 기준 8/8 완결
+
+### U3 재현 경로 (보조 관찰 대상)
+수정은 했지만 재현 자체가 안 돼서 이벤트가 실제로 발생하는 타이밍은 미확인. 장시간 idle 후(~24h+) 앱 복귀 시 관찰 필요. Supabase JWT 기본 만료가 1h이므로 1일 방치 후 재기동하면 재현 가능성 있음.
+
+## 🧭 다음 사이클 후보
+
+1. **Phase 3.2 PREMIUM_LLM 완결** (빅뱅 8/8 마지막) — API keys supabase secrets 주입 선행
+2. **추가 기능 빌드**: 사용자 우선순위 대기 (설계서 `docs/design/00-master-architecture.md` Phase 15 이후 로드맵에서 고르거나, 새 기능 요청 수신)
+3. **docs/design 업데이트**: 이번 세션에서 `VoiceRecordingPanel` 포팅하면서 설계서 03 와이어프레임이 구현과 일치하도록 정합성 확인 (미완, 선택)
+4. **마이그레이션 `20260411000002_realtime_publication.sql` 검증**: Supabase 대시보드에서 publication에 `meetings/history/dictionary` 실제 등록 상태 확인 (U1 재현 방지)
+
+## 🔑 Mac에서 이어받을 때 즉시 확인할 것 (같은 기기 계속)
+
+```bash
+git status # clean이어야 함
+git log --oneline -5 # HEAD=dc7f933
+git rev-parse HEAD # dc7f933...
+
+# dev가 살아있는지 (feedback_dev_autostart 규칙)
+pgrep -lf "Electron\.app|electron-vite dev|next dev" | head -5
+lsof -i :5173 -sTCP:LISTEN
+lsof -i :3000 -sTCP:LISTEN
+
+# 죽어있으면 (반드시 nohup + disown)
+nohup npm run dev > /tmp/d3ro-desktop.log 2>&1 & disown
+nohup npm --workspace=@d3ro/web run dev > /tmp/d3ro-web.log 2>&1 & disown
+
+# 검증
+cd apps/desktop && npx tsc --noEmit
+```
+
+## ⚠️ 환경 / 주의사항
+
+- **Electron main 변경 시 HMR 안 먹음**: `src/main/**` 수정 후에는 Electron kill + nohup 재기동 필수. 이번 세션에서 2번 재기동(U6/U7 구현 중).
+- **dev 기동은 반드시 `nohup + & disown`**: `run_in_background: true` 직접 사용 시 shell lifecycle로 Vite dev(5173)가 조기 종료됨.
+- **Realtime TIMED_OUT**: 이제 재구독까지 시도하므로 로그가 여러 줄 찍힘. 3회 실패 후 "Auto push fallback으로만 동작" 메시지가 정상 상태. Phase 3.3 Auto push가 실 데이터 경로.
+- **Voice Conversation 동작**: Mic 클릭(hold 아님) → 말함 → ⏹ 클릭 → 전사 → LLM → TTS → 자동 listening 재진입. 연속 대화 지원.
+- **음성 대화 Bug 13 힌트**: 너무 짧게 녹음하거나 VAD가 무음 판정 시 이제 `'No speech detected. ...'` 에러 이벤트 + error 사운드 발생. renderer에서 이 이벤트를 받아 UI 표시하는 건 현재 스켈레톤(에러 핸들러는 있지만 배너 UI는 미구현 — 추후 필요 시 구현).
+
+## 📦 의존성 / 도구
+
+- Node 22.22.2
+- Python 3.13.13
+- Supabase CLI 2.84.2
+- Ollama 0.20.5 (qwen3:4b 권장)
+- Electron 33.4.11
+- Next.js 15.5.14
+- SoX 14.4.2 (brew)
+- `/usr/bin/say`, `/usr/bin/afplay`, `/usr/sbin/system_profiler` — macOS 시스템 유틸
+
+## 🧪 마지막 검증 상태
+
+| 게이트 | 결과 |
+|---|---|
+| desktop `tsc --noEmit` | ✅ EXIT 0 |
+| Voice Conversation Mic → Stop → 전사 → LLM → TTS | ✅ 사용자 "잘됩니당!" |
+| `Found 2 audio device(s) on darwin` | ✅ U7 해소 |
+| Realtime 재구독 1/2/3 백오프 로그 | ✅ U1 동작 확인 |
+| Phase 3.3 Auto push (pushed=11, errors=0) | ✅ 재기동 후 정상 |
+| git push origin main | ✅ `dc7f933` 까지 |
+| Realtime SUBSCRIBED | ❌ TIMED_OUT (U1 잔존, 재구독 로직은 정상 동작) |
+
+## 🎯 다음 세션 시작 프롬프트
+
+```
+D3RO-VOICE 빅뱅 Phase 5 Part 7 (또는 Phase 3.2 PREMIUM_LLM 완결).
+
+지난 세션(Part 6)에서 Voice Conversation 몰입 UX 패널(9바 waveform + 사운드
+4개 + state 분기) 구현 완료 + 남은 U1/U3/U4/U7 작은 이슈들 모두 정리.
+빅뱅 8/8 성공 기준 중 마지막 1개(Phase 3.2 PREMIUM_LLM)만 남음.
+
+## 시작 전 반드시
+1. /session-handoff resume
+2. memory/handoff-latest.md
+3. memory/project_status.md — 빅뱅 Phase 5 Part 6/6+/6++ 섹션
+4. CLAUDE.md 강제 규칙 13개
+5. **auto memory** feedback_dev_autostart.md
+
+## 첫 액션 (0번) — dev 서버/앱/웹 기동 ⭐ (사용자 고정 요구사항)
+pgrep + lsof로 생존 확인, 죽어있으면 nohup으로 기동. feedback_dev_autostart
+메모리 참조.
+
+## 첫 액션 (1번) — 사용자에게 우선순위 질문
+
+아래 3개 중 어느 걸로 갈지 사용자 컨펌:
+
+A) Phase 3.2 PREMIUM_LLM 완결 (빅뱅 8/8 마지막)
+ - 선행: supabase secrets set ANTHROPIC_API_KEY=... OPENAI_API_KEY=...
+ - 사용자가 키 보유. 주입 스크립트:
+ cd server && SUPABASE_ACCESS_TOKEN= supabase secrets set \
+ ANTHROPIC_API_KEY=sk-ant-... OPENAI_API_KEY=sk-proj-...
+ - 주입 후 llm-proxy Edge Function 호출 테스트 + desktop PREMIUM_LLM
+ feature flag 경로 검증
+
+B) Voice Conversation 에러 배너 UI (Part 6 후속)
+ - VoiceConversationPage에서 onError 이벤트 수신 후 Snackbar/Alert 표시
+ - Bug 13 피드백 '"No speech detected"'를 사용자가 실제로 보게 하는 마무리
+ - 작음 (1-2시간)
+
+C) 새 기능 요청 (사용자 제안)
+
+## 강제 규칙 (CLAUDE.md 13개)
+- any 금지, console.log 금지 (logger 사용)
+- 테마 SSOT: d3roPalette/d3roShadow
+- i18n t() 사용
+- 커밋 단위는 작업별, push 여부는 컨펌
+- 작업 완료 즉시 memory/project_status.md 갱신 (규칙 13)
+
+자, 첫 액션부터 시작해.
+```
+
+## 🔗 참고 문서
+
+- `memory/project_status.md` — 빅뱅 Phase 5 Part 6/6+/6++ 전체 이력
+- `memory/handoff-archive/2026-04-11-2240-phase5-part5.md` — 직전 스냅샷
+- `docs/design/03-db-and-ui.md:428+` — RecordingTip waveform 스펙 (VoiceRecordingPanel 포팅 원본 참고)
+- `apps/desktop/src/renderer/popups/recording-tip/script.js` — Vanilla JS 원본 (포팅 소스)
+- `apps/desktop/src/main/services/CloudSyncService.ts:362+` — Realtime 재구독 로직
+- Supabase 대시보드: https://supabase.com/dashboard/project/llnocwyqvhgwpdjcqqyw
diff --git a/memory/handoff-latest.md b/memory/handoff-latest.md
index 9c6f24a..ebc253c 100644
--- a/memory/handoff-latest.md
+++ b/memory/handoff-latest.md
@@ -1,198 +1,162 @@
-# Session Handoff — 2026-04-11 23:30 (빅뱅 Phase 5 Part 6 — Voice Conversation 몰입 UX + 남은 이슈 전체 정리)
+# Session Handoff — 2026-04-12 18:30 (Phase 3.2 Premium LLM 완결 + Bug 13 + Gemma4)
> From: macOS (yunchan-ui-MacBookPro)
-> Last commit: `dc7f933` — fix(desktop): CloudSync Realtime 자동 재구독 + Refresh token 실패 재인증 알림 (U1+U3)
+> Last commit: `6e52c18` — feat(desktop+server): Phase 3.2 Premium LLM
> Branch: `main`
> Remote: `https://gitlab.twentyoz.kr:8443/d3r0/voice.git`
-> Working tree: clean, push 완료
+> Working tree: clean (push 필요)
-## ✅ 이번 세션 요약 — 빅뱅 Phase 5 Part 6 (commit 3개)
+## ✅ 이번 세션 요약 — 빅뱅 8/8 마지막 성공 기준 달성 (commit 3개)
-Part 5-C 설계만 완료 상태로 이월된 Voice Conversation UX 몰입 패널을 구현하고, Mac 플랫폼 감사에서 남은 작은 이슈들(U1/U3/U4/U7)을 **한 세션에 모두 털음**.
+### `a744551` — Bug 13 전면 해소
+- VoiceConversationPage: Snackbar 에러 배너 (severity=warning filled, 4초)
+- VoiceModeService: minBytes 가드 + 빈 STT 가드 + _errorHideTimer leak 수정
+- i18n: conversation.error.phase.{stt,llm,tts} + noSpeech 키
-### `412a2e7` — Voice Conversation 몰입 UX + 사운드 피드백 + Bug 13 빈 STT (U6 + U8)
+### `d397bcb` — LLM 기본 모델 gemma4:e4b 전면 전환
+- LocalLLMService 3개 fallback + think:false 명시
+- 12개 locale JSON + 10개 site locale TS + OnboardingModal/OllamaGuideModal
+- 설계서 00/05, phase-14, V2-5 Mac 가이드 업데이트
+- 실측: refine 42.9초 → 3.9초 (gemma4:e4b, non-reasoning)
-**신규 컴포넌트**: `apps/desktop/src/renderer/components/voice-conversation/VoiceRecordingPanel.tsx`
-- `recording-tip/script.js`의 9바 cos-분포 waveform(BAR_COUNT=9, SMOOTHING=0.5, RANDOM_FACTOR=0.35, UPDATE_INTERVAL=100, MIN=2, MAX=28)을 React로 포팅
-- `useRef`로 `audioLevelRef` 유지, `useEffect`에서 `setInterval` 100ms. 렌더링은 `setHeights(next)`로 반영.
-- REC LED(pulse) + elapsed 타이머 + "SPEAK NOW · PRESS ⏹ TO SEND" 힌트(i18n)
-- 색상은 전부 `d3roPalette.accent.amber` + `amberGlow` (hex 하드코딩 없음)
-
-**서비스 변경**: `VoiceConversationService.ts`
-- `_onAudioLevel` 추가 — `AudioCaptureService`의 `audio-level` 이벤트를 listening 상태일 때만 `VOICE_CONVERSATION.AUDIO_LEVEL` 채널로 렌더러 forwarding
-- 사운드 훅 4개: `_startListening()`에 `recording-start`, `finishListening`에 `recording-stop`, TTS finished 직후 `chime`, `_emitError()`에 `error`
-- **Bug 13(U8) 동반 해소**: minBytes 미달(`< 16000 bytes`) 또는 `transcribe` 결과 빈 텍스트일 때 조용히 listening으로 복귀하던 것을 `_emitError('stt', 'No speech detected...')`로 사용자 피드백 추가
-
-**SoundEffectService**: `SoundName`에 `'chime'` 추가, 파일은 `recording-stop.wav` 재사용
-
-**VoiceConversationPage 레이아웃 분기**:
-- `state === 'listening'`: `` 풀 몰입 모드 (메시지 리스트/TextField 모두 숨김)
-- 그 외: 기존 메시지 리스트 + `state === 'thinking' && !streamingText` 일 때 점 3개 typing indicator 버블 추가
-- TextField: `state !== 'listening'` 조건부 렌더 + `thinking/speaking`에선 `disabled` + placeholder 교체
-
-**IPC**: `packages/core/src/ipc-channels.ts`에 `VOICE_CONVERSATION.AUDIO_LEVEL` 채널 추가. preload에 `voiceConversation.onAudioLevel` 구독 API.
-
-**i18n**: `ko.json` / `en.json`에 3개 키 추가 (나머지 10개 locale은 conversation 네임스페이스 자체가 없어 en fallback):
-- `conversation.recording.hint` — "말씀하세요 · ⏹로 전송"
-- `conversation.thinking.placeholder` — "응답 대기 중…"
-- `conversation.state.recording` — "녹음 중"
-
-**실측**: 사용자 "잘됩니당!" 보고 + 로그에서 5.1초 한국어 오디오 전사 성공(`"에이에이에이에이 또 검중이래요"`).
-
-### `dd591d0` — AudioCaptureService.getDevices Mac 분기 (U7)
-
-`getDevices()`가 플랫폼 불문 `powershell -NoProfile`을 호출해 Mac에서 "Failed to enumerate audio devices via PowerShell" 경고 + 1 device fallback이 발생하던 문제. `process.platform` 분기로 `_getDevicesWindows` / `_getDevicesMac` helper 분리. Mac은 `system_profiler SPAudioDataType -json`으로 `coreaudio_device_input > 0` 인 항목만 추출.
-
-macOS SoX는 실제 캡처 경로에서 `-d`(default) 고정이므로 `deviceId='default'`로 통일하고 label만 사용자 구분용 노출. Linux는 default 1개만 반환.
-
-**실측 로그**: `Found 2 audio device(s) on darwin` (default + MacBook Pro 마이크).
-
-### `dc7f933` — CloudSync Realtime 자동 재구독 + Refresh token 재인증 (U1 + U3 + U4)
-
-**U1 — Realtime TIMED_OUT 지수 백오프 재구독**
-- `CloudSyncService.startRealtime()`의 `subscribe()` 콜백이 `TIMED_OUT` 한 줄만 로깅하고 끝나던 것을 재구독 로직으로 교체
-- `SUBSCRIBED` → retry count reset + timer clear
-- `TIMED_OUT / CHANNEL_ERROR / CLOSED` → `_scheduleRealtimeRetry()` (중복 예약 가드 있음)
-- 백오프: 1s → 3s → 10s (MAX 3회), 초과 시 경고 로그 후 포기
-- `stopRealtime()`에서 타이머/카운터 cleanup
-- **실측 확인**: `(1/3): 1000ms → (2/3): 3000ms → (3/3): 10000ms` 순서대로 로그 찍힘. 근본 원인은 Supabase 서버 transient 의심(이 패치로 완전 해결은 아님), 네트워크 jitter 복구 윈도우 확보가 목표.
-
-**U3 — Refresh token 실패 시 재인증 알림**
-- `init()`의 `refreshSession` 실패 브랜치에서 `warn` 로깅 + 토큰 삭제만 하던 것을 `error` 레벨 승격 + `sync-error` 이벤트 + `auth-changed { user: null }` emit으로 교체
-- `catch` 브랜치도 동일 처리 (기존엔 로깅만)
-- 재현이 쉽지 않아 예방적 개선. 사용자가 "왜 로그아웃됐지?" 상태 방치 방지.
-
-**U4 — package-lock optional dep (close)**
-- lock에 rollup 전 플랫폼 optional deps가 기록돼 있지만 Windows 빌드자 사전 fetch 효율 + 크로스플랫폼 CI 고려해 **유지**. `apps/desktop/package.json:56`에 `@rollup/rollup-win32-x64-msvc` 명시 의존. 이 이슈는 확인 후 close.
+### `6e52c18` — Phase 3.2 Premium LLM (23파일, +1107/-307)
+- **PremiumLLMService** 신규 — Supabase functions.invoke() 기반, processText/chatStream
+- **llm-prompts.ts** — SYSTEM_PROMPTS 공유 모듈 (resolveSystemPrompt)
+- **VoiceModeService** — _getLLMProcessor/_runProcessorWithFallback 라우터 + premium-llm-fallback 이벤트
+- **CloudSyncService** — getAccessToken(async), invokeFunction(에러 body 파싱)
+- **quota.ts** — 모델별 쿼터 (llm_haiku/sonnet/opus × free/pro/pro_plus), 주간/일간 분리
+- **llm-proxy** — 모델별 체크+소비, verify_jwt=false (2026 sb_publishable_ 호환)
+- **Migration** — tier team→pro_plus, overage_credits, consume_quota RPC
+- **Settings UI** — Backend 드롭다운 + Premium 시 Ollama 숨김 + 라이선스 모달 오픈
+- **LicenseModal** — SaaS 전환 (키 입력 제거 → 구독 관리 + 업그레이드 버튼)
+- **AppLayout** — 상단 중앙 fallback Snackbar 배너
+- **코드 리뷰 /simplify** — 7개 이슈 수정 (SYSTEM_PROMPTS 중복, IPC 헬퍼, consumeQuota 이중 DB, AbortController 동시성, 등)
## 🚧 미해결 이슈 (이월)
-### U1 근본 원인 (부분 개선)
-재구독 로직 추가로 jitter 복구는 가능하지만 매 기동 시 초기 구독에서 `TIMED_OUT`이 나오는 현상 자체의 원인은 여전히 불명. Publication/RLS/setAuth 모두 정상이라 Supabase 서버 측 transient 의심. **블로커 아님**(Phase 3.3 Auto push fallback 완벽).
+### 1. 세션 만료 렌더러 미동기화 버그
+CloudSyncService.init()에서 stored token 없을 때 auth-changed null 미emit → UI가 로그인 상태로 보이지만 실제론 비인증. Premium 선택 시 fallback으로 떨어짐. (memory/feedback_session_expiry_bug.md)
-### Phase 3.2 PREMIUM_LLM (빅뱅 8번 기준 중 8/8 마지막)
-**선행 조건**: `supabase secrets set` 으로 `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` (선택: `STRIPE_*`, `RESEND_*`, `GOOGLE_CLOUD_STT_KEY`) 주입 필요. 키는 사용자 소유. 주입 안 된 상태에서는 `llm-proxy` Edge Function 400/500 반환.
+### 2. Payple 결제 연동 (Phase 3.2-B)
+업그레이드 버튼 → "결제 연동 준비 중 (Payple)" alert 스텁. 사용자 Payple 가맹점 등록 선행 필요 (CST_ID + CUST_KEY). Stripe Edge Function 3개(checkout/webhook/portal) → Payple로 이관.
-**다음 세션 액션**:
-1. 사용자가 `cd server && SUPABASE_ACCESS_TOKEN=... supabase secrets set ANTHROPIC_API_KEY=sk-ant-... OPENAI_API_KEY=sk-proj-...`
-2. `web/api/llm-proxy` 경로 실측 (Pro 이상 티어 가드 통과 여부 확인)
-3. desktop에서 PREMIUM_LLM feature 호출 경로 검증 — 빅뱅 성공 기준 8/8 완결
+### 3. 대시보드 Premium 상태 표시
+현재 대시보드에 Premium LLM 쿼터/모델/사용량 정보 없음. CRT 서비스 섹션에 Premium LLM indicator 추가 필요.
-### U3 재현 경로 (보조 관찰 대상)
-수정은 했지만 재현 자체가 안 돼서 이벤트가 실제로 발생하는 타이밍은 미확인. 장시간 idle 후(~24h+) 앱 복귀 시 관찰 필요. Supabase JWT 기본 만료가 1h이므로 1일 방치 후 재기동하면 재현 가능성 있음.
+### 4. Supabase verify_jwt 비호환
+2026 sb_publishable_ 키와 Edge Function Gateway의 JWT 검증 비호환. llm-proxy만 verify_jwt=false. 다른 함수들(stt-proxy, team-invite 등)도 같은 이슈 있을 수 있음 — 사용 시 확인.
+
+### 5. LemonSqueezy 코드 잔존
+LicenseService에 LemonSqueezy API 호출 코드가 아직 남아있음 (LEMONSQUEEZY_API 상수). SaaS(Payple) 전환 시 정리 필요.
## 🧭 다음 사이클 후보
-1. **Phase 3.2 PREMIUM_LLM 완결** (빅뱅 8/8 마지막) — API keys supabase secrets 주입 선행
-2. **추가 기능 빌드**: 사용자 우선순위 대기 (설계서 `docs/design/00-master-architecture.md` Phase 15 이후 로드맵에서 고르거나, 새 기능 요청 수신)
-3. **docs/design 업데이트**: 이번 세션에서 `VoiceRecordingPanel` 포팅하면서 설계서 03 와이어프레임이 구현과 일치하도록 정합성 확인 (미완, 선택)
-4. **마이그레이션 `20260411000002_realtime_publication.sql` 검증**: Supabase 대시보드에서 publication에 `meetings/history/dictionary` 실제 등록 상태 확인 (U1 재현 방지)
+1. **Phase 3.2-B — Payple 결제 연동** (Stripe → Payple 전면 이관)
+ - Payple 가맹점 등록 → CST_ID/CUST_KEY 발급
+ - Edge Function 재작성 (payple-checkout/webhook/portal)
+ - DB subscriptions 컬럼 개편 (stripe_* → payple_*)
+ - 웹 결제 페이지 UI 교체
+ - LemonSqueezy 코드 정리
-## 🔑 Mac에서 이어받을 때 즉시 확인할 것 (같은 기기 계속)
+2. **대시보드 Premium 상태 표시**
+ - CRT 서비스 섹션에 Premium LLM indicator
+ - 쿼터 사용량 카드 (오늘/주간)
+ - 모델 선택 UI (Haiku/Sonnet/Opus — 티어별 가용)
+
+3. **세션 만료 버그 수정**
+ - CloudSyncService.init() no-token 경로에 auth-changed emit
+ - 렌더러에 Cloud 기능 "로그인 필요" 배지
+
+4. **SSE 스트리밍 Premium** (현재 비스트리밍 → 실시간 토큰 yield)
+
+5. **Voice Conversation Premium** (chatStream도 Premium 경로 + SSE)
+
+## 🔑 다음 세션 시작 시 확인
```bash
-git status # clean이어야 함
-git log --oneline -5 # HEAD=dc7f933
-git rev-parse HEAD # dc7f933...
-
-# dev가 살아있는지 (feedback_dev_autostart 규칙)
-pgrep -lf "Electron\.app|electron-vite dev|next dev" | head -5
+git status # clean
+git log --oneline -5 # HEAD=6e52c18
+pgrep -lf "Electron\.app|next dev"
lsof -i :5173 -sTCP:LISTEN
lsof -i :3000 -sTCP:LISTEN
-
-# 죽어있으면 (반드시 nohup + disown)
+# 죽어있으면
nohup npm run dev > /tmp/d3ro-desktop.log 2>&1 & disown
nohup npm --workspace=@d3ro/web run dev > /tmp/d3ro-web.log 2>&1 & disown
-
-# 검증
-cd apps/desktop && npx tsc --noEmit
```
-## ⚠️ 환경 / 주의사항
-
-- **Electron main 변경 시 HMR 안 먹음**: `src/main/**` 수정 후에는 Electron kill + nohup 재기동 필수. 이번 세션에서 2번 재기동(U6/U7 구현 중).
-- **dev 기동은 반드시 `nohup + & disown`**: `run_in_background: true` 직접 사용 시 shell lifecycle로 Vite dev(5173)가 조기 종료됨.
-- **Realtime TIMED_OUT**: 이제 재구독까지 시도하므로 로그가 여러 줄 찍힘. 3회 실패 후 "Auto push fallback으로만 동작" 메시지가 정상 상태. Phase 3.3 Auto push가 실 데이터 경로.
-- **Voice Conversation 동작**: Mic 클릭(hold 아님) → 말함 → ⏹ 클릭 → 전사 → LLM → TTS → 자동 listening 재진입. 연속 대화 지원.
-- **음성 대화 Bug 13 힌트**: 너무 짧게 녹음하거나 VAD가 무음 판정 시 이제 `'No speech detected. ...'` 에러 이벤트 + error 사운드 발생. renderer에서 이 이벤트를 받아 UI 표시하는 건 현재 스켈레톤(에러 핸들러는 있지만 배너 UI는 미구현 — 추후 필요 시 구현).
-
## 📦 의존성 / 도구
-- Node 22.22.2
-- Python 3.13.13
-- Supabase CLI 2.84.2
-- Ollama 0.20.5 (qwen3:4b 권장)
-- Electron 33.4.11
-- Next.js 15.5.14
-- SoX 14.4.2 (brew)
-- `/usr/bin/say`, `/usr/bin/afplay`, `/usr/sbin/system_profiler` — macOS 시스템 유틸
+- Node 22.22.2 / Python 3.13.13 / Supabase CLI 2.84.2
+- Ollama 0.20.5 (gemma4:e4b 기본, qwen3:4b 레거시)
+- Electron 33.4.11 / Next.js 15.5.14
+- SoX 14.4.2 (brew) / faster-whisper base (CPU int8)
+- Anthropic API: SUPABASE_SECRETS에 ANTHROPIC_API_KEY 주입됨
## 🧪 마지막 검증 상태
| 게이트 | 결과 |
|---|---|
| desktop `tsc --noEmit` | ✅ EXIT 0 |
-| Voice Conversation Mic → Stop → 전사 → LLM → TTS | ✅ 사용자 "잘됩니당!" |
-| `Found 2 audio device(s) on darwin` | ✅ U7 해소 |
-| Realtime 재구독 1/2/3 백오프 로그 | ✅ U1 동작 확인 |
-| Phase 3.3 Auto push (pushed=11, errors=0) | ✅ 재기동 후 정상 |
-| git push origin main | ✅ `dc7f933` 까지 |
-| Realtime SUBSCRIBED | ❌ TIMED_OUT (U1 잔존, 재구독 로직은 정상 동작) |
+| Premium LLM (Claude Haiku refine) | ✅ 1.6~3.2초, fallback 없음 |
+| Local LLM (gemma4:e4b refine) | ✅ 3.9초 |
+| Bug 13 Dictation 가드 | ✅ |
+| Bug 13 Conversation 배너 | ✅ |
+| Recording tip hide 타이머 leak | ✅ |
+| LicenseModal SaaS UI | ✅ (Payple 스텁) |
+| Settings Backend 드롭다운 | ✅ (Premium/Local 전환) |
+| DB migration (tier + overage) | ✅ 배포 완료 |
+| Edge Function llm-proxy 배포 | ✅ verify_jwt=false |
+| git push | ⏳ 미완 (사용자 확인 후) |
## 🎯 다음 세션 시작 프롬프트
```
-D3RO-VOICE 빅뱅 Phase 5 Part 7 (또는 Phase 3.2 PREMIUM_LLM 완결).
+D3RO-VOICE Phase 3.2-B (Payple 결제 연동) 또는 대시보드 Premium 표시.
-지난 세션(Part 6)에서 Voice Conversation 몰입 UX 패널(9바 waveform + 사운드
-4개 + state 분기) 구현 완료 + 남은 U1/U3/U4/U7 작은 이슈들 모두 정리.
-빅뱅 8/8 성공 기준 중 마지막 1개(Phase 3.2 PREMIUM_LLM)만 남음.
+지난 세션에서 빅뱅 8/8 마지막 성공 기준(Phase 3.2 Premium LLM) 달성.
+Claude Haiku를 통한 프리미엄 받아쓰기 refine 1.6~3.2초로 동작 확인.
## 시작 전 반드시
1. /session-handoff resume
2. memory/handoff-latest.md
-3. memory/project_status.md — 빅뱅 Phase 5 Part 6/6+/6++ 섹션
+3. memory/project_status.md
4. CLAUDE.md 강제 규칙 13개
-5. **auto memory** feedback_dev_autostart.md
-## 첫 액션 (0번) — dev 서버/앱/웹 기동 ⭐ (사용자 고정 요구사항)
-pgrep + lsof로 생존 확인, 죽어있으면 nohup으로 기동. feedback_dev_autostart
-메모리 참조.
+## 첫 액션 — dev 서버/앱 기동
+pgrep + lsof로 생존 확인, 죽어있으면 nohup 기동.
-## 첫 액션 (1번) — 사용자에게 우선순위 질문
+## 우선순위 제안
+A) Phase 3.2-B — Payple 결제 연동
+ - 선행: 사용자 Payple 가맹점 등록 + CST_ID/CUST_KEY 발급
+ - Stripe Edge Function 3개 → Payple 이관
+ - 웹 결제 UI 교체
+ - LemonSqueezy 코드 정리
-아래 3개 중 어느 걸로 갈지 사용자 컨펌:
+B) 대시보드 Premium 상태 표시
+ - CRT 서비스에 Premium LLM indicator
+ - 쿼터 사용량 카드
+ - 모델 선택 UI
-A) Phase 3.2 PREMIUM_LLM 완결 (빅뱅 8/8 마지막)
- - 선행: supabase secrets set ANTHROPIC_API_KEY=... OPENAI_API_KEY=...
- - 사용자가 키 보유. 주입 스크립트:
- cd server && SUPABASE_ACCESS_TOKEN= supabase secrets set \
- ANTHROPIC_API_KEY=sk-ant-... OPENAI_API_KEY=sk-proj-...
- - 주입 후 llm-proxy Edge Function 호출 테스트 + desktop PREMIUM_LLM
- feature flag 경로 검증
+C) 세션 만료 버그 수정
+ - CloudSyncService.init() auth-changed emit
+ - 렌더러 Cloud 기능 로그인 배지
-B) Voice Conversation 에러 배너 UI (Part 6 후속)
- - VoiceConversationPage에서 onError 이벤트 수신 후 Snackbar/Alert 표시
- - Bug 13 피드백 '"No speech detected"'를 사용자가 실제로 보게 하는 마무리
- - 작음 (1-2시간)
+D) 새 기능 (사용자 제안)
-C) 새 기능 요청 (사용자 제안)
-
-## 강제 규칙 (CLAUDE.md 13개)
-- any 금지, console.log 금지 (logger 사용)
+## 강제 규칙
+- any 금지, console.log 금지
- 테마 SSOT: d3roPalette/d3roShadow
- i18n t() 사용
-- 커밋 단위는 작업별, push 여부는 컨펌
-- 작업 완료 즉시 memory/project_status.md 갱신 (규칙 13)
-
-자, 첫 액션부터 시작해.
+- 작업 완료 즉시 memory/project_status.md 갱신
```
## 🔗 참고 문서
-- `memory/project_status.md` — 빅뱅 Phase 5 Part 6/6+/6++ 전체 이력
-- `memory/handoff-archive/2026-04-11-2240-phase5-part5.md` — 직전 스냅샷
-- `docs/design/03-db-and-ui.md:428+` — RecordingTip waveform 스펙 (VoiceRecordingPanel 포팅 원본 참고)
-- `apps/desktop/src/renderer/popups/recording-tip/script.js` — Vanilla JS 원본 (포팅 소스)
-- `apps/desktop/src/main/services/CloudSyncService.ts:362+` — Realtime 재구독 로직
-- Supabase 대시보드: https://supabase.com/dashboard/project/llnocwyqvhgwpdjcqqyw
+- `memory/project_status.md` — Phase 3.2 전체 기록
+- `memory/feedback_session_expiry_bug.md` — 세션 만료 렌더러 미동기화 버그
+- `memory/feedback_dev_autostart.md` — 세션 시작 시 dev 기동 규칙
+- `apps/desktop/src/main/services/PremiumLLMService.ts` — Premium LLM 서비스
+- `apps/desktop/src/main/services/llm-prompts.ts` — 공유 시스템 프롬프트
+- `server/supabase/functions/llm-proxy/index.ts` — Edge Function (모델별 쿼터)
+- `server/supabase/functions/_shared/quota.ts` — 쿼터 정책 + consume_quota
+- Supabase Dashboard: https://supabase.com/dashboard/project/llnocwyqvhgwpdjcqqyw
diff --git a/memory/project_status.md b/memory/project_status.md
index f242c13..830e0a9 100644
--- a/memory/project_status.md
+++ b/memory/project_status.md
@@ -1,8 +1,52 @@
# D3RO-VOICE 프로젝트 현황
-> 마지막 갱신: 2026-04-11 (빅뱅 Phase 5 Part 7 — Bug 13 전면 해소 + Gemma4 전환)
+> 마지막 갱신: 2026-04-12 (Phase 3.2 Premium LLM 완결 — 빅뱅 8/8 달성)
> 규칙 13: 작업 완료 즉시 이 파일 갱신 의무
+## Phase 3.2 Premium LLM 완결 (2026-04-12) — 빅뱅 8/8 마지막 성공 기준 ✅
+
+Supabase Edge Function(llm-proxy)을 통해 Anthropic Claude를 호출하는 PremiumLLMService 구현. 사용자가 Settings에서 Local/Premium 백엔드를 선택하면 VoiceModeService가 자동 분기하고, Premium 실패 시 Local로 silent fallback + 상단 중앙 배너 알림.
+
+### 커밋 3개 (이번 세션)
+
+| SHA | 내용 |
+|---|---|
+| `a744551` | fix: Bug 13 전면 해소 — Conversation 에러 배너 + Dictation 빈 STT 가드 + recording tip 타이머 leak |
+| `d397bcb` | feat: LLM 기본 모델 qwen3:4b → gemma4:e4b 전면 전환 + think:false 안전장치 |
+| `6e52c18` | feat: Phase 3.2 Premium LLM — Anthropic Claude 파이프라인 + 모델별 쿼터 + SaaS UI |
+
+### 실측 결과
+
+| 경로 | LLM refine 시간 |
+|---|---|
+| 이전 qwen3:4b (local, reasoning) | 42.9초 |
+| gemma4:e4b (local, non-reasoning) | 3.9초 |
+| **Claude Haiku (premium)** | **1.6~3.2초** |
+
+### 주요 신규/변경 파일 (23개)
+
+**신규**: `PremiumLLMService.ts`, `llm-prompts.ts`, migration `20260412000001`
+**서버**: `quota.ts` (모델별 쿼터), `llm-proxy/index.ts`, `config.toml` (verify_jwt=false)
+**데스크톱**: `VoiceModeService` (router+fallback), `CloudSyncService` (invokeFunction, getAccessToken async), `ConfigService` (llmBackend), `LicenseService` (쿼터 250/1500), `llm-handlers` (safeSendToRenderer+premium IPC), `preload` (premium API)
+**UI**: `SettingsModal` (backend 드롭다운+conditional), `LicenseModal` (SaaS 전환), `AppLayout` (fallback 배너)
+**타입**: `AppConfig.llmBackend`, `SubscriptionTier pro_plus`, `IPC_CHANNELS.LLM.PREMIUM_*`
+
+### 모델별 쿼터 정책
+
+| | Free | Pro ₩9,900 | Pro+ ₩29,900 |
+|---|---|---|---|
+| 로컬 | ∞ | ∞ | ∞ |
+| Haiku | 250/주간 | 1,500/일 | ∞ |
+| Sonnet | ❌ | 300/일 | 1,500/일 |
+| Opus | ❌ | 50/일 | 300/일 |
+
+### 알려진 이슈 (이월)
+
+1. **세션 만료 시 렌더러 미동기화** — CloudSyncService.init()에서 stored token 없을 때 auth-changed null 미emit → UI 불일치 (memory/feedback_session_expiry_bug.md)
+2. **Payple 결제 연동 미완** — 업그레이드 버튼 클릭 시 "결제 연동 준비 중 (Payple)" alert 스텁. Phase 3.2-B에서 실제 연결
+3. **대시보드 Premium 상태 표시** — 현재 대시보드에 Premium LLM 쿼터/모델 정보 미노출
+4. **Supabase 2026 verify_jwt 비호환** — sb_publishable_ 키와 Gateway JWT 검증 비호환으로 verify_jwt=false 설정. 함수 내부 requireUser()에서 직접 인증 처리 중
+
## 빅뱅 Phase 5 Part 7 (2026-04-11) — Bug 13 전면 해소 + Gemma4 기본 모델 전환 ✅
Part 6에서 Voice Conversation 몰입 패널까지 가고 남은 숙제였던 **Bug 13 전면 해소**와 사용자 실측에서 발견된 **LLM refine 42.9초 병목**(qwen3 reasoning 모델 strip empty fallback)을 한 사이클에 정리. 받아쓰기 한 바퀴 51초 → 기대 7~10초로 대폭 단축 예상.