# 3-3 [process] Rust Web Tree Realtime Event Stream 方案 v1

> 更新时间：2026-05-09
>
> 关联文档：
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`

## 1. 目标

这份文档用于固定 Stage C-1 的正式实时链路口径：

- 保留 Convex 作为 realtime substrate
- Rust 成为 tree-first graph 的 semantic owner
- Rust Web 负责正式页面 transport 与实时事件流
- 前端只消费 projection 与 delta，不再消费实验壳真相

目标效果是接近网页版 wolai / notion 的体验：

- 快速进入页面
- 树结构实时同步
- 局部变化快速响应
- 浏览器端不持有第二套树真相

## 2. 职责划分

### 2.1 Convex substrate

Convex 继续承担：

- 持久化
- mutation / query 底座
- 实时订阅底座
- 文件 / 对象存储协作

Convex 在这里是：

> storage / realtime substrate

而不是页面树语义 owner。

### 2.2 Rust semantic owner

Rust kernel 负责：

- `page_tree / sidebar_tree / file_tree / subtree` 语义
- node / edge / projection 规则
- command / query 的语义收口
- domain event 的统一格式
- trace / audit / version 口径

Rust 在这里是：

> Rust semantic owner

### 2.3 Rust Web transport

Rust Web 负责：

- SSR 页面壳
- SSE / WS 主链
- workspace / subtree 订阅入口
- 把 Convex 订阅与 Rust domain event 连接起来
- 向前端输出稳定的 projection snapshot + delta stream

### 2.4 前端 projection consumer

前端只负责：

- 首屏渲染
- islands 交互
- optimistic UI
- 应用 delta 到本地 projection cache

前端不再负责：

- 重新定义树结构
- 重新计算页面树语义
- 维护实验壳 iframe 作为正式实时主链

## 3. 为什么当前 tree shell 不是正式实时链路

当前 `mnote-web tree shell` 仍然只是实验壳，原因有三点：

1. 它依赖 iframe / HTML shell / postMessage 交互。
2. 它的 command path 和状态边界更接近实验 viewer，而不是正式页面 transport。
3. 它没有成为首页、sidebar、文档页共享的正式实时订阅主链。

因此当前 tree shell 可以继续保留为：

- 实验验证壳
- 独立 tree viewer
- picker / filetree 的可选增强壳

但不能继续当成正式 realtime 主链。

## 4. 正式 realtime tree event stream 分层

正式链路建议固定为四层：

### 4.1 Convex 持久化/订阅底座

这里负责：

- 命令落账
- 持久化页面树状态
- 输出 mutation 后可订阅的数据变化

### 4.2 Rust kernel 语义 owner

这里负责：

- 把命令结果解释为 domain event
- 把底层 mutation 变化转成树域可消费的语义事件
- 维护 projection rebuild 与 delta 生成规则

### 4.3 Rust Web SSE/WS transport

这里负责：

- 暴露正式 `/api/tree/events` 或等价 stream 入口
- 管理 workspace / subtree 订阅
- 发送 snapshot、delta、cursor、ack、resync 信号

推荐策略：

- 首屏与重连优先使用 snapshot
- 连续变化优先推送 delta
- 大规模漂移或 cursor 失配时回退到 resync snapshot

### 4.4 前端 projection consumer

这里负责：

- 接收初始 projection snapshot
- 接收增量事件
- 合并到 sidebar / filetree / page subtree cache
- 必要时触发局部重渲染

## 5. 建议事件形状

正式事件建议至少覆盖三类 scope：

- workspace
- subtree
- tree delta

### 5.1 Workspace stream

用于：

- 当前工作区根树更新
- 垃圾桶、模板、共享页等全局区域变化

建议形状：

```json
{
  "stream": "workspace",
  "workspaceId": "ws_123",
  "cursor": "evt_1001",
  "kind": "snapshot|delta|resync",
  "projection": "sidebar_tree",
  "data": {}
}
```

### 5.2 Subtree stream

用于：

- 当前文档子树
- file tree 局部展开区域
- picker 只关注的局部节点集合

建议形状：

```json
{
  "stream": "subtree",
  "workspaceId": "ws_123",
  "rootNodeId": "page_1",
  "cursor": "evt_1002",
  "kind": "snapshot|delta|resync",
  "projection": "page_tree",
  "data": {}
}
```

### 5.3 Tree delta payload

建议包含：

- `eventId`
- `commandId`
- `traceId`
- `aggregateType`
- `aggregateId`
- `op`
- `node`
- `parentNodeId`
- `position`
- `removedNodeIds`
- `changedProjectionKeys`

参考形状：

```json
{
  "eventId": "evt_1003",
  "commandId": "cmd_2001",
  "traceId": "trace_3001",
  "aggregateType": "page",
  "aggregateId": "page_9",
  "op": "create|rename|move|delete|restore|purge|embed",
  "node": {
    "id": "page_9",
    "title": "新页面"
  },
  "parentNodeId": "page_root",
  "position": 3,
  "removedNodeIds": [],
  "changedProjectionKeys": ["sidebar_tree", "file_tree"]
}
```

## 6. 与首页和首屏的关系

首页与 `/api/sidebar` 的 P0 原则必须保持不变：

- 首页不能再依赖 3104 实验壳
- `3000` 主入口必须可稳定进入 `/auth` 或文档页
- 首屏仍要有稳定 SSR snapshot，但不再通过第二条 runtime projection fallback 偷偷补拉 live 语义

也就是说：

- 首屏可用性优先于实时增强
- realtime stream 是正式增强层，不是新的首屏阻塞点

## 7. 实施顺序

推荐顺序如下：

1. 先固定首页、sidebar、picker 不再依赖 tree shell 实验壳。
2. 再固定 Rust kernel 的 tree command / projection owner 身份。
3. 然后补正式 Rust Web SSE/WS tree stream。
4. 最后把 sidebar、page subtree、filetree 逐步切到统一 stream。

## 8. 最终固定口径

长期口径固定为：

- Convex substrate 不拆
- Rust semantic owner 收口语义
- Rust Web 提供正式实时 transport
- 前端只消费 projection snapshot 与 tree delta
- iframe/postMessage tree shell 不是正式 realtime 主链


## 9. 当前实现复核（2026-05-09）

这份方案继续留在 `process/`，因为 Rust Web transport 与当前 `3000` 主壳 live consumer 已落地，但 page subtree / filetree / preferred snapshot 仍未完全统一到同一条正式 live cache。

### 9.1 已完成

- [x] Rust Web 已暴露正式 `/api/tree/events`，并返回 `x-mnote-web-owner: mnote-web` 与 `x-mnote-tree-stream-owner: rust-web`。
- [x] `/api/tree/events` 已能输出 workspace / subtree snapshot。
- [x] `stream_support.rs` 已有 cursor、delta、resync 的基础判定逻辑。
- [x] `/api/stream/events` 与 WebSocket snapshot / resync 骨架已存在。
- [x] legacy React 侧已有 `useSidebarTreeStream` 与 `EventSource` consumer，并有协议 / delta 单测。
- [x] 当前 `3000` Rust shell 已直接挂载 tree live `EventSource` consumer，并通过 `data-mnote-tree-live-applied` 应用 delta / resync。
- [x] `task112` / `task120` / `task123` 已覆盖 `/api/tree/events` snapshot、delta / resync 与 stream owner 可用性。
- [x] `task165` 已验证双 pane 不重复建立第二条 tree live stream。

### 9.2 仍未完成

- [ ] Sidebar、page subtree、filetree 还没有全部统一到同一条 live stream cache。
- [ ] WS 目前只证明 snapshot/resync 骨架，尚未成为主实时链路。
- [ ] 不能把本稿移动到 `done/`，直到 `3000` 当前主界面的 page subtree / filetree / preferred snapshot 补偿链也完成统一验收。
