Version: 0.9.62.dev.260502

后端:
1. 主动调度补齐 `unfinished_feedback` 定位闭环——用户补充信息先在滚动窗口内定位到可校验的日程块,定位失败则继续 ask_user,不再硬猜 target_id 或直接跑 graph。
2. 聊天占管重跑链路加并发保护——`waiting_user_reply -> rerunning` 改为 DB CAS 抢占,重复补充只返回可见等待提示,避免并发生成多份 preview。
3. rerun 结果回写继续收口——新 preview_id 同步回 trigger 审计指针,session 只在拿到新 preview 时更新当前预览,ready_preview 后清空追问状态并释放回普通聊天。
4. 主动调度事件校验放宽 unfinished_feedback 的空 target 场景,允许先触发、后定位,再进入 graph + preview 主链路。
This commit is contained in:
Losita
2026-05-02 12:41:50 +08:00
parent a3eaa9b2c2
commit ba23ebd201
12 changed files with 891 additions and 103 deletions

View File

@@ -4,6 +4,8 @@
目标只有一个:把主动调度剩下的缺口按阶段补完,并且每个阶段都能明确验收、明确自动化边界、明确是否已经完成。后续我会在这里持续把 `[ ]` 改成 `[x]`
补充约定:本文档是阶段进度看板,不再重复写设计细节;已经落地的阶段必须及时改成 `[x]`,并保留简短验证记录。实现方案和边界口径以《第二阶段主动调度 MVP 实现方案.md》为准。
---
## 0. 当前仓库基线
@@ -25,9 +27,19 @@
- [x] `CreatePreview` 已切到 graph + 受限 selector不再是固定 top1 / `Candidates[0]`
- [x] `active_schedule_sessions` 已正式进入代码,并接好缓存链路。
- [x] 聊天入口已按 session 状态拦截,`waiting_user_reply / rerunning` 会接管补信息链路。
- [ ] `unfinished_feedback` 的“定位 -> ask_user -> 重跑 graph”闭环还没完全做实
- [ ] 聊天页里的主动调度 preview 卡片 / 微调弹窗还没有最小适配。
- [ ] 剩余极限验收项还没完全脚本化。
- [x] `unfinished_feedback` 的“定位 -> ask_user -> 重跑 graph”后端闭环已经接上,前端最小适配也已落地
- [x] 聊天页里的主动调度 preview 卡片 / 微调弹窗已完成最小适配。
- [ ] 剩余极限验收项已做过实测验证,但还在继续脚本化沉淀
### 近期实测记录2026-05-02
这轮重新核验后,主动调度最小闭环已经再次跑通,可作为后续阶段 5 负向边界验收的基线。
1. 测试账号为 `test0424 / 123456`,任务为“做马原大作业”,`mock_now` 固定到 `2026-04-30T01:14:21+08:00` 的周四窗口。
2. 这次链路跑出了 `trigger_id=ast_1bb62e3e-f2cf-48a9-8f29-1461b99bff6b``preview_id=asp_e79db789-ba16-4108-a843-cd33c03aa3f6``conversation_id=ce525dc0-101a-50ca-8993-7fc466328de2``notification_records.id=22`
3. DB 对账显示 `active_schedule_triggers.status=preview_generated``active_schedule_previews.status=ready``active_schedule_sessions.status=ready_preview`,而 `schedule_events` 对该 preview 的正式写入计数为 0说明这次只到预览态没有执行正式 apply。
4. 浏览器里周四列同时存在已有课程和待确认任务块;周六窗口只剩一个孤立任务块属于正常现象,不是后端漏课程。
5. 这批结果说明:当前收口已经不在“主链路能不能跑通”,而是在“阶段 5 的过期、重复、篡改、冲突、幂等、通知和 outbox 负向边界能不能全部挡住”。
### 代码锚点
@@ -71,8 +83,8 @@
| 阶段 0 | [x] | 补 `estimated_sections` 写入入口 | 创建任务时能稳定写入 1~4 节,主动调度只消费落库值 | 可以API + DB + `go test` |
| 阶段 1 | [x] | 补主动调度 Eino graph 和 LLM 解释 / 补全兜底 | 产生候选、有限裁决、输出解释、保留 fallback | 可以,后端单测 + API 验证 |
| 阶段 2 | [x] | 补 `active_schedule_sessions`、聊天拦截和缓存链路 | `waiting_user_reply / rerunning` 拦截生效,`ready_preview` 释放 | 可以API + DB + 路由验证 |
| 阶段 3 | [ ] | `unfinished_feedback``ask_user` 闭环和前端最小适配 | 用户在聊天页补信息后能重跑 graph 并刷新 preview | 后端可自动,前端需浏览器验证 |
| 阶段 4 | [ ] | 收口飞书通知与会话链接 | `action_url` 指向 `/assistant/{conversation_id}`,通知 payload 从简 | 可以webhook POST + DB 验证 |
| 阶段 3 | [x] | 完成 `unfinished_feedback``ask_user` 闭环和前端最小适配 | 用户在聊天页补信息后能重跑 graph 并刷新 preview | 后端可自动,前端需浏览器验证 |
| 阶段 4 | [x] | 收口飞书通知与会话链接 | `action_url` 指向 `/assistant/{conversation_id}`,通知 payload 从简 | 可以webhook POST + DB 验证 |
| 阶段 5 | [ ] | 跑完第五阶段剩余验收和失败注入脚本 | 冲突、过期、重复确认、重试、dead/skipped 全覆盖 | 可以,基本全自动 |
---
@@ -268,9 +280,9 @@
**当前状态**
`unfinished_feedback` 目前还偏向“已有目标就能做”,但“定位不稳怎么办、用户回一句怎么办、如何重跑 graph”还没有完全闭环
`unfinished_feedback` 的后端定位闭环、聊天页最小适配和并发兜底都已经收口,阶段 3 已完成
**要做什么**
**已落地内容**
1. 定位逻辑按这个顺序走:
- LLM 上下文推断
@@ -287,6 +299,30 @@
- 只是把 timeline 新类型和主动调度 confirm API 接起来
7. 后端负责把主动调度 preview DTO 转成前端容易复用的结构,前端不背脏活。
**补充链路图**
```mermaid
flowchart TD
A[用户在聊天页发送消息\nPOST /api/v1/agent/chat] --> B{查询 active_schedule_sessions}
B -->|未占管| N[进入普通 newAgent]
B -->|waiting_user_reply / rerunning| C[拦截到主动调度分支]
C --> D[写入用户消息到 timeline]
D --> E[LLM JSON 定位节点\n只负责补齐缺失事实]
E --> F{能否定位 schedule_event?}
F -->|否| G[生成 ask_user\nsession=waiting_user_reply]
F -->|是| H[重跑 active scheduler graph]
H --> I[BuildContext -> Observe -> GenerateCandidates -> SelectAndExplain]
I --> J[CreatePreview]
J --> K[session=ready_preview]
K --> N
```
这个定位节点只做一件事:把用户补充的话术转成后端可校验的 JSON 事实,不负责写正式日程,不负责生成新排程策略,也不负责替代后端候选裁决。
**验收点**
1. 用户补完当前主动调度缺失事实后,能刷新 preview 并解除锁定;解锁后再说“我周末不想学习”这类偏好话术时,直接走现有 newAgent memory / execute 链路。
@@ -300,69 +336,40 @@
- 前端卡片展示和按钮分支,建议用浏览器实际打开一次做可视确认。
- 如果只是检查 DOM / 路由 / 请求是否发对,能自动;如果要看卡片样式是否真的对齐,还是需要浏览器看一眼。
**验证记录**
1. `go build ./cmd/all` 已通过。
2. `go test ./...` 已通过。
3. 并发补充场景已验证:同一会话两条补充同时到来时,只会有一条抢到 rerun另一条返回占管提示不会重复生成 preview。
---
### 阶段 4收口飞书通知与会话链接
**当前状态**
用户级 webhook 配置、通知投递、测试接口已经有基础,但主入口还需要统一收口到聊天会话链接,不再把旧的 `/schedule-adjust/{preview_id}`新目标
已完成。用户级 webhook 配置、通知投递、测试接口和会话链接已经统一到 `/assistant/{conversation_id}`,不再把旧的 `/schedule-adjust/{preview_id}`作新入口
**要做什么**
**已落地内容**
1. 通知前先绑定或预创建 `conversation_id`
2. `action_url` 统一走:
1. 通知前由后端预创建或绑定 `conversation_id`,保证飞书点击后直接进入同一会话
2. `action_url` 统一`/assistant/{conversation_id}`;通知 payload 保持从简,只保留会话跳转和排障所需字段。
3. 用户级飞书 webhook 的保存 / 查询 / 删除 / 测试接口已接通,真实投递与测试共用同一套 provider 校验和 JSON 拼装。
4. `notification_records` 已覆盖 `sent / failed / dead / skipped` 和 retry 相关状态。
5. 用户未配置或禁用 webhook 时,通知记录会落 `skipped`,不阻塞主链路。
```text
/assistant/{conversation_id}
```
**验证记录**
3. 本地测试和示例配置继续用 `localhost`,上线后再换正式域名
4. 业务 JSON 保持从简,只让飞书流程去编排消息,不把复杂卡片协议塞进 webhook
5. 维持当前通知状态机:
- `sent`
- `failed`
- `dead`
- `skipped`
- retry 相关状态
1. `PUT /api/v1/notification/channels/feishu` 已跑通
2. `POST /api/v1/notification/channels/feishu/test` 已跑通,成功时会回写 `last_test_status / last_test_at`
3. `POST /api/v1/active-schedule/trigger` 后生成的通知请求 payload 中,`message.action_url` 已指向 `/assistant/{conversation_id}`
4. 真实飞书消息样式和外部页面交互仍属于最终验收,不影响后端收口结论。
**建议 payload 形态**
**建议保留的边界**
```json
{
"event": "smartflow.schedule_adjustment_ready",
"version": "1",
"notification_id": 123,
"user_id": 5,
"preview_id": "asp_xxx",
"conversation_id": "conv_xxx",
"trigger_id": "ast_xxx",
"trigger_type": "important_urgent_task",
"target_type": "task_pool",
"target_id": 81,
"message": {
"title": "SmartFlow 日程调整建议",
"summary": "把重要且紧急任务放入滚动 24 小时内的空闲节次。",
"action_text": "查看并确认调整",
"action_url": "http://localhost:5173/assistant/conv_xxx"
},
"trace_id": "trace_xxx",
"sent_at": "2026-04-30T17:34:52+08:00"
}
```
**验收点**
1. 通知里的跳转链接能直接进聊天页。
2. 用户级 webhook 的保存、查询、删除、测试都能跑通。
3. 未配置、临时失败、不可恢复失败的状态都能在 `notification_records` 里看见。
4. 用户已经在聊天页时,不再强依赖飞书通知承接回复。
**自动化测试**
- 可以自动跑。
- 建议路径Webhook POST、测试接口、`notification_records` 状态断言、真实 webhook 收到后人工看一次消息。
- 如果需要验证“飞书真的收到”,最终还是要看外部页面一次,但 HTTP 层和状态层可以自动。
1. 这里不再恢复旧的 `/schedule-adjust/{preview_id}` 主入口。
2. 业务 JSON 继续从简,不把复杂卡片协议塞进 webhook。
3. 后续如果要扩展新的通知渠道,先复用 `notification_records` 状态机和 `FeishuProvider` 的抽象边界。
---
@@ -370,7 +377,7 @@
**当前状态**
主链路已经有了,但极限边界还需要系统化收口
主链路已经有了,而且周四窗口的最小闭环已经再次跑通;现在阶段 5 的重点只剩极限边界、失败注入和脚本化收口。这里不再重跑阶段 3 的整套 `ask_user` 主链路,只保留 1 条真实 chat 烟测确认入口未回归
**要做什么**
@@ -391,7 +398,12 @@
1. 所有核心状态机都能串起来排障。
2. 同一条 preview / notification / apply 不会被重复落库。
3. 过期、冲突、篡改、失败注入都能拒绝。
4. 最终能把这一轮主动调度缺口标成完成
4. 预览过期、重复 confirm、错误 candidate / 跨用户 preview / preview-session 不匹配都能挡住
5. 同一 `idempotency_key / dedupe_key` 不能重复生成有效 trigger / preview / notification。
6. notification 的 `skipped / failed / dead / sent` 状态都能在 DB 里对上。
7. outbox 重复消费不会重复投递或重复写通知记录。
8. `api / worker / all` 三种启动边界相关 handler / job 注册不能缺失。
9. 最终能把这一轮主动调度缺口标成完成。
**自动化测试**
@@ -414,6 +426,7 @@
8. `unfinished_feedback` 先定位,再 `ask_user`,定位成功后直接生成补做 preview不移动原任务。
9. 用户在聊天页说偏好时,不归主动调度接管;解锁后直接走现有 newAgent memory / execute 链路。
10. 只有后台离线自动触达才走飞书;用户已经在会话里时,不需要再先走飞书通知。
11. `ask_user` 闭环只新增一个 LLM JSON 定位节点,沿用正常 `chat` 入口和 session 拦截,不单独再造工具系统。
---

View File

@@ -1,5 +1,19 @@
# 第二阶段主动调度 MVP 功能预期
## 0. 当前状态摘要2026-05-02
本文档仍作为主动调度 MVP 的产品边界说明;具体实现拆分、表结构和阶段进度以《第二阶段主动调度 MVP 实现方案.md》和《主动调度缺口分阶段实施计划.md》为准。
截至当前仓库状态MVP 主闭环已经从“功能预期”推进到可演示、可排障的实现状态:
1. `important_urgent_task` 已能从任务池生成主动调度 trigger写入 `active_schedule_previews`,并通过飞书 webhook 触达用户。
2. 飞书链接已统一进入 `/assistant/{conversation_id}`,不再使用独立 `/schedule-adjust/{preview_id}` 入口。
3. 用户进入助手页后,可以看到主动调度卡片,并打开日程预览与精排弹窗;确认前只展示 preview不写正式 `schedule_events`
4. 用户确认后才进入同步 apply 链路apply 失败必须回写可排障状态,不能半写正式日程。
5. `unfinished_feedback` 的“定位 -> ask_user -> 重跑 graph -> 刷新 preview”闭环已经落地阶段 5 不再重复全套主链路,只保留 1 条真实 chat 烟测确认入口未回归。
演示和验收时需要特别注意:主动调度窗口是“从当前时刻起滚动 24 小时”,不是自然日或整周。如果窗口落在周六且该用户没有课程,预览里只出现待安排任务是符合语义的;需要展示“已有课程 + 待确认任务”同屏时,应把 `mock_now` 固定到有课程的周四窗口。
## 1. 文档目的
本文档先讨论第二阶段最终想做成什么功能,不进入具体代码拆分和表结构实现。
@@ -530,20 +544,40 @@ assumed_completed
---
## 11. MVP 验收标准
## 11. MVP 验收标准与当前状态
第一版功能算完成,需要满足:
第一版验收按“已落地口径、阶段 5 主验收、后置能力”三类看,避免把后续演进项混进本轮收口。
已落地并作为 MVP 口径的能力:
1. 只扫描滚动 24 小时内的问题。
2. 能识别四象限“重要且紧急”池中尚未进入日程视图的任务。
3.在用户反馈未完成后,识别受影响的 schedule 任务与后继任务
4. 动态任务计划时间过去后默认按已完成推进,不主动追问
5. 当前动态任务失败且影响后继时,能按“局部重排 -> 延后结束 -> 压缩融合”顺序生成候选
6.输出结构化 metrics / issues / decision / candidates
7. 候选项必须来自后端,不让 LLM 自由生成正式写库参数LLM 只做解释、追问和有限选择
8. 不直接写正式 schedule只写预览或触达用户
9. 能发布 `notification.feishu.requested` 提醒用户回系统确认
10. 用户确认后才允许进入正式应用链路
3.输出结构化 `metrics / issues / decision / candidates`
4. 候选项必须来自后端,不让 LLM 自由生成正式写库参数LLM 只做解释、追问和有限选择
5. 确认前不直接写正式 `schedule_events / schedules`,只写 `active_schedule_previews` 或触达用户
6.发布 `notification.feishu.requested`,并通过 `notification_records` 记录 `sent / failed / dead / skipped` 等状态
7. 飞书触达后进入 `/assistant/{conversation_id}`,由助手会话页承载预览、微调和确认
8. 用户确认后才允许进入同步 apply 链路;成功写正式日程,失败回写预览状态和错误原因
9. `unfinished_feedback` 已支持用户补充事实后的 `ask_user -> rerun -> ready_preview` 闭环
10. 动态任务计划时间过去后默认按 `assumed_completed` 推进,不主动追问
阶段 5 主验收重点:
1. preview 过期后 confirm 必须拒绝。
2. 同一 preview 重复 confirm 只能生效一次。
3. 错误 `candidate_id`、跨用户 `preview_id`、preview 与 session 不匹配都必须拒绝。
4. 冲突或不可写入时apply 应失败并保留可排障状态。
5. 相同 `idempotency_key / dedupe_key` 不能重复生成有效 trigger / preview / notification。
6. notification 要覆盖未配置 webhook 的 `skipped`、临时失败的 `failed/retry`、永久失败的 `dead` 和成功的 `sent`
7. 重复消费同一 `notification.feishu.requested` 不能重复投递或重复写 `notification_records`
8. `api / worker / all` 三种启动边界相关 handler / job 注册不能缺失。
后置能力,不作为当前 MVP 完成阻塞:
1. 课程变化触发、疲劳反馈触发和更复杂的跨天全局重排。
2. `compress_with_next_dynamic_task` 压缩融合候选,当前只保留 schema / 口径,不默认生成。
3. apply 成功后的撤销按钮和完整回滚体验。
4. 更细颗粒的偏好打破策略,例如哪些偏好是硬约束、哪些偏好可在高风险任务前让位。
---
@@ -563,9 +597,18 @@ assumed_completed
---
## 13. 待讨论问题
## 13. 已收口与后续演进问题
1. 主动观测能力是否最终落成独立工具,以及具体命名是什么。
2. 四象限懒加载轮换要如何补齐:后台 worker 定时刷新、主动调度前同步刷新,还是抽公共轮换服务。
3. 前后对比回滚机制复用现有哪条链路,是否需要新增主动调度预览类型
4. 魔改粗排时,哪些用户偏好可以被打破,哪些偏好必须保持为硬约束。
已收口:
1. 主动观测能力当前落在 `backend/active_scheduler` 准独立模块内,主链路走 graph / service pipeline不进入 ReAct 工具循环
2. 主入口统一到 `/assistant/{conversation_id}`,飞书只负责触达和跳转,不在飞书内完成复杂确认。
3. 预览独立写入 `active_schedule_previews`,不塞进 `agent_schedule_states`;正式 apply 仍走后端重校验。
4. task_pool 任务进入正式日程时使用 `schedule_events.task_source_type=task_pool`,不创建孤儿 `task_item`
后续演进:
1. 四象限懒加载轮换仍建议继续评估后台刷新或抽公共轮换服务,避免主动调度读取到过期任务池。
2. 前后对比与回滚体验还可以继续增强,但不影响当前“确认前只预览、确认后同步 apply”的 MVP 语义。
3. 压缩融合和复杂偏好打破策略暂不打开,等主链路和失败注入脚本稳定后再评估。
4. 课程变化、疲劳反馈和跨天计划优化后置到后续版本。

View File

@@ -2,7 +2,7 @@
## 0. Handoff 说明
本文档已收口为第二阶段主动调度 MVP 的最终实施版。截至 2026-04-30,后端第一至第四阶段主体代码已实现并通过本地 `go test ./...`;真实飞书 webhook 配置接口和 `important_urgent_task` 主动触发端到端链路已通过本地后端验收。接手者请优先阅读本节、第 10 章装配边界和第 14 章验证 checklist再从第五阶段剩余验收继续推进。
本文档已收口为第二阶段主动调度 MVP 的最终实施版。截至 2026-05-02,后端第一至第四阶段主体代码已实现并通过本地 `go test ./...`;真实飞书 webhook 配置接口`/assistant/{conversation_id}` 会话链接`important_urgent_task` 主动触发端到端链路已通过本地后端验收。阶段进度看板请以《主动调度缺口分阶段实施计划.md》为准那里会把已完成阶段标成 `[x]`接手者请优先阅读本节、第 10 章装配边界和第 14 章验证 checklist再从第五阶段剩余验收继续推进。
当前核心共识:
@@ -60,7 +60,7 @@
3. task_pool 正式落库写 `schedule_events(type=task, task_source_type=task_pool, rel_id=tasks.id)`
4. 补做块新增 event不移动原已排任务。
第四阶段worker 与 notification。主体代码已完成,真实 webhook 配置接口已验收)
第四阶段worker 与 notification。已完成真实 webhook 配置接口已验收)
1. 接入 `active_schedule.triggered` worker handler 和 due job scanner。
2. 接入 `notification.feishu.requested` handler。
@@ -75,7 +75,7 @@
3. 根据日志和测试结果补齐 trace 字段与错误码。
4. 主链路稳定后再评估是否打开压缩融合候选。
第六阶段:主动调度 graph 补齐、会话桥与聊天页合流。(待实施
第六阶段:主动调度 graph 补齐、会话桥与聊天页合流。(主体已完成,剩余最终验收与文档收口
0. `estimated_sections` 写入入口已经补完:普通任务创建请求、转换层和 quick task / 随口记创建任务时,都会把 LLM 估计的 1~4 节写入 `tasks.estimated_sections`;主动调度只消费该字段,不在 graph 内重新猜任务耗时。
1. 补主动调度 Eino graph把现有 `BuildContext -> Observe -> GenerateCandidates -> CreatePreview` 固定 pipeline 整理成 graph 节点,并新增 LLM 解释 / 有限选择、`ask_user`、fallback 分支;当前代码里的 first-fit / `Candidates[0]` 只能作为过渡实现。
@@ -141,7 +141,7 @@
- 已实现 preview 写入、详情查询、`apply_id + idempotency_key`、候选转换、同步 apply adapter。
- `add_task_pool_to_schedule` 已能正式写入 `schedule_events(type=task, task_source_type=task_pool, rel_id=tasks.id)` 和对应 `schedules`。
- `create_makeup` 转换与 adapter 已预留并实现基本写入路径,但尚需在第四 / 第五阶段结合正式 unfinished feedback worker 场景补端到端验收。
4. 第四阶段worker 与 notification 主体代码。
4. 第四阶段worker 与 notification 主体代码(已完成)
- 已接入 `active_schedule.triggered` worker handler、due job scanner、`notification.feishu.requested` handler 和 notification retry loop。
- 已新增 `backend/notification` provider / service 分层mock provider 保留,真实投递切到用户级飞书 Webhook 触发器 provider。
- 已新增 `user_notification_channels` model / DAO并接入 AutoMigrate 与 `RepoManager`。
@@ -201,17 +201,30 @@
下一阶段入口:
1. 下一步继续第五阶段剩余验收,不需要重做 dry-run / preview / confirm 主链路,也不需要重做第四阶段 provider / handler 主体代码。
2. 第五阶段剩余重点:
- confirm apply 冲突失败、过期拒绝
1. 下一步继续第五阶段剩余验收,不需要重做 dry-run / preview / confirm 主链路,也不需要重做第四阶段 provider / handler 主体代码,也不需要重做第六阶段 graph / session bridge / 聊天页合流主体代码
2. 第五阶段剩余重点已经收口为负向边界和脚本化验收
- 保留前置验证:`go build ./cmd/all`、backend 下 `go test ./...`,并清理本次生成的 `.gocache`
- 阶段 3 主链路不再全量重跑,只做 1 条真实 chat 烟测确认入口未回归。
- preview 过期、重复 confirm、candidate / preview 篡改、preview 与 session 不匹配都必须拒绝。
- 冲突失败或不可写入场景必须失败并保留可排障状态。
- trigger 幂等、notification 状态矩阵、outbox 重复消费和 `api / worker / all` 启动边界必须补齐证据。
- 更完整的边界清理:测试数据隔离策略、失败注入脚本化、前端真实地址替换为正式域名配置。
4. 工作区注意:
3. 工作区注意:
- 另一个前端对话可能在改前端;后端阶段不要碰 `frontend` 相关改动。
- 当前允许单个 Go 文件 700 行以内;超过 700 再评估拆分。
- 每次执行 `go test` 后必须清理根目录 `.gocache`。
- 后续阶段必须优先自动化验收能由代码、API、DB 查询、日志查询验证的内容,由实现者自己跑完并记录结果。
- 如果受限于外部账号、真实飞书环境、浏览器人工交互、权限或本地环境,导致某项验收无法完成,不能默认为通过,也不能在报告中省略;必须明确写出未验收项、阻塞原因、建议由用户执行的操作和预期结果。
### 0.4 2026-05-02 最新验收记录
这轮接手时,周四窗口的主动调度最小闭环已经再次跑通,可作为周报截图和阶段 5 续验的基线。
1. 测试账号使用 `test0424 / 123456`,任务为“做马原大作业”,并把 `mock_now` 固定到 `2026-04-30T01:14:21+08:00` 的周四窗口,避免周六空窗造成的误判。
2. trigger `ast_1bb62e3e-f2cf-48a9-8f29-1461b99bff6b` 生成 preview `asp_e79db789-ba16-4108-a843-cd33c03aa3f6`,会话 `ce525dc0-101a-50ca-8993-7fc466328de2` 挂接当前 preview`notification_records.id=22` 发送成功。
3. DB 里 `active_schedule_triggers.status=preview_generated`、`active_schedule_previews.status=ready`、`active_schedule_sessions.status=ready_preview`,而 `schedule_events` 对该 preview 的正式写入计数为 0说明这次只到预览态还没有执行正式 apply。
4. 浏览器里能看到周四列同时存在已有课程和待确认任务块,证明“滚动 24 小时窗口 + 课程事实 + task_pool 预览”的展示链路是完整的。
## 1. 文档目的
本文档承接《第二阶段主动调度 MVP 功能预期》和《微服务四步迁移与第二阶段并行开发计划》,用于把产品预期逐步落成可执行的工程方案。