核对完成。以下为实现状态地图（证据均为相对 `Sources/VoiceInputCore/` 等仓库根路径）。

# VoiceInput 文档 vs 实现状态地图

总量：Sources 39 个 Swift 文件约 7800 行 + Tests 5 个文件约 660 行。文档状态「设计定稿，待实现」已严重过时——核心逻辑层（Session/ASR/Store/Injection）大体写完，但 **App 装配层停在 M0/M1：热键、录音链路、模型配置 UI 均未接线**。

## 1. 目录结构差异

**结构级差异**：文档 §5.3 是单 target `Sources/VoiceInput/`；实际是 SwiftPM 双 target `Sources/VoiceInputCore/`（纯逻辑库）+ `Sources/VoiceInputApp/`（仅 94 行 `VoiceInputAppMain.swift` 入口），由 `Package.swift:20-32` 与 `project.yml:26-50` 双入口装配。

实际存在但文档没写的文件：
- `App/AppEnvironment.swift` — 进程级单例，持有 AppState/OverlayPanel/SettingsWindowController 并提供 openSettings 入口
- `App/AppState.swift` — 文档写在 §5.3（有），但实际含 `Phase.degraded` 状态与 overlayDisplayText 计算
- `App/PermissionStatus.swift` — 两项 TCC 权限探测/引导
- `ASR/GzipCodec.swift` — 手工 gzip 封装（RFC1952），豆包 gzip 帧依赖
- `ASR/HttpFileASRSession.swift` — 文档 §5.3 只列 WS Providers，未列它
- `ASR/ProtocolImplementations.swift`（570 行）— 四套线格式实现（AliyunFun/OpenAIRealtime/DoubaoBinary/GeminiLive）+ ASRJSON 宽松解析
- `UI/Settings/SettingsChrome.swift` — 设置窗口视觉框架（对齐另一项目 Usage Monitor，非 Voxt）
- `UI/Settings/SettingsShellView.swift` — 设置页清单/侧栏/权限页/关于页
- `UI/Settings/{GeneralFormContent,MicrophoneFormContent,SettingsWindowController}.swift` — 通用/麦克风表单、显式 NSWindow 设置窗口
- `UI/MenuBar/MenuBarContentView.swift` — 菜单栏下拉内容
- `UI/DesignSystem/{DesignTokens,WaveformHistoryState}.swift` — 设计令牌与波形状态机
- `Tests/VoiceInputCoreTests/`（5 个文件，文档写 `Tests/VoiceInputTests/`）
- `Store/ModelConfiguration.swift` — L2–L4 数据模型（ProviderInstance/ModelEntry/ModelProfile/ModelParameters）

文档写了但实际不存在的文件：
- `App/VoiceInputApp.swift`、`App/AppDelegate.swift`、`App/MenuBarController.swift`（AppDelegate 合并进 VoiceInputAppMain.swift:67；菜单栏用 MenuBarExtra 无独立 controller）
- `Session/SessionState.swift`（状态并入 SessionCoordinator）
- `Session/GenerationGuard.swift`（并入 SessionCoordinator.swift:18-32）
- `ASR/ASRConnectionManager.swift`（保活/重连下沉进 WebSocketASRSession actor）
- `ASR/Providers/*.swift` 5 个文件（改为 ASRProviderRegistry + ProtocolImplementations 声明式模板）
- `Enhance/LLMClient.swift`、`Enhance/OpenAICompatibleLLM.swift`、`Enhance/PromptTemplates.swift`（合并为单文件 TextEnhancer.swift，枚举即 prompt 模板）
- `Injection/FocusRestorer.swift`（焦点恢复并入 TextInjector.restoreFocusIfNeeded）
- `Hotkey/HotkeyTrigger.swift`（并入 HotkeyManager.swift:20-84）
- `Store/SQLiteDatabase.swift`（SQLite 直接嵌在 HistoryStore.swift）
- `Support/Diagnostics.swift`（无任何诊断导出）
- `docs/PROVIDERS.zh-CN.md`、`docs/UI-SPEC.zh-CN.md`、`scripts/`
- Toast 组件、`UI/Components/` 目录（§10.5 Toast 完全无实现）

## 2. 里程碑进度

- **M0 骨架 — 已完成**：`project.yml`+`run`（157 行 xcodebuild 包装，含 -t/--check）；菜单栏图标 `VoiceInputAppMain.swift:25-34`；设置窗口可开 `AppEnvironment.swift:26-28`；设计令牌 `DesignTokens.swift`
- **M1 音频+设备 — 部分完成（库代码全、未接线）**：AudioDeviceManager/AudioDeviceIdentity（幽灵过滤 `AudioDeviceIdentity.swift:157-171`）、电平表 `AudioLevelMonitor.swift`、麦克风设置页 `MicrophoneFormContent.swift` 均可用；但 `AudioCaptureEngine.prepare()` 无任何调用者（预热未接）、HotkeyManager 无任何实例化调用者、muteSystemOutput 只有设置项无实现
- **M2 注入 — 部分完成**：TextInjector/PasteboardGuard/TextTargetProbe 逻辑完整（§3 详列），但因 SessionCoordinator 未装配、热键未启动，运行时永远走不到
- **M3 协议内核 — 部分完成**：协议+事件模型+6 模板注册表+models.json 读写全部存在；**「模型配置界面」缺失**——`SettingsShellView.swift:76-81` 明确 `isImplemented` 仅 general/microphone/permissions/about，models 页是占位「该页面将在 M3 / M5 里程碑实现」；`ASRProviderRegistry.builtInInstances():312` 定义了但无调用者（首次启动不播种）
- **M4 Provider 落地 — 部分完成**：5 个流式模板+http-file 全部注册（`ASRProviderRegistry.swift:16-23`），但全部未经真实服务端验证，豆包 finish 帧未接（见 §4）
- **M5 自定义闭环 — 未开始**：无测试连接流程、无 `GET /v1/models` 拉取；导入导出仅有底层 `ModelConfigurationStore.swift:199-219`（exportData/importData），无 UI、无「包含密钥」选项
- **M6 延迟优化 — 部分完成**：逐句上屏（SessionCoordinator.swift:305-307）、保活重连背压（WebSocketASRSession）、LatencyMetrics 全套打点+P50/P95 均实现；**诊断面板是占位页**（diagnostics 未实现）；终态 800ms 有界等待未实现（watchdogTask 声明于 SessionCoordinator.swift:79 但从未调度）
- **M7 润色与历史 — 部分完成**：TextEnhancer 完整（4 模式 prompt + chat/completions）；HistoryStore 完整（SQLite 建表/读写/裁剪/检索）；**但 enhancer 属性在 SessionCoordinator 里从未被调用**（grep 仅见声明与注入），历史 UI 为占位，菜单栏无「最近使用」
- **M8 打磨 — 未开始**

**关键问题的直接回答**：
- SessionCoordinator 存在（460 行，`Session/SessionCoordinator.swift`），实现完整会话编排：代次守卫、exactly-once（hasClaimedOutput:350）、逐句/整段注入、失败兜底 `finishWithPartialOutput:387`、历史写入与延迟打点——但 **全工程无任何 `SessionCoordinator(...)` 实例化**，是孤儿代码
- LatencyMetrics 在用：`SessionCoordinator.finishSession:404` 调 `recorder.record(trace)`；LatencyRecorder 容量 20、P50/P95；但因其宿主未装配，运行时无数据，诊断 UI 也没有
- Session/ 目录：SessionCoordinator.swift + LatencyMetrics.swift 两个文件（SessionState/GenerationGuard 已合并）
- AppState 承担：Phase 状态机、权限刷新、文本/状态消息、悬浮层呈现标记（AppState.swift:42-141），头注释自称「M0 阶段只承载状态外壳；会话编排在 M6 接入」；AppEnvironment 承担：单例持有 AppState+OverlayPanel+SettingsWindowController、openSettings、激活时权限轮询（AppEnvironment.swift:19-44）——**二者都不做会话编排**

## 3. 逐节实现核对

**§6 延迟设计**
- 60ms 分片：✅ `AudioCaptureEngine.handleCapturedBuffer:305-310` 聚合到 chunkByteCount(60ms=1920B)；stop 时 flushTail 补尾包
- 常驻引擎：⚠️ `prepare()`/常驻设计存在（AudioCaptureEngine.swift:119-125）但无人调用，且 start() 每次装 tap；「启动后 2 秒预热」无实现
- 预热优先 UI：❌ 无（悬浮层 show 从未被调用）
- 重采样：✅ `AudioResampler.swift` 用 AVAudioConverter，16k/mono/Int16 目标；tap 格式优先硬件采样率 `AudioDeviceManager.captureTapFormat:129-145`
- 打点：✅ LatencyTrace 9 阶段+degraded+degradationReasons；但 `overlayToFirstPCMMS` 的采样点有逻辑问题（在 `.ready` 事件时回填，SessionCoordinator.swift:284-287，与 PCM 无关）
- 防卡顿：✅ 回调只拷贝投递（AudioCaptureEngine.swift:218-224）；波形 20fps 节流在 WaveformHistoryState.ingest；文本 70ms 节流 ❌ 无（overlay 直接绑定 state）

**§7 设备管理**
- 三档选择策略：⚠️ 枚举有 autoSelect（SettingsStore.swift:24-28）+ 菜单脚注，但 **MicrophoneFormContent.swift:31-41 的 Picker 只有跟随系统/锁定两项**，autoSelect 无 UI、无「录音时选最响」实现
- 电平表：✅ 独立引擎+10fps+每设备一行 `AudioLevelMonitor.swift`；峰值 dB 显示是占位（MicrophoneFormContent.peakLevel 永远 0）
- 幽灵设备过滤：✅ `AudioDeviceIdentityResolver.isPhantom:157-171`（含 CADefaultDeviceAggregate 前缀+transport 判定）；去重 ✅ normalizedName 后缀表 + `AudioDeviceManager.deduplicate:70-82`
- 看门狗：✅ 1.2s 无 PCM → 重启输入图一次 → 降级系统默认再试（AudioCaptureEngine.swift:358-401）
- 设备变化监听：✅ AudioDeviceObserver（插拔+默认设备双监听）；`AVAudioEngineConfigurationChange` 自愈 ❌ 无

**§8 Provider 体系**
- 协议+声明表：✅ ASRSession 协议（含 resumeSupport）、ASRProtocolTemplate；与文档差异：无独立 `ASRProvider.connect()` 层，sessionFactory 直接产 session
- L1–L4：✅ 全套（ModelConfiguration.swift）；inputSampleRate 默认 16000 而非文档必填
- 注册了 **6 个模板**：openai-realtime、aliyun-qwen-realtime、aliyun-fun-realtime、doubao-binary、gemini-live、http-file（ASRProviderRegistry.swift:16-23），含 4 个内置预填实例与 3 个示例模型条目
- WebSocketASRSession：分片节奏 ✅（由上层 60ms append 驱动）；保活 ⚠️（200ms 静音帧存在，但只在**收到服务端消息后**检查 maybeSendKeepalive:278，服务端静默时永不触发）；重连 ✅（0.5/1/2s 指数退避上限 3 次+3s 音频回填:314-364）；背压 ✅（1MB 上限丢最旧+计数:190-199）；**终态有界等待 ❌**（无 800ms 超时，靠 reducer 的 finished/failed 自然终止）
- 握手前本地缓冲 ⚠️：pre-ready 音频只进 recentAudio 环形缓冲（3s）且 ready 后**不回放**（WebSocketASRSession.swift:148-149 `guard isReady else { return }`）——与文档「握手未完成则本地缓冲，不等待」不符，首段音频可能丢
- HttpFileASRSession：支持 multipart POST（model/language/hotwords 字段+WAV 容器+多形态响应提取+250ms 最短音频短路），✅ 但无 JSON 上传形态（ASRTransport.httpJSON 定义了无人用）
- 良性错误分类：✅ ASREvent.swift:137-173（ENOTCONN/Cancelled/NetworkConnectionLost）；取消≠失败 ✅（WebSocketASRSession.receive:280-283）
- 无音频短路：⚠️ 仅 http-file 有（HttpFileASRSession.swift:70-74）；WS 路径无 VAD 能量检测（§8.7 承诺 600ms 无语音不发请求）

**§9 注入**
- PasteboardGuard：✅ 全项 + 超出文档的重叠粘贴继承原快照（PasteboardGuard.swift:61-67）；自适应恢复=轮询 AX 值 50ms/次、上限 1.5s（scheduleRestore:120-134）
- AX 探测：✅ TextTargetProbe 完整——聚焦元素逐级回退、可写角色表、非可写排除表、AXSecureTextField 拒绝、AX 调用带超时（copyAttribute:316-326）、能力声明表 AppCapabilityRules（10 条规则:67-78）
- exactly-once：✅ hasClaimedOutput 单闸 + 代次守卫（SessionCoordinator.swift:145/350）
- 自触发标记：✅ eventSourceUserData 魔法值 0x564F49585649 + tap 内过滤（HotkeyManager.swift:95-103/174-176）
- 目标记录：✅ 会话开始 captureFrontmostTarget（SessionCoordinator.swift:167）；focus 恢复+40ms 等待（TextInjector.swift:61/114-115）
- AX 校验：✅ 读回 AXValue contains 比对、0.3s 重试、terminal 目标豁免（TextInjector.verify:232-252）
- 隐患：inject/verify 在 @MainActor 同步路径上 Thread.sleep（TextInjector.swift:115/244），与「主线程 0 帧 >16ms」目标冲突

**§10 UI**
- 悬浮层：⚠️ OverlayPanel+OverlayRootView 完整（nonactivating/canBecomeKey/token 防竞态/脱离动画事务），但 **show() 无调用者**，M0 固定 ignoresMouseEvents=true；结果态/Toast ❌
- 波形：✅ 全参数与文档一致（WaveformMetrics+WaveformHistoryState.default，idle 图案/辉光公式/处理中 5 点 loader 均有）
- 设置页：4/9 页实现（general/microphone/permissions/about）；models/enhancement/hotwords/history/diagnostics 均为占位（SettingsShellView.swift:75-94）；通用页有悬浮层外观/快捷键展示（只读）/注入模式/分片/终态等待滑杆
- 菜单栏：状态行+3 个热键项（禁用）+历史/设置/诊断入口+开机自启（假勾选）+退出；缺最近使用 5 条与检查更新（MenuBarContentView.swift:28-51）
- 设置窗口视觉对齐 Usage Monitor 而非文档 §10.7 的 Voxt 规格（SettingsChrome.swift 头注释）

**§11 持久化**
- SettingsStore：✅ 全部 17 键与文档一致（SettingsStore.swift:37-83），仅 hotkey 存 String 展示文本而非 KeyCombo 结构（不可绑定录制）
- models.json：✅ 原子写+变更前 .bak+解析失败回退+schema 迁移+拒绝高版本导入（ModelConfigurationStore.swift:47-99/206-218）；`$key:` 引用语法+Keychain 解析（KeychainStore.swift:111-147）
- Keychain：✅ AfterFirstUnlockThisDeviceOnly、update-then-add、存在性探测
- HistoryStore：✅ 存在，schema 与文档一致（列名 template_id vs 文档 provider_id 小偏差），200 条裁剪+关键词检索；**调用者仅 SessionCoordinator**（未装配）

## 4. 实现与文档的关键偏差

1. **装配断层（最大偏差）**：文档 §5.1 的完整数据流（热键→音频→ASR→注入）各环节都有库实现，但 App 入口只建了菜单栏+设置窗口；HotkeyManager、AudioCaptureEngine、SessionCoordinator、HistoryStore、TextEnhancer、OverlayPanel.show、ModelConfigurationStore、builtInInstances 全部无运行时调用者
2. 命名/形状：`ASRProvider` 协议被 `ASRProtocolTemplate.sessionFactory` 取代（无 connect(configuration:generation) 独立层）；`finish()` 变为 `finish(reason: ASRFinishReason)`
3. 豆包收尾帧缺失：`ASRProviderRegistry.swift:154` `finishFrames: { [] }`，负序列号结束帧 `DoubaoBinaryProtocol.finish(sequence:)`（ProtocolImplementations.swift:355-360）从未接线；`finishSequenceProvider` 参数（Registry:227）在 makeWebSocketSession 体内未使用，是死参数
4. 热键默认值不一致：文档 §11.1 `hotkey.pushToTalk = ⌥⇧Space`；代码 `HotkeyManager.pushToTalkDefault = ⌃Space`（HotkeyManager.swift:60）；settings 里字符串仍为 "⌥⇧Space"，两处脱节
5. toggleRecording 语义未实现：HotkeyBinding 注释自认「切换录音需要双击式再设计」（HotkeyManager.swift:56-58），三种触发器仅按下/松开配对，toggle 与 hold 实际同构
6. 保活触发时机缺陷（见 §3）；pre-ready 音频不回放（见 §3）
7. WS 会话 timeoutIntervalForRequest=45s 无差应用于全部模板；文档的「最近 endpoint 可达性缓存」无
8. 设置窗口视觉基准换成 Usage Monitor（SettingsChrome.swift:1-8），文档 §10.7 的 Voxt SettingsUIStyle 令牌未采用
9. Gemini reducer 的 finished 判定逻辑可疑（`state.sawStreamEnd && !sawStreamEnd` 时才 finished，ProtocolImplementations.swift:557-563，依赖收到 streamEnd 之后的**又一帧**），可能终态发不出
10. `AppState.Phase` 含文档没有的 `degraded`；菜单栏/图标相应扩展

## 5. 测试覆盖（5 个文件）

- `DesignTokensTests.swift`（149 行）：波形/悬浮层尺寸令牌契约——88.7pt 视觉宽、圆角/不透明度钳制范围等 §10 数值断言
- `GzipCodecTests.swift`（115 行）：gzip 往返、不可压缩返回 nil、损坏/魔数/截断拒绝、CRC32 已知向量、zip-bomb 膨胀比防护、MTIME=0 不泄漏时间
- `PrivacyTests.swift`（110 行）：日志敏感键脱敏（diagnosticLine 含 `<redacted>`）、权限面=2 项的静态守护、PermissionStatus 聚合一致性
- `SettingsStoreTests.swift`（111 行）：默认值规格、越界钳制、空串归一 nil、非法枚举回退（独立 UserDefaults suite）
- `WaveformHistoryStateTests.swift`（175 行）：默认参数与 §10.2 一致、非法参数收敛、未激活不累积等波形状态机行为（时钟显式注入）

**测试盲区**：SessionCoordinator、WebSocketASRSession、四个协议 reducer、ModelConfigurationStore、HistoryStore、PasteboardGuard/TextInjector/TextTargetProbe、HotkeyManager 全部零测试——已测的恰是最外圈的令牌/编解码/配置层，核心状态机无守护。