# [recycle] mnote 单仓收口 Rust 内核回迁计划 > 更新时间:2026-04-14 > > 当前状态:已完成第一批 P0 crate 与核心设计文档的真实复制,复制目标目录为 `/mnt/Data1T/mnote/rust/`。 ## 1. 结论 基于当前代码现状、`/mnt/Data1T/mnote-rust/design/` 的长期原则,以及你现在明确提出的要求: - 不希望长期维护两个仓库 - 不希望跨仓链接 - 最终只希望在一个文件夹里启动和保存 当前最合适的路线已经明确并开始落地: 1. **停止把 `/mnt/Data1T/mnote-rust/` 继续当作未来主产品仓推进** 2. **把 `mnote-rust` 的必要 Rust 内核内容真实复制进 `/mnt/Data1T/mnote/`** 3. **最终只保留 `/mnt/Data1T/mnote/` 作为唯一主仓** 4. **`/mnt/Data1T/mnote/wolai-frontend/` 继续承担主产品前端壳** 5. **Rust 内核在 `/mnt/Data1T/mnote/rust/` 下统一收口** 一句话版: > **不是双仓回接,而是单仓收口:把必要 Rust 内核复制进 `mnote`,最终只在 `/mnt/Data1T/mnote` 启动与保存。** --- ## 2. 为什么要改成单仓复制,而不是继续双仓 ## 2.1 先做 Rust 内核本身没有错 `/mnt/Data1T/mnote-rust/design/blueprint/ai-native-note-architecture-blueprint-v0.md` 与 `/mnt/Data1T/mnote-rust/design/phases/phase5/packaging-and-shell-strategy-v0.md` 已明确长期原则: 1. Convex 仍是主事实层 2. Rust 负责统一协议、命令、查询、事件、索引、adapter 3. 前端主壳继续复用成熟 React/Next 也就是说: - “先写 Rust 内核”是对的 - “不重写一套新前端主栈”也是对的 真正要调整的是载体: - 不再把 `mnote-rust` 作为第二个长期主仓 - 不再维持“一个仓写内核,一个仓跑产品前端”的长期分裂状态 ## 2.2 双仓会持续制造启动、保存、认知和迁移成本 如果继续保留: - `/mnt/Data1T/mnote/` - `/mnt/Data1T/mnote-rust/` 长期并行,会持续出现四类成本: 1. **启动成本** - 需要判断到底从哪个目录启动 - 脚本、环境变量、依赖路径容易双份化 2. **保存口径成本** - 用户会天然希望“最终只有一个真实主仓” - 双仓天然让“哪个仓是现在的真主线”不断摇摆 3. **迁移成本** - 每做一项功能,都要先判断该落在 `mnote` 还是 `mnote-rust` - 这会导致团队持续在仓库边界上损耗 4. **认知成本** - 新协作者很难快速判断: - 哪个仓是产品主线 - 哪个仓只是实验壳 - 哪些文档是真约束 ## 2.3 当前真正成熟的是 `mnote` 前端,而不是 `mnote-rust` 前端 当前成熟可直接承载产品面的能力主要在: - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/` - `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/` - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/` 而 `mnote-rust` 当前前端更多是: 1. 联调壳 2. smoke 壳 3. 协议验证壳 4. 部分对象页桥接壳 因此,单仓收口时,正确的继承关系应该是: - **保留 `mnote` 前端主壳** - **复制 `mnote-rust` 的内核与协议成果** 而不是反过来复制一整套 `mnote-rust` 的 Next 前端壳。 --- ## 3. 新目标架构 单仓收口后的目标结构应是: ```text /mnt/Data1T/mnote/ wolai-frontend/ # 唯一主前端 wolai-backend/ # 现有辅助后端 infra/convex/ # 现有 Convex 基础设施 src/components/onlyoffice/ scripts/ # 仓库级统一启动入口 design/ # 全局设计与总览 rust/ # 新增:Rust 内核统一收口目录 ``` 连接关系应变成: ```text mnote 前端 -> mnote 内部 API / BFF / bridge -> rust/ 内核协议层 -> Convex 主事实层 + Rust 事件/索引层 ``` 这意味着: 1. 前端只认 `/mnt/Data1T/mnote` 2. Rust 也只存在于 `/mnt/Data1T/mnote/rust` 3. 启动、构建、联调都只从 `/mnt/Data1T/mnote` 发起 --- ## 4. 单仓目录落位方案 推荐把 Rust 相关内容整体收进: - `/mnt/Data1T/mnote/rust/` 而不要把 `crates/`、`design/`、`scripts/` 直接摊到仓库根。 目标结构: ```text /mnt/Data1T/mnote/ wolai-frontend/ wolai-backend/ infra/convex/ src/components/onlyoffice/ scripts/ design/ rust/ Cargo.toml Cargo.lock crates/ core-domain/ core-protocol/ event-log/ storage-convex-bridge/ index-fts/ bridge-runtime/ # Phase 1 最小真实执行器 mnote-cli/ # Phase 2 已落位,先冻结 --json 协议 adapter-onlyoffice/ # 后置按需引入 adapter-mindmap/ # 后置按需引入 adapter-legacy-mnote/ # 后置按需引入 bridge/ design/ INDEX.md core/ blueprint/ phases/ scripts/ fixtures/ tests/ ``` ### 4.1 `rust/` 下各目录职责 - `rust/crates/` 放所有 Rust workspace 成员,保持 Cargo workspace 语义集中。 - `rust/bridge/` 放 Rust 侧桥接层、协议适配、生成代码、FFI/IPC glue。 - `rust/design/` 放 Rust 专属设计、阶段文档、路线图。 - `rust/scripts/` 放 Rust 专属构建、测试、检查、保存脚本。 - `rust/fixtures/` / `rust/tests/` 放 Rust 侧测试资源与测试代码。 ### 4.2 根目录哪些保持不动 以下现有目录建议明确保持不动: - `/mnt/Data1T/mnote/wolai-frontend/` - `/mnt/Data1T/mnote/wolai-backend/` - `/mnt/Data1T/mnote/infra/convex/` - `/mnt/Data1T/mnote/src/components/onlyoffice/` - `/mnt/Data1T/mnote/recycle/` - `/mnt/Data1T/mnote/scripts/` - `/mnt/Data1T/mnote/design/` 其中: - `design/` 继续承担全局总览和跨系统路线说明 - Rust 的详细阶段文档放进 `rust/design/` --- ## 5. 启动与保存口径必须怎么统一 ## 5.1 启动口径 统一口径只有一个: > 所有开发、构建、联调命令都从 `/mnt/Data1T/mnote` 发起。 执行方式: 1. 仓库级脚本统一保留在 `/mnt/Data1T/mnote/scripts/` 2. Rust 命令只作为子任务存在,例如: - `cargo --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml ...` 3. 不再保留“先进 `mnote-rust` 再进 `mnote`”的双仓启动方式 ## 5.2 保存口径 统一口径只有一个: > 页面、块、文档、任务、索引的最终保存都只认 Convex 主事实层。 这意味着: 1. 人工操作、CLI、Agent 的写入都应先进入 Rust 统一命令层,再落到 Convex 2. Rust 的 `event-log`、`index-fts`、fixtures、本地缓存都只是派生层 3. 文件系统只保存: - 静态资源 - 附件 - 导出物 - 缓存 - 测试数据 4. 文件系统不承载主业务真相 --- ## 5.3 当前已落地的首批复制结果 截至 2026-04-13,以下内容已经真实复制进 `/mnt/Data1T/mnote/rust/`: ### 已复制的 workspace 根文件 - `/mnt/Data1T/mnote/rust/Cargo.toml` - `/mnt/Data1T/mnote/rust/Cargo.lock` ### 已复制的 P0 crate - `/mnt/Data1T/mnote/rust/crates/core-domain/` - `/mnt/Data1T/mnote/rust/crates/core-protocol/` - `/mnt/Data1T/mnote/rust/crates/event-log/` - `/mnt/Data1T/mnote/rust/crates/storage-convex-bridge/` - `/mnt/Data1T/mnote/rust/crates/index-fts/` ### 已复制的核心设计文档 - `/mnt/Data1T/mnote/rust/design/INDEX.md` - `/mnt/Data1T/mnote/rust/design/core/01-domain-model-v0.md` - `/mnt/Data1T/mnote/rust/design/core/02-command-query-tool-protocol-v0.md` - `/mnt/Data1T/mnote/rust/design/core/03-storage-event-indexing-v0.md` - `/mnt/Data1T/mnote/rust/design/core/04-onlyoffice-integration-boundary-v0.md` 这一状态意味着: 1. `mnote` 主仓里已经存在可继续扩展的 Rust workspace 根 2. P0 内核定义已经不再只存在于 `/mnt/Data1T/mnote-rust/` 3. 后续回迁工作可以直接以 `/mnt/Data1T/mnote/rust/` 为唯一落位继续推进 --- ## 6. 第一阶段应该真实复制进 `mnote` 的内容 子 agent 的结论一致:第一阶段应复制“定义真相和协议”的内核,而不是复制第二套产品前端。 ## 6.1 必须复制的 crate ### P0:第一阶段必须复制 以下 crate 应真实复制到: - `/mnt/Data1T/mnote/rust/crates/` #### `core-domain` 来源: - `/mnt/Data1T/mnote-rust/crates/core-domain` 复制后职责: 1. 领域模型 2. 一等对象定义 3. ID / revision / 时间 /审计基础类型 #### `core-protocol` 来源: - `/mnt/Data1T/mnote-rust/crates/core-protocol` 复制后职责: 1. `Command / Query / Tool` 统一协议壳 2. actor/source/target/meta 结构 3. `CreatePage / InsertBlock / SearchPages` 等协议模型 #### `event-log` 来源: - `/mnt/Data1T/mnote-rust/crates/event-log` 复制后职责: 1. 命令日志 2. 领域事件 3. 写入后的事件生成规则 #### `storage-convex-bridge` 来源: - `/mnt/Data1T/mnote-rust/crates/storage-convex-bridge` 复制后职责: 1. 协议到 Convex 的桥接 2. 命令执行与回写 3. 命令日志和事件落账 #### `index-fts` 来源: - `/mnt/Data1T/mnote-rust/crates/index-fts` 复制后职责: 1. 可重建的索引与搜索层 2. 事件投影 3. page/block 搜索 ### P1:建议第二阶段复制 #### `mnote-cli` 来源: - `/mnt/Data1T/mnote-rust/crates/mnote-cli` 复制后职责: 1. 批处理入口 2. 导入导出入口 3. 内核 smoke 与排障入口 ### P2:按需后置复制 #### `adapter-onlyoffice` 来源: - `/mnt/Data1T/mnote-rust/crates/adapter-onlyoffice` 复制后职责: 1. OnlyOffice 资产定位 2. 会话定位 3. 任务边界适配 #### `adapter-mindmap` 来源: - `/mnt/Data1T/mnote-rust/crates/adapter-mindmap` 复制后职责: 1. 思维导图结构化 ops 2. 节点读取与引用边界 #### `adapter-legacy-mnote` 来源: - `/mnt/Data1T/mnote-rust/crates/adapter-legacy-mnote` 复制后职责: 1. 旧逻辑兼容 2. 迁移期桥接 ## 6.2 必须复制的设计资产 建议同步复制到: - `/mnt/Data1T/mnote/rust/design/` ### 必复制 - `/mnt/Data1T/mnote-rust/design/INDEX.md` - `/mnt/Data1T/mnote-rust/design/core/01-domain-model-v0.md` - `/mnt/Data1T/mnote-rust/design/core/02-command-query-tool-protocol-v0.md` - `/mnt/Data1T/mnote-rust/design/core/03-storage-event-indexing-v0.md` - `/mnt/Data1T/mnote-rust/design/core/04-onlyoffice-integration-boundary-v0.md` 原因: 1. 这些是长期稳定规范 2. 直接对应 `core-domain / core-protocol / event-log / storage-convex-bridge / index-fts / adapter-onlyoffice` ### 暂不建议第一阶段复制的设计资产 以下内容先留在 `mnote-rust` 作为参考,不作为第一阶段主迁移目标: - `design/blueprint/**` - `design/phases/**` - `design/execution/**` - `design/UI/**` 原因: 1. 这些更多是阶段叙事与历史推进资料 2. 不是必须进入主仓的长期规范 --- ## 7. 第一阶段明确不应复制的内容 这里要明确“禁止误复制”的范围。 ## 7.1 不应继续作为第一阶段复制目标的前端内容 ### 不复制为主线 - `/mnt/Data1T/mnote-rust/app/documents/[id]/mindmap/page.tsx` - `/mnt/Data1T/mnote-rust/app/documents/[id]/office/page.tsx` - `/mnt/Data1T/mnote-rust/components/sidebar/sidebar.tsx` - `/mnt/Data1T/mnote-rust/app/page.tsx` - `/mnt/Data1T/mnote-rust/app/onlyoffice/page.tsx` 原因: 1. 它们大多属于对象页壳、诊断页、smoke 页或单文件硬拼壳 2. 不应再形成第二套产品前端主线 ## 7.2 只能保留为参考 / smoke / 实验的内容 以下内容可以保留在 `mnote-rust` 参考,但不作为第一阶段复制目标: 1. `app/documents/[id]/mindmap/page.tsx` 里的 diagnostics 区 2. `app/documents/[id]/office/page.tsx` 里的 diagnostics 区 3. `app/onlyoffice/OnlyOfficeClientPage.tsx` 里的环境兼容 hack 4. `app/page.tsx` 的 smoke 首页文案 5. `phase7` 中已经标注为历史错误方向的文档 ## 7.3 可复制的少量薄桥接代码 以下内容可以作为后续参考性复制对象,但仍不是第一阶段主目标: - `components/editor/document-shell.tsx` - `components/editor/document-content.tsx` - `components/editor/blocknote-editor.tsx` - `hooks/use-convex-sidebar-data.ts` - `lib/sidebar-tree.ts` - `lib/file-tree/rows.ts` - `lib/document-embeds.ts` - `app/layout.tsx` 原因: 1. 这些属于宿主桥接、投影层或 provider 接线 2. 不是完整产品前端壳 --- ## 8. 单仓路线下,`mnote` 里最先该接入的 5 条能力链 这 5 条仍然是最值得优先打通的主线。 ## 8.1 文档正文命令链 接入目标: - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/save/route.ts` 目标: 1. 保留现有成熟 BlockNote 前端 2. 把正文保存逐步切到 Rust command 层 3. 让 block 操作、正文快照、stats 逐步进入统一协议 ## 8.2 页面元信息与页面属性链 接入目标: - `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx` 目标: 1. 标题更新 2. 页面选项 3. 文档统计 4. 页面属性变更 ## 8.3 Sidebar 数据聚合链 接入目标: - `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/private-tree.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/file-tree.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/hooks/use-convex-sidebar-data.ts` 目标: 1. 保留现有 UI 2. 逐步让数据聚合层走 Rust query / index 层 ## 8.4 Mindmap 数据链 接入目标: - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx` - `/mnt/Data1T/mnote/wolai-frontend/src/lib/mindmap/mindmapLocalStore.ts` 目标: 1. 保留现有成熟思维导图前端 2. 逐步把数据读写、ops 和边界纳入 Rust adapter / protocol ## 8.5 OnlyOffice 边界链 接入目标: - `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/` - `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/` - `/mnt/Data1T/mnote/src/components/onlyoffice/` 目标: 1. 保留现有 OnlyOffice 页面和静态资源 2. 把对象解析、回调、签名和桥接逐步回到 Rust adapter 统一边界 当前补记: 1. `OnlyOffice callback` 已从 route 里直接调用 `api.mediaAssets.replaceStorageFromUpload`,推进到最小 `media.assets.replace_storage` bridge 命令边界。 2. 当前主仓实现会先在 `wolai-frontend/src/app/api/onlyoffice/callback/route.ts` 下载 ONLYOFFICE 输出文件、上传到 Convex Files 获得 `storageId`,再通过 `buildDocumentBridgeContextWithActor`、`buildDocumentCommandEnvelope` 与 `executeMediaAssetWritebackBridgeCommand` 构造和执行与 `storage-convex-bridge` 对齐的运行时 request。 3. Rust 侧已补 `core-protocol::ReplaceMediaAssetStorage` 与 `storage-convex-bridge` 的 `media.assets.replace_storage -> mediaAssets:replaceStorageFromUpload` 映射;因此 OnlyOffice 附件写回现已进入与页面元信息、正文保存一致的协议接缝,只是最终执行器仍在 TypeScript 侧落到 Convex mutation。 --- ## 9. 推荐实施顺序 ## Phase A:先把 Rust 内核真实复制进 `mnote` 第一阶段执行动作: 1. 在 `/mnt/Data1T/mnote/` 下建立 `/rust/` 2. 复制 `P0` crate 3. 复制 `design/core` 四份长期文档和 `design/INDEX.md` 4. 建立新的 `rust/Cargo.toml` 与 `rust/Cargo.lock` 5. 不动 `mnote` 主前端壳 6. 不复制 `mnote-rust` 的第二套产品前端壳 当前状态: - 以上动作已经完成 - `bridge/` 已初始化最小说明目录,但 `scripts/`、`fixtures/`、`tests/` 仍未初始化 - `mnote-cli` 已并入当前 workspace,`adapter-*` 仍未并入 ## Phase B:在 `mnote` 中建立 Rust bridge 接口 执行动作: 1. 在 `mnote` 现有 API/BFF 层加 Rust command/query 入口 2. 保留当前 UI,不先大改组件 3. 先打通低风险调用链 当前建议的第一批 bridge 入口落位: 1. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/content/route.ts` - 作为优先读入口,后续先接 `storage-convex-bridge` 的 `read_path` 2. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/save/route.ts` - 作为优先写入口,后续先接 `write_path` 3. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/title/route.ts` - 作为页面标题更新的低风险写入口 4. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/stats/route.ts` - 作为页面统计的低风险入口 5. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts` - 作为 Sidebar 聚合读入口,后续切 Rust query / index 6. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/blocks/patch/route.ts` - 作为块级局部写入口,适合正文链路第二步接入 当前判断: 1. `storage-convex-bridge` crate 内已经具备 `context / validation / read_path / write_path / mapping` 的桥接骨架 2. `mnote` 主仓已为 `documents.title.update`、`documents.stats.update`、`documents.options.update`、`documents.save` 补上与 `storage-convex-bridge::build_write_request` 对齐的最小运行时接缝,会先生成 `functionName/payloadJson/args` 形式的 bridge mutation request,再交给统一执行器落到 Convex 3. 但主仓 API route 仍未直接调用 Rust crate,`documents.content`、`blocks.patch` 等链路也还没有切到同一层执行器,因此 Phase B 还不能视为完全关闭 ## Phase C:先接低风险元信息,再接正文链 顺序建议: 1. 页面标题 / 页面选项 2. Sidebar 数据聚合 3. BlockNote 正文保存 ## Phase D:再接 Mindmap 与 OnlyOffice 原则: 1. 前端仍然在 `mnote` 2. 数据、ops、边界逐步切到 Rust adapter / protocol ## Phase E:让 `mnote-rust` 退出主产品角色 处理方式: 1. `mnote-rust` 可保留为历史参考或过渡仓 2. 但不再承担短期产品主线 3. 最终只保留 `/mnt/Data1T/mnote/` 作为可启动、可保存主仓 4. 历史资料分类与主仓维护边界统一以 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-single-repo-maintenance-boundary.md` 为准 --- ## 10. 最不该做的事 1. 不要继续把 `mnote-rust` 的 Next 前端补成完整产品前端 2. 不要让 `mnote` 和 `mnote-rust` 长期各维护一套产品前端 3. 不要跨仓链接代码来“假装已迁移” 4. 不要把对象页壳、诊断页、smoke 页当作主产品实现复制进来 5. 不要把 Rust 内容散落复制到 `mnote` 根目录多个平行源码根 --- ## 11. 一句话路线判断 最优路线不是: > 继续在 `mnote-rust` 里补前端,再想办法替换 `mnote` 而是: > 把 `mnote-rust` 的必要 Rust 内核真实复制进 `/mnt/Data1T/mnote/rust/`,保留 `mnote` 现有成熟前端作为唯一主产品壳,最终只在 `/mnt/Data1T/mnote` 这个单仓里启动和保存。