feat: cut over rust web main shell
This commit is contained in:
@@ -0,0 +1,443 @@
|
||||
# 3-4 [done] mnote-web `3104` 边界收口与退役方案 v1
|
||||
|
||||
> 更新时间:2026-04-23
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.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-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档只回答一个已经进入架构层的问题:
|
||||
|
||||
> **当前 `127.0.0.1:3104` 还有没有存在意义,是否应从系统主链中移除。**
|
||||
|
||||
先给结论:
|
||||
|
||||
> **`3104` 不应该继续作为用户、浏览器、前端 runtime 可感知的端口存在。**
|
||||
>
|
||||
> **在当前代码基线上,它已经不再是默认开发链、前端主链或 AI 主链的必需端口;`mnote-web` 若仍启动,也应退回为显式 debug/internal runtime。**
|
||||
|
||||
因此当前正确目标不是:
|
||||
|
||||
- 继续接受多端口长期并存
|
||||
- 或者为了“看起来只剩 3000”而直接硬删 `3104`
|
||||
|
||||
而是:
|
||||
|
||||
> **先把 `3104` 收口为 internal-only 边界,再按依赖拆解顺序逐步退役,最终让前端公开主链只剩 `3000`。**
|
||||
|
||||
## 1.1 完成结论
|
||||
|
||||
截至 2026-04-23,这份方案针对“`3104` 作为默认/公开边界”的收口已经完成,当前可固定为:
|
||||
|
||||
- `3000` 是唯一前端公开入口
|
||||
- 正式主链已移除 `MNOTE_WEB_BASE_URL`、浏览器侧 `mnoteWebBaseUrl / mnoteWebTreeShellEnabled`
|
||||
- `mnote-web` 默认监听地址已改为 `127.0.0.1:0`,不再默认绑定 `3104`
|
||||
- `/tree`、`/document-debug` 等 debug 壳仅在显式环境变量下注册
|
||||
- legacy smoke 只有在显式设置 `MNOTE_WEB_SMOKE_BASE_URL` 时才会命中 debug runtime
|
||||
|
||||
需要额外说明的是:
|
||||
|
||||
> **若本机仍看到 `127.0.0.1:3104` 监听,应视为旧 `mnote-web` 进程残留,不代表当前默认启动链。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前真实状态
|
||||
|
||||
### 2.1 `3104` 当前是什么
|
||||
|
||||
`3104` 曾对应 Rust `mnote-web` 的默认监听地址,而不是首页入口端口。
|
||||
|
||||
当前 `mnote-web` crate 仍承载的能力包括:
|
||||
|
||||
- `/health`
|
||||
- `/tree`
|
||||
- `/document-debug`
|
||||
- `/api/tree/commands`
|
||||
- `/api/stream/events`
|
||||
- `/api/hermes/bridge`
|
||||
- `/api/compat/next/sidebar`
|
||||
|
||||
这说明它不是单纯“没人用的历史残留壳”,而是:
|
||||
|
||||
> **一个仍保留 debug / compat / internal transport 能力的 Rust Web 组件。**
|
||||
|
||||
### 2.2 当前真正的问题不在“它存在”,而在“它泄漏”
|
||||
|
||||
当前最不应该发生的事情,不是 `mnote-web` 作为内部服务存在,而是:
|
||||
|
||||
- 浏览器 runtime 直接知道 `mnoteWebBaseUrl`
|
||||
- 前端页面逻辑直接拼 `3104`
|
||||
- smoke / 调试脚本把 `3104` 当成正式主入口前提
|
||||
- 新功能继续优先接到 `mnote-web compat` 路由
|
||||
|
||||
换句话说,问题的关键不是:
|
||||
|
||||
> “有没有本机内部回源端口”
|
||||
|
||||
而是:
|
||||
|
||||
> “这个内部回源边界是否继续污染前端主链与长期架构”。
|
||||
|
||||
### 2.3 当前已经完成的一次收口
|
||||
|
||||
当前已经确认完成的收口包括:
|
||||
|
||||
- 浏览器公开 runtime 不再暴露 `mnoteWebBaseUrl`
|
||||
- Sidebar tree stream 浏览器侧统一走同源 `3000/api/mnote-web/stream`
|
||||
- `public/mnote-env.json` 不再公开 `mnoteWebBaseUrl`
|
||||
- AI orchestrator 主链已不再依赖 `MNOTE_WEB_BASE_URL`
|
||||
- 默认开发链不再启动或要求 `mnote-web:3104`
|
||||
- legacy smoke 不再默认指向 `3104`,必须显式传入 debug runtime 地址
|
||||
- `mnote-web` 默认监听地址已不再固定为 `127.0.0.1:3104`
|
||||
|
||||
因此现在的架构判断应改写为:
|
||||
|
||||
> **`3000` 已经是前端唯一公开主入口,而 `3104` 已从默认边界退回到显式 debug/internal-only。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 为什么现在还不能直接删除 `3104`
|
||||
|
||||
当前已经不再存在正式产品链必须依赖 `3104` 的情况,但仍保留三类遗留能力面需要继续降级:
|
||||
|
||||
### 3.1 Tree realtime contract 仍未完全对齐最终主链协议
|
||||
|
||||
当前 `3000/api/mnote-web/stream` 已不再回源 `mnote-web /api/stream/events`,但它仍是一个 Next route 级的过渡 transport,而不是最终的统一 realtime 主链。
|
||||
|
||||
因此当前现实不是:
|
||||
|
||||
- `3000` 已经原生持有 tree realtime transport
|
||||
|
||||
而是:
|
||||
|
||||
- `3000` 已完成对 `3104` 的切断
|
||||
- 正式 realtime contract 仍需继续向 `snapshot / delta / resync` 单一主链收口
|
||||
|
||||
### 3.2 Hermes / AI tool registry 归属仍需继续收口
|
||||
|
||||
当前前端 AI route 与后端 `openai-agents-python` 编排层已经不再通过 `MNOTE_WEB_BASE_URL` 回源 `mnote-web /api/hermes/bridge` 获取 runtime tool 结果,而是走后端 orchestration + 本地 `bridge-runtime`。
|
||||
|
||||
这意味着:
|
||||
|
||||
- `3104` 已不再阻塞 AI 主链
|
||||
- 但 AI tool registry / tool result contract 的正式归属还需继续明确
|
||||
|
||||
### 3.3 仍保留实验 / 调试 / compat 壳
|
||||
|
||||
以下链路仍带有明显过渡期性质:
|
||||
|
||||
- `/tree`
|
||||
- `/document-debug`
|
||||
- `compat/next/sidebar`
|
||||
- 旧 smoke 脚本仍可在显式传入 runtime 地址后命中 debug runtime
|
||||
- `/api/auth/mnote-web-token` 仍保留给 legacy debug/runtime smoke
|
||||
|
||||
这些路径已不在当前主路径上,但说明:
|
||||
|
||||
> **`mnote-web` 仍处于“内核能力 + compat + 调试壳”混杂状态。**
|
||||
|
||||
在这种状态下直接删掉全部 debug/runtime 入口,会丢失排障和对照手段;因此当前应做的是“默认退役 + 显式 debug 化”,而不是直接物理删除所有相关代码。
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前必须立即固定的架构口径
|
||||
|
||||
### 4.1 对用户与浏览器的公开口径
|
||||
|
||||
当前必须固定为:
|
||||
|
||||
- 页面访问入口只有 `3000`
|
||||
- 浏览器不得显式依赖 `3104`
|
||||
- 前端公开 runtime / public config 不得再暴露 `mnoteWebBaseUrl`
|
||||
|
||||
### 4.2 对系统内部的口径
|
||||
|
||||
当前必须固定为:
|
||||
|
||||
- `3104` 只允许作为 loopback internal service 存在
|
||||
- 它不是产品入口
|
||||
- 它不是长期对外 contract
|
||||
- 它只是 Rust Web transport 尚未并入最终主链前的内部边界
|
||||
|
||||
### 4.3 对未来收口方向的口径
|
||||
|
||||
当前必须固定为:
|
||||
|
||||
> **长期目标不是保留 `3000 + 3104` 双长期入口,而是把 `3104` 承担的正式能力迁走,最终退役 `3104`。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 需要立即回退或禁止继续扩散的方向
|
||||
|
||||
下面这些方向应该立即视为错误方向:
|
||||
|
||||
### 5.1 禁止浏览器继续读取 `mnoteWebBaseUrl`
|
||||
|
||||
包括但不限于:
|
||||
|
||||
- 浏览器组件中直接构造 `http://127.0.0.1:3104`
|
||||
- 通过 runtime config 让前端知道 `mnote-web` 基址
|
||||
- 新页面逻辑绕过同源 `3000`,直接访问 `3104`
|
||||
|
||||
### 5.2 禁止把新长期业务继续堆进 `compat`
|
||||
|
||||
尤其不能继续把以下长期语义放进 `mnote-web compat`:
|
||||
|
||||
- 正式 sidebar 数据 contract
|
||||
- 正式 page aggregate contract
|
||||
- 正式 tree realtime contract
|
||||
- 正式 AI tool orchestration contract
|
||||
|
||||
`compat` 只能继续承担:
|
||||
|
||||
- 过渡转接
|
||||
- 旧链路对照
|
||||
- fixture / smoke 支持
|
||||
|
||||
不能继续成为新增长期业务的落点。
|
||||
|
||||
### 5.3 禁止把 `/tree` 与 `/document-debug` 当成正式页面主链
|
||||
|
||||
它们可以保留为:
|
||||
|
||||
- 实验壳
|
||||
- 调试入口
|
||||
- 迁移对照入口
|
||||
|
||||
但不能继续被当成:
|
||||
|
||||
- 首页入口前置条件
|
||||
- 正式文档页 transport
|
||||
- 页面内主编辑区 runtime 壳
|
||||
|
||||
### 5.4 禁止新 smoke 把 `3104` 当产品主链前提
|
||||
|
||||
后续 smoke 要区分两类:
|
||||
|
||||
- `3000` 主站 smoke
|
||||
- `mnote-web` 内部服务 smoke
|
||||
|
||||
不能再写成:
|
||||
|
||||
> “只要功能涉及 Rust,就默认直接打 `3104`”
|
||||
|
||||
---
|
||||
|
||||
## 6. `3104` 退役的阶段方案
|
||||
|
||||
## 6.1 Phase 0:公开边界收口
|
||||
|
||||
目标:
|
||||
|
||||
- 前端主链只公开 `3000`
|
||||
- 浏览器不再感知 `3104`
|
||||
- `3104` 收回 internal-only
|
||||
|
||||
当前状态:
|
||||
|
||||
- [x] 已完成
|
||||
|
||||
完成定义:
|
||||
|
||||
- [x] 浏览器公开 runtime 不再暴露 `mnoteWebBaseUrl`
|
||||
- [x] Sidebar realtime 浏览器侧走同源 `3000/api/mnote-web/stream`
|
||||
- [x] `public/mnote-env.json` 不再公开 `3104`
|
||||
- [x] 浏览器主链已不再消费 `mnoteWebBaseUrl / mnoteWebTreeShellEnabled`
|
||||
- [x] `public/mnote-env.json` 已去除 `mnoteWebTreeShellEnabled`
|
||||
|
||||
说明:
|
||||
|
||||
> 这一步解决的是“前端被多端口污染”的问题,不等于 `3104` 已退役。
|
||||
|
||||
## 6.2 Phase 1:把正式 sidebar/query 能力移出 `compat`
|
||||
|
||||
目标:
|
||||
|
||||
- `sidebar` 正式查询链不再依赖 `mnote-web /api/compat/next/sidebar`
|
||||
- `3000` 直接消费 Rust bridge / kernel projection 正式 contract
|
||||
|
||||
当前状态:
|
||||
|
||||
- [x] 已完成
|
||||
|
||||
已完成:
|
||||
|
||||
- [x] `/api/sidebar` 已优先走 Rust bridge query,而不是继续扩 `compat sidebar`
|
||||
- [x] 正式前端链路已不再调用 `fetchSidebarDatasetFromMnoteWeb`
|
||||
|
||||
已完成补充:
|
||||
|
||||
- [x] `compat/next/sidebar` 已明确降级为过渡 / 调试用途
|
||||
- [x] 依赖 `compat sidebar` 的旧链路已收口为 legacy debug smoke,且必须显式传入 `MNOTE_WEB_SMOKE_BASE_URL`
|
||||
|
||||
## 6.3 Phase 2:tree realtime transport 正式并入主链
|
||||
|
||||
目标:
|
||||
|
||||
- `3000` 或其背后的正式服务直接承载 tree realtime contract
|
||||
- 不再通过 Next route 反代 `3104 /api/stream/events`
|
||||
|
||||
当前状态:
|
||||
|
||||
- [x] 已完成
|
||||
|
||||
完成定义:
|
||||
|
||||
- [x] `3000` 的 tree stream 不再依赖 `MNOTE_WEB_BASE_URL`
|
||||
- [ ] realtime stream contract 与 `3-3` 文档统一
|
||||
- [ ] snapshot / delta / resync 成为正式主链,而不是代理式过渡链
|
||||
- [x] `mnote-web /api/stream/events` 不再是前端主站 realtime 前置
|
||||
|
||||
备注:
|
||||
|
||||
> 这是 `3104` 能否退役的第一硬前提。只要 realtime stream 还在内部回源 `3104`,它就不能删除。
|
||||
|
||||
## 6.4 Phase 3:Hermes runtime bridge 从 `mnote-web` 迁出
|
||||
|
||||
目标:
|
||||
|
||||
- AI runtime tool bridge 不再强依赖 `MNOTE_WEB_BASE_URL`
|
||||
- `openai-agents-python` 编排层与工具桥接边界收口到正式 AI 服务面
|
||||
|
||||
推荐方向:
|
||||
|
||||
- 由 `wolai-backend:8000` 持有 AI orchestration 正式入口
|
||||
- Rust `bridge-runtime` 作为内核语义执行层
|
||||
- `3000` 只负责页面与同源 API 边界,不再额外绕回 `3104`
|
||||
|
||||
当前状态:
|
||||
|
||||
- [x] 已完成
|
||||
|
||||
完成定义:
|
||||
|
||||
- [x] 前端 AI run route 不再通过 `mnote-web /api/hermes/bridge` 获取 tool 结果
|
||||
- [x] 后端 `ai_document_agent.py` 不再把 `MNOTE_WEB_BASE_URL` 作为正式硬依赖
|
||||
- [ ] AI tool registry / tool result contract 有独立正式归属
|
||||
|
||||
备注:
|
||||
|
||||
> 这是 `3104` 能否退役的第二硬前提。只要 AI tool bridge 还依赖它,它就不是可删除端口。
|
||||
|
||||
## 6.5 Phase 4:实验壳与调试入口降级
|
||||
|
||||
目标:
|
||||
|
||||
- `/tree`
|
||||
- `/document-debug`
|
||||
- 旧 `tree shell`
|
||||
- 与 `3104` 直连的实验 host
|
||||
|
||||
全部明确降级为:
|
||||
|
||||
- debug only
|
||||
- spike only
|
||||
- legacy fallback only
|
||||
|
||||
当前状态:
|
||||
|
||||
- [~] 部分完成
|
||||
|
||||
完成定义:
|
||||
|
||||
- [x] 正式产品链不再依赖 `/tree`
|
||||
- [x] 正式文档页不再依赖 `/document-debug`
|
||||
- [x] 仍保留的实验组件不再混入默认主链
|
||||
- [x] legacy smoke 已改为显式传入 `MNOTE_WEB_SMOKE_BASE_URL` 才会触发 debug runtime
|
||||
- [x] `/tree` 与 `/document-debug` 已改成仅在显式 debug 开关下注册
|
||||
|
||||
已完成:
|
||||
|
||||
- [x] 浏览器侧 runtime config 不再兜底暴露 `mnoteWebBaseUrl`
|
||||
- [x] `leptos_tiptap_iframe_debug` 不再通过 `mnoteWebBaseUrl` 推导调试 runtime 地址
|
||||
- [x] `MnoteWebTreeShell` 已从前端正式代码路径移除
|
||||
- [x] `mnote-web` 默认已不再注册 `/tree`、`/document`、`/document-debug`
|
||||
|
||||
## 6.6 Phase 5:正式退役 `3104`
|
||||
|
||||
当前状态:
|
||||
|
||||
- [x] 已完成
|
||||
|
||||
前提必须同时满足:
|
||||
|
||||
- [x] Sidebar / tree query 正式主链不再依赖 `compat sidebar`
|
||||
- [x] tree realtime transport 已从 `3104` 迁出
|
||||
- [x] Hermes runtime bridge 已从 `3104` 迁出
|
||||
- [x] `/tree` 与 `/document-debug` 已降为非正式必需
|
||||
- [x] 开发启动链不再要求单独拉起 `mnote-web:3104`
|
||||
|
||||
完成定义:
|
||||
|
||||
- [x] 默认开发工作流中不再需要 `3104`
|
||||
- [x] `MNOTE_WEB_BASE_URL` 从正式主链配置中删除
|
||||
- [x] `mnote-web` 若仍存在,则只作为显式 debug/internal app 运行,而不是长期内网网关
|
||||
- [x] 运行态旧 `mnote-web:3104` 残留进程已清理;最新二进制默认启动不再监听 `3104`
|
||||
|
||||
说明:
|
||||
|
||||
> **`3-4` 的完成定义是退役 `3104` 作为默认/公开边界,不等于 `mnote-web`、tree realtime contract 或 AI tool registry 的所有后续演进已经结束。**
|
||||
>
|
||||
> **相关后续工作分别继续由 `3-3-rust-web-tree-realtime-event-stream-v1.md` 与 `07-ai` 主线承接,但它们不再构成 `3104` 退役阻塞。**
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐执行顺序
|
||||
|
||||
当前建议顺序固定为:
|
||||
|
||||
1. 完成 `Phase 2` tree realtime transport 收口
|
||||
2. 完成 `Phase 3` Hermes runtime bridge 迁移
|
||||
3. 完成 `Phase 4` 实验 / 调试壳降级
|
||||
4. 最后执行 `Phase 5` 退役 `3104`
|
||||
|
||||
原因很简单:
|
||||
|
||||
- realtime 是前端主链的硬依赖
|
||||
- AI bridge 是在线 AI 的硬依赖
|
||||
- 调试入口最后再降级,避免迁移过程失去观测手段
|
||||
|
||||
---
|
||||
|
||||
## 8. 与 `8123` 的关系
|
||||
|
||||
`8123` 与 `3104` 不是同一类问题。
|
||||
|
||||
`8123` 当前更接近:
|
||||
|
||||
- spike runtime
|
||||
- leptos-tiptap 独立开发端口
|
||||
- 开发期资产构建 / 调试端口
|
||||
|
||||
而 `3104` 当前更接近:
|
||||
|
||||
- `mnote-web` 内部 transport 边界
|
||||
- Rust Web 正式能力尚未完全并入主链前的内网服务
|
||||
|
||||
所以当前不应把两者混为一谈。
|
||||
|
||||
正确顺序是:
|
||||
|
||||
1. 先解决 `3104` 的正式能力迁移与边界退役
|
||||
2. 再继续压缩 `8123` 这类 spike / dev runtime 端口
|
||||
|
||||
---
|
||||
|
||||
## 9. 一句话收口
|
||||
|
||||
当前关于 `3104` 的正确判断不是:
|
||||
|
||||
> “它已经没用了,可以直接删。”
|
||||
|
||||
也不是:
|
||||
|
||||
> “既然浏览器看不到,就可以长期保留。”
|
||||
|
||||
而是:
|
||||
|
||||
> **`3104` 现在仍有内部存在意义,但这种意义是过渡性的。当前必须把它严格收口为 internal-only,并以 tree realtime 与 AI runtime bridge 迁移为前提,最终退役。**
|
||||
@@ -0,0 +1,583 @@
|
||||
# 3-5 [process] Rust Web 主 Web 执行面迁移实施计划 v1
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 让 `mnote-web` / Rust family 成为主 Web 执行面,`3000` 继续作为唯一公开入口,但不再由 Next App Router / React 旧壳承担产品主执行语义。
|
||||
|
||||
**Architecture:** 采用“Rust Web gateway + server-first Rust shell + islands + legacy Next compat”分层。`mnote-web` 先接管公开入口、runtime config、auth/session、页面 shell、API transport 与 realtime stream;Next App Router 降为内部 compat/legacy adapter,最终只在显式 legacy/debug 开关下启动。
|
||||
|
||||
**Tech Stack:** Rust workspace、`mnote-web`、`axum`、Leptos/WASM islands、Convex substrate、Next App Router legacy compat、Vitest、Playwright smoke、Cargo tests。
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前基线
|
||||
|
||||
树域 `Sidebar / 页面树 / 文件树 / picker` 已完成 Rust family 默认执行面切换,但这不等于主 Web 执行面已经迁移完成。当前仍成立的事实是:
|
||||
|
||||
- `3000` 是唯一公开入口。
|
||||
- `wolai-frontend/src/app` 仍是 Next App Router 主应用入口。
|
||||
- `mnote-web` 已具备 documents、tree、kernel、stream、bridge、debug shell 等 route,但默认不是公开主站进程。
|
||||
- `scripts/desktop-hot.js` 默认启动 `pnpm dev` 作为前端 3000,并启动 FastAPI 8000;没有默认启动 Rust Web gateway。
|
||||
- `3104` 已退役为显式 debug/internal 边界,不能把“恢复 3104 双入口”当作迁移方案。
|
||||
|
||||
因此,本计划的完成判定不是“某个 route 可从 Rust 调用”,而是:
|
||||
|
||||
> **主站公开入口仍是 3000,但 3000 背后的主执行 owner 从 Next App Router 迁到 `mnote-web`。**
|
||||
|
||||
## 2. 边界原则
|
||||
|
||||
- `3000` 保持唯一公开入口,不新增长期公开端口。
|
||||
- `mnote-web` 成为主 gateway、主 SSR shell、主 API transport、主 realtime transport 的 owner。
|
||||
- Next App Router 只能保留为内部 compat,负责尚未迁完的 legacy 页面或 React island bundle,不再新增长期业务语义。
|
||||
- Convex 保留为 storage / realtime substrate,不因为主 Web Rust 化而先拆。
|
||||
- 页面、树、搜索、AI、Mindmap 的事实源继续向 Rust kernel / Page Aggregate / projection contract 收口。
|
||||
- `BlockNote` 保留为 fallback / 对照链,不作为主执行面迁移的阻塞项。
|
||||
|
||||
## 3. 目标文件结构
|
||||
|
||||
### Rust Web
|
||||
|
||||
- `rust/crates/mnote-web/src/app.rs`
|
||||
- 扩展主 gateway 配置、公开入口配置、legacy Next upstream 配置。
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 注册主页面 shell、runtime config、auth/session、API transport、stream route。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- 新建主 Web shell route:`/`、`/auth`、`/documents/:id`、必要的 workspace shell。
|
||||
- `rust/crates/mnote-web/src/routes/session.rs`
|
||||
- 新建 Rust-owned session / runtime config / token handoff route。
|
||||
- `rust/crates/mnote-web/src/routes/gateway.rs`
|
||||
- 新建 legacy Next compat proxy,只代理尚未迁移的路径。
|
||||
- `rust/crates/mnote-web/src/routes/search.rs`
|
||||
- 新建 server-first search shell 和 Rust-owned search transport。
|
||||
- `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
|
||||
- 新建独立对象页 shell,先承接轻壳与 island mount。
|
||||
|
||||
### Frontend Legacy Compat
|
||||
|
||||
- `wolai-frontend/src/lib/rust-web-main-execution-boundary.ts`
|
||||
- 新建主执行面边界 manifest,固定 route owner、legacy owner、forbidden Next semantics。
|
||||
- `wolai-frontend/src/lib/rust-web-main-execution-boundary.test.ts`
|
||||
- 新建负向门禁,阻止把 Next App Router 再写成主执行 owner。
|
||||
- `wolai-frontend/src/app/api/**`
|
||||
- 逐步变成 thin proxy / compat adapter;新增业务语义必须失败。
|
||||
- `wolai-frontend/src/app/(app)/**`
|
||||
- 逐步从主入口降级为 legacy compat shell。
|
||||
- `wolai-frontend/src/components/**`
|
||||
- 可继续提供 islands 和重交互组件,但不能再拥有 page/tree/search/AI 的系统事实源。
|
||||
|
||||
### Scripts / Smoke
|
||||
|
||||
- `scripts/desktop-hot.js`
|
||||
- 改为默认启动 Rust Web gateway 作为 3000 owner,Next dev 只在内部 legacy port 启动。
|
||||
- `scripts/desktop-hot.test.js`
|
||||
- 覆盖 Rust gateway、legacy Next upstream、端口释放、跳过 legacy 的组合。
|
||||
- `scripts/task114-rust-web-gateway-entry-smoke.js`
|
||||
- 新建公开入口 smoke,证明 3000 由 Rust gateway 服务。
|
||||
- `scripts/task115-rust-web-document-shell-smoke.js`
|
||||
- 新建文档页 Rust shell smoke。
|
||||
- `scripts/task116-rust-web-islands-smoke.js`
|
||||
- 新建 search / AI / Mindmap islands smoke。
|
||||
- `scripts/task117-next-retirement-guard.js`
|
||||
- 新建最终退役门禁,证明主 smoke 不依赖 Next dev server。
|
||||
|
||||
### Design
|
||||
|
||||
- `design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md`
|
||||
- 作为本计划的执行 checklist 和 harness task 来源。
|
||||
- `design/03-rust-web/done/*`
|
||||
- 只有当 checklist 全部完成并验证后,才移动完成项。
|
||||
|
||||
## 4. 非目标
|
||||
|
||||
- 不恢复 `3104` 作为公开入口。
|
||||
- 不在本阶段强制删除 Convex。
|
||||
- 不要求一次性重写所有 React 组件;重交互可以作为 island/legacy bundle 保留。
|
||||
- 不把 `BlockNote` 的最终删除作为主 Web 执行面迁移的完成前提。
|
||||
- 不把“Rust kernel 拥有业务语义”误写成“Rust Web 已拥有主 Web 执行面”。
|
||||
|
||||
## 5. 实施任务
|
||||
|
||||
### Task W-0:冻结主执行面 owner manifest 与负向门禁
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `wolai-frontend/src/lib/rust-web-main-execution-boundary.ts`
|
||||
- Create: `wolai-frontend/src/lib/rust-web-main-execution-boundary.test.ts`
|
||||
- Modify: `design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md`
|
||||
- Modify: `ARCHITECTURE.md`
|
||||
|
||||
- [ ] **Step 1: 写主执行面 manifest**
|
||||
|
||||
```ts
|
||||
export const RUST_WEB_MAIN_EXECUTION_BOUNDARY = {
|
||||
schema: "mnote.rust_web.main_execution_boundary.v1",
|
||||
publicEntry: "3000",
|
||||
mainWebOwner: "mnote-web",
|
||||
legacyCompatOwner: "next-app-router",
|
||||
rustOwnedPlanes: [
|
||||
"web_gateway",
|
||||
"runtime_config",
|
||||
"auth_session_handoff",
|
||||
"document_shell",
|
||||
"page_aggregate_transport",
|
||||
"tree_projection_transport",
|
||||
"tree_command_transport",
|
||||
"tree_realtime_stream",
|
||||
"search_shell",
|
||||
"ai_bridge_transport",
|
||||
"mindmap_object_shell",
|
||||
],
|
||||
nextAllowedDuties: [
|
||||
"legacy_react_bundle",
|
||||
"legacy_app_router_compat",
|
||||
"island_asset_source_until_migrated",
|
||||
],
|
||||
forbiddenNextSemantics: [
|
||||
"主 Web gateway owner",
|
||||
"页面事实源",
|
||||
"树排序 canonical plan",
|
||||
"Page Aggregate command family owner",
|
||||
"tree realtime event contract owner",
|
||||
"搜索事实源",
|
||||
"AI tool protocol owner",
|
||||
],
|
||||
} as const;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 写负向测试**
|
||||
|
||||
```ts
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { RUST_WEB_MAIN_EXECUTION_BOUNDARY } from "./rust-web-main-execution-boundary";
|
||||
|
||||
describe("RUST_WEB_MAIN_EXECUTION_BOUNDARY", () => {
|
||||
it("固定 3000 公开入口由 mnote-web 拥有,Next 只能作为 legacy compat", () => {
|
||||
expect(RUST_WEB_MAIN_EXECUTION_BOUNDARY.publicEntry).toBe("3000");
|
||||
expect(RUST_WEB_MAIN_EXECUTION_BOUNDARY.mainWebOwner).toBe("mnote-web");
|
||||
expect(RUST_WEB_MAIN_EXECUTION_BOUNDARY.legacyCompatOwner).toBe("next-app-router");
|
||||
expect(RUST_WEB_MAIN_EXECUTION_BOUNDARY.nextAllowedDuties).not.toContain("main_web_owner");
|
||||
expect(RUST_WEB_MAIN_EXECUTION_BOUNDARY.forbiddenNextSemantics).toContain("主 Web gateway owner");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 更新架构文档口径**
|
||||
|
||||
把 `ARCHITECTURE.md` 的 Rust Web 状态改成:
|
||||
|
||||
> `mnote-web` 已成为主 Web 执行面 owner;Next App Router 降为 legacy compat / island bundle source,`3-5 / 3-6` 收口项已由 task-019..task-026 验证完成。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/rust-web-main-execution-boundary.test.ts src/lib/runtime-config.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
git diff --check -- ARCHITECTURE.md design/03-rust-web wolai-frontend/src/lib
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Vitest 通过。
|
||||
- `git diff --check` 无输出。
|
||||
|
||||
### Task W-1:让 `mnote-web` 默认成为 3000 gateway owner
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `rust/crates/mnote-web/src/app.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- Create: `rust/crates/mnote-web/src/routes/gateway.rs`
|
||||
- Modify: `scripts/desktop-hot.js`
|
||||
- Modify: `scripts/desktop-hot.test.js`
|
||||
- Create: `scripts/task114-rust-web-gateway-entry-smoke.js`
|
||||
|
||||
- [ ] **Step 1: 扩展 Rust gateway 配置**
|
||||
|
||||
新增配置项:
|
||||
|
||||
```rust
|
||||
pub public_bind_addr: String,
|
||||
pub legacy_next_base_url: Option<String>,
|
||||
pub enable_legacy_next_compat: bool,
|
||||
```
|
||||
|
||||
默认值:
|
||||
|
||||
- `MNOTE_WEB_PUBLIC_BIND` 默认 `127.0.0.1:3000`
|
||||
- `MNOTE_WEB_LEGACY_NEXT_BASE_URL` 默认 `http://127.0.0.1:3100`
|
||||
- `MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT` 默认 `true`
|
||||
|
||||
- [ ] **Step 2: 新建 legacy gateway route**
|
||||
|
||||
`gateway.rs` 只允许代理尚未迁移的 legacy 页面和 asset,不允许承接新业务语义。代理响应必须加:
|
||||
|
||||
```text
|
||||
x-mnote-web-owner: mnote-web
|
||||
x-mnote-legacy-upstream: next-app-router
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 修改 `desktop-hot` 启动链**
|
||||
|
||||
默认启动顺序改为:
|
||||
|
||||
1. 释放 `3000 / 3100 / 8000`。
|
||||
2. 启动 Next dev 到 `127.0.0.1:3100`,只作为 legacy upstream。
|
||||
3. 启动 `mnote-web` 到 `127.0.0.1:3000`。
|
||||
4. 启动 FastAPI 8000。
|
||||
|
||||
保留环境变量:
|
||||
|
||||
- `MNOTE_WEB_SKIP_GATEWAY=1`:临时恢复旧 Next 3000,仅用于紧急回退。
|
||||
- `NEXT_LEGACY_PORT=3100`:覆盖 legacy upstream 端口。
|
||||
|
||||
- [ ] **Step 4: 写 smoke**
|
||||
|
||||
`task114` 必须断言:
|
||||
|
||||
- `GET http://127.0.0.1:3000/health` 或等价 gateway health 返回 Rust owner header。
|
||||
- `GET http://127.0.0.1:3000/auth` 可进入。
|
||||
- 主请求响应头含 `x-mnote-web-owner: mnote-web`。
|
||||
- 未显式设置 legacy smoke 时不依赖 `3104`。
|
||||
|
||||
- [ ] **Step 5: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
node --test /mnt/Data1T/mnote/scripts/desktop-hot.test.js
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web gateway
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task114-rust-web-gateway-entry-smoke.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- `desktop-hot` 测试证明默认 3000 owner 是 Rust gateway。
|
||||
- Cargo gateway 测试通过。
|
||||
- Smoke 证明 3000 主入口不再直接依赖 Next dev owner。
|
||||
|
||||
### Task W-2:把 runtime config 与 auth/session handoff 迁到 Rust Web
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `rust/crates/mnote-web/src/routes/session.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- Modify: `wolai-frontend/src/lib/runtime-config.ts`
|
||||
- Modify: `wolai-frontend/src/lib/runtime-config.test.ts`
|
||||
- Modify: `wolai-frontend/src/app/api/auth/mnote-web-token/route.ts`
|
||||
|
||||
- [ ] **Step 1: Rust Web 输出运行时配置**
|
||||
|
||||
新增:
|
||||
|
||||
```text
|
||||
GET /api/runtime/config
|
||||
GET /api/auth/session
|
||||
POST /api/auth/session/refresh
|
||||
```
|
||||
|
||||
响应必须只暴露浏览器安全字段,禁止暴露 `CONVEX_SELF_HOSTED_ADMIN_KEY`、内部 `MNOTE_WEB_*_BASE_URL`、legacy upstream secret。
|
||||
|
||||
- [ ] **Step 2: 前端 runtime config 改为消费 Rust-owned contract**
|
||||
|
||||
浏览器侧继续只读同源 `/api/runtime/config`,不再从 `NEXT_PUBLIC_MNOTE_WEB_BASE_URL` 等 legacy env 兜底恢复 internal-only 边界。
|
||||
|
||||
- [ ] **Step 3: 保留 Next auth route 为 compat**
|
||||
|
||||
`/api/auth/mnote-web-token` 只做兼容转发或测试入口,文档中标记为 legacy debug,不再作为主 session owner。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web session runtime_config
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/runtime-config.test.ts src/app/api/auth/mnote-web-token/route.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Rust Web session/config 测试通过。
|
||||
- 前端 runtime config 负向测试仍证明 internal `mnote-web` URL 不会泄漏回浏览器。
|
||||
|
||||
### Task W-3:建立 Rust-owned 文档页 / Page Aggregate server-first shell
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `rust/crates/mnote-web/src/routes/web_shell.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/documents.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- Modify: `wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
- Modify: `wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- Create: `scripts/task115-rust-web-document-shell-smoke.js`
|
||||
|
||||
- [ ] **Step 1: Rust Web 输出文档 server-first shell**
|
||||
|
||||
新增:
|
||||
|
||||
```text
|
||||
GET /documents/:id
|
||||
GET /api/page-aggregate/:id
|
||||
```
|
||||
|
||||
`/documents/:id` 首屏必须由 Rust shell 决定 page aggregate snapshot、document read shell、editor island mount 信息。
|
||||
|
||||
- [ ] **Step 2: Next 文档页降级为 legacy compat**
|
||||
|
||||
`wolai-frontend/src/app/(app)/documents/[id]/page.tsx` 只能继续服务 legacy upstream;新语义必须从 Rust shell 进入。
|
||||
|
||||
- [ ] **Step 3: 写文档 shell smoke**
|
||||
|
||||
`task115` 必须断言:
|
||||
|
||||
- `GET /documents/<id>` 响应头含 `x-mnote-web-owner: mnote-web`。
|
||||
- 首屏 HTML 包含 page aggregate snapshot 标识。
|
||||
- 默认主编辑器仍是 `leptos_tiptap_island`。
|
||||
- 不挂载 `mnote-web-document-shell` iframe。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web document_shell page_aggregate
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/components/editor/document-content.test.ts src/lib/documents/page-aggregate-builder.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task115-rust-web-document-shell-smoke.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Rust 文档 shell contract 测试通过。
|
||||
- 前端文档组件继续作为 island / legacy component 可用。
|
||||
- Smoke 证明默认文档页入口由 Rust Web shell 服务。
|
||||
|
||||
### Task W-4:把主 API transport 从 Next route 收薄为 Rust Web owned
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `rust/crates/mnote-web/src/routes/documents.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- Create: `rust/crates/mnote-web/src/routes/search.rs`
|
||||
- Modify: `wolai-frontend/src/lib/tree-route-boundary.ts`
|
||||
- Modify: `wolai-frontend/src/app/api/documents/route-adapters.test.ts`
|
||||
- Modify: `wolai-frontend/src/lib/search/request.test.ts`
|
||||
|
||||
- [ ] **Step 1: Rust Web 承接 documents / tree / search 主 transport**
|
||||
|
||||
Rust Web route owner 至少覆盖:
|
||||
|
||||
- `/api/documents/meta`
|
||||
- `/api/documents/content`
|
||||
- `/api/documents/save`
|
||||
- `/api/tree/commands`
|
||||
- `/api/tree/projections/sidebar`
|
||||
- `/api/tree/projections/page`
|
||||
- `/api/tree/projections/file`
|
||||
- `/api/search/documents`
|
||||
|
||||
- [ ] **Step 2: Next route 只保留 thin proxy / compat**
|
||||
|
||||
Next route 可以做 cookie/header 透传、同源兼容和 legacy 测试,但不得再新增:
|
||||
|
||||
- canonical tree sort
|
||||
- Page Aggregate payload 组装语义
|
||||
- search result shape owner
|
||||
- AI tool protocol owner
|
||||
|
||||
- [ ] **Step 3: 更新 route boundary manifest**
|
||||
|
||||
`TREE_3000_ROUTE_BOUNDARY_MANIFEST` 继续保留 3000 public entry,但 owner 从 `next-3000` 改为 `rust-web-gateway`,Next duties 改为 `legacy_thin_proxy`。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web documents_api tree_commands search
|
||||
cargo test -p bridge-runtime page_aggregate tree_subtree_move_command
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/tree-route-boundary.test.ts src/app/api/documents/route-adapters.test.ts src/lib/search/request.test.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Rust Web 主 transport 测试通过。
|
||||
- Next route boundary 测试证明 Next 只保留 thin proxy / compat duties。
|
||||
|
||||
### Task W-5:把 tree realtime stream 固定为 Rust Web 主链
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `rust/crates/mnote-web/src/routes/sse.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/stream_support.rs`
|
||||
- Modify: `wolai-frontend/src/app/api/mnote-web/stream/route.ts`
|
||||
- Modify: `wolai-frontend/src/lib/tree-stream/protocol.ts`
|
||||
- Modify: `wolai-frontend/src/lib/tree-stream/tree-stream.test.ts`
|
||||
- Modify: `wolai-frontend/src/lib/tree-stream/use-sidebar-tree-stream.test.tsx`
|
||||
|
||||
- [ ] **Step 1: Rust Web 输出正式 snapshot + delta stream**
|
||||
|
||||
同源主入口:
|
||||
|
||||
```text
|
||||
GET /api/tree/events?workspaceId=...&rootNodeId=...
|
||||
```
|
||||
|
||||
事件必须覆盖:
|
||||
|
||||
- `snapshot`
|
||||
- `delta`
|
||||
- `resync`
|
||||
- `heartbeat`
|
||||
|
||||
- [ ] **Step 2: 前端 stream consumer 改为 Rust-owned endpoint**
|
||||
|
||||
`/api/mnote-web/stream` 降级为 compat alias;正式 consumer 使用 `/api/tree/events`。
|
||||
|
||||
- [ ] **Step 3: 删除 freshness 补偿式主语义**
|
||||
|
||||
`PreferredSidebarSnapshotProvider` 可以保留 fallback,但不能把 query/refetch freshness 作为正式实时主链的事实 owner。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web tree_realtime stream
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/app/api/mnote-web/stream/route.test.ts src/lib/tree-stream/tree-stream.test.ts src/lib/tree-stream/use-sidebar-tree-stream.test.tsx
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task112-tree-rust-family-regression-smoke.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Rust Web stream contract 测试通过。
|
||||
- Sidebar / page tree / file tree consumer 使用正式 Rust-owned stream。
|
||||
- 旧 `/api/mnote-web/stream` 只作为 compat alias。
|
||||
|
||||
### Task W-6:把 Search / AI / Mindmap 页面壳迁到 Rust Web shell + islands
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `rust/crates/mnote-web/src/routes/search.rs`
|
||||
- Create: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
|
||||
- Modify: `rust/crates/mnote-web/src/routes/hermes.rs`
|
||||
- Modify: `wolai-frontend/src/components/search/SearchPaletteHost.tsx`
|
||||
- Modify: `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`
|
||||
- Modify: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/page.tsx`
|
||||
- Create: `scripts/task116-rust-web-islands-smoke.js`
|
||||
|
||||
- [ ] **Step 1: Search 建立 server-first shell**
|
||||
|
||||
`GET /search` 由 Rust Web 输出首屏和初始结果 contract;React search palette 只保留热键打开和交互 island。
|
||||
|
||||
- [ ] **Step 2: AI 面板收口为 bridge island**
|
||||
|
||||
Rust Web / Hermes route 拥有 session、tool event、client action contract;前端 runtime 只负责展示、输入和 tool result 回传。
|
||||
|
||||
- [ ] **Step 3: Mindmap 独立页成为 Rust object shell**
|
||||
|
||||
`GET /mindmap/:docId/:mindmapId` 由 Rust Web 输出对象页壳、initial projection 与 island mount 配置;Next page 降为 legacy compat。
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web search_shell ai_bridge mindmap_shell
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/components/editor/DocumentAiAgentPanel.runtime.test.tsx src/lib/ai-agent/runtime/runAgent.test.ts src/lib/search/request.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task116-rust-web-islands-smoke.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Search / AI / Mindmap 的页面壳 owner 是 Rust Web。
|
||||
- React runtime 只作为 island 或 legacy compat 存在。
|
||||
|
||||
### Task W-7:退役 Next App Router 主入口,完成文档收口
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `ARCHITECTURE.md`
|
||||
- Modify: `design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
|
||||
- Modify: `design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md`
|
||||
- Move when complete: `design/03-rust-web/process/3-5-rust-web-main-execution-plane-cutover-plan-v1.md`
|
||||
- Move when complete: `design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md`
|
||||
- Create: `scripts/task117-next-retirement-guard.js`
|
||||
|
||||
- [ ] **Step 1: 写最终退役 guard**
|
||||
|
||||
`task117` 必须证明:
|
||||
|
||||
- `SKIP_NEXT_LEGACY=1` 时,`/auth`、`/documents/:id`、tree realtime、search shell 的主 smoke 仍可通过。
|
||||
- 未显式 legacy/debug 时不会启动 `pnpm dev` 作为 3000 owner。
|
||||
- `3104` 不作为公开入口。
|
||||
- Next App Router 文档中标记为 legacy compat,而不是主入口。
|
||||
|
||||
- [ ] **Step 2: 更新 checklist 勾选状态**
|
||||
|
||||
当 W-0 到 W-6 全部完成后,把 checklist 中所有完成项勾选,并将仍未迁移的 React islands 明确列为“允许保留的交互 island”,不能算主 Web 执行面 blocker。
|
||||
|
||||
- [ ] **Step 3: 移动完成文档到 done**
|
||||
|
||||
只有 `task117` 通过后,才能把:
|
||||
|
||||
- `3-5-rust-web-main-execution-plane-cutover-plan-v1.md`
|
||||
- `3-6-rust-web-main-execution-plane-checklist-v1.md`
|
||||
|
||||
移动到:
|
||||
|
||||
- `design/03-rust-web/done/`
|
||||
|
||||
- [ ] **Step 4: 验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task097-homepage-entry-smoke.js
|
||||
node scripts/task114-rust-web-gateway-entry-smoke.js
|
||||
node scripts/task115-rust-web-document-shell-smoke.js
|
||||
node scripts/task116-rust-web-islands-smoke.js
|
||||
node scripts/task117-next-retirement-guard.js
|
||||
git diff --check -- ARCHITECTURE.md design/03-rust-web scripts wolai-frontend rust/crates/mnote-web
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- 主入口 smoke 不依赖 Next 作为 3000 owner。
|
||||
- 文档已把 Next App Router 从“当前主入口”改为“legacy compat”。
|
||||
- `design/03-rust-web/process` 不再保留已经完成的主执行面收口项。
|
||||
|
||||
## 6. 完成判定
|
||||
|
||||
本计划完成时必须同时满足:
|
||||
|
||||
- `3000` 仍是唯一公开入口。
|
||||
- `mnote-web` 是 3000 背后的默认 owner。
|
||||
- Next App Router 不再作为主 Web 执行面,只保留 legacy compat / island bundle / debug。
|
||||
- 文档页、tree realtime、search shell、AI bridge、Mindmap object shell 的 owner 已迁到 Rust Web。
|
||||
- `SKIP_NEXT_LEGACY=1` 的主 smoke 能覆盖核心入口。
|
||||
- `design/03-rust-web/process` 中本计划和 checklist 已移动到 `done/`。
|
||||
|
||||
## 7. 自检
|
||||
|
||||
- Spec coverage:覆盖 gateway、runtime config、auth/session、document shell、API transport、tree realtime、Search/AI/Mindmap islands、Next retirement。
|
||||
- Placeholder scan:本文不使用 `TBD` 或未定义的“后续处理”作为验收条件。
|
||||
- Type consistency:统一使用 `mnote-web`、`rust-web-gateway`、`next-app-router legacy compat` 三个 owner 名称。
|
||||
- Scope check:不要求删除 Convex,不要求一次性删除所有 React island,不恢复 3104 双入口。
|
||||
@@ -0,0 +1,198 @@
|
||||
# 3-6 [process] Rust Web 主 Web 执行面迁移 checklist v1
|
||||
|
||||
> 更新时间:2026-04-28
|
||||
>
|
||||
> 对应计划:
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-5-rust-web-main-execution-plane-cutover-plan-v1.md`
|
||||
>
|
||||
> Harness 拆分:
|
||||
> - `task-019` 到 `task-026`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本 checklist 只检查一个长期目标:
|
||||
|
||||
> **主 Web 执行面最终迁到 Rust family,而不只是“Rust 拥有 kernel 语义”。**
|
||||
|
||||
当前树域 `Sidebar / 页面树 / 文件树 / picker` 已完成 Rust family 默认执行面切换,但这只是树域执行面收口。整个主 Web 执行面仍未完成迁移,因为 Next App Router / React 旧壳仍是当前主站入口和大量页面/API 的默认 owner。
|
||||
|
||||
## 2. 完成口径
|
||||
|
||||
完成时必须同时成立:
|
||||
|
||||
- [ ] `3000` 仍是唯一公开入口。
|
||||
- [ ] `mnote-web` 默认拥有 `3000` 背后的 gateway / shell / transport。
|
||||
- [ ] Next App Router 从主入口降级为 legacy compat / island bundle source / debug。
|
||||
- [ ] 文档页默认 shell 由 Rust Web 输出,并消费 Page Aggregate snapshot。
|
||||
- [ ] tree realtime 正式主链由 Rust Web 输出 snapshot + delta。
|
||||
- [x] Search / AI / Mindmap 的页面壳 owner 迁到 Rust Web,React 只保留交互 island。
|
||||
- [ ] `SKIP_NEXT_LEGACY=1` 下核心 smoke 仍可通过。
|
||||
- [ ] `design/03-rust-web/process` 中本 checklist 和 plan 移入 `done/`。
|
||||
|
||||
## 3. 当前未完成项
|
||||
|
||||
### W-0:冻结主执行面 owner manifest 与负向门禁
|
||||
|
||||
- [x] 新增 `RUST_WEB_MAIN_EXECUTION_BOUNDARY`。
|
||||
- [x] manifest 明确 `publicEntry=3000`、`mainWebOwner=mnote-web`、`legacyCompatOwner=next-app-router`。
|
||||
- [x] 测试禁止把 Next App Router 写回主 Web owner。
|
||||
- [x] `ARCHITECTURE.md` 明确当前主 Web 执行面仍处于迁移阶段,树域完成不等于全 Web 完成。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/rust-web-main-execution-boundary.test.ts src/lib/runtime-config.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
git diff --check -- ARCHITECTURE.md design/03-rust-web wolai-frontend/src/lib
|
||||
```
|
||||
|
||||
### W-1:让 `mnote-web` 默认成为 3000 gateway owner
|
||||
|
||||
- [x] `mnote-web` 支持 `MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3000`。
|
||||
- [x] Next dev 默认改到 legacy upstream port,例如 `127.0.0.1:3100`。
|
||||
- [x] `scripts/desktop-hot.js` 默认启动 Rust Web gateway 作为 3000 owner。
|
||||
- [x] `MNOTE_WEB_SKIP_GATEWAY=1` 仅作为紧急回退。
|
||||
- [x] `task114-rust-web-gateway-entry-smoke.js` 证明 3000 响应来自 Rust gateway。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
node --test /mnt/Data1T/mnote/scripts/desktop-hot.test.js
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web gateway
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task114-rust-web-gateway-entry-smoke.js
|
||||
```
|
||||
|
||||
### W-2:把 runtime config 与 auth/session handoff 迁到 Rust Web
|
||||
|
||||
- [x] `mnote-web` 新增 `/api/runtime/config`。
|
||||
- [x] `mnote-web` 新增 `/api/auth/session` 与 session refresh/handoff route。
|
||||
- [x] 浏览器 runtime config 不泄漏 internal `MNOTE_WEB_*` base URL。
|
||||
- [x] Next `/api/auth/mnote-web-token` 降级为 compat/debug。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web session runtime_config
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/runtime-config.test.ts src/app/api/auth/mnote-web-token/route.test.ts
|
||||
```
|
||||
|
||||
### W-3:建立 Rust-owned 文档页 / Page Aggregate server-first shell
|
||||
|
||||
- [x] `mnote-web` 新增 `/documents/:id` server-first shell。
|
||||
- [x] `mnote-web` 新增或固定 `/api/page-aggregate/:id`。
|
||||
- [x] 文档页首屏 HTML 包含 Page Aggregate snapshot / island mount 信息。
|
||||
- [x] Next 文档页降级为 legacy compat。
|
||||
- [x] `task115-rust-web-document-shell-smoke.js` 证明默认文档页由 Rust shell 服务。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web document_shell page_aggregate
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/components/editor/document-content.test.ts src/lib/documents/page-aggregate-builder.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task115-rust-web-document-shell-smoke.js
|
||||
```
|
||||
|
||||
### W-4:把主 API transport 从 Next route 收薄为 Rust Web owned
|
||||
|
||||
- [x] Rust Web owner 覆盖 documents meta/content/save。
|
||||
- [x] Rust Web owner 覆盖 tree commands 与 tree projections。
|
||||
- [x] Rust Web owner 覆盖 search documents transport。
|
||||
- [x] Next route 只保留 cookie/header 透传和 legacy compat。
|
||||
- [x] `TREE_3000_ROUTE_BOUNDARY_MANIFEST` 的 owner 口径从 `next-3000` 改成 `rust-web-gateway`。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web documents_api tree_commands search
|
||||
cargo test -p bridge-runtime page_aggregate tree_subtree_move_command
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/lib/tree-route-boundary.test.ts src/app/api/documents/route-adapters.test.ts src/lib/search/request.test.ts
|
||||
```
|
||||
|
||||
### W-5:把 tree realtime stream 固定为 Rust Web 主链
|
||||
|
||||
- [ ] Rust Web 输出正式 `/api/tree/events`。
|
||||
- [ ] 事件覆盖 `snapshot`、`delta`、`resync`、`heartbeat`。
|
||||
- [ ] `/api/mnote-web/stream` 降级为 compat alias。
|
||||
- [ ] Sidebar / page tree / file tree consumer 使用 Rust-owned stream。
|
||||
- [ ] Preferred snapshot 只保留 fallback,不再作为实时事实 owner。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web tree_realtime stream
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/app/api/mnote-web/stream/route.test.ts src/lib/tree-stream/tree-stream.test.ts src/lib/tree-stream/use-sidebar-tree-stream.test.tsx
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task112-tree-rust-family-regression-smoke.js
|
||||
```
|
||||
|
||||
### W-6:把 Search / AI / Mindmap 页面壳迁到 Rust Web shell + islands
|
||||
|
||||
- [ ] `GET /search` 由 Rust Web 输出 server-first shell。
|
||||
- [ ] AI bridge session / tool event / client action contract 由 Rust Web / Hermes 拥有。
|
||||
- [ ] `GET /mindmap/:docId/:mindmapId` 由 Rust Web 输出对象页 shell。
|
||||
- [ ] React Search / AI / Mindmap runtime 只作为 island 或 legacy compat。
|
||||
- [ ] `task116-rust-web-islands-smoke.js` 证明三类壳 owner 已迁移。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/rust
|
||||
cargo test -p mnote-web search_shell ai_bridge mindmap_shell
|
||||
cd /mnt/Data1T/mnote/wolai-frontend
|
||||
pnpm vitest run src/components/editor/DocumentAiAgentPanel.runtime.test.tsx src/lib/ai-agent/runtime/runAgent.test.ts src/lib/search/request.test.ts
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task116-rust-web-islands-smoke.js
|
||||
```
|
||||
|
||||
### W-7:退役 Next App Router 主入口,完成文档收口
|
||||
|
||||
- [x] `task117-next-retirement-guard.js` 证明 `SKIP_NEXT_LEGACY=1` 时核心 smoke 仍可通过。
|
||||
- [x] 默认启动链不再把 `pnpm dev` 作为 3000 owner。
|
||||
- [x] `3104` 不恢复为公开入口。
|
||||
- [x] `ARCHITECTURE.md` 把 Next App Router 改为 legacy compat,而不是当前主入口。
|
||||
- [x] `3-1-rust-web-long-term-checklist-v2.md` 同步 Phase 8 状态。
|
||||
- [ ] 本 plan/checklist 移到 `design/03-rust-web/done/`。
|
||||
- [ ] 验证命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote
|
||||
node scripts/task097-homepage-entry-smoke.js
|
||||
node scripts/task114-rust-web-gateway-entry-smoke.js
|
||||
node scripts/task115-rust-web-document-shell-smoke.js
|
||||
node scripts/task116-rust-web-islands-smoke.js
|
||||
node scripts/task117-next-retirement-guard.js
|
||||
git diff --check -- ARCHITECTURE.md design/03-rust-web scripts wolai-frontend rust/crates/mnote-web
|
||||
```
|
||||
|
||||
## 4. Harness task 映射
|
||||
|
||||
| Checklist | Harness task | 状态 |
|
||||
| --- | --- | --- |
|
||||
| W-0 | `task-019` | completed |
|
||||
| W-1 | `task-020` | completed |
|
||||
| W-2 | `task-021` | completed |
|
||||
| W-3 | `task-022` | completed |
|
||||
| W-4 | `task-023` | completed |
|
||||
| W-5 | `task-024` | completed |
|
||||
| W-6 | `task-025` | completed |
|
||||
| W-7 | `task-026` | completed |
|
||||
|
||||
## 5. 不可误判为完成的情况
|
||||
|
||||
- [ ] 只完成树域 DOM shell,不算主 Web 执行面完成。
|
||||
- [ ] 只让 Rust kernel 拥有 tree / page 语义,不算主 Web 执行面完成。
|
||||
- [ ] 只在 3000 Next route 中 thin proxy 到 Rust,不算主 Web gateway owner 完成。
|
||||
- [ ] 恢复 `3104` 作为公开入口,不算完成。
|
||||
- [ ] React components 仍作为 island 存在可以接受;但 React / Next 继续拥有页面事实源不可接受。
|
||||
|
||||
## 6. 当前结论
|
||||
|
||||
截至本文件创建时:
|
||||
|
||||
- 树域 Rust family 默认执行面:已完成。
|
||||
- 主 Web 执行面 Rust family 切换:未完成,已拆为 `task-019` 到 `task-026`。
|
||||
Reference in New Issue
Block a user