# 语音弱网第二轮：ESP32 实体验收

## 安全边界

本轮仅提交、测试、构建；不刷机、不部署、不重启生产网关。设备 `28:84:85:43:a2:00` 当前绑定生产网关 `gw.pangtiao.men`，实体验收须经授权使用独立 staging 网关，避免与生产连接争抢。重启后的录音恢复不在范围内；PCM 不默认落盘。测试 STT 替身通过不能替代真实 STT 验证。

## 证据与判定

每轮保留固件 SHA、构建时间、设备串口日志、staging 网关日志和时间轴；使用 voice_id 关联两端。日志只记录字节数、sequence、状态，不记录原始 PCM、token 或敏感 transcript。每个弱网场景至少重复 10 次；任何一次部分录音被当作成功即 FAIL。

| 操作 | 期望日志/状态 | PASS 条件 | 失败时重点抓什么证据 |
|---|---|---|---|
| 打开 Help，核对候选固件 | 版本及构建时间可见 | 与候选构建记录一致 | Help 照片、启动日志、刷写产物校验值 |
| 网络正常，长按 A 录制 3–5 秒后松开 | RECORDING → UPLOADING → TRANSCRIBING → AWAITING_CONFIRMATION | PCM 全量确认后才 STT；确认前 Agent 不收到输入 | capture bytes、voice_end total_bytes、STT 调用次数 |
| 持续长按 A，故意限速上传 | 松开前一直 RECORDING；麦克风持续采集 | 上传进度不取代录音状态；松开后完整识别首尾语句 | 手指/屏幕视频、capture END 时间、ACK 时间轴 |
| 松开 A 后限速，延长上传超过 20 秒 | 上传继续；网关 cap 不启动 STT | 全量 voice_end 到达前 STT 调用为零；录音时长不含上传等待 | cap、end、STT 时间戳与 recorded_duration_ms |
| 上传途中断网 3–10 秒，再恢复 | 保留模态/PCM；新连接 voice_status → voice_ack → resume | 从已确认位置恢复，不漏、不重复拼接 PCM | 两端 generation、voice_id、next_sequence、received bytes |
| 录音中断网，继续说话后松开 A，再恢复网络 | 录音仍受 20 秒/内存上限约束；离线松开结束 capture | 首尾语句均完整；END 不因旧 generation 丢弃 | capture START/END、字节数、断连时间、release 日志 |
| 网络恢复但旧连接延迟退出 | 新连接已恢复请求保持当前状态 | 旧连接 finally 不取消或再次 suspend 新连接录音 | 两个 connection_id 的退出顺序及状态日志 |
| staging 注入重复 chunk、重复 ACK、超前 ACK、重复 end | 重复 chunk 重 ACK；非法 ACK 不前移；end 幂等 | 无跳过 PCM、无重复 STT | sent_sequence、ack next_sequence、ACK reject、STT 次数 |
| staging 故意令 total_bytes 与收齐 PCM 不一致 | cancel/error，不启动 STT | 不输出部分 transcript，不报成功 | total_bytes_mismatch、received bytes、provider 调用记录 |
| STT 期间断网，恢复或在离线完成后恢复 | voice_status 重新交付待确认 transcript | STT 不重复执行，不自动发送 Agent | STT 起止、voice_status、transcript 重交付、Agent 输入次数 |
| 上传失败后 A RETRY，再 B DISCARD | RETRY 使用保留 PCM；DISCARD 即时销毁 | 重试有界、不无限发送；丢弃后无后续上传/STT | retry 时序、超时日志、cancel、PSRAM 状态 |
| 录音/上传期间 B 取消、切换 target 或出现 Approval | 明确销毁并停止后续操作 | 旧 voice_id 不复活、不发送到新 target | target generation、approval、cancel 及后续帧 |
| 持续录音超过 20 秒 | 到容量上限正常结束 capture，无覆写 | 内存有界；网关收到完整录音才 STT | capture 容量/字节数、heap、total_bytes |

## 最终收口新增验收项

以下场景均至少 10 轮，使用 staging 注入故障；软件回归通过不替代这些真机操作。

| 操作 | 期望日志/状态 | PASS 条件 | 失败时重点抓什么证据 |
|---|---|---|---|
| 全部 PCM 已 ACK，在发送 `voice_end` 瞬间断线；分别测试 END 未到达与 terminal ACK 未到达 | 未收到 `voice_end_ack` 前保留上传状态；重连查询状态，必要时重发 END | 只有 Gateway 明确接受完整 END 后才释放上传 PCM；无假成功、无重复 STT | voice_id、最后 chunk ACK、END send 与远端接收时间、terminal ACK、设备 retained 状态 |
| 上传无法完成，持续离线超过 120 秒，再按 A 并重连 | `UPLOAD EXPIRED / B CLOSE`，无 A RETRY；本地 PCM 已释放，恢复连接补发取消 | A 不触发上传；Gateway 请求最终清理；B 可关闭；区别于 45 秒的可重试失败 | 硬期限起点、expiry 日志、PCM 字节/heap、屏幕照片、重连取消与 Gateway 请求状态 |
| 已确认 END，STT 尚未完成时断线并重连；再测试 STT 在离线时完成后重连 | 保留 remote-result pending 的 voice_id；hello/target 同步后主动 `voice_status`，结果重新交付 | provider 只调用一次；transcript 可显示并等待确认；不自动或重复向 Agent send | 新旧 connection/generation、hello/target/status 时序、provider 调用数、transcript 交付数、Agent 输入数 |
| remote-result pending 期间切换 target、出现 Approval 或显式取消，再重连 | pending 被销毁，不查询/恢复旧 Voice | 旧 transcript 不复活、不进入其他 target；Gateway 请求释放 | invalidation 原因、target generation、取消日志、后续 status/transcript 拒绝记录 |

## 软件门禁

必须同时通过固件 host 回归、网关全量与语音专项、脚本 smoke、ESP-IDF 5.5.4 固件构建及构建后校验，才可标记 `SOFTWARE READY FOR ESP32 PHYSICAL ACCEPTANCE`。macOS 的 Linux peer-credential 测试不能被跳过后宣称全绿，应在隔离 Linux 候选目录复验。
### 第二轮实际软件结果（历史记录）

- Linux 隔离目录：`/home/lighthouse/esp32-voice-round2-candidate`，未变更生产运行目录。
- `scripts/test-server.sh` 全部通过：固件 host、24 项 Node、320 项 Python、网关 smoke 和 Pi/Codex TLS WSS voice smoke。
- Python 仅有现有 Starlette/AnyIO `DeprecationWarning`；非固件编译 warning。
- ESP-IDF 5.5.4 完整及最终增量构建通过，无编译 `warning:`；字体覆盖和 PMIC/IOE ABI 校验通过。
- 本机产物：`dist/voice-round2-candidate/m5stack-stopwatch/`（不提交）。
- `firmware.bin` SHA256：`aea60a0251884116a903cf79c7368f40201988009d9624b806cad130878ede89`。
- 45 秒自动重试窗到期后暂停，仅明确 RETRY 或新连接 generation 可重启；120 秒从录音 START 起计算的硬期限不可续期。

### 剩余风险

- 若最初 `voice_start` 根本未到网关，`voice_status` 无法恢复未知请求；本轮明确有界失败，不盲重发 start，也不报成功。
- ACK/重连/end 并发组合仍需设备日志验证；最后 deadline 分支已编译并跑现有回归，但没有专用 task 全时序测试。
- STT smoke 使用测试替身；真实服务质量、PSRAM 峰值及户外热点 10 轮成功率未验证。
- 未刷机、未部署、未重启生产服务；重启后的 PCM 恢复不支持。

第二轮软件门禁已通过；最终收口结果见下。

## 最终收口软件验证记录

- 已将 `origin/main` 的 `0383700` 合入修复分支；保留 Voice 修复和 Pi Running timer 修复，未将修复分支合回 main。
- 新协议 `voice_end_ack` 明确确认 Gateway 接受 END；仅本地发送成功不得释放上传状态。
- 120 秒 hard expiry 显示 `UPLOAD EXPIRED / B CLOSE`，不能 A RETRY；设备 PCM 释放，离线 Gateway cancel cleanup 重连后补发。45 秒失败仍可保留重试。
- terminal ACK 后设备保留 remote-result pending，hello/target 同步后重连查询；STT/结果可以重新交付，不重复转写或重放 Agent send。
- Voice/STT 专项：70 passed。
- Linux 隔离候选目录：`/home/lighthouse/esp32-voice-final-candidate`。
- `scripts/test-firmware-host.sh`：Full firmware host gates PASS。
- `scripts/test-server.sh`：Node/Pi 24 passed、Python 325 passed、HTTP/WebSocket/Pi/Codex TLS voice smoke 全通过。
- ESP-IDF 5.5.4 空 build 目录完整构建通过，未检出编译 `warning:`；字体覆盖及 PMIC/IOE ABI 校验通过。Python 仍有已有的 Starlette/AnyIO 弃用提示。
- 本机最终产物：`dist/voice-final-candidate/m5stack-stopwatch/`（不提交）。
- `firmware.bin` SHA256：`a027fe2fc98233e18c9bbfe8bdce6767910c480cb1c02962a26a69d345010ffc`。
- 新增生产 delivery helper、board/WebSocket fragment 和真实认证 Gateway 回归，覆盖 END tail-loss、terminal ACK、hard expiry、STT reconnect、重复交付及 target/approval/cancel 失效。

仍未进行真机弱网、真实 STT、内存峰值验证；最初 START 未达 Gateway 时仍为有界失败。没有刷机、部署或重启生产服务。

`SOFTWARE READY FOR FINAL ESP32 PHYSICAL ACCEPTANCE`
