DSH / Atlas
2026-09-11implementedbug-fixarchived 2026-09-16

Session Controller forks stop at the selected turn end

Session Controller 分叉截到选中轮次的结束事件

A user input enters the durable inbox before its `turn/start`. Extending a completed-turn fork through the following between-turn events can copy the next input's insertion without its later removal. Continuing the child then executes an input from beyond the selected turn.

English

Problem

A user input enters the durable inbox before its turn/start. Extending a completed-turn fork through the following between-turn events can copy the next input's insertion without its later removal. Continuing the child then executes an input from beyond the selected turn.

Decision

The Session Controller copies the contiguous prefix through the selected turn/end, inclusive. Explicit anchors select the first closing event at or after the anchor; omitted and past-end anchors select the last closing event. No event after that closing event belongs to the seed, including queued input, titles, and model settings.

The lower-level SessionStore.fork() retains its explicit stable-event semantics from the log-only event decision. Selecting a completed turn in the controller does not request a later stable event.

Alternatives considered

Stop only at the next inbox event. Event-type exceptions still copy unrelated state changes after the selected turn and require the controller to classify plugin-owned events.

Copy the tail and clear the child's inbox. Clearing adds child events to cancel input that lies outside the requested prefix, while still inheriting other state from after the selected turn.

Consequences

A fork inherits the configuration recorded through its selected turn. Later configuration and title events are excluded. The client may independently assign the child's fork title. Events already inside the selected prefix retain their ordinary replay semantics; this decision does not redefine pending input inserted before the selected closing event.

Verification

Controller tests execute the production loop and check that sending C after forking A excludes the parent's later B from child history and produces one model request. Message, closing-event, omitted, and past-end anchors share this assertion. Model-routing coverage excludes a later configuration change. The Web message-actions snapshot seeds a queued input after the completed turn and verifies the branch action creates a child without that input or its inbox insertion.

中文

Problem

用户输入会先进入持久化 inbox,再出现对应的 turn/start。如果已结束轮次的分叉继续复制后面的轮次间事件,就可能复制下一条输入的入队事件,却没有复制其后的出队事件。继续子会话时,就会执行选中轮次之后的输入。

Decision

Session Controller 复制截至选中 turn/end 的连续前缀,并包含该事件。显式锚点选择位于锚点或其后的第一条结束事件;省略锚点或锚点超出日志末尾时,选择最后一条结束事件。结束事件之后的所有事件均不属于种子,包括排队输入、标题和模型设置。

底层 SessionStore.fork() 保留纯日志事件决策规定的显式稳定事件语义。在控制器中选择已结束轮次,不代表请求复制到更晚的稳定事件。

Alternatives considered

仅在下一条 inbox 事件处停止。 按事件类型添加例外仍会复制选中轮次之后的无关状态变化,并要求控制器分类插件所属的事件。

复制尾部后清空子会话 inbox。 清空操作会添加子会话事件,以取消本来就在所请求前缀之外的输入,同时仍然继承选中轮次之后的其他状态。

Consequences

分叉继承截至选中轮次记录的配置。之后的配置和标题事件被排除。客户端可以独立设置子会话的分叉标题。已经位于选中前缀内的事件保留通常的回放语义;本决策不重新定义在选中结束事件之前入队的待处理输入。

Verification

控制器测试执行生产循环,检查从 A 分叉后发送 C 时,子会话历史不包含父会话后续的 B,并且只产生一次模型请求。消息、结束事件、省略及越界锚点共用此断言。模型路由覆盖排除了后续配置变更。Web 消息操作快照在已结束轮次后植入排队输入,验证分叉操作创建的子会话不包含该输入或其入队事件。