Files

134 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 验收记录
日期:2026-10-23。此记录区分代码回归、真实系统测试与尚未通过的验收。
## 已通过
- SwiftPM debug 编译及 Swift Testing:17 项测试通过,0 失败。
- Release `.app` 构建,真实 Apple Development 身份签名,沙箱外 `codesign --verify --strict` 通过。
- Info.plist 格式、构建/签名/测试脚本语法检查通过。
- 图标脚本生成 10 个尺寸 PNG、1024 px logo 和 ICNS;大图人工查看,16/32 px 简化细节。图标已包含在 app 中。
- 通过 macOS 原生界面检查通用、快捷键、语音和关于页面。右 Option、按一次开始再按一次结束、固定普通话、自动补句号均与用户确认一致;麦克风与辅助功能显示已授权。
- Release app 约 1.3 MiB(不含系统下载模型),满足 <10 MB 包体目标。
## 自动回归范围
1. 标点保留、补句号、引号、内部空格、口头语与空白输入。
2. 热键 JSON 往返和左右键命名。
3. 静音/有声音量估计与 48 kHz → 16 kHz 重采样。
4. 超时不会等待不响应取消的操作结束。
5. 准备中结束并立即重开:旧会话不会接管新会话。
6. 收尾中取消:不发布结果,不写入。
7. 开始录音失败:清理会话与测试状态。
8. 正常测试模式结束:保留结果,退出测试模式,不写入。
9. 拒绝麦克风:不创建识别会话。
10. 识别运行时失败:取消录音并反馈错误。
11. Secure Input:不开始采集。
12. 右 Option 使用事件自带左右键标志,左键保持按下时仍正确检测右键释放。
13. 等待旧版语音授权时取消,不启动麦克风。
14. 测试模式返回空文字时明确提示失败。
其中状态机回归使用可控会话替身,不代表真实麦克风和跨应用兼容性。
## 早期真实模型测试:失败记录(后续恢复见下)
分别运行已签名 app 的 `--selftest` 与 `--selftest --dictation`。两者均确认中文系统模型为 `installed`;备用测试已完成系统合成语音与文件读取。两条路径都在 SpeechAnalyzer 准备阶段被系统语音服务拒绝:
```
Espresso V2 failure: ANE model load failure:
loadModel:sandboxExtension:options:qos:withReply:: timed out
```
错误来自 Apple EmbeddedAcousticRecognition / localspeechrecognition 服务,已在沙箱外重现。自检返回非零退出码,不会把空结果或关键词不匹配算作成功。自检有 90 秒总超时保护。
因此没有可报告的成功识别延迟,首字 <300 ms、结束到上屏 <500 ms 均未验收。自动模式包含 SpeechTranscriber → DictationTranscriber → 强制本地 SFSpeechRecognizer 降级;旧版真实麦克风路径未验收,不保证能绕过当前系统服务错误。
## 资源与待验收项
一次已运行、打开过四页 SwiftUI 设置的进程快照为 CPU 0.0%、RSS 97,840 KiB(约 95.5 MiB)。这不是长期采样,且未达到 <80 MB 内存目标。后续已加入关闭设置即释放界面、录音时才创建 AVAudioEngine、预热 10 分钟后释放;该优化后尚未获得可比的完整资源验收。
下列项目仍需在本地语音服务正常的机器上完成,不能凭单元测试推定通过:
- 真人麦克风:右 Option 开始/结束、Esc 取消、12 秒无声结束。
- TextEdit、Chrome 输入框、终端分别测试插入点、选区替换、剪贴板恢复与失败恢复。终端使用无副作用的测试文本,不按回车执行。
- AX 回读不一致时不会重复粘贴;前台应用改变或安全输入阻止写入;取消后旧会话不写入。
- 模型首次下载、断网下载失败、重试;全本地降级的真实识别。
- 多屏、Space、全屏窗口的 HUD 定位与不抢焦点。
- 菜单栏录音/收尾动画、设置改绑后真实按键、重启后的偏好保存。
- 登录启动与系统批准;长时间待机 CPU、预热过期后内存;实际真人语音延迟。
## 复现
```bash
./scripts/test.sh
./scripts/build.sh
codesign --verify --strict dist/TinyTTS.app
./dist/TinyTTS.app/Contents/MacOS/TinyTTS --selftest
./dist/TinyTTS.app/Contents/MacOS/TinyTTS --selftest --dictation
```
受限执行环境中 SwiftPM 缓存目录会有不可写提示;脚本已将模块缓存放在仓库 `.build`。macOS 图像服务、dSYM 和签名信任校验可能需要在普通本机终端执行。沙箱内 `CSSMERR_TP_NOT_TRUSTED` 已用沙箱外严格签名校验排除,未更改系统信任设置。
## 本次续作验证
- 重新运行 `./scripts/test.sh`:14 项通过,0 失败。
- 重新构建 0.1.2,使用现有 Apple Development 固定身份签名,严格签名校验通过。
- 现有日志确认修复后右 Option 事件已触发,备用引擎曾在约 126–198 ms 内开始采集,收到非静音音频,但最终返回 0 字。该记录不能当作成功识别或写入验收。
- 普通本机环境运行 `--selftest --legacy`:授权状态为 `notDetermined`,明确失败退出。先前 GUI 进程显示已授权与命令行自检结果不同,因此命令行结果不能证明 GUI 权限被撤销,也不能验证旧版识别成功。
- 测试模式现在会对空结果显示错误;取消权限等待后不再继续获取识别会话。
本次重新运行 `--selftest` 仍以非零状态失败,原始错误为 `Espresso V2 failure: ANE model load failure: loadModel:sandboxExtension:options:qos:withReply:: timed out`,发生在已安装模型的准备阶段;未采集麦克风。
## ANE 恢复与字幕合并修复
先定向重启当前用户的语音识别服务,主模型仍返回同一 ANE 模型加载超时。经用户选择原生管理员授权后,使用 macOS 授权对话框尝试 `launchctl kickstart`,被 SIP 拒绝;随后通过正常 SIGTERM 终止已确认的 `aned` 服务,由系统重新拉起,未关闭或修改 SIP。ANE PID 从 588 变为 42024。
恢复后真实模型自检通过:
| 引擎 | 识别关键词 | 音频开始至首字 | 音频结束至最终结果 |
|---|---|---:|---:|
| SpeechTranscriber | 天气、开会 | 1230 ms | 157 ms |
| DictationTranscriber | 天气、开会 | 581 ms | 89 ms |
这些是固定语句实时喂入的单次结果,包含语音起始内容与模型调度,不代表麦克风首字延迟或跨应用上屏。
GUI 真实麦克风通过扬声器播放固定测试语句:实时字幕返回完整测试句,收尾成功。首次实录发现最终结果的时间起点与临时结果不完全一致,按起点精确匹配使整句重复。现改用时间覆盖范围合并,补充起点偏移、过时临时结果、连续多句的回归测试;修复后再次实录,收尾前后文字一致,不再重复。该次启动采集总耗时 241 ms,收尾 108 ms。
语音页增加「重新准备引擎」,系统服务恢复后无需退出即可清除降级状态。合成语音诊断改为检查当前所选引擎,诊断期间禁止同时开始麦克风测试或更改引擎。
最新自动回归:17 项通过,0 失败。Release 固定身份签名与严格校验通过。主应用包约 1.44 MiB。一次实录后关闭设置的快照为 RSS 106464 KiB、CPU 4.1%;它不是长期空闲采样,不能认定 <80 MB / <1% 目标已通过。
## 默认直接输入修复(0.1.3)
物理右 Option 已触发录音与结束,得到非空结果;旧版 AX 返回成功但回读不一致,只留下剪贴板结果,不满足自动输入要求。
默认方式现改为 Unicode 键盘事件,直接发送到开始时的目标进程,清除仍按住的修饰键状态,不读写剪贴板。事件分块不拆开 UTF-16 代理对,并标记自发事件以免触发自定义热键。前台进程改变或安全输入仍阻止注入,结果仅保留在进程内供用户找回。显式选择「始终使用粘贴」才走剪贴板兼容路径。
新增 `--selftest --input`,由正常 LaunchServices 启动已签名 app,创建真实 AppKit 文本输入窗口,自动验证中文/英文/emoji 长文本实际输入、选区替换以及剪贴板版本号保持不变。实测输出:
```
SELFTEST PASSED (real AppKit Unicode input, selection replacement, clipboard unchanged)
```
Swift Testing 当前 17 项通过;0.1.3 Release 固定身份签名与严格校验通过。外部应用的物理热键听写验收继续以 UI 实际内容与日志为准,不能只看事件发出。
## 物理热键与实际输入反馈
用户在本聊天输入框实测右 Option 开始、说话、再次按键结束,并反馈直接输入已通过。同期日志记录多次 `SpeechSession` 采集及 `insert via 直接输入 ok=true`,结束到输入事件发出约 64–81 ms。该用户反馈与真实 AppKit 输入自检共同确认当前主链可用;TextEdit 文稿并未收到这次用户测试内容,不把该次测试写成 TextEdit 兼容性通过。Chrome 与终端的独立验收仍未完成。
按用户要求,完成输入提示音改为系统 Glass「叮」声,仅在输入派发成功时播放。开始录音的提示保持原设置。
## 应用更名
按用户纠正,将当前工程、Swift 模块、测试模块、界面及产物统一改为 TinySTT / `dist/TinySTT.app`。保留 `com.eccstartup.tinystt` bundle ID 与既有偏好键以保持权限和设置关联。新的签名覆盖变量为 `TINYSTT_SIGN_IDENTITY`,仍兼容旧变量和已安装的 TinyTTS Dev 证书。旧验收段落中的 TinyTTS 路径为历史记录;当前复现请使用 README 中的 TinySTT 命令。
新版 T 字母图标使用内置 image_gen 生成,保存项目内源图和完整提示词,再以 AppKit 脚本生成 16–1024 px PNG 及 ICNS;不依赖外部临时图片路径。更名后的 TinySTT.app 再次通过真实 AppKit 直接输入、选区替换与剪贴板未变自检。
最终 TinySTT.app(0.1.3)包体 2840 KiB(约 2.77 MiB),固定身份签名与严格校验通过;包内 ICNS 与项目内图标 SHA-256 完全一致。16/32 px 图标已查看,T 字轮廓可辨。旧名称的构建产物已移除,仅保留 TinySTT.app。
## 0.1.4 菜单栏标识与 DMG
菜单栏原先独立绘制系统 mic/waveform 图标,未使用新品牌图标。现统一加载包内同款 T 图标:待机显示 T,录音保留 T 并显示红点和呼吸效果,准备/收尾保留 T 并显示旋转角标。
17 项回归通过,Release 固定身份签名及严格校验通过。新增 `scripts/package-dmg.sh` 生成只读 `dist/TinySTT-0.1.4-arm64.dmg`(约 2.2 MB),包含应用、Applications 快捷方式和安装说明。hdiutil 校验通过,实际只读挂载后检查应用签名、版本、可执行文件与构建产物一致、Applications 链接指向正确;验收后已卸载镜像。