DSH / Atlas
2026-06-20rejectedsimplification

Drop durable step boundary events

移除持久化的步骤边界事件

The session log stores `step/start` and `step/end` events even though every step-scoped event already carries `{ turn, step }`: assistant chunks, assistant messages, tool calls, tool results, usage, and errors. `deriveMessages()` ignores step boundaries, ACP ignores them for UI, and the main consumers are invariants, tests, snapshot expected outputs, and crash repair. The rejected argument was that boundary events ma

English

Problem

The session log stores step/start and step/end events even though every step-scoped event already carries { turn, step }: assistant chunks, assistant messages, tool calls, tool results, usage, and errors. deriveMessages() ignores step boundaries, ACP ignores them for UI, and the main consumers are invariants, tests, snapshot expected outputs, and crash repair.

The rejected argument was that boundary events make the log more ceremonial than informative. In practice, step/end is concrete information: a reader can tell whether a model request finished, crashed, or is being repaired without deriving that state from the next event. A bare step/start is likewise useful for a model request that began but produced no chunks before failing.

Proposal

Make the turn the only durable boundary. Remove step/start and step/end from SessionEventMap; keep the numeric step field on events that need grouping. The loop increments the step counter and records step-scoped events with that number, but it no longer appends open/close boundary events. Consumers infer step groups from contiguous events sharing (turn, step).

The invariants plugin should enforce that step-scoped events have valid positive step numbers within an open turn, not that separate boundary records surround them. Crash repair should not synthesize step/end; if an interrupted turn is preserved, the repair path can still close the turn without inventing step boundary records.

Acceptance criteria

  • SessionEventMap no longer includes step/start or step/end.
  • The loop has no closeStep() finalization path.
  • ACP snapshots and persistence contract fixtures stop expecting step-boundary lines.
  • deriveMessages() and replay derive the same message history from step-scoped events.
  • The event taxonomy docs describe turns as the durable boundary and steps as a field on step-scoped records.
  • The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.

What we give up

The log no longer records "a model request started but produced no event before the process died" as a durable fact, and no longer has an explicit "this step completed" marker. That loss is not acceptable while the session log is the durable replay and audit surface.

<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

中文

问题

会话日志存储了 step/startstep/end 事件,尽管每个步骤级事件本身已经携带 { turn, step }:assistant 分片、assistant 消息、工具调用、工具结果、用量和错误。deriveMessages() 忽略步骤边界,ACP(Agent Client Protocol)在 UI 层面也忽略它们,主要消费方是不变式检查、测试、快照预期输出和崩溃恢复。

被否决的论点是:边界事件使日志更像仪式而非信息。实际上,step/end 是具体信息:读者无需从下一个事件推导状态,就能判断一次模型请求是已完成、已崩溃还是正在修复。同样,单独一条 step/start 对于「模型请求已发起但在产生任何分片之前就失败了」的场景也有价值。

提案

将轮次作为唯一的持久化边界。step/startstep/end 将从 SessionEventMap 中移除;在需要分组的事件上保留数值型 step 字段。agent loop(智能体循环)递增步骤计数器并以该编号记录步骤级事件,但不再追加开始与结束边界事件。消费方通过共享 (turn, step) 的连续事件推断步骤分组。

不变式插件应当强制步骤级事件在一个已打开的轮次内具有有效的正整数步骤编号,而非要求独立的边界记录包围它们。崩溃恢复不应合成 step/end;如果一个被中断的轮次被保留,修复路径仍然可以关闭该轮次而无需捏造步骤边界记录。

验收标准

  • SessionEventMap 不再包含 step/startstep/end
  • agent loop 中不再有 closeStep() 终结路径。
  • ACP 快照和持久化约定 fixture(测试前置数据)不再期望步骤边界行。
  • deriveMessages() 和回放从步骤级事件推导出相同的消息历史。
  • 事件分类体系文档将轮次描述为持久化边界,将步骤描述为步骤级记录上的一个字段。
  • 会话格式版本和已记录的 fixture 被刷新;按预发布格式策略,非当前版本的已存储日志被拒绝。

放弃了什么

日志不再将「一次模型请求已发起但进程死亡前未产生任何事件」记录为持久化事实,也不再有显式的「此步骤已完成」标记。在会话日志仍是持久化回放与审计表面的当下,这一损失不可接受。

<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->