
== 1. 3-10 人边界 ==
  ✓ capacity=3 建桌成功
  ✓ capacity=10 建桌成功
  ✓ capacity=2 建桌被拒
  ✓ capacity=11 建桌被拒
  ✓ seatIndex=11 被拒
  ✓ capacity 3..10 共 8 档全部真实开局（实测 8/8）

== 2. 免费筹码全流程（零链上） ==
  ✓ 三人到齐并 ready 后自动开局
  ✓ handId 格式合规
  ✓ 发牌与盲注事件齐全
  ✓ 全跟注路线跑完整手并回到等待态
  ✓ 确实走到摊牌
  ✓ 结算与结束事件齐全
  ✓ 一手只产生一条 hand.settle
  ✓ hand.settle 为单播（只发结算订阅者）
  ✓ manifest.engineHash 等于引擎基线哈希
  ✓ manifest.mode=free
  ✓ manifest 守恒：Σdelta=0 且 balanced
  ✓ hand.settle 通过协议校验
  ✓ manifest 通过独立守恒校验函数
  ✓ 免费模式：结算客户端零调用（零链上）
  ✓ 底牌逐人单播，未进广播信道
  ✓ 广播信道中出现底牌数 = 0
  ✓ 本桌 seq 严格单调递增

== 3. 全下与边池 ==
  ✓ 短筹码触发分层池（实测 2 个池）
  ✓ 第二个池标记 isSidePot
  ✓ 池总额 = 各方实际投入之和 2500
  ✓ 全下边池后筹码守恒（Σdelta=0）
  ✓ 边池 manifest 通过守恒校验
  ✓ manifest 记录了 allIn 标记

== 4. 多桌并发（桌间零共享） ==
  ✓ 4 张桌（3/4/6/10 人）同时进入对局中
  ✓ 4 张桌并发各打完一手且互不干扰
  ✓ 事件按 4 张桌正确分流（实测 4 桌）
  ✓ 每张桌的 seq 各自独立且单调（无跨桌污染）
  ✓ 4 张桌各产出 1 份结算 manifest
  ✓ 4 份 manifest 全部通过守恒校验
  ✓ 4 份 manifest 的 tableId 互不相同

== 5. 串行队列 / 单播 / 重放 ==
  ✓ 存在待行动的 hand.turn
  ✓ 非行动玩家的动作被拒（NOT_YOUR_TURN）
  ✓ 过期动作被拒（STALE_SEQ）
  ✓ 过期手牌动作被拒（STALE_HAND）
  ✓ 同一 turn 的 20 条并发动作只有 1 条生效（实测 1）
  ✓ 其余 19 条被串行队列按 NOT_YOUR_TURN 拒绝
  ✓ 重放缓冲可返回完整消息
  ✓ 重放按玩家过滤（底牌只回给本人）
  ✓ 玩家 z 恰好只看到自己 1 条底牌事件

== 6. space 模式 + /settle/hand 契约（mock 结算服务） ==
  ✓ space 模式入座买入逐笔上报结算服务（实测 3/3）
  ✓ space 模式每手结算上报 1 次（实测 1）
  ✓ 上报的 manifest 带正确 engineHash
  ✓ 上报的 manifest 通过守恒校验
  ✓ 上报的 manifest mode=space
  ✓ 结算服务不可达时入座被拒（fail closed，不发生半截入账）

== 7. 洗牌承诺/揭示可独立复算 ==
  ✓ hand.start 带 64 位 hex 的 deckCommit
  ✓ hand.end 揭示的 seed 与本地记录一致
  ✓ 用揭示的 seed 独立重算 commit = hand.start 的 deckCommit
  ✓ 篡改 commit 会被检出（负向）
  ✓ 一手发出的 6 张底牌互不重复
  ✓ 牌面编码合法（无占位/未知牌）

== 8. HTTP 传输通道（/rpc、/events、/health、reveal） ==
  ✓ /health 返回引擎哈希与协议版本
  ✓ /rpc 建桌成功并回 tableId
  ✓ /rpc 回带 tableSeq（客户端据此起轮询）
  ✓ /lobby 返回桌列表
  ✓ /events 返回订阅后的事件
  ✓ /events 事件流可用（未开局时无 hand.turn 属正常）
  ✓ HTTP 通道下三人 ready 即开局
  ✓ HTTP 通道可完整打完一手
  ✓ /hand/.../reveal 返回可复算且 verified=true
  ✓ 非法 JSON 回 400 而非崩溃

== 8b. WebSocket 主通道（薄层：复用同一 handler 与同一信封） ==
  ✓ WS 握手 session.hello → session.ack 带引擎哈希
  ✓ WS 握手回带协议版本
  ✓ WS 建桌后即刻收到 table.state（客户端无需额外请求即知 tableId）
  ✓ WS 拿到的 tableId 格式合规
  ✓ WS 建桌返回 ack
  ✓ WS 通道三人入座成功
  ✓ WS 通道三人 ready 即开局
  ✓ WS 通道可完整打完一手（四轮下注至摊牌结算）
  ✓ 每个 WS 客户端恰好收到 1 条手牌中的底牌事件（实测 1/1/1）
  ✓ 三条底牌事件 seq 互不相同（未被广播给他人）
  ✓ hand.settle 未泄露给任何玩家连接（实测泄露 0 条连接）
  ✓ 三个 WS 客户端均收到摊牌事件
  ✓ 三个 WS 客户端均收到手牌结束事件
  ✓ WS 出站消息全部通过协议校验（共 151 条，违规 0）
  ✓ 未握手就发消息被拒（BAD_MESSAGE）
  ✓ WS 收到非法 JSON 时回 error 而非断开/崩溃
  ✓ WS 连接可正常建立与关闭（stats 可用）

== 9b. 建桌者广播投递 + 投递可观测性 + 复用窗口回退（小红 D2 第二轮报告驱动） ==
  ✓ 空闲的建桌者连接收到新玩家的 seat.update（服务端 listener 广播路径有效）
  ✓ 建桌者未收到别人的底牌（单播语义未破）
  ✓ /stats 暴露每连接的 ws 诊断信息
  ✓ 建桌者连接在服务端登记为该桌订阅者（订阅表可见）
  ✓ 建桌者的 listener 路径投递计数 > 0（实测 2）
  ✓ 建桌者的 pushTo / direct 路径计数均 > 0
  ✓ 事件数已超过复用缓冲深度（实测 seq=604 > 512）
  ✓ 缓冲窗口已被冲掉时，以陈旧 lastSeq 订阅仍拿到全量 table.state（无静默截断）
  ✓ 可从 attachWebSocket 取到连接对象做白盒探针
  ✓ 缓冲最老一条 seq=94 已远大于该连接的 lastSent=1
  ✓ 落后超窗的连接被回退为全量 table.state（不是把 lastSent 直接推到 t.seq 静默跳段）
  ✓ （诊断口径）POKER_EVENT_LOG=1 时每次投递写入 JSONL：connId/playerId/tableId/seq/type/via

== 9c. 行动超时驱动器 + 投递日志健壮性（主席批准项 / 小红实测问题） ==
  ✓ 超时桌：两人到齐即开局（此后客户端不再做任何动作）
  ✓ 超时值已注入本桌（实测 80ms）
  ✓ 无人应答时服务端按超时代打（实测 1 条 hand.action）
  ✓ 代打只做安全动作 fold/check（实测 fold）
  ✓ 代打广播与真人动作同形（含 playerId / stackAfterSat，未新增协议字段）
  ✓ 代打广播通过协议校验
  ✓ 整手在零客户端动作下被超时驱动推进至结束
  ✓ 手结束后定时器已清除（不残留）
  ✓ 手结束后不再有迟到的代打动作
  ✓ POKER_ACTION_TIMEOUT_MS=0 等价（actionTimeoutMs:0）时不做任何代打，手停在该轮
  ✓ 关闭态下零 hand.action 广播
  ✓ 未显式配置时按 env/默认取值（实测 30000ms）
  ✓ POKER_EVENT_LOG 开启时日志文件在 attach 瞬间即创建（不再「文件不存在」）
  ✓ 日志首行为 log-open header（含确切路径）
  ✓ 握手应答已入日志（此前只有它没记，是「文件为空/不存在」的成因之一）
  ✓ 建桌快照入日志且 via=pushTo
  ✓ 每条投递都带 connId/playerId/via（实测 3 条）
  ✓ /stats.ws.eventLog 回带确切日志路径（调用方可直接取用）

== 9. E1 引擎 95 断言回归门禁 ==
  ✓ vendored 引擎回归：95 通过 / 0 失败
  ✓ vendored engine.js 哈希 = E1 基线

== 10. 全局协议合规 ==
  ✓ 全部出站消息通过协议校验（共 378 条，违规 0 条）
  ✓ manager.stats() 可用

== 9a. D2 联调阻塞回归（由小红的实测报告驱动，修前 4/4 复现、修后必须全绿） ==
  ✓ WS table.list → 收到 table.list.result（阻塞项 1 已修）
  ✓ table.list.result.tables 为数组
  ✓ table.list.result 通过协议校验
  ✓ 新建桌出现在大厅列表里（含 seatedCount/capacity/stage）
  ✓ 后到玩家 table.subscribe → 收到全量 table.state（阻塞项 2 已修）
  ✓ 该快照含 config / stage（后到玩家据此才能渲染桌面）
  ✓ 快照 contains 全部 3 个座位位（含空位）
  ✓ 订阅快照通过协议校验
  ✓ 入座后订阅快照带 yourSeatIndex = 1（逐人视角正确）
  ✓ holeCardsKnownToYou 只对自己座位为 true（不泄露他人视角）
  ✓ WS account.state → account.state.result 且通过协议校验
  ✓ OPTIONS 预检回 204（实测 204）
  ✓ OPTIONS 预检带 Access-Control-Allow-Origin: *（阻塞项 3 已修）
  ✓ 预检允许 POST
  ✓ 普通 GET 响应也带 CORS 头
  ✓ POST /rpc 响应带 CORS 头
  ✓ 第三名玩家同样靠 subscribe 拿到快照
  ✓ 三名玩家（其中两名后到）全部入座
  ✓ 三人 ready → 开局（后到入桌路径完全打通）
  ✓ 后到入桌的三人同桌可完整打完一手（四轮下注 → 摊牌 → 结算 → 结束）
  ✓ 每人恰好 1 条自己的底牌事件（实测 1/1/1）
  ✓ 三人都收到摊牌
  ✓ P.MESSAGES 共 26 种（实测 26；c2s 10 + s2c 16）
  ✓ c2s=10 / s2c=16（实测 10/16）

========================================
E2b 自测: 142 通过 / 0 失败（共 142 项）
========================================
