@@ -2571,3 +2571,151 @@ P0 问题已解决, DevicePlugin 现已完全集成到 ShowenV2 运行时!
项目判断:
- 当前文档已足够支撑 Flutter 技术方案设计、UI 细化与开发排期。
- 后续需要在正式开发前冻结客户端共享数据模型,并安排一次 API/WebSocket/BLE 联调校准。
---
[2026-07-02] 周雅婷(测试工程师) → 陈逸飞(CEO): M1.2 目标机验收
**设备 HEAD**: 5770b7007c35f283adb462ca53517f02faab95b1 (chore: remove unused Config type alias, add deny_unknown_fields to remaining config structs) — 已从 Windows master 经 git bundle 同步(原落后 20 提交, fast-forward, 无冲突; 设备本地未提交改动仅 configs/dog_state_machine.json, 未触碰)
**cargo check --workspace --all-targets**: 编译通过( Finished dev profile, 1m 22s) , 但有 3 个 warning, 全部在新测试文件( src/ 零 warning) :
- tests/m1_2_http.rs:141 — method ` sender` is never used
- tests/m1_2_dynamic_plugin.rs:14 — unused imports: ` Destination` and ` Envelope`
- tests/m1_2_dynamic_plugin.rs:87 — function ` manifest_with_caps` is never used
**cargo test --workspace --no-fail-fast** 统计(/tmp/m12_acceptance3.log) :
` ``
Running unittests src/lib.rs (showen_example_plugin)
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.01s
Running unittests src/lib.rs (showen_plugin_sdk)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (showen_v2)
test result: ok. 105 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s
Running unittests src/main.rs (showen_v2)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/m1_2_dynamic_plugin.rs
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Running tests/m1_2_http.rs
test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Running tests/m1_2_service_manager.rs
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Doc-tests showen_plugin_sdk
test result: ok. 0 passed; 0 failed; 13 ignored; 0 measured; 0 filtered out; finished in 0.01s
Doc-tests showen_v2
test result: ok. 0 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 0.00s
` ``
**总计: 146 passed / 1 failed / 17 ignored (doc-tests)**( 注: m1_2_http 实际 12 个测试,非记录中的 9 个)
**唯一失败测试**: ` test_empty_playlist_does_not_panic` (tests/m1_2_http.rs)
` ``
thread 'test_empty_playlist_does_not_panic' (8196) panicked at tests/m1_2_http.rs:102:53:
test config should load: playlist 不能为空
` ``
根因分析(未修改,仅上报):该测试用空 playlist 构造配置并期望 ` AppConfig::from_file` 成功,但 src/core/config.rs:334 的校验 ` bail!("playlist 不能为空")` 自 init 提交(23f4d46)起就存在——空 playlist 配置从设计上就无法通过加载。属测试前提错误(测试从未在任何机器实跑过),非产品回归。修复归属请 CEO 派人裁决(改测试预期 Err 或放宽校验)。
**过程事件**: 首轮 cargo test 因设备磁盘满失败( ld: final link failed: No space left on device, 根分区 98%) 。已清理项目自身构建产物: target/debug/incremental (1.6G) + 3月14日过期 deps 大文件 (~640M) + 同步用 bundle, 恢复至 2.7G 可用后重跑成功。未动项目外任何文件;服务所用 target/release/showen_v2 未受影响。
**现场恢复**: showen_v2.service 已重启, is-active=active, 单实例 (pid 10192, target/release/showen_v2)。
**第二轮(返工后)[2026-07-03] 周雅婷 → 陈逸飞(CEO): M1.2 最终验收 — 全绿**
变更文件(赵雨薇/张明远返工, Windows 未提交版本,经 scp 覆盖到设备, sha256 两端一致):
- tests/m1_2_http.rs (b09cc3f0...) — test_empty_playlist_does_not_panic 重写为 test_empty_playlist_rejected + 删死代码 sender()
- tests/m1_2_dynamic_plugin.rs (aa75dfc6...) — 删未用 import 和 manifest_with_caps
**cargo check --workspace --all-targets**: Finished dev profile in 1m 31s, ` grep -cE "^warning" /tmp/m12_acceptance4.log` = **0( 零 warning) **
**cargo test --workspace**( /tmp/m12_acceptance4.log, Finished test profile 7m 58s) :
` ``
Running unittests src/lib.rs (showen_example_plugin)
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.00s
Running unittests src/lib.rs (showen_plugin_sdk)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (showen_v2)
test result: ok. 105 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.38s
Running unittests src/main.rs (showen_v2)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/m1_2_dynamic_plugin.rs
test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
Running tests/m1_2_http.rs
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
Running tests/m1_2_service_manager.rs
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
Doc-tests showen_plugin_sdk
test result: ok. 0 passed; 0 failed; 13 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests showen_v2
test result: ok. 0 passed; 0 failed; 4 ignored; 0 measured; 0 filtered out; finished in 0.00s
` ``
**总计: 147 passed / 0 failed / 17 ignored( doc-tests) , test_empty_playlist_rejected ... ok( log 208 行)**
现场恢复: showen_v2.service 重启后 is-active=active, 单实例 (pid 12896, target/release)。
磁盘:开跑前 1001M, 清 incremental(958M) 后 2.0G,跑完剩 1.2G。
⚠️ 注意:设备上两个测试文件当前与 git HEAD (5770b700) **不一致**( scp 的未提交返工版) 。Windows 提交后需再同步设备( bundle 或重新 scp) , 否则下次设备端 git 操作会看到脏文件。
---
## [2026-07-03] 王浩然(网络工程师) → 陈逸飞(CEO): M1.2 风险3 消息链路核查报告
纯代码审计,零代码改动。范围:` src/core/message.rs` 的 Message enum 全部 17 个变体 + plugin-sdk 镜像。判定标准:生产代码中"至少一个构造点 + 一个匹配点";测试文件构造不算生产者。
### 一、全量核查表( 17/17 变体)
路由背景: ServiceManager 主循环 ` src/core/service_manager.rs:356-383`,按 Destination 分三路: Plugin(id)→deliver_to_plugin(:370)、Broadcast→broadcast_message(:373)、Manager→handle_manager_message(:376)。示例插件 plugins/example-plugin 是动态参考模板,其消费点单独标注。
| # | 变体 | 生产者(生产代码) | 消费者(生产代码) | 判定 |
|---|------|------------------|------------------|------|
| 1 | PlayerCommand | src/core/dispatch.rs:32-52( BLE 文本命令,经 src/plugins/ble/gatt.rs:120-124 实际发送) ; src/plugins/http/routes.rs:236,247,260,276,298,331 | src/plugins/video/mod.rs:145; 示例插件 lib.rs:517 | ✅ 闭环 |
| 2 | PlayerStatus | src/plugins/video/mod.rs:54, 330 | src/plugins/http/mod.rs:334; Manager 转发 service_manager.rs:770; 示例插件 lib.rs:520 | ✅ 闭环 |
| 3 | Trigger | src/core/dispatch.rs:67; src/plugins/http/routes.rs:347,362 | src/plugins/video/mod.rs:187; 示例插件 lib.rs:523 | ✅ 闭环 |
| 4 | StateChanged | src/plugins/video/mod.rs:76, 351 | src/plugins/http/mod.rs:346; Manager 转发 service_manager.rs:773; 示例插件 lib.rs:526 | ✅ 闭环 |
| 5 | ScreenLockRequest | src/plugins/video/mod.rs:156, 167 | src/plugins/screen/mod.rs:104; 示例插件 lib.rs:532 | ✅ 闭环 |
| 6 | CursorVisibility | **无**(仅测试构造 src/core/tests.rs:1521) | src/plugins/screen/mod.rs:111; 示例插件 lib.rs:535 | ⚠️ 有消费无生产 |
| 7 | WifiCommand | src/core/dispatch.rs:75-100( BLE 经 gatt.rs:120-124 发送) ; src/plugins/http/routes.rs:966 | src/plugins/wifi/mod.rs:321 | ✅ 闭环 |
| 8 | WifiResult | src/plugins/wifi/mod.rs:97 | Manager 转发 service_manager.rs:767; src/plugins/ble/mod.rs:120; src/plugins/http/mod.rs:333; 示例插件 lib.rs:541 | ✅ 闭环 |
| 9 | **WifiProvisioned** | **无**(仅测试构造 src/core/tests.rs:1534) | Manager 转发臂 service_manager.rs:782( 死路径, 永不触发) ; 示例插件 lib.rs:544 | ❌ 有消费无生产( Phase 2 占位) |
| 10 | ConfigReloaded | src/core/service_manager.rs:792 | src/plugins/http/mod.rs:340; src/plugins/video/mod.rs:197; 示例插件 lib.rs:554 | ✅ 闭环 |
| 11 | ConfigReloadRequest | src/core/dispatch.rs:107( BLE config_reload) ; src/plugins/http/routes.rs:492, 782 | src/core/service_manager.rs:785 | ✅ 闭环 |
| 12 | Shutdown | **初始生产者无**( 仅测试注入; dynamic_plugin.rs:348 亦在 #[cfg(test)] 内) ; Manager 转发 service_manager.rs:765 | ble/mod.rs:117; screen/mod.rs:112; http/mod.rs:358; video/mod.rs:223; device/mod.rs:139; 示例插件 lib.rs:566 | ⚠️ 消费者齐全,生产环境无初始触发者(见延伸发现①) |
| 13 | PluginReady | src/plugins/video/mod.rs:136; src/plugins/http/mod.rs:305; src/plugins/ble/gatt.rs:343; 示例插件 lib.rs:169 | Manager 转发 service_manager.rs:799; src/plugins/http/mod.rs:357( BLE ready 门控) | ✅ 闭环 |
| 14 | DeviceCommand | src/plugins/screen/mod.rs:30, 41, 52; 示例插件 lib.rs:227 | src/plugins/device/mod.rs:119 | ✅ 闭环 |
| 15 | DeviceResponse | src/plugins/device/mod.rs:134( Broadcast) | 核心静态插件**无消费者**( screen 发命令后不读响应);仅示例插件 lib.rs:576 | ⚠️ 弱闭环(见延伸发现②) |
| 16 | **DeviceEvent** | **无**( device/mod.rs 全文件不构造; DeviceBackend trait backend.rs:52-99 只有 name/init/handle_command/capabilities/shutdown 五个方法,架构上无事件上报通道) | 示例插件 lib.rs:606 | ❌ 有消费无生产( Phase 2 占位) |
| 17 | Custom | http/routes.rs:1873(plugin_switch), 1908(plugin_install), 1944(send_plugin_command, 被 enable:1822 / disable:1833 / rollback:1844-1851 / check-updates:1925-1933 共用); service_manager.rs:752(plugin_states 广播);示例插件多处(lib.rs:211,273,307…) | service_manager.rs:803-875( 6 种 kind 全处理) ; http/mod.rs:359(plugin_states);示例插件 lib.rs:609 | ✅ 闭环 |
plugin-sdk 镜像( plugin-sdk/src/lib.rs:70-149) 与主库 17 个变体一一对应, JSON 兼容,无脱节。
### 二、WifiProvisioned / DeviceEvent 专项结论
**WifiProvisioned = 有消费无生产,判定 Phase 2 占位,非回归缺陷。**
- 定义: src/core/message.rs:45; SDK 镜像 plugin-sdk/src/lib.rs:116。
- 全仓生产代码零构造点。wifi 插件 connect 成功只发 ` WifiResult`( wifi/mod.rs:94-98) , BLE 配网链路是 gatt.rs:120 → dispatch.rs → ` WifiCommand::Connect`,均不产生 WifiProvisioned。
- 消费侧: service_manager.rs:782 的转发臂是死代码路径(上游无人发);示例插件 lib.rs:544 在白等。
- **风险点**: SDK 文档 plugin-sdk/src/lib.rs:522 教第三方插件开发者监听此消息——当前版本永不触发,会误导开发者。
- 建议:入 Phase 2 backlog——wifi 插件 connect_network 成功后取 ssid+ip 补发;短期在 SDK 文档注明"当前版本未触发"。不建议删( BLE 配网完成通知是合理设计意图)。
**DeviceEvent = 有消费无生产,判定 Phase 2 占位,非回归缺陷。**
- 定义: src/core/message.rs:62, 221-234( 6 个事件变体) ; SDK 镜像 lib.rs:139, 273。
- DevicePlugin( device/mod.rs 全文件)只处理 DeviceCommand→回 DeviceResponse( :119-137) , 从不构造 DeviceEvent。更根本的是 DeviceBackend trait( backend.rs:52-99) 没有任何事件轮询/回调接口——**生产者在架构层面还不存在**,与 CLAUDE.md "DevicePlugin 阶段三(触摸/音频/多平台)" 规划一致。
- 消费侧:仅示例插件 lib.rs:606。
- 建议:标 Phase 2 backlog, 待 DeviceBackend 增加事件通道时接入; 不删( README、docs/DEVICE_PLUGIN_DESIGN.md:92 已承诺该接口)。
- DeviceCommand/DeviceResponse 一族其余两个: DeviceCommand 闭环 ✅; DeviceResponse 见延伸发现②。
### 三、与赵雨薇旁证结论的异同( tests/m1_2_http.rs:6-18)
| 她的结论 | 核查结果 |
|---------|---------|
| enable/disable 闭环已修 | ✅ **证实**。routes.rs:1822/1833 → Custom → service_manager.rs:805/814 → set_plugin_enabled(:401-430) → broadcast_plugin_states(:750) → http/mod.rs:359 读回。全链路通。 |
| plugin_rollback/switch/install "仅发送消息, ServiceManager 侧仅打印日志,尚无完整业务逻辑" | ❌ **已过时,推翻**。三者现均为完整实现: rollback_plugin(service_manager.rs:432-447, 真实回滚)、switch_plugin_version(:617-663, 版本切换+热替换)、install_plugin(:665-728, repo 下载+registry 更新+加载)。张明远 #25 修复( handle_manager_message Custom 分支)已落地,她的注释写于修复前。**建议 M1.2 收尾时顺手更新 tests/m1_2_http.rs:10,17-18 的过时注释**(本次未动)。 |
| plugin_check_updates 同上"仅打印日志" | ⚠️ **部分成立**。check_plugin_updates(:730-748) 会真实查询 repo.check_update, 但结果只 println(:736-743),不回传 HTTP/不广播——调用方只收到"命令已发送"(routes.rs:1949),拿不到检查结果。结果回传链路确实断在日志,属功能不完整(非崩溃性缺陷),建议 Phase 2 把结果并入 plugin_states 或单独 Custom 广播。 |
| WifiProvisioned/DeviceEvent( 她未核查) | 本次补齐:均为"有消费无生产( Phase 2 占位)",非回归缺陷,与 docs/M1.2_TEST_PLAN.md:17 的风险预判一致。 |
### 四、延伸发现(主动排查,均不阻塞 M1.2)
① **Shutdown 无生产环境初始触发者**: main.rs Ctrl+C handler(:117-120) 只置 AtomicBool, 不注入 Message::Shutdown; HTTP 也无 shutdown 路由。而 manager.run() 阻塞在 rx.recv()(:361), Ctrl+C 后要等**下一条任意消息**到达才能退出主循环。行为性瑕疵,建议 Phase 2 加"信号→Shutdown 消息"桥接。
② **DeviceResponse 弱闭环**: device 广播响应(:134), 但核心静态插件无人读——screen 发 DeviceCommand(:30,41,52) 后不看结果,` DeviceResponse::Error`(:125) 广播后无人记录,设备层故障静默。建议至少在一处记录 Error 响应,或在 screen 注明 fire-and-forget 语义。
③ **CursorVisibility 有消费无生产 ** : screen/mod.rs:111 在等一条没人发的消息;实际光标控制链路已改走 screen→DeviceCommand::SetCursorVisible(:52)→device。建议: 删变体或补 HTTP/dispatch 生产者(低优先级,倾向 Phase 2 一并清理)。
### 五、总判定
17 个变体中:**11 个闭环 ✅; WifiProvisioned/DeviceEvent 为 Phase 2 占位 ❌( 非回归缺陷) ; Shutdown/DeviceResponse/CursorVisibility 弱链 ⚠️ ( 行为性瑕疵, 已给建议) ; Custom 六种 plugin_* kind 全部闭环(修正赵雨薇过时结论)**。风险3 可关闭:无"消息发出去没人处理"的黑洞(除示例性 Custom heartbeat 外),断链项全部是已规划的 Phase 2 占位,且每条均有 file:line 可复核。