docs(gateway): WS 帧门相关 11 个文件的注释改中文(只改注释,代码零字节改动)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人 2026-10-03 拍板「注释一律中文」。本次先把本会话动过的文件就地翻;
不做全仓一次性翻译,存量按包分批排期(见 工作小结.md 待办)。

范围:ws_frame_scope.go / ws_frame_scope_test.go / broadcast.go / ws_hub_turn.go /
ws_hub_message_event.go / session_read.go / server.go / ws_hub_stream_scope_test.go /
history_write_broadcast_test.go / mood_test.go / extra_test.go
含中文的整行注释 18 → 382。

校验(判据 = "只改注释、零代码字节改动"):
- `git diff -U0 | 排除 +++/--- | 排除注释行/空行/行尾注释` → 为空
- 独立实现:两侧剥掉注释后逐行比对代码文本 → 11/11 文件全等,不一致数 0
- 行尾注释 7 行逐条列出,代码列与原文逐字节相同(gofmt 未要求重排)
- gofmt -l 干净;全树 go build/vet 绿;go test ./... = 58 包 ok / 0 FAIL;
  linux/amd64 + darwin/arm64 交叉编译绿
语义未删减:部分折行英文长句合并为更少行,故整行注释数 438 → 399。
This commit is contained in:
AskaEth committed 2026-10-03 22:07:56 +08:00
1 parent 770a83c3fb
commit fe1ff33115
11 files changed
+392 -431

No files matched your search

@@ -14,45 +14,42 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// These tests lock the design/11 §7.1 frame gate: a viewer connection may follow
// the session state machine but must never receive content, while owner and admin
// keep both classes. frameScopeCases is the design/11 §7.1 table in code — every
// frame type named there, published on the subject that really produces it — so a
// classification mistake (a content frame made public, or a state signal made
// private) fails here instead of leaking silently.
// 这些测试锁住 design/11 §7.1 的帧门:viewer 连接可以跟随会话状态机,
// 但绝不可收到内容,而 owner 与 admin 两类都保留。frameScopeCases 是
// design/11 §7.1 表在代码中的体现 —— 表中点名的每种帧类型,都发布在
// 真正产生它的主题上 —— 因此分类错误(内容帧被公开,或状态信号被
// 私有化)会在这里失败,而不是静默泄露。
// frameScopeCase is one row of the design/11 §7.1 table.
// frameScopeCase 是 design/11 §7.1 表的一行。
type frameScopeCase struct {
typ string // downstream frame type
subject string // bus subject that produces it
payload any // payload published on that subject
public bool // 「状态机信号」: no content → every authenticated connection
typ string // 下行帧类型
subject string // 产生它的 bus 主题
payload any // 发布在该主题上的载荷
public bool // 「状态机信号」:不含内容 → 每个已认证连接
}
// scopeSentinel is published last: the bus runs one worker per subscription
// sequentially, so a connection that has seen the sentinel has already been
// offered every frame published before it. That turns "the viewer received nothing
// else" into an exact count instead of a guessed idle window.
// scopeSentinel 最后发布:bus 每个订阅跑一个 worker 且按序处理,因此
// 已经看到哨兵的连接必定已经被投递过它之前发布的每一个帧。这把
// 「viewer 没有收到别的」从猜测的空闲窗口变成精确计数。
const scopeSentinel = EventSessionIdle
// The batch pacing used by publishFrameScopeCases, kept well under the hub's
// wsSendBuffer depth so a burst never trips the slow-client drop.
// publishFrameScopeCases 使用的批次节流,远低于 hub 的 wsSendBuffer
// 深度,因此一次突发永不触发慢客户端丢弃。
const (
scopePaceFrames = 4
scopePacePause = 50 * time.Millisecond
)
// scopeRead is the outcome of one connection's reader goroutine.
// scopeRead 是一个连接读取 goroutine 的结果。
type scopeRead struct {
role auth.Role
types []string
err error
}
// frameScopeCases returns the design/11 §7.1 table. Relayed frames use their real
// ws.<type> subject; stream frames publish the real model.StreamEvent the loop
// emits on ws.stream, so the test drives the production path
// bus → relay → broadcast instead of a hand-built frame.
// frameScopeCases 返回 design/11 §7.1 表。转发帧使用它们真实的 ws.<type>
// 主题;流帧发布 loop 在 ws.stream 上发出的真实 model.StreamEvent,因此
// 测试驱动的是生产路径 bus → relay → broadcast,而不是手工构造的帧。
func frameScopeCases() []frameScopeCase {
relayed := func(typ string, public bool) frameScopeCase {
return frameScopeCase{typ: typ, subject: wsPrefix + typ, payload: map[string]any{"sessionId": "web"}, public: public}
@@ -113,12 +110,11 @@ func frameScopeCases() []frameScopeCase {
}
}
// TestWSFrameScopePerRole drives the whole design/11 §7.1 table through real
// WebSocket connections with real role tokens: owner and admin receive every frame
// of the table, while the viewer receives exactly the whitelisted state signals, in
// publish order. The viewer assertions the design calls out by name (message.delta /
// thinking.delta / message_persisted / graph_update / mood denied, session_idle
// delivered) are re-stated explicitly so the contract is readable in the test.
// TestWSFrameScopePerRole 用真实角色 token 经真实 WebSocket 连接驱动
// 整张 design/11 §7.1 表:owner 与 admin 收到表中的每一帧,而 viewer
// 恰好收到白名单状态信号,且按发布顺序。设计点名的 viewer 断言
// (message.delta / thinking.delta / message_persisted / graph_update /
// mood 被拒,session_idle 被投递)在此显式复述,使契约在测试中可读。
func TestWSFrameScopePerRole(t *testing.T) {
t.Parallel()
cases := frameScopeCases()
@@ -140,9 +136,9 @@ func TestWSFrameScopePerRole(t *testing.T) {
auth.RoleViewer: len(public),
}
// The clients drain on their own goroutines while the batch is published: the
// hub drops frames for a client whose send buffer is full (its slow-client
// policy), so a client that only started reading afterwards would lose the tail.
// 各客户端在批次发布期间于自己的 goroutine 上排空:hub 会丢弃
// 发送缓冲已满的客户端的帧(其慢客户端策略),因此之后才开始
// 读取的客户端会丢掉尾部。
results := make(chan scopeRead, len(conns))
for role, want := range wants {
role, want := role, want
@@ -177,11 +173,10 @@ func TestWSFrameScopePerRole(t *testing.T) {
}
}
// TestHubBroadcastScopeMatrix is the per-frame-type × per-role matrix, driven
// directly through the delivery entry so every row of the table is asserted in
// isolation (a leaked frame would otherwise be hard to attribute). claims are set
// explicitly per role, so a default-deny regression shows up as the viewer losing
// the frame it is entitled to.
// TestHubBroadcastScopeMatrix 是「每种帧类型 × 每种角色」矩阵,直接经
// 投递入口驱动,使表中每一行都被孤立断言(否则泄露的帧难以归因)。
// claims 按角色显式设置,因此默认拒绝的回归会表现为 viewer 丢掉它
// 本应收到的帧。
func TestHubBroadcastScopeMatrix(t *testing.T) {
t.Parallel()
hb := newTestHub(nil, nil)
@@ -217,11 +212,10 @@ func TestHubBroadcastScopeMatrix(t *testing.T) {
}
}
// TestFrameScopeAllowsExactlyTheStateSignals pins the two properties that keep the
// gate from drifting: an unclassified frame type is never public (so a frame added
// by a later milestone cannot leak by omission), and the public set is exactly the
// state signals design/11 §7.1 registers — widening it means editing the design, the
// gate and this table together.
// TestFrameScopeAllowsExactlyTheStateSignals 钉住两条使帧门不漂移的
// 性质:未分类的帧类型永不是公开的(因此后续里程碑新增的帧不会因
// 漏分类而泄露),且公开集合恰好是 design/11 §7.1 登记的状态信号
// —— 扩大它意味着要同时改设计、帧门和这张表。
func TestFrameScopeAllowsExactlyTheStateSignals(t *testing.T) {
t.Parallel()
if frameReachesAll("frame_type_nobody_classified") {
@@ -250,8 +244,7 @@ func TestFrameScopeAllowsExactlyTheStateSignals(t *testing.T) {
assertFrameTypes(t, "design/11 §7.1 whitelist", public, want)
}
// frameTypes returns the table's frame types in table order (all of them, or only
// the public ones).
// frameTypes 按表顺序返回表中的帧类型(全部,或仅公开的那些)。
func frameTypes(cases []frameScopeCase, publicOnly bool) []string {
out := make([]string, 0, len(cases))
for _, tc := range cases {
@@ -263,10 +256,10 @@ func frameTypes(cases []frameScopeCase, publicOnly bool) []string {
return out
}
// publishFrameScopeCases publishes every row on its production subject, followed by
// the sentinel that closes the batch. The batch is paced: a client whose send buffer
// is full has its frames dropped (the hub's documented slow-client policy), so a
// 37-frame burst would exercise the drop policy rather than the gate.
// publishFrameScopeCases 在每行各自的生产主题上发布它,随后发布关闭
// 批次的哨兵。批次是节流的:发送缓冲已满的客户端其帧会被丢弃
// (hub 有文档的慢客户端策略),因此 37 帧的突发放送会去考验丢弃
// 策略而不是帧门。
func publishFrameScopeCases(t *testing.T, env *testEnv, cases []frameScopeCase) {
t.Helper()
for i, tc := range cases {
@@ -282,9 +275,9 @@ func publishFrameScopeCases(t *testing.T, env *testEnv, cases []frameScopeCase)
}
}
// readFrameTypes reads exactly want frames and returns their types in arrival order.
// It reports an error rather than failing the test so it can run on the reader
// goroutines that drain each client while the batch is published.
// readFrameTypes 恰好读取 want 个帧并按到达顺序返回其类型。它返回错误
// 而不是让测试失败,以便能跑在批次发布期间排空各客户端的读取
// goroutine 上。
func readFrameTypes(conn *websocket.Conn, want int) ([]string, error) {
got := make([]string, 0, want)
for len(got) < want {
@@ -302,7 +295,7 @@ func readFrameTypes(conn *websocket.Conn, want int) ([]string, error) {
return got, nil
}
// assertFrameTypes compares the received frame types to the expected sequence.
// assertFrameTypes 比较收到的帧类型与期望序列。
func assertFrameTypes(t *testing.T, what string, got, want []string) {
t.Helper()
if len(got) != len(want) {
@@ -315,7 +308,7 @@ func assertFrameTypes(t *testing.T, what string, got, want []string) {
}
}
// containsFrameType reports whether typ is among the received frame types.
// containsFrameType 报告 typ 是否在收到的帧类型之中。
func containsFrameType(types []string, typ string) bool {
for _, got := range types {
if got == typ {