Skip to content

✨ feat(timing): 支持提醒规则创建与更新 - #161

Merged
jing-gou merged 4 commits into
1024XEngineer:mainfrom
jing-gou:dev/141-upsert-reminder-rules
Aug 6, 2026
Merged

✨ feat(timing): 支持提醒规则创建与更新#161
jing-gou merged 4 commits into
1024XEngineer:mainfrom
jing-gou:dev/141-upsert-reminder-rules

Conversation

@jing-gou

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

Copy link
Copy Markdown
Collaborator

结论

完成 Issue #141UpsertReminderRules 已支持原子创建与更新提醒规则。请重点评审批量 Store Port 的原子语义、规则冲突边界和 snooze 约束。

背景

现有 TimingTask Service 仅为该接口返回 kUnavailable,无法安全地管理用户定义的提醒规则。

改动范围

  • 新增 TimingTaskStorePort::UpsertRules,承诺同任务规则批量全成或全败,且不修改已物化的 trigger。
  • 实现 DefaultTimingTaskService::UpsertReminderRules:创建分配 ID,更新保留 ID,返回完整规则列表。
  • 校验 weak 禁止 snooze、strong 的次数与间隔为正数、提醒不得晚于任务开始、准点 active strong 唯一、日程归属及重复规则 ID。
  • 内存 fake 支持原子更新和一次性读写故障注入;补充主机测试。
  • 未实现 ReminderTrigger 实际触发、Runner、消息投递、SQLite/NVS 生产 Adapter,也未引入新的 Runtime 依赖。

接口与依赖影响

  • TimingTaskStorePort 增加纯虚 UpsertRules;现有内存实现和测试替身已同步实现。
  • DTO、Profile、协议、持久化格式和组件依赖方向均未改变。

TDD 记录

  • RED:timing_task_service_test 中的合法 weak/strong upsert 用例在 stub 的 kUnavailable 上失败。
  • GREEN:最小实现通过规则批量 Port 原子写入并返回候选完整规则集。
  • REFACTOR:将实现放入独立翻译单元,保持剩余未实现 Service 方法仍在 stub 边界内。

测试与构建证据

  • ./scripts/run_host_tests.sh -R timing_task_service_test
  • ./scripts/run_checks.sh:14 个 Host 测试、公共 API 文档、架构、Profile、Python 检查通过;idf.py build 通过。
  • Node 24 + pnpm 10.17.1:pnpm --dir services/im-gateway run ci 通过。
  • 完整提交前门禁在 Node 24、Ruff 0.12.7、clang-format 18 下通过 C++、Python、Host、架构、Profile 与 Gateway 阶段。本机无 GNU GCC,gcov 覆盖率报告由 CI 确认。

已知风险

Issue 未定义 strong snooze 的业务数值上限,因此本提交只拒绝非正次数和间隔;具体最大值应通过独立规格收紧,避免在实现中擅自固化产品策略。

兼容窗口与回退

新 Port 尚无生产 Adapter,实现当前只影响内存 fake 和 Host 测试。若需回退,直接回退本 PR 即可恢复原 stub 行为,无数据迁移或兼容窗口。

Closes #141

为提醒规则管理提供原子批量写入,避免逐条保存造成部分更新。

服务校验 weak/strong snooze、日程归属和准点 strong 唯一性;内存 fake 与主机测试覆盖成功、冲突和写入失败路径。

Host、架构、Profile、Python 检查及 Node 24 Gateway CI 已通过;本机缺少 GNU GCC,gcov 覆盖率由 CI 确认。

Closes 1024XEngineer#141
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.00000% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...e_timing/src/timing_task_service_reminder_rules.cc 85.00% 2 Missing and 10 partials ⚠️

📢 Thoughts on this report? Let us know!

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

I found one issue that can break the rule uniqueness guarantee under concurrent upserts.

Verified locally with cmake -S tests/host -B /tmp/voicelife-host-tests-pr161 && cmake --build /tmp/voicelife-host-tests-pr161 && ctest --test-dir /tmp/voicelife-host-tests-pr161 --output-on-failure.

@jing-gou

jing-gou commented Aug 5, 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 commented Aug 5, 2026

Copy link
Copy Markdown

严格 Review 结果

结论:不建议直接合并到可上机/可接生产 Adapter 的分支。当前 host 用例通过,但 Upsert 语义仍有并发原子性、幂等、资源上限和生产持久化契约缺口。PR 未包含硬件驱动、原理图或结构模型变更,因此硬件专项只能评估 timing 规则对下游语音/硬件执行链路的风险,不能验证引脚、电平或结构干涉。

  1. 🔴严重阻塞【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:52】先 ListRules 再本地校验,再调用 UpsertRules,准点 strong 唯一性不是同一个 Store 原子事务内完成。并发两个请求都能读到旧快照并各自写入一条 offset_minutes == 0 strong,后果是同一任务触发多次强提醒,语音/执行链路重复动作。修改方案:把完整候选规则集或条件约束传给 Store,在 SQLite 事务内加唯一约束/条件索引并用 BEGIN IMMEDIATE 或版本 CAS 保证校验与写入不可分割。
  2. 🔴严重阻塞【components/voicelife_timing/include/voicelife/timing/timing_task_store.h:43】UpsertRules 接口只声明“全成或全败”,没有版本号、expected revision、幂等键或条件写入字段,无法表达防丢更新语义。两个客户端同时改同一批规则时后写覆盖先写,用户以为保存成功但提醒策略被静默丢失。修改方案:给任务/规则集增加 rules_revisionupdated_at 条件,UpsertRules(task_id, expected_revision, changes) 不匹配返回 kConflict
  3. 🔴严重阻塞【components/voicelife_timing/include/voicelife/timing/timing_task_contracts.h:99】UpsertReminderRulesCommand 没有 request_id,创建规则时由服务端生成 ID。客户端超时重试无法区分“写入成功但响应丢失”和“未写入”,会重复创建提醒规则,最终导致重复唤醒/重复语音播报。修改方案:新增 upsert 幂等键,Store 记录 request_id -> result;或允许创建时提交客户端生成的稳定 rule id。
  4. 🔴严重阻塞【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:13】max_snooze_countsnooze_interval_minutes 只校验正数,没有业务上限。恶意或错误输入可提交极大 snooze 次数/间隔,后续 Runner 计算时可能出现时间溢出、队列膨胀或长期占用 flash/内存。修改方案:在 TimingPolicy 中定义上限,例如最大 snooze 次数、最大间隔分钟数,并补齐拒绝边界测试。
  5. 🔴严重阻塞【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:14】offset_minutes 只拒绝大于 0,未限制负数下界。极大负偏移会让计划触发时间落到 Unix 早期或在 start_at + offset * 60 中溢出,导致补偿扫描风暴或错误触发。修改方案:定义最大提前窗口,使用安全乘加检查 offset_minutes * 60,越界返回 kInvalidArgument
  6. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:14】channelsource 只校验非空,任意字符串都能进入领域对象。下游若把 channel 映射到语音、IM 或硬件执行通道,错误值会造成路由失败,未授权值还可能绕过预期的通道控制。修改方案:将 channel/source 改为枚举或集中 allowlist,并在服务层拒绝未知值。
  7. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:37】单次请求 rules 没有数量上限。嵌入式环境上 unordered_mapunordered_setvector 会按请求规模分配内存,超大批量可触发堆耗尽、任务重启或 watchdog。修改方案:定义 kMaxRulesPerTaskkMaxRulesPerUpsert,先验校验后再分配容器。
  8. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:57】对 ListRules 返回的数据没有校验 rule.task_id == command.task_id。一旦生产 Adapter 查询条件写错或数据库损坏,跨任务规则会进入候选集,可能被错误返回或参与冲突判断。修改方案:遍历 stored_rules 时检测 task_id,不匹配直接返回 kInternal 并记录存储一致性错误。
  9. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:59】rules_by_id.emplace 会静默忽略重复规则 ID,隐藏 Store 数据损坏。结果是服务继续基于不完整规则集写入,进一步扩大持久化污染。修改方案:检查 emplace(...).second,重复 ID 返回 kInternal/数据一致性错误,并阻止写入。
  10. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:84】更新已禁用规则时会保留 kDisabled 状态但返回成功。调用方可能以为规则已重新生效,实际不会派生未来提醒,造成漏提醒。修改方案:禁止更新 disabled 规则并返回 kConflict,或在命令中显式加入恢复/启用动作,不能隐式成功。
  11. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:77】服务在完成全批校验前调用 NextReminderRuleId(),失败请求会消耗 ID。若 ID 生成器依赖 NVS/flash 计数器,非法请求可造成写放大和 ID 快速耗尽。修改方案:先完成所有不依赖 ID 的校验,再批量申请 ID;flash 型生成器必须缓存或事务化。
  12. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:92】规则更新允许任意修改 type,弱提醒可直接变强提醒。若上层未做权限控制,普通编辑入口可制造强交互提醒并触发语音打断。修改方案:把“类型变更”拆成显式命令或加入权限/来源校验;至少对系统默认 strong 规则的降级/升级设独立策略。
  13. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:96】channel 字符串未做长度限制。超长字符串会增加 SQLite 写入、序列化和消息投递负载,在 2 MiB 数据分区设备上会放大 flash 磨损和存储失败风险。修改方案:定义字段最大长度并在服务层截断禁止,测试覆盖边界值。
  14. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:97】source 字符串未做长度和枚举限制。错误 source 会污染审计语义,后续无法可靠区分 system/user/IM 导入规则,故障追踪会失真。修改方案:使用枚举或固定字符串常量,并限制长度。
  15. 🟡建议优化【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:118】成功返回的 result_rules 是写入前本地构造结果,不是 Store 实际持久化后结果。生产 Adapter 若补齐字段、归一化字符串或触发数据库默认值,API 返回值会与持久化状态不一致。修改方案:UpsertRules 返回持久化后的完整规则集,或保存成功后重新 ListRules
  16. 🟡建议优化【components/voicelife_timing/include/voicelife/timing/timing_task_store.h:45】接口承诺“不修改已物化的提醒触发”,但没有定义哪些未来 trigger 已物化、规则更新后如何使未来 trigger 失效或重建。结果是用户修改规则后,预生成提醒仍按旧规则触发,出现漏改或重复提醒。修改方案:定义 materialization horizon,新增 InvalidateFutureTriggers(rule_ids, from) 或让 Store 在同一事务内维护规则与未来 trigger 的一致性。
  17. 🟡建议优化【components/voicelife_timing/include/voicelife/timing/timing_task_store.h:48】Store Port 只返回 Status,无法区分“创建数、更新数、版本号、冲突字段”。上层无法给调用方准确反馈,排障时只能看到粗粒度错误。修改方案:改为 Result<UpsertRulesWriteResult>,包含 revision、created_ids、updated_ids 和冲突原因。
  18. 🟡建议优化【tests/host/support/timing_fakes.h:80】内存 fake 没有锁,也不模拟并发冲突。当前最危险的重复准点 strong 和丢更新问题不会在测试中暴露。修改方案:新增专用 Store fake,在 ListRulesUpsertRules 之间注入并发写入,验证服务/Store 合同必须拒绝陈旧写。
  19. 🟡建议优化【tests/host/support/timing_fakes.h:75】fake 的 ListRules 排序只按 offset_minutes,服务结果排序按 offset_minutes + id。测试若直接依赖 fake 返回顺序,会产生不稳定行为,掩盖生产 Adapter 排序差异。修改方案:统一排序规则或在测试中显式按稳定键排序后断言。
  20. 🟡建议优化【tests/host/timing_task_service_test.cc:297】创建成功用例只断言 size 和 ID,不断言 created_atupdated_attask_idsourcestatus、strong snooze 字段全部落库。字段映射写错仍可能通过测试。修改方案:对新增 weak/strong 规则逐字段断言。
  21. 🟡建议优化【tests/host/timing_task_service_test.cc:332】更新用例只确认 offset 改变,没有断言 created_at 保持不变、updated_at 更新、ID 不变、状态不被错误重置。生产中审计时间线会被破坏而测试不报警。修改方案:在更新前保存完整规则快照,更新后逐字段比对。
  22. 🟡建议优化【tests/host/timing_task_service_test.cc:435】只测“新增第二条准点 strong 被拒绝”,未测“把已有非准点 strong 更新为准点”与“把默认准点 strong 改成非准点后再新增准点”的边界。唯一性规则很容易在更新路径漏掉。修改方案:补齐这两类更新路径测试。
  23. 🟡建议优化【tests/host/timing_task_service_test.cc:453】未知任务只测 FindTask not found,未测 terminated task。服务有 task.value->status != kActive 分支,但无回归保护,后续重构可能允许已终止任务继续改规则。修改方案:构造 terminated task 并断言 upsert 返回 kConflict 且不写入。
  24. 🟡建议优化【tests/host/timing_task_service_test.cc:389】输入校验测试缺少未知 enum、空 source、空 channel、超长 channel/source、负 snooze、极大 offset 的组合。非法值会直接影响下游调度和消息路由。修改方案:把 ValidateReminderRuleInput 边界做成表驱动测试。
  25. 🟢可选改进【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:11】IsKnownReminderType 对 enum 扩展是硬编码白名单,后续新增提醒类型必须改服务实现,违反开闭原则。修改方案:把类型能力放进策略表,例如 ReminderTypePolicy{allows_snooze, default_limits},新增类型只扩展策略。
  26. 🟢可选改进【components/voicelife_timing/src/timing_task_service_reminder_rules.cc:26】排序逻辑只在该翻译单元私有存在,Store fake 又有另一套排序。长期会出现 API 返回顺序不一致。修改方案:提取 CompareReminderRuleForDisplay 到 timing 公共内部 helper,服务和测试替身复用。
  27. 🟢可选改进【components/voicelife_timing/CMakeLists.txt:2】新增实现文件已加入 component,但没有按功能拆测试 target;timing_task_service_test 继续膨胀成单个大 main。后续失败定位困难。修改方案:新增 timing_task_reminder_rules_test,只覆盖 UpsertReminderRules。
  28. 🟢可选改进【docs/engineering/board-storage-validation.md:3】仓库文档明确板上存储基线是 SQLite + FATFS/Wear Levelling,但本 PR 没有给新增 UpsertRules 定义 SQLite 事务/索引草案。等生产 Adapter 补实现时容易各自解释原子语义。修改方案:补一份 Store Adapter 合同文档或 ADR,明确事务隔离级别、唯一约束、索引和断电恢复验证。

验证

  • 已按要求运行 fennoai-task-setup --workspace-only,并锁定固定范围 afc8e095977ef2e9d860b29885d77145296d3244...d7231779c9a8da0f2825238969ae6c79794d54e6
  • 已运行 ./scripts/run_host_tests.sh -R timing_task_service_test,结果通过:1/1 test passed。
  • 未运行实板测试、硬件引脚/电平验证或结构模型检查;PR 上下文未提供硬件资料/结构模型变更,无法客观验证这些专项。

汇总

① 整体仓库风险总结:本 PR 的业务主路径能在 host fake 下工作,但把“规则全局约束”和“并发一致性”留在服务层读写窗口中,进入生产 SQLite/NVS Adapter 后会出现重复强提醒、丢更新、重试重复创建和资源耗尽风险。硬件相关直接代码未改,但提醒系统会驱动语音/设备动作,重复或错误触发会向硬件执行链路放大。

② 优先级整改清单:先修 Store 原子事务/CAS、upsert 幂等键、snooze/offset/batch/string 上限;再补 channel/source allowlist、disabled rule 行为、持久化后返回值一致性;最后拆分测试并补齐并发、边界、字段映射和 terminated task 用例。

③ 长期架构与软硬件协同优化方案:把 TimingPolicy 扩展为统一规则策略中心;为 Store Port 定义生产级事务协议和 SQLite 索引;为 Runner/Trigger materialization 定义规则变更后的失效重建机制;在硬件执行入口前增加通道授权和去重保护,保证异常规则不会造成重复语音打断、设备误动作或 flash 写入风暴。

View job run

服务层基于旧快照校验后写入时,多个并发请求可能绕过准点强提醒唯一性。

Store Port 现在要求在同一原子写入边界复核该不变量;内存 fake 和 Store 合同测试覆盖第二个 writer 被拒绝的场景。

./scripts/run_checks.sh 已通过。

Refs 1024XEngineer#141

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

1

@jing-gou

jing-gou commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Review 复核结论(以 Issue 141 / 父 Issue 137 为准)

我按关联 Issue 141 的验收与范围外,以及父 Issue 137 的模块边界逐项复核。本 PR 只交付 UpsertReminderRules 的一条可独立验收链路;不把未定义的产品策略或生产 Adapter 工作混入该切片。

  • 问题 1 已采纳并修复:Issue 141 明确要求每 task 至多一条 active 准点 strong;父 Issue 137 要求 Store Port 承担原子性。580d416 将复核放入 TimingTaskStorePort::UpsertRules 原子边界,内存 fake 与 Store Port 回归测试验证第二个 writer 得到 kConflict。SQLite 事务实现仍属明确范围外,但未来 Adapter 必须遵守该 Port 合同。
  • 问题 2 不采纳(后续规格):revision/CAS 是新的调用方并发协议;Issue 141 未定义 revision,父 Issue 137 的“按需扩展”不足以凭空确定冲突语义。
  • 问题 3 不采纳(后续规格):upsert request_id 与持久化重放记录会改变 DTO/Port;Issue 141 没有该验收。为创建请求定义幂等语义后再单独切片。
  • 问题 4 不采纳(规格缺口):Issue 141 只要求 strong 的次数与间隔满足范围约束,现实现为正数;数值上限是产品决定,不能在实现中擅定。
  • 问题 5 不采纳(后续时间策略):提前窗口、溢出计算与 Runner 扫描在 Issue 141 未定义,且实际 trigger/Runner 明确范围外。
  • 问题 6 不采纳(后续通道策略):channel/source 枚举或 allowlist 没有被 Issue 141 或父 Issue 137 定义。
  • 问题 7 不采纳(后续资源策略):每任务/每次写入上限未被规格给出;应由嵌入式资源预算驱动独立 Issue。
  • 问题 8、问题 9 不采纳(生产 Adapter 防御):跨 task 返回和持久化数据损坏属于未来 Adapter 的一致性/诊断策略;本切片没有生产存储实现。
  • 问题 10 不采纳:输入没有“启用/禁用”动作;更新保留既有 disabled 状态,避免把普通字段更新隐式变成恢复操作。
  • 问题 11 不采纳(ID 分配器策略):ID 持久化、写放大与事务化不在 Issue 141 的 ID 合同内。
  • 问题 12、问题 13、问题 14 不采纳(后续授权与字段策略):类型升级权限、channel/source 值域与长度限制均未被两个 Issue 定义,不能由本 PR 反推。
  • 问题 15 不采纳(当前不适用):内存 Store 持久化值与写入值一致;生产 Adapter 的默认值/归一化行为属范围外。
  • 问题 16 不采纳(且不能在此实现):Issue 141 明确排除 trigger 实际触发,Port 也承诺不修改已物化 trigger;未来 trigger 的物化窗口与失效机制应在 trigger 子 Issue 定义。
  • 问题 17 不采纳(后续 API 设计):丰富 Store 写入结果不是 Issue 141 返回 UpsertReminderRulesResult 所必需的公开合同。
  • 问题 18 不采纳为阻塞项:该 fake 已在一次 UpsertRules 调用内构造候选状态、复核不变量并一次提交,覆盖 Issue 141 所需的 Store 行为;Port 未声明多线程调用模型。若后续需要让 host fake 本身成为并发压力工具,应连同线程安全合同和确定性同步 fixture 另立测试切片,避免用不稳定竞态测试扩大本 PR。
  • 问题 19 不采纳:服务返回结果已以 offset_minutes + id 做稳定排序;fake 的内部读取顺序不是 Issue 141 的公开结果合同。
  • 问题 20、问题 21 不采纳(可选测试加强):当前成功链路覆盖创建/更新的关键可观察结果;审计字段逐项断言可在后续补充,非缺陷证据。
  • 问题 22 不采纳(已有覆盖):Store Port 测试已覆盖第二条准点 strong 被拒绝;服务层在候选完整规则集上同样复核。
  • 问题 23 不采纳(已有覆盖):服务已检查非 active task 并返回 kConflict;额外回归用例可以后续补充,但不是当前行为遗漏。
  • 问题 24 不采纳(与问题 4 至问题 7、问题 13、问题 14 相同):未知 enum、空字段已校验;其余边界需先有产品值域。
  • 问题 25、问题 26、问题 27 不采纳(可选重构):策略表、排序复用与函数拆分均不改变 Issue 141 的可观察验收;当前实现保持最小切片。函数规模已记录为非阻塞 review 关注点。
  • 问题 28 不采纳(明确范围外):SQLite/NVS Adapter 与其事务文档被 Issue 141 和父 Issue 137 明确排除;新增 Adapter 时必须落实上述 Store 原子合同。

补充:Codecov 报告的 patch coverage 为 85%,高于仓库 80% 门槛,CI 的 coverage gate 已通过;该报告不构成阻塞。另一条仅含 1 的人工 review 没有可评估的技术诉求。

因此,本轮没有发现 580d416 之后仍需在 Issue 141 内修复的阻塞项。后续工作应以独立 Issue 记录:调用方并发/重试协议、产品数值/资源上限、授权与 channel/source 词汇表、以及生产 SQLite/NVS Adapter。

合入 upstream/main 后,保留 UpsertReminderRules 的验收测试,并保留其余未实现服务接口的 unavailable 回归断言。\n\n完整提交前门禁已通过。\n\nRefs 1024XEngineer#141
主线已包含 UpdateTimerTask 和 CancelTimerTask,本次合并保留两者并恢复 UpsertRules Port、内存 fake、提醒规则服务测试和 Store 原子性测试。\n\n完整提交前门禁已通过。\n\nRefs 1024XEngineer#141
@jing-gou
jing-gou merged commit 1f11137 into 1024XEngineer:main Aug 6, 2026
13 checks passed
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] 实现 UpsertReminderRules 提醒规则管理

3 participants