✨ feat(schedule): 实现MCP 工具定义编排以及日程模块的接口定义与实现 - #259
Conversation
将整数范围和字符串长度约束合并到同一个 Property 构造函数,根据参数类型分别生成 minimum/maximum 或 minLength/maxLength。删除 WithStringLength 接口并迁移相关测试,补充默认值、非法类型、负长度和跨平台边界校验。 clang-format-18、ruff==0.12.7 和 52 项主机测试已通过。 BREAKING CHANGE: 删除 Property::WithStringLength,调用方应改用 Property(name, PropertyType::kString, minimum, maximum[, default_value])。
- 新增 ScheduleOperationRepository 接口与操作记录/撤销的 SQLite 实现、迁移与行映射 - StorageBootstrap 暴露日程与操作仓储,Runtime 装配两仓储到 ScheduleService - 修复 MCP 工具调用时 worker 任务 SQLite 栈溢出(12KB→32KB) - 删除 get_user_name 临时测试工具及全部引用
| @@ -108,10 +106,13 @@ add_voicelife_library(linx_esp voicelife_linx_esp | |||
| "${ROOT_DIR}/components/voicelife_linx_esp/src/websocket_fragment_assembler.cc") | |||
| target_link_libraries(linx_esp PUBLIC contracts linx) | |||
| add_voicelife_library(storage_sqlite voicelife_storage_sqlite | |||
There was a problem hiding this comment.
[P1] Add the new rule migration/repository sources to the host target. VoiceLifeSchema now references ApplyV003CreateScheduleRule, but storage_sqlite still omits v003_create_schedule_rule.cc (and the new rule mappers/SQL/repository). As a result, ./scripts/run_host_tests.sh fails to link voicelife_schema_test and sqlite_schedule_repository_unit_test with an undefined reference to ApplyV003CreateScheduleRule. The host schedule and mcp libraries also omit the new rule service/planner/tool sources, so the new feature is not compiled or tested by this suite.
| // 安全上限:正常数年内即命中,上限仅用于防御异常规则。 | ||
| const int64_t k_limit = k_start + 200000; | ||
|
|
||
| for (int64_t k = k_start; k < k_limit; ++k) { |
There was a problem hiding this comment.
[P1] Enforce occurrence_count when selecting occurrences. The rule accepts and persists a maximum occurrence count, but NextOccurrence only checks status/end date and never compares the candidate's ordinal with rule.occurrence_count. A daily rule created with occurrence_count = 1 can therefore keep producing instances through generate_next_schedule_instance, and query previews also show occurrences beyond the requested limit.
| std::optional<Schedule> first_instance; | ||
| if (first_time.has_value()) first_instance = MakeSchedule(rule, *first_time); | ||
|
|
||
| const Result<ScheduleRule> updated = rule_repository_.UpdateAndRebuild(rule, first_instance); |
There was a problem hiding this comment.
[P1] Run conflict detection before rebuilding an updated rule. UpdateScheduleRuleCommand exposes ignore_conflict, but this path never loads existing active schedules or calls SchedulesConflict; it immediately deletes/rebuilds future instances. Moving a rule's next occurrence onto another active schedule therefore succeeds even when ignore_conflict is false, unlike rule creation and normal schedule updates.
将日程 MCP 工具收敛为 schedule.create、schedule.query、schedule.update、schedule.delete 四个入口,周期规则通过 repeat 对象表达,未来周期实例和单次例外不通过查询物化。 回调内部组合 ScheduleService 与 ScheduleRuleService,统一返回 status/message、可读时间格式和周期定位参数。 BREAKING CHANGE: 移除原 schedule_rule.*、schedule_occurrence.* 工具,schedule.create/update 的时间参数由 Unix 秒改为 YYYY-MM-DD HH:mm:ss 字符串。
|
MS3 收口关闭:该 PR 跨越日程、存储、MCP、运行时等 110 个文件,正文明确声明新增实现未补单元测试、未完成 ESP-IDF 构建与真机验证,且未关联可关闭的 Issue。当前已落后 main,不能作为本轮可合并交付。请按当前主干把必要的 MVP 能力拆为小 PR,并附完整测试、构建和实机证据后重新提交。 |
There was a problem hiding this comment.
本次审阅覆盖 MCP 工具契约、周期规则计算、规则/例外 SQLite 持久化、运行时装配及主机测试目标。当前提交仍有会阻断主机验证的构建配置问题,并且日期查询、周期规则更新和日期输入校验存在用户可见的错误;下面的行内意见需要在合并前处理。
验证:git diff --check 通过;./scripts/run_host_tests.sh 在 schedule_recurrence_planner_test 的头文件搜索路径处失败,单独构建 voicelife_schema_test 时又复现了缺少 v003 迁移源文件导致的链接错误。
Additional findings
components/voicelife_mcp/src/tools/schedule_tool_output.h:?: [P1] 拒绝不存在的公历日期: 当前校验只检查月份 1–12 和日期 1–31,没有检查该日期在对应月份是否存在。于是 MCP 接口会接受诸如2026-02-31,随后DaysFromCivil将其归一化为三月的日期,导致创建/更新的日程落到用户没有请求的时间。请用DaysInMonth(year, month)(含闰年)验证day后再转换。components/voicelife_schedule/src/service/schedule_rule_service.cc:?: [P1] 按查询日期范围生成周期 occurrence: 这里固定从当前时间生成每条规则的 3 个 occurrence,而 MCP 层随后才按start_date/end_date过滤。查询一个超过这 3 次结果范围的未来日期(例如查询下个月的某一天)会返回空的future_occurrences,即使规则在该日期明确会发生;查询历史日期也同样无法得到结果。应把查询范围传入规则查询/规划逻辑,或至少从请求范围的起点生成候选,而不是固定以now开始。
| "${ROOT_DIR}/components/voicelife_linx_esp/src/websocket_fragment_assembler.cc") | ||
| target_link_libraries(linx_esp PUBLIC contracts linx) | ||
| add_voicelife_library(storage_sqlite voicelife_storage_sqlite | ||
| "${ROOT_DIR}/components/voicelife_storage_sqlite/src/mapping/operation_row_mapper.cc" |
There was a problem hiding this comment.
[P1] 将规则迁移和仓储源文件加入主机目标
tests/host 的 storage_sqlite 静态库仍未编译 v003_create_schedule.cc、规则/例外 row mapper、规则 SQL 和 sqlite_schedule_rule_repository.cc。但 voicelife_schema.cc 已把 ApplyV003CreateScheduleRule 放入迁移表,因此 voicelife_schema_test 和 SQLite 仓储测试会在链接阶段报 undefined reference;新增周期持久化能力也不会被该套件实际覆盖。请同步 ESP-IDF 组件的完整源文件清单。
| "${ROOT_DIR}/components/voicelife_schedule/test/schedule_query_test.cc") | ||
| target_link_libraries(schedule_query_test PRIVATE schedule) | ||
|
|
||
| add_voicelife_test(schedule_recurrence_planner_test "unit;schedule" |
There was a problem hiding this comment.
[P1] 为 recurrence planner 测试目标补充 schedule 头文件路径
这个新增测试目标直接编译 recurrence_planner.cc 和测试文件,但只把 components/voicelife_schedule/src 加入 include path,没有加入公共头文件目录 components/voicelife_schedule/include。因此 ./scripts/run_host_tests.sh 在该目标的首个编译阶段就因找不到 voicelife/schedule/calendar.h 和 schedule_types.h 失败,主机测试无法启动。请为该目标传播或显式添加 schedule 的公共 include 目录。
| Property::Optional("status", PropertyType::kString) | ||
| .with_description("更新日程状态;跳过某次周期日程时传 cancelled,恢复时传 active"), | ||
| Property("ignore_conflict", PropertyType::kBoolean, bool{false}).with_description("是否忽略时间冲突"), | ||
| Property::OptionalObject("repeat", RepeatProperties()).with_description("更新周期规则时使用的新周期配置"), |
There was a problem hiding this comment.
[P1] 允许周期规则更新使用部分 repeat 字段
UpdateProperties 把 RepeatProperties() 作为可选对象复用,但其中 freq_type、start_date 和 start_time 仍是嵌套 schema 的必填字段。McpServer::call 会在进入下面的 ParseRepeat(repeat, false) 之前执行嵌套对象校验,所以一个只想修改 interval_val 或 end_date 的 schedule.update 调用会直接返回“缺少参数”,无法使用 UpdateScheduleRuleCommand 明确提供的部分更新语义。请为 update 定义允许部分字段的 repeat schema,或在注册时使用独立的更新属性定义。
结论
为日程模块补齐周期日程能力:支持 daily / weekly / monthly / yearly 四类周期规则、单次例外(修改 /
跳过)、以及按需生成下一条实例;同时把一次性日程的 update / delete 也暴露成 MCP
工具。周期计算(月末、闰年、短月跳过、interval 从首次发生锚定、周一起算、东八区)在领域层自行实现,不依赖
希望 Reviewer 重点判断:
Refs
变更
周期算法、ScheduleRuleService(建 / 查 / 改 / 取消规则、改 / 跳单次、生成下一条)。
original_start_time) 与查询索引);新增 SQL、行映射、SqliteScheduleRuleRepository(含跨表事务复合方法)。
明确未包含:
架构与兼容
data-model.md 的 date 有差异,以实现为准)。
协议。
验证
证据:
风险与回退
迁移。