# 04. 설계서 vs Speakly 소스 대조 검증 리포트 > 검증일: 2026-04-04 > 검증 대상: 설계서 00~03 vs Speakly 추출 소스 (`/tmp/speakly-extracted/dist/src/`) > 검증자: verifier agent --- ## 요약 | 등급 | 건수 | 설명 | |------|------|------| | **Critical** | 5 | 설계서가 잘못 반영하거나 핵심 로직이 누락된 항목 | | **Major** | 10 | 기능 동작에 영향을 줄 수 있는 불일치/누락 | | **Minor** | 8 | 정확도 개선이 필요한 세부 사항 | --- ## 1. 상태 머신 검증 ### CRITICAL-01: RecognitionState에 CONNECTING, DESTROYED 누락 - **설계서 위치**: `02-ipc-and-types.md` 394-403행, `01-service-specifications.md` 559-569행 - **실제 소스 위치**: `types/recording.js` 34-54행 - **차이점**: Speakly `RecognitionState`는 9개 상태를 정의한다: - `IDLE`, `PREPARING`, **`CONNECTING`**, `READY`, `RECOGNIZING`, `COMPLETED`, `CANCELLED`, `ERROR`, **`DESTROYED`** - 설계서 `02`에는 `CONNECTING` 누락, `COMPLETING` 상태가 추가되어 있다 (Speakly에 없음). - 설계서 `01`에는 `Processing` 상태가 추가되어 있으나, Speakly에는 `Processing`이 없고 `RECOGNIZING` 상태에서 서버 측 처리가 진행된다. - `DESTROYED`는 dispose 전용 최종 상태로, 설계서에 누락. - **수정 제안**: - `CONNECTING` 상태 추가 (WebSocket/sidecar 연결 중) - `COMPLETING`을 `RECOGNIZING`의 하위 플래그로 변경하거나 제거 - `DESTROYED` 상태 추가 (리소스 정리 완료 전용) - `Processing`은 로컬에서는 LLM 후처리 시 의미가 있으나, Speakly의 원래 상태와 다름을 명시 ### MAJOR-01: AudioState는 일치 - **결과**: 설계서 `AudioState` (`IDLE`, `INITIALIZING`, `STREAMING`, `STOPPED`) = Speakly 소스 완전 일치. 문제 없음. ### MAJOR-02: RecordingTipUIState 누락 - **실제 소스 위치**: `types/recording.js` 60-76행 - **차이점**: Speakly에는 `RecordingTipUIState` 열거형이 별도 존재: - `OPENING`, `RECORDING`, `THINKING`, `CANCELLED`, `NOTICE`, `ERROR`, `ERROR_WITH_RETRY` - 설계서에 RecordingTip UI 상태 매핑이 없다. - **수정 제안**: `02-ipc-and-types.md` 또는 `03-db-and-ui.md`에 RecordingTipUIState 추가 --- ## 2. 이벤트 페이로드 검증 ### MAJOR-03: VoiceMode 열거형 불일치 - **설계서 위치**: `01-service-specifications.md` 579-585행, `02-ipc-and-types.md` 388행 - **실제 소스 위치**: `VoiceModeService.js` 89-95행 - **차이점**: Speakly `VoiceMode`는 4개 모드: - `DICTATION`, `HANDS_FREE`, **`CUSTOM_INSTRUCTION`**, **`HANDS_FREE_NO_WAKE`** - 설계서는 `dictation`과 `hands-free` 2개만 정의. - **수정 제안**: - `custom-instruction` 모드는 D3RO의 CustomInstructionService로 매핑 가능, 설계서에 명시 필요 - `hands-free-no-wake` 모드는 검증 항목 3에서 별도 기술 ### MAJOR-04: HotkeyService 이벤트 구조 차이 - **설계서 위치**: `00-master-architecture.md` 257-262행 - **실제 소스 위치**: `HotkeyService.js` 35-51행 - **차이점**: - 설계서: `hotkey:dictation-pressed`, `hotkey:dictation-released`, `hotkey:dictation-double-press`, `hotkey:command-pressed` - Speakly 실제: 범용 `hotkey:pressed` / `hotkey:released` 이벤트에 `commandId` 페이로드 포함 - 더블 프레스는 HotkeyService가 아니라 VoiceModeService에서 타이밍으로 감지 - **수정 제안**: HotkeyService 이벤트를 범용 `hotkey:pressed(commandId, timestamp)` / `hotkey:released(commandId, timestamp)` 패턴으로 변경. 더블 프레스 감지는 VoiceModeService 책임으로 명시. ### MINOR-01: 오디오 샘플레이트 불일치 - **설계서 위치**: `01-service-specifications.md` 68행 (`sampleRate: 16_000`) - **실제 소스 위치**: `config/constants.js` 47행 (`SAMPLE_RATE: 24000`) - **차이점**: 설계서는 Whisper 기본인 16kHz를 기술하나, Speakly는 24kHz로 캡처한다 (Opus 인코딩 후 서버 전송). - **수정 제안**: D3RO는 로컬 Whisper를 사용하므로 16kHz가 맞을 수 있으나, Speakly 원본이 24kHz임을 각주로 명시. 코덱이 Opus인 경우 24kHz가 필요할 수 있음. --- ## 3. 누락 패턴 검증 ### CRITICAL-02: accidentalPress 감지 패턴 미기술 - **실제 소스 위치**: `VoiceModeService.js` 116행, 3251-3293행 - **패턴 설명**: - `MIN_AUDIO_DURATION_MS = 700ms` 미만 키 누름은 accidental press로 판정 - 두 가지 판정 기준: (1) key press duration < 700ms, (2) session lifetime < 700ms - 세션에 `accidentalPress = true` 마킹 후 즉시 취소 - `checkAndMarkAccidentalPress()` 메서드로 녹음 정지 전에 조기 체크 - **설계서 상태**: 완전 누락 - **수정 제안**: `01-service-specifications.md` VoiceModeService 섹션에 accidentalPress 감지 로직 추가: ``` 키 릴리스 시점에서 duration < MIN_AUDIO_DURATION_MS(700ms) → 세션 취소 ``` ### CRITICAL-03: Audio Mute 연동 패턴 미기술 - **실제 소스 위치**: `VoiceModeService.js` 118-120행, 2726-2810행 - **패턴 설명**: - 녹음 시작 시 시스템 오디오 음소거 (`NativeService.muteSystemAudio()`) - `MUTE_DELAY_MS = 500ms` — 사운드 이펙트 재생 후 음소거 - `wasMutedBeforeRecording` — 이미 음소거였으면 녹음 후 언뮤트 안함 - `UNMUTE_SOUND_DELAY_MS = 100ms` — 언뮤트 후 종료 효과음 재생 지연 - `config:getMuteAudioWhenDictating` 설정으로 on/off 가능 - 루프백 마이크 사용 시 음소거 스킵 (`_isLoopbackMic()`) - **설계서 상태**: 완전 누락 - **수정 제안**: `01-service-specifications.md` VoiceModeService에 mute/unmute 시퀀스 추가 ### CRITICAL-04: Retry 로직 (이전 세션 오디오 재전송) 미기술 - **실제 소스 위치**: `VoiceModeService.js` 181-182행, 2563-2594행, 3316-3320행+ - **패턴 설명**: - `retryRecognition(historyId)` — 이전 세션의 저장된 오디오 파일을 읽어 재전송 - `retryingSession` / `retryingHistoryId` 상태 추적 - `history:retry` IPC 채널로 렌더러에서 호출 가능 - ESC 키로 retry 취소 가능 - No-wake 모드 취소 시 `undoNoWakeCancel()` → 자동 retry - 오디오 파일이 디스크에 저장된 세션만 retry 가능 - **설계서 상태**: 완전 누락 - **수정 제안**: 별도 섹션으로 Retry 흐름도 추가 ### CRITICAL-05: Action Queue (이벤트 직렬화) 패턴 미기술 - **실제 소스 위치**: `VoiceModeService.js` 186-275행 - **패턴 설명**: - `actionQueue: NXAction[]` — 키보드 이벤트를 큐에 넣고 순차 처리 - `isProcessingActionQueue` 플래그로 동시 실행 방지 - 핫키 press/release 이벤트 간 race condition 방지 - NXAction 클래스: `type`, `timestamp`, `hotkeyId`, `hotkeyTimestamp` - ESC 시 `clearActionQueue()`로 전체 큐 클리어 - **설계서 상태**: 완전 누락 - **수정 제안**: VoiceModeService 명세에 Action Queue 패턴 추가. 이것은 동시 키 이벤트 안정성의 핵심. ### MAJOR-05: hands-free-no-wake 모드 미기술 - **실제 소스 위치**: `VoiceModeService.js` 94행, 103행, 656-689행 - **패턴 설명**: - 별도 핫키로 진입하는 핸즈프리 모드 - 녹음 중 취소 시 오디오를 디스크에 보존 (`cancelledSessionId`) - 나중에 `undoNoWakeCancel()`로 자동 retry 가능 - `getNoWakeModeEnabled()` 설정으로 on/off - **설계서 상태**: 완전 누락 - **수정 제안**: VoiceModeService VoiceMode 열거형에 추가, 전용 핫키 등록 로직 기술 ### MAJOR-06: RecordingTip 2-phase 리사이즈는 기술됨, 하지만 preload 전략 누락 - **설계서 위치**: `02-ipc-and-types.md` 159-166행 (tipMeasured, tipPrepare, tipShow) - **실제 소스 위치**: `RecordingTipWindow.js` 157행 (`preload()` 메서드) - **차이점**: 설계서는 2-phase IPC는 기술했으나, RecordingTipWindow의 `preload()` 메서드 — 앱 시작 시 윈도우를 미리 생성해놓고 숨겨두는 전략이 누락. - **수정 제안**: 초기화 시퀀스(00-master 단계 10)에 "팝업 윈도우 프리로드" 설명 보강 ### MAJOR-07: 마이크 디바이스 팁(NewMicrophonePrompt) 미기술 - **실제 소스 위치**: `main/index.js` 74행, 1090-1108행, `main/NewMicrophonePromptWindow.js` - **패턴 설명**: - 새 마이크 디바이스 연결 시 전용 프롬프트 윈도우 표시 - "이 마이크로 전환?" + "다시 묻지 않기" 옵션 - `config:getDontPromptNewMicrophone` / `config:setShowMicDeviceTip` 설정 - `NewMicrophonePromptWindow` 클래스 — 독립 윈도우 - **설계서 상태**: 완전 누락 - **수정 제안**: `03-db-and-ui.md`에 NewMicrophonePrompt 윈도우 추가 ### MAJOR-08: ResultPopup auto-close 타이밍 미기술 - **실제 소스 위치**: `main/ResultPopupWindow.js` 27-28행, 249-269행 - **패턴 설명**: - 기본 `autoCloseDelay = 10000ms` (10초) - 마우스 호버 시 타이머 일시 정지 - 마우스 떠나면 타이머 재개 - `autoCloseDelay: 0`이면 auto-close 비활성화 - `content-ready` 이벤트 시 타이머 시작 - **설계서 상태**: `02-ipc-and-types.md`에 `window:showResultPopup` 채널은 있으나 auto-close 동작 미기술 - **수정 제안**: ResultPopup 동작 명세에 auto-close 타이밍 추가 ### MAJOR-09: Typing Nudge 패턴 미기술 - **실제 소스 위치**: `VoiceModeService.js` 196-208행, 706-796행 - **패턴 설명**: - 사용자가 5초간 연속 타이핑 (10키 이상) 시 "음성 사용해보세요" 넛지 표시 - 하루 최대 3회, 30분 간격 - 오늘 이미 음성 사용했으면 억제 - 자정 넘으면 자동 리셋 - `typing-nudge` 이벤트 emit - **설계서 상태**: 완전 누락 - **수정 제안**: D3RO에서 이 기능이 필요한지 결정 후, 채택 시 VoiceModeService 명세에 추가 ### MAJOR-10: Mode Switch 패턴 (녹음 중 모드 전환) 미기술 - **실제 소스 위치**: `VoiceModeService.js` 626-651행 - **패턴 설명**: - 녹음 중 다른 모드 핫키 누르면 오디오 청크를 보존하며 모드 전환 - `savedAudioChunks = [...this.audioChunks]` → cancelRecording → startRecording(savedAudioChunks) - `sessionType: 'mode-switch'` — 시작 사운드 억제, UI 전환 최소화 - **설계서 상태**: 완전 누락 - **수정 제안**: VoiceModeService 시퀀스 다이어그램에 mode-switch 분기 추가 --- ## 4. IPC 채널 누락 분석 설계서 IPC 채널 수: **113개** (02-ipc-and-types.md 기준) Speakly 실제 IPC 채널 수: **~160개** (preload/index.js에서 추출) ### 의도적 제거 (클라우드/인증 관련) — 문제 없음 | 네임스페이스 | 채널 수 | 이유 | |---|---|---| | `auth:*` | 11 | 로컬 전용, 인증 불필요 | | `feedback:*` | 1 | 클라우드 피드백 | | `userInfo:*` | 6 | 클라우드 사용자 정보 | | `update:*` | 8 | Phase 7으로 연기 | | `logUpload:*` | 4 | 클라우드 로그 업로드 | | `banner:*` | 4 | 클라우드 배너 | | `report:*` | 1 | 클라우드 오류 보고 | | `diagnostics:*` | 3 | 디버그 진단 | | `debug:*` | 1 | 접근성 트리 탐색 | ### 실수 누락 가능성 (검토 필요) | 채널명 | 설계서 | Speakly | 판정 | |--------|--------|---------|------| | `history:retry` | 없음 | 있음 | **누락** — retry 기능에 필수 | | `history:getLatestId` | 없음 | 있음 | **누락** — undo/retry에 사용 | | `history:getRecentSessions` | 없음 | 있음 | **누락** — 대시보드에 사용 | | `history:deleteByDuration` | 없음 | 있음 | 선택적 — 짧은 녹음 일괄 삭제 | | `history:getStats` / `resetStats` | 없음 | 있음 | `stats:*`로 통합된 것으로 보이나 확인 필요 | | `config:getMuteAudioWhenDictating` | 없음 | 있음 | **누락** — mute 연동 설정 | | `config:getSelectedMicrophone` | 없음 | 있음 | **누락** — 마이크 선택 저장 | | `config:setSelectedMicrophone` | 없음 | 있음 | **누락** | | `config:getTranslateLanguage` | 없음 | 있음 | **누락** — 번역 언어 설정 | | `config:getSoundEffectsEnabled` | 없음 | 있음 | **누락** — `system:isSoundEnabled`으로 매핑? | | `config:getDontPromptNewMicrophone` | 없음 | 있음 | **누락** — 마이크 프롬프트 설정 | | `config:shouldShowHandsFreePromo` | 없음 | 있음 | 선택적 — 프로모션 UI | | `hotkey:getHandsFreeNoWakeShortcut` | 없음 | 있음 | **누락** — no-wake 모드 핫키 | | `hotkey:setHandsFreeNoWakeShortcut` | 없음 | 있음 | **누락** | | `hotkey:getVoiceModeEnabled` | 없음 | 있음 | **누락** — 모드별 활성화 토글 | | `hotkey:setVoiceModeEnabled` | 없음 | 있음 | **누락** | | `hotkey:pauseVoiceMode` | 없음 | 있음 | **누락** — 설정 UI 중 핫키 일시정지 | | `hotkey:resumeVoiceMode` | 없음 | 있음 | **누락** | | `hotkey:validateHotkey` | 없음 | 있음 | **누락** — 핫키 유효성 검증 | | `hotkey:resetToDefault` | 없음 | 있음 | **누락** — 기본값 복원 | | `hotkey:getAvailableKeys` | 없음 | 있음 | **누락** — 사용 가능한 키 목록 | | `hotkey:setOverrideAppBundleId` | 없음 | 있음 | 선택적 — 온보딩/테스트 전용 | | `hotkey:refreshRequired` | 없음 | 있음 | **누락** — 핫키 재등록 알림 | | `clipboard:copy` | 없음 | 있음 | **누락** — 결과 클립보드 복사 | | `input:insertText` | 없음 | 있음 | `system:insertText`로 매핑됨, 확인 필요 | | `input:getCursorState` | 없음 | 있음 | 선택적 — postInsertCursorContext | | `app:getActiveApplication` | 없음 | 있음 | `system:getActiveApp`으로 매핑됨 | | `app:getAppIcon` | 없음 | 있음 | 선택적 — 앱 아이콘 표시 | | `voice:startWithCustomInstruction` | 없음 | 있음 | **누락** — Custom Instruction 녹음 | | `voice:stopForCustomInstruction` | 없음 | 있음 | **누락** | | `voice:cancel` | 없음 | 있음 | **누락** — 별도 취소 채널 (voice:cancelRecording과 다름) | | `customInstruction:*` (9개+) | 있음(일부) | 있음(많음) | 키 이벤트 관련 채널 다수 누락 | --- ## 5. 타이밍 상수 검증 | 상수 | 설계서 값 | Speakly 실제 값 | 일치 | 비고 | |------|----------|-----------------|------|------| | 오디오 샘플레이트 | 16,000 Hz | **24,000 Hz** | **불일치** | 설계서는 Whisper 기본, Speakly는 Opus 기반 | | 오디오 프레임 크기 | 60ms | 60ms | 일치 | | | 바이트/프레임 | 1,920 | 계산 시 **2,880** (24000*2*0.06) | **불일치** | 샘플레이트 차이에 따름 | | 디바이스 폴링 간격 | 2초 | 확인 필요 | - | | | ERROR_AUTO_HIDE_DELAY | 미기술 | **3,000ms** | 누락 | | | ERROR_WITH_RETRY_AUTO_HIDE_DELAY | 미기술 | **10,000ms** | 누락 | | | NOTICE_AUTO_HIDE_DELAY | 미기술 | **3,000ms** | 누락 | | | MAX_RETRY_ATTEMPTS | 미기술 | **3** | 누락 | | | PRESS_HOLD_THRESHOLD | 미기술 | **10ms** | 누락 | Speakly 주석에 "changed from 300ms" | | SECOND_PRESS_THRESHOLD | 미기술 | **300ms** | 누락 | 더블 프레스 감지 | | STOP_DELAY_MS | 미기술 | **200ms** | 누락 | 키 릴리스 후 정지 지연 | | MIN_AUDIO_DURATION_MS | 미기술 | **700ms** | 누락 | accidentalPress 판정 | | MUTE_DELAY_MS | 미기술 | **500ms** | 누락 | 사운드 이펙트 후 음소거 | | UNMUTE_SOUND_DELAY_MS | 미기술 | **100ms** | 누락 | 언뮤트 후 효과음 지연 | | POST_RECORDING_WAIT_TIME | 미기술 | **4,000ms** | 누락 | 녹음 후 연결 대기 | | RETRY_MAX_CONNECT_WAIT_TIME | 미기술 | **15,000ms** | 누락 | 파일 리플레이 연결 대기 | | FINISH_IDLE_TIMEOUT | 미기술 | **30,000ms** | 누락 | commit 후 완료 대기 | | FINISH_MAX_WAIT | 미기술 | **120,000ms** | 누락 | 완료 절대 최대 대기 | | ResultPopup autoCloseDelay | 미기술 | **10,000ms** | 누락 | | | HEARTBEAT_INTERVAL | 미기술 | **5,000ms** | 누락 | WebSocket 핑 간격 | | HEARTBEAT_TIMEOUT | 미기술 | **15,000ms** | 누락 | 서버 응답 타임아웃 | | SESSION_MONITOR_INTERVAL | 미기술 | **5,000ms** | 누락 | 세션 모니터링 타이머 | | SYNC_DELAY_MS (HotkeyService) | 미기술 | **500ms** | 누락 | 핫키 동기화 디바운스 | **수정 제안**: `01-service-specifications.md` 각 서비스 섹션에 타이밍 상수 표 추가. D3RO 고유 값이 다를 경우 주석으로 Speakly 원본 값 명시. --- ## 6. DB 스키마 검증 ### MINOR-02: history 테이블 컬럼 차이 | 컬럼 | 설계서 | Speakly 실제 | 판정 | |------|--------|-------------|------| | `focused_app_bundle_id` | 제거됨 | **있음** | 설계서에서 의도적 제거 (macOS 전용) → OK | | `window_web_title` | 제거됨 | **있음** | 설계서에서 의도적 제거 → OK | | `window_web_domain` | 제거됨 | **있음** | 설계서에서 의도적 제거 → OK | | `window_web_url` | 제거됨 | **있음** | 설계서에서 의도적 제거 → OK | | `audio_metadata` | 제거됨 | **있음** (JSON) | 설계서에서 의도적 제거 → OK | | `mic_device_info` | 제거됨 | **있음** (JSON) | 설계서에서 의도적 제거 → OK | | `user_id` | 제거됨 | **있음** | 로컬 단일 사용자 → OK | | `error_code` | **추가됨** | 없음 | D3RO 신규 — OK | | `stt_model` | **추가됨** | 없음 | D3RO 신규 — OK | | `llm_model` | **추가됨** | 없음 | D3RO 신규 — OK | | `stt_latency_ms` | **추가됨** | 없음 | D3RO 신규 — OK | | `llm_latency_ms` | **추가됨** | 없음 | D3RO 신규 — OK | | `selected_text` | 미언급 | **없음** | 설계서 "제거" 목록에 있으나 Speakly에도 없음 → 각주 정정 필요 | ### MINOR-03: history 모드 열거형 차이 - 설계서: `'dictation' | 'translate' | 'command'` - Speakly: `'dictation' | 'hands-free' | 'translation'` - **수정 제안**: D3RO 고유 모드명 사용은 문제 없으나, Speakly 원본과의 매핑을 각주로 명시 ### MINOR-04: stats 테이블 차이 - 설계서에 `streak_days`, `last_session_at` 추가 — Speakly에 없는 D3RO 신규 필드 → OK - Speakly의 `stats` 테이블은 설계서와 동일한 구조 (id=1 싱글턴) ### MINOR-05: 인덱스 차이 - 설계서: `idx_history_created_at` (단일 컬럼) - Speakly: `idx_history_user_created_at` (user_id, created_at 복합 인덱스) - 로컬 단일 사용자이므로 설계서의 단순화가 적절 → OK --- ## 7. 추가 발견 사항 ### MINOR-06: NativeService koffi+DLL 대체 방안 명확화 필요 - **실제 소스**: `NativeService.js` 44행 — `koffi` 모듈로 네이티브 DLL 로드 - **설계서 위치**: `00-master-architecture.md` 166행 — "제거, npm 패키지로 대체" - **이슈**: NativeService는 키보드 모니터링, 마이크 캡처, 시스템 오디오 음소거, 앱 모니터링 등 광범위한 기능 제공. 이를 대체할 npm 패키지 목록이 불완전. - **수정 제안**: NativeService가 제공하는 기능별 대체 패키지 매핑 표 작성: - 키보드 모니터링 → `uiohook-napi` (기술됨) - 마이크 캡처 → 미명시 (Web Audio API? node-audiorecorder?) - 시스템 오디오 음소거 → 미명시 (`loudness`? `@aspect-build/napi-audio`?) - 앱 모니터링 → 미명시 (`active-win`?) - 클립보드 → `@nut-tree/nut` (기술됨) - 텍스트 삽입 → `@nut-tree/nut` (기술됨) - 핫키 인터셉트 → `uiohook-napi` (기술됨) ### MINOR-07: TextOperationStrategy의 앱별 삽입 전략 미기술 - **실제 소스**: `TextOperationStrategy.js` 12-36행 - **패턴**: 앱별로 다른 텍스트 삽입 방법 (clipboard vs keyboard), 선택 방법 (clipboard vs ax vs none), 검증 모드 (auto vs skip) 설정 - **설계서**: `TextInsertService`에서 일률적으로 "clipboard save → set → Ctrl+V → restore" 기술 - **수정 제안**: 앱별 규칙(rules) 시스템 최소한으로 기술. D3RO 로컬 환경에서는 원격 config 불필요하나, 기본 fallback 규칙은 유지 필요. ### MINOR-08: 세션 모니터링 패턴 - **실제 소스**: `VoiceModeService.js` 184-185행, 209-210행 - **패턴**: `sessionMonitorTimer` (5초 간격) — 좀비 세션 감지 및 정리 - **설계서 상태**: 누락 - **수정 제안**: VoiceModeService 안정성 패턴으로 추가 --- ## 8. 설계서에 추가해야 할 패턴 목록 (우선순위순) | # | 패턴 | 중요도 | 대상 설계서 | |---|------|--------|------------| | 1 | **Action Queue (이벤트 직렬화)** | Critical | 01-service-specifications.md | | 2 | **accidentalPress 감지** | Critical | 01-service-specifications.md | | 3 | **Audio Mute 연동** | Critical | 01-service-specifications.md | | 4 | **Retry 로직 (오디오 파일 재전송)** | Critical | 01-service-specifications.md | | 5 | **RecognitionState CONNECTING/DESTROYED** | Critical | 02-ipc-and-types.md | | 6 | **hands-free-no-wake 모드** | Major | 01-service-specifications.md | | 7 | **Mode Switch (녹음 중 모드 전환)** | Major | 01-service-specifications.md | | 8 | **ResultPopup auto-close 타이밍** | Major | 03-db-and-ui.md | | 9 | **NewMicrophonePrompt 윈도우** | Major | 03-db-and-ui.md | | 10 | **RecordingTipUIState 열거형** | Major | 02-ipc-and-types.md | | 11 | **타이밍 상수 표** | Major | 01-service-specifications.md | | 12 | **누락 IPC 채널 (~25개)** | Major | 02-ipc-and-types.md | | 13 | **NativeService 대체 패키지 매핑** | Minor | 00-master-architecture.md | | 14 | **앱별 TextOperation 규칙** | Minor | 01-service-specifications.md | | 15 | **Typing Nudge** | Minor | 결정 후 추가 | | 16 | **세션 모니터링 타이머** | Minor | 01-service-specifications.md | | 17 | **RecordingTip preload 전략** | Minor | 00-master-architecture.md | --- ## 9. 결론 설계서는 Speakly의 기본 아키텍처와 주요 서비스 구조를 잘 반영하고 있으나, **VoiceModeService의 핵심 런타임 패턴**(Action Queue, accidentalPress, mute 연동, retry, mode switch)이 거의 완전히 누락되어 있다. 이 패턴들은 사용자 경험의 안정성과 직결되므로 구현 전에 반드시 설계서에 반영해야 한다. 또한 IPC 채널이 약 25개 누락되어 있으며, 특히 hotkey 관련 채널(pause/resume, validate, availableKeys, no-wake 단축키)과 history:retry 채널이 중요하다. 상태 머신은 대체로 일치하나 `CONNECTING`과 `DESTROYED` 상태가 빠져 있어 상태 전이 다이어그램 수정이 필요하다.