Skip to content

✨ feat(schedule): 实现MCP 工具定义编排以及日程模块的接口定义与实现 - #259

Open
HuXiaohui424 wants to merge 6 commits into
1024XEngineer:mainfrom
HuXiaohui424:dev/schedule
Open

✨ feat(schedule): 实现MCP 工具定义编排以及日程模块的接口定义与实现#259
HuXiaohui424 wants to merge 6 commits into
1024XEngineer:mainfrom
HuXiaohui424:dev/schedule

Conversation

@HuXiaohui424

Copy link
Copy Markdown
Collaborator

结论

为日程模块补齐周期日程能力:支持 daily / weekly / monthly / yearly 四类周期规则、单次例外(修改 /
跳过)、以及按需生成下一条实例;同时把一次性日程的 update / delete 也暴露成 MCP
工具。周期计算(月末、闰年、短月跳过、interval 从首次发生锚定、周一起算、东八区)在领域层自行实现,不依赖

希望 Reviewer 重点判断:

  1. 周期展开算法的边界规则(月末 / 短月 31 号 / 闰年 2-29、interval 锚定、weekdays_mask 位图)是否符合预期;
  2. 跨表事务的落点是否合理(规则 / 实例 / 例外在同一事务提交);
  3. 数据模型 v003 迁移与 data-model.md 的差异(original_start_time 用 datetime)。

Refs

变更

  • 领域层(voicelife_schedule):新增 ScheduleRule / ScheduleException 实体、calendar 日历换算、recurrence_planner
    周期算法、ScheduleRuleService(建 / 查 / 改 / 取消规则、改 / 跳单次、生成下一条)。
  • 存储层(voicelife_storage_sqlite):v003 迁移新增 schedule_rule 与 schedule_rule_exception 表(含 UNIQUE(rule_id,
    original_start_time) 与查询索引);新增 SQL、行映射、SqliteScheduleRuleRepository(含跨表事务复合方法)。
  • MCP 层:新增 schedule_rule_mcp_tools(7 个周期工具);schedule_mcp_tools 补上 schedule.update / schedule.delete。
  • 装配层:StorageBootstrap 注入规则 / 例外仓储;runtime 装配 ScheduleRuleService 并注册工具。

明确未包含:

  • 撤销(undo)工具未暴露(一次性与周期均暂缓)。
  • 提醒(reminder)。
  • 单元测试(本轮先交付实现,测试后续补)。
  • 完整 ESP-IDF 构建与真机验证(本环境无法交叉编译)。

架构与兼容

  • 数据模型:新增 v003 迁移,Schema 版本 2 → 3;schedule_rule_exception.original_start_time 采用 datetime(相对
    data-model.md 的 date 有差异,以实现为准)。
  • 依赖方向:voicelife_mcp 通过 PRIV_REQUIRES 依赖 voicelife_schedule(周期工具放在 mcp 组件内)。
  • 组件边界:不新增组件,改动集中在 schedule / storage_sqlite / mcp / runtime 四个既有组件;不引入新 Port / Profile /
    协议。

验证

  • ./scripts/run_pre_submit_checks.sh
  • 远端 CI 的工作流、格式、IM Gateway、主机测试、架构、ESP-IDF 和 CodeQL 均通过;依赖图已启用时依赖审查也通过
  • ESP-IDF 对应 Profile 构建
  • 真机或外部服务验证(如适用)

证据:

  • 周期算法 6 组边界用例(每周一 / 每月末 / 每月 31 号短月跳过 / 闰年 2-29 / 隔 N 天 / 隔 N 周)用独立脚本核对通过。
  • v003 迁移的 UNIQUE(rule_id, original_start_time) 约束、skip 不能带 override 的 CHECK 约束用 sqlite3 实测生效。
  • 所有新增源文件通过主机端 -fsyntax-only 语法检查。
  • 未跑完整构建与真机(诚实标注,见「风险与回退」)。

风险与回退

  • 未跑完整 ESP-IDF 构建,存在零星编译 / 链接风险;runtime.cc 的改动未做语法检查(依赖 ESP-IDF 头文件)。
  • original_start_time 类型与 data-model.md 不一致,需确认是否同步文档。
  • 回退:本 PR 尚未真机部署,直接 revert 两个 commit(b8c3229、f5cf7e9)即可;若已升级到 v3 再回退,需额外提供 v4
    迁移。

将整数范围和字符串长度约束合并到同一个 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 临时测试工具及全部引用

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

Found three functional/build issues in the schedule-rule implementation. Validation: ./scripts/run_host_tests.sh fails during the host build; git diff --check passes.

Comment thread tests/host/CMakeLists.txt
@@ -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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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 字符串。
@HuXiaohui424 HuXiaohui424 changed the title ✨ feat(schedule): 实现周期日程规则与单次例外能力 ✨ feat(schedule): 实现MCP 工具定义编排以及日程模块的接口定义与实现 Aug 13, 2026
@ZhaoXingPeng

Copy link
Copy Markdown
Collaborator

MS3 收口关闭:该 PR 跨越日程、存储、MCP、运行时等 110 个文件,正文明确声明新增实现未补单元测试、未完成 ESP-IDF 构建与真机验证,且未关联可关闭的 Issue。当前已落后 main,不能作为本轮可合并交付。请按当前主干把必要的 MVP 能力拆为小 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.

本次审阅覆盖 MCP 工具契约、周期规则计算、规则/例外 SQLite 持久化、运行时装配及主机测试目标。当前提交仍有会阻断主机验证的构建配置问题,并且日期查询、周期规则更新和日期输入校验存在用户可见的错误;下面的行内意见需要在合并前处理。

验证:git diff --check 通过;./scripts/run_host_tests.shschedule_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 开始。

Comment thread tests/host/CMakeLists.txt
"${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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P1] 将规则迁移和仓储源文件加入主机目标

tests/hoststorage_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 组件的完整源文件清单。

Comment thread tests/host/CMakeLists.txt
"${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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.hschedule_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("更新周期规则时使用的新周期配置"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P1] 允许周期规则更新使用部分 repeat 字段

UpdatePropertiesRepeatProperties() 作为可选对象复用,但其中 freq_typestart_datestart_time 仍是嵌套 schema 的必填字段。McpServer::call 会在进入下面的 ParseRepeat(repeat, false) 之前执行嵌套对象校验,所以一个只想修改 interval_valend_dateschedule.update 调用会直接返回“缺少参数”,无法使用 UpdateScheduleRuleCommand 明确提供的部分更新语义。请为 update 定义允许部分字段的 repeat schema,或在注册时使用独立的更新属性定义。

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.

2 participants