-- 0018_chat_message_turn_id: 给聊天消息挂上「它属于哪一路回合」(design/15 §2.5.9)。 -- -- 并行回合(design/15 §2.5)落地后,一个会话可以同时有两条在飞的**路**,同一个 -- 会话的行因此不再能靠「顺序」唯一回答「这条回复在答哪一句提问」。turn_id 就是 -- 编排器生成、该路全部流帧与 `turn_begin` 帧壳同值的那一份权威回合标识 -- (internal/services/gateway 的 turnLeg.turnID,经 loop.WithTurnID 盖在帧上)。 -- -- 用途(客户端依赖):① 「回复 ↔ 提问」精确配对 —— 同一 turn_id 的 user 行与 -- assistant 行互为配对;② 重连 / 回放时从历史行恢复「回复侧分组小标」。 -- -- 它**可选**:'' 意味着「这一行不属于任何已知的回合」—— 加入本列之前的全部历史行、 -- 以及未经编排器的写入路径(IM 镜像等)都是这种情形,读起来因此与加入本列之前 -- 逐字相同(DTO 里该键缺席)。列口径与同族可选列(persona_id / client_msg_id / -- document)完全一致:NOT NULL DEFAULT '',**绝不用 NULL** —— 于是「旧行」与 -- 「没写」是同一个值,读取侧只有一种空情形要处理。这一列因此**不需要索引**: -- 它只被随行读出,没有任何按它过滤/排序的查询。 -- -- 写入是**两段式**(与 document 列同一手法):消息在它所属的那一路已经存在时 -- (新起的一路、以及助手回复)就把 turn_id 写进 INSERT;排队 / 被转向吃进的消息 -- 在落库那一刻还没有自己的路,由它**实际被处理的那一路**在起跑/消费时补写同一 -- 列(先到先得,见 internal/runtime/history/turn_id.go 的 SetMessagesTurnID)。 -- -- 0003 的 chat_messages 表在此已存在,故 ALTER ADD COLUMN 是非破坏性的正确一步; -- 全新库由 internal/runtime/history.NewStore 幂等地前滚(含该列)。表名沿用复数 -- 口径(援引 AGENTS §10 已登记的例外)。 ALTER TABLE chat_messages ADD COLUMN turn_id TEXT NOT NULL DEFAULT '';