# 4-5 [done] Tree Command Envelope 第一批收口方案 v1

> 更新时间：2026-04-17
>
> 关联文档：
> - `/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`

## 1. 文档目标

这份文档用于固定 Stage B-2 的真实边界：

- 前端已经不应该再扩散树命令语义。
- 但当前仓库里，浏览器组件层仍然散落着大量 `fetch("/api/documents/...")` 入口。
- 第一批要先收口 `create / rename / move`，把前端限制为 optimistic UI 与用户交互壳。

这里的核心口径是：

> 前端只保留 optimistic UI，真语义和 command envelope 收口到 Rust/bridge。

## 2. 当前命令入口盘点

### 2.1 Sidebar 主入口

当前树命令入口主要集中在：

- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/menus/CustomSideMenu.tsx`

其中：

- `sidebar.tsx`
  - `create` 走 `/api/documents/create`
  - `rename` 走 `/api/documents/title`
  - `move` 走 `/api/documents/move`
  - `delete` 走 `/api/documents/delete`
  - `restore` 走 `/api/documents/restore`
  - `purge` 走 `/api/documents/purge`
  - `embed` 走 `/api/documents/embed`
  - `copy-tree` 走 `/api/documents/copy-tree`
- `document-content.tsx`
  - 文档标题更新走 `/api/documents/title`
  - 移动页走 `/api/documents/move`
  - 页面嵌入走 `/api/documents/embed`
- `CustomSideMenu.tsx`
  - block 转子页面走 `/api/documents/create-child`
  - `pageReference` 删除走 `/api/documents/delete`

### 2.2 当前哪些 route 已接到 envelope

第一批最关键的三条主路径，已经接入 `buildDocumentCommandEnvelope`：

- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/create/route.ts`
  - 命令名：`documents.create`
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/title/route.ts`
  - 命令名：`documents.title.update`
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/move/route.ts`
  - 命令名：`documents.move`

当前真实执行链路是：

1. Next route 构造 `buildDocumentCommandEnvelope`
2. `resolveRustBridgeCommandPlan`
3. TS runtime 负责 transport dispatch
4. Convex mutation 负责实际持久化

因此当前形态是：

- `Convex substrate`
- `Rust semantic owner`
- `TS runtime transport dispatcher`

不是“Rust 已经直接替代 Convex 落库”。

### 2.3 当前仍留在前端的树语义

虽然 route 已接入 envelope，但浏览器端仍残留较多树语义：

- `sidebar.tsx`
  - 本地 `insertNode` / `moveLocalNode`
  - 非法拖拽判断
  - `targetParentId` / `position` 推导
  - `copy-tree` 的递归策略与拖放目标推导
- `document-content.tsx`
  - 移动页时仍由前端给出固定 `position`
- `CustomSideMenu.tsx`
  - block 转子页面的 UI 级决策仍在前端

这说明当前主问题已经不是 route 是否接 bridge，而是：

> 命令 transport 已经收口，但命令入口和部分树策略仍散落在前端组件层。

## 3. 第一批 cutover 范围

Stage B-2 第一批只处理：

- `create`
- `rename`
- `move`

原因：

- 这三条覆盖 sidebar 主交互、文档页主交互、block 转页入口。
- 它们已经具备稳定的 route -> envelope -> Rust bridge -> Convex 主链。
- 继续向前推进时，前端只需要保留 optimistic UI 和错误提示，不需要继续扩散 transport 细节。

## 4. 第一批 cutover 顺序

推荐固定为两段：

### 4.1 第一段：先 `create / rename / move`

目标：

- 浏览器组件不再直接散落 `fetch("/api/documents/create|title|move")`
- 收成共享的 tree command client
- 让 `sidebar.tsx`、`document-content.tsx`、`CustomSideMenu.tsx` 只表达交互和 optimistic UI

这一步已经开始落地：

- 新增共享入口：
  - `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tree-command-client.ts`
- 第一批已接入的组件：
  - `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
  - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
  - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/menus/CustomSideMenu.tsx`

### 4.2 第二段：再 `delete / restore / purge / embed`

原因：

- `delete / restore / purge` 已接 envelope，但仍有多处页面级分叉入口。
- `embed` 当前最特殊，它不是独立 `documents.embed` 命令，而是 TS adapter 里拼 `pageReference` 后走 `documents.save`。
- `embed` 必须等 Rust kernel 定义正式页面嵌入语义后，再从“TS adapter 语义”切成“Rust tree command 语义”。

## 5. 不纳入第一批的内容

以下内容先不纳入 Stage B-2 第一批：

- `copy-tree`
  - 当前仍强依赖前端拖放和递归策略
- block 级 `blocks.move / blocks.embed`
  - 这是块域命令，不是页面树命令主链
- 树排序算法本身
  - 当前仍有一部分位置推导在前端，后续要继续收回 Rust kernel

## 6. 长期边界说明

长期固定如下：

- 前端负责：
  - 用户交互
  - optimistic UI
  - 选择器、确认框、拖拽体验
- Next route / shared client 负责：
  - 稳定 transport 边界
  - 请求格式统一
- Rust kernel / bridge 负责：
  - `tree command` 语义
  - `page_tree / sidebar_tree / file_tree` 真相
  - 审计、trace、版本口径
- Convex 负责：
  - 持久化
  - 订阅
  - 实时底座

## 7. 下一步建议

Stage B-2 后续应继续做三件事：

1. 把 `delete / restore / purge` 也收成同一层 shared tree command client。
2. 为 `embed` 定义独立的 Rust page-tree 命令，而不是继续停留在 TS adapter + `documents.save`。
3. 把 `targetParentId / sortOrder / subtree move legality` 等策略继续从前端移回 Rust kernel。
