Skip to content

♻️ refactor(runtime): 迁移 ESP 事件循环与行为轨迹 - #282

Open
ZhaoXingPeng wants to merge 5 commits into
1024XEngineer:mainfrom
ZhaoXingPeng:refactor/runtime-event-trace
Open

♻️ refactor(runtime): 迁移 ESP 事件循环与行为轨迹#282
ZhaoXingPeng wants to merge 5 commits into
1024XEngineer:mainfrom
ZhaoXingPeng:refactor/runtime-event-trace

Conversation

@ZhaoXingPeng

@ZhaoXingPeng ZhaoXingPeng commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

结论

Runtime 的公共装配契约仍位于 voicelife_runtime;ESP-only 实现、任务和调度已迁入 voicelife_runtime_esp。交互状态由应用编排器拥有,ESP 适配器保留既有 FreeRTOS 队列、任务和定时器调度;唤醒参数被作为值拥有的动作数据传递。请审查迁移后行为轨迹与原状态机语义是否一致。

Fixes #280

Lifecycle: Ready for review. Merge after prerequisite PR #275 is merged, all required checks pass, and a non-author reviewer approves. This PR targets main and temporarily includes #275 as ancestry; after #275 merges,GitHub 会自动收敛 diff。

Scope

  • Move ESP-only Runtime bootstrap, worker, provisioning, Linx, IM, task and timer implementation into voicelife_runtime_esp.
  • Keep voicelife_runtime as the public API and platform-assembly contract; the ESP component explicitly depends on it and provides the API implementation.
  • Make InteractionOrchestrator own VoiceInteractionController and project legal transitions through a platform action sink.
  • Preserve FreeRTOS scheduling and existing queues in the ESP Runtime adapter.
  • Cover boot, wake, interruption, endpoint/finalization timeout, TTS follow-up, and invalid events with host behavior traces; wake words are retained per action.
  • Remove the hidden relative component include and make the dependency graph explicit; the architecture check rejects that bypass pattern.

Migration Mapping

  • Runtime API: unchanged runtime::Start and runtime::RequestInterrupt; their implementation is linked from the ESP Runtime component, with compatibility forwarding retained for runtime_esp callers.
  • Interaction event: existing ESP queue -> EspInteractionTaskHost -> InteractionOrchestrator -> existing queue/timer/display action projection.
  • Interaction state: direct Runtime-owned controller -> application-owned controller exposed through InteractionOrchestrator::state().
  • Wake word: event input -> value-owned action -> Runtime sink; no side-channel mutable wake-word state.
  • ESP-only helpers: physically relocated from voicelife_runtime/src to voicelife_runtime_esp/src with declared build dependencies.

Non-goals

  • No public Runtime API, voice-provider, Wi-Fi, IM, MCP protocol, display text, task priority, queue capacity, timeout, shutdown order, or log-event change.
  • No merge of this PR without human approval.

Verification

  • ./scripts/run_checks.sh(73/73 主机测试)
  • ./scripts/check_architecture.sh
  • ./scripts/check_format.sh
  • 以 GNU 编译器及 -Wmissing-field-initializers -Werror 构建 interaction_orchestrator_test
  • python3 scripts/check_public_api_docs.py
  • python3 scripts/check_contract_dual_end.py
  • python3 -m unittest discover -s tests/python -p "test_*.py"(110 通过、1 跳过)
  • GitHub 的 ESP-IDF、覆盖率与全部必需检查均通过后方可合并

Risk and Closeout

Primary risks are action-projection ordering and component-link ownership across synchronous state transitions. Host trace tests assert state, directive, wake-word and invalid-event behavior; the architecture test fixes the dependency graph and rejects hidden cross-component includes. GitHub CI and a non-author review remain required before merge.

AI assistance: Codex performed the mechanical relocation, contract-preserving refactor, and local verification. The resulting module responsibilities, public API stability, and verification evidence are explicitly documented above for human review.

@ZhaoXingPeng ZhaoXingPeng added the FullSpec 规格粒度-影响面大的完整规格 label Aug 18, 2026
@ZhaoXingPeng ZhaoXingPeng self-assigned this Aug 18, 2026
@ZhaoXingPeng
ZhaoXingPeng requested a review from JunLang-7 August 18, 2026 04:39

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论

未发现需要阻止合并的正确性问题。逐项核对了应用层 InteractionOrchestrator 对既有 VoiceInteractionController 状态/动作的映射、ESP 事件循环到动作投影的同步调用链、Runtime/ESP 组件依赖边界,以及启动、唤醒、打断、端点、最终 STT 超时和 TTS follow-up 轨迹;迁移后的状态读取和指令执行与原路径保持一致。

验证

  • ./scripts/check_architecture.sh 通过。
  • interaction_orchestrator_testvoice_interaction_controller_test 可执行文件均通过。
  • 聚合主机构建在未改动的 display/audio 头文件上因 GCC 将 [[maybe_unused]] 视为错误而中止;相关失败不位于本 PR 的变更路径。

View job run

@ZhaoXingPeng
ZhaoXingPeng force-pushed the refactor/runtime-event-trace branch from 76c4d58 to a30a682 Compare August 18, 2026 04:47
@ZhaoXingPeng
ZhaoXingPeng force-pushed the refactor/runtime-event-trace branch from a30a682 to 01167fe Compare August 18, 2026 05:26
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...celife_application/src/interaction_orchestrator.cc 90.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 严格仔细检查

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

审查结论

逐方法对照固定范围 1ad310a...01167fe 后,ESP Runtime 的队列、任务、定时器、显示与语音动作实现除预期的 InteractionOrchestrator/Submit 拆分外,与旧路径保持一致;未发现直接的状态迁移或调度顺序回归。

[P2] 行为轨迹没有记录唤醒动作的关键参数

components/voicelife_application/include/voicelife/application/interaction_orchestrator.h:12InteractionEvent 只有事件枚举,InteractionAction 也只有来源、状态和指令。真实唤醒词却由 Runtime::active_wake_word_components/voicelife_runtime_esp/src/esp_runtime_interaction.cc:133 通过带外可变状态传给动作投影。因此,两个仅唤醒词不同的输入会生成完全相同的主机 trace;即使迁移时丢失、串换或错误复用唤醒词,当前 interaction_orchestrator_test 仍会通过。这不满足 #280 对“关键参数”和旧路径/编排器零差异轨迹的验收要求,也使 PR 所述的唤醒/打断行为覆盖 materially incomplete。建议把唤醒词等执行参数纳入稳定事件/动作模型并由 sink 直接消费,或增加覆盖 EspInteractionTaskHost -> Runtime action projection -> BoardRequest 参数的主机测试。

验证

  • ./scripts/check_architecture.sh:通过。
  • GCC 主机构建并运行 interaction_orchestrator_testvoice_interaction_controller_test:通过。
  • python3 -m unittest tests.python.test_im_wifi_credential_isolation:5/5 通过。
  • 固定 SHA 范围 git diff --check:通过。
  • 逐方法 token 对照确认拆分后的 Runtime 方法与旧实现一致;HandleInteractionEvent 的变化仅为同步转交编排器及动作 sink。
  • 本环境没有 clang-format,未能本地复跑格式脚本;当前提交对应的 GitHub 格式检查为成功。

View job run

@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 请对本 PR 做一次独立、严格且可追溯的代码审查。不要因其标为重构、已有通过检查或先前结论而降低标准,也不要回避可能阻塞合并的问题。\n\n审查方法:\n1. 以本 PR 的 merge base 与当前 head 为准,逐文件检查 diff,并将被迁移/拆分的实现与迁移前路径逐段对照。\n2. 核对所有公开接口、调用路径、状态机转换、异常与边界值、资源/并发/生命周期语义;重构 PR 必须证明可观察行为、执行顺序和错误语义没有非预期差异。\n3. 对涉及事件、队列、异步任务或数据持久化的改动,检查参数是否完整传递、所有权与生命周期是否稳定、是否可能丢失/串换/重复执行,并用可复现的最小输入说明问题。\n4. 审查测试是否真正覆盖本 PR 的风险,而非只覆盖表面分支;指出任何缺失的回归测试、断言不足或无法由现有测试发现的行为回归。\n5. 审查模块边界、依赖方向、私有实现与公开 API 的一致性,指出会造成架构退化、重复职责或跨层泄漏的具体位置。\n6. 运行或明确说明实际运行的检查;无法运行时必须说明受限原因、未验证范围与残余风险,不能将未运行项表述为通过。\n\n输出要求:\n- 发现问题时按 P0/P1/P2 排序,每项包含准确文件/行、触发条件、实际影响、为何违反本 PR/Issue 验收,以及最小修复建议。\n- 没有问题时,也必须列出已对照的行为、已运行命令及结果、未覆盖的风险,不能仅给出“looks good”。\n- 不要修改代码或提交;只给出审查结论。

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

审查结论

基于固定范围 1ad310a37488bb53a6ce505c142d677b7d1df8f4...01167fe4b3e573d0b56941c512400dcd8fb27d07 逐文件检查,并将拆分后的 Runtime 方法与基线实现逐段对照。未发现队列容量、任务优先级、定时器、关闭顺序、状态转换或动作执行顺序的直接生产回归;发现以下两个需要整改的 P2 问题。

P2 — 行为轨迹丢失唤醒动作的关键参数

  • 位置: components/voicelife_application/include/voicelife/application/interaction_orchestrator.h:9;实际带外传参发生在 components/voicelife_runtime_esp/src/esp_runtime_interaction.cc:133
  • 触发条件: 对同一 kWakeDetected/kInterruptAndAcknowledge 状态事件使用不同唤醒词,或迁移后发生唤醒词丢失、串换、错误复用。
  • 实际影响: InteractionEvent 只包含事件枚举,InteractionAction 只包含来源、状态和指令;真实唤醒词暂存在 Runtime::active_wake_word_,不进入编排器 trace。最小复现对两个无法编码不同唤醒词的事件序列输出 identical,因此现有 interaction_orchestrator_test 即使平台最终收到错误唤醒词也会通过。
  • 违反验收: [Refactor] 固化 Runtime 启动与中断事件的行为轨迹 #280 明确要求轨迹断言“动作类别、顺序、关键参数”及旧路径/编排器同输入零差异;当前测试仅覆盖枚举和状态,不能证明唤醒/打断动作参数完整传递。
  • 最小修复: 将唤醒词等执行参数纳入稳定 InteractionEvent/InteractionAction 并由 sink 直接消费;或者增加可在主机运行的 EspInteractionTaskHost -> Runtime action projection -> BoardRequest 参数测试,至少验证两种不同唤醒词不会生成相同平台请求。

P2 — runtime_esp 通过手工 include 路径形成未声明反向依赖

  • 位置: components/voicelife_runtime_esp/CMakeLists.txt:9,消费点为 components/voicelife_runtime_esp/src/esp_runtime_internal.h:27
  • 触发条件: 独立构建、复用或调整 voicelife_runtime/voicelife_runtime_esp 边界时。
  • 实际影响: CMake 声明 voicelife_runtime -> voicelife_runtime_esp,但 runtime_esp 又通过 PRIV_INCLUDE_DIRS "../voicelife_runtime/include" 直接读取 voicelife_runtime 所有的 PlatformAssembly 公共头。依赖图门禁仍报告通过,因为该真实源码依赖没有通过 REQUIRES 表达;若如实声明则形成组件环。这使“Runtime 薄组合边界”和“ESP Adapter 可独立替换”的边界在构建系统中不可验证,并违反仓库规则“跨组件只能引用公开 include,依赖只能从外向内”。
  • 违反验收: PR 声明 Runtime 仅保留组合/启动入口,并要求模块边界与依赖方向稳定;当前实现只是通过相对 include 路径隐藏了反向依赖。
  • 最小修复:PlatformAssembly/相关稳定 Port 移到二者共同依赖的内层公共组件,或把 ESP 启动入口改为只接收内层 Port/装配参数;删除跨组件 PRIV_INCLUDE_DIRS,让所有源码依赖由 REQUIRES/PRIV_REQUIRES 如实表达,并给架构脚本增加禁止 ../<other-component>/include 的检查。

已对照行为

  • Runtime::StartRequestInterrupt 的公开转发路径。
  • 启动、唤醒、按键采集、打断并唤醒、VAD 端点、最终 STT 超时、TTS follow-up、断线/重连、失败恢复。
  • 事件队列溢出策略、唯一状态写者、FreeRTOS board/MCP/IM 任务、定时器回调、显示快照与绑定展示租约。
  • ESP-only helper 迁移、主机测试源路径及组件依赖声明。
  • 动作 sink 失败会在动作执行失败后保留已迁移状态;该语义与迁移前路径一致,因此未作为本 PR 新问题报告。

实际验证

  • git diff --check <fixed-range>:通过。
  • ./scripts/check_architecture.sh:通过,但不能发现上述手工 include 形成的隐藏依赖。
  • python3 scripts/check_public_api_docs.py:通过,检查 90 个公共头文件。
  • python3 scripts/check_contract_dual_end.py:通过。
  • 受影响的 8 个主机目标:全部通过(编排器、控制器、Linx MCP、IM binding、binding presentation/lease、MCP policy、Wi-Fi provisioning)。
  • python3 -m unittest discover -s tests/python -p 'test_*.py':110 个通过,1 个跳过;命令退出成功。
  • 最小参数复现:两个无法表达不同唤醒词的编排输入产生相同 trace(输出 identical)。
  • GitHub 当前 ESP-IDF、主机、格式、CodeQL 等检查均为成功;未据此降低审查标准。

未验证与残余风险

  • 本地全量主机构建在未由本 PR 修改的 components/voicelife_audio_esp/src/esp32s3_pcm_audio_port_internal.h 上被 GCC -Werror=attributes 中止,因此不能把本次全量 ctest 表述为通过;随后仅运行并通过了本 PR 直接影响的 8 个目标。
  • 环境没有 clang-format,未本地复跑格式脚本。
  • 未在本环境重新执行 ESP-IDF 固件构建或真机行为轨迹;仅确认当前 head 对应 GitHub ESP-IDF 检查成功。

View job run

@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 请基于此 PR 的当前 head进行独立严格复审。先确认本次审查的 head SHA 与 merge base;不得复用旧 head 的结论,也不得因 CI、重构标签或已有 Review 省略逐文件核对。\n\n必须检查:\n- 逐项验证此前已报告问题是否被真正修复,并尝试构造最小反例使其重新出现;\n- 对新增/修改测试做 mutation 思考:改变关键实现值、参数或调用顺序后,测试是否会失败;\n- 对接口、所有权、生命周期、异常、并发/状态机和组件依赖方向进行实际代码级检查;\n- 运行可用的相关检查;未运行项必须说明原因、影响与残余风险。\n\n发现问题请按 P0/P1/P2 给出准确位置、触发条件、影响、违反的 Issue/PR 验收及最小修复。未发现问题也必须列出对照行为、命令结果和未覆盖范围。不要修改代码或提交。

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

当前 Head 与基线

  • Head: c4512839a548fe4cc5e4a941dea9315da3eef323
  • Merge base: 1ad310a37488bb53a6ce505c142d677b7d1df8f4
  • 全部 diff 检查均使用固定范围 1ad310a...c451283;未复用旧 head 的结论。

审查结论

逐文件复核完整固定 diff,并将拆分后的 Runtime 方法与 merge base 实现逐方法对照。此前“唤醒参数不进入 trace”的问题已从模型、投影路径和测试断言上真正修复;mutation 能捕获参数丢失、常量替换、状态错误和动作错误。但当前 head 新增一个 P1 编译回归,且此前报告的组件隐藏反向依赖仍存在。

P1 — 新增字段后测试无法在仓库告警策略下编译

  • 位置: tests/host/interaction_orchestrator_test.cc:41tests/host/interaction_orchestrator_test.cc:48tests/host/interaction_orchestrator_test.cc:83tests/host/interaction_orchestrator_test.cc:88
  • 触发条件:tests/host/CMakeLists.txt 配置的 GCC/Clang 参数构建 interaction_orchestrator_test;该配置启用 -Wmissing-field-initializers -Werror
  • 实际影响: InteractionEvent 新增 wake_wordInteractionAction 新增 wake_word 后,多处非唤醒聚合初始化没有显式初始化新字段。最小复现 cmake --build /tmp/voicelife-pr282-current --target interaction_orchestrator_test -j2 稳定失败,错误为 missing initializer for member ... wake_word [-Werror=missing-field-initializers]。当前 GitHub 的“主机测试与架构边界”、Codecov 和 CodeQL C++ 也均失败,ESP-IDF 检查被跳过。
  • 违反验收: PR/Issue 要求主机行为轨迹可执行并通过;当前新增回归测试本身不能编译,无法提供所声称的参数轨迹保护,也直接阻塞必需检查。
  • 最小修复: 为所有聚合初始化显式补 .wake_word = {},包括非唤醒事件、预期动作、TTS follow-up 动作和乱序事件;然后重新运行完整 host build/CTest。也可为字段提供显式默认成员初始化,但仍应确认仓库编译器对 designated initializer 的告警行为。

P2 — runtime_esp 仍通过手工 include 路径隐藏反向依赖

  • 位置: components/voicelife_runtime_esp/CMakeLists.txt:9;消费点 components/voicelife_runtime_esp/src/esp_runtime_internal.h:27
  • 触发条件: 独立构建、复用或调整 voicelife_runtime/voicelife_runtime_esp 边界。
  • 实际影响: 构建图声明 voicelife_runtime -> voicelife_runtime_esp,但 ESP 组件通过 PRIV_INCLUDE_DIRS "../voicelife_runtime/include" 直接读取 Runtime 所有的 PlatformAssembly 头。真实依赖没有由 REQUIRES 表达;如实声明会形成环。check_architecture.sh 因只检查声明图而错误报告通过,无法保证 PR 声称的薄 Runtime/可替换 Adapter 边界。
  • 违反验收: 仓库规则要求依赖从外向内、跨组件只引用公开 include 且由组件依赖表达;PR 也声明 Runtime 只保留组合和启动边界。当前实现以相对 include 路径绕过了该约束。
  • 最小修复:PlatformAssembly/稳定 Port 移入两者共同依赖的内层公共组件,或让 ESP 启动入口只接收内层 Port;删除跨组件 PRIV_INCLUDE_DIRS,并在架构门禁中禁止 ../<other-component>/include

此前问题复验

  • 唤醒参数轨迹:已修复。 InteractionEvent::wake_word 被同步复制到拥有所有权的 InteractionAction::wake_word,Runtime sink 直接消费 transition.wake_word,旧的 active_wake_word_ 带外成员已删除。
  • 最小反例:未能重现旧问题。 不同唤醒词现在生成不同 action trace。
  • Mutation:有效。 在仅为执行 mutation 而关闭上述单一编译告警的临时副本中,删除唤醒参数、替换为常量、篡改状态、篡改指令四种变异均使测试失败。

已对照行为

  • Start/RequestInterrupt 公开转发和错误返回。
  • boot、wake、interrupt-and-ack、按键采集、VAD endpoint、final STT timeout、TTS follow-up、断线重连和失败恢复。
  • 事件队列容量/溢出策略、唯一状态写者、board/MCP/IM 任务、timer 回调、显示 revision/generation、绑定展示租约和关闭顺序。
  • 19 个 ESP-only helper 为 100% 内容重命名;拆分后的 Runtime 方法除编排器和参数传递改造外,与 merge base 逐方法一致。
  • std::string_view 输入在同步 Handle 中复制为 std::string 后再交给 sink,当前调用路径未发现悬空生命周期。

实际运行

  • git rev-parse HEADc4512839a548fe4cc5e4a941dea9315da3eef323
  • git merge-base ...1ad310a37488bb53a6ce505c142d677b7d1df8f4
  • git diff --check <fixed-range>:通过。
  • ./scripts/check_architecture.sh:通过,但不能检测上述隐藏 include 依赖。
  • python3 scripts/check_public_api_docs.py:通过,90 个公共头。
  • python3 scripts/check_contract_dual_end.py:通过。
  • python3 -m unittest tests.python.test_im_wifi_credential_isolation:5/5 通过。
  • interaction_orchestrator_test编译失败,原因即 P1。
  • 当前 GitHub checks:主机测试、Codecov、CodeQL C++ 失败;ESP-IDF 跳过;不能表述为 CI 通过。

未验证与残余风险

  • 因 P1 导致主机测试目标不能构建,未能在当前 head 上完成完整 CTest;修复后必须重新运行。
  • 本环境没有 clang-format,未本地复跑格式检查。
  • ESP-IDF 检查在当前 head 被跳过,本次未重新执行固件构建或真机轨迹,因此 ESP 编译和设备行为仍是未验证范围。

View job run

@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 请基于刚推送的当前 head 严格复审。先确认 head SHA 与 merge base;不要复用对旧 head 的结论。逐项检查:(1) 所有聚合初始化在 GNU -Wmissing-field-initializers -Werror 下可编译,非唤醒事件/动作显式为空;(2) wake word 是动作值所有而非 Runtime 侧带外状态;(3) runtime -> runtime_esp 的反向依赖与相对 include 绕过已清除,公共 API 和链接归属没有重复或断裂;(4) 行为轨迹和未知/乱序事件无回归。请以最小反例和 mutation 思考验证测试有效性。禁止修改代码或提交。只报告可复现 P0/P1/P2、证据、命令和剩余风险;无问题也请明确检查范围。

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

An internal error occurred. Please try again later.

View job run

@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 请基于刚推送的当前 head 严格复审。先锁定 head SHA 与 merge base,禁止复用旧结论。此前架构和编译问题已在前一 head 修复;本次新增验证只涉及交互动作投影失败与完整动作等价比较。请验证:(1) Result::ok() 已是状态和值的单一真源,删除重复 has_value() 条件不改变行为;(2) sink 返回失败时状态迁移与错误返回保持既有语义,动作内容完整;(3) source/state/directive/wake_word 的任一 mutation 都能使等价断言失败;(4) 覆盖率门槛修复没有掩盖生产分支。运行可用命令并报告可复现 P0/P1/P2、证据和剩余风险。禁止修改代码或提交。

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

严格复审结论

未发现可复现的 P0 / P1 / P2。 本次结论仅针对当前锁定提交,不复用旧 head 结论。

锁定范围

  • Head:2cf2ae9f1de818dfd5610adec483812b62b189a6
  • Merge base:1ad310a37488bb53a6ce505c142d677b7d1df8f4
  • 固定范围:git diff 1ad310a37488bb53a6ce505c142d677b7d1df8f4...2cf2ae9f1de818dfd5610adec483812b62b189a6
  • 复审结束时 GitHub PR head/base 仍与上述 SHA 一致,未发生漂移。

逐项证据

  1. Result::ok() 是状态和值的单一真源,删除重复条件不改变行为。

    • components/voicelife_contracts/include/voicelife/contracts/status.h:46 定义为 status.ok() && value.has_value()
    • 因此旧条件 !transition.ok() || !transition.value.has_value() 的第二项被第一项完全蕴含:无值时 transition.ok() 必为 false;有值但状态失败时同样由第一项拒绝。
    • 成功后解引用 transition.value 仍由同一个 ok() 不变量保护;本次删除的是不可独立到达的重复判断,不是生产行为分支。
  2. sink 失败保持既有状态迁移与错误返回语义,且动作完整。

    • InteractionOrchestrator::Handle 先调用 controller 完成迁移,再构造包含 source/state/directive/wake_word 的值拥有动作,最后直接返回 actions.Submit(...)components/voicelife_application/src/interaction_orchestrator.cc:5
    • RejectingSink 用例验证返回 kUnavailable 原样透传、收到完整 boot 动作、controller 已保持 kStandby 且不回滚:tests/host/interaction_orchestrator_test.cc:112
    • 基线 1ad310aRuntime::HandleInteractionEvent 同样先调用 controller,再执行动作投影;后续投影错误直接返回且无回滚机制。当前顺序与历史语义一致。
  3. 四字段任一 mutation 都会使等价断言失败。

    • operator== 明确同时比较四字段:components/voicelife_application/include/voicelife/application/interaction_orchestrator.h:24
    • 测试以同一 baseline 分别只改变 sourcestatedirectivewake_word,四个非等价断言均执行通过:tests/host/interaction_orchestrator_test.cc:90
    • 选取的替代值均与 baseline 实际值不同,不存在“mutation 未生效”的假阳性。
  4. 覆盖率修复未掩盖生产分支。

    • PR 未修改 codecov.yml.github/workflows/ci.yml,项目/patch 的 C++ 门槛仍为 80%、threshold 0%。
    • GCC 覆盖率构建运行目标测试后,原生 gcov -b -c 显示 interaction_orchestrator.cc:行覆盖 100%(10/10),分支被执行 100%(10/10);if (!transition.ok()) 的错误返回与成功投影两侧分别执行 1 次和 11 次。
    • 未命中的仅为编译器生成的异常边;正常生产控制流两侧均有证据。当前 SHA 的 codecov/patch/cpp 与“覆盖率(Codecov)”检查均成功。

可复现验证

  • g++ + -Wmissing-field-initializers -Werror 构建 interaction_orchestrator_test:通过。
  • 普通目标测试:1/1 通过。
  • VOICELIFE_ENABLE_COVERAGE=ON 目标构建与测试:1/1 通过。
  • git diff --check b7031fa..2cf2ae9:通过。
  • 仓库工作区保持干净,未修改代码、未提交。

剩余风险

  • GitHub 当前除 ESP-IDF 6.0 / ESP32-S3 仍在运行外,其余已返回的检查均成功;PR 仍为 mergeable_state: blocked,应等待该检查和既定人工审批条件完成。
  • 本地 ./scripts/run_checks.sh 在 GCC 11 全量构建时被未改动的 display/audio 文件 [[maybe_unused]] 警告(全局 -Werror)阻断;该失败不位于本 PR diff,且当前 SHA 的 GitHub“主机测试与架构边界”已成功,因此未列为本 PR 的 P0/P1/P2。

View job run

@ZhaoXingPeng
ZhaoXingPeng force-pushed the refactor/runtime-event-trace branch from 2cf2ae9 to e5e1802 Compare August 18, 2026 08:01
@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator Author

@fennoai 请仅基于当前 head e5e1802e635235820a5dd5b0718da80593a351d0 做一次独立、严格、可复现的复审。先报告实际取得的 head SHA 与 merge base;不得沿用旧 SHA 的结论,也不得因本次声明为元数据重推、CI 通过或已有审查而跳过核对。

审查要求:

  1. 对当前固定 diff 逐文件检查,并确认与先前已审版本相比源码树确实没有意外漂移;若发现任何可观察行为、构建输入或测试内容变化,按其实际风险审查。
  2. 重新检查 InteractionEvent/InteractionAction 的 wake word 值所有权、Result::ok() 错误语义、sink 失败后的状态迁移与错误传播,以及 Runtime 与 runtime_esp 的依赖方向和公开 API/链接归属。
  3. 用 mutation 思考验证测试:分别篡改 source、state、directive、wake_word、投影错误返回或调用顺序时,现有测试是否必然失败;指出无法捕获的反例。
  4. 运行可用的相关命令。不能运行的项目必须明确原因、影响和残余风险,严禁表述为已通过。

输出规则:只报告可复现的 P0/P1/P2,每项包含准确位置、触发条件、影响、违反的验收要求与最小修复。若没有问题,也必须列出核对范围、实际命令结果及未覆盖风险。禁止修改代码或提交。

@fennoai

fennoai Bot commented Aug 18, 2026

Copy link
Copy Markdown

独立严格复审结论

实际取得的 head SHA: e5e1802e635235820a5dd5b0718da80593a351d0
实际 merge base: 1ad310a37488bb53a6ce505c142d677b7d1df8f4

固定审查范围为:
git diff 1ad310a37488bb53a6ce505c142d677b7d1df8f4...e5e1802e635235820a5dd5b0718da80593a351d0

未发现可复现的 P0 / P1 / P2。 本次从当前固定 diff 独立核对,未以 CI 或旧审查结论代替检查。

源码漂移核对

  • 当前 head tree:9ce3db00430647c43f676d37e693f5e7868d9f62
  • 前一已审 SHA 2cf2ae9f1de818dfd5610adec483812b62b189a6 的 tree 也是同一对象;git diff 2cf2ae9..e5e1802 无任何文件、源码、构建输入或测试内容变化。当前重推仅改变提交对象元数据。
  • 当前固定 diff 共 39 个文件,已逐文件按类别核对:19 个 R100 文件的旧/新 blob object 完全相同;其余 20 个文件包括 application 契约/实现、Runtime CMake 与旧聚合文件删除、runtime_esp CMake/头文件/7 个拆分实现、main 链接输入、架构门禁、host CMake/测试及 Python 路径测试。
  • git diff --check 通过,工作区保持干净。

语义核对

  • Wake word 所有权: InteractionEvent 使用同步借用的 std::string_view,但 ESP 事件队列先复制进 InteractionEventItem::wake_wordstd::stringcomponents/voicelife_runtime_esp/src/esp_runtime_event_loop.cc:9。编排器再复制为 InteractionAction::wake_wordstd::string,sink 按值接收:components/voicelife_application/include/voicelife/application/interaction_orchestrator.h:12components/voicelife_application/src/interaction_orchestrator.cc:10。当前调用链不存在依赖队列项或调用方临时缓冲区的动作数据。
  • Result::ok() components/voicelife_contracts/include/voicelife/contracts/status.h:46 同时要求 status.ok()value.has_value();删除外层重复 has_value() 不改变错误语义,成功后解引用仍由同一不变量保护。
  • Sink 失败: controller 在 sink 调用前完成状态迁移;编排器直接返回 actions.Submit(...)。失败测试验证 kUnavailable、完整四字段动作及迁移后 kStandby 不回滚:tests/host/interaction_orchestrator_test.cc:112。当前实现与迁移前“先迁移、后投影、错误直返且不回滚”的顺序一致。
  • 依赖与链接归属: voicelife_runtime 仅保留公共头和平台装配依赖,不依赖 ESP;voicelife_runtime_esp 显式依赖 application/contracts/runtime,并拥有 ESP 实现:components/voicelife_runtime/CMakeLists.txt:1components/voicelife_runtime_esp/CMakeLists.txt:1。公共 runtime::Start/RequestInterrupt 只在 voicelife_runtime_esp/src/esp_runtime.cc:263 定义,runtime_esp 仅兼容转发;main 显式链接两组件。未发现反向依赖或相对 include 绕过。

Mutation 灵敏度

  • 篡改生成动作或 operator== 中的 sourcestatedirectivewake_word:现有完整轨迹断言及四个单字段 mutation 断言必然失败,替代值均与 baseline 实际值不同:tests/host/interaction_orchestrator_test.cc:85tests/host/interaction_orchestrator_test.cc:90
  • 将 sink 失败改为成功或改成其他错误码:projection_failure 断言必然失败;回滚 controller 状态也会被最终状态断言捕获。
  • 不能捕获的反例: 若实现丢弃 sink 原始 message、重新构造相同 kUnavailable code,当前测试仍会通过,因为只比较 ok() 与 code;当前生产代码直接返回 sink Status,静态核对正确,但测试未证明 message 原样传播。
  • 不能捕获的反例: 若 mutant 在 sink 调用时尚未提交状态、但随后补做迁移并产生相同动作/最终状态,当前 RejectingSink 不在 Submit 内观察 orchestrator 状态,因此调用时序瞬间不可见;同样,失败路径重复提交两次相同动作也可能通过,因为 RejectingSink 不计调用次数。当前生产代码顺序正确,但测试不能排除这些反例。
  • 不能捕获的反例: 若同时把动作 wake word 类型退化为 string_view,现有测试使用稳定字符串字面量,可能无法暴露悬垂生命周期;当前公开类型为 std::string,因此本 head 不存在该缺陷。

实际命令结果

  • python3 scripts/check_public_api_docs.py:通过(90 个公共头)。
  • ./scripts/check_architecture.sh:通过。
  • python3 scripts/check_contract_dual_end.py:通过。
  • python3 -m unittest tests.python.test_im_wifi_credential_isolation:5/5 通过。
  • GNU 11.4 + -Wmissing-field-initializers -Werror 构建 interaction_orchestrator_test:通过。
  • 普通目标测试:1/1 通过。
  • VOICELIFE_ENABLE_COVERAGE=ON 目标构建与测试:1/1 通过。
  • 原生 gcov -b -cinteraction_orchestrator.cc 行覆盖 100%(10/10),分支被执行 100%(10/10);正常错误与成功路径均执行。

未覆盖风险

  • ./scripts/check_format.sh 未执行成功:本地缺少 clang-format。因此不将本地格式检查表述为通过;当前固定 SHA 的 GitHub“格式与 Python 静态检查”为成功。
  • 干净临时目录的全量 host 构建在 GNU 11.4 下被未改动的 display/audio 头文件 [[maybe_unused]] 警告配合全局 -Werror 阻断,故全量 ctest 未运行。失败位置不在本 PR diff;当前固定 SHA 的 GitHub“主机测试与架构边界”为成功,但本地无法提供第二份全量复现证据。
  • 本地未配置 ESP-IDF 6.0 工具链,未执行固件构建;截至复审结束,GitHub ESP-IDF 6.0 / ESP32-S3 仍在运行,不能表述为通过。PR 当前 mergeable_state 仍为 blocked
  • 本地仅生成相关目标的 GCC 覆盖率,未生成完整 Codecov XML;当前固定 SHA 的 GitHub coverage 与 codecov/patch/cpp 已成功,但这不替代尚在运行的 ESP 构建。

View job run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FullSpec 规格粒度-影响面大的完整规格

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] 固化 Runtime 启动与中断事件的行为轨迹

1 participant