work-graph-time-travel-debugging-requirements.md 8.53 KB

Work Graph 执行历史 Time-Travel 调试——需求文档

日期: 2026-08-05 性质: 需求文档(BRD/PRD 层面),不含详细技术设计。回应 docs/todo/graph-engineering-open-items.md 条目 D 的确认结果——"time travel 可以立项"。 状态: 草案,待评审。

命名消歧(仿照 CLAUDE.md「两个 Supervisor」的先例):本文档说的"Time-Travel 调试"跟 docs/done/supervisor-auto-rollback-design.md 描述的 Deploy Supervisor 自动回滚是两件完全不同的事——不要混淆:

Time-Travel 调试(本文档) Deploy Supervisor 自动回滚
回滚的对象 某一次任务的 Work Graph 执行过程(某个 work_node_execution 的中间状态) graphlemon 自身的代码版本(git commit)
触发者 人工调试意图("我想看看如果这一步换个输入会怎样") 自动健康检查失败信号
影响范围 单个 task 的执行历史,不影响其他任务 整个 graphlemon 进程的运行版本
相关文档 本文档 docs/done/supervisor-auto-rollback-design.md

1. 背景

综述版对照笔记(docs/reference/graph-engineering-vs-loop-engineering-overview.diff-with-graphlemon.md §6)指出:LangGraph 的 checkpointer 机制带来四个能力——人在回路、记忆、时间旅行调试、容错。逐项对照 graphlemon 现状:

LangGraph 能力 graphlemon 现状
容错(部分节点失败保留已成功节点输出) ✅ 已具备(work_node_execution 按节点独立持久化状态)
人在回路(暂停/恢复) ✅ 已具备(src/task_execution.py 的 Start/Pause/Stop)
记忆(多轮交互间保留上下文) ✅ 已具备(.claude/agent-memory/)
时间旅行调试(回到任意历史检查点重放/分叉) ❌ 未实现,现状是"用 git 历史 + 手动重跑"

work_node_execution 表(src/schema.py:138)目前按节点持久化 input_payload/output_payload/status/retry_count 等字段,work_graph_run 表(src/schema.py:118)持久化 graph_manifest 和整体 status——这些数据本身已经具备"回放某一次执行发生了什么"的信息基础,缺的是"从某个历史节点分叉出一条新的执行分支"这个操作本身。

2. 目标

  1. 允许人工从某次 Work Graph 执行的某个历史节点(work_node_execution)出发,用新的输入(不同的 prompt、不同的上游产物)重新执行该节点及其下游依赖节点,产出一条与原执行并存、可独立查看的新分支,而不覆盖或污染原始执行记录。
  2. 分叉出来的新分支要能在控制台里被查看、区分(哪条是原始记录、哪条是分叉出来的调试分支)。
  3. 这个能力主要服务于调试/复盘场景(例如:"如果 SDD 阶段换一版方案,后续的 Coder 实现会有什么不同"),不是生产环境的正常任务执行路径的一部分。

3. 非目标

  1. 不是自动化机制。分叉重放由人工在控制台里主动触发,不是系统自主决定"要不要回溯重跑"的行为——这跟 Org-level Loop 那种系统自主触发的回退是两回事。
  2. 不追求支持任意粒度的时间点(比如"回到某个节点执行到一半的中间状态")。粒度限定在 work_node_execution 这个已有的持久化单元——从"某个节点完成之后(它的 output_payload 已经落库)"这个时间点分叉,不试图捕获节点内部一次 claude -p 调用执行过程中的中间状态。
  3. 不在第一版里做完整的可视化 diff/比较工具(比如"原分支和新分支的产物并排对比高亮差异")。第一版先解决"分叉出一条新分支并能分别查看"这个核心能力,比较/diff 视图留给后续迭代。

4. 功能需求

# 需求描述 优先级
FR-1 给定一个已完成(status='done')的 work_node_execution,人工可以指定新的输入(覆盖原 input_payload 的部分或全部字段),触发从这个节点开始的重新执行。 必须
FR-2 分叉执行产生的是一次新的 work_graph_run(不是修改原 work_graph_run 的记录),需要能追溯"这次运行是从哪个历史节点、哪个原始 run 分叉出来的"。 必须
FR-3 分叉出的新 run 只重新执行"从分叉点开始,到该节点的下游依赖链",分叉点之前的节点(分叉点本身的上游)直接复用原始 run 已经产出的 output_payload,不重新执行——这跟现有断点续跑"复用已完成节点输出"的逻辑是同一个原则的延伸。 必须
FR-4 控制台(架构文档 §13 的人机交互控制台)需要能列出"某个 task 下所有的分叉分支",并支持切换查看每条分支各自的节点执行详情(复用现有 v_task_detail 一类页面的节点级展示能力)。 应该
FR-5 分叉出的调试分支不应该影响原始任务的正常执行状态——即使分叉分支的执行结果比原分支"更好",也不会自动替换/合并回原始任务的 task.status,是否采纳分叉分支的结果需要人工另外决定(比如人工把分叉分支的产物手动复制回主线,或者认可分叉分支本身就是新的正确路径)。 必须

5. 验收标准

  1. 给定一个已经跑完的简单 Work Graph(比如 Planner 的 BRD→PRD→SDD→Benchmark 四节点链),从 PRD 节点分叉、换一个不同的 PRD 内容,触发重新执行,验证 SDD、Benchmark 两个下游节点用新的 PRD 内容重新跑了一遍,而 BRD 节点没有被重新执行(直接复用原值)。
  2. 验证原始 run 的记录在分叉操作后完全不受影响——分叉前后分别查询原始 work_graph_run/work_node_execution,字段值一致。
  3. 验证同一个历史节点可以被分叉多次,产生多条互不干扰的分支,且每条分支都能独立追溯到"从哪个节点、哪个原始 run 分叉而来"。
  4. 控制台里能看到某个 task 下全部分支的列表,并能分别打开查看。

6. 约束与假设

  • work_graph_run 表当前有一条唯一约束:ux_work_graph_run_active——同一个 (task_id, org_node) 组合同一时间只能有一个非终态的 run(src/schema.py:134)。分叉产生的新 run 如果沿用同一个 task_id,会撞上这条约束(分叉分支和原分支可能同时处于非终态)。需要在设计阶段决定:分叉出的 run 是否需要一个独立的标识维度(比如新增一个 parent_run_id/fork_of_node_exec_id 字段,并调整唯一约束的范围,使其只约束"同一条分支线"内部,而不是整个 task_id),这是本需求对现有数据模型影响最大的一处,必须在设计阶段仔细过一遍现有依赖这条约束的代码路径(比如断点续跑逻辑是否假设了"同一 task_id+org_node 只有一个活跃 run")。
  • 分叉执行复用现有的每节点独立子进程执行机制(work_node_run.run_work_node/work_graph_execute.execute_work_graph),不需要新的执行引擎——分叉本质上是"用一份新的 graph_manifest(去掉已复用的上游节点)+ 新的初始 outputs 字典(预填充复用节点的输出)调用一次 run_graph()",复用度很高。

7. 风险

  • 数据模型改动的影响面需要在设计阶段仔细评估,ux_work_graph_run_active 这条唯一约束背后可能还有其他代码路径隐式依赖"一个 task_id+org_node 只有一个活跃 run"这个假设(比如查询任务当前状态的逻辑、WebSocket 推送逻辑),改动前需要逐一排查,不能只看 schema 本身。
  • 控制台可视化工作量可能比后端数据模型改动更大——FR-4 涉及新的 UI 交互(选择分叉点、对比分支列表),需要跟现有 docs/wireframes/ 里的 v_task_detail 类页面设计对齐,这部分工作量需要在设计阶段单独评估,不要低估。

8. 开放问题(留给详细设计阶段回答)

  • [ ] parent_run_id/fork_of_node_exec_id 这类新字段的具体 schema 设计,以及 ux_work_graph_run_active 约束要怎么调整。
  • [ ] 分叉操作的触发入口——只在控制台里做,还是也要开一个后端 API 供其他方式调用?
  • [ ] FR-5"是否采纳分叉分支结果"的人工操作具体是什么交互(一个"设为主线"按钮?还是纯只读展示,人工另外手动处理)?
  • [ ] 是否需要给分叉分支设置生命周期/清理策略(长期堆积的调试分支会不会需要定期清理,类似原始执行日志的保留天数机制)?