# Round 1 开发自查记录 状态:开发自查记录 所属子项目:`02-P-NBL` Round:Round 1 日期:2026-06-02 对应文档: - [`prd.md`](prd.md) - [`engineering-tasks.md`](engineering-tasks.md) - [`qa-test-plan.md`](qa-test-plan.md) ## 1. 基本信息 | 项目 | 内容 | |---|---| | 分支 | `codex/round1-model-profile` | | commit id | 由最终 QA 交接信息填写 | | 研发负责人 | Codex | | 自查日期 | 2026-06-02 | | App 版本 / build | 未打包 | | macOS 版本 | 当前本机环境 | | 芯片 | 当前本机环境 | ## 2. 本次实现范围 已完成: - 新增 `ModelProfile` / `ModelRegistry`。 - 新增 `LanguageProfile` / `LanguageRegistry` 最小实现。 - 新增 `RequiredFileRule`,支持 `AllOf` / `AnyOf`。 - 新增 `ModelInstallState`,支持 `models/state.json` 读写。 - 新增 `ModelResolver`,支持 state、新目录、旧目录 fallback。 - 新增 `ResolvedModel`。 - 新增 `sensevoice-zh` profile。 - `config.Config` 增加隐藏字段 `SelectedModelID`,默认 `sensevoice-zh`。 - `engine.New()` 保持外部兼容,内部改为通过 resolver 加载 selected/default model。 - macOS/Linux/Windows 平台 engine 改为接收 `ResolvedModel`。 - 下载器改为临时下载、解压、staging 校验、安全安装到 `models/sensevoice-zh`。 - 下载流程已覆盖“primary 下载成功但解压/校验失败时继续尝试 fallback URL”。 - 用户主动触发下载时,`models/sensevoice-zh` 可被 staging 校验通过的新模型安全替换;替换失败会尝试恢复 backup。 - Resolver 已拒绝 `state.json` 中的绝对路径或逃逸 `models` 根目录的相对路径。 - macOS engine 已使用 profile 中的 `ProviderOrder`。 - engine 初始化失败时会区分“缺模型”和“结构存在但不可加载”:后者发送 `engine:status = error`。 - 保留 `EngineService.ModelExists()` 和 `EngineService.DownloadModel(primaryURL, fallbackURL)` 兼容签名。 - 新增 `EngineService.GetStatus()`,用于前端补查可能早于订阅发生的 engine 状态。 - Onboarding 已处理 `engine:status = need_model` 且带错误信息的场景。 - 新增模型相关单元测试。 未做,符合 Round 1 非目标: - 未接入 Moonshine。 - 未接入 Parakeet。 - 未接入 Qwen3-ASR。 - 未新增语言 UI。 - 未新增模型选择 UI。 - 未新增模型删除 UI。 - 未自动迁移旧模型目录。 ## 3. 变更文件 代码: - `privatevoice.src/internal/model/profile.go` - `privatevoice.src/internal/model/registry.go` - `privatevoice.src/internal/model/validator.go` - `privatevoice.src/internal/model/install_state.go` - `privatevoice.src/internal/model/resolver.go` - `privatevoice.src/internal/model/downloader.go` - `privatevoice.src/internal/model/model_test.go` - `privatevoice.src/internal/engine/engine.go` - `privatevoice.src/internal/engine/engine_darwin.go` - `privatevoice.src/internal/engine/engine_linux.go` - `privatevoice.src/internal/engine/engine_windows.go` - `privatevoice.src/internal/config/config.go` - `privatevoice.src/internal/paths/paths.go` 文档: - `02-P-NBL/round-1/prd.md` - `02-P-NBL/round-1/engineering-tasks.md` - `02-P-NBL/round-1/qa-test-plan.md` - `02-P-NBL/round-1/dev-checklist.md` - `02-P-NBL/round-1/dev-checklist-current.md` - `02-P-NBL/round-1/qa-report-template.md` - `02-P-NBL/round-1/README.md` ## 4. 自动化验证 | 命令 | 结果 | 说明 | |---|---|---| | `cd privatevoice.src/frontend && npm run build` | 通过 | 存在既有 Svelte a11y warning,构建成功 | | `cd privatevoice.src && go test ./internal/model -count=1` | 通过 | 强制重跑模型包测试,非缓存 | | `cd privatevoice.src && go test ./... -count=1` | 通过 | 强制重跑全量 Go 测试,非缓存;新 checkout 需先完成前端构建以生成 `frontend/dist` | 前端构建警告: - `UpdateDialog.svelte` 的 dialog focus / noninteractive listener 警告。 - `ToggleSwitch.svelte` 的 button aria label 警告。 这些警告不由本轮改动引入,本轮未修改相关组件。 ## 5. 已覆盖的自动化场景 - 默认 model profile 为 `sensevoice-zh`。 - 未知 model id 返回错误。 - `AnyOf` 规则允许 `model.onnx`。 - `model.int8.onnx` 优先于 `model.onnx`。 - 缺少 `tokens.txt` 时校验失败。 - 0 字节模型文件校验失败。 - 无 state 时旧 `models/sensevoice` 可解析。 - 无 state 时新旧目录都存在,优先新 `models/sensevoice-zh`。 - 非法 `state.json` 时 fallback 到旧目录。 - state 指向坏目录时 fallback 到旧目录。 - state 指向绝对路径或逃逸 `models` 根目录的路径时被拒绝并 fallback。 - 不完整目录返回 `installed_incomplete`。 - state round trip。 - 下载失败不删除旧模型目录。 - primary 坏包时尝试 fallback URL。 - bad archive 不写入 installed state。 - 用户触发下载可替换已有 `models/sensevoice-zh`。 ## 6. 未完成验证 以下需要 QA 在真机或测试包阶段执行: - 真实 SenseVoice 模型下载。 - 真实模型初始化。 - 真实录音识别。 - replacement 在真实识别链路中生效。 - 历史记录真实写入。 - macOS 麦克风权限未授权/已授权回归。 - 断网已有模型完整使用。 - 网络观察确认识别阶段无上传。 - 菜单栏、Dock、窗口层级、全屏、Split View smoke。 ## 7. 移交 QA 结论 开发自查结论: ```text 可进入 QA 预检。 ``` 说明: ```text 代码级自动化测试和前端构建已通过,但尚未打包,也尚未执行真机真实模型回归。 根据项目交付门禁,本状态不能直接交付用户验收,需要 QA 按 qa-test-plan.md 继续验证。 ```