Files
mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md
T
lix-2026 f292c6710a feat: EditorRuntimeActor - 三层缓存/delta/事件架构
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
2026-05-16 22:03:30 +08:00

15 KiB
Raw Blame History

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}.mdrowId 为 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 Treeworkspace 内对象与资源的 canonical hierarchy。它不是 UI 文件树组件,而是 kernel 层的资源组织语义。
  • File TreeResource Tree 面向 VS Code Explorer 体验的 projection。
  • Page TreeResource Tree 中页面对象的导航 projection,可做快捷显示,但不拥有资源归属。
  • ObjectIdentity:打开、保存、草稿、tab、command 使用的对象身份。
  • BlockAssetRelation:页面正文 block 与资源对象之间的稳定关联,例如 documentId + blockId + assetId + assetKind

3. 非目标

  • 不在本阶段重写 Convex 底层存储。
  • 不在本阶段一次性删除所有 compat/fallback。
  • 不把文件树 UI 组件提升为真源。
  • 不让 Page Tree 接管附件、mindmap 或 OnlyOffice 的归属。

4. 前置顺序与执行门

本清单是 5-12 的前置合同。执行顺序固定为:

  1. 先完成本文件的 Resource Tree / File Tree / Page Tree 真源合同。
  2. 再执行 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 已有 KernelProjectionResourceKindKernelProjectionAssetKindKernelProjectionResourceMetaKernelProjectionItem
  • rust/crates/bridge-runtime/src/lib.rs 已有 build_file_tree_projection_resultmake_projection_resource_metanormalize_file_tree_asset 等 file tree projection 构造逻辑。
  • rust/crates/mnote-web/src/routes/tree.rsrust/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-10 bug 的直接落点,但长期合同归属仍应先在本文件冻结。

阶段门:

  • G1:协议能表达 index.md、mindmap、OnlyOffice、普通附件、代码附件的不同 object identity。
  • G2file_tree projection row 能稳定输出 object identity 或等价 resourceMeta.extra.objectIdentity
  • G3page_tree projection 被约束为页面导航投影,不承载 asset 归属。
  • G4tree.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.rs re-export。
    • 当前已有 KernelProjectionResourceKind,本阶段应补齐或映射为长期 ResourceKind 语义。
    • 最小资源种类:pageindexmindmapattachmentonlyofficecode
    • ObjectIdentity 最小字段:objectKinddocumentIdblockIdassetId
    • BlockAssetRelation 最小字段:documentIdblockIdassetIdassetKind
    • 命名规则: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_tree projection 的 row contract。

    • 每个 row 必须带稳定 row_idnode_idprojection_kindresource_meta
    • resource_meta 至少包含 resourceKinddocumentIdassetIdassetKindobjectIdentity
    • 优先修改点:rust/crates/core-protocol/src/kernel.rsKernelProjectionResourceMeta,以及 rust/crates/bridge-runtime/src/lib.rsmake_projection_resource_meta
    • 兼容策略:已有 resourceKind/documentId/assetId/assetKind 保持不破坏;新增字段可先放入 resourceMeta.extra.objectIdentity,待前后端消费稳定后再提升为强类型字段。
  • 固定 page_tree projection 的降级边界。

    • page_tree 只输出页面导航关系。
    • page_tree 不输出附件归属、mindmap 归属、OnlyOffice 归属的最终判断。
    • 如需显示资源状态,只能引用 resource_meta 或 resource projection,不得在 page tree renderer 内拼装。
    • 优先检查点:rust/crates/bridge-runtime/src/lib.rspage_tree projection 构造逻辑、wolai-frontend/src/lib/tree-projection.tswolai-frontend/src/lib/documents/page-subtree.ts
  • 补 Rust projection 测试。

    • 文件优先看:rust/crates/bridge-runtime/src/lib.rsrust/crates/mnote-web/src/tree_shell/
    • 测试要求:同一页面下的 index.md 与 mindmap asset 生成不同 row,但共享同一 documentId,并通过 BlockAssetRelation 建立关系。
    • 建议新增或扩展测试:
      • kernel_project_view_query_executes_into_file_tree_projection_contract
      • kernel_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 -- --nocapture
      • cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web file_tree_projection -- --nocapture

5.3 File Tree 派生规则

  • 页面节点下固定派生 index.md row。

    • index.md row 只能打开 Page Aggregate body。
    • index.md row 不得根据页面内第一个 block 类型改变打开目标。
    • 优先检查点:rust/crates/bridge-runtime/src/lib.rsbuild_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.rsnormalize_file_tree_assetinfer_file_tree_asset_shapeclassify_asset_resource_kind
    • 必须保留 documentIdassetId,供 5-12 的 object tab identity 使用。
  • 文件树 row 不得从前端多份数据临时拼真相。

    • 允许前端显示、筛选、选中、展开。
    • 不允许前端决定资源归属、排序真相或 block-asset relation 真相。
    • 前端允许做的本地状态:expandedselectedhoverfocusdraggingdrop target
    • 前端禁止新增长期字段:assetParentDocumentIdmindmapOwnerPageIdpageTreeAssetChildren 这类与 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.attach
    • tree.asset.detach
    • tree.resource.rename
    • tree.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.rs command plan
      • rust/crates/mnote-web/src/transport/convex.rs command adapter
      • wolai-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 后,同一 assetIddocumentId / objectIdentity 未变化。

6. 建议提交切片

本文件只定义设计顺序,不要求一次提交完成全部实现。后续执行时建议按以下切片推进:

  1. 协议切片:core-protocol 增补 object identity / relation 类型与测试。
  2. Projection 切片:bridge-runtimemnote-web file tree projection 输出 object identity。
  3. UI 消费切片:file tree host 只转发 object open intent,不拼归属真相。
  4. Page Tree 负向切片:补 page tree 不持有资源归属的单测或 smoke。
  5. 验收切片:跑 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 TreeFile TreePage Tree 三个术语在协议和文档中不再混用。
  • file_tree row 能表达 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 增加 KernelObjectIdentityKernelObjectKindKernelBlockAssetRelation,并由 rust/crates/core-protocol/tests/resource_tree_contract.rs 覆盖。
  • Projectionrust/crates/bridge-runtime/src/lib.rs 的 file tree projection 输出 resourceMeta.objectIdentity / blockAssetRelation,并覆盖 index、mindmap、OnlyOffice、代码附件、普通附件。
  • UI intentrust/crates/mnote-web/src/ssr/pages/layout.rsrust/crates/mnote-web/src/routes/tree.rsrust/crates/mnote-web/src/tree_shell/filetree_renderer.rsobjectIdentity 传入 file tree DOM 与 tree.asset.open
  • Smokenode scripts/task112-tree-rust-family-regression-smoke.js 通过,page tree host / file tree host 主链为 domdebug tree shell 关闭项按当前主线记录为 skipped。
  • Mindmap 链路:node scripts/task169-mindmap-realtime-smoke.js 通过,覆盖 mindmap asset 打开、长中文编辑、保存、切页、回 index.md、再打开 mindmap。

10. 交接给 5-12 的输入

完成本文件后,5-12 可以依赖以下输入推进主编辑区:

  • 文件树 index.md row 的 objectIdentitypage:index:{documentId}
  • 文件树 mindmap row 的 objectIdentityresource:mindmap:{documentId}:{assetId}
  • 文件树 OnlyOffice row 的 objectIdentityresource:onlyoffice:{documentId}:{assetId}
  • 文件树普通附件 row 的 objectIdentityresource:attachment:{documentId}:{assetId}
  • 文件树代码附件 row 的 objectIdentityresource:code:{documentId}:{assetId}
  • Page Tree 不再作为资源归属输入;它只提供页面导航选择。