feat(gateway): 忙时并行帧层(turn_begin + steer_ack.turnId + interrupt.turnId)

新下行帧 turn_begin:编排器每启动一路恰好一帧(单路 kind:"turn" / 并行路 "parallel"),
data{kind, messageIds, leg, activeTurns};角色门 fail-closed 仅 owner+admin,未进 frameReachesAll。
steer_ack 增帧壳 turnId(与该路 turn_begin 同源)—— 并行下没有它配不上正确的 turn.end。
上行 interrupt.turnId(仅 scope:"current" 生效):精确停某一路;目标不存在 ⇒ 定向
{type:"error",data:{code:"bad_request"}},不广播、不动任何会话;省略 turnId = v0.65 原口径(全部在途路)。
帧序按实现:session_busy / pending_drain → message_persisted(user) → turn_begin
(规范草案 §2.9 的时序图需按实现更正:messageIds 必须是已落库的权威 id,故只能在落库之后)。
测试:编排器侧 + 真 WS/JWT 端到端;变异 5 条(每路一帧 / 帧壳 turnId / 目标化打断 / 取消筛选)。
Docs 待升 v0.66(由监工登记)。
This commit is contained in:
AskaEth committed 2026-10-05 09:38:11 +08:00
1 parent e5cc9c6054
commit c0dc435770
13 files changed
+800 -45

No files matched your search

+2 -2
View File
@@ -12,8 +12,8 @@ import "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
type StateSink func(sessionID, typ string, data map[string]any)
// TurnStateSink 是 StateSink 的**带 turnId 变体**(design/15 §2.5.9):帧壳上的
// `turnId` 是客户端按回合两级分桶的依据,而少数帧(后续切片的 `turn_begin`、
// 以及 `steer_ack`)必须让客户端知道**是哪一路**,只有 data 不足以表达。
// `turnId` 是客户端按回合两级分桶的依据,而少数帧(`turn_begin`、`steer_ack`)
// 必须让客户端知道**是哪一路**,只有 data 不足以表达。
//
// 它刻意是**另一个**回调而不是改 StateSink 的签名:既有接线点(server.go、
// 大量测试)的签名因此零改动,且空 turnID 时明确退化为现签名(帧壳上没有