diff --git a/internal/services/gateway/broadcast.go b/internal/services/gateway/broadcast.go index 3fd358f..07cdbb2 100644 --- a/internal/services/gateway/broadcast.go +++ b/internal/services/gateway/broadcast.go @@ -21,14 +21,14 @@ import ( // 当 hub 接好广播时它是安全空操作。两种帧类型都携带消息内容, // 因此 design/11 §7.1 帧门只把它们投递给 owner/admin 连接。 // -// 它**绝不**带 `proactive` 键:普通消息的帧顶永远没有那个结构化判据,客户端因此 +// 它**绝不**带 `proactive` 键:普通消息的帧的 `data` 里永远没有那个结构化判据,客户端因此 // 不可能把普通消息误判成主动消息去弹通知(切片 P0b 的「不误伤」那一条)。主动推送 // 那条路走 BroadcastProactivePersisted(proactive.go)。 func (s *Server) BroadcastPersisted(msg history.Message) { s.broadcastPersisted(msg, false) } -// broadcastPersisted 是上面两个入口共用的帧构造与扇出:proactive 决定帧顶是否带上 +// broadcastPersisted 是上面两个入口共用的帧构造与扇出:proactive 决定帧的 `data` 里是否带上 // 「这是她主动说的话」的结构化判据 `proactive:true`(切片 P0b,见 proactive.go 的 // BroadcastProactivePersisted)。两条入口走**同一个**函数,因此帧类型推导、thinking // 抹除与角色门(design/11 §7.1)逐字一致 —— 差别只有那一个键。 diff --git a/internal/services/gateway/proactive.go b/internal/services/gateway/proactive.go index ba0c306..8556e97 100644 --- a/internal/services/gateway/proactive.go +++ b/internal/services/gateway/proactive.go @@ -93,7 +93,7 @@ func (s *Server) SendProactive(ctx context.Context, msg adapter.UnifiedMessage) } // BroadcastProactivePersisted 把一条**刚落进主会话的主动消息**广播为**带结构化判据**的 -// `message_persisted` 帧(切片 P0b)。判据就是帧顶的 `proactive:true`:它是「她主动说的 +// `message_persisted` 帧(切片 P0b)。判据就是帧的 `data` 里的 `proactive:true`:它是「她主动说的 // 话」在线上唯一的标记,客户端据此走与 `proactive_message` **同一套**提醒逻辑(非前台才 // 补一发通知,同一条只提醒一次),于是「她该不该提醒我」**与后端走了哪条路由无关** // (主人 2026-10-05 裁定 C)。 @@ -104,7 +104,7 @@ func (s *Server) SendProactive(ctx context.Context, msg adapter.UnifiedMessage) // ⇒ 实际最可能发生的路径恰恰不弹。 // // 判据为什么不会误伤普通消息:只有主动推送这条通道会调用本函数,普通消息(Web 回合 / -// IM 回复 / IM 镜像)走的仍是 BroadcastPersisted,那条路径**永不**往帧顶放该键。 +// IM 回复 / IM 镜像)走的仍是 BroadcastPersisted,那条路径**永不**往帧的 `data` 里放该键。 // 帧类型仍是 `message_persisted`,因此角色门(design/11 §7.1)与普通持久化帧逐字相同。 func (s *Server) BroadcastProactivePersisted(msg history.Message) { s.broadcastPersisted(msg, true) diff --git a/internal/services/gateway/proactive_im_notify_test.go b/internal/services/gateway/proactive_im_notify_test.go index 9c1f9b0..5277c57 100644 --- a/internal/services/gateway/proactive_im_notify_test.go +++ b/internal/services/gateway/proactive_im_notify_test.go @@ -15,13 +15,13 @@ import ( // 路由过去只发普通 `message_persisted` —— 浏览器通知挂在 `proactive_message` 上,于是 // **实际最可能发生的路径恰恰不弹**。主人裁定「她说话该不该提醒我,与走哪条路由无关」。 // -// 判据是**结构化**的:主动推送在落库之后追加一条**帧顶带 `proactive:true`** 的 +// 判据是**结构化**的:主动推送在落库之后追加一条**帧的 `data` 里带 `proactive:true`** 的 // `message_persisted`(gateway.BroadcastProactivePersisted)。普通消息(Web 回合 / IM // 回复 / IM 镜像)走的仍是 BroadcastPersisted,那条路**永不**放该键 —— 因此普通消息 // 不可能被误判成主动消息。下面分别钉住两端:IM 路由真的发得出这条带判据的帧,而普通 // 广播路径永远发不出它。 -// dataFlag 报告帧顶是否带 proactive 判据(以及它的值)。 +// dataFlag 报告帧的 `data` 里是否带 proactive 判据(以及它的值)。 func dataFlag(t *testing.T, ev wsEvent) (any, bool) { t.Helper() data, ok := ev.Data.(map[string]any) @@ -49,7 +49,7 @@ func frameMessageID(t *testing.T, ev wsEvent) string { // TestChannelBridge_SendProactive_EmitsProactiveFlaggedFrame 是切片 P0b 的端到端证据: // IM 路由的主动消息**既**照旧广播普通镜像帧(P0 起的行为,任何客户端都能渲染),**又** -// 追加一条帧顶带 `proactive:true`、并携带落库行权威 id 的帧 —— 前端据此走与 +// 追加一条帧的 `data` 里带 `proactive:true`、并携带落库行权威 id 的帧 —— 前端据此走与 // `proactive_message` 同一套提醒逻辑(非前台补一发通知,两帧同 id ⇒ 去重后一个气泡、 // 一次提醒)。它同时守住 P0 的红线:主会话里仍**恰好一条**(绝无双份)。 func TestChannelBridge_SendProactive_EmitsProactiveFlaggedFrame(t *testing.T) { @@ -94,7 +94,7 @@ func TestChannelBridge_SendProactive_EmitsProactiveFlaggedFrame(t *testing.T) { t.Fatalf("普通镜像帧带上了 proactive=%v,want 该键缺席(判据只属于主动那条)", v) } - // 第二帧:主动帧 —— 帧顶带判据,且消息 id 就是落库行的权威 id。 + // 第二帧:主动帧 —— 帧的 `data` 里带判据,且消息 id 就是落库行的权威 id。 second := readFakeFrame(t, owner) if second.Type != "message_persisted" { t.Fatalf("frame 2 type = %q, want message_persisted(帧类型不变,只多一个结构化键)", second.Type) @@ -114,7 +114,7 @@ func TestChannelBridge_SendProactive_EmitsProactiveFlaggedFrame(t *testing.T) { } // TestServer_BroadcastPersisted_NeverCarriesProactiveFlag 是「不误伤」那一条的后端对照: -// 普通广播路径(Web 回合 / IM 回复 / IM 镜像全走它)**永远**不在帧顶放 proactive 判据, +// 普通广播路径(Web 回合 / IM 回复 / IM 镜像全走它)**永远**不在帧的 `data` 里放 proactive 判据, // 因此客户端不可能把普通消息误判成主动消息去弹通知。同一行改走主动那条入口时,判据才 // 出现 —— 两条入口的差别只有这一个键。 func TestServer_BroadcastPersisted_NeverCarriesProactiveFlag(t *testing.T) { @@ -132,7 +132,7 @@ func TestServer_BroadcastPersisted_NeverCarriesProactiveFlag(t *testing.T) { t.Fatalf("frame type = %q, want message_persisted", plain.Type) } if v, has := dataFlag(t, plain); has { - t.Fatalf("普通消息的帧顶带上了 proactive=%v,want 该键缺席(否则会被误判成主动消息)", v) + t.Fatalf("普通消息的帧的 `data` 里带上了 proactive=%v,want 该键缺席(否则会被误判成主动消息)", v) } env.srv.BroadcastProactivePersisted(row)