Files
mnote/design/old/04-tree-domain/process/4-10-tree-rust-family-cutover-checklist-v1.md
T

35 KiB
Raw Blame History

4-10 [recycle] 树域 Rust 家族化执行清单 v1

更新时间:2026-04-26

回收说明(2026-04-28):本文是早期总执行清单,未完成项已被 4-114-164-18 拆分并覆盖。本文保留为历史执行口径,不再作为当前 process 入口;后续以 design/04-tree-domain/process/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.mddone/4-11done/4-18 为准。

关联文档:

  • /mnt/Data1T/mnote/design/old/04-tree-domain/process/4-9-tree-rust-family-cutover-remaining-architecture-and-capability-preservation-v1.md
  • /mnt/Data1T/mnote/design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.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-6-tree-command-protocol-cutover-stage2-v1.md
  • /mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md
  • /mnt/Data1T/mnote/design/04-tree-domain/done/4-11-tree-rust-family-final-renderer-and-host-thinning-checklist-v1.md
  • /mnt/Data1T/mnote/ARCHITECTURE.md

1. 文档目的

这份清单只服务一件事:

把树域从“Rust 持有语义、React 持有主执行面”的过渡态,推进到“Rust 家族同时持有语义与主执行面”的下一阶段。

本文不是上位方案稿,不重复讨论:

  • 为什么要继续 Rust 家族化
  • 为什么 Convex 要保留
  • 为什么 page tree / file tree 不能退化

这些已经在 4-9 与关联文档中固定。

本文只给:

  • 分阶段执行顺序
  • 每阶段 checklist
  • 每阶段阻塞项
  • 每阶段验收口径

2. 当前基线

当前默认真实状态(2026-04-25):

  • 树域 truth 已收口到 tree-first graph kernel
  • Convex 继续提供存储、同步、内容与实时底座
  • mnote-web 已具备 projection / command / transport 基础出口
  • page tree / file tree / picker 已开始消费 projection family
  • 主站默认 treeRendererFamily 已切到 rust_family
  • page tree / file tree / picker 默认主路径已进入 same-origin iframe compat host
  • 但当前切主的是 compat host,不是 final Rust renderer;宿主与 fallback 仍保留

因此本清单的目标不是“再做一轮 projection 收口”,而是:

  • 补齐 Rust 侧剩余契约
  • 迁掉主路径 React 树 renderer
  • 保住 page tree / file tree 现有能力

3. 总体执行顺序

建议固定为下面五段:

  1. Phase A:补齐 file_tree Rust 直接输出
  2. Phase B:完成 tree.* 命令主路径切换
  3. Phase C:补齐树域正式 realtime contract
  4. Phase D:迁移 page tree Rust 家族 renderer
  5. Phase E:迁移 file tree / picker Rust 家族 renderer,并收薄宿主

原因:

  • 先补 file_tree 输出,避免 renderer 迁移时继续背前端 adapter。
  • 先收命令与 realtime,避免 UI 壳迁完后还卡在旧协议。
  • page tree 先迁,能先验证主行 renderer 与交互骨架。
  • file tree 后迁,避免最复杂对象面成为第一块落地风险点。

4. Phase A:补齐 file_tree Rust 直接输出

当前进度(2026-04-25

  • 前端 file tree 主路径已优先消费 Rust kernelFileTreeProjection.items
  • /api/sidebar、SSR loader、/api/mnote-web/stream 已补 kernelFileTreeProjection
  • 非过滤主路径不再默认依赖 pageRows + assetsByDoc 拼装
  • file tree 同源 host 的 inline override 已收口到 kernel file_tree items
    • inlineFileTreeRows 旧注入边界已移除
    • TreeShellIframeHost 不再接受 FileTreeRow[] 作为正式 inline renderer 输入
  • 搜索过滤态不再回退 pageRows + assets 二次重建
    • buildVisibleRows 缺少 kernel file_tree items 时稳定返回空列表
    • filterKernelFileTreeProjectionItems 宿主裁剪 helper 已移除
    • 正式 Rust 搜索 projection 已在 4-11 Phase H 接入 3000 同源主链

目标

file_tree 不再依赖当前前端在 pageRows + assets 上继续拼主路径可见行语义。

checklist

  • 盘点当前 file_tree 仍由前端 adapter 补齐的对象语义:
    • index.md
    • 附件
    • mindmap 文件夹
    • 导图子附件
    • 表格 / 书籍 / PDF 等扩展对象
  • 在 Rust 侧明确 file_tree projection 的正式输出字段:
    • resource_kind
    • asset_kind
    • icon_hint
    • expandable
    • expanded_by_default
  • index / asset / asset_folder / mindmap / table / book / pdf 补 fixture
    • tree-delta.test.ts 已覆盖 delta 重建后的 mindmap / pdf / book / table
    • route.test.ts 已覆盖 snapshot route 中的 index / asset / asset-folder / mindmap / table / book / pdf
  • file_tree route 增加契约测试
    • /api/mnote-web/stream route snapshot 已断言 file_tree item contract 与 iconHint
  • 把前端 file tree 的主路径输入改成“消费 Rust 直接输出的 item 列表”,而不是继续拼对象真相
    • sidebar 主链默认输入已收口到 kernelFileTreeProjection.items
    • rust_family host 已不再接受 inlineFileTreeRows 旧 rows 注入

阻塞项

  • file_tree canonical 基础对象语义已由 projection contract 覆盖
  • 搜索态已接入 Rust 搜索 projection 主链;更完整的搜索输入扩展与 final renderer 收口已转入 4-11 Phase H/F

验收口径

  • file_tree 主路径不再依赖临时 adapter 维持对象层级
  • Rust route 输出足以直接驱动 renderer
  • index.md / asset-folder / asset 行语义在 fixture 与 route test 中稳定存在

5. Phase B:完成 tree.* 命令主路径切换

当前进度(2026-04-25

  • 已确认正式命令名:
    • create -> tree.node.create
    • rename -> tree.node.rename
    • move -> tree.subtree.move
  • 已新增前端 /api/tree/commands route,主路径可直接发正式 tree action
  • createDocumentCommand 已切到 /api/tree/commands
  • renameDocumentCommand 已切到 /api/tree/commands
  • moveDocumentCommand 已切到 /api/tree/commands
  • delete / restore / purge / embed / copy-tree 已切到 /api/tree/commands
  • create 保留本地 ensureDocumentScaffold 副作用,不因切流丢失
  • /api/documents/create 已收薄为 compat alias,转发 /api/tree/commands
  • /api/documents/title 已按命令语义分流:
    • 树重命名兼容请求 -> /api/tree/commands
    • page.head.updateTitle -> page write
  • /api/documents/move 已收薄为 compat alias,转发 /api/tree/commands
  • /api/documents/delete / restore / purge / embed / copy-tree 已收薄为 compat alias,转发 /api/tree/commands
  • bridge-runtime 已补 tree.node.archive / restore / purge / embedtree.subtree.copy 别名执行计划
  • tree.* 主路径已补路由 / shared client / compat alias / runtime 回归测试
  • move 的 target-parent legality、self / descendant 拦截、canonical parent_id / sort_order / workspace_id / updated_at 已下沉到 Convex documents.move
  • move legality 已继续离开 Next route 结果拼装,开始转到 Rust bridge-runtime 计算
    • /api/tree/commands 不再自己构造 movePreflight
    • Next route 现在只把 documents 快照作为 preflightData 交给 Rust
    • Rust bridge-runtime 已可从快照推导 target parent / ancestor chain 并拒绝 descendant / missing-parent
  • move legality 与更完整排序规则仍未完全进入最终 Rust kernel 主链;当前实际写入仍停留在 Convex compat mutation
    • Rust bridge-runtime 已新增 canonical move order plan,并在 command plan 中输出 normalizedMove
    • Convex documents.move 已增加 normalizedMove 可选校验,执行前确认当前 patch plan 与 Rust plan 一致
    • 最终 Rust 写路径执行下沉仍转入 4-11 Phase I

目标

让树域主路径默认走正式 tree.* 命令面,documents.* 退到 compat。

checklist

  • 盘点当前主路径仍在调用的 documents.* 入口:
    • create
    • rename
    • move
    • archive
    • restore
    • purge
    • embed
    • copy-tree
  • 明确每条命令对应的 tree.* 正式名
  • 前端 shared command client 默认发 tree.*
  • documents.* 仅保留 alias / compat
  • 把 move legality、target parent、position normalize 继续从 Convex compat mutation 回收给 Rust kernel
    • legality 与 normalized order plan 已在 Rust 可测试;TS transport 已透传 normalizedMoveConvex 已可选校验 Rust plan;最终写入仍待迁移
  • tree.* 路径补命令回归测试

阻塞项

  • embed / copy-tree 仍有较强前端语义残留
  • move legality 与排序规则已离开 Next route 拼装;排序计划已进入 Rust 可观测 plan 并由 Convex 可选校验,但实际执行仍待下沉

验收口径

  • 新增主路径不再继续扩写 documents.*
  • 主调用路径默认发 tree.*
  • 组件层不再承担长期树命令语义解释

6. Phase C:补齐树域正式 realtime contract

当前进度(2026-04-24

  • tree stream 前端主链已从“freshness 仲裁”收口到“stream live 优先,query/fallback 兜底”
  • usePreferredSidebarSnapshot 已按 treeStream.status 做主链选择:
    • live / connecting / idle 有 stream 数据时优先 stream
    • fallback 时回退 query
  • useSidebarData 手动 refetch 已收窄为临时 snapshot
    • 仅在当前 live 基线未变化时暂时覆盖
    • live 基线变化后回到新的 live snapshot
  • /api/mnote-web/stream 已升级为同源长连接 SSE
    • 首帧固定发 snapshot
    • 后续按最新 command_log cursor 轮询检测变化
    • 当前已支持最小 delta/noop/resync 分流,不伪造不稳定的复杂树增量
  • stream cursor 已从“复用 overview 分页 cursor”改成“stream 自己的 latest command-log cursor”
  • stream cursor 已补 domain_events 感知
    • command_logs 未推进、但 domain_events 新增时,不再静默停在旧 cursor
    • 当前最小口径先触发 resync,避免 workspace realtime 漏感知事件侧推进
  • workspace / subtree 双 scope 已在 route + test 形成正式 envelope 边界:
    • workspace -> sidebar_tree
    • subtree(rootNodeId) -> page_tree
  • /api/tree/commands 主路径已开始写入 bridgeLogs
    • create / rename / archive / purge / embed / copy / move / restore 都会推进 stream 可见 cursor
    • failed mutation 也会写 failure artifact,但 stream 主查询已收窄为只看 succeeded
  • stream 当前已能消费显式 streamDelta
    • create -> upsert_document
    • rename -> upsert_document(局部 patch)
    • page.head.updateTitle -> upsert_document(局部 patch)
    • archive / purge -> remove_document
    • embed -> noop
    • move -> move_document(细粒度 delta)
    • restore -> upsert_document(细粒度 delta)
    • copy -> upsert_documents(细粒度 delta)
    • documents 列表不可用时,仍保留 replace_sidebar / replace_documents 作为兼容兜底
  • move / restore / copy 已从“只能纯 resync”推进到显式 delta
    • move 已推进到 move_document
    • restore 已推进到 upsert_document
    • copy 已推进到 upsert_documents
    • Rust bridge-runtime 已开始输出 streamDeltaHintNext route 仅按 Rust plan hint 与 mutation result 物化 delta
  • 已新增树域 renderer 统一 delta 应用边界:
    • tree-delta.ts 已补 applyTreeStreamDeltaToProjectionState
    • sidebar_tree / page_tree / file_tree 已可从同一 delta 边界派生各自输入
  • replace_documents delta 重建时已保留 mindmapAssetChildren
    • file_tree 不再把 asset-folder 在 delta 重建时退化成普通 asset
  • Rust / 3000 同源 stream 已能从单条新 domain_event.payload.streamDelta 发出 delta
  • Rust / 3000 同源 stream 已能对同一 command_id 的 command log + domain event 双写做 delta 去重
    • streamDelta 一致时发一次 delta
    • 不一致或多条混杂时继续保守 resync
  • Rust command 侧已开始生成正式 tree delta / event hint
    • streamDeltaHint 覆盖 create / rename / archive / restore / purge / embed / move / copy
    • domainEventHint 覆盖 tree.node.created / renamed / archived / restored / purged / embedded / duplicatedtree.subtree.moved / copied
    • Next route 不再按 action 手写主 delta,而是按 Rust plan hint + mutation result 物化 streamDelta
  • resource command 侧已开始进入正式 delta / event hint
    • tree.resource.copy / move / upload 已输出 asset_result -> upsert_assets
    • domainEventHint 覆盖 tree.resource.copied / moved / uploaded
    • 资源 copy/move/upload 的 artifact 已由 3000 Rust artifact writer 持久化
  • Rust 写路径仍未覆盖所有主写入口的完整 domain event 持久化
    • mnote-web /api/tree/commands 已可由 Rust transport 直接生成并持久化 command_logs / domain_events
    • 3000 tree/media 主写入口与文档类 Rust transport adapter 已改为 Rust artifact writer
      • /api/tree/commands
      • /api/media/batch
      • /api/media/upload
      • page-command-adapter
      • page-lifecycle-command-adaptercreate / move / delete / restore / duplicate / copy_tree
      • save-command-adapter
      • metadata-command-adaptertitle / stats / options
      • lib/documents/block-command-adapter
      • page-write-command-adapterpage.head.updateTitle / page.layout.updateOptions / page.body.save
    • 当前显式 adapter 层已不再直接调用 TS recordBridgeCommandArtifacts(...)
  • 3000 仍是浏览器入口,但 polling transport 主体已在 Rust SSENext route 仍保留薄代理与过渡职责

目标

让树域从“可用 stream + fallback”进入正式 snapshot + delta + resync 合同。

checklist

  • 明确 workspace 与 subtree 两类 stream scope
  • 统一 snapshot / delta / resync / cursor 协议
    • snapshot / cursor 已在同源 stream route 固定
    • delta 已支持最小显式 contract,但复杂树变更仍待 Rust 正式事件面补齐
    • resync 仍是复杂树变更与漂移场景的保守回退
    • 当前勾选口径以 3000 同源 Next route contract 为准,server.ts / server.test.ts / route.test.ts 已覆盖;Rust 原生 transport 仍待继续收口
  • 补 cursor 漂移与 resync 策略
  • 明确大漂移时的回退口径
    • 当前保守口径:无法稳定解释的新 cursor 统一退 resync snapshot
  • page tree / file tree / sidebar_tree 补事件 fixture
    • server.test.ts 已覆盖 sidebar_tree snapshot / delta / resync
    • route.test.ts 已覆盖 page_tree subtree snapshot fixture
    • route.test.tstree-delta.test.ts 已覆盖 file_treedocument / index / asset-folder / asset 行语义
  • 为树域 renderer 定义统一 delta 应用边界
    • 当前口径是 documents/sidebar -> projection state 的统一边界,不等于 final Rust domain-event 细粒度 reducer
  • 让主路径不再主要依赖 freshness 选择与补偿式 refetch
    • sidebar stream 主链已变成“长连接 snapshot/delta/resync”
    • query/fallback 继续保留为护栏

阻塞项

  • Next route 级过渡 transport 仍在承担一部分正式职责
  • delta payload 的对象覆盖面还不够完整
  • Rust command 已给 move / restore / copy 输出 delta/event hint3000 tree/media 主写入口 artifact 持久化与显式 adapter 层已切到 Rust artifact writer;剩余未完成项转入更正式的 block/save-snapshot domain-event contract 收口
  • final domain event delta contract 已转入 4-11 Phase J

验收口径

  • 树域实时主链存在正式协议说明与测试
  • 主路径能稳定处理 snapshot、delta、resync
  • fallback 成为护栏,而不是主同步策略本身

7. Phase D:迁移 page tree Rust 家族 renderer

当前进度(2026-04-24

  • 前端 page tree / file tree surface 已显式暴露 rendererFamily 边界
  • SidebarTreeSurface 已成为更正式的 renderer host 边界,而不只是隐式 React 直连
  • 已新增 TreeShellHostpage tree / file tree / pickerrust_family 下会进入显式宿主分支,而不再只是透传 data-renderer-family
  • 主站已存在极薄挂载位;当前 rust_family 分支先受控包住 React fallback,后续再替换成真正的 Rust family renderer
  • 已新增 3000 同源 /api/tree/shell proxy route;浏览器不再直接持有 mnote-web base url
  • 未过滤 page treerust_family 下已可切到 same-origin iframe compat host,并 bridge
    • tree.navigate
    • tree.page.context-menu
    • tree.node.created / renamed / moved
  • page tree 已新增 tree.page.focus.changed bridge
    • 宿主已显式持有 focusedDocumentId 本地状态
    • page focus 不再只停留在 iframe 内部私有状态
    • iframe host 与 Rust shell contract 已补定向测试
  • 过滤态 page treerust_family 下已可通过 inline override 进入 same-origin iframe compat host
    • 浏览器端直接注入过滤后的 projection rows
    • 不再因为 filter 非空而强制回退 React fallback
  • 过滤态 page tree 即使筛到 0 条,也会继续通过 inline override 留在 same-origin iframe compat host
  • 默认主路径已切到 rust_family same-origin compat host
    • task112 主站 smoke 已验证 page tree hostKind 为 iframe
    • 当前切主的是 compat host,不等于 final Rust renderer 已完成
  • page tree 主路径 React renderer 已退出
    • TreeShellSurface 缺少 iframe 宿主时只显示 page-tree-renderer-removed
    • 主路径运行时已不再挂载旧 React PrivateTree
  • task112 已补 page tree 的 create / rename / 大树滚动真实 smoke
    • compat shell hover 动作区已用于实跑新建子页面与重命名
    • 大树场景下,键盘导航可把目标行稳定带入可视区
  • 当前 page tree 仍是 mnote-web tree shell compat host,不是最终 Rust renderer
  • 过滤态 page tree 当前仍是 compat host + inline override,不是最终 Rust family renderer

目标

page tree 的主渲染与主交互骨架退出当前 React 主壳。

checklist

  • 明确 page tree renderer 最小输入:
    • projection items
    • local UI state
    • command dispatcher
  • 抽离正式模块边界:
    • row model
    • focus model
    • keyboard model
    • DnD state machine
    • context action registry
  • 实现 Rust 家族 page tree renderer
  • 迁移展开/折叠
  • 迁移当前页高亮与祖先自动展开
  • 迁移 hover 动作区
  • 迁移右键菜单入口
  • 迁移键盘导航
  • 迁移基础拖拽排序
  • 在主站预留极薄挂载位
  • 用 feature flag 控制新旧 page tree 切换

page tree 能力保留硬门槛

  • 稳定层级展开/折叠
  • 当前页高亮
  • 祖先自动展开
  • 新建子页面
  • 重命名
  • 页面移动
  • 上下文菜单
  • 键盘导航
  • 基础拖拽排序
  • 大树场景稳定滚动

说明:

  • 上面已勾选的 右键菜单入口 / 上下文菜单 / feature flag,当前口径是“same-origin compat host + 宿主 bridge 已闭环”,不等于 final Rust renderer 已切主。
  • 本轮新增勾选的 展开/折叠 / 当前页高亮 / 祖先自动展开 / 键盘导航 / 拖拽排序 / 页面移动 / 新建子页面 / 重命名 / 大树场景稳定滚动 / hover 动作区,口径同样是“默认 rust_family compat host + task112 真实 smoke 已通过”,不等于 final Rust renderer 已完成。

阻塞项

  • 宿主边界仍过厚
  • 旧 React 组件仍承担主交互状态机

验收口径

  • 至少一条默认真实流量使用 Rust 家族 page tree renderer
  • 不再依赖当前 React PrivateTree 作为主路径 renderer
  • page tree 功能不低于当前产品合同

8. Phase E:迁移 file tree / picker Rust 家族 renderer,并收薄宿主

目标

page tree 主路径稳定后,迁移 file treepicker,并继续收薄主站宿主职责。

当前进度(2026-04-24

  • picker 空查询态与搜索态已统一复用 TreePickerSurface
  • picker 已开始复用与 tree surface 同一 renderer family 边界
  • file tree / picker / page tree surface 已显式输出 data-renderer-family
  • file tree / picker / page tree 已统一经由 TreeShellHost 进入显式 host 选择边界,并输出 data-tree-host-kind
  • picker 已接入 rust_family host 边界;当前 host 仍先包住 React fallback,不代表已切到 Rust renderer
  • 未过滤 picker 空查询态在 rust_family 下已切到 same-origin iframe compat host
  • picker 搜索结果态在 rust_family 下已切到 same-origin iframe compat host
    • 浏览器端通过 inline override 注入搜索结果
    • 不再因为搜索态而强制回退 React fallback
  • pickerrust_family 下,空查询且无可选页面时也会继续留在 same-origin iframe compat host
    • 不再因为空结果而直接退回宿主内纯文本 div
  • picker 根节点与排除项逻辑已补组件回归:
    • 空查询时保留根节点
    • excludeIds 不再把当前页误放回候选列表
    • 组件测试与真实浏览器核对已覆盖该口径
  • picker 空查询态也已支持键盘高亮与 Enter 选中
    • 不再只在搜索结果态依赖输入框 keydown hack
    • React fallback 与 same-origin compat host 都会同步当前高亮项
  • picker same-origin compat host 已透传 activePickerItemKey
    • 根节点高亮不再只能停留在 React fallback
    • Rust tree shell picker 页面测试已覆盖该 query/state
  • pickerrust_family 下已把输入框键盘命令继续下沉到 same-origin compat shell
    • 宿主改为发送 tree.picker.command
    • iframe 会回传 tree.picker.focus.changed
    • MoveEmbedPickerDialog 不再直接决定 rust_family 主路径的高亮切换与 Enter 选中
  • picker runtime 状态更新已对齐 Rust renderer input reducer contract
    • mnote-web /tree 与 3000 inline host 均暴露 rust_picker_state_reducer_v1
    • tree.picker.command 的 next/previous/home/end/pick/focus 已先统一经 applyPickerStateAction
    • 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
  • 未过滤 file treerust_family 下已可切到 same-origin iframe compat host,并 bridge
    • tree.navigate
    • tree.filetree.context-menu
    • tree.asset.open
    • tree.node.created / renamed / moved
  • file tree 已新增 tree.filetree.selection.changed bridge
    • 宿主可同步 selectedRowIds / anchorRowId / focusedRowId
    • 现有复制 / 删除 / 粘贴等依赖宿主选择态的链路不再只能停留在 React FileTree
  • file tree selection truth 已从宿主 legacy reducer 收薄为 renderer event snapshot
    • rust_familySidebar 只订阅 tree.filetree.selection.changed
    • 删除 / 复制 / 粘贴 / 上传 / 内部 drop 只读消费 renderer snapshot
    • SidebarTreeSurface 不再向 Rust file tree host 传 selectedRowIds 控制 prop
    • 该项不代表 compat runtime 内部选择算法已经 Rust runtime 化;后续仍转入 4-11 Phase F/G
  • file tree runtime 选择入口已对齐 Rust renderer input reducer contract
    • mnote-web /tree 与 3000 inline host 均暴露 rust_filetree_selection_reducer_v1
    • 单选 / 多选 / Shift 范围选 / 右键选中 / 可见行归一化 / drag rows 解析已先统一经 applyFileTreeSelectionAction
    • 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
  • file tree 删除目标归一化已进入 Rust preflight 主链:
    • tree.filetree.delete.preflight 已由 Rust bridge-runtime 输出 fileTreeDeletePlan
    • 3000 同源 /api/tree/filetree/delete-preflight route 与 client 已覆盖
    • Sidebar 删除链已不再自己推导 rust_family 主路径的 doc/asset 删除列表
  • file tree 粘贴目标与复制分类已进入 Rust preflight 主链:
    • tree.filetree.paste.preflight 已由 Rust bridge-runtime 输出 fileTreePastePlan
    • 3000 同源 /api/tree/filetree/paste-preflight route 与 client 已覆盖
    • Sidebar 粘贴链在 rust_family 下已不再自己推导目标页面、doc/index 递归语义和可复制 asset 列表
  • file tree 外部上传目标推导已进入 Rust preflight 主链:
    • tree.filetree.upload-target.preflight 已由 Rust bridge-runtime 输出 fileTreeUploadTargetPlan
    • 3000 同源 /api/tree/filetree/upload-target-preflight route 与 client 已覆盖
    • Sidebar 外部文件 drop 链已不再自己推导 target row、focused row、workspace fallback 与 mindmap target
  • same-origin compat shell 已补最小 file tree 交互骨架:
    • 单选 / 多选 / Shift 范围选
    • 右键聚焦选中
    • 空白区清空选择
    • doc / index / asset 双击打开
  • same-origin compat shell 已补 file tree 内部拖放 bridge
    • iframe 可发 tree.filetree.internal-drop
    • 宿主已回接现有 onInternalDrop
    • Alt copy / move 区分继续沿用宿主已有语义
  • same-origin compat shell 已补 file tree 外部文件拖入 bridge
    • 行级 drop 与空白区 drop 都会向宿主发 tree.filetree.external-drop
    • 宿主已回接现有 onDropFiles
  • same-origin compat shell 已补最小 asset-folder 行语义:
    • mindmap 不再只表现为普通 asset
    • 已可渲染 asset-folder -> asset 层级
    • asset-folder 已接入双击打开、右键菜单与 drop target
  • file tree 资源类型图标语义已补最小闭环
    • same-origin compat shell 已区分 pdf / image / video / audio / book / table / mindmap
    • React fallback FileTree 也已补同口径图标分支,避免切流前后视觉合同分裂
  • 过滤态 file treerust_family 下已可通过 inline override 进入 same-origin iframe compat host
    • 浏览器端已改为直接注入过滤后的 kernel file_tree items
    • 不再因为 filter 非空而强制回退 React fallback
  • 过滤态 file tree 已先从 kernelFileTreeProjection.items 收敛正式 item contract,再统一生成 rows 与 same-origin host inline 输入
    • 不再回退 pageRows + assets 二次重建主可见行
    • sidebar 主路径过滤态已优先消费 kernel file tree items
  • file tree 搜索态已切到 Rust projection query 主链
    • core-protocolKernelProjectionFilter 已包含 query
    • bridge-runtimekernel.project_view(file_tree) 可按 query 输出命中项与必要祖先,并裁剪 edges
    • mnote-web /api/tree/projections/file?...&query= 已透传 query 到 Rust projection
    • wolai-frontend 新增 3000 同源 /api/tree/projections/file routeSidebar 搜索态只消费该 route 返回的 Rust file_tree projection items
    • filterKernelFileTreeProjectionItems 宿主裁剪主链已移除
  • file tree same-origin compat host 已能直接消费 kernel file tree items
    • 过滤态宿主不再必须先把 FileTreeRow[] 重新翻译回 inline item
  • file tree inline override 已移除 FileTreeRow[] 正式输入
    • TreeShellIframeHost 现在只接受 kernel file_tree items 作为 inline projection contract
  • file tree 默认主路径已切到 rust_family same-origin compat host
    • task112 主站 smoke 已验证 file tree hostKind 为 iframe
    • 当前切主的是 compat host;复杂交互执行面仍依赖宿主 bridge
  • file tree 主路径 React renderer 已退出
    • TreeShellSurface 缺少 iframe 宿主时只显示 file-tree-renderer-removed
    • 主路径运行时已不再挂载旧 React FileTree
  • file tree 首屏 Rust initial DOM 的 hydrate 后状态 patch 已继续收薄
    • TreeShellIframeHostdata-rust-filetree-renderer=initial_v1 hydrate 成功后,对宿主 active / selection patch 只更新 data-active / data-selected
    • 该项只是减少 compat JS 整树重绘,不等于 final Rust renderer 已完成
  • 3000 inline host 已减少第二份 projection 注入真相
    • TreeShellIframeHost 主路径现在把 inline projection items 放入 tree-shell-state.items
    • rendererInputitems 同时进入 appState JSONcompat runtime 优先从该合同读取 expanded / selection / picker active / exclude
    • __MNOTE_TREE_SHELL_OVERRIDE__ 不再作为 3000 主路径默认注入第二份 items,只保留旧兼容入口
  • 3000 inline host 已对齐 Rust state reducer 合同
    • page focus/keyboard 暴露 rust_page_focus_keyboard_reducer_v1
    • filetree selection 暴露 rust_filetree_selection_reducer_v1
    • picker state 暴露 rust_picker_state_reducer_v1
    • 当前只是把默认主路径 compat runtime 的状态更新入口统一到 Rust contract,不等于 final renderer 完成
  • file tree / picker 的真正 Rust family renderer 仍未接入;当前只是接入 same-origin compat host,而不是最终 renderer
  • 宿主仍承担实际命令调度、上传执行与部分可见行 / DOM runtime 过渡职责;还不是最终 Rust family 正式模块边界
    • 内部 drop / 删除 / 粘贴 / 外部上传目标的目标与对象分类已下沉 Rust preflight,但文件字节读取、菜单状态、实际 transport 与通知仍在宿主

checklist

  • 明确 file tree renderer 的最小输入与可见行模型
    • 当前主链最小输入已先统一为 kernel file_tree items + expanded document ids + expanded asset-folder ids
  • 迁移 doc -> index -> asset-folder -> asset 可见行语义(compat shell 最小版)
  • 迁移资源图标与资源菜单分支
    • 资源图标 contract 已在 route.test.ts / tree-delta.test.ts 覆盖
    • tree.filetree.context-menu bridge 已在 tree-shell-iframe-host.test.tsx 覆盖
  • 迁移单选 / 多选 / Shift 范围选 / 右键选中
  • 迁移双击打开资源
  • 迁移内部拖放(bridge 到宿主既有 drop 处理)
  • 迁移外部文件拖入上传(bridge 到宿主既有上传处理)
  • 迁移 copy / move 区分与非法投放校验(Rust preflight 主链 + 宿主 transport 过渡)
  • 迁移粘贴目标推导与复制分类(Rust preflight 主链 + 宿主 transport 过渡)
  • picker 复用同一 renderer / state family 的轻量模式(先完成统一 TreePickerSurface
  • picker 搜索结果态进入 rust_family same-origin compat host
  • 收掉主路径 React FileTree renderer
  • picker 键盘高亮 / Enter 主链继续从宿主 input keydown 收到 iframe shell
  • 收掉宿主内剩余树结构重建与主交互状态骨架(本批可验证范围)
    • 3000 主路径已固定为 state.items + rendererInput,不再通过 __MNOTE_TREE_SHELL_OVERRIDE__ 注入第二份 itemsReact FileTree/PageTree fallback 不再作为 rust_family 主路径。完整 Rust runtime 替换仍归入后续 final renderer。

file tree 能力保留硬门槛

  • 页面
  • index.md
  • 附件
  • mindmap 文件夹
  • 导图子附件
  • 多选
  • 范围选
  • 资源级右键菜单
  • 双击打开
  • 内部拖放
  • 外部文件拖入
  • 资源类型图标语义

picker 能力保留硬门槛

  • 空态稳定显示
  • 搜索结果稳定显示
  • 高亮与键盘选中稳定
    • 组件回归与 Rust picker shell 定向测试已补
    • task113 已在 3000 主站默认 rust_family 配置下实跑通过
  • 根节点与排除项逻辑不回退

阻塞项

  • file_tree 搜索过滤已不再由宿主裁剪 items,但 compat shell 内仍有一层把 items 规范化为 DOM 行的过渡逻辑
  • 选择 truth 对宿主已变成 renderer event snapshot,但 compat runtime 内部仍有 TS 选择/拖放/菜单 DOM 状态机,仍未形成完全独立的 Rust family 正式模块
  • file tree 复杂拖放 / 粘贴 / 上传态的执行仍依赖宿主 bridge;内部 drop、删除、粘贴目标、外部上传目标 preflight 已下沉 Rust,过滤态已不再强制回退 React fallback
  • picker 搜索态虽已进入 same-origin compat host,但当前仍是 compat shell + inline override,不是最终 Rust renderer / 正式搜索合同
  • picker 键盘高亮链已补组件回归、shell query/state 测试与 3000 主站真实 smoke
  • 当前剩余差距不在“键盘能否工作”,而在“仍是 compat shell + 宿主状态机,而非 final Rust renderer”
  • final renderer 与宿主收薄已转入 4-11 Phase F/G

验收口径

  • file tree 不再依赖当前 React FileTree 作为主路径 renderer
  • picker 复用统一 renderer 家族
  • 宿主不再承担主要树域交互状态机
  • 当前实现距离该验收口径仍有明确差距:
    • same-origin compat host 已接入
    • picker 搜索态已进入 compat host,但仍不是最终 Rust renderer
    • file tree 的内部拖放 / 外部上传 / asset-folder 最小语义与搜索 projection 已接入,但还停留在 compat shell + 宿主 bridge
    • 还不能宣称 Phase E 已完成

9. 跨阶段通用回归要求

每一阶段切流前都必须至少补齐以下回归:

  • 页面树切页 smoke
  • 页面树展开/折叠 smoke
  • 页面树拖拽 smoke
  • 页面树右键菜单 smoke
  • 文件树多选与范围选 smoke
  • 文件树双击打开 smoke
  • 文件树内部拖放 smoke
  • 文件树外部文件拖入 smoke
  • picker 搜索 / 空态 / 选中 smoke
  • stream 中断 fallback smoke

要求:

  • 不只验证“组件能渲染”
  • 要验证“关键交互能力未退化”

补充口径(2026-04-24):

  • 当前以上 10 项由 scripts/task112-tree-rust-family-regression-smoke.js 实跑覆盖:宿主页负责切页、右键菜单、文件树双击打开、picker 与 stream fallback/api/tree/shell 直连页负责页面树展开/折叠/拖拽,以及文件树多选、范围选、内部拖放、外部文件拖入。
  • 其中页面树 / 文件树拖放与 picker 当前主要验证 same-origin compat shell、宿主 bridge 与命令回写链路;宿主完全收薄后的 final renderer 端到端 smoke,仍应在 Phase D / E 收尾时继续补强。

10. 当前最近一步

按当前优先级,最近一步建议固定为:

  1. 先补 Phase A,把 file_tree Rust 直接输出缺口补齐。
  2. 同时推进 Phase B,把主路径命令面收口到 tree.*
  3. 在这两件事没有完成前,不建议宣称“树域主执行面已经可以正式切到 Rust 家族 renderer”。

原因:

  • 如果 file_tree 语义还要靠前端补
  • 如果命令面还主要停在 compat

那么换 renderer 只会把现有双轨问题搬进新壳,而不是收掉它。


11. 完成判定

只有同时满足下面几条,才可以宣称“树域 Rust 家族化这一轮完成”:

  • page tree / file tree / picker 主路径 renderer 已进入 Rust 家族
  • file_tree 已由 Rust 直接输出正式对象投影
  • 主调用路径默认走 tree.*
  • realtime 已进入正式 snapshot + delta + resync 合同
  • 宿主只保留极薄挂载与桥接职责
  • page tree / file tree 的现有能力没有因迁移退化

12. 一句话收口

这轮工作的正确推进方式不是:

  • “先把现有 React 组件翻译成 Rust”

而是:

先补齐 file_tree / command / realtime 三条主链,再以能力不退化为硬门槛,逐步把 page tree / file tree / picker 的主 renderer 和主交互骨架迁入 Rust 家族。