# 0.6a Hold A Back 收口

2026-10-05。本轮严格 A → B → C；任何 Gate 未通过都不能推进下一阶段。
阶段 A 软件与实体 Gate 均 **PASS**，允许进入阶段 B。Speech Output = **FROZEN / NOT ACCEPTED**。

## 修复

基线：`codex/physical-ui-cleanup-0.6a`，`d6d6c45d89123716b166a7e6624ad4d0a2003dd1`。

P0 根因：`board_update()` 只对 Normal 状态启用 A 的 `allow_long`，Settings
使 `normal=false`。真实 ButtonGesture 因此无法产生 LONG；人工注入 LONG 的
旧 shell_input_test 只能证明 reducer 正确，不能证明 GPIO 输入链路正确。

`shell_input_policy.h` 为 A/B 共用长按资格：没有 Approval，且 owner 为 Normal
或 Settings。`poll_shell_button()` 是生产 GPIO 轮询与 host 回归共用的小入口，
真实 ButtonGesture 输出直接交给既有 `handle_gesture()`。未重构 input stack。

Normal Hold A 启动 Voice；Settings Hold A 只执行 Back；Voice 保持原 owner；
Approval 不产生 LONG，不启动 Voice 或导航。Approval 内松手仍按既有审批
按键语义响应，早于 Approval 出现的按下不能回答新请求。

Root 关闭 Settings 回原 App；Agent/Device/Display/System 返回 Root；Edit 撤销
draft 回 parent；Brightness 恢复 applied 值且不保存；Effort 返回 Model；
Model/结果页返回 Agent；Speech Mode 返回 Speech，再返回 Agent。
LOADING/APPLYING/SAVING SPEECH 阻止 Back；Hold B 仍整体关闭。

Watch 的 `WallClock::local_ms()` 固定 UTC+8。UTC epoch、RTC 编解码、monotonic
UTC 和 Gateway offset 元数据保留，NVS schema 未改。offset=0/-300/480 都显示
同一东八区时间，不增加 timezone 设置。TIME 日志同时显示 Gateway offset 和
Demo display offset，避免把显示偏移误判为写入 RTC 的值。

## 回归与 Gate

`physical_input_test.cpp` 覆盖 raw press → 29ms 尚未消抖 → 30ms 稳定按下 →
599ms 尚未长按 → 601ms 产生 LONG → Settings Back → LONG_RELEASE，确认没有
尾随 CLICK。覆盖全部本地 Settings parent，Settings Voice starts=0、Normal
Voice 资格、Approval/Voice owner，并加入 wearable-shell 软件 Gate。

`shell_input_test.cpp` 同时保留 reducer 回归，并提取生产 `poll_shell_button()`
经过真实 ButtonGesture 验证全部 Settings Back 路径、brightness preview 恢复/不
保存、busy 阻止返回、Hold B 整体关闭、Voice release、Approval stale press。

`rtc_time_test.cpp` 覆盖 00:30 UTC → 08:30，16:30 UTC → 次日 00:30、月末、
年末、闰日；offset=0 冷启动/重连与 RTC 保持 UTC，未同步显示不可用。

完整软件 Gate 必须逐项 exit 0：

```bash
git diff --check
./scripts/test-wearable-shell.sh
./scripts/test-network-resilience.sh
./scripts/test-server.sh
./scripts/build.sh m5stack-stopwatch
```

Mac 缺少 Pillow/ESP-IDF，完整 Gate 使用现有 Lighthouse 同源隔离工作树，
不是旧 artifact 的测试结果。原生产 Gateway 不修改。

## 实体 Gate：PASS（2026-10-05）

前一候选 `.cache/physical/flash-result.json` 记录用户确认 BAT 数值和 Watch 正常，
4190mV/98%、valid=1、failures=0；这是 d6d6c45 的既有证据，不替代新版本复测。
用户本轮确认旧版本 Settings 所有 Hold A 无效；本次修复后的全部实体测试组回复“没问题”。
验收固件源版本 `48d4719`，软件 Gate 均 exit 0；141 项 Python、87 项网络
Python、18 项 Node 及固件 build/linked CJK/PMIC ABI 均通过。
实际仅刷写 `0x10000`，SHA-256 为
`b6289bd2b24d3db1b95d4f893cbeadad6eda5f35045a7282d176c295401325d7`。
启动 BAT 4184mV/98%、valid=1、failures=0；Gateway offset=0，display offset=480。
已捕获 9 次 Settings 实体 Hold A、录音启动 0 次；用户确认完整按键/UTC+8
和 BAT/Navigation/CJK/Watch/Agent/Voice/Approval/Network 两组测试无问题。
完整机器记录见 [physical-acceptance-0.6.0.json](physical-acceptance-0.6.0.json)。

本次已逐项通过：Hold A Back、BAT、Navigation、Settings Back、UTC+8、CJK、
Watch、Agent、Voice Input baseline、Approval baseline、Network Resilience baseline。
实体结果只能来自实际设备日志与用户观察，不用 host PASS 代替。

刷写仅 application `0x10000`，先验证 artifact hash 和 partition-table 与前一
候选一致，保留 Settings/NVS。原候选 artifact 保留作回退。

## 阶段 B / C

A 实体 Gate 全 PASS 前，main 保持 `3b46835`，不创建 v0.6.0、不归档/删除分支、
不启动 0.6b。下一阶段先核对 remote main，若未知变化即停；使用 `-s ours`
history-only promotion 并验证 tree，再重跑全部软件 Gate，ff-only main，禁止 force。

0.6b 要求以用户本轮完整规格为准，从稳定 main 独立 worktree：Agent Navigator、
原生权限/Approval/User Input、新 Session + 第一条输入、设备级 Voice Send 默认
Confirm，Direct 只跳过 transcript confirmation，≤1000ms 从实际录音开始计时
并取消上传音频、不 STT、不 Agent input。Double B/Touch/Speech Output 不实现。
