feat(tree): complete rust family runtime checklist

- add tree shell runtime artifact contracts and page/filetree/picker runtime reducers
- sink tree.subtree.move write operation through Rust and formalize command event plans
- harden file tree search projection contract and route thin-proxy boundaries
- record completed harness tasks and move design docs into process/done
This commit is contained in:
lix-2026
2026-04-27 10:27:15 +08:00
parent e564dfde02
commit 4ab36a9386
30 changed files with 2502 additions and 432 deletions
@@ -0,0 +1,419 @@
# 4-3 [done] Tree-First Graph Kernel Phase 4 专项任务拆分 v1
> 更新时间:2026-04-16
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-1-tree-first-graph-kernel-checklist-v2.md`
> - `/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
## 1. 文档目的
这份文档只处理一件事:
> **把 `Kernel Phase 4Sidebar / 页面树 / 文件树切到 kernel projection` 单独拆开,按当前真实代码重写成可执行任务。**
这个阶段的目标不是:
- 直接重写整个 Sidebar
- 直接把树 UI 改成 `Leptos`
- 直接启动树域 Rust Web shell 替换
这个阶段只负责:
> **把树域的 truth、projection、consumer 协议先统一。**
也就是先把树“变对”,再谈“换壳”。
---
## 2. Phase 4 的准确定位
`tree-first graph kernel` 里:
- 树是主骨架
- 页面树 / 文件树都只是 projection
- Sidebar 是树域承载壳,不是事实源
所以 `Phase 4` 的本质不是普通前端整理,而是:
> **让树域 consumer 不再自己拼树,而是统一消费稳定的 kernel projection。**
如果这一步不先完成,后面的 Rust Web tree shell 重构就会在协议未冻结前重复返工。
---
## 3. 当前真实完成情况
基于当前 git`Phase 4` 已经有一些关键前置。
### 3.1 已有 projection 起点
- `sidebar.dataset.list` 已能产出:
- `kernel_sidebar_projection`
- `kernelSidebarTree`
- 对应文件:
- [sidebar-data.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/sidebar-data.ts)
- [kernel-sidebar.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/kernel-sidebar.ts)
### 3.2 Sidebar 主树已切到统一 tree consumer
- 主 Sidebar 已以 `sidebarData.kernelSidebarTree` 作为主树来源
- `PrivateTree` 已改为直接消费 `page_tree` projection 可见行
- 对应文件:
- [sidebar.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx)
- [private-tree.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/private-tree.tsx)
### 3.3 文件树已收口为统一 projection consumer
- 文件树当前不再直接走嵌套 `SidebarTreeNode[]` 递归拼树
- 它已经建立在:
- `page_tree projection rows`
- `assetsByDoc`
- `buildVisibleRows(...)`
之上
- 对应文件:
- [rows.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/file-tree/rows.ts)
- [types.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/file-tree/types.ts)
- [file-tree.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/file-tree.tsx)
### 3.4 picker 已退出旧拼树主路径
- `move-embed picker` 空查询态已不再读取 `documents[] -> buildDocumentTree(...)`
- 它现在直接消费 `kernelSidebarTree -> page_tree projection -> picker rows`
- 对应文件:
- [move-embed-picker-dialog.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/documents/move-embed-picker-dialog.tsx)
- [tree-projection.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts)
---
## 4. 本轮完成后的保留边界
### 4.1 旧拼树 helper 已退出主路径,但仍保留兼容边界
- [`buildDocumentTree(...)`](/mnt/Data1T/mnote/wolai-frontend/src/lib/documents.ts) 仍保留在兼容 helper 与旧单测里
- 但运行时主路径已不再由它构造 Sidebar / 页面树 / 文件树 / picker
### 4.2 文件树仍不是 Rust 直接输出的完整 file_tree projection
- 当前文件树已经消费统一 `page_tree projection`
-`asset-folder` / `asset` / `index` 仍由前端 adapter 基于现有数据集补齐
### 4.3 Sidebar 壳仍然过重
当前 Sidebar 仍是超大客户端组件。
这不是 `Phase 4` 的阻塞项,但它解释了为什么下一个任务必须单列为独立 Rust Web tree shell 重构。
---
## 5. Phase 4 的重新定义
建议把 `Phase 4` 重新定义成下面这个最小目标:
> **统一树域 projection protocol,让 Sidebar 主树、页面树、文件树、move/embed picker 都只消费同一套 kernel projection family,并让旧拼树 helper 退出主路径。**
这版定义刻意不包含:
- 树域独立 Rust Web shell
- `Leptos`/`Dioxus` UI 重写
- Sidebar 整体壳替换
这些是 `Phase 4` 完成后的下一层任务。
---
## 6. Phase 4 拆分原则
### 原则 1
先冻结协议,再收 consumer。
### 原则 2
先收掉旧 helper 的主路径依赖,再谈树域 UI 重构。
### 原则 3
文件树不是第二套树,而是更宽对象范围的 projection。
### 原则 4
每一条树域 consumer 都必须能明确回答:
- 它消费的 projection 是什么
- 它本地状态只剩什么
- 它是否还在自己定义结构真相
---
## 7. Phase 4 任务拆分
建议把 `Phase 4` 拆成 6 个子任务。
---
## 8. P4-1:冻结树域 projection protocol
**状态:`DONE`**
**目标**
先把 Sidebar / 页面树 / 文件树要共同使用的 tree protocol 冻结下来。
**要做的事**
- 明确最小 tree item / tree row 字段
- 明确 `sidebar_tree``page_tree``file_tree` 的共用字段与差异字段
- 明确:
- `node_id`
- `parent_node_id`
- `node_type`
- `projection_kind`
- `depth`
- `position`
- `capabilities`
- `resource_meta`
- 明确哪些字段属于 projection
- 明确哪些字段只能留在 UI 本地状态
**交付物**
- [x] 树域 projection protocol 文档与类型:
- [tree-projection.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts)
- [x] 当前前端类型定义对齐方案
**完成判定**
- [x] 后续所有树域 consumer 都有统一协议可依赖
---
## 9. P4-2:统一 Sidebar 主树与页面树 consumer
**状态:`DONE`**
**目标**
让主 Sidebar、页面树相关视图都明确只消费 `kernelSidebarTree` / projection protocol。
**要做的事**
- 盘点 Sidebar 主树的所有树读取入口
- 清理仍然从 `documents[]` 直接拼主树的路径
- 把排序、层级、childCount、expand hint 统一收口到 projection
- 让主树 consumer 只保留:
- expanded
- selected
- hover
- DnD 中间态
**交付物**
- [x] Sidebar 主树消费边界收口
- [x] `PrivateTree` / 顶部树列表统一改读 `page_tree projection`
**完成判定**
- [x] 主 Sidebar 不再自己定义页面树结构
---
## 10. P4-3:把文件树收口为统一 projection consumer
**状态:`DONE`**
**目标**
让文件树从“前端继续拼装的树行系统”收口为统一 tree protocol 的 consumer。
**要做的事**
- 明确文件树和页面树的关系:
- 文件树 = 页面树骨架 + 更宽对象投影
-`asset-folder` / `asset` / `index` 的协议层次化
- 收紧 `buildVisibleRows(...)` 的职责
- 它只负责 projection -> visible rows
- 不再负责定义结构真相
- 明确哪些对象后续应由 Rust projection 直接输出:
- `mindmap`
- `table`
- `asset`
- `book`
- `pdf`
**交付物**
- [x] 文件树 row model 收口方案
- [x] 文件树 projection consumer 边界
**完成判定**
- [x] 文件树不再被视为独立第二套树系统
---
## 11. P4-4:统一 move / embed picker 等兼容树域
**状态:`DONE`**
**目标**
把兼容树域 consumer 也切到 projection,结束双轨树模型。
**要做的事**
- 改成直接消费 projection tree / flat rows
- 搜索态和空查询态统一到同一目标对象口径
- 明确 picker 只消费 page tree,不再自带另一套树构造逻辑
**交付物**
- [x] picker 树域消费统一
**完成判定**
- [x] `buildDocumentTree(...)` 退出 picker 主路径
---
## 12. P4-5:收敛旧 helper 与兼容逻辑
**状态:`DONE`**
**目标**
让旧树 helper 不再占据主路径,只保留必要过渡边界。
**要做的事**
- 盘点 `buildDocumentTree(...)` 所有 consumer
- 区分:
- 主路径必须替换
- 过渡路径可暂留
- 收敛 `lib/documents.ts` 在树域中的责任
- 给保留的兼容 helper 标明过渡边界
**交付物**
- [x] 树域旧 helper 清单
- [x] 主路径替换完成
**完成判定**
- [x] 树域主路径不再依赖旧对象数组拼树
---
## 13. P4-6Phase 4 验收矩阵与下一阶段交接
**状态:`DONE`**
**目标**
避免 `Phase 4` 再被提前写成“树域已重构完成”。
**要做的事**
- 建立 Phase 4 验收矩阵:
- Sidebar 主树 consumer
- 页面树 consumer
- 文件树 consumer
- picker consumer
- 旧 helper 退出主路径
- tree protocol 冻结
- 回写 checklist
- 明确 Phase 4 完成后才能进入:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
**交付物**
- [x] Phase 4 验收矩阵
- [x] 下一阶段交接说明
**完成判定**
- [x] 能明确回答“为什么现在可以做树域 Rust Web shell 重构”
### 验收矩阵
| 验收项 | 当前状态 | 说明 |
| --- | --- | --- |
| Sidebar 主树 consumer | `DONE` | 主 Sidebar 与 `PrivateTree` 已统一消费 `kernelSidebarTree -> page_tree projection` |
| 页面树 consumer | `DONE` | 顶部树列表、页面树扁平化视图不再各自 flatten 嵌套树 |
| 文件树 consumer | `DONE` | 文件树只消费 `page_tree projection + asset 映射``buildVisibleRows(...)` 只做 visible rows |
| picker consumer | `DONE` | `move-embed picker` 空查询态已改读 projection family |
| 旧 helper 退出主路径 | `DONE` | 运行时树域主路径不再调用 `buildDocumentTree(...)` |
| tree protocol 冻结 | `DONE` | `rowId/nodeId/parentNodeId/projectionKind/depth/position/capabilities/resourceMeta` 已固定 |
### 下一阶段交接
- 本阶段完成后,下一步唯一合理的大任务是:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
- 原因不是“树域 UI 已重写完成”,而是:
- 协议已经冻结
- consumer 已经统一
- 旧拼树 helper 已退出主路径
- 现在可以稳定做一次“换壳”,而不是边改协议边重写 UI
---
## 14. 建议执行顺序
建议顺序如下:
1. `P4-1` 冻结协议
2. `P4-2` 收主 Sidebar / 页面树 consumer
3. `P4-3` 收文件树 consumer
4. `P4-4` 收 picker / 兼容树域
5. `P4-5` 收旧 helper
6. `P4-6` 做验收矩阵和下一阶段交接
原因:
- 如果不先冻结协议,后面的 consumer 收口会一边做一边改字段
- 如果不先收主树和文件树,picker 再怎么改也只是边角修补
- 如果不收掉旧 helper,后面的 Rust Web tree shell 会继续背着双轨数据模型前进
---
## 15. 暂不纳入 Phase 4 的内容
下面这些先不要塞进 `Phase 4`
-`Leptos` / `Dioxus` 重写树域 UI
- 独立 Rust Web tree shell
- Sidebar 全量业务壳替换
- 搜索主面板迁移
- AI 主面板迁移
- Mindmap 主画布迁移
这些都属于 `Phase 4` 之后的阶段。
---
## 16. Phase 4 完成的最终标准
只有同时满足下面几条,才建议把 `Phase 4` 标成完成:
- Sidebar 主树、页面树、文件树、picker 都直接消费 kernel projection family
- `buildDocumentTree(...)` 不再处于树域主路径
- 文件树被正式定义为更宽对象投影,而不是第二套树
- tree protocol 已冻结
- React/Next 树域只保留渲染与局部交互状态,不再定义树结构真相
当前已经满足上述标准,因此 `Phase 4` 可以标记为完成;但这不意味着树域 Rust Web shell 已经完成。
---
## 17. 最终结论
`Phase 4` 当前最准确的职责是:
> **先把树域 consumer、协议、旧 helper 边界全部统一,确保树域真正只剩一个 truth 和一套 projection family。**
现在这一步已经完成,所以下一步的:
- Sidebar / 页面树 / 文件树 Rust Web tree shell 重构
才会变成一次稳定的“换壳”,而不是一次边重写协议边重写 UI 的高风险返工。
@@ -0,0 +1,215 @@
# 4-16 [process] 树域 Rust 家族剩余 final runtime checklist v1
> 创建时间:2026-04-27
>
> 来源:`4-9 / 4-10 / 4-11` 中仍未完成的后续目标,以及 `4-12` 到 `4-15` 完成后的剩余边界。
>
> 本文只规划后续目标,不回滚已完成的 3000 inline host、Rust initial DOM、rendererInput/state family 与现有 smoke 覆盖。
## 0. 当前执行状态(2026-04-27
`harness` 本轮从本文拆出的 `task-001``task-009` 已全部执行完成,`harness-progress.txt` 记录为 `completed=9 / pending=0 / in_progress=0 / failed=0`
### 已完成
- Phase K 已完成 `rust_tree_shell_runtime_artifact_v1` artifact 边界,以及 page / filetree / picker 的 Rust-side runtime reducer contract。
- Phase K 已固定 3000 inline host 与 `mnote-web /tree` debug shell 都输出同名 runtime artifact3000 主路径继续使用 same-origin inline host`/api/tree/shell -> mnote-web:3104` debug 直连默认关闭。
- Phase L 已引入 Rust `treeWriteOperation`Convex `documents.move` 优先执行 Rust write operation,并保留 `normalizedMove` 作为兼容 fallback。
- Phase M 已将 `page.body.saved``block.patched``block.moved``block.embedded` 切到 formal `domainEventPlan` payload schema,复合命令 stream delta 使用 `resync_required` 保守策略。
- Phase N 已新增 route thinning manifest 与 boundary 测试,明确 `/api/tree/commands``/api/tree/stream``/api/tree/shell` 的 thin proxy / debug / compat 边界。
- Phase O 已完成 `file_tree` 搜索 hardening`maxResults=80`、祖先补全/排序/异步可见性元数据、空结果文案,以及 `index / asset-folder / asset / mindmap child / book / pdf` fixture 覆盖。
### 仍剩余
- `TreeShellIframeHost` 内联模板中的运行时执行层仍是 compat JS 镜像;已完成的是 Rust artifact/reducer contract,不是完整 wasm/Rust DOM runtime 替换。
- `normalizedMove` fallback 仍保留;后续可在确认所有调用方都稳定传入 `treeWriteOperation` 后删除兼容 fallback。
- `document.snapshot.saved` 仍未作为独立 event type 固定;当前 snapshot 信息随 `page.body.saved` payload 进入 formal schema。
- `tree.node.embed``pageReference` block 组装仍在 3000 route compat 边界内,后续应进入 Page Aggregate / Rust artifact。
- Phase O 的异步索引可见性目前是 projection request meta / move-embed picker 搜索路径契约,不是完整索引指标平台。
## 1. 当前基线
- `harness` task-001 至 task-009 已完成。
- `page tree / file tree / picker` 默认主路径已使用 3000 same-origin inline host,不再请求 `/api/tree/shell -> mnote-web:3104`
- 首屏 DOM 已由 Rust initial renderer 输出:
- `data-rust-page-renderer="initial_v1"`
- `data-rust-filetree-renderer="initial_v1"`
- `data-rust-picker-renderer="initial_v1"`
- 主路径已统一为 `state.items + rendererInput`,不再通过 `__MNOTE_TREE_SHELL_OVERRIDE__` 注入第二份 items。
- page focus、filetree selection、picker state 已暴露并消费 reducer contract;运行时执行仍主要是 compat JS 镜像。
- 3000 inline host 与 `mnote-web /tree` debug shell 已同步输出 `rust_tree_shell_runtime_artifact_v1`,供后续真正 Rust runtime / wasm 替换执行层。
- tree command move 写路径已优先使用 Rust `treeWriteOperation`Convex 继续作为 substrate 与一致性校验层。
- block/save 复合命令已具备 formal `domainEventPlan``resync_required` 保守 delta 合同。
## 2. 后续总目标
把树域从“Rust 持有语义 + compat JS 持有运行时 DOM/state machine”的过渡态,推进到:
- [x] Rust family 持有 renderer runtime / state machine contract。
- [ ] Rust family 真正持有 renderer runtime / DOM 执行层,不再依赖 `TreeShellIframeHost` compat JS 镜像。
- [x] 3000 host 收薄为挂载、认证、transport bridge、artifact writer 与浏览器能力边界。
- [x] Convex compat mutation 不再优先承担 tree command 的核心 legality / normalize / write truth。
- [x] block/save 复合命令进入正式 Rust artifact / domain-event contract。
- [ ] snapshot 独立事件与 Page Aggregate 深层收口继续后续推进。
## 3. Phase K:正式 Rust runtime / wasm 承接 tree shell 运行时
### 目标
完整替换 `TreeShellIframeHost` 内联模板中的主要 compat JS runtime,至少先把 page/filetree/picker 的核心状态机从字符串模板 JS 迁到 Rust runtime/wasm 或等价正式 Rust family runtime。
### checklist
- [x] 定义 runtime artifact 边界:
- [x] 输入:`rendererInput`、projection items、expanded ids、selected row ids、active picker item、focused id。
- [x] 输出:DOM patch / intent event / command dispatch event。
- [x] 事件:focus、keyboard、expand/collapse、selection、context menu、drag/drop、pick。
- [x] 为 page tree runtime 建立 Rust-side 状态机:
- [x] focus normalize / next / previous / home / end。
- [x] expand / collapse / open / context-menu intent。
- [x] drag move intent 与 drop feedback state。
- [x] 为 file tree runtime 建立 Rust-side 状态机:
- [x] selection / range / context selection / normalize visible rows。
- [x] drag row ids / copy-move effect / drop target feedback。
- [x] asset-folder / asset / index / doc 的打开与菜单 intent。
- [x] 为 picker runtime 建立 Rust-side 状态机:
- [x] next / previous / home / end / focus / pick。
- [x] excluded ids 与 root pick。
- [x] 搜索态输入框焦点不被 iframe/runtime 抢走。
- [x] 3000 inline host 与 `mnote-web /tree` debug shell 输出并消费正式 runtime artifact contract。
- [ ] 3000 host 不再维护主路径 JS 镜像状态机;仍需把 `TreeShellIframeHost` 内联模板 compat JS 执行层迁到 Rust wasm/runtime 或等价正式 runtime。
- [x] `mnote-web /tree` debug shell 只保留 debug/internal runtime 验证入口。
### 验收
- [x] `pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx`
- [x] `cargo test -p mnote-web page_renderer filetree_renderer picker_renderer`
- [x] `node scripts/task112-tree-rust-family-regression-smoke.js`
- [x] `node scripts/task113-picker-keyboard-regression-smoke.js`
- [x] smoke 中 `direct_tree_shell_debug_disabled` 仍成立,3000 主路径不请求 3104。
### 当前保留边界
- 这里的完成口径是 artifact / reducer contract / debug 边界完成,不代表 `TreeShellIframeHost` 的 DOM runtime 已完全从 JS template 替换为 Rust wasm。
## 4. Phase Ltree.subtree.move 最终写路径下沉 Rust kernel
### 目标
`tree.subtree.move` 的 legality、target parent、position normalize、canonical sort plan 与最终写入从 Convex compat mutation 继续回收到 Rust kernel / Rust write path。
### checklist
- [x] 梳理当前 `normalizedMove` 写入链:
- [x] `/api/tree/commands` preflightData。
- [x] `bridge-runtime` canonical order plan。
- [x] Convex `documents.move` optional validation。
- [x] Rust kernel 输出最终 write operation
- [x] parent_id。
- [x] sort_order patch set。
- [x] workspace_id / updated_at。
- [x] self / descendant / missing-parent legality error。
- [x] Convex mutation 优先执行 Rust `treeWriteOperation`,退为 substrate 写入执行器与一致性断言层。
- [x] 前端 Rust runtime / command adapter 已透传 `treeWriteOperation`,不再以 documents move compat 语义作为主路径。
- [x] domain event 与 streamDelta 使用 Rust move result,而不是 TS/Convex 再拼。
- [ ] 删除 `normalizedMove` fallback,并把无 `treeWriteOperation` 的旧 compat move 降级为显式 legacy/debug 路径。
### 验收
- [x] `cd rust && cargo test -p bridge-runtime tree_subtree_move_command`
- [x] `cd wolai-frontend && pnpm vitest run src/app/api/tree/commands/route.test.ts src/lib/documents/tree-command-client.test.ts src/lib/tree-stream/tree-delta.test.ts`
- [x] 增加或复用 Convex 层测试,证明 Convex 优先接受 Rust `treeWriteOperation` 并校验当前排序状态。
### 当前保留边界
- `normalizedMove` 仍作为旧调用链 fallback 保留;这不是第二份主排序真相,但仍是后续可删除的 compat 面。
## 5. Phase Mblock/save-snapshot 复合命令 formal artifact 与 domain-event contract
### 目标
`page.body.save / documents.save / blocks.patch|move|embed / snapshot save` 等复合命令补齐最终 formal Rust artifact / domain-event contract,避免停留在 noop 或过渡 hint。
### checklist
- [x] 固定复合命令 event type
- [x] `page.body.saved`
- [ ] `document.snapshot.saved`
- [x] `block.patched`
- [x] `block.moved`
- [x] `block.embedded`
- [x] 固定 payload schema
- [x] document id / page id。
- [x] block ids / patch summary / move source-target。
- [x] snapshot version / content hash / updated_at 作为 `page.body.saved` payload 的一部分。
- [x] Rust command plan 输出正式 `domainEventPlan``streamDeltaHint`,不再只用 noop 占位。
- [x] 3000 adapters 与 Rust artifact writer 按同一 schema 落 `command_logs / domain_events`
- [x] SSE reducer 对这些事件有明确 delta 或保守 resync 策略。
- [ ] 将 snapshot save 是否需要独立 `document.snapshot.saved` 事件定稿,并避免与 `page.body.saved` payload 重叠。
### 验收
- [x] `cd rust && cargo test -p bridge-runtime documents_save_command_plans_include_formal_domain_event_contract block_commands_include_formal_domain_event_contract`
- [x] `cd wolai-frontend && pnpm vitest run src/lib/documents/rust-runtime.test.ts src/lib/documents/page-write-command-adapter.test.ts src/lib/blocks/block-command-adapter.test.ts`
- [x] 至少一个非 noop 复合命令事件可在 command log 与 domain event 中稳定关联同一 `command_id`
### 当前保留边界
- `document.snapshot.saved` 未独立落地;当前完成的是 `page.body.saved` formal payload 中包含 snapshot 摘要。
## 6. Phase N3000 Next route 过渡职责继续收薄
### 目标
`3000` 保持唯一浏览器公开入口,但 Next route 只做薄代理、认证/浏览器能力桥和兼容边界,不继续承担树域主语义、主 delta 或主 renderer runtime。
### checklist
- [x] 盘点 3000 route 中仍在拼树域主语义的入口。
- [x] 将 route 内 action switch 收敛到 Rust command result / artifact writer。
- [x] 对 polling / SSE / snapshot route 标注 Rust 主体与 Next 薄代理边界。
- [x] 隔离只服务旧 compat host 的 runtime 分支,`/api/tree/shell` 默认关闭 debug 直连。
- [x] 保留文件字节读取、upload URL、cookie/auth 等浏览器入口必须职责。
- [ ]`tree.node.embed``pageReference` block 组装从 3000 compat 边界迁到 Page Aggregate / Rust artifact。
### 验收
- [x] `cd wolai-frontend && pnpm vitest run src/app/api/tree/commands/route.test.ts src/lib/tree-stream/tree-delta.test.ts`
- [x] `node scripts/task112-tree-rust-family-regression-smoke.js`
- [x] 新增文档说明哪些 route 是 thin proxy,哪些仍是待迁移 compat`design/04-tree-domain/process/4-17-tree-3000-route-thin-proxy-boundary-v1.md`
### 当前保留边界
- 3000 仍是唯一浏览器公开入口;thin proxy 不等于删除浏览器能力桥。
- `tree.node.embed` 的目标读取与 `pageReference` block 组装仍是明确标注的 compat 待迁移项。
## 7. Phase O:搜索语义 hardening
### 目标
`file_tree` 搜索过滤主链已在 Rust projection query;后续只补更丰富搜索语义和稳定性,不作为当前 renderer/runtime gate。
### checklist
- [x]`index.md / asset / asset-folder / mindmap child / book / pdf` 搜索命中补更多 fixture。
- [x] 明确 `maxResults` 与祖先补全的排序/截断规则。
- [x] 增加异步索引可见性元数据,特别是 move/embed picker 搜索结果路径。
- [x] 空结果与 fallback 文案保持稳定。
- [ ] 如后续需要完整索引平台,再补真实异步索引延迟、刷新和可见性指标,而不是只依赖 projection request meta。
### 验收
- [x] `cd wolai-frontend && pnpm vitest run src/lib/tree-stream/tree-delta.test.ts`
- [x] 搜索 route / projection fixture 覆盖新增资源类型与边界。
### 当前保留边界
- 本阶段完成的是 file tree projection 搜索契约 hardening,不是独立搜索服务或完整索引指标平台。
## 8. 非目标
- 不拆除 Convex substrate。
- 不把 OnlyOffice 嵌入 BlockNote 画布。
- 不恢复旧 React PageTree/FileTree 作为 rust_family 主路径 fallback。
- 不把 3104 重新暴露为默认前端入口。
- 不在前端新增第二套树结构真相或排序真相。
@@ -0,0 +1,72 @@
# 4-17 [process] tree 3000 route thin proxy boundary v1
> 创建时间:2026-04-27
>
> 来源:`4-16` Phase N。
## 1. 结论
`3000` 继续是唯一浏览器公开入口,但 tree route 不再承载长期树域主语义。
- Rust runtime 负责 tree command legality、canonical plan、write operation、domain event plan 与稳定 stream delta 语义。
- Next route 负责 cookie/auth、Convex client、browser/substrate 能力桥、CommandEnvelope、transport、artifact writer 调用与用户可见回包。
- Convex 继续作为 substrate 执行器,不重新生成 tree truth。
## 2. Thin Proxy Route
### `/api/tree/commands`
保留职责:
- 读取 auth / cookie 并获取 Convex client。
- 构造 `CommandEnvelope`
- 调用 `resolveRustBridgeCommandPlan`
- 调用 `executeRustBridgeMutationTransport`
- 调用 `recordRustBridgeCommandArtifacts` / failure artifact writer。
- 对浏览器返回 `requestId / traceId / result`
不得新增:
- 树合法性判断。
- 排序 canonical plan。
- 第二套 tree write truth。
- 长期 streamDelta 主语义拼装。
当前仍保留的 substrate / browser 边界:
- create / copy 后创建页面 scaffold。
- copy 后复制 mindmap 文件。
- move 前采集 sidebar snapshot 作为 Rust preflight data。
- `tree.node.embed` 的目标内容读取与 `pageReference` block 组装。
`tree.node.embed` 的 pageReference 组装仍是待迁移 compat,后续应进入 Page Aggregate / Rust artifact,而不是继续扩写在 route 中。
### `/api/tree/stream`
保留职责:
- 作为浏览器 SSE / polling 入口。
- 读取 bridge overview。
- 优先转发 command log / domain event 中的 Rust `streamDelta`
- 缺少稳定 delta 或 command/event 不一致时保守 `resync`
不得新增:
- 在 Next 层重新解释树排序或 projection truth。
- 为复合命令伪造局部树补丁。
## 3. Debug / Compat Route
### `/api/tree/shell`
定位:
- 仅显式 debug/internal runtime 验证入口。
- 3000 主路径不应默认请求 `mnote-web:3104`
- 主路径继续使用 same-origin inline host 与 Rust runtime artifact。
## 4. 代码锚点
- `wolai-frontend/src/lib/tree-route-boundary.ts` 固定 3000 route boundary manifest。
- `wolai-frontend/src/app/api/tree/commands/route.test.ts` 绑定 tree commands route 的 thin proxy 定位。
- `wolai-frontend/src/lib/tree-stream/tree-delta.ts` 明确 `resync_required` 为保守等待 snapshot/resync 的 delta。