# Scheduler Module Rules

## 范围与职责

适用于 `src/host/scheduler/`。

按用户配置的**执行窗口**与**触发策略**，驱动既有编排运行时运行所选工作流模板的实例：

- 任务配置的校验、规范化与持久化；
- 时间语义：时区换算、触发点推算、窗口判定；
- 决策与推进：何时触发、何时挂起、何时续跑、何时跳过；
- 触发时的实例化语义（模板 → 实例）与会话策略分派。

判断归属：该修改是否改变**定时任务的配置契约、时间语义或触发/挂起/续跑决策**？若主要属于时间基元之外的其他领域（编排推进、Agent 语义、持久化原语、UI 展示、HTTP 端点），应放到对应模块。

## 时间语义契约

时间语义必须确定、可测、与时区显式绑定：

- 时间的解析/格式化/换算/推算一律为**纯函数**：相同输入 → 相同输出，不读系统时钟、不读环境时区、不依赖全局状态；需要"当前时刻"时经参数传入；
- 绝对时间在存储与比较层面统一用 UTC 毫秒；用户可见的时刻（"HH:mm"、日期）一律按任务自身声明的时区解释——**禁止**使用进程本地时区或隐式时区；
- 跨天区间（结束不晚于开始）必须与跨天触发点、跨日期窗口扫描的语义一致，不得各处自行解释；
- 调度语义分两层且语义固定：「执行窗口」决定何时允许运行，「触发策略」决定何时应当触发；**触发成立的条件是两层同时满足**。

## 子域边界

模块内部存在变化原因不同的子域：

- 子域之间只通过明确的输入输出协作，不得互相承担对方职责；
- 时间基元不理解"任务"；窗口判定与触发计算不理解持久化；配置契约不读盘、不决策；决策引擎不重复实现时间算法；
- 新增能力先判断属于哪个子域，再放到该子域的职责中，不得为就近而堆进已有位置。

## 依赖边界

- 只允许依赖共享契约层（任务实体与窗口/触发配置的形状）与本模块内部；
- 对外部能力的依赖必须是抽象缝（接口 + 注入），方向单向：调用方依赖本模块的决策，本模块不反向依赖任何调用方。

## 验证补充

- 时间相关断言全部使用**固定日期与时区**，不依赖运行机器的本地时区与当前时间；
- 窗口边界、跨天区间、跨日期扫描、无解配置（永不触发）等边界行为均有测试锁定；
- 决策路径的关键分支（触发 / 触发失败 / 并发跳过 / 窗口挂起 / 窗口续跑 / 用户接管 / 停用 / 重启游标）均有测试锁定；
- 重叠扫描、重复启动、终止后再启动等并发与生命周期行为有测试锁定；

## 核心原则

1. 时间语义确定且时区显式，纯计算不读时钟。
2. 窗口与触发两层语义固定，缺一不触发。
3. 收敛、校验、规范化各有唯一实现且顺序固定。
4. 任务文件单一写者；持久化复用 storage 原子原语，不自建协议。
5. 扫描可重入安全，终止后不复活。
6. 外部能力经显式依赖缝注入。
