Skip to content

✨ feat(timing): 实现提醒规则删除 - #169

Merged
jing-gou merged 3 commits into
1024XEngineer:mainfrom
jing-gou:dev/142-delete-reminder-rule-main
Aug 6, 2026
Merged

✨ feat(timing): 实现提醒规则删除#169
jing-gou merged 3 commits into
1024XEngineer:mainfrom
jing-gou:dev/142-delete-reminder-rule-main

Conversation

@jing-gou

@jing-gou jing-gou commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

结论

本 PR 完成 #142DeleteReminderRule 通过 Timing Store 的单一原子操作关闭规则、取消未来 pending trigger,并保留历史 trigger 事实。请 Reviewer 重点确认未来时间边界、规则与 trigger 的原子性,以及重复删除和关联错误的语义。

Refs #142

背景

DeleteReminderRule 此前仍由 Service stub 返回 kUnavailable,调用方无法关闭不再需要的提醒规则。本 PR 基于最新 main,仅包含 #142 的删除行为。

变更

  • 新增 TimingTaskStorePort::DisableReminderRule:同一原子边界内关闭规则并取消尚未发生的 pending trigger,返回受影响数量。
  • 实现 DefaultTimingTaskService::DeleteReminderRule,校验空标识并透传 Store 的 NotFound、Conflict 与基础设施错误。
  • 内存 fake 支持规则关闭、trigger 取消、关联完整性校验和一次性写入失败注入。
  • Host 测试覆盖成功、未来 trigger 取消、历史 trigger 保留、NotFound、重复删除、关联错误和 Store 原子失败。

明确未包含:

  • SQLite/NVS 生产 Adapter、持久化迁移或数据清理。
  • Runner/FreeRTOS 生命周期、实际 trigger 投递、音频或 IM 消息投递。
  • ListReminderTriggers、snooze、dismiss、完整 recurrence 展开或 Gateway 编排。

架构与兼容

TimingTaskStorePort 增加纯虚 DisableReminderRule,后续真实 Adapter 必须在其事务中实现同样的全成或全败语义。公开 Service DTO、Profile、外部协议、组件依赖方向和持久化格式均未改变;当前只影响 Host 内存 fake。

验证

  • ./scripts/run_pre_submit_checks.sh:本机未完成,Ruff 0.16.1 与仓库固定的 0.12.7 不匹配。
  • 远端 CI 的工作流、格式、IM Gateway、主机测试、架构、ESP-IDF、覆盖率、CodeQL 及依赖审查状态。
  • ESP-IDF 对应 Profile 构建:本机无 idf.py,仅完成 python3 scripts/firmware.py validate
  • 真机或外部服务验证:不适用,实际投递属于范围外。

证据:

  • ctest --test-dir build-host --output-on-failure:14/14 通过。
  • python3 scripts/check_public_api_docs.pyscripts/check_architecture.sh:通过。
  • LLVM 18 clang-format --dry-run --Werrorgit diff --check:通过。

TDD 记录

  • RED:先在 timing_task_service_test 写入活动规则删除场景,执行后因 DeleteReminderRule stub 返回 kUnavailable 失败。
  • GREEN:引入原子 Store Port 并实现 Service 后,删除结果、规则状态和 future pending trigger 状态均满足断言。
  • REFACTOR:将实现从 timing_task_service_stubs.cc 移到独立翻译单元,保留其余未实现 Service 方法的 stub 边界。

风险与回退

未来 trigger 的定义为 planned_trigger_at >= now 且状态为 pending;此语义已写入 Port 文档,真实 Adapter 需要一致实现。真实持久化、并发和掉电恢复尚未覆盖,属于后续 Adapter 任务。

当前无数据迁移。若需回退,回退本 PR 的单一提交即可恢复 kUnavailable stub;已物化数据不会被本 PR 的 Host fake 之外代码修改。

@jing-gou
jing-gou requested a review from ZhaoXingPeng August 6, 2026 04:00
通过 Timing Store 的原子关闭操作将规则置为 disabled,并取消未发生的 pending trigger,保留历史触发事实。\n\nHost 测试覆盖成功、NotFound、重复删除、关联错误和 Store 原子失败;SQLite/NVS、Runner 与投递仍不在本子任务范围内。\n\nRefs 1024XEngineer#142
@jing-gou
jing-gou force-pushed the dev/142-delete-reminder-rule-main branch from cfb9519 to c74b365 Compare August 6, 2026 04:02
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

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

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

📢 Thoughts on this report? Let us know!

@jing-gou
jing-gou marked this pull request as ready for review August 6, 2026 04:05
@jing-gou

jing-gou commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai 你是资深后端/全栈架构师 + 嵌入式硬件工程师复合型专家,执行严格、客观、不留情面的代码仓库Review,请遵循下面所有评审规则,逐条输出审查结果,禁止敷衍、禁止只说空话、禁止笼统概括。
硬性约束:最终输出评审条目数量不少于20条,若直观可见问题不足,主动挖掘隐性架构隐患、软硬件兼容风险、长期运行潜在缺陷补足条目,不得简化评审内容。

评审维度

  1. 架构与模块设计
  • 模块职责是否清晰,是否存在循环依赖、职责混杂
  • 分层是否合理,是否违反单一职责原则、开闭原则
  • 接口抽象、依赖注入设计是否规范,有无硬编码耦合
  • 软件与硬件模块边界是否清晰,硬件相关逻辑是否侵入业务层
  1. 代码规范与可读性
  • 命名:变量、函数、类、文件命名是否语义清晰,禁止模糊命名、拼音命名
  • 注释:复杂逻辑、硬件时序、特殊寄存器配置必须注释;冗余注释、无效注释、过期注释需要指出
  • 代码格式、风格是否统一,是否存在大量魔法数字、魔法字符串,硬件参数无常量定义
  1. 性能隐患
  • 循环内IO、数据库重复查询、不必要的内存占用、低效算法
  • 资源是否释放(连接、句柄、定时器、文件流、硬件外设句柄)
  • 嵌入式场景:阻塞轮询、中断处理耗时过长、内存频繁分配释放
  1. 安全性检查【重点】
  • 输入校验、SQL注入、XSS、权限控制、敏感信息明文打印
  • 密钥、token、数据库地址、硬件访问口令是否硬编码提交到仓库
  • 外部指令下发至硬件驱动缺少权限校验,存在设备失控风险
  1. 健壮性 & 异常处理
  • 是否缺少异常捕获、错误分支处理
  • 参数判空、边界条件、失败重试逻辑是否完备
  • 硬件通讯异常(I2C/SPI/UART断线、设备无应答)缺少容错、恢复逻辑
  • 缺少硬件故障状态上报、故障隔离机制
  1. 硬件驱动 & 软硬件协同评审(新增专项)
  • 驱动代码与硬件原理图引脚定义是否匹配,无硬件版本兼容逻辑
  • 外设操作缺少电平保护、超时判断,存在烧毁外设芯片风险
  • 运动控制逻辑(如有)缺少软限位、急停、碰撞检测保护
  • 上下位机通讯协议:缺少校验和、重传、断线重连机制
  • 硬件参数(电流、电压、速度阈值)直接写死代码,无统一配置入口
  1. 测试覆盖
  • 是否缺少单元测试、边界用例
  • 核心业务逻辑、硬件驱动逻辑无测试直接合并风险
  • 缺少硬件异常工况、极限压力场景测试用例
  1. 工程规范
  • 无用代码、注释掉的废弃代码是否大量遗留
  • 依赖包版本风险、冗余依赖、存在已知漏洞依赖
  • 配置区分环境(开发/样机/生产),环境配置是否混写
  • 固件、上位机、3D模型、原理图版本号不统一,无关联管理
  1. 可维护性 & 扩展性
  • 新增需求是否需要大面积修改原有代码
  • 是否存在大量重复代码,缺少公共封装
  • 硬件型号迭代时,软件需要大规模改动,缺少适配抽象层

输出要求

  1. 问题分级:🔴严重阻塞(必须修改才能合并/上机测试) / 🟡建议优化(不阻塞但强烈建议重构) / 🟢可选改进
  2. 每条问题固定格式:【文件路径+代码位置】问题描述 + 风险说明 + 可直接落地的修改方案
  3. 条目数量强制≥20条;问题较少时区分表层问题与隐性长期风险,拆分为独立条目逐条列出,禁止多条问题合并为一条
  4. 最后汇总三部分:
    ① 整体仓库风险总结
    ② 优先级整改清单(阻断项优先)
    ③ 长期架构、软硬件协同优化方案
  5. 如果存在架构层面缺陷,单独提炼顶层设计问题,不要只局限单行代码
  6. 不要美化结论,发现隐患直接指出,客观评估风险等级;不使用“建议考虑”这类温和模糊表述,明确写出故障后果(程序崩溃、硬件烧毁、机构失控、数据泄露等)

现在开始对提供的仓库代码/PR变更、硬件资料、结构模型进行评审

@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.

No issues found in this review.

@fennoai

fennoai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review 结果

🔴 [components/voicelife_timing/src/timing_task_service_delete_reminder_rule.cc:7-8] 只校验空串,不校验空白/控制字符。风险:外部协议传入脏 ID 会下沉到 store,形成“看似合法但永远 not found”的请求。修改:复用统一 ID 校验器,至少 trim 后再判空。

🟡 [components/voicelife_timing/src/timing_task_service_delete_reminder_rule.cc:11-13] 直接透传 store 错误但不附带 rule id。风险:线上定位删除失败时无法知道是哪条规则。修改:在错误消息里补上 reminder_rule_id 或结构化上下文。

🟢 [components/voicelife_timing/src/timing_task_service_delete_reminder_rule.cc:15-18] 返回 DTO 固定 kDisabled。风险:未来 store 若区分 soft-delete/disabled,这层会吞掉状态差异。修改:让结果状态来自 store 映射。

🔴 [components/voicelife_timing/include/voicelife/timing/timing_task_store.h:45-48] 新 pure virtual 没有迁移窗口。风险:任何外部 TimingTaskStorePort 实现都会编译失败。修改:先同步迁移所有适配器,或给旧实现留默认 NotImplemented 过渡。

🟡 [components/voicelife_timing/include/voicelife/timing/timing_task_store.h:45-48] 只返回受影响数量,不能回传被取消 trigger 列表。风险:调用方无法审计 side effects。修改:把结果升级为包含取消摘要的 struct。

🟢 [components/voicelife_timing/include/voicelife/timing/timing_task_store.h:45-48] 取消边界写死在注释里。风险:第二个 adapter 很容易实现出不同语义。修改:抽共享 helper 或常量定义。

🟡 [tests/host/support/timing_fakes.h:75-79] 失败注入是全局单次状态。风险:以后新增并发用例时,非目标 delete 可能先消费掉失败。修改:把失败注入挂到 rule id 或单独队列。

🟡 [tests/host/support/timing_fakes.h:81-90] 只校验 task 存在,不校验 trigger 与 rule 的 task 一致性。风险:跨 task 污染不会被测试暴露。修改:在 trigger 读取/删除时同时校验 trigger.task_id == rule.task_id

🟡 [tests/host/support/timing_fakes.h:92-109] 每次删除都复制整张 rules/triggers map。风险:大状态下内存翻倍,删除路径退化成 O(n)。修改:只复制受影响项,或用事务式 staging 记录变更集。

🔴 [tests/host/support/timing_fakes.h:99-105] 只取消 planned_trigger_at >= now 的 pending 记录。风险:调度器滞后时,planned_trigger_at < now 但仍 pending 的记录会在删规则后继续可投递。修改:删除规则时取消所有该 rule 的 pending trigger,或把“未来”定义改成“尚未执行”。

🟢 [tests/host/support/timing_fakes.h:102-105] 被取消的 trigger 没有写 deleted_at。风险:后续如果按终态时间做查询/回放,会把取消记录看成未终结。修改:同步写入 deleted_at = now,或明确字段约定。

🟢 [tests/host/support/timing_fakes.h:115-118] AddReminderTrigger / AddReminderRule 公开暴露。风险:测试可以构造任何脏状态,掩盖真实 adapter 的约束缺陷。修改:把这些 helper 收窄到私有/受控工厂,或显式标成 corruption injection。

🟢 [tests/host/support/timing_fakes.h:121-126] FindReminderTrigger 按值返回整对象。风险:payload 变大后测试辅助会产生不必要拷贝。修改:返回指针或 reference_wrapper

🟡 [tests/host/timing_task_service_test.cc:291-292] front() 依赖 ListRules 的排序实现。风险:规则排序策略一改,测试就炸,但业务语义没变。修改:按 typeid 选目标规则,不要依赖排序位置。

🟡 [tests/host/timing_task_service_test.cc:293-306] 测试时间戳全是硬编码。风险:exact-now 边界和时钟漂移都没覆盖。修改:用 clock.Now() 派生相对时间,补 == now 用例。

🟢 [tests/host/timing_task_service_test.cc:308-320] 只断言状态和计数。风险:updated_atdeleted_atlast_action_at 回归不会被发现。修改:把终态元数据也断言进去。

🟢 [tests/host/timing_task_service_test.cc:321-334] 重复删除 / 孤儿规则只看返回码。风险:冲突路径可能意外污染 store 状态却没人发现。修改:冲突后再回读 rule / trigger 状态。

🟡 [tests/host/timing_task_service_test.cc:336-349] 只验证“调用前失败”。风险:真实事务中间态回滚没有被覆盖。修改:在 fake 中增加“写规则后故障”钩子,验证整批回滚。

🟢 [tests/host/timing_task_service_test.cc:328-349] 通过 helper backdoor 注入 orphan state。风险:测试把非法状态当成正常路径,后续会掩盖 adapter 的约束缺陷。修改:把这类注入隔离到专门的 corruption fixture。

🟡 [tests/host/timing_task_service_test.cc:279-289] 新 delete 路径只在 host fake 上验证。风险:真实持久化适配器未覆盖时,生产行为可能偏离。修改:补一层 adapter / 端到端测试或至少接口契约测试。

整体风险

这次改动的主逻辑是通的,timing_task_service_test 编译并通过,git diff --check 也干净。真正的高风险点只有一个:删除时对 pending trigger 的时间边界过窄,会把“已经过期但尚未投递”的记录漏掉,直接留下后续仍可能执行的脏状态。

优先级

  1. 先修正 pending trigger 的取消边界。
  2. 再补 exact-now / overdue-pending / metadata 断言。
  3. 最后收紧 fake store 的完整性校验和 adapter 迁移覆盖。

长期方案

把“规则关闭 + 触发取消”的语义抽成共享领域约束,别让每个 adapter 各写一套边界判断;同时把测试夹具从“可随意注入脏状态”收敛成“显式 corruption fixture”,这样后续持久化适配器接进来时,测试才不会继续放过数据一致性问题。

已验证:cmake -S /workspace/VoiceLife/tests/host -B /tmp/voicelife-host-buildcmake --build /tmp/voicelife-host-build -j2 --target timing_task_service_test/tmp/voicelife-host-build/timing_task_service_testgit -C /workspace/VoiceLife diff --check 85aad72e68695ea0eec3766cc7d4b52b2ee4c7eb...c74b365d96da236fe5fb5a1b8c6c95874810d78c

删除规则时校验 trigger 与规则任务归属一致;故障注入在候选变更构造后返回,以验证规则和 trigger 均不会部分提交。\n\n补充 now 边界、关联错误和原子失败的 Host 测试,未扩展 Runner、投递或生产存储适配器。\n\nRefs 1024XEngineer#142
@jing-gou

jing-gou commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Reference: fennoai review and Codecov report.

判断依据:#142 只交付 DeleteReminderRule 的规则关闭、未来 trigger 影响计数、历史事实保留及原子失败;parent #137 明确排除 Runner/投递、SQLite/NVS Adapter 和完整 trigger 查询。本回复按该边界逐条处理。必要修复已在 12bd285 完成。

  1. 空白/控制字符 ID:不采纳。[Timing] 实现 DeleteReminderRule 删除提醒规则 #142 只规定空标识为参数错误,当前稳定 ID 格式没有统一 trim 语义;扩大输入规范应由独立契约 Issue 决定。
  2. Store 错误追加 rule ID:不采纳。Service 的职责是保留结构化错误码;错误消息上下文格式及日志策略不在 [Timing] 实现 DeleteReminderRule 删除提醒规则 #142 范围。
  3. DTO 固定 kDisabled:不采纳。DeleteReminderRuleResult 已将关闭结果定义为 kDisabled,引入 soft-delete 状态会改变既有公开合同。
  4. 新 pure virtual 的迁移窗口:不采纳。parent [Timing] 完善定时任务模块核心业务能力 #137 明确允许按需扩展 Store Port;仓库全部实现者/测试替身已同步,生产 Adapter 本来就在范围外。
  5. 返回取消 trigger 列表:不采纳。[Timing] 实现 DeleteReminderRule 删除提醒规则 #142 验收只要求受影响数量;审计列表属于后续 ListReminderTriggers/审计模型设计。
  6. 用共享 helper 固化时间边界:不采纳。Port 文档已经是 adapter 共同合同;当前没有第二个 Adapter,抽取实现 helper 属于提前设计。
  7. 按 rule ID 的故障注入:不采纳。fake 仅服务单线程 Host 测试;并发/队列语义是 Runner 或生产 Adapter 范围。现有注入仍精确覆盖一次调用。
  8. trigger 与 rule 的 task 关联:采纳并修复。12bd285 在候选事务中验证两者 task 一致,不一致返回 kConflict 且不提交任何变更。
  9. 复制完整 map 的复杂度:不采纳。内存 fake 用 copy-on-write 明确表达原子性,不是生产存储实现;优化会削弱该测试模型的可读性。
  10. 取消 overdue pending trigger:不采纳。[Timing] 实现 DeleteReminderRule 删除提醒规则 #142 规定的是未来 trigger,Port 合同明确为 planned_trigger_at >= now;将 overdue pending 视为可取消会改变历史事实边界,需由 Runner/投递规则另行定义。
  11. cancelled trigger 写 deleted_at:不采纳。取消是状态转换而不是物理/软删除;写 deleted_at 会混淆终态与删除语义。
  12. 公开 fake helper:不采纳。它们是 tests/host/support 的受控 fixture,用于构造验收要求的历史、未来和关联异常事实,不是生产 API。
  13. FindReminderTrigger 按值返回:不采纳。测试对象很小且返回值避免悬垂引用;payload 扩容的性能优化不属于本切片。
  14. ListRules 排序依赖:采纳并修复。测试现在按 weak/strong 类型选择规则,不再依赖 front()/位置。
  15. 硬编码时间与 now 边界:采纳并修复。测试改用 clock.Now() 派生过去/未来值,并覆盖 planned_trigger_at == now 会被取消。
  16. 终态元数据断言:不采纳。本 Issue 的验收关注状态、历史保留和受影响计数;updated_at/deleted_at 的查询合同尚未定义,单独固定会越过 DTO/查询边界。
  17. 冲突路径后回读:部分采纳。12bd285 对跨任务关联错误回读并确认规则仍 active;重复删除和孤儿规则没有触发候选写入,现有错误分类已满足 [Timing] 实现 DeleteReminderRule 删除提醒规则 #142
  18. 只在调用前注入失败:采纳并修复。故障注入已移动到候选规则/trigger 变更构造之后、提交之前;测试同时断言规则 active 和 future trigger pending。
  19. orphan 注入为 backdoor:不采纳。[Timing] 实现 DeleteReminderRule 删除提醒规则 #142 明确要求关联关系错误;fixture 的显式 orphan 注入是验证该错误语义的最小手段,不作为正常生产路径。
  20. 缺少真实 Adapter/端到端测试:不采纳。[Timing] 实现 DeleteReminderRule 删除提醒规则 #142 与 parent [Timing] 完善定时任务模块核心业务能力 #137 均明确排除 SQLite/NVS Adapter、Runner 和实际投递;Host fake + Port 合同正是本子 Issue 指定验证层。

Codecov 的 90% patch 报告已记录;其必需的覆盖率检查通过。本次补充的边界与原子失败测试覆盖了 review 中属于 #142 的新增行为。

@HuXiaohui424 HuXiaohui424 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@fennoai 你是资深后端/全栈架构师 + 嵌入式硬件工程师复合型专家,执行严格、客观、不留情面的代码仓库Review,请遵循下面所有评审规则,逐条输出审查结果,禁止敷衍、禁止只说空话、禁止笼统概括。
硬性约束:最终输出评审条目数量不少于20条,若直观可见问题不足,主动挖掘隐性架构隐患、软硬件兼容风险、长期运行潜在缺陷补足条目,不得简化评审内容。

评审维度

  1. 架构与模块设计
  • 模块职责是否清晰,是否存在循环依赖、职责混杂
  • 分层是否合理,是否违反单一职责原则、开闭原则
  • 接口抽象、依赖注入设计是否规范,有无硬编码耦合
  • 软件与硬件模块边界是否清晰,硬件相关逻辑是否侵入业务层
  1. 代码规范与可读性
  • 命名:变量、函数、类、文件命名是否语义清晰,禁止模糊命名、拼音命名
  • 注释:复杂逻辑、硬件时序、特殊寄存器配置必须注释;冗余注释、无效注释、过期注释需要指出
  • 代码格式、风格是否统一,是否存在大量魔法数字、魔法字符串,硬件参数无常量定义
  1. 性能隐患
  • 循环内IO、数据库重复查询、不必要的内存占用、低效算法
  • 资源是否释放(连接、句柄、定时器、文件流、硬件外设句柄)
  • 嵌入式场景:阻塞轮询、中断处理耗时过长、内存频繁分配释放
  1. 安全性检查【重点】
  • 输入校验、SQL注入、XSS、权限控制、敏感信息明文打印
  • 密钥、token、数据库地址、硬件访问口令是否硬编码提交到仓库
  • 外部指令下发至硬件驱动缺少权限校验,存在设备失控风险
  1. 健壮性 & 异常处理
  • 是否缺少异常捕获、错误分支处理
  • 参数判空、边界条件、失败重试逻辑是否完备
  • 硬件通讯异常(I2C/SPI/UART断线、设备无应答)缺少容错、恢复逻辑
  • 缺少硬件故障状态上报、故障隔离机制
  1. 硬件驱动 & 软硬件协同评审(新增专项)
  • 驱动代码与硬件原理图引脚定义是否匹配,无硬件版本兼容逻辑
  • 外设操作缺少电平保护、超时判断,存在烧毁外设芯片风险
  • 运动控制逻辑(如有)缺少软限位、急停、碰撞检测保护
  • 上下位机通讯协议:缺少校验和、重传、断线重连机制
  • 硬件参数(电流、电压、速度阈值)直接写死代码,无统一配置入口
  1. 测试覆盖
  • 是否缺少单元测试、边界用例
  • 核心业务逻辑、硬件驱动逻辑无测试直接合并风险
  • 缺少硬件异常工况、极限压力场景测试用例
  1. 工程规范
  • 无用代码、注释掉的废弃代码是否大量遗留
  • 依赖包版本风险、冗余依赖、存在已知漏洞依赖
  • 配置区分环境(开发/样机/生产),环境配置是否混写
  • 固件、上位机、3D模型、原理图版本号不统一,无关联管理
  1. 可维护性 & 扩展性
  • 新增需求是否需要大面积修改原有代码
  • 是否存在大量重复代码,缺少公共封装
  • 硬件型号迭代时,软件需要大规模改动,缺少适配抽象层

输出要求

  1. 问题分级:🔴严重阻塞(必须修改才能合并/上机测试) / 🟡建议优化(不阻塞但强烈建议重构) / 🟢可选改进
  2. 每条问题固定格式:【文件路径+代码位置】问题描述 + 风险说明 + 可直接落地的修改方案
  3. 条目数量强制≥20条;问题较少时区分表层问题与隐性长期风险,拆分为独立条目逐条列出,禁止多条问题合并为一条
  4. 最后汇总三部分:
    ① 整体仓库风险总结
    ② 优先级整改清单(阻断项优先)
    ③ 长期架构、软硬件协同优化方案
  5. 如果存在架构层面缺陷,单独提炼顶层设计问题,不要只局限单行代码
  6. 不要美化结论,发现隐患直接指出,客观评估风险等级;不使用“建议考虑”这类温和模糊表述,明确写出故障后果(程序崩溃、硬件烧毁、机构失控、数据泄露等)

现在开始对提供的仓库代码/PR变更、硬件资料、结构模型进行评审

@fennoai

fennoai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review 结果

🔴 【tests/host/support/timing_fakes.h:101-105】只取消 planned_trigger_at >= now 的 pending trigger,planned_trigger_at < now 但仍未投递的记录会漏掉。风险:删规则后旧提醒仍可能继续执行,直接破坏“删除即停止未来投递”的语义。修改:删除时取消所有同规则且仍处于 pending 的 trigger,或把边界改成“未终态且未执行”。

🟡 【tests/host/support/timing_fakes.h:83-85】只要 rule 对应的 task 缺失,删除就直接返回冲突。风险:迁移、修复或脏数据回放后,规则会变成不可清理的死数据。修改:允许先关闭规则并记录 orphan 状态,或者把缺失 task 视为可回收异常而不是硬阻断。

🟡 【tests/host/support/timing_fakes.h:98-100】任何 task_id 不一致的旧 trigger 都会让整条规则删除失败。风险:单条历史脏 trigger 就能把正常规则锁死,cleanup 路径失效。修改:在事务内隔离这类不一致记录,并返回可诊断的完整错误集,不要让删除被单条脏数据完全阻断。

🟢 【tests/host/support/timing_fakes.h:102-105】取消 trigger 时没有写 deleted_at。风险:后续按终态时间做回放、审计或查询时,会把已取消记录当成未终结。修改:同步写入 deleted_at = now,或者把字段语义明确成“物理删除时间”。

🟡 【components/voicelife_timing/include/voicelife/timing/timing_task_store.h:43-48】契约把“未来触发”直接绑定到 wall clock 的 now,但没有定义时钟回拨/跳变的处理。风险:嵌入式设备时间漂移后,同一批 trigger 会在不同调用里被不同地保留/取消。修改:改用单调时钟,或在契约里明确回拨时的判定规则。

🟢 【components/voicelife_timing/include/voicelife/timing/timing_task_store.h:43-48DisableReminderRule 只返回受影响数量,不返回被取消 trigger 的身份。风险:调用方无法审计副作用,也无法做精确回滚/告警。修改:把返回值升级为包含取消摘要的结构体。

🟡 【tests/host/support/timing_fakes.h:87-115】删除时先复制整张 rules_triggers_,再遍历整张 trigger 表。风险:数据量上来后,删除路径变成 O(n) 拷贝 + O(n) 扫描,内存和 CPU 都翻倍。修改:只对命中 rule 的 trigger 做局部变更,或者在 fake 里保留事务日志而不是整表 staging。

🟡 【tests/host/support/timing_fakes.h:108-111】故障注入检查放在整轮复制/扫描之后。风险:即使是纯错误路径,也要付出完整的遍历成本,掩盖真实故障代价并拖慢大数据测试。修改:先短路失败注入,再做状态复制和扫描。

🟢 【tests/host/support/timing_fakes.h:118-123FailNextRuleDisable 是全局单次状态,没有按 rule 或场景隔离。风险:后续并发或复用用例里,错误可能被错消费。修改:把失败注入做成队列或按 rule id 绑定。

🟢 【tests/host/support/timing_fakes.h:120-123AddReminderRule / AddReminderTrigger 公开暴露任意写入后门。风险:测试会持续构造生产环境不可能出现的脏状态,掩盖真实 adapter 的约束缺陷。修改:收窄为私有工厂,或明确标记为 corruption fixture。

🟢 【tests/host/support/timing_fakes.h:126-131FindReminderTrigger 按值返回整条 trigger。风险:payload 变大后测试辅助会产生不必要拷贝。修改:返回 const 引用、指针,或只返回断言所需的字段。

🟡 【tests/host/timing_task_service_test.cc:291-302】默认规则选择依赖遍历结果和“只有弱/强两条规则”的隐含前提。风险:一旦默认规则数量或类型增加,测试会悄悄变脆。修改:按显式 id 选择目标规则,别依赖迭代顺序和当前恰好两个默认项。

🟡 【tests/host/timing_task_service_test.cc:303-324】只插了 future-triggerhistorical-triggerat-delete-trigger,但没有单独断言 at-delete-trigger 的终态。风险:边界语义回归时,测试会漏掉最关键的 now 边界。修改:补一条对 at-delete-trigger 的状态检查。

🟢 【tests/host/timing_task_service_test.cc:325-333】只断言 affected_trigger_count == 2,没有把它和具体被取消的 trigger ID 绑定。风险:如果实现误取消了错误对象但数量相同,测试仍会过。修改:对每个被取消的 trigger 单独回读并断言状态。

🟢 【tests/host/timing_task_service_test.cc:330-337】历史 trigger 只断言状态,没有断言 deleted_atlast_action_at 等终态元数据。风险:状态对了,审计字段仍可能回归。修改:把终态字段一起断言。

🟢 【tests/host/timing_task_service_test.cc:338-343】重复删除和缺失 ID 只检查返回码。风险:如果第二次调用污染了 store 状态,测试完全看不出来。修改:重复删除后再回读 rule/trigger,确认状态没有被二次修改。

🟡 【tests/host/timing_task_service_test.cc:360-373】失败路径只检查一个 trigger 和一个 rule,没有做全量回读。风险:局部状态可能正确,其他关联记录已经被误改。修改:在失败后比较整组 rule/trigger 快照,确认完全回滚。

🟢 【tests/host/timing_task_service_test.cc:374-380】脏数据注入通过公共 helper 直接完成。风险:后续会把“可注入脏数据”误当成正常路径,持续掩盖约束问题。修改:把这类注入集中到专用 corruption fixture,并只在相关测试里启用。

🟢 【components/voicelife_timing/CMakeLists.txt:2-7】新增翻译单元靠手工写进组件 CMake。风险:后续 timing 模块再拆文件时,很容易漏编译某个实现。修改:把 timing 源文件清单集中管理,避免分散手写。

🟢 【tests/host/CMakeLists.txt:50-55】host 测试也手工复制了一份 timing 源文件列表。风险:组件 CMake 和 host CMake 会持续漂移,某个目标缺文件时只在部分构建里炸。修改:抽一份共享源列表,两个构建目标复用同一份定义。

🟢 【components/voicelife_timing/src/timing_task_service_delete_reminder_rule.cc:15-18】服务层输出固定 kDisabled,没有把 store 真实终态映射出来。风险:未来如果 store 区分 soft-delete、disabled、reconciled 等状态,这层会直接吞掉差异。修改:把终态状态从 store 结果里显式映射,而不是在 service 里硬编码。

整体仓库风险总结

这版功能线是闭合的,timing_task_service_test 已通过,git diff --check 也干净。真正需要优先修的是删除边界和脏数据清理语义:现在最容易出事故的不是“删不掉”,而是“删了以后仍有遗漏的 pending trigger 继续活着”。

优先级整改清单

  1. 先修 overdue pending trigger 的取消边界。
  2. 再处理 orphan rule 和脏 trigger 不能阻断 cleanup 的问题。
  3. 补全 exact-now、metadata、全量回滚断言。
  4. 最后收紧 fake store 和 CMake 的重复维护点。

长期架构、软硬件协同优化方案

把“规则关闭 + 触发取消”的一致性规则提到共享领域层,统一定义时钟边界、终态字段和回滚语义,不要让 fake、service、真实 adapter 各写各的。测试夹具也要分成“正常数据构造器”和“显式脏数据注入器”,否则后续持久化适配器一接入,回归会继续藏在测试假象里。

已验证:cmake -S /workspace/VoiceLife/tests/host -B /tmp/voicelife-host-build-169cmake --build /tmp/voicelife-host-build-169 -j2 --target timing_task_service_test/tmp/voicelife-host-build-169/timing_task_service_test

…nder-rule-main

# Conflicts:
#	components/voicelife_timing/CMakeLists.txt
#	components/voicelife_timing/include/voicelife/timing/timing_task_store.h
#	components/voicelife_timing/src/timing_task_service_stubs.cc
#	tests/host/CMakeLists.txt
#	tests/host/support/timing_fakes.h
#	tests/host/timing_task_service_test.cc
@jing-gou
jing-gou merged commit fa5800c into 1024XEngineer:main Aug 6, 2026
13 checks passed
@jing-gou jing-gou linked an issue Aug 7, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Timing] 实现 DeleteReminderRule 删除提醒规则

2 participants