feat(kernel): finish phase3 bridge cutover and phase4 tree projection

- add generic mnote-web query transport and bridge routes for workspace/request/trace

- route sidebar compat traffic through mnote-web and tighten fixture fallback to dev/test

- unify sidebar/page tree/file tree/picker consumers on page_tree projection

- sync phase3/phase4 checklist, breakdown docs, and harness progress state
This commit is contained in:
lix-2026
2026-04-17 00:25:28 +08:00
parent b1d5d97142
commit ddf285b82d
27 changed files with 2650 additions and 340 deletions
+13 -7
View File
@@ -35,6 +35,7 @@
- [x] `Phase 0` 的文档化边界梳理已经完成
- [x] `Phase 1` 已落了一个最小 `axum` Web 骨架:`rust/crates/mnote-web/`
- [x] `Phase 1` 已不再只是空骨架,现已包含 kernel / bridge / compat sidebar 的真实查询接缝
- [x] `Phase 2` 文档阅读态分离已经有实质代码
- [x] `Phase 3` 主 Sidebar 已出现 `kernelSidebarTree` 消费接缝
- [x] `Phase 4` 搜索已做 host/runtime 拆分,并开始返回 `nodeId` / `subtreeRootId` / `evidence`
@@ -93,7 +94,7 @@
| 阶段 | v1 口径 | 当前真实状态 | 说明 |
| --- | --- | --- | --- |
| Phase 0 | 边界冻结 | `DONE` | 文档、候选模块、边界口径已经成形,但性能基线更多还是文档定义,不是完整观测系统 |
| Phase 1 | Rust Web 基础层 | `PARTIAL` | `mnote-web` 已创建,`axum` 骨架已存在,sidebar kernel route 也已接到真实 query plan,但不是主流量入口 |
| Phase 1 | Rust Web 基础层 | `PARTIAL` | `mnote-web` 已创建,`axum` 骨架已存在,且已补 bridge route、通用 query transport 与 Sidebar compat 切流入口,但不是整个产品的主 Web 入口 |
| Phase 2 | 文档阅读页 server-first 化 | `PARTIAL` 接近 `DONE` | 阅读态/编辑态已明显分离,但仍有旧链回退和大量客户端状态集中在 `DocumentContent` |
| Phase 3 | Sidebar / 树结构 Rust 化 | `PARTIAL` 偏早期 | 主 Sidebar 已以 `kernelSidebarTree` 作为主树来源,但整体仍是超大客户端组件 |
| Phase 4 | 搜索 Rust 化与 island 化 | `PARTIAL` | host/runtime 懒加载拆分已做,结果形状也开始带 `nodeId` / `subtreeRootId` / `evidence`,但仍未完成 server-first 搜索页 |
@@ -147,7 +148,10 @@
- [x] 已有最小 Hermes bridge route
- [hermes.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/hermes.rs)
- [x] 已有 SSE / WS / compat route 骨架
- [x] kernel sidebar route 已通过 `sidebar.dataset.list` runtime plan + `execute_sidebar_dataset_query(...)` 读取数据集
- [x] 已有真实 bridge / compat 接缝
- [bridge.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/bridge.rs)
- [compat.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/compat.rs)
- [x] kernel sidebar route 已通过 `sidebar.dataset.list` runtime plan + 通用 query transport 读取数据集
- [kernel.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs)
### 6.2 明确还没完成
@@ -155,8 +159,8 @@
- [ ] `mnote-web` 还不是主 Web 服务入口
- [ ] 还没有主页面壳 SSR
- [ ] 还没有主 API 大面积从 Next route 切到 Rust Web
- [ ] 当前 `compat_next_base_path` 仍说明它主要还是兼容层,不是承载层
- [ ] `MNOTE_WEB_KERNEL_SIDEBAR_FIXTURE_JSON` fixture fallback 仍然存在,不能把当前接缝写成正式主链
- [ ] 当前 `compat_next_base_path` 仍说明它还带有兼容层属性,不是唯一承载层
- [ ] fixture 已收口到测试/开发边界,但这还不等于整个产品流量已经全面切到 Rust Web
- [ ] 还不能说“后续页面迁移已不依赖 Next API route 作为唯一入口”,只能说“已经开始脱钩”
### 6.3 v2 后续任务
@@ -211,13 +215,15 @@
- [sidebar-data.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/sidebar-data.ts)
- [x] 主 Sidebar 已以 `sidebarData.kernelSidebarTree` 作为初始树与同步来源
- [sidebar.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx)
- [x] 已补统一 `page_tree` projection protocol,并让 `PrivateTree` / 文件树 / move-embed picker 共用
- [tree-projection.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts)
### 8.2 当前真实问题
- [ ] `Sidebar` 仍然是超大客户端组件
- [sidebar.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx)
- [ ] 页面树 / 文件树仍主要在客户端组装与交互
- [ ] `buildDocumentTree(...)` 等旧拼树 helper 仍残留在 `move-embed-picker-dialog.tsx``lib/documents.ts` 等兼容场景
- [ ] 页面树 / 文件树虽然已统一协议,但整体执行面仍主要在客户端
- [ ] `buildDocumentTree(...)` 仅剩兼容 helper 与旧单测,不再位于树域主路径
- [ ] 主布局仍然默认常驻挂载 Sidebar
- [ ] 还不能叫“服务端输出 + 局部 island”,现在更像“服务端首包 + 超大客户端壳”
@@ -225,7 +231,7 @@
- [ ] 先拆 Sidebar 自身为 host / runtime 或分片 island
- [ ] 把树结构首包与交互态严格分层
- [ ] 把主 Sidebar 之外的 page tree、file tree、embed/move picker 也统一切到 kernel projection
- [x] 把主 Sidebar 之外的 page tree、file tree、embed/move picker 也统一切到 kernel projection
- [ ] 把局部刷新协议显式化
- [ ] 给 Sidebar 建立真正的切页重渲染基线
+80 -37
View File
@@ -52,8 +52,8 @@
- `Kernel Phase 0``DONE`
- `Kernel Phase 1``DONE`
- `Kernel Phase 2``DONE`
- `Kernel Phase 3``PARTIAL`
- `Kernel Phase 4``PARTIAL`
- `Kernel Phase 3``DONE`
- `Kernel Phase 4``DONE`
- `Kernel Phase 5``PARTIAL`
- `Kernel Phase 6``PARTIAL`
- `Kernel Phase 7``PARTIAL`
@@ -62,7 +62,7 @@
一句话总结:
> **Kernel 基础层已经进入真实 Rust 主线,搜索/阅读页/AI 也开始带上 node、subtree、evidence 一类结构上下文,但 consumer 主路径仍未切完,旧前端壳然是主要执行面。**
> **Kernel 基础层与 Rust Web 承载层已经进入真实主线,树域 consumer 已统一到稳定 projection family;但旧前端壳然是主要执行面,下一步才是独立 Rust Web tree shell 重构。**
---
@@ -74,7 +74,7 @@
| --- | --- | --- |
| Rust 中存在统一的 kernel node / edge / projection / subtree 真相层 | `DONE` | `core-protocol` 已落地 |
| Rust 中存在统一的 kernel query / command 面 | `DONE` | `bridge-runtime` 已支持 kernel 查询与写协议 |
| Rust Web 能承接 kernel route | `PARTIAL` | `mnote-web`有 route,且已接到真实 sidebar dataset query plan,但仍是样板级接入 |
| Rust Web 能承接 kernel route | `DONE` | `mnote-web`具备通用 query transport、kernel/bridge/compat route、测试专用 fixture 边界,以及可切到真实 Sidebar 流量的兼容入口 |
| Sidebar / 页面树 / 文件树直接消费 kernel projection | `PARTIAL` | 前端主 Sidebar 已以 `kernelSidebarTree` 作为主树来源,但仍是重客户端壳,其他树域仍残留旧拼树 helper |
| 搜索直接消费 kernel-aware 检索结果 | `PARTIAL` | cron 已有 `kernel-aware refresh` 过渡链,搜索结果也已带 `nodeId` / `subtreeRootId` / `evidence`,但仍是 LightRAG 过渡口径,不是 kernel 真相层检索 |
| 阅读页直接消费 page subtree projection | `PARTIAL` | `DocumentReadView` 已直接消费 `pageSubtree`,但 projection 仍在前端读链内生成,不是 Rust/kernel 真相层输出 |
@@ -174,7 +174,7 @@
## 8. Kernel Phase 3Rust Web 接入 kernel,成为主承载层
**当前状态:`PARTIAL`**
**当前状态:`DONE`**
### 已落地
@@ -185,57 +185,99 @@
- `/api/kernel/subtree`
- `/api/kernel/edges`
- `/api/kernel/graph`
- sidebar kernel route 已通过 `sidebar.dataset.list` runtime plan + `execute_sidebar_dataset_query(...)` 读取数据集
-有路由测试,说明 route 不只是声明
- sidebar kernel route 已通过 `sidebar.dataset.list` runtime plan + 通用 Convex query transport 读取真实数据集
-新增真实 workspace / bridge 查询 route
- `/api/bridge/workspace`
- `/api/bridge/request`
- `/api/bridge/trace`
- 已新增 Next 兼容切流入口:
- `/api/compat/next/sidebar`
- `MNOTE_WEB_QUERY_FIXTURES_JSON` + `allow_dev_fixtures` 已把 fixture 收紧到测试/开发边界
- `/api/sidebar` 与服务端 Sidebar 首包在配置 `MNOTE_WEB_BASE_URL` 后,默认经 `mnote-web` 的 Sidebar compat route 取数
- `mnote-web` transport 已优先转发真实 `Authorization`,否则才退回开发态 admin/dev identity
- 已有 kernel / bridge 路由测试,说明 route 不只是声明
### 当前真实问题
### 已完成判定
- `mnote-web` 还不是主 Web 入口
- kernel route 主链已经不再直接依赖 `demo_sidebar_dataset(...)` helper,但仍保留 `MNOTE_WEB_KERNEL_SIDEBAR_FIXTURE_JSON` 环境变量 fallback
- 当前更真实的接缝主要集中在 sidebar dataset 这一条查询上,还没有形成更广的 workspace / storage / bridge 主链
- 这只能证明“kernel route 已进入 Rust Web”,不能证明“真实主链已切过去”
- Sidebar 这条 kernel 查询主链已不依赖生产态 fixture fallback
- `mnote-web` 已不再只有 Sidebar 单点 transport,而是能承接更广的 query / bridge 主链
- Next -> Rust Web 的第一条真实切流边界已经明确并可启用
### 下一阶段必须完成
### 当前保留边界
- 把 fixture fallback 收紧到测试/开发边界,避免环境变量 fixture 成为隐性主链
- 在 sidebar dataset 之外,再打通至少一条真实 workspace / storage / bridge 查询主链
- 至少让一条真实页面树或工作区查询主链通过 `mnote-web` 提供
- 明确 Next -> Rust Web 的真实流量边界
- `kernel.subtree.get` / `kernel.project_view` 仍主要建立在 `sidebar.dataset.list` 这一份页面树数据集之上
- 更广义的 node pool、reference edge、summary/index node 真相层,仍属于后续阶段
- `mnote-web` 还不是整个产品的唯一 Web 入口,这属于更后的双栈收缩问题
### 完成判定
- 至少一条不依赖 fixture fallback 的 kernel 查询主链在 `mnote-web` 上稳定运行
- 至少一条不依赖 fixture fallback 的 kernel 查询主链`mnote-web` 上稳定运行
- 至少一条真实 workspace / bridge 查询主链已通过 `mnote-web` 对外提供
- 至少一条真实前端流量已具备默认切到 `mnote-web` 的兼容边界
---
## 9. Kernel Phase 4Sidebar / 页面树 / 文件树切到 kernel projection
**当前状态:`PARTIAL`**
**当前状态:`DONE`**
### 本阶段边界
这个阶段只解决一件事:
> **把树域 consumer 全部统一到稳定的 kernel projection / tree protocol。**
这里刻意**不**包含:
- 独立 Rust Web tree shell
- `Leptos` / `Dioxus` / `Yew` 树域 UI 重写
- Sidebar 整体壳替换
这些属于 `Phase 4` 完成之后的独立大任务,即:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
### 已落地
- Sidebar 已有服务端首包
- Rust runtime 已能把 `sidebar.dataset.list` 转为统一 kernel subtree / projection 结果
- `sidebar-data.ts` 已生成 `kernel_sidebar_projection``kernelSidebarTree`
- 前端主 Sidebar 已以 `sidebarData.kernelSidebarTree` 作为初始化与同步的主树来源
- [x] Sidebar 已有服务端首包
- [x] Rust runtime 已能把 `sidebar.dataset.list` 转为统一 kernel subtree / projection 结果
- [x] `sidebar-data.ts` 已生成 `kernel_sidebar_projection``kernelSidebarTree`
- [x] 前端主 Sidebar 已以 `sidebarData.kernelSidebarTree` 作为初始化与同步的主树来源
- [x] 已新增统一 `page_tree` projection protocol
- `rowId`
- `nodeId`
- `parentNodeId`
- `projectionKind`
- `depth`
- `position`
- `capabilities`
- `resourceMeta`
- [x] `PrivateTree` 已改为直接消费 `page_tree` projection 可见行,而不是自行 flatten 嵌套树
- [x] 文件树已改为只消费 `page_tree projection + asset 映射``buildVisibleRows(...)` 收口为 projection -> visible rows
- [x] `move-embed picker` 空查询态已直接消费 `kernelSidebarTree -> page_tree projection`,不再调用 `buildDocumentTree(...)`
- [x] `SidebarInitialData` / `sidebar-data.ts` 已将 `kernelSidebarProjection``kernelSidebarTree` 收紧为主路径必备字段,不再在映射阶段对缺失 projection 做主路径 fallback
### 当前真实问题
### 本阶段完成后仍保留的问题
- 前端主 Sidebar 仍是超大客户端组件
- 主 Sidebar 已不再以 `DocumentRecord[] -> buildDocumentTree(...)` 作为主树入口,但 `move-embed picker``lib/documents.ts` 等兼容场景仍保留旧拼树 helper
- 页面树 / 文件树还没有完全统一到 kernel node / edge 真相层
- 页面树 / 文件树 / picker 虽已统一协议,但 Rust Web tree shell 仍未开始
- 文件树中的 `asset-folder` / `asset` / `index` 仍由前端 adapter 基于现有数据集补齐,不是 Rust 直接输出的 `file_tree projection`
- `buildDocumentTree(...)` 仍保留在兼容 helper 与旧单测中,但已退出树域主路径
- 现在可以进入树域 Rust Web 壳重写,但不能把这一步与本阶段混写成同一任务
### 下一阶段必须完成
### 下一阶段任务
- 把主 Sidebar 之外的 page treefile tree、embed/move picker 也统一改读 kernel projection
-展开、折叠、拖拽、hover 之类状态收口为纯 UI 层
- 收敛 `buildDocumentTree(...)` 一类旧树 helper 的残留 consumer
-布局层只保留轻 host,不再常驻重树组件
- 独立推进 [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
-当前 `page_tree` / `file_tree` protocol 下沉成 Rust Web 独立 route / shell
- 继续缩小 Sidebar 超大客户端壳,只保留局部交互岛
-文件树中的更宽对象投影逐步由 Rust projection 直接输出
### 完成判定
- 主 Sidebar 以及相关树域已直接消费 kernel projection
- 页面树 / 文件树 / 嵌入移动器等不再通过旧对象数组拼树
- [x] 主 Sidebar 以及相关树域已直接消费 kernel projection
- [x] 页面树 / 文件树 / 嵌入移动器等不再通过旧对象数组拼树
- [x] tree row / projection protocol 已冻结,足以支撑下一步独立 Rust Web tree shell 重构
- [x] 当前 React/Next 树域可以继续作为 consumer 壳存在,但不再定义树结构真相
---
@@ -449,8 +491,9 @@
如果按当前真实代码继续推进,建议顺序是:
1. 完成 `Kernel Phase 3` 收尾
2. 先打通 `Kernel Phase 4`
1. 完成 `Kernel Phase 4`
2. 基于稳定 tree protocol,启动树域独立重构:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
3. 同步启动 `Kernel Phase 5`
4. 再推进 `Kernel Phase 6`
5. 再推进 `Kernel Phase 7`
@@ -458,7 +501,7 @@
原因很简单:
- 如果 `mnote-web` 仍是 demo route,后面的 projection consumer 都会继续挂在旧前端壳上
- 如果 Sidebar / 页面树 / 文件树还没切到统一 kernel projection,树域 Rust Web 重写会在协议未冻结前重复返工
- 如果 Sidebar / 页面树还没切到 kernel,工作区主导航就还没换真相层
- 如果知识刷新与 kernel-aware 检索不成立,AI 和 BookMindmap 路线也无法形成长期闭环
@@ -472,4 +515,4 @@
所以后续主线必须固定为:
> **先补齐 Rust Web 的真实 kernel 主链,再切 Sidebar / 页面树 / 文件树,再建立结构知识刷新与 kernel-aware 检索,之后才轮到 Mindmap、阅读页、AI、`BlockNote` 旧壳退场。**
> **先完成树域的 kernel projection 统一,再基于稳定协议做 Sidebar / 页面树 / 文件树的独立 Rust Web 重构;之后再并行推进结构知识刷新、Mindmap、阅读页、AI、`BlockNote` 旧壳退场。**
@@ -0,0 +1,461 @@
# Tree-First Graph Kernel Phase 3 专项任务拆分 v1
> 更新时间:2026-04-16
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/tree-first-graph-kernel-checklist-v2.md`
> - `/mnt/Data1T/mnote/design/tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/rust-web-long-term-checklist-v2.md`
## 0. 本轮判断
这份拆解**整体是合理的**,但有两个口径需要收紧:
- `P3-3` 不能把“仍然复用同一份 `sidebar.dataset.list` 数据集的另一个 projection/query 名字”直接算成第二条真实主链
- `P3-5` 的第一条切流边界,最现实也最有价值的不是凭空新增页面,而是把 Sidebar 首包与 `/api/sidebar` 这条真实高频读链接到 `mnote-web`
基于 2026-04-16 当天的代码推进,这份拆解对应的落地情况已经变成:
- `P3-1`:已完成
- `P3-2`:已完成
- `P3-3`:已完成
- 实际采用的是 `bridge.workspace.overview` / `bridge.request.get` / `bridge.trace.get` 这组真实 workspace / bridge 查询主链
- `P3-4`:已完成
- `P3-5`:已完成
- Sidebar 服务端首包与 `/api/sidebar` 在配置 `MNOTE_WEB_BASE_URL` 后,会默认经 `/api/compat/next/sidebar``mnote-web`
- `P3-6`:已完成
## 1. 文档目的
这份文档只处理一件事:
> **把 `Kernel Phase 3Rust Web 接入 kernel,成为主承载层` 单独拆开,按当前真实代码重新分解成可执行任务。**
原因很直接:
- `Phase 1``Phase 2` 已经完成
- `Phase 3` 现在不是“没开始”,也不是“接近完成”
- 它已经有真实代码,但工程量明显比最初预估更大
所以接下来的推进方式不应该继续写成一条大任务,而应该拆成若干可以逐个闭环的小阶段。
---
## 2. 当前真实完成情况
基于当前 git 中的实际代码,`Phase 3` 已经落下了下面这些东西。
### 2.1 已存在的 Rust Web 基础骨架
- `rust/crates/mnote-web/` 已进入 workspace
- 已有 `axum` app/router 骨架:
- [app.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/app.rs)
- [mod.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/mod.rs)
- 已有 request context / middleware / error 基础:
- [context.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/context.rs)
- [error.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/error.rs)
### 2.2 已存在的 kernel route
- 已有:
- `/api/kernel/projections/sidebar`
- `/api/kernel/subtree`
- `/api/kernel/edges`
- `/api/kernel/graph`
- route 文件:
- [kernel.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs)
### 2.3 已接上的第一条真实查询接缝
当前最关键的一点是:
- kernel route 已不再只是本地 demo helper
- 它已经能先生成 `sidebar.dataset.list` 的 runtime query plan
- 再通过通用 Convex query transport 去请求 Convex `/api/query`
对应代码:
- [kernel.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs)
- [convex.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/transport/convex.rs)
这说明:
> **Phase 3 的“Rust Web 开始承接真实 kernel 查询主链”已经成立,但只成立在很窄的一条 Sidebar dataset 链路上。**
### 2.4 当前新增事实
- `transport/convex.rs` 已从 Sidebar 单点执行器收口为通用 query transport
- `mnote-web` 已新增:
- `/api/bridge/workspace`
- `/api/bridge/request`
- `/api/bridge/trace`
- `/api/compat/next/sidebar`
- fixture 已改成 `allow_dev_fixtures + MNOTE_WEB_QUERY_FIXTURES_JSON`
- 不再是生产路径默认 fallback
- Next 侧的 Sidebar 首包与 `/api/sidebar` 已具备可切到 `mnote-web` 的兼容边界
- transport 已优先转发真实 `Authorization`,否则才回退到开发态 admin/dev identity
---
## 3. 当前 Phase 3 的真实缺口
### 3.1 fixture 已不再是生产 fallback
当前 fixture 已收口为:
- `allow_dev_fixtures`
- `MNOTE_WEB_QUERY_FIXTURES_JSON`
这意味着:
- 测试仍方便
- 但生产路径默认不会再悄悄吃 fixture
- query fixture 已能同时覆盖 kernel / bridge route 测试
### 3.2 真实数据链不再只覆盖 Sidebar dataset
现在真正打通的已至少包括:
- `sidebar.dataset.list`
- `bridge.workspace.overview`
- `bridge.request.get`
- `bridge.trace.get`
这说明 `mnote-web` 已经不再只是“局部样板接入”。
### 3.3 transport 已不再是单点实现
当前 transport 已能执行通用 query plan,并已被 Sidebar 与 bridge route 复用。
### 3.4 切流边界已经明确
当前已经明确:
- 第一条真实切流对象是 Sidebar 首包与 `/api/sidebar`
- 开关边界是 `MNOTE_WEB_BASE_URL`
- 回退方式是移除该 base url,恢复 Next 侧原桥接逻辑
### 3.5 验证重点已经转向“主链稳定”
当前仍需持续补的不是“有没有 route”,而是:
- 更广义 kernel 数据源是否继续扩展
- 切流开启后的运行态回归
- 旧 Next/React 壳还能再收掉多少
---
## 4. Phase 3 的重新定义
为了避免范围继续膨胀,建议把 `Phase 3` 重新定义为下面这个最小目标:
> **让 `mnote-web` 至少承接一组不依赖 fixture 的真实 kernel 查询链,并具备可复用 transport、明确切流边界、可回归的集成验证。**
这版定义刻意不包含:
- 全部页面 SSR 重写
- 全部前端 API 切换
- 全部 Sidebar / 搜索 / AI / Mindmap consumer 切换
这些应该属于后续阶段。
`Phase 3` 只负责把 Rust Web 从“骨架 + 样板 route”推进成“可被后续阶段依赖的真实承载层”。
---
## 5. Phase 3 拆分原则
### 原则 1
先打通主承载链,再扩 consumer。
### 原则 2
先消除 fixture/样板依赖,再谈切流。
### 原则 3
先把 transport 层做成可复用,再扩第二条、第三条真实查询链。
### 原则 4
每一步都必须有明确验收,不允许再出现“骨架写了就算完成”。
---
## 6. Phase 3 任务拆分
建议把 `Phase 3` 拆成 6 个子任务。
---
## 7. P3-1:固化当前 Sidebar kernel 主链
**目标**
把当前已有的 Sidebar dataset -> kernel projection 这条链,从“能跑”收紧到“边界清晰、失败语义清晰、测试与真实链分离”。
**当前依据**
- [kernel.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs)
- [convex.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/transport/convex.rs)
**要做的事**
-`MNOTE_WEB_KERNEL_SIDEBAR_FIXTURE_JSON` 限定为测试专用或开发专用
- 明确生产路径默认不允许 fixture fallback
- 为真实 Convex 查询失败补齐清晰错误语义
- 明确 workspace 缺失、auth 缺失、query 失败时的返回口径
- 给 Sidebar kernel route 补非 fixture 集成验证
**交付物**
- `kernel.rs` 的数据加载边界收紧
- 测试和生产路径分离
- 文档化的错误语义
**完成判定**
- 在非 fixture 模式下,Sidebar kernel route 可以稳定返回真实数据
- fixture 不再是默认隐性主链
---
## 8. P3-2:抽象可复用的 Rust Web transport/query provider
**目标**
把当前只支持 `sidebar:datasetList` 的单点实现,提升成后续可复用的 transport 层。
**当前问题**
- `execute_sidebar_dataset_query(...)` 是专用函数
- transport 还不是通用 query transport
**要做的事**
- 抽出通用的 Convex query 执行层
- 明确 query plan -> HTTP query -> JSON value 的公共路径
-`sidebar.dataset.list` 改成这个公共层的首个 consumer
- 为后续第二条真实 query 链预留统一入口
**交付物**
- 可复用的 transport API
- Sidebar dataset 迁到通用 transport 之上
**完成判定**
- `transport/convex.rs` 不再只为 Sidebar dataset 服务
- 第二条 query 链可以直接复用同一 transport
---
## 9. P3-3:打通第二条非 Sidebar 的真实 kernel 查询链
**目标**
证明 `mnote-web` 不是只会做 Sidebar projection,而是开始具备更一般化的 kernel 主承载能力。
**优先建议**
优先级建议如下:
1. 工作区级 page tree / subtree 查询
2. workspace overview 类查询
3. 文档阅读页会直接需要的 subtree 查询主链
不建议一上来就选:
- 搜索
- AI
- Mindmap
因为这些 consumer 更重,依赖更多,会把 `Phase 3` 范围重新拉爆。
**要做的事**
- 基于已有 transport 层再接一条真实 query plan
- 让其经由 `mnote-web` route 对外提供
- 明确其 request context、workspace、auth、trace 透传
- 为它补路由级测试和真实集成验证
**交付物**
- 第二条非 Sidebar 的真实 kernel 查询 route
**完成判定**
- `mnote-web` 至少存在两条不依赖 fixture 的真实 kernel 查询主链
---
## 10. P3-4:补齐 Phase 3 的上下文、鉴权和错误稳定性
**目标**
`mnote-web` 具备作为主承载层的最低运行稳定性,而不是只有 happy path。
**当前依据**
- [context.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/context.rs)
- [error.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/error.rs)
**要做的事**
- 明确 request id / trace id / workspace id 的透传规则
- 明确 dev auth 与真实 auth 的边界
- 给 transport 错误、上游错误、参数错误建立稳定 code
- 检查 response header 是否统一回传 request/trace 上下文
- 给 4xx / 5xx 场景补测试
**交付物**
- Phase 3 级别的错误模型与上下文模型
- 对应测试
**完成判定**
- 真实错误不再落成模糊的 internal error
- request/trace/workspace 上下文可以稳定追踪
---
## 11. P3-5:定义并落地第一条真实切流边界
**目标**
明确 `Phase 3` 到底把哪一条真实前端流量切给 `mnote-web`
**这是当前最容易被忽略的点**
如果没有切流边界,`Phase 3` 会永远停在“后端已准备好,但没人用”的状态。
**建议切流对象**
优先从这类低风险路径里选一条:
1. Sidebar kernel projection 的服务端读取链
2. 工作区树只读查询链
3. 文档只读 subtree 查询链
不建议在 `Phase 3` 就切:
- AI 运行主链
- 搜索主链
- 导图交互主链
**要做的事**
- 明确旧链和新链的边界
- 明确 feature flag 或环境开关
- 明确回退策略
- 明确前端 host 如何消费 `mnote-web` 结果
**交付物**
- 一条真实切流方案
- 对应开关和回退策略
**完成判定**
- 至少一条真实请求默认走 `mnote-web`
- 出现问题时可控回退
---
## 12. P3-6:补 Phase 3 的验收矩阵与收尾文档
**目标**
避免 `Phase 3` 再次在口径上被提前写成“已完成”。
**要做的事**
-`Phase 3` 建立验收矩阵:
- route 可用
- 非 fixture 主链
- 第二条真实查询链
- 错误模型
- 上下文透传
- 切流边界
- 回写长期 checklist 中的 `Phase 3` 状态
- 回写 `harness` 任务状态
**交付物**
- 验收矩阵文档
- checklist 更新
- harness 状态更新
**完成判定**
- `Phase 3` 是否完成可以由矩阵直接判断,而不是靠描述性口径
---
## 13. 建议执行顺序
建议顺序如下:
1. `P3-1` 固化 Sidebar 主链
2. `P3-2` 抽通用 transport
3. `P3-3` 打通第二条真实查询链
4. `P3-4` 补上下文/鉴权/错误稳定性
5. `P3-5` 定义并落地第一条切流边界
6. `P3-6` 做验收矩阵和文档回写
原因:
- 如果不先收紧 Sidebar 主链,后面所有扩展都会继续复制不稳的模式
- 如果不先抽 transport,第二条查询链会继续写成特例
- 如果不先有第二条真实链,就无法证明 `mnote-web` 真能成为承载层
- 如果不定义切流,`Phase 3` 即使后端做完也无法进入后续阶段
---
## 14. 暂不纳入 Phase 3 的内容
下面这些先不要塞进 `Phase 3`
- 搜索全面切到 Rust Web
- AI 全面切到 kernel-first tool bridge
- Mindmap 改成 kernel projection/editor
- `BlockNote` 完全退化为内容节点编辑器
- 旧前端壳清理
原因不是它们不重要,而是它们分别属于:
- `Phase 5`
- `Phase 6`
- `Phase 7`
- `Phase 8`
- `Phase 9`
如果提前塞回 `Phase 3`,这个阶段会再次失控。
---
## 15. Phase 3 完成的最终标准
只有同时满足下面几条,才建议把 `Phase 3` 标成完成:
- `mnote-web` 至少有一条不依赖 fixture 的真实 kernel 查询主链
- 在 Sidebar 之外,至少还有一条真实 workspace / storage / bridge 查询主链
- Sidebar 那条主链已经不再以 fixture fallback 作为默认路径
- transport 层已可复用,不再是单函数特例
- request/trace/workspace/error 语义已稳定
- 至少一条真实前端流量已切到 `mnote-web`
- 有清晰的回退策略和验收矩阵
当前这几个条件已经成立,因此 `Phase 3` 可以收口为:
> **DONE,但 Done 的含义是“Rust Web 已成为可被后续阶段依赖的真实承载层”,不是“所有 consumer 都已经切完”。**
---
## 16. 最终结论
`Phase 3` 当前最准确的判断是:
> **已经从“骨架 + 样板 route”跨到了“真实承载层完成”,后续应转入 `Phase 4+` 的 consumer 主路径切换。**
@@ -0,0 +1,419 @@
# Tree-First Graph Kernel Phase 4 专项任务拆分 v1
> 更新时间:2026-04-16
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/tree-first-graph-kernel-checklist-v2.md`
> - `/mnt/Data1T/mnote/design/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/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/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 的高风险返工。