Files
qianbian/DECISIONS.md

35 lines
12 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.
# 决策日志 (DECISIONS)
| 日期 | 决策 | 背景 | 理由 |
|------|------|------|------|
| 2026-07-24 | 以 Web 上位机 JS 为协议第一证据源 | 三条证据线:Web JS / APK / 固件 | Web Bluetooth JS 通常未重度混淆,可读性最高,可 fastest path 拿到 UUID 与命令格式,再与 APK/固件互证 |
| 2026-07-24 | CLI 用 Python+bleak | 本地化+agent 化需求 | bleak 跨平台、无需 root 以外依赖;JSON I/O 便于 agent 编排;Pillow 覆盖全部抖动算法 |
| 2026-07-24 | 项目纳入 git 管理 | 治理规则要求 | 原始目录非 git 仓库;逆向产物多、需频繁提交与回溯 |
| 2026-07-24 | 倒计时前缀位图帧**不**复刻 JS 的 off-by-one | timedjs2.js:206-224 循环 `i < len-1` 丢末字节 | 判定为 JS 端笔误(设备缓冲兼容完整帧);CLI 发完整 n*32 字节,避免在末字符右下角丢 8 像素 |
| 2026-07-24 | 倒计时日期/休眠小时采用 BCD 编码 | timedjs2.js:242-243 / ble.js:4013-4015 均为十进制字符串直拼后经 hexToBytes | 实测 JS 证据:month=12 → 字节 0x12,hour=23 → 0x23;与 setTime 的真 hex(intToHex)不同,需注意区分 |
| 2026-07-24 | 固件头格式确认(magic 'pQ'/flags 0x01AA/长度/CRC32(body)/64B 头/0x20 填充 0x00) | 以 zlib.crc32 反推验证 CRC 字段 = body 的 CRC32,重打包字节级往返一致 | fw_pack 可生成与原镜像格式一致的文件,是固件可控(G4)的基础 |
| 2026-07-24 | 中英混排 0.5 单位的倒计时前缀在 CLI 拒绝 | JS `'0'+charCount` 遇 3.5 生成非法 hex "03.5" | 属于上游 bug 路径,CLI 显式报错优于发送畸形帧 |
| 2026-07-28 | 真机测试阻塞定责:USB/IP 透传链路(hci0=vhci_hcd,源 192.168.61.35:3240 Intel AX200)在 BLE 连接数据窗口 ~3s 后硬停 | btmon 两次复现:连接建立成功→MTU 交换成功(设备 RX MTU 512)→服务发现推进→~19 个 ACL 包后悬崖式断流→Disconnect 0x08;对照设备(小米温湿度计 RSSI -27)同样失败 | 排除设备与协议软件问题(扫描/连接建立/ATT 初期均正常);属 usbip-host 中断端点饥饿类故障;需在 usbip 源机原生运行或修 usbip,详见 PROGRESS.md 阻塞节 |
| 2026-07-28 | **结论修正**:根因 = **大 MTU GATT 流量触发链路断流**(非此前判断的"中断端点饥饿"),具体归因于 usbipd-win/VBoxUSB/VHCI/AX200 固件链中某一环,无法唯一归因 | 熠管家在 Windows 侧排查:76B 响应完整到达 guest、下一请求已被 AX200 接收;**ATT MTU=23 时全部服务+22 个 characteristic 发现成功、无监督超时**;usbipd-win 5.3.0 + Ubuntu 6.8.0-136 均为最新 | 不能全局降 MTU(EPD 图像帧 244B > MTU23),故转向 bridge 架构:BLE 在 host-win 原生执行,guest 经 RPC 调用(见下条) |
| 2026-07-28 | 引入 **bridge 能力**:host-win 原生跑 bleak(WinRT) 并以 JSONL/TCP 暴露 RPC,ubuntu-openclaw 的 ppclock 经 BridgeTransport 远程调用 | usbip 链路无法承载大 MTU GATT;Windows 原生 BLE 无此问题;NFS 仓库两边可达 | 交付:tools/bridge_server.py(单文件服务端)+ src/ppclock/bridge.py(与 BLETransport 同接口)+ CLI `--via bridge://host:port` / `PPCLOCK_VIA` 环境变量 |
| 2026-07-28 | **RXTX 命令通道 = 331f(非 web 文档的 1f1f)** | 实机 GATT 诊断(bridge services op):1f1f(h=53) 仅 write 无 notify,写入 ATT-ACK 但设备不执行;331f(h=57) notify+write+read,经其实测 EFEF/模式/倒计时/停车牌全部生效 | ppclock 全栈切换(protocol.RXTX_CHAR_UUID=331f、BLETransport/BridgeTransport 写路径);保留 1f1f 为 legacy 常量 |
| 2026-07-28 | **设备 ID 反解参数序实机校正**:snprintf 实参序为 MAC5,MAC4(非固件代理读的 MAC4,MAC5) | 实机 ID '81233F3C267112' 仅按 MAC5 在前才能反解出广播地址 18:BC:5A:5D:BF:28(NVDS 反转序);按原序则掩码矛盾 | protocol.recover_mac_from_device_id 已修正并锁定实机向量测试;firmware-analysis §4.2 对应行以本条为准 |
| 2026-07-28 | 设备睡眠行为适配:广播窗口短(约 30-60s),连接态保持清醒 | 实测:空闲即停广播;单连接 batch 可在窗口内跑完全部命令 | 现场测试统一用 `ppclock batch` 单连接;唤醒守候循环(每 15-20s 重试)跨窗口执行 |
| 2026-07-29 | **SUOTA 状态码 8/16 = 会话信息码而非失败**(仅本固件)〔本条被 2026-07-29 第 5 条(状态码分两类)取代,保留溯源〕 | 反汇编 MEM_DEV handler(0x07FCB6F8):type 0x13 命中 bit4 位测试 → 发状态 16 且 SPOTA 状态保持(0x07FCB770-0x07fcb772);type 0x12 → 状态 8 并清会话(0x07FCB780);空镜像探针与正式烧录均先收 16 后流程完好 | bridge ota 对 8/16 记录并继续,块确认只认 2;APK 错误表未覆盖 16,以固件证据为准 |
| 2026-07-29 | bridge OTA 大请求需显式 StreamReader limit | asyncio 默认 64KiB 行限制,OTA 镜像 hex 请求 ~144KB 永不可达(三轮"静默停滞"根因);空镜像探针 0.6s 全通证明管线无恙 | build 9: start_server(limit=4MiB);教训:大载荷 RPC 协议设计必须考虑传输层帧限 |
| 2026-07-29 | **SUOTA 状态码分两类:会话信息码(8/16)与流程错误码**;16=MEM_DEV type 0x13 位测试应答(固件 0x07FCB770),状态保持 | 反汇编 MEM_DEV handler + 三轮烧录对照(16 恒在 MEM_DEV 后 ~1s 到达,块未受影响) | bridge 对 8/16 记录继续、块确认只认 2;APK 表未覆盖 16,固件证据为准 |
| 2026-07-29 | **SUOTA 状态 22/20 根因分层**:22=0x38000 产品头读电气失败(GPIO_MAP 引脚错);20=读成功但非 'pR'(引脚有效、0x38000 疑为空) | GPIO 探针(max_blocks=2 安全模式):A{00,00,06,05}→22、B{40,00,06,05}→22、C{05,06,00,40}→20;C=APK 整数 0x05060040 的 BE 字节,为唯一电气有效候选 | GPIO_MAP 采用 C(BE 序);剩余阻断=0x38000 产品头('pR')缺失/无效,见下条 |
| 2026-07-29 | **0x38000 产品头阻断为 vendor 级问题**:固件在首 0x200 缓冲处理时校验 0x38000 的 'pR' 产品头(0x07FCB4AA),无效则发 20 并中止该缓冲处理;本机疑以裸 'pQ' 直启(无产品头) | 反汇编完整校验链;固件镜像内 0x3A19 处的 70 52 系代码巧合非真产品头;MEM_INFO 读回 00000000 | 强推 END 有变砖风险(END 会写 0xAA 标志+复位,裸启兜底可恢复但需 JTAG 兜底方案);暂缓强推,先与用户决策 |
| 2026-07-29 | **vendor 固件目录实证**:qbsg.top /api/firmware/catalog 免认证开放,PP_da14585_4.2_CH.img 与本地镜像 sha256 完全一致(vendor OTA 镜像=本镜像,无产品头附加);NEW 变体同样引用 0x38000 检查 | 下载比对(analysis/apk/vendor_catalog.json + vendor_PP_CH/NEW.img) | 排除"vendor 镜像含产品头"假设;阻断确在设备端 0x38000 内容,非镜像差异 |
| 2026-07-29 | **全量烧录实证安全性论证**:产品头校验失败时所有缓冲处理在写 flash 前中止(state[0x70] 恒 0),END 仅写 0xAA 于 [state[0x60]+2]=0x0002 而该字节原本就是 0xAA(无操作),复位后裸启原固件 | 反汇编 END 路径(0x07FCB7C6-0x07FCB7E4)与缓冲写路径(0x07FCB514 依赖中止的头部处理产物 state[0x74]) | 全量跑 OTA 的最坏结果=安全空转+复位;若中途校验通过则自然转正为真实烧录。用户授权下可执行 |
| 2026-07-29 | **用户明确授权全量烧录**(301 块+END+复位,gpio_map=BE 序 C),要求全程记录与 review | 用户原话:"授权 注意做好记录和review"(此前已授权"做一下审慎测试后刷机") | 执行范围:fw_tst.img(DIS 版本串 3 字符差异);留证:状态流日志 docs/suota-flash-log.jsonl;事后验证:scan/connect/DIS 读 |
| 2026-07-29 | **G4 固件可控路径选型:B(免线刷软件利用)**,放弃 UART 线刷恢复工厂布局(路径 A 文档保留备查) | 用户原话:"肯定是B";B 路径已具备:图像 flash 持久化写(0x2E000)、EF 激活 flash 写(0x3Fxxx)+ 离线 keygen、全部 BLE 功能可控、SUOTA 工具链就绪(工厂布局设备可直接复用) | 任务说明目标3 以 B 路径达标;A 路径(线刷 .bin + UART)作为可选增强留存 docs/firmware-layout.md |
| 2026-07-30 | MCP 连接管理选型**方案 C:按需连接 + 空闲保持 + 串行锁 + 掉线重连一次** | 三方拉扯:设备长睡眠(广播窗口仅 30-60s)vs agent 连续操作体验(每操作重连会撞睡眠窗口)vs 电池(常连使设备保持清醒耗电) | 首次操作守候连接(窗口覆盖 30-60s 广播),操作后保持连接、空闲 `--idle-timeout`(默认 300s)自动断开让设备睡眠;一把 asyncio.Lock 串行全部操作;操作中掉线重连重试一次再向上抛;退化配置:`--idle-timeout 0`(每操作即断,最省电)或大值(近常连,体验最佳);详见 docs/superpowers/specs/2026-07-30-ppclock-mcp-design.md |
| 2026-07-31 | 引入 mcp 依赖(ppclock-mcp extra)并钉 `>=1.23,<2` | 0.2.0-beta 新增 MCP 服务器层;GOVERNANCE 要求依赖引入留痕;mcp 2.0 移除 FastMCP/memory API | `<2` 防 2.0 API 断裂;`>=1.23` 因 `transport_security`(DNS 重绑定防护,`--allowed-hosts` 依赖)自 1.23 引入,且 1.23 起含 CVE-2025-66416 修复;本地与 Windows 实测均为 1.29.0 |
| 2026-07-31 | 0.2.0-beta 终审裁定放行项终裁:TRANSPORT_ERRORS 含 OSError 的理论变宽、token 非恒定时间比较、Image.open 惰性解码失败归入 INTERNAL | 终审提出的三处理论风险,需终裁留痕 | 均判低风险放行:OSError 变宽仅使极端平台错误归入 BLE_ERROR(语义可接受);token 比较在可信 LAN + Bearer 场景时序攻击不可行;Image.open 惰性解码异常有 INTERNAL 兜底且 message 带类型详情 |
| 2026-07-31 | `--allowed-hosts ""` 空串按 None 处理 | 空串逗号拆分得空列表,`TransportSecuritySettings(allowed_hosts=[])` 启用防护后连 localhost 族也 421(自锁) | main() 解析层 `_parse_allowed_hosts` 以 `or None` 归一,保持 mcp 默认 localhost 族;含回归测试 |
| 2026-07-31 | **双 TransportError 历史包袱在 MCP 层适配,SDK 零改动** | `transports.base.TransportError` 与 `transports.local.TransportError` 同名但互不继承(历史包袱);真实 bleak 写/读路径还裸抛 `BleakError` 不包装 | 改 SDK 会动公开语义、风险大于收益;MCP 层 `device_manager.py:19-26` 以 `TRANSPORT_ERRORS` 全家捕获兜底(base/local 两类 + BleakError + OSError + asyncio.TimeoutError),掉线判定不漏形态 |
| 2026-08-01 | 建立 Gitea 远端(chenwei/qianbian)并增补 Git 协作治理(GOVERNANCE §6) | 用户要求版本控制上云 + 后续治理迭代;此前仓库仅本地 | main 主干直提、分支短命、身份诚实不改写、推送前三项检查(净树/全绿/无 secret)、大文件仅原始输入随库。**远端落地**:origin=git@git.b.aeroprop.xyz:chenwei/qianbian.git(实例 git.b.aeroprop.xyz,展示域 git.banytech.com);认证=本机 ~/.ssh/id_ed25519(Gitea key 名 chenwei@bany.tech@openclaw-server);2026-08-02 全历史已推送(ls-remote 校验一致) |
| 2026-08-02 | 项目级规则 review 打结:8 项不一致修复(PROGRESS 首日快照误标"最终状态"、GOVERNANCE §2 目录/§5 工具过期、PLAN P0 jadx、详细计划 Task 9 终态、DECISIONS 条目 18/20 重复标注、README 依赖缺 qrcode) | 用户要求规则 review;项目已从 0.1.0 演进至 0.2.0-beta(SDK/MCP/bridge/实机验证),多文档口径落后于现实 | 规则文件必须与交付现实一致;不改写历史(快照保留标注、重复条目保留溯源),仅修正指向与口径;GOVERNANCE 变更按 §4 留痕本条 |
| 2026-08-03 | **RXTX 通道按固件家族选择**:`HM42_AIO_*` 使用写入型 `1F1F`;已验证 `PP_da14585_4.2` 继续使用 `331F`;未知固件优先 `331F` 并按 GATT 存在性回退 | HP 侧同型号异供应商设备报告 `HM42_AIO_V1.0.9`;原 SDK 写 `331F` 均返回成功但屏幕不变。GATT 枚举显示其 `1F10` 控制服务只有 write-only `1F1F`;直写 Web-BLE 序列 `e10202` + `e2` 后用户目视确认切至“日历+表” | ATT 写成功不等于设备执行。连接时读取 DIS `2A26` 并选型;仅在特征支持 notify/indicate 时订阅,避免 write-only HM42 通道误启通知。状态接口公开固件串和实际 RXTX UUID,便于运维审计 |