Files
mnote/design/old/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md
T
lix-2026 cdff672aa5 feat: align local-first workspace direction
Document the VSCode-like local-first product shape, demote Convex to a control-plane role, and retire stale architecture drafts.

Add local workspace migration/export references plus smoke coverage for no-Convex managed workspace startup, local markdown title/body/options persistence, asset upload behavior, and Convex fixture export.

Verification: git diff --cached --check; node scripts/check-local-first-convex-guard.js --staged; node scripts/task444-convex-workspace-export-local-fixture-smoke.js; node scripts/task166-local-first-managed-workspace-no-convex-smoke.js; node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
2026-05-19 08:11:58 +08:00

511 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2 [recycle][legacy] Convex 保留前提下的 Tree-First Graph 长期架构方案 v1
> 更新时间:2026-05-18
>
> 2026-05-18 口径更新:
> - 本文保留为历史过渡判断:它解释“不要在没有替代控制面、迁移工具和兼容策略时硬拆 Convex”。
> - 当前新增能力的上位方向已由 `design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md` 覆盖:本地文件夹是早期产品默认数据真相,Rust kernel 是唯一语义真相,Convex / 服务端降级为账号、分享、同步、协作和 AI 隔离控制面。
> - 因此,本文中“Convex 作为默认存储 / 实时 / 文件底座”的表述只代表旧代码过渡态和历史决策背景,不再作为新增功能默认方向。
>
> 当前优先级入口:
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.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。**
本文件在 2026-04-22 给出的历史结论是:
- **不建议把 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 当前已完成
- [x] 固定历史过渡口径:不无计划硬拆 Convex,旧主链保留为 storage / realtime substrate。
- [x] 固定长期口径:Rust 作为 tree-first graph 的 semantic owner。
- [x] Sidebar 主链恢复为 Convex `useQuery` live subscription 优先,HTTP fallback 只作兜底。
- [x] `/api/sidebar` 已接入 Rust query envelope,并通过 Convex transport 执行。
- [x] 浏览器公开 runtime 已移除 `mnoteWebBaseUrl / mnoteWebTreeShellEnabled`
- [x] legacy tree shell / document runtime 已降级为显式 debug smoke,不再混入默认主链。
- [x] `create / rename / move` 已接入 shared tree command client。
- [x] `create / title.update / move / delete / restore / purge` 已接入 Rust bridge / command envelope 主链。
- [x] 已产出 `tree-command-envelope-cutover-stage1-v1.md`,明确第一批命令收口边界。
- [x] 已产出 `rust-web-tree-realtime-event-stream-v1.md`,明确正式实时树事件流分层方案。
## 8.2 本批新增完成项
### P0:主链边界继续固定
- [x] 首页、Sidebar 与文档页首屏继续保持“不依赖 `3104` tree shell 或 compat viewer 才能进入”。
- [x] tree shell 已继续固定为显式增强能力,不再回流为默认主路径。
- [x] 首页 smoke、Sidebar live 优先与 tree shell fallback 回归检查已补到当前 harness 主链。
### P1tree command 收口继续推进
- [x] `delete / restore / purge` 已统一走 shared tree command client。
- [x] `pageReference` 删除路径已移出组件内直调 route,改走共享 command client。
- [x] `embed` 已切成独立 `documents.embed` Rust page-tree command,而不再停留在 `documents.save` 过渡语义。
- [x] `copy-tree` 已收口到 shared tree command client,并保持 Rust bridge command 主链。
### P2:树规则与 projection 契约继续回收
- [x] `targetParentId / sortOrder / subtree move legality` 已形成可测试的统一前端边界,并通过 Rust/bridge 相关回归验证。
- [x] `sidebar_tree / page_tree / file_tree` 的主路径 projection 契约已继续统一,去掉主路径 synthetic projection fallback。
- [x] Sidebar / picker / host 主路径已继续压缩本地树真相,只消费统一 projection。
- [x] compat / fallback 中重复的主路径树拼装已继续清理,避免再把旧树真相带回主链。
### P3Rust Web 正式 realtime 主链已打通第一阶段
- [x] Rust Web 已落地正式 `workspace / subtree` snapshot stream,不再是 placeholder。
- [x] 已建立 `snapshot + delta + resync` 的前后端基础协议。
- [x] Sidebar 已接入 `useSidebarTreeStream` 正式 consumer。
- [x] tree stream 真实连接路径已对齐 Rust Web `/api/stream/events`,修复了前端错误连接 `/api/tree/events` 的运行时 bug。
### P4:页面壳与 QA 收口
- [x] Rust Web `kernel projection` SSR 路由已通过测试,主链继续保持 server-first 页面壳与安全 fallback。
- [x] 文档页 subtree 已移除 synthetic projection id,避免前端继续暴露自造 projection 标识。
- [x] 前端全量测试已恢复通过,`AiAgentPanel` 过期断言已更新为当前稳定语义。
- [x] 本批相关 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. 最终固定口径
截至 2026-04-22 仓库状态,本文曾固定为:
> **mnote 的长期路线不是拆掉 Convex,而是在 Convex 继续作为底层 substrate 的前提下,让 Rust 逐步拿回 tree-first graph 的 query、command、projection 与 realtime 语义主导权。**
截至 2026-05-18,当前新增能力的上位口径已调整为:
> **mnote 的早期产品路线是 local-first workspace:本地文件夹是默认数据真相,Rust kernel 是唯一语义真相,Convex / 服务端降级为账号、分享、同步、协作和 AI 隔离控制面。**
当前已经完成的是:
> **主路径边界已基本纠正,tree shell 已降级为显式实验增强,Sidebar 与第一批 tree command 已进入 Convex substrate + Rust envelope 主链。**
未来还需要完成的是:
> **让树规则真正从前端退出,并让 Rust Web 的正式 realtime transport 接住统一主链。**