feat(scheduler): 支持启动时补跑停机期间错过的自动化任务
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 来源
- 来源频道:Discord #功能建议
- 来源帖子:希望 Cindy 能帮我完成:自动化任务错过增加事后补跑
- 原帖链接:https://discord.com/channels/1524291334654132335/1535524181096276058/1535524181096276058
- 提问人显示名称:青川凝
- 脱敏问题摘要:用户每天需要运行一项自动化任务,但电脑开机时间不固定;希望 Cindy 启动后检查是否错过当天的计划时间,并自动补跑。
- 使用频率:每天
## 用户实际需求
用户不是要求固定延迟重试,而是希望自动化任务具备明确的“错过后补跑”策略:
- 任务仍按每天的计划时间运行;
- 如果计划时间到达时 Cindy 没有运行,则在下次启动后补跑;
- 不要求在原计划时间附近启动电脑;
- 该能力应由用户主动为任务开启。
## 已确认的当前行为
截至 2026-08-11,当前 Scheduler 不会在应用启动时补跑停机期间错过的自动化任务:
- Scheduler 启动时会加载 active 任务,并以当前时间重新计算 nextFireAt;
- recurring cron 会直接排到严格晚于当前时间的下一个时间点;
- interval 任务的旧计划若已经过期,会重新排为 now + intervalMs;
- 代码注释明确说明冷启动时“漏掉的不补发”;
- 运行期因并发限制而到点未执行的任务会继续排队,但这只适用于进程仍在运行的情况,不覆盖关机或 Cindy 未启动期间。
关键实现与测试:
- packages/maker-scheduler/src/engine/scheduler.ts:start() 启动归一、computeNextFireAt() 以及 due tick 逻辑;
- packages/maker-scheduler/src/__tests__/scheduler.test.ts:已有用例确认过去的 nextFireAt 会在启动时直接重排到未来,不产生补跑。
因此这是当前产品语义缺口,不是现有功能的使用咨询。
## 产品判断
建议新增每个自动化任务独立配置的“错过后处理”策略,并保持现有行为为默认值,避免升级后大量旧任务突然补跑。
建议优先级:P2。
## 建议的 MVP
### 1. 每任务补跑策略
首版提供两种策略:
- 跳过错过的运行:默认值,保持当前行为;
- 启动时补跑最近一次:Cindy 下次启动后,对最近一次错过的计划只执行一次。
首版不逐次回放所有错过的时间点,避免长时间离线后出现启动风暴和重复消耗。
### 2. 适用边界
- 优先覆盖 active 的 recurring cron 任务;
- paused、manual 任务不自动补跑;
- 被动 Scheduler 实例不执行补跑;
- interval recurring 和未执行 one-shot 的语义需要单独确认,不能静默套用 cron 规则。
### 3. 补跑后的排期
- 补跑不应吞掉下一次正常计划;
- recurring cron 补跑完成后,nextFireAt 仍指向下一个正常 cron 时间点;
- 补跑应进入正常 Run History,并明确标记为“启动补跑”或等价原因;
- 成功、失败和通知行为与普通自动触发保持一致。
### 4. 去重与资源保护
- 多实例共享数据库时,同一个错过的计划点只能由一个 active 实例认领;
- 复用现有 claimDueFire / CAS 语义或提供等价的持久化认领;
- 补跑必须进入现有并发闸门,不能绕过最大并发限制;
- Cindy 在补跑尚未结束时再次重启,不能重复执行同一计划点。
## 非目标
- 不在一次启动时回放全部历史遗漏;
- 不为 manual 任务增加隐式自动运行;
- 不把任务失败后的自动重试与“应用离线导致错过”混为同一策略;
- 不改变未主动开启补跑的存量任务行为。
## 仍需产品确认
1. “最近一次补跑”是否允许无限追溯,还是需要可配置的最大补跑窗口?
2. interval recurring 任务是否也支持最近一次补跑,还是仅从启动时间重新倒计时?
3. 从未执行的 one-shot 任务在过期后是否应补跑一次?
4. 补跑失败后是否按普通失败处理,还是允许在本次启动周期内自动重试?
5. UI 应在任务表单中展示简单开关,还是“跳过 / 补跑最近一次”的策略选择?
## 与现有 Issue / PR 的关系
本轮已检索 open/closed Issue 以及 open/closed/merged PR,未发现覆盖“Cindy 启动后补跑停机期间错过的自动化任务”的现有项。
相邻但不重复的能力包括自动化任务创建、模型配置、任务失败续跑以及 Codex Automations 导入;这些都没有改变冷启动跳过 missed run 的现有语义。
## 验收标准
- 默认策略保持现状,旧任务升级后不会自动补跑;
- 用户为每日 cron 任务开启“补跑最近一次”后,计划时间到达时 Cindy 未运行,下次启动会补跑一次;
- 离线跨过多个计划点时只补跑最近一次,不连续回放全部遗漏;
- 同一遗漏计划点在重复启动或多实例环境下只执行一次;
- 补跑遵守现有并发上限,资源不足时可排队但不丢失;
- paused、manual 和 passive Scheduler 不触发补跑;
- 补跑记录、状态、失败信息和通知在 Run History 中可辨认;
- 补跑后下一次正常 cron 排期保持正确。
Contributor guide
Research direction
Start with packages/maker-scheduler/src/engine/scheduler.ts, especially start(), computeNextFireAt(), and the due-tick logic, then review packages/maker-scheduler/src/__tests__/scheduler.test.ts. Resolve the listed product questions before implementation; done means the acceptance criteria hold for cron catch-up, defaults, deduplication, concurrency, run history, and subsequent scheduling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100