Files
mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md
T

17 KiB
Raw Blame History

2 [process] Convex 保留前提下的 Tree-First Graph 长期架构方案 v1

更新时间:2026-04-22

当前优先级入口:

  • /mnt/Data1T/mnote/design/01-05-current-priority-overview.md

关联文档:

  • /mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md
  • /mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md
  • /mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md
  • /mnt/Data1T/mnote/ARCHITECTURE.md

1. 文档目的

这份文档用于固定一个容易在讨论中被混淆的问题:

当 mnote 沿着 Tree-First Graph 与 Rust 主导路线继续重构时,是否要拆掉当前本地自托管 Convex。

本文件给出的结论是:

  • 不建议把 Convex 从当前主线中拆掉。
  • 长期要收口的是“语义主导权”和“统一执行面”,不是物理上把 Convex 替换掉。
  • Convex 继续保留为本地自托管的存储 / 实时 / 文件底座;Rust 负责统一 kernel 语义、命令、查询、投影与页面承载。

这份文档要解决的不是短期修 bug,而是长期架构方向误判的问题。


2. 先给结论

结论固定为四句:

2.1 不拆 Convex

当前仓库里的 Convex 不是一个“外部云黑盒”,而是本地自托管主线的一部分。

在当前语境下,它承担的不是单一数据库职责,而是接近下面这些能力的组合:

  • 结构化持久化主链
  • 实时订阅能力
  • 与文件 / 对象存储协作的业务底座
  • 当前已经跑通的部署、权限、工具与调试体系

因此,长期不建议为了“Rust 化”而先把 Convex 拆掉。

2.2 要收口的是语义,而不是先替换底座

长期真正应该统一的是:

  • 树结构真相定义权
  • node / edge / subtree / projection 语义
  • 命令入口
  • 查询入口
  • 审计 / 版本 / trace 口径
  • 页面投影分发口径

这些都应逐步收口到 Rust kernel,而不是继续散落在:

  • 前端页面层
  • Next route 层
  • 临时 compat adapter
  • 多套 tree / sidebar 拼装逻辑

2.3 长期正确形态是“Rust 驾驭 Convex”

长期推荐形态不是:

  • Rust 替代 Convex

而是:

  • Convex 继续作为 storage / realtime substrate
  • Rust 成为 tree-first graph kernel 的唯一语义拥有者

也就是说:

Convex 保留底座,Rust 收口语义,前端只消费稳定 projection。

2.4 当前主要问题不是 Convex 性能,而是前端主链边界错误

当前页面体感与树实时体验不理想,主因不是 Convex 不够快,而是:

  • 主页面首屏错误依赖实验性 Rust compat/sidebar 路径
  • Sidebar 主数据链路从 Convex 实时订阅退化成 HTTP 拉取
  • Tree shell 仍是实验壳,不是真实时订阅主链
  • 树逻辑仍有一部分散落在前端拼装层

所以当前修正重点应当是:

  • 恢复前端主路径的实时链路
  • 限制实验壳进入首屏关键路径
  • 继续把树语义收回 Rust kernel

而不是直接怀疑 Convex 物理底座本身。


3. 与已有 Tree-First Graph 文档的关系

/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md 已经明确固定了四层边界:

  1. 事实源:tree-first graph kernel
  2. 投影:sidebar tree / file tree / read view / search / mindmap
  3. 编辑器:BlockNote / Mindmap canvas / OnlyOffice
  4. 外挂:AI、评论、历史、回链等

其中关键规则已经写得非常清楚:

  • 事实源只在 kernel
  • 投影不拥有对象真相
  • 编辑器不等于对象模型
  • 外挂只消费 kernel 或 projection

这份文档在该边界上进一步明确一件事:

这里的“kernel 是事实源”并不要求物理上先废弃 Convex。

更准确地说:

  • kernel 是语义事实源
  • Convex 是当前推荐继续保留的物理存储与实时底座

因此,长期路线不是“两个事实源并存”,而是:

  • 一个语义真相:Rust kernel
  • 一个底层持久化 / 实时底座:Convex

4. 长期推荐分层

4.1 Convex Substrate

Convex 长期保留为底层能力面,职责包括:

  • 主数据持久化
  • 附件与对象存储协作
  • 当前态读写落账
  • 实时订阅能力
  • 当前 deployment / auth / tooling 基础设施

这层不应再承载散落的页面级语义。

4.1.1 Convex 保留,但不再继续承担“上层树语义拼装”

长期应避免继续新增:

  • 前端专用临时树拼装逻辑
  • 只为某个页面壳存在的 query 契约
  • 与 Rust kernel 并行演化的第二套页面结构规则

4.2 Rust Kernel

Rust kernel 长期应成为:

  • node / edge / subtree / projection 的唯一语义拥有者
  • 唯一命令入口
  • 唯一查询语义入口
  • 唯一排序 / 子树 / 引用 / 投影规则来源
  • 唯一版本 / trace / audit 规则来源

4.2.1 当前应重点收口的 kernel 语义

优先级最高的是:

  • sidebar_tree
  • page_tree
  • file_tree
  • read_view
  • mindmap projection
  • search_results

也就是说,前端不应再拥有:

  • 页面树结构的独立真相
  • 文件树行语义的独立真相
  • Sidebar 视图拼装的独立真相

它们都应回到 Rust kernel 定义,再由底层数据承接。


4.3 Rust Web

Rust Web 层长期继续按 axum 方向推进,职责包括:

  • API
  • SSR 页面壳
  • SSE / WS
  • projection 请求分发
  • 鉴权上下文、workspace 上下文、trace 注入
  • 为前端 islands 提供稳定读取与流式协议

4.3.1 Rust Web 的定位

Rust Web 不是第二套业务内核。

它应当:

  • 承接 Web transport
  • 调用 Rust kernel
  • 利用底层 Convex 数据与实时能力
  • 给页面壳输出稳定 projection

它不应:

  • 在 route 层重新发明树语义
  • 在 compat 层长期保存第二套逻辑

4.4 View Shell

长期页面模型继续建议:

  • server-first
  • 阅读优先
  • 少量 islands hydration

也就是:

  • 页面先出来
  • 阅读先可用
  • 局部交互再进浏览器
  • 编辑器最后挂载

这与 /mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md 的方向一致。


4.5 Browser Islands

浏览器端长期只保留必要交互壳:

  • Sidebar 交互
  • 文件树交互
  • 搜索交互
  • AI bridge panel
  • Mindmap 交互
  • BlockNote 编辑岛

浏览器端不再承担对象真相定义。


5. 长期运行原则

长期固定以下原则:

5.1 不拆 Convex 主底座

  • 不为了“Rust 化”先拆掉当前自托管 Convex。
  • 不引入新的第二主数据库去与 Convex 长期双写对抗。
  • 不让本地缓存、本地 SQLite、浏览器存储升级为主事实层。

5.2 Rust 拥有语义主导权

  • 新增树规则、页面结构规则、引用规则、投影规则,只能进 Rust kernel。
  • 不再把新的业务规则继续写回前端布局层、Next route 层或 compat adapter。

5.3 前端不再定义树真相

  • 前端只能消费 projection。
  • 前端可做乐观更新,但必须以 Rust 定义的命令/投影契约为准。
  • 不再允许多个 tree adapter 在前端各自维护独立结构语义。

5.4 实验壳不进入首屏关键主链

  • Rust tree shell、compat sidebar 等实验路径,在未完成稳定实时协议前,不允许阻塞主页面首屏。
  • 任何实验壳都必须有明确 fallback,且 fallback 不影响用户进入页面。

6. 当前实现审计

以下结论基于 2026-04-17 当前仓库代码,而不是早期方案假设。

6.1 当前已经落地的部分

6.1.1 Sidebar 主链已恢复为“Convex live 优先,HTTP fallback 兜底”

当前主数据链路不是“默认退化成 HTTP 拉取”。

已确认:

  • useSidebarData 先走 useConvexSidebarData
  • useConvexSidebarData 内部使用 Convex useQuery
  • 只有没有 live subscription 且允许 fallback 时,才启用 /api/sidebar + React Query

这说明当前 Sidebar 主链已经回到:

  • Convex realtime substrate 为主
  • HTTP route 仅作兜底和兼容

6.1.2 /api/sidebar 已不是旧 compat 主路径,而是 Rust query envelope + Convex transport

当前 /api/sidebar 在 Convex 模式下会:

  1. 建立 buildDocumentBridgeContext
  2. 构造 sidebar.dataset.list query envelope
  3. 解析 Rust bridge query plan
  4. 通过 Convex client 执行 transport
  5. 再映射回前端 SidebarInitialData

这意味着当前服务端查询链路已经是:

  • Rust 负责 query 语义与 envelope
  • Convex 负责底层执行与数据承接

不是旧意义上的“前端自己拼 sidebar 数据”。

6.1.3 Tree shell 已被收紧为显式开关,默认关闭

当前运行时边界已经改成:

  • 浏览器公开 runtime 已不再暴露 mnoteWebBaseUrl / mnoteWebTreeShellEnabled
  • mnote-web 默认监听地址已改为 127.0.0.1:0,不再默认绑定 3104
  • /tree/document-debug 仅在 MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES=1 时注册
  • legacy tree shell / document runtime smoke 必须显式传入 MNOTE_WEB_SMOKE_BASE_URL

这说明 tree shell 当前定位已经收口为“显式 debug/runtime 对照壳”,不再是默认首屏主链。

6.1.4 第一批 tree command 已接入 Rust command envelope

当前至少以下页面树命令已经接入 bridge / envelope 主链:

  • documents.create
  • documents.title.update
  • documents.move
  • documents.delete
  • documents.restore
  • documents.purge

其中 create / rename / move 已进一步收成共享 tree-command-client,并被 Sidebar、DocumentContent 等主入口复用;CustomSideMenu 已退到 recycle/ 历史参考区。

6.1.5 Rust Web 正式实时树事件流已经有方案文档,但还不是运行中主链

design/rust-web-tree-realtime-event-stream-v1.md 已经明确:

  • Convex 是 realtime substrate
  • Rust 是 semantic owner
  • Rust Web 负责正式 SSE / WS transport

但当前仓库里真正运行中的树主链,仍然不是这套正式 stream。

6.2 当前仍存在的差距

6.2.1 Tree shell 仍然是显式实验壳,不是正式主链壳

当前 tree shell 已被明确降级为:

  • 显式开关
  • iframe + postMessage
  • ready timeout fallback

因此它现在适合作为:

  • picker / filetree / tree viewer 的增强壳
  • UI 协议验证壳

而不是首页或 Sidebar 首屏前提。

6.2.2 Tree stream 已接入正式 consumer,但 delta 真相还不是最终形态

当前已经落地:

  • Rust Web workspace / subtree snapshot stream
  • snapshot / delta / resync 协议
  • 前端 useSidebarTreeStream 正式 consumer

但当前 delta 仍是前端基于已有 sidebar dataset builder 做最小重建,不是 Rust 直接下发的细粒度 projection delta 真相。

6.2.3 页面 subtree 仍以本地 projection 组装为主

当前文档页已去掉 synthetic projection id,但正文 subtree 仍主要由前端本地 buildPageSubtreeProjection 生成。

这意味着:

  • Sidebar / file tree 的 projection 契约已明显收口
  • 文档正文 page_tree / read_view 仍未完全切成 Rust server-first projection

6.2.4 SSR 页面壳已经验证边界,但 server-first 分发仍有继续收口空间

当前已经确认:

  • Rust Web kernel projection 路由可通过测试
  • 首页 smoke 继续证明 3000 主链不依赖 3104 可用
  • (app) 首屏仍维持“先可进入页面”的安全边界

但服务端侧边栏首屏数据仍是安全优先的稳定路径,并未把所有读取都强制切成 Rust Web 首选。

6.2.5 代码层还有存量 warnings

当前相关 lint 均为 0 error,但仍保留一些历史 warning,主要集中在:

  • sidebar.tsx
  • recycle/wolai-frontend/src/components/editor/menus/CustomSideMenu.tsx

这些不是本批必须修复的主链 bug,但后续仍应继续压缩。


7. 与目标架构的差距结论

如果按“Convex 保留底座,Rust 收口语义,前端只消费 projection”来打分,当前状态更接近:

  • Convex substrate:成立
  • Rust command/query envelope:已进入主路径
  • 前端首屏不依赖实验壳:成立
  • Rust Web 正式 realtime snapshot stream:已成立
  • Sidebar 主路径消费统一 stream/projection:已成立
  • 页面 subtree / read view 完整 server-first projection:仍未完全成立

因此当前真正剩余的差距,不再是“是否拆 Convex”,而主要变成下面两点:

  1. 页面正文 subtree / read view 仍有一部分语义停留在前端本地 projection。
  2. tree stream 的长期目标仍应继续从“snapshot + 最小 delta”推进到“Rust 直接分发更稳定的 projection/delta 真相”。

8. Checklist

下面 checklist 分成“当前已完成”和“后续待完成”,后续维护时应优先更新这里。

8.1 当前已完成

  • 固定长期口径:不拆 Convex,保留为 storage / realtime substrate。
  • 固定长期口径:Rust 作为 tree-first graph 的 semantic owner。
  • Sidebar 主链恢复为 Convex useQuery live subscription 优先,HTTP fallback 只作兜底。
  • /api/sidebar 已接入 Rust query envelope,并通过 Convex transport 执行。
  • 浏览器公开 runtime 已移除 mnoteWebBaseUrl / mnoteWebTreeShellEnabled
  • legacy tree shell / document runtime 已降级为显式 debug smoke,不再混入默认主链。
  • create / rename / move 已接入 shared tree command client。
  • create / title.update / move / delete / restore / purge 已接入 Rust bridge / command envelope 主链。
  • 已产出 tree-command-envelope-cutover-stage1-v1.md,明确第一批命令收口边界。
  • 已产出 rust-web-tree-realtime-event-stream-v1.md,明确正式实时树事件流分层方案。

8.2 本批新增完成项

P0:主链边界继续固定

  • 首页、Sidebar 与文档页首屏继续保持“不依赖 3104 tree shell 或 compat viewer 才能进入”。
  • tree shell 已继续固定为显式增强能力,不再回流为默认主路径。
  • 首页 smoke、Sidebar live 优先与 tree shell fallback 回归检查已补到当前 harness 主链。

P1tree command 收口继续推进

  • delete / restore / purge 已统一走 shared tree command client。
  • pageReference 删除路径已移出组件内直调 route,改走共享 command client。
  • embed 已切成独立 documents.embed Rust page-tree command,而不再停留在 documents.save 过渡语义。
  • copy-tree 已收口到 shared tree command client,并保持 Rust bridge command 主链。

P2:树规则与 projection 契约继续回收

  • targetParentId / sortOrder / subtree move legality 已形成可测试的统一前端边界,并通过 Rust/bridge 相关回归验证。
  • sidebar_tree / page_tree / file_tree 的主路径 projection 契约已继续统一,去掉主路径 synthetic projection fallback。
  • Sidebar / picker / host 主路径已继续压缩本地树真相,只消费统一 projection。
  • compat / fallback 中重复的主路径树拼装已继续清理,避免再把旧树真相带回主链。

P3Rust Web 正式 realtime 主链已打通第一阶段

  • Rust Web 已落地正式 workspace / subtree snapshot stream,不再是 placeholder。
  • 已建立 snapshot + delta + resync 的前后端基础协议。
  • Sidebar 已接入 useSidebarTreeStream 正式 consumer。
  • tree stream 真实连接路径已对齐 Rust Web /api/stream/events,修复了前端错误连接 /api/tree/events 的运行时 bug。

P4:页面壳与 QA 收口

  • Rust Web kernel projection SSR 路由已通过测试,主链继续保持 server-first 页面壳与安全 fallback。
  • 文档页 subtree 已移除 synthetic projection id,避免前端继续暴露自造 projection 标识。
  • 前端全量测试已恢复通过,AiAgentPanel 过期断言已更新为当前稳定语义。
  • 本批相关 smoke、vitest、cargo test、eslint 已全部通过既定 validation。

后续演进观察

下面这些仍是后续长期演进方向,但不再作为本批 checklist:

  • 页面正文 page_tree / read_view 仍应继续向 Rust server-first projection 收口。
  • tree stream 仍可继续从“snapshot + 最小 delta”推进到更稳定的 Rust 侧 projection/delta 分发。
  • 若后续要让 Rust Web 承接更多 SSR 数据分发,应继续坚持“3104 不可用时 3000 仍能进入页面”的安全边界。
  • 现有 lint warnings 仍需后续逐步清理,但不影响当前批次主链验收。

9. 最终固定口径

截至当前仓库状态,可以固定为:

mnote 的长期路线不是拆掉 Convex,而是在 Convex 继续作为底层 substrate 的前提下,让 Rust 逐步拿回 tree-first graph 的 query、command、projection 与 realtime 语义主导权。

当前已经完成的是:

主路径边界已基本纠正,tree shell 已降级为显式实验增强,Sidebar 与第一批 tree command 已进入 Convex substrate + Rust envelope 主链。

未来还需要完成的是:

让树规则真正从前端退出,并让 Rust Web 的正式 realtime transport 接住统一主链。