# 3-18 [done][bug] WS 与 SSE delta 载荷合同分裂 v1

> 发现时间：2026-05-17
>
> 状态：`[done]`
>
> 关联主线：`03-rust-web`

## 1. 问题定义

SSE polling 可以从 bridgeLogs / domainEvents 解析 `streamDelta` 并附带 snapshot / resync 信息；WS 当前广播的是 `command_committed` 外壳，两者不是同一套 delta 合同。

## 2. 证据

- [stream_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/stream_support.rs:577) 附近解析 `streamDelta`。
- [command_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/command_support.rs:102) 附近广播 command committed 事件。
- [ws.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/ws.rs:40) 处理 WS socket。

## 3. 影响

- 同一 tree command 在 WS 与 SSE 下可能给客户端不同结构。
- 前端 live cache 需要维护两套 reducer，增加状态分裂风险。
- WS 切主时，SSE 已覆盖的 snapshot/resync 语义可能丢失。

## 4. 建议修复

- 抽出统一的 tree realtime envelope，WS 与 SSE 只作为 transport。
- 对同一命令分别跑 WS / SSE 合同测试，断言事件 kind、cursor、snapshot、resync 语义一致。
- 前端 reducer 只消费统一 envelope，不感知 transport 差异。

## 5. 修复

已修复：

- [stream_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/stream_support.rs:577) 新增 `build_stream_push_delta_hint`，统一 command committed broadcast 的 tree realtime hint envelope。
- [sse.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/sse.rs:97) SSE push 分支改为复用统一 hint envelope。
- [ws.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/ws.rs:43) WS broadcast 分支改为发送同一 hint envelope，不再输出嵌套 `data.kind=command_committed` 的专有结构。
- [ws.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/ws.rs:190) 增加 WS delta serializer 合同测试，断言输出为 `kind=delta`、`hint=command_committed`，并且没有额外 `data` 外壳。

## 6. 验证

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::ws::tests -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::sse::tests -- --nocapture
```

结果：WS 3 个测试通过，SSE 5 个测试通过。
