feat(gateway,loop): 忙时并行回合基础管线(I1′ 有条件放宽)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- turnId 由编排器生成、loop Run 复用(硬前提:否则 turn_begin 与流帧各铸一个,客户端两级分桶立即失配)
- StateSink 增带 turnId 的变体(未接线时退化为现签名,不丢状态帧)
- 上行 chat.parallel(WS chat 帧 + REST 三入口同形;省略 = 逐字不变)
- 单会话额度默认 2 / 硬上限 3 / <2 关闭;额度满或未声明 ⇒ 落回既有排队语义
- I1′:checkpoint 归属、历史配对、忙态计数三处从会话级单例升为路级
- session_idle 收紧为「所有在途路收尾且 pending 空」(仅并行时生效;单路逐字不变)
- 预留接口(形状定死、无实现无接线):tool/legfile.go 的 FileClaimer / ErrFileBusy / WithLegID
- 顺手修一个真 bug:并行路 ctx 派生自提交请求作用域,REST 同步入口一写响应就把它连坐杀死(改 context.WithoutCancel 派生)
- 23 个新用例(含 -race -count=3);变异 6 条(turnId 复用 / idle 时点 / 并行升级 / checkpoint 键 / 编排器交 id / pending_ack reason)
- 契约变更点 10 条待监工升 v0.65(本人未改 Docs/**)
This commit is contained in:
AskaEth committed 2026-10-05 09:18:50 +08:00
1 parent 7d2ff74cfc
commit 89e13cf53d
23 files changed
+1921 -275

No files matched your search

@@ -28,8 +28,8 @@ import (
// 三类**不补**的情形(边界,与既有语义不冲突):
// - 被 interrupt 取消的回合:用户意图就是「停下」,此刻补一条「我出问题了」
// 反而违背那条指令;
// - 被更新的回合取代的陈旧代次(st.gen 已前进):那次收尾不属于当前会话状态,
// 补消息会落到别人的回合里;
// - 已经失效的路(被 interrupt 取消后又被清空、或会话从未登记过它):那次收尾
// 不属于当前会话状态,补消息会落到别人的回合里;
// - 静默内部流程(backthink、子代理):它们各自驱动 loop,从不经过本编排器,
// 因而天然不受这条保证影响(它们的产出只进内部状态,不进聊天历史)。
@@ -37,13 +37,17 @@ import (
// internal/runtime/turnreply 统一给出(IM 渠道桥走另一条编排路径,两边必须是
// 同一份诚实说明;各写一套迟早漂移)。本文件只负责判定**这次收尾该不该落库**。
// turnIsCurrent 报告该代次是否仍是该会话的当前代次。陈旧代次(已被 interrupt
// 或更新的顶层回合取代)意味着这次收尾属于**上一个**回合,不该再往历史里写。
// turnIsCurrent 报告该路是否仍是该会话的**在途路**。已失效的路(被打断、被
// interrupt-all 清空、或会话从未登记过它)意味着这次收尾属于**一条已经不存在的
// 回合**,不该再往历史里写。
//
// 判据从旧的「世代号等于会话当前代次」改成「这条路的世代号还在 st.legs 里」:
// 真并行下启动第二路会给它分配一个新世代号,但**绝不会**让第一路失效
// (design/15 §2.5.9 —— 这正是旧的会话级单例代次做不到的那件事)。
func (o *Orchestrator) turnIsCurrent(sessionID string, gen uint64) bool {
o.mu.Lock()
defer o.mu.Unlock()
st := o.states[sessionID]
return st != nil && st.gen == gen
return o.legLocked(o.states[sessionID], gen) != nil
}
// persistTurnReply 是本回合助手回复的**唯一落库入口**(completeTurn 的收尾第一