chore: align mvp design governance
- 统一 local-first MVP 后阶段架构口径,补充 process 执行总序和 Reasonix 协作记录 - 归档已完成的 design checklist,标注参考型 process,更新 AGENTS/REASONIX/架构文档 - 补充文件树/主编辑器下载与上下文菜单相关实现、bug 记录和 smoke 脚本 验证:git diff --check;codegraph sync .;cargo test -p mnote-web;node --check scripts/task476-filetree-editor-context-menu-download-smoke.js
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# 4-27 Resource Lifecycle Command Cutover v1
|
||||
|
||||
> 状态:done
|
||||
> 归档说明(2026-05-21):核心 `tree.resource.*` 生命周期命令 cutover 已完成;双浏览器 no-refresh、restore reveal/focus 等尾项已由 `4-34`、`4-28` 继续跟踪。
|
||||
> 更新时间:2026-05-15
|
||||
> 范围:`tree.resource.archive / restore / purge / rename` 正式命令面,以及 `/api/media/*`、mindmap、table 资源垃圾箱兼容入口的收口边界。
|
||||
|
||||
## 1. 本阶段目标
|
||||
|
||||
- 普通附件 file asset 的删除、恢复、永久删除、重命名必须具备正式 `tree.resource.*` 命令。
|
||||
- `/api/media/batch` 与 `/api/media/purge` 可以继续作为历史 URL,但只能作为兼容 alias,内部必须生成正式 command plan 并记录 command artifact。
|
||||
- mindmap / table 旧 URL 可以保留为兼容 route,但 delete / restore / purge 必须迁移到同一 `tree.resource.*` 命令 payload,不再扩写分散 route 语义。
|
||||
|
||||
## 2. 已落地范围
|
||||
|
||||
- `bridge-runtime` 新增 file asset 生命周期命令计划:
|
||||
- `tree.resource.archive -> mediaAssets:patchById`
|
||||
- `tree.resource.restore -> mediaAssets:patchById`
|
||||
- `tree.resource.purge -> mediaAssets:purgeById`
|
||||
- `tree.resource.rename -> mediaAssets:patchById`
|
||||
- `storage-convex-bridge` 为上述命令提供默认 Convex 映射。
|
||||
- `wolai-frontend` 的 `/api/media/batch`:
|
||||
- `delete` 改为 `tree.resource.archive`
|
||||
- `restore` 改为 `tree.resource.restore`
|
||||
- `rename` 改为 `tree.resource.rename`
|
||||
- `wolai-frontend` 的 `/api/media/purge` 改为 `tree.resource.purge`。
|
||||
- `mnote-web` 的 Rust 3000 `/api/media/batch` 与 `/api/media/purge` 改为通过 runtime command 执行,旧 URL 仅作为兼容 alias。
|
||||
- `mnote-web` 的 Rust 3000 `/api/mindmap/{docId}/{mindmapId}` DELETE / PATCH restore / PATCH purge 改为通过 `tree.resource.archive / restore / purge` 执行,旧 URL 仅作为兼容 alias。
|
||||
- `mnote-web` 的 Rust 3000 `/api/tables/{tableId}` DELETE、`/api/tables/restore`、`/api/tables/purge` 改为通过 `tree.resource.archive / restore / purge` 执行,旧 URL 仅作为兼容 alias。
|
||||
- tree shell command event schema 已补资源生命周期事件。
|
||||
|
||||
## 3. 未完成边界
|
||||
|
||||
- mindmap 与 table 的 DELETE / restore / purge 已进入 `tree.resource.*` runtime command;但旧 URL 仍保留为兼容入口,前端 Sidebar 仍按资源类型分流到这些旧 URL。
|
||||
- `tree.resource.rename` 当前只覆盖普通附件 file asset;mindmap rename 暂不支持,table rename 需要结合 `tables:update` 与 UI 标题语义另行收口。
|
||||
- `empty-trash` 仍是 workspace 级批量兼容合同:`mediaAssets.emptyTrashByWorkspace`、`mindmaps.emptyTrashByWorkspace`、`tables.emptyTrashByWorkspace`,暂不展开为逐资源 `tree.resource.purge`。
|
||||
- 双浏览器 no-refresh、垃圾箱恢复后的 reveal/focus、跨资源 projection delta 细粒度更新仍由后续阶段覆盖。
|
||||
|
||||
## 4. 验收
|
||||
|
||||
- 普通附件通过 `/api/media/batch delete/restore/rename` 或 `/api/media/purge` 操作时,测试中应观察到正式命令名 `tree.resource.archive/restore/rename/purge`。
|
||||
- Mindmap 通过 `/api/mindmap/{docId}/{mindmapId}` DELETE / PATCH restore / PATCH purge 操作时,响应必须携带 `canonicalCommand=tree.resource.archive/restore/purge` 与 `resourceKind=mindmap`。
|
||||
- Table 通过 `/api/tables/{tableId}` DELETE、`/api/tables/restore`、`/api/tables/purge` 操作时,响应必须携带 `canonicalCommand=tree.resource.archive/restore/purge` 与 `resourceKind=table`。
|
||||
- Rust runtime plan 必须包含 `domainEventPlan` 与 `streamDeltaHint`。
|
||||
- Rust 3000 兼容 route 不再直接写 `mediaAssets:patchById / purgeById`,而是先进入 runtime command。
|
||||
- 现有垃圾箱 route 回归保持通过。
|
||||
|
||||
## 5. 验证命令
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p bridge-runtime tree_resource_lifecycle_plans_cover_file_asset_commands
|
||||
cargo test -p bridge-runtime tree_resource_lifecycle_plans_cover_mindmap_and_table_commands
|
||||
cargo test -p storage-convex-bridge tree_resource_lifecycle_commands_have_convex_mapping
|
||||
cargo test -p mnote-web trash
|
||||
cargo test -p mnote-web runtime_command_event_schema_covers_tree_and_resource_command_channels
|
||||
```
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm test src/app/api/media/purge/route.test.ts src/app/api/media/batch/route.test.ts src/lib/documents/rust-runtime.test.ts
|
||||
```
|
||||
|
||||
## 6. 执行记录
|
||||
|
||||
- 2026-05-15:先补 `mnote-web` 路由测试断言 mindmap / table DELETE、restore、purge 响应必须携带 `canonicalCommand=tree.resource.archive/restore/purge` 与对应 `resourceKind`,测试红灯确认旧路由仍只返回 legacy mutation 结果。
|
||||
- 2026-05-15:`mnote-web` mindmap / table 兼容 route 已改为构造 `tree.resource.archive/restore/purge` runtime command,并通过 `execute_runtime_command_via_convex_with_artifacts` 执行;旧 URL 保留。
|
||||
- 2026-05-15:新增 `bridge-runtime` 合同测试 `tree_resource_lifecycle_plans_cover_mindmap_and_table_commands`,断言 mindmap 映射到 `mindmaps:softDelete/restore/purge`,table 映射到 `tables:remove/restore/purge`,并携带 `resourceLifecyclePlan`、`domainEventHint` 与 stream delta hint。
|
||||
- 2026-05-15 验证:`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime tree_resource_lifecycle_plans_cover_mindmap_and_table_commands -- --nocapture` 通过,1 项通过。
|
||||
- 2026-05-15 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web trash_routes -- --nocapture --test-threads=1` 通过,3 项通过。
|
||||
- 2026-05-15 验证:`cargo test --manifest-path rust/Cargo.toml -p storage-convex-bridge tree_resource_lifecycle_commands_have_convex_mapping -- --nocapture` 通过,1 项通过。
|
||||
|
||||
## 7. 剩余边界
|
||||
|
||||
- `tree.resource.rename` 仍只覆盖普通附件 file asset;mindmap rename 继续明确不支持,table rename 待结合 `/api/tables/{tableId}` PATCH 与表格标题语义单独收口。
|
||||
- `empty-trash` 仍是 workspace 级批量合同,不把它伪装成逐资源 `tree.resource.purge`。
|
||||
- 前端 Sidebar 仍按类型调用旧 URL;这是兼容 alias,不是新增长期业务语义。后续如要统一客户端命令面,应在 `resource-command-client` 引入 mindmap/table payload,并保留旧 URL 适配层。
|
||||
|
||||
## 8. 子阶段:local_folder file_path resource lifecycle
|
||||
|
||||
### 8.1 目标与范围
|
||||
|
||||
本子阶段只覆盖 `local_folder` workspace 中由本地文件系统提供的非 md 资源文件,典型对象包括图片、PDF、音视频、Office 文件、压缩包和其他附件型二进制文件。Markdown 页面本体、目录节点、远端 media asset、mindmap、table 不在本子阶段内。
|
||||
|
||||
最小目标是把本地非 md 资源文件的 delete / restore / purge 纳入 `tree.resource.*` 正式命令面,同时保留本地文件路径作为用户可理解的 identity 输入。实现不要求立即接入云端文件删除,也不要求新增复杂版本库;只要求命令可审计、垃圾箱可索引、冲突可判定、恢复行为可预测。
|
||||
|
||||
### 8.2 最小设计
|
||||
|
||||
- 资源归属:`resourceKind=local_file`,`resourceScope=local_folder`。
|
||||
- 资源定位:命令 payload 必须携带 `workspaceId`、`rootId`、`filePath`、`resourceKind`;`filePath` 使用相对 `local_folder` 根目录的规范化 UTF-8 路径,不允许绝对路径、空路径、`..` 逃逸或平台分隔符混用。
|
||||
- md 排除:扩展名判定为 Markdown 的文件继续走页面/树节点生命周期,不进入 `local_file` 资源生命周期。
|
||||
- delete:`tree.resource.archive` 不直接物理删除文件,先把源文件移动到本工作区受控 trash 存储区,并写入 trash index。
|
||||
- restore:`tree.resource.restore` 只从 trash index 中恢复已归档资源,默认恢复到原 `filePath`;若原路径冲突,按冲突策略处理。
|
||||
- purge:`tree.resource.purge` 只允许删除 trash index 中已归档资源对应的 trash 文件和索引记录;不允许对当前活跃文件路径直接 purge。
|
||||
- 事件:三类命令都产出 `domainEventPlan` 与 `streamDeltaHint`,至少能通知资源列表、附件引用状态、垃圾箱视图刷新。
|
||||
|
||||
### 8.3 命令 identity
|
||||
|
||||
`local_folder` 文件的稳定命令 identity 分两层:
|
||||
|
||||
- 用户输入 identity:`workspaceId + rootId + normalizedFilePath`。这是兼容 UI、外部变更监听和命令 payload 的最小输入。
|
||||
- 归档后 identity:`trashEntryId`。第一次 archive 成功后生成并写入 trash index,restore / purge 必须优先使用 `trashEntryId`,同时校验其记录的原始 `filePath`、`workspaceId`、`rootId` 与命令 payload 一致。
|
||||
|
||||
`resourceId` 不直接等同于裸 `filePath`。对活跃文件可派生为 `local_file:{workspaceId}:{rootId}:{pathHash}`,但该值只作为事件和 projection 的稳定 key;实际文件操作仍以规范化路径和 trash index 记录为准,避免路径重命名、大小写差异或 Unicode 归一化导致误删。
|
||||
|
||||
### 8.4 Trash index
|
||||
|
||||
每个 `local_folder` root 维护一个受控 trash index,最小字段如下:
|
||||
|
||||
- `trashEntryId`
|
||||
- `workspaceId`
|
||||
- `rootId`
|
||||
- `resourceKind=local_file`
|
||||
- `originalFilePath`
|
||||
- `trashedFilePath`
|
||||
- `fileSize`
|
||||
- `contentHash`,可延后异步补齐,但 purge 前若存在则用于防误删校验
|
||||
- `archivedAt`
|
||||
- `archivedByCommandId`
|
||||
- `restoreStatus`
|
||||
- `purgedAt`
|
||||
- `purgedByCommandId`
|
||||
|
||||
trash index 是 restore / purge 的唯一可信入口。目录扫描只能用于发现活跃文件或 orphan trash 文件,不能绕过 index 直接恢复或永久删除。
|
||||
|
||||
### 8.5 冲突策略
|
||||
|
||||
- archive 时源文件不存在:命令返回 `not_found`,不生成新的 trash entry;若 index 已存在同一路径的活跃归档记录,返回该 `trashEntryId` 并标记为幂等归档结果。
|
||||
- archive 时目标 trash 路径已存在:生成新的 `trashEntryId` 与唯一 `trashedFilePath`,不覆盖旧 trash 文件。
|
||||
- restore 时原路径不存在:直接恢复到 `originalFilePath`,并把 index 状态改为 restored。
|
||||
- restore 时原路径已存在且 contentHash 相同:视为幂等恢复,index 状态改为 restored,不覆盖活跃文件。
|
||||
- restore 时原路径已存在且 contentHash 不同或无法判定:不得覆盖活跃文件;返回 `conflict=file_path_occupied`,UI 可选择恢复为带后缀副本或让用户改名,但默认命令不自动改名。
|
||||
- purge 时 trash 文件不存在但 index 仍为 archived:允许把 index 标记为 purged,并记录 `missing_trash_file`,用于容忍外部手动清理。
|
||||
- purge 时 index 状态不是 archived:返回幂等结果,不删除任何活跃文件。
|
||||
- 任一命令发现 `filePath` 逃逸 root、大小写归一化后指向不同文件、或 symlink 指向 root 外部:返回 `invalid_path`,不移动、不删除。
|
||||
|
||||
### 8.6 验收标准
|
||||
|
||||
- 本地非 md 资源文件 delete 只能通过 `tree.resource.archive` 进入 trash,不允许直接 unlink 活跃文件。
|
||||
- restore / purge 必须优先基于 `trashEntryId` 与 trash index 执行;仅传 `filePath` 时只能定位唯一 archived entry,不能模糊匹配多条记录。
|
||||
- trash index 中能追踪原路径、trash 路径、归档命令、恢复/永久删除命令和当前状态。
|
||||
- restore 冲突时默认不覆盖用户已有文件,并返回可被 UI 展示的结构化冲突原因。
|
||||
- purge 只能作用于 archived trash entry,不能对 active `filePath` 执行永久删除。
|
||||
- Markdown 文件不进入本子阶段;测试中应验证 `.md` / `.markdown` 路径被拒绝或转交页面生命周期。
|
||||
- 命令事件必须包含 `resourceKind=local_file`、`resourceScope=local_folder`、`filePath`、`trashEntryId`(archive 成功后)和 `canonicalCommand=tree.resource.archive/restore/purge`。
|
||||
- 外部变更监听与本命令并发时,以文件系统当前状态和 trash index 状态共同判定;不得因 stale projection 覆盖真实文件系统变化。
|
||||
|
||||
### 8.7 执行记录
|
||||
|
||||
- 2026-05-15:先补 `tree_command_local_folder_asset_trash_restore_and_purge_use_trash_index` 红灯,确认 `local:asset:docs/photo.png` delete 原本掉到 Markdown 删除路径。
|
||||
- 2026-05-15:`mnote-web` local folder executor 已把 `delete / restore / purge` 分流到 entry 级处理;`local:asset:*` / `local:node:*` raw file 通过 `tree.resource.archive/restore/purge` 进入 `.mnote/trash` 与 `trash-index.json`,Markdown 页面继续走原页面生命周期。
|
||||
- 2026-05-15:Rust 3000 主壳 filetree delete plan 已对 `sourceKind=local_folder && rowKind=asset` 使用 rowId 作为本地资源 identity,Delete / Backspace 走 `/api/tree/commands`,不走 `/api/media/batch`。
|
||||
- 2026-05-15:新增 `scripts/task437-local-folder-asset-trash-lifecycle-smoke.js`,真实浏览器覆盖 local asset Delete 进入本地回收站、restore 回原路径、purge 清理 trash 文件与索引;结果文件 `tmp/task437-local-folder-asset-trash-lifecycle-smoke/result.json` 记录 `navigationEvents=[]`。
|
||||
|
||||
### 8.8 验证命令
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_asset_trash_restore_and_purge_use_trash_index -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_create_rename_copy_trash_restore_and_purge_use_same_endpoint -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_delete_keys_support_mixed_doc_and_asset_selection -- --nocapture
|
||||
MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3427 node scripts/task437-local-folder-asset-trash-lifecycle-smoke.js
|
||||
```
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# 4-39 FileTree Open Target 与 Resource Identity Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):FileTree open target 与 resource identity 合同已完成;`rootUri` 收尾由 `4-42` 覆盖并已归档。
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:File Tree runtime / open target 合同
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-4:扩展 `FileTreeOpenTarget` 和 runtime API,使 File Tree 能稳定表达 local raw file、directory、asset folder、office/image/pdf/code/mindmap/table 等资源身份,而不是只靠 UI 侧字符串分流。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/tree_shell/filetree_runtime.rs`
|
||||
- `rust/crates/mnote-web/src/tree_shell/runtime_api.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- 相关 Rust 单测
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不改前端打开 UI 大流程;只输出更稳定的 open target / intent 合同。
|
||||
- 不做 resource lifecycle delete/restore/purge cutover;该项由 4-40 处理。
|
||||
- 不引入新的树真相层,Resource Tree / File Tree / Page Tree 口径保持不变。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] 扩展 `FileTreeOpenTarget` 或新增字段,能表达 `local_file`、`directory`、`asset_folder`、`office`、`image`、`pdf`、`code/text`、`mindmap`、`table`。
|
||||
- [x] open target 包含 canonical resource identity、resource kind、relative path、document id / parent id(若有)。
|
||||
- [x] `parse_open_target_from_row` 支持现有 row dataset,不破坏 document/index/asset-folder/asset。
|
||||
- [x] runtime API 序列化保持 camelCase,旧字段兼容。
|
||||
- [x] Rust 单测覆盖 local raw file、directory、office/image/pdf/code/mindmap/table open target。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `cargo test -p mnote-web filetree_runtime -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web runtime_api -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web tree_shell_filetree_mode_embeds_asset_state -- --test-threads=1`
|
||||
|
||||
## 6. 本轮执行记录
|
||||
|
||||
- 已完成:
|
||||
- `FileTreeOpenTarget::Asset` 已补可选 `resourceKind`,并新增 `LocalFile` / `Directory` variant。
|
||||
- `resolve_open_target` 支持 `local-file` / `directory` / `office` / `image` / `pdf` / `code` / `text` / `mindmap` / `table` row kind。
|
||||
- 新增序列化合同测试,覆盖 resource kind、local file 中文路径、directory。
|
||||
- 未完成:
|
||||
- 后续仍需把更多上游 row dataset 补齐真实 `rootUri`,当前 `LocalFile` / `Directory` variant 的 rootUri 仍为空字符串占位。
|
||||
- 风险:`FileTreeOpenTarget` schema 已扩展,需保留旧 `Asset` JSON 兼容;当前 `resourceKind=None` 时不序列化。
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不改变资源实际打开方式。
|
||||
- 不处理 OnlyOffice 回源。
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# 4-40 Resource Lifecycle Command Cutover 与垃圾箱 Smoke Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):resource lifecycle browser smoke 与 command cutover 验证已完成;bulk delete 剩余项由 `4-41` 覆盖并已归档。
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:resource lifecycle / tree.resource.* / local trash smoke
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-5:前端本地资源删除、恢复、永久删除优先走 `/api/tree/commands` 的 `tree.resource.*` 语义,并用浏览器 smoke 覆盖“资源行删除 -> 垃圾箱可见 -> 恢复 -> purge”的全链路。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- `rust/crates/mnote-web/src/routes/resource_trash.rs`
|
||||
- `rust/crates/mnote-web/src/routes/gateway.rs`
|
||||
- `scripts/task437-local-folder-asset-trash-lifecycle-smoke.js`
|
||||
- 可新增聚焦 smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不删除旧兼容 resource_trash route;只让新主路径优先 tree command。
|
||||
- 不改变云端 Convex resource lifecycle 语义。
|
||||
- 不实现 undo/redo。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [ ] 前端 local resource delete/bulk delete 优先调用 `/api/tree/commands` archive/purge/restore。
|
||||
- [x] 旧 `/api/mindmap`、`/api/media`、`/api/tables` lifecycle route 保留为兼容 fallback。
|
||||
- [x] tree command response 保留 `canonicalCommand` / artifact / trash index 信息,前端可刷新对应行。
|
||||
- [x] 垃圾箱页面或 modal 能显示 local resource trash entry,并支持 restore/purge。
|
||||
- [x] 新增浏览器 smoke:本地文件夹页面上传或创建资源,单独删除资源,垃圾箱可见,恢复回原路径,再永久删除清理索引。
|
||||
- [x] smoke 隔离 tmp root,不复用历史云 workspace。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task437-local-folder-asset-trash-lifecycle-smoke.js`
|
||||
- [x] `node scripts/task437-local-folder-asset-trash-lifecycle-smoke.js`
|
||||
- [x] 新增 resource trash browser smoke 通过。
|
||||
- [x] `cargo test -p mnote-web resource_trash -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web tree_command_local_folder -- --test-threads=1`
|
||||
|
||||
## 6. 本轮执行记录
|
||||
|
||||
- 已完成:
|
||||
- `/api/tree/commands` 支持 local resource archive / restore / purge,并返回 `tree.resource.*` canonical command。
|
||||
- local trash workbench 已从 `trash-index.json` 渲染 resource row,并通过 `/api/tree/commands` restore / purge。
|
||||
- `scripts/task437-local-folder-asset-trash-lifecycle-smoke.js` 覆盖 API 级 delete / restore / purge。
|
||||
- 新增 `scripts/task464-local-folder-resource-trash-ui-smoke.js`,覆盖真实垃圾箱页面 resource row 可见、UI 恢复、UI 彻底删除和索引清理。
|
||||
- 未完成:
|
||||
- 前端 bulk delete 场景尚未覆盖 tree command。
|
||||
- 风险:无
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不做完整 trash undo。
|
||||
- 不做 Convex 云端 parity。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 4-41 FileTree Bulk Resource Command Cutover Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):FileTree bulk resource command cutover 已完成,云端 table 旧 fallback 为显式保留边界。
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:File Tree bulk delete / `layout.rs`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-5 剩余项:前端本地文件夹 bulk delete 对 file asset、mindmap、table、raw local file 等资源优先走 `/api/tree/commands` 的 `tree.resource.*` 语义,旧 route 仅保留云端或兼容 fallback。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- `scripts/task464-local-folder-resource-trash-ui-smoke.js`
|
||||
- 可新增聚焦 bulk delete smoke
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不删除旧 `/api/mindmap`、`/api/media`、`/api/tables` 兼容 route。
|
||||
- 不改变 Convex 云端 asset lifecycle。
|
||||
- 不把 bulk delete 改成永久删除;默认仍是 archive/trash。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] local file asset bulk delete 使用 `dispatchTreeCommand({ action: 'archive', documentId })`。
|
||||
- [x] local mindmap bulk delete 使用同一 tree command,不再走 `/api/mindmap`。
|
||||
- [x] local table bulk delete 使用同一 tree command,不再走 `/api/tables`。
|
||||
- [x] bulk delete 成功后刷新本地 file tree snapshot / trash index 可见。
|
||||
- [x] 失败项保留可见错误,不清空未成功项。
|
||||
- [x] smoke 覆盖至少两个不同 resource kind 的批量删除进入垃圾箱。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `node --check scripts/task464-local-folder-resource-trash-ui-smoke.js`
|
||||
- [x] `node scripts/task464-local-folder-resource-trash-ui-smoke.js`
|
||||
- [x] `node --check scripts/task471-local-folder-bulk-resource-trash-smoke.js`
|
||||
- [x] `node scripts/task471-local-folder-bulk-resource-trash-smoke.js`
|
||||
- [x] 新增 bulk delete 合同测试通过:`cargo test -p mnote-web sidebar_filetree_local_table_bulk_delete_uses_tree_command -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web tree_command_local_folder -- --test-threads=1`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:local table bulk delete 分支切到 `/api/tree/commands` archive;file asset / mindmap 原先已经走同一命令。
|
||||
- 已保留:云端 table 仍走 `/api/tables/` fallback,旧 route 未删除。
|
||||
- 已新增:`scripts/task471-local-folder-bulk-resource-trash-smoke.js`,覆盖 local folder 两个资源行多选 Delete、两个 archive tree command、文件移入垃圾箱和 trash index。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不实现 undo。
|
||||
- 不实现云端 bulk lifecycle parity。
|
||||
@@ -0,0 +1,51 @@
|
||||
# 4-42 FileTree Open Target RootUri Contract Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):FileTree open target `rootUri` 合同已完成,缺失 `rootUri` 的旧 payload 兼容仍保留。
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:FileTree runtime open target / `filetree_runtime.rs`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P0-4 的剩余合同问题:`FileTreeOpenTarget::LocalFile` / `Directory` 已存在,但 `rootUri` 仍为空字符串占位。需要让 runtime 环境携带本地 root,并让 open target 输出真实 `rootUri`。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/tree_shell/filetree_runtime.rs`
|
||||
- `rust/crates/mnote-web/src/tree_shell/runtime_api.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- 相关 Rust 单测
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不改变 File Tree row 的长期资源真相。
|
||||
- 不改前端打开行为。
|
||||
- 不删除旧 `Asset` JSON 兼容字段。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] `FileTreeRuntimeEnvironment` 携带可选 `rootUri`。
|
||||
- [x] `resolve_open_target` 对 `LocalFile` / `Directory` 使用环境中的 `rootUri`。
|
||||
- [x] tree shell SSR app state / runtime reduce 请求填入 local folder `rootUri`。
|
||||
- [x] runtime API 反序列化对缺失 `rootUri` 保持兼容。
|
||||
- [x] Rust 单测覆盖 local file / directory open target 输出真实 `rootUri`。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `cargo test -p mnote-web filetree_runtime -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web runtime_api -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web tree_shell_filetree_mode_embeds_asset_state -- --test-threads=1`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:`FileTreeRuntimeEnvironment.rootUri`,并在 filetree runtime open target 中用于 local file / directory。
|
||||
- 已补充:tree shell 前端 runtime 环境构造会在 local folder 模式传入 `rootUri`。
|
||||
- 已验证:缺失 `rootUri` 的旧 runtime API JSON 仍可反序列化。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不处理 resource lifecycle。
|
||||
- 不处理 tab URL 同步。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 4-43 Local Folder Sort Order Persistence Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):local folder sort order 持久化、projection 消费与浏览器 smoke 均已完成。
|
||||
>
|
||||
> 日期:2026-05-20
|
||||
>
|
||||
> Owner:local folder tree command / file tree projection ordering
|
||||
|
||||
## 1. 目标
|
||||
|
||||
补齐 P1:`local_folder` 的 `move` 当前只把 `sortOrder` 回显到 `_unsupportedFields`,不能形成拖拽排序后的持久顺序语义。目标是在不引入第二套 UI 真相的前提下,让本地文件夹排序通过本地 metadata 持久化,并被 file tree / page tree projection 消费。
|
||||
|
||||
## 2. 允许修改范围
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs` 中仅限合同测试或调用透传
|
||||
- 可新增聚焦 smoke:`scripts/task47*-local-folder-sort-order-*.js`
|
||||
|
||||
## 3. 禁止事项
|
||||
|
||||
- 不通过重命名文件前缀实现排序。
|
||||
- 不把排序真相放到前端 DOM 或 localStorage。
|
||||
- 不改变 Markdown 页面正文事实源。
|
||||
- 不改变垃圾箱、restore、purge 的既有路径语义。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
- [x] 本地 metadata 增加可持久化排序记录,按 parent/container 维度表达子项顺序。
|
||||
- [x] `move` 带 `sortOrder` 时写入排序记录,不再返回 `_unsupportedFields.sortOrder`。
|
||||
- [x] projection 读取排序记录并稳定排序,未记录项继续按现有自然排序兜底。
|
||||
- [x] rename / move / archive / restore 后排序记录不会留下明显悬挂项或破坏 projection。
|
||||
- [x] Rust 合同测试覆盖 move sortOrder 持久化、重新加载 snapshot 后顺序保持。
|
||||
- [x] 如涉及可见行为,补 browser/API smoke 验证排序后重新加载 projection 不回退。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] `cargo test -p mnote-web local_tree_command_move_with_sort_order -- --test-threads=1`
|
||||
- [x] `cargo test -p mnote-web tree_command_local_folder -- --test-threads=1`
|
||||
- [x] 如新增 smoke:`node scripts/task472-local-folder-sort-order-persistence-smoke.js`
|
||||
|
||||
## 5.1 本轮执行记录
|
||||
|
||||
- 已合入:`.mnote/file-order.json` 按 parent relative path 记录子项相对路径顺序,projection 扫描时消费该顺序。
|
||||
- 已退役:`_unsupportedFields.sortOrder` 合同测试,改为持久化顺序和 snapshot 顺序断言。
|
||||
- 已合入:rename / archive / purge 对 file-order path 做 rewrite / remove,restore 回到文件系统自然排序或原 parent 现有顺序。
|
||||
- 本轮补齐:`scripts/task472-local-folder-sort-order-persistence-smoke.js` 使用隔离 tmp root,经 `/api/tree/commands` 执行 local_folder `move sortOrder`,再通过 `/api/tree/projections/file` 重拉 projection,验证刷新/重新加载后顺序不回退。
|
||||
- 执行记录:`node --check scripts/task472-local-folder-sort-order-persistence-smoke.js` 通过;`node scripts/task472-local-folder-sort-order-persistence-smoke.js` 在本地 `http://127.0.0.1:3000` 服务可用时通过。
|
||||
|
||||
## 6. 非目标
|
||||
|
||||
- 不实现跨设备 sync 的排序冲突合并。
|
||||
- 不实现完整 VSCode Explorer sort comparator 配置。
|
||||
Reference in New Issue
Block a user