# [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` 这个单仓里启动和保存。
