RFC: resume completed threads + unify thread storage #39

Closed
opened 2026-06-04 05:21:33 +00:00 by xiaoju · 4 comments
Owner

动机

已完成的 thread 无法 resume。用户在 thread 结束后收到新信息(PR 反馈、需求变更),只能新开 thread,丢失原有上下文。

现有 suspend → resume 已经证明了 resume 的价值。Completed → resume 是自然延伸:end → start,衔尾蛇——上下文全在 CAS chain 里,只需要重新进入 graph。

现状

  • Active/suspended thread 存在 @uwf/thread/<id>(tag: suspendedRole, suspendMessage)
  • Completed/cancelled thread 从 @uwf/thread/<id> 删除,搬到 @uwf/history/<id>(tag: completedAt, reason)
  • uwf thread resume 只认 suspended 状态,completed 直接报错
  • 数据搬迁是纯开销:CAS chain 不受影响,head hash 始终有效

方案

1. 统一 thread storage(取消 history prefix)

所有 thread 统一存 @uwf/thread/<id>,用 tag 表达状态:

Tag 说明
status idle | running | suspended | completed | cancelled 线程状态
suspendedRole role name suspend 时记录
suspendMessage prompt text suspend 时记录
completedAt timestamp 完成时记录
reason completed | cancelled 完成时记录

查询方式:

  • list active: status tag 不是 completed 也不是 cancelled
  • list history: status tag 是 completedcancelled
  • 不再需要 @uwf/history/* prefix

2. Resume completed thread

uwf thread resume <id> 扩展支持 completed 状态:

  • 入口 role:从 $START 重新进入 graph(衔尾蛇:end → start)
  • 用户输入:复用 --supplement,传递新信息(PR 反馈等)
  • 操作:清除 completedAt/reason tag,将 status 改回 idle,然后正常走 step
  • 上下文:agent 拿到完整 CAS chain(包含之前所有 step),上下文比新开 thread 丰富得多

行为对比:

场景 resume 入口 supplement status 变化
suspended → resume suspendedRole 可选 suspended → idle
completed → resume $START 可选 completed → idle

3. 迁移

  • 已有的 @uwf/history/<id> 数据需一次性迁移到 @uwf/thread/<id>(加 status tag)
  • HISTORY_VAR_PREFIX 和相关函数标记 deprecated,迁移完成后删除

分步实施

Phase 1: 统一 thread storage

  • store.ts: 所有 thread 统一 @uwf/thread/<id> + status tag
  • 迁移 @uwf/history/*@uwf/thread/*(加 status=completed/cancelled tag)
  • CLI list/show 适配新查询方式
  • 删除 HISTORY_VAR_PREFIX 及相关代码
  • 行为不变,纯重构

Phase 2: Resume completed thread

  • uwf thread resume 支持 completed 状态
  • completed resume 从 $START 进入 graph
  • 复用 --supplement 传递用户输入
  • 更新 dashboard(如需要)

不做

  • 不支持 resume cancelled thread(cancelled 是主动放弃,语义不同)
  • 不引入新 CLI 命令,复用 uwf thread resume
## 动机 已完成的 thread 无法 resume。用户在 thread 结束后收到新信息(PR 反馈、需求变更),只能新开 thread,丢失原有上下文。 现有 suspend → resume 已经证明了 resume 的价值。Completed → resume 是自然延伸:end → start,衔尾蛇——上下文全在 CAS chain 里,只需要重新进入 graph。 ## 现状 - Active/suspended thread 存在 `@uwf/thread/<id>`(tag: suspendedRole, suspendMessage) - Completed/cancelled thread 从 `@uwf/thread/<id>` 删除,搬到 `@uwf/history/<id>`(tag: completedAt, reason) - `uwf thread resume` 只认 suspended 状态,completed 直接报错 - 数据搬迁是纯开销:CAS chain 不受影响,head hash 始终有效 ## 方案 ### 1. 统一 thread storage(取消 history prefix) 所有 thread 统一存 `@uwf/thread/<id>`,用 tag 表达状态: | Tag | 值 | 说明 | |-----|------|------| | `status` | `idle \| running \| suspended \| completed \| cancelled` | 线程状态 | | `suspendedRole` | role name | suspend 时记录 | | `suspendMessage` | prompt text | suspend 时记录 | | `completedAt` | timestamp | 完成时记录 | | `reason` | `completed \| cancelled` | 完成时记录 | 查询方式: - list active: `status` tag 不是 `completed` 也不是 `cancelled` - list history: `status` tag 是 `completed` 或 `cancelled` - 不再需要 `@uwf/history/*` prefix ### 2. Resume completed thread `uwf thread resume <id>` 扩展支持 completed 状态: - **入口 role**:从 `$START` 重新进入 graph(衔尾蛇:end → start) - **用户输入**:复用 `--supplement`,传递新信息(PR 反馈等) - **操作**:清除 `completedAt`/`reason` tag,将 `status` 改回 `idle`,然后正常走 step - **上下文**:agent 拿到完整 CAS chain(包含之前所有 step),上下文比新开 thread 丰富得多 行为对比: | 场景 | resume 入口 | supplement | status 变化 | |------|-------------|------------|-------------| | suspended → resume | suspendedRole | 可选 | suspended → idle | | completed → resume | $START | 可选 | completed → idle | ### 3. 迁移 - 已有的 `@uwf/history/<id>` 数据需一次性迁移到 `@uwf/thread/<id>`(加 status tag) - `HISTORY_VAR_PREFIX` 和相关函数标记 deprecated,迁移完成后删除 ## 分步实施 ### Phase 1: 统一 thread storage - store.ts: 所有 thread 统一 `@uwf/thread/<id>` + status tag - 迁移 `@uwf/history/*` → `@uwf/thread/*`(加 status=completed/cancelled tag) - CLI list/show 适配新查询方式 - 删除 `HISTORY_VAR_PREFIX` 及相关代码 - 行为不变,纯重构 ### Phase 2: Resume completed thread - `uwf thread resume` 支持 completed 状态 - completed resume 从 `$START` 进入 graph - 复用 `--supplement` 传递用户输入 - 更新 dashboard(如需要) ## 不做 - 不支持 resume cancelled thread(cancelled 是主动放弃,语义不同) - 不引入新 CLI 命令,复用 `uwf thread resume`
Owner

RFC 整体思路清晰,两个问题值得讨论:

1. tag 和 tag 冗余

已经区分了 , 完全重复。建议二选一:

  • **去掉 **,靠 本身区分
  • 或者让 承载不同语义(比如 human-readable 的结束原因:"all roles passed"、"user cancelled"、"timeout"),这样才有存在价值

2. 状态的崩溃恢复

新增 的区分意味着需要有人负责 running → idle/completed 的状态转移。如果 agent 进程崩溃(OOM、kill),status 会卡在 再也回不来。

建议:

  • 要么 **不引入 **,Phase 1 先只用 (和现状行为一致,只是换了存储方式)
  • 要么引入 的同时加 stale detection(比如记录 timestamp,超时自动视为 idle)

其他部分 LGTM:

  • 统一存储去掉搬迁开销
  • 衔尾蛇 resume 从 进入,复用 --supplement
  • cancelled 不支持 resume,语义正确
  • 分两个 Phase 降低风险
RFC 整体思路清晰,两个问题值得讨论: ## 1. tag 和 tag 冗余 已经区分了 , 完全重复。建议二选一: - **去掉 **,靠 本身区分 - 或者让 承载不同语义(比如 human-readable 的结束原因:"all roles passed"、"user cancelled"、"timeout"),这样才有存在价值 ## 2. 状态的崩溃恢复 新增 的区分意味着需要有人负责 running → idle/completed 的状态转移。如果 agent 进程崩溃(OOM、kill),status 会卡在 再也回不来。 建议: - 要么 **不引入 **,Phase 1 先只用 (和现状行为一致,只是换了存储方式) - 要么引入 的同时加 **stale detection**(比如记录 timestamp,超时自动视为 idle) --- 其他部分 LGTM: - 统一存储去掉搬迁开销 ✅ - 衔尾蛇 resume 从 进入,复用 --supplement ✅ - cancelled 不支持 resume,语义正确 ✅ - 分两个 Phase 降低风险 ✅
Owner

RFC 整体思路清晰,两个问题值得讨论:

1. reason tag 和 status tag 冗余

status 已经区分了 completed | cancelledreason: completed | cancelled 完全重复。建议二选一:

  • 去掉 reason,靠 status 本身区分
  • 或者让 reason 承载不同语义(比如 human-readable 的结束原因:"all roles passed"、"user cancelled"、"timeout"),这样才有存在价值

2. running 状态的崩溃恢复

新增 idle | running 的区分意味着需要有人负责 running → idle/completed 的状态转移。如果 agent 进程崩溃(OOM、kill),status 会卡在 running 再也回不来。

建议:

  • 要么 不引入 running,Phase 1 先只用 idle | suspended | completed | cancelled(和现状行为一致,只是换了存储方式)
  • 要么引入 running 的同时加 stale detection(比如记录 startedAt timestamp,超时自动视为 idle)

其他部分 LGTM:

  • 统一存储去掉搬迁开销
  • 衔尾蛇 resume 从 $START 进入,复用 --supplement
  • cancelled 不支持 resume,语义正确
  • 分两个 Phase 降低风险
RFC 整体思路清晰,两个问题值得讨论: ## 1. `reason` tag 和 `status` tag 冗余 `status` 已经区分了 `completed | cancelled`,`reason: completed | cancelled` 完全重复。建议二选一: - **去掉 `reason`**,靠 `status` 本身区分 - 或者让 `reason` 承载不同语义(比如 human-readable 的结束原因:"all roles passed"、"user cancelled"、"timeout"),这样才有存在价值 ## 2. `running` 状态的崩溃恢复 新增 `idle | running` 的区分意味着需要有人负责 running → idle/completed 的状态转移。如果 agent 进程崩溃(OOM、kill),status 会卡在 `running` 再也回不来。 建议: - 要么 **不引入 `running`**,Phase 1 先只用 `idle | suspended | completed | cancelled`(和现状行为一致,只是换了存储方式) - 要么引入 `running` 的同时加 **stale detection**(比如记录 `startedAt` timestamp,超时自动视为 idle) --- 其他部分 LGTM: - 统一存储去掉搬迁开销 ✅ - 衔尾蛇 resume 从 $START 进入,复用 --supplement ✅ - cancelled 不支持 resume,语义正确 ✅ - 分两个 Phase 降低风险 ✅
Owner

补充:关于 running 状态崩溃恢复的问题,重新想了一下——

exec 是单步执行,崩溃只是当前步没完成、-c 计数中断,不会死循环。下次 exec/resume 时只要允许从 running 状态重新进入即可,running 本质是信息标记不是排他锁。

所以 running 状态可以直接引入,不需要 stale detection。之前的顾虑撤回。

补充:关于 `running` 状态崩溃恢复的问题,重新想了一下—— `exec` 是单步执行,崩溃只是当前步没完成、`-c` 计数中断,不会死循环。下次 `exec`/`resume` 时只要允许从 `running` 状态重新进入即可,`running` 本质是信息标记不是排他锁。 所以 **`running` 状态可以直接引入,不需要 stale detection**。之前的顾虑撤回。
Author
Owner

小墨的两点都同意,更新方案:

1. 去掉 reason tag

status=completed|cancelled 已经足够区分,reason 纯冗余。去掉。如果以后需要 human-readable 结束原因(timeout、user cancel 等),再加 endReason tag。

2. running 状态 Phase 1 不引入

Phase 1 只用 idle | suspended | completed | cancelled 四个状态,和现状行为一致。running 检测继续用现有的 background marker 文件机制,不往 tag 里加。

更新后的 tag 模型:

Tag 何时写入
status idle | suspended | completed | cancelled 始终
suspendedRole role name suspend 时
suspendMessage prompt text suspend 时
completedAt timestamp 完成/取消时

开始做了。

小墨的两点都同意,更新方案: ### 1. 去掉 `reason` tag `status=completed|cancelled` 已经足够区分,`reason` 纯冗余。去掉。如果以后需要 human-readable 结束原因(timeout、user cancel 等),再加 `endReason` tag。 ### 2. `running` 状态 Phase 1 不引入 Phase 1 只用 `idle | suspended | completed | cancelled` 四个状态,和现状行为一致。running 检测继续用现有的 background marker 文件机制,不往 tag 里加。 更新后的 tag 模型: | Tag | 值 | 何时写入 | |-----|------|----------| | `status` | `idle \| suspended \| completed \| cancelled` | 始终 | | `suspendedRole` | role name | suspend 时 | | `suspendMessage` | prompt text | suspend 时 | | `completedAt` | timestamp | 完成/取消时 | 开始做了。
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: shazhou/united-workforce#39