Skip to content

Latest commit

 

History

History
96 lines (65 loc) · 9.47 KB

File metadata and controls

96 lines (65 loc) · 9.47 KB

测试完成,环境已清理。以下是完整结论。

回退范围测试结果(在重启后的真实运行进程里实测)

方法说明/rollback 是人类命令,我没有对应的 model tool 无法直接输入它;所以我注册了一个动态探测插件,在当前运行进程里直接驱动 ctx.rollback.rollbackTo(),在合成 scratch 会话上跑完整边界矩阵(有效 fork 产生的是真实子会话,会真实落盘)。

1. 加载状态:全部就位 ✅

  • ctx.rollback 已加载(此前 Inspect 服务目录看不到它,是因为那个目录是发布期静态清单,不反映动态安装——运行态探测才是权威)
  • /rollback 命令已注册(描述:Fork a child session through a history boundary
  • checkpoint 锚点自动记录生效:每个 turn/end 后自动追加 rollback/checkpoint(turn 0 → seq 4,turn 1 → seq 9;后续真实会话中 turn 10、11 亦各追加一条,格式正确)

2. 边界矩阵:全部符合设计 ✅

探针 boundary 结果
负数 -1 boundary 拒绝
超出日志末端 21 boundary 拒绝
NaN NaN boundary 拒绝
落在 open turn 内 3 / 8 boundary 拒绝("ends inside open turn N")
落在 compaction bracket 内 6 boundary 拒绝
合法:turn 结束处 4 ✅ fork 出子会话,标记 rollback/start(11)→end(12)
合法:checkpoint 锚点 9 ✅ fork 出子会话,标记 13/14
agent 忙(runMaintenance 抛错) busy 拒绝
信号已中止 ❌ 拒绝(reason: probe-abort)

事务闭合验证通过:每次成功 fork 都在源会话留下成对的 rollback/startrollback/end,无悬空 bracket。

3. 一个值得注意的发现

每个新会话的前 3 个事件是 harness 自动附加的会话策略初始化事件(permission/presetsandbox/modeapproval/policy)。这意味着 seq 0-2 也是合法的"轮间"回退点——fork 出来的是一个只有策略事件、没有任何 turn 的空历史子会话。这是符合设计的(boundary 只要求不落在 open turn/compaction 内),但和你之前说明书里的"boundary 只能是原始 seq 或 checkpoint"结合起来,边界语义更完整了。

4. 清理

  • 探测插件 rbp-1 已停止(版本保留,需要复测可随时重启)
  • 14 个合成会话文件(4 个 scratch + 10 个 fork 子会话)已从 .dsh/sessions 删除,只留你的真实会话;live store 里的 fork 子会话会在下次重启后自然消失(已通过重启验证)

最终结论

插件在当前 harness 里完全可用。 你现在可以直接在 GUI 输入框试:/rollback(无参数,看用法和 checkpoint 列表)、/rollback checkpoint(回到最近锚点)、/rollback checkpoint:<turn>/rollback <seq>。回滚后子会话 ID 会显示在结果里,但按已知限制,GUI 里激活子时间线的面板还没做。

结合轨迹(trajectory)来设计的话,我的完整方案如下——核心思路:把回退做成轨迹本身的原生交互,而不是旁边另开一个面板。理由:checkpoint 锚点、rollback/start|end 标记本来就是会话日志里的事件,它们有精确的 seq 和时间位置,放在轨迹里既有真实锚点,又天然只暴露"合法边界"。

〇、实现前必读:rc.6 运行时 API 核查结果

方案落地前对 rc.6 全局安装做了逐项 API 核查,事实基础如下:

方案依赖的能力 核查结果 说明
轨迹扩展机制 ctx.conversationEvents.register(definition) 轨迹内所有节点的真实接入点,不是 Slot。definition 含 match(按事件类型匹配)/start/update/buildViewNode/publication 字段。trajectory 包现有 8 个定义:assistant、turn-end、compaction 括号、session-end、inbox、message、request-header、tool——rollback 锚点/括号照抄 compaction 定义的模式即可
执行回滚 commands.execute(Remote RPC) 走完整命令链路(command/run、command/done 事件 + sourceCommandId 记录),复用事务与 invariant 全链路
切换活跃会话 sessions.open(id)(client 侧) 已有现成先例:sessions.fork({ sessionId, atSeq, increaseTitle }).then(childId => sessions.open(childId))
谱系数据 sessionQuery.traceSession(sessionId) 公共方法存在;fork 子会话 header 记录 parentSession: 源会话id(dsh-session L1851),谱系无需新造
结构化 childSessionId 缺失,MVP 前置 commands.execute('rollback') 返回的 CommandResult 只有 { kind, text, sourceEventSeq },childSessionId 被埋在 text 字符串里("new child session xxx")。自动激活需要给 command-rollback 的返回增加 childSessionId 字段(源码约 3 行),重建后重部署——这是动手前的第一个补丁

一、轨迹内的三个视觉层

1. 锚点节点(rollback/checkpoint → 可点击回退点)

每个 rollback/checkpoint 事件渲染成 turn 之间的一个小节点(圆点/旗帜,带 turn 号和相对时间),位置精确落在它所属的 turn 块尾部——因为锚点本身就是在 turn/end 之后追加的。

实现方式:新插件注册 conversationEvents 定义,match: event.type === 'rollback/checkpoint'buildViewNode 产出锚点节点(与 turn-end 节点同层,落在 turn 块尾)。

  • 悬停:显示 "turn N 处锚点 · 回退到此处" 工具条
  • 点击:弹确认框(见下),确认后执行回滚
  • 已派生标记(增量建议):源会话里被 fork 过的锚点显示"已从这里派生子会话"徽标——数据现成,rollback/startboundarySeq 恰好指向被使用的锚点 seq,用户一眼知道哪些锚点用过了,避免重复派生
  • 这个设计有个关键红利:锚点是"构造上合法"的边界(只在轮间产生),UI 只暴露合法回退点,用户永远不会撞上 boundary 错误——手敲 seq 那种易错路径留给 /rollback <seq> 命令作为高级入口。

2. 交易括号(rollback/start → rollback/end → 子会话入口)

rollback/start…end 渲染为一个括起来的 span(视觉语言与 compaction bracket 对称——compaction 的括号定义在 trajectory 包里已存在,直接复用那套样式和 definition 结构),span 上带一个小徽标:"衍生出子会话 xxx",点击直接跳转/打开子会话。这样"源会话上发生了什么"在轨迹里一目了然:历史没变,只多了一对成对的括号。

3. 谱系条(子会话打开后的"从哪来")

打开子会话时,轨迹顶部显示一条 lineage 面包屑:"← 从 [源会话] 的 turn N 处派生"(数据端已经有 sessionQuery.traceSession 返回谱系,fork 时 parentSession 已记录,不用新造)。子会话的轨迹 = 源历史前缀 + session/end-seed,锚点节点原样继承,交互一致。

二、关键交互细节

  1. 确认弹窗(必做):目标 turn、将会派生子会话(源历史不变)、以及诚实提示"工作区/沙箱文件状态不会回退"——这是已知限制里最大的用户陷阱,UI 必须在确认时讲清楚。可选:给出"将放弃 turn N 之后共 M 轮内容"的预览。
  2. 禁用态
    • 轮次正在运行 → 所有锚点禁用,提示"agent 忙碌中,回退需空闲"
    • 锚点落在 compaction bracket 内 → 该锚点禁用并说明原因。注意:核查后确认 rollback/checkpointqueueMicrotaskturn/end 同一宏任务内追加的,而 compaction/start 是后续异步维护任务,时序上 checkpoint 必然先落——该场景实际几乎不会发生。此禁用逻辑是低成本防御(保险,而非已知故障模式),保留但不阻塞 MVP。
  3. 执行 + 自动激活:确认 → client 调 commands.execute(Remote RPC,复用事务/invariant 全链路)→ 从返回的 childSessionId(需先完成"〇"表中的前置补丁)→ sessions.open(childId) 切换活跃会话。这一步顺手就把 deferred 的另一半("切换 UI 活跃会话")也交付了——积木都是现成的。
  4. 失败反馈RollbackError 的 code(busy/boundary/persistence…)映射成人类文本显示在锚点旁的内联提示,而不是只丢进结果文本。
  5. 源会话保持在侧栏(增量建议):sessions.open 只切换不替换,源会话仍在会话列表中——天然支持"两边对照",V3 的双侧对比可以更早以弱形式出现。

三、渐进路线

阶段 内容 依赖
前置补丁 command-rollback 返回增加 childSessionId 字段,重建重部署 源码 3 行 + 重部署
MVP(最小可用) 锚点节点渲染(conversationEvents 定义)+ 点击确认回滚 + 成功自动打开子会话 + 失败内联提示 + 已派生标记 前置补丁 + commands.execute + sessions.open
V2 交易括号 span + 子会话徽标(源会话视角)+ lineage 面包屑(子会话视角) rollback/start|end 事件定义、traceSession
V3 谱系树(多代派生)、双侧对比、工作区快照提示(等 WorkspaceSnapshotter seam) 更大;快照能力建议等官方 seam,收益后置

设计上的两个原则:锚点即合法边界(UI 替用户消掉 boundary 错误类)、与 compaction 渲染对称(复用视觉语言和 definition 逻辑)。