Files
mnote/design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md
T

584 lines
21 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.
# 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 streamNext 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 ownerNext 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 执行面 ownerNext 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 输出首屏和初始结果 contractReact 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 双入口。