Phase A — EditorRuntimeActor 内存缓存层 - 新增 editor_actor.rs: EditorBlockDocument 内存态 + apply_command + load_or_init - block.rs 四个写工具(replace/insert/delete/move)接入 actor 路径 - editor_actor feature flag(MNOTE_WEB_ENABLE_EDITOR_ACTOR=true 默认开启) - bridge-runtime 三个核心函数公开化 - rust-toolchain: 1.89 → stable(修复 spike WASM 编译阻塞) Phase B — 编辑器增量 delta channel - BlockDelta/DeltaOperation 类型 + actor.build_block_delta() - leptos-tiptap spike: mnote:editor:block-delta CustomEvent 监听 + JSON patch - DocumentAiAgentPanel: 拦截 blockDelta → window dispatchEvent - 工具响应含 blockDelta 字段供前端消费 Phase C — 事件 stream delta - broadcast channel 在 AppState/actor/SSE 三层贯通 - tree_events SSE 端点发 block.delta 事件 - 旧客户端降级兼容 环境修复 - rustc recursion_limit = 1024(修复 Leptos SSR 类型深度溢出) - run-convex-deploy.js(封装 Convex function 部署到本地后端 3210) ref: design/07-ai/process/7-13-page-block-editor-runtime-actor-v1.md
15 KiB
4-24 [done] Resource Tree / File Tree / Page Tree 真源合同与执行清单 v1
更新时间:2026-05-13
2026-05-15 口径更新:
- 本文件完成时的
index.md可见 UI 模型已被/mnt/Data1T/mnote/design/04-tree-domain/done/4-35-convex-filetree-title-md-source-alignment-v1.md覆盖。- 后续 Convex File Tree 默认页面正文行显示为
{title}.md,rowId 为doc:<documentId>;本文中的index.md仅表示历史正文 object identity / 兼容语义,不再作为默认可见子行继续扩展。上游依据:
/mnt/Data1T/mnote/design/10-review/05-tree.md/mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md/mnt/Data1T/mnote/design/04-tree-domain/done/4-20-vscode-explorer-file-tree-alignment-v1.md/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md/mnt/Data1T/mnote/design/10-review/README.md
1. 目标
本清单用于把 design/10-review/05-tree.md 中的判断落到树域主线:
Resource Tree是长期对象组织真源,由 Rust kernel 持有语义。File Tree是 Resource Tree 的主组织投影,展示页面、index.md、附件、mindmap、OnlyOffice、代码附件等资源。Page Tree是页面导航投影 / 快捷视图,不拥有排序、父子、附件归属或资源归属的最终真相。- Sidebar、文件树 UI、页面树 UI 都只能消费 projection 和发 command,不能重新拼第二套结构真相。
2. 术语冻结
Resource Tree:workspace 内对象与资源的 canonical hierarchy。它不是 UI 文件树组件,而是 kernel 层的资源组织语义。File Tree:Resource Tree 面向 VS Code Explorer 体验的 projection。Page Tree:Resource Tree 中页面对象的导航 projection,可做快捷显示,但不拥有资源归属。ObjectIdentity:打开、保存、草稿、tab、command 使用的对象身份。BlockAssetRelation:页面正文 block 与资源对象之间的稳定关联,例如documentId + blockId + assetId + assetKind。
3. 非目标
- 不在本阶段重写 Convex 底层存储。
- 不在本阶段一次性删除所有 compat/fallback。
- 不把文件树 UI 组件提升为真源。
- 不让 Page Tree 接管附件、mindmap 或 OnlyOffice 的归属。
4. 前置顺序与执行门
本清单是 5-12 的前置合同。执行顺序固定为:
- 先完成本文件的
Resource Tree / File Tree / Page Tree真源合同。 - 再执行
design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md中的主编辑区 object tab、草稿隔离和打开路径调整。
不允许绕过本文件直接在编辑区里新增第二套 asset / page 归属判断。编辑区可以先做 mindmap 止血,但止血代码必须只消费本文件定义的 object identity / file tree intent,不得把临时前端判断提升为长期合同。
当前代码基线:
rust/crates/core-protocol/src/kernel.rs已有KernelProjectionResourceKind、KernelProjectionAssetKind、KernelProjectionResourceMeta、KernelProjectionItem。rust/crates/bridge-runtime/src/lib.rs已有build_file_tree_projection_result、make_projection_resource_meta、normalize_file_tree_asset等 file tree projection 构造逻辑。rust/crates/mnote-web/src/routes/tree.rs与rust/crates/mnote-web/src/ssr/pages/layout.rs已消费resourceMeta并发出tree.asset.open。rust/crates/mnote-web/src/ssr/pages/layout.rs当前仍把 mindmap asset 主路径导航到/mindmap/{documentId}/{assetId},这是5-10bug 的直接落点,但长期合同归属仍应先在本文件冻结。
阶段门:
- G1:协议能表达
index.md、mindmap、OnlyOffice、普通附件、代码附件的不同 object identity。 - G2:
file_treeprojection row 能稳定输出 object identity 或等价resourceMeta.extra.objectIdentity。 - G3:
page_treeprojection 被约束为页面导航投影,不承载 asset 归属。 - G4:
tree.asset.open只表达打开资源对象的 intent,不决定“把资源当正文打开”。 - G5:完成 G1-G4 后,
5-12才能把主编辑区打开路径、草稿 key、保存命令接入 object tab / object editor 规则。
5. 执行清单
5.1 协议模型
-
在
rust/crates/core-protocol/增加或扩展资源对象协议。- 优先落点:
rust/crates/core-protocol/src/kernel.rs,必要时拆出rust/crates/core-protocol/src/resource.rs并在lib.rsre-export。 - 当前已有
KernelProjectionResourceKind,本阶段应补齐或映射为长期ResourceKind语义。 - 最小资源种类:
page、index、mindmap、attachment、onlyoffice、code。 ObjectIdentity最小字段:objectKind、documentId、blockId、assetId。BlockAssetRelation最小字段:documentId、blockId、assetId、assetKind。- 命名规则:Rust 类型使用
KernelObjectIdentity/KernelBlockAssetRelation或等价项目内前缀;序列化字段使用 camelCase。
- 优先落点:
-
补协议单测。
- 文件建议:
rust/crates/core-protocol/tests/resource_tree_contract.rs。 - 断言:mindmap、OnlyOffice、普通附件都能表达为 resource object。
- 断言:
index.md的 object identity 与 mindmap asset 的 object identity 不相等。 - 断言:
BlockAssetRelation能表达documentId + blockId + assetId + assetKind,且不会被反序列化为页面正文 identity。 - 运行命令:
cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol --test resource_tree_contract -- --nocapture- 预期:新增协议测试通过,失败时不得继续进入
5-12的 P1/P2。
- 文件建议:
5.2 Projection 合同
-
扩展
file_treeprojection 的 row contract。- 每个 row 必须带稳定
row_id、node_id、projection_kind、resource_meta。 resource_meta至少包含resourceKind、documentId、assetId、assetKind、objectIdentity。- 优先修改点:
rust/crates/core-protocol/src/kernel.rs的KernelProjectionResourceMeta,以及rust/crates/bridge-runtime/src/lib.rs的make_projection_resource_meta。 - 兼容策略:已有
resourceKind/documentId/assetId/assetKind保持不破坏;新增字段可先放入resourceMeta.extra.objectIdentity,待前后端消费稳定后再提升为强类型字段。
- 每个 row 必须带稳定
-
固定
page_treeprojection 的降级边界。page_tree只输出页面导航关系。page_tree不输出附件归属、mindmap 归属、OnlyOffice 归属的最终判断。- 如需显示资源状态,只能引用
resource_meta或 resource projection,不得在 page tree renderer 内拼装。 - 优先检查点:
rust/crates/bridge-runtime/src/lib.rs中page_treeprojection 构造逻辑、wolai-frontend/src/lib/tree-projection.ts、wolai-frontend/src/lib/documents/page-subtree.ts。
-
补 Rust projection 测试。
- 文件优先看:
rust/crates/bridge-runtime/src/lib.rs、rust/crates/mnote-web/src/tree_shell/。 - 测试要求:同一页面下的
index.md与 mindmap asset 生成不同 row,但共享同一documentId,并通过BlockAssetRelation建立关系。 - 建议新增或扩展测试:
kernel_project_view_query_executes_into_file_tree_projection_contractkernel_file_tree_projection_query_supports_extended_resources_and_max_results- 新增
kernel_file_tree_projection_separates_index_and_mindmap_object_identity
- 运行命令:
cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime file_tree_projection -- --nocapturecd /mnt/Data1T/mnote/rust && cargo test -p mnote-web file_tree_projection -- --nocapture
- 文件优先看:
5.3 File Tree 派生规则
-
页面节点下固定派生
index.mdrow。index.mdrow 只能打开 Page Aggregate body。index.mdrow 不得根据页面内第一个 block 类型改变打开目标。- 优先检查点:
rust/crates/bridge-runtime/src/lib.rs的build_file_tree_projection_result,当前已固定创建index:{documentId}node。 - 前端消费检查点:
rust/crates/mnote-web/src/ssr/pages/layout.rs的 file tree row click 分支。
-
页面内资源固定派生为 child resource row。
- mindmap row 打开 mindmap object editor。
- OnlyOffice row 打开 OnlyOffice object editor。
- 代码附件 row 打开代码/预览编辑器。
- 普通附件 row 打开签名资源或预览。
- 优先检查点:
rust/crates/bridge-runtime/src/lib.rs的normalize_file_tree_asset、infer_file_tree_asset_shape、classify_asset_resource_kind。 - 必须保留
documentId与assetId,供5-12的 object tab identity 使用。
-
文件树 row 不得从前端多份数据临时拼真相。
- 允许前端显示、筛选、选中、展开。
- 不允许前端决定资源归属、排序真相或 block-asset relation 真相。
- 前端允许做的本地状态:
expanded、selected、hover、focus、dragging、drop target。 - 前端禁止新增长期字段:
assetParentDocumentId、mindmapOwnerPageId、pageTreeAssetChildren这类与 kernel projection 重复的归属真相。
5.4 Page Tree 派生规则
-
Page Tree 只消费页面导航投影。
- 可显示页面标题、层级、当前页、快捷入口。
- 不负责附件、mindmap、OnlyOffice、代码附件的生命周期。
-
Page Tree command 只发页面导航相关 command。
- 页面新建、重命名、移动、归档继续走
tree.node.*或正式 page/tree command。 - 资源 attach/detach/rename/move 不进入 page tree 私有命令。
- 负向检查:Page Tree renderer / host 中不得新增 mindmap、OnlyOffice、附件归属推导分支。
- 页面新建、重命名、移动、归档继续走
5.5 Command 边界
-
新增或确认资源关系 command family。
tree.asset.attachtree.asset.detachtree.resource.renametree.resource.move
-
明确不同对象写入 owner。
- 页面正文:
page.body.save - 页面标题:
page.head.updateTitle - 页面设置:
page.layout.updateOptions - mindmap 内容:
mindmap.command.apply - OnlyOffice 内容:OnlyOffice callback / forcesave 写回链
- 资源归属:
tree.asset.*/tree.resource.* - 优先检查点:
rust/crates/bridge-runtime/src/lib.rscommand planrust/crates/mnote-web/src/transport/convex.rscommand adapterwolai-frontend/src/lib/documents/rust-runtime.test.ts
- 页面正文:
5.6 Smoke 与验收
-
增加 Resource Tree / File Tree projection smoke。
- 断言文件树同页下可见
index.md、mindmap、附件。 - 断言点击
index.md只打开 Page Aggregate body。 - 断言点击 mindmap 不进入 Page Aggregate body 保存链。
- 可复用脚本:
scripts/task112-tree-rust-family-regression-smoke.js。 - 建议新增断言脚本:
scripts/task170-resource-tree-filetree-source-smoke.js,或在 task112 中增加独立 case。 - 运行命令:
node scripts/task112-tree-rust-family-regression-smoke.js- 若新增脚本:
node scripts/task170-resource-tree-filetree-source-smoke.js
- 断言文件树同页下可见
-
增加 Page Tree 负向 smoke。
- 断言 Page Tree 不显示资源归属为自己的结构真相。
- 断言 Page Tree 操作不会改变 mindmap/附件归属。
- 可复用脚本:
scripts/task112-tree-rust-family-regression-smoke.js。 - 负向检查输出需记录:操作 Page Tree 后,同一
assetId的documentId/objectIdentity未变化。
6. 建议提交切片
本文件只定义设计顺序,不要求一次提交完成全部实现。后续执行时建议按以下切片推进:
- 协议切片:
core-protocol增补 object identity / relation 类型与测试。 - Projection 切片:
bridge-runtime与mnote-webfile tree projection 输出 object identity。 - UI 消费切片:file tree host 只转发 object open intent,不拼归属真相。
- Page Tree 负向切片:补 page tree 不持有资源归属的单测或 smoke。
- 验收切片:跑 Rust 测试与真实浏览器 smoke,把证据补回对应 bug / design 文档。
7. 验收命令
最低验收命令:
cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol --test resource_tree_contract -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime file_tree_projection -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web file_tree_projection -- --nocapture
node scripts/task112-tree-rust-family-regression-smoke.js
如执行环境没有启动 3000,先运行仓库常用入口:
npm run desktop:hot
真实浏览器 smoke 必须使用默认测试账号 mnote.e2e@example.com / MnoteE2E123!,不能用 devFallback 代替登录态。
8. Done Gate
本文移动到 done/ 前必须满足:
Resource Tree、File Tree、Page Tree三个术语在协议和文档中不再混用。file_treerow 能表达index.md、mindmap、OnlyOffice、附件、代码附件的不同 object identity。page_tree被验证为页面导航 projection,不拥有资源归属真相。- mindmap 与
index.md的打开、保存、草稿身份互相隔离。 - 至少一条真实浏览器 smoke 覆盖
index.md + mindmap asset + 切页 + 再打开的完整链路。
9. 完成证据
- 协议:
rust/crates/core-protocol/src/kernel.rs增加KernelObjectIdentity、KernelObjectKind、KernelBlockAssetRelation,并由rust/crates/core-protocol/tests/resource_tree_contract.rs覆盖。 - Projection:
rust/crates/bridge-runtime/src/lib.rs的 file tree projection 输出resourceMeta.objectIdentity/blockAssetRelation,并覆盖 index、mindmap、OnlyOffice、代码附件、普通附件。 - UI intent:
rust/crates/mnote-web/src/ssr/pages/layout.rs、rust/crates/mnote-web/src/routes/tree.rs、rust/crates/mnote-web/src/tree_shell/filetree_renderer.rs将objectIdentity传入 file tree DOM 与tree.asset.open。 - Smoke:
node scripts/task112-tree-rust-family-regression-smoke.js通过,page tree host / file tree host 主链为dom;debug tree shell 关闭项按当前主线记录为 skipped。 - Mindmap 链路:
node scripts/task169-mindmap-realtime-smoke.js通过,覆盖 mindmap asset 打开、长中文编辑、保存、切页、回index.md、再打开 mindmap。
10. 交接给 5-12 的输入
完成本文件后,5-12 可以依赖以下输入推进主编辑区:
- 文件树
index.mdrow 的objectIdentity:page:index:{documentId}。 - 文件树 mindmap row 的
objectIdentity:resource:mindmap:{documentId}:{assetId}。 - 文件树 OnlyOffice row 的
objectIdentity:resource:onlyoffice:{documentId}:{assetId}。 - 文件树普通附件 row 的
objectIdentity:resource:attachment:{documentId}:{assetId}。 - 文件树代码附件 row 的
objectIdentity:resource:code:{documentId}:{assetId}。 - Page Tree 不再作为资源归属输入;它只提供页面导航选择。