Files
mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md
T

238 lines
15 KiB
Markdown
Raw Normal View History

2026-05-13 22:43:16 +08:00
# 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/process/4-35-convex-filetree-title-md-source-alignment-v1.md` 覆盖。
> - 后续 Convex File Tree 默认页面正文行显示为 `{title}.md`rowId 为 `doc:<documentId>`;本文中的 `index.md` 仅表示历史正文 object identity / 兼容语义,不再作为默认可见子行继续扩展。
>
2026-05-13 22:43:16 +08:00
> 上游依据:
> - `/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` 的前置合同。执行顺序固定为:
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` 已有 `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-10` bug 的直接落点,但长期合同归属仍应先在本文件冻结。
阶段门:
- G1:协议能表达 `index.md`、mindmap、OnlyOffice、普通附件、代码附件的不同 object identity。
- G2`file_tree` projection row 能稳定输出 object identity 或等价 `resourceMeta.extra.objectIdentity`
- G3`page_tree` projection 被约束为页面导航投影,不承载 asset 归属。
- G4`tree.asset.open` 只表达打开资源对象的 intent,不决定“把资源当正文打开”。
- G5:完成 G1-G4 后,`5-12` 才能把主编辑区打开路径、草稿 key、保存命令接入 object tab / object editor 规则。
## 5. 执行清单
### 5.1 协议模型
- [x]`rust/crates/core-protocol/` 增加或扩展资源对象协议。
- 优先落点:`rust/crates/core-protocol/src/kernel.rs`,必要时拆出 `rust/crates/core-protocol/src/resource.rs` 并在 `lib.rs` re-export。
- 当前已有 `KernelProjectionResourceKind`,本阶段应补齐或映射为长期 `ResourceKind` 语义。
- 最小资源种类:`page``index``mindmap``attachment``onlyoffice``code`
- `ObjectIdentity` 最小字段:`objectKind``documentId``blockId``assetId`
- `BlockAssetRelation` 最小字段:`documentId``blockId``assetId``assetKind`
- 命名规则:Rust 类型使用 `KernelObjectIdentity` / `KernelBlockAssetRelation` 或等价项目内前缀;序列化字段使用 camelCase。
- [x] 补协议单测。
- 文件建议:`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 合同
- [x] 扩展 `file_tree` projection 的 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`,待前后端消费稳定后再提升为强类型字段。
- [x] 固定 `page_tree` projection 的降级边界。
- `page_tree` 只输出页面导航关系。
- `page_tree` 不输出附件归属、mindmap 归属、OnlyOffice 归属的最终判断。
- 如需显示资源状态,只能引用 `resource_meta` 或 resource projection,不得在 page tree renderer 内拼装。
- 优先检查点:`rust/crates/bridge-runtime/src/lib.rs``page_tree` projection 构造逻辑、`wolai-frontend/src/lib/tree-projection.ts``wolai-frontend/src/lib/documents/page-subtree.ts`
- [x] 补 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_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 派生规则
- [x] 页面节点下固定派生 `index.md` row。
- `index.md` row 只能打开 Page Aggregate body。
- `index.md` row 不得根据页面内第一个 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 分支。
- [x] 页面内资源固定派生为 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 使用。
- [x] 文件树 row 不得从前端多份数据临时拼真相。
- 允许前端显示、筛选、选中、展开。
- 不允许前端决定资源归属、排序真相或 block-asset relation 真相。
- 前端允许做的本地状态:`expanded``selected``hover``focus``dragging``drop target`
- 前端禁止新增长期字段:`assetParentDocumentId``mindmapOwnerPageId``pageTreeAssetChildren` 这类与 kernel projection 重复的归属真相。
### 5.4 Page Tree 派生规则
- [x] Page Tree 只消费页面导航投影。
- 可显示页面标题、层级、当前页、快捷入口。
- 不负责附件、mindmap、OnlyOffice、代码附件的生命周期。
- [x] Page Tree command 只发页面导航相关 command。
- 页面新建、重命名、移动、归档继续走 `tree.node.*` 或正式 page/tree command。
- 资源 attach/detach/rename/move 不进入 page tree 私有命令。
- 负向检查:Page Tree renderer / host 中不得新增 mindmap、OnlyOffice、附件归属推导分支。
### 5.5 Command 边界
- [x] 新增或确认资源关系 command family。
- `tree.asset.attach`
- `tree.asset.detach`
- `tree.resource.rename`
- `tree.resource.move`
- [x] 明确不同对象写入 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 与验收
- [x] 增加 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`
- [x] 增加 Page Tree 负向 smoke。
- 断言 Page Tree 不显示资源归属为自己的结构真相。
- 断言 Page Tree 操作不会改变 mindmap/附件归属。
- 可复用脚本:`scripts/task112-tree-rust-family-regression-smoke.js`
- 负向检查输出需记录:操作 Page Tree 后,同一 `assetId``documentId` / `objectIdentity` 未变化。
## 6. 建议提交切片
本文件只定义设计顺序,不要求一次提交完成全部实现。后续执行时建议按以下切片推进:
1. 协议切片:`core-protocol` 增补 object identity / relation 类型与测试。
2. Projection 切片:`bridge-runtime``mnote-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. 验收命令
最低验收命令:
```bash
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`,先运行仓库常用入口:
```bash
npm run desktop:hot
```
真实浏览器 smoke 必须使用默认测试账号 `mnote.e2e@example.com` / `MnoteE2E123!`,不能用 `devFallback` 代替登录态。
## 8. Done Gate
本文移动到 `done/` 前必须满足:
- [x] `Resource Tree``File Tree``Page Tree` 三个术语在协议和文档中不再混用。
- [x] `file_tree` row 能表达 `index.md`、mindmap、OnlyOffice、附件、代码附件的不同 object identity。
- [x] `page_tree` 被验证为页面导航 projection,不拥有资源归属真相。
- [x] mindmap 与 `index.md` 的打开、保存、草稿身份互相隔离。
- [x] 至少一条真实浏览器 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.md` row 的 `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 不再作为资源归属输入;它只提供页面导航选择。