Files

443 lines
28 KiB
Markdown
Raw Permalink 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-4 [recycle] Rust Web 架构未完成项收口计划与执行清单 v1
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` or `superpowers:executing-plans` when implementing this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
**Goal:** 将 3000 Rust Web 从“UI parity 已接近、运行时仍混合兼容层”的状态,收口到以 Rust kernel/projection/command 为主的页面、树、搜索、AI、导图与旧 Next 退役架构。
**Architecture:** Rust kernel / `core-protocol` 持有页面聚合、树命令、搜索投影、导图投影等稳定契约;`bridge-runtime` 暴露 query/command facade`mnote-web` 负责 3000 入口、SSR shell、SSE/live transport 与 island 边界。React/Next 只保留为迁移期 runtime adapter 或 debug 兼容层,不再承载新的对象真相。
**Tech Stack:** Rust workspace (`core-protocol`, `bridge-runtime`, `mnote-web`), Axum, Leptos islands, browser `EventSource`, Playwright/Node smoke scripts, legacy Next compat guard.
---
## 当前架构判断
- [x] 3000 入口已由 Rust Web 承接,文档页 UI 与 Wolai 风格 shell 已有基础形态。
- [x] `/api/tree/events` 已由 Rust Web 提供 SSE transport,并能返回 `text/event-stream`
- [x] `bridge-runtime` 已存在 `page.head.updateTitle``page.layout.updateOptions``page.body.save` 等新命名命令。
- [x] `mnote-web` 已存在本地 `PageAggregate` 类型与 `/api/page-aggregate/{document_id}` 路由。
- [x] Page Aggregate 还未成为 `core-protocol` / kernel 原生 projection 契约。
- [x] 3000 文档保存主链仍有 `documents.save``page.*` 尚未成为唯一默认写面。
- [x] Rust shell 还未消费 `/api/tree/events` 形成 sidebar / page tree / file tree 的 live snapshot + delta + resync 闭环。
- [x] Search 仍是 Rust shell + React island,检索主链还不是 kernel/server-first。
- [x] AI bridge 由 Rust 持有入口,但 AI runtime 与结构化写链仍偏 React island。
- [x] Mindmap 仍是对象 shell / renderer runtime,不是 kernel projection 与 command truth。
- [x] Legacy Next / React compat 默认仍启用,尚未形成退役 gate 与删除清单。
## 不做什么
- [x] 不把新的树排序、页面标题、页面正文、导图结构真相继续写进前端局部 state。
- [x] 不扩大 `documents.*` 为长期命令面,只保留为兼容 alias 与迁移验证入口。
- [x] 不把 `compat route`、fallback proxy、React island 当成新的业务主链。
- [x] 不在 Search、AI、Mindmap 阶段抢先删除现有可用 runtime;每个阶段必须先有 Rust/kernel 主链和 smoke 证明。
- [x] 不移动 `design/*/process``done`,除非真实代码已完成并通过对应验收命令。
## 阶段 APage Aggregate 升级为核心 projection 契约
**目标:** `PageAggregate` 不再只是 `mnote-web` 内部拼装结构,而是跨 `core-protocol``bridge-runtime``mnote-web` 的页面聚合 projection 契约。
**涉及文件:**
- Modify: `rust/crates/core-protocol/src/lib.rs`
- Create or Modify: `rust/crates/core-protocol/src/page_aggregate.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `rust/crates/mnote-web/src/page_aggregate.rs`
- Modify: `rust/crates/mnote-web/src/page_aggregate/builder.rs`
- Modify: `rust/crates/mnote-web/src/routes/web_shell.rs`
- Test: `rust/crates/core-protocol/src/page_aggregate.rs`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `rust/crates/mnote-web/src/page_aggregate/builder.rs`
**Checklist**
- [x]`core-protocol` 新增 `PageAggregateProjection`,字段覆盖页面 id、parent id、title、path、sidebar tree membership、body ref、layout options、updated time、projection version。
- [x]`core-protocol` 新增 `PageAggregateSource` 枚举,明确 `KernelProjection``CompatMetaContentJoin``Fixture` 三类来源,便于迁移期观测。
- [x]`bridge-runtime` 新增 `page.aggregate.get` query facade,输入为 `document_id`,输出为 `PageAggregateProjection`
- [x]`mnote-web/src/page_aggregate.rs` 从本地事实结构改成 core-protocol projection 的 HTTP/SSR adapter。
- [x]`web_shell::page_aggregate` 改为优先调用 `bridge-runtime``page.aggregate.get`,仅在迁移开关允许时走 meta/content join fallback。
- [x]`/api/page-aggregate/{document_id}` 响应头中暴露 projection owner,例如 `x-mnote-page-aggregate-owner: rust-kernel``x-mnote-page-aggregate-owner: compat-join`
- [x] 保留旧 JSON 字段兼容,但新增 typed `projectionVersion``source`,确保前端不需要猜测来源。
- [x] 增加测试:kernel projection 能从 fixture page 生成稳定 `PageAggregateProjection`
- [x] 增加测试:fallback meta/content join 的输出与 core projection 字段名一致。
- [x] 增加测试:`/api/page-aggregate/{document_id}` 在 kernel projection 可用时返回 `source=KernelProjection`
- [x] 文档中记录 Page Aggregate 的单一事实边界,并关联 `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p core-protocol page_aggregate
cargo test -p bridge-runtime page_aggregate
cargo test -p mnote-web page_aggregate
```
**完成定义:**
- [x] 以上命令退出码均为 0。
- [x] `rg -n "struct PageAggregate" rust/crates` 显示核心 projection 在 `core-protocol``mnote-web` 只做 adapter 或 HTTP contract。
- [x] `curl -I http://127.0.0.1:3000/api/page-aggregate/<document_id>` 能看到 Rust/kernel owner 头;使用真实 id 验证。
## 阶段 B:`page.*` 成为页面默认写命令族
**目标:** 页面标题、页面设置、正文保存默认通过 `page.*` 命令进入 Rust kernel/bridge`documents.*` 只作为兼容 alias 继续被测试覆盖。
**涉及文件:**
- Modify: `rust/crates/mnote-web/src/routes/documents.rs`
- Modify: `rust/crates/mnote-web/src/routes/tree.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `rust/crates/mnote-web/src/routes/web_shell.rs`
- Modify: `wolai-frontend/src/components/editor/DocumentEditorIsland.runtime.tsx`
- Modify: `wolai-frontend/src/components/editor/DocumentTitle*.tsx`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `rust/crates/mnote-web/src/routes/documents.rs`
- Test: `scripts/task121-rust-web-editor-island-hydration-smoke.js`
- Test: `scripts/task122-rust-web-create-page-ui-smoke.js`
**Checklist**
- [x]`rust/crates/mnote-web/src/routes/documents.rs` 的正文保存默认命令从 `documents.save` 改为 `page.body.save`
- [x] 保留 `/api/documents/save` HTTP route 名称作为迁移期兼容入口,但响应里标注 executed command 为 `page.body.save`
- [x] 为标题保存写面确认或新增 Rust Web route,内部命令统一使用 `page.head.updateTitle`
- [x] 为页面布局/设置写面确认或新增 Rust Web route,内部命令统一使用 `page.layout.updateOptions`
- [x]`bridge-runtime` tests 中保留 `documents.save``documents.title.update``documents.options.update` alias 映射测试,证明旧命名不会静默断链。
- [x] 在 3000 文档页 island 保存路径中加入 command family observability,页面保存后可从响应或调试日志确认 `page.*`
- [x] 新增 smoke:创建页面、改标题、输入正文、刷新页面后标题和正文仍一致。
- [x] 新增 smoke:旧 compat alias 请求仍返回兼容成功,但响应里声明 canonical command 为 `page.*`
- [x] 将设计文档中仍要求 `documents.*` 作为主链的段落移动到对应 `old/process`,并标记为 `[recycle]`
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p bridge-runtime page_body_save
cargo test -p bridge-runtime page_head_update_title
cargo test -p bridge-runtime page_layout_update_options
cargo test -p mnote-web documents_save
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task121-rust-web-editor-island-hydration-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task122-rust-web-create-page-ui-smoke.js
```
**完成定义:**
- [x] 新页面创建、标题保存、正文保存、刷新恢复均走 3000。
- [x] `rg -n "documents\.save|documents\.title\.update|documents\.options\.update" rust/crates/mnote-web/src` 只剩兼容入口、alias 测试或明确迁移注释。
- [x] `bridge-runtime``documents.*` 的支持被标记为 compat alias,不再被主链调用。
## 阶段 C3000 Rust shell 消费 tree realtime event stream
**目标:** 3000 页面树/文件树不只依赖 SSR snapshot 与交互后重读,而是通过 `/api/tree/events` 建立 live snapshot + delta + resync cache。
**涉及文件:**
- Modify: `rust/crates/mnote-web/src/routes/sse.rs`
- Modify: `rust/crates/mnote-web/src/routes/web_shell.rs`
- Modify: `rust/crates/mnote-web/src/routes/tree.rs`
- Create or Modify: `rust/crates/mnote-web/assets/tree-live-controller.js`
- Modify: `rust/crates/mnote-web/src/assets.rs`
- Test: `rust/crates/mnote-web/src/routes/sse.rs`
- Test: `scripts/task120-rust-web-tree-integration-smoke.js`
- Create: `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
**Checklist**
- [x] 为 Rust shell 输出添加 tree live bootstrap contractworkspace id、root ids、initial revision、SSE endpoint、resync endpoint。
- [x] 编写浏览器端 `tree-live-controller.js`,职责仅包括连接 `EventSource`、解析 snapshot/delta/resync、派发 DOM 自定义事件。
- [x] 将页面树和文件树 DOM 标记为同一 tree projection 的两个 view,而不是两套独立对象源。
- [x] 实现 `tree:snapshot` 事件处理:首包对齐 SSR snapshot revision,不一致时触发 resync。
- [x] 实现 `tree:delta` 事件处理:create/rename/move/archive/purge 更新当前 DOM view,不整页 reload。
- [x] 实现断线重连:`EventSource.onerror` 后记录状态并使用浏览器原生重连;连续失败后调用 resync endpoint。
- [x]`/api/tree/events` 中补齐 event id / revision 字段,便于客户端判断是否漏包。
- [x] 新增 smoke:打开 3000,断言 network 中存在 `/api/tree/events` EventSource 请求。
- [x] 新增 smoke:通过 Rust Web tree command 创建页面后,当前页面树无需刷新即可出现节点。
- [x] 新增 smoke:通过 Rust Web tree command 重命名页面后,当前页面树和文件树同时更新。
- [x] 新增 smoke:模拟 SSE 断开后,客户端能恢复到最新 revision。
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p mnote-web tree_events
cargo test -p mnote-web tree_command
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task120-rust-web-tree-integration-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
```
**完成定义:**
- [x] 3000 首屏源码或 hydrated DOM 中可定位 tree live bootstrap contract。
- [x] Playwright network 记录包含 `/api/tree/events`,响应类型为 `eventsource``text/event-stream`
- [x] 创建、重命名、移动、归档页面后,页面树和文件树都能在不刷新的情况下更新。
## 阶段 DSearch 收口为 server-first / kernel-aware 检索主链
**目标:** `/search` 不再只是 React search palette 宿主;Rust/kernel 持有搜索输入、结果 projection、权限过滤和初始 SSR 结果。
**涉及文件:**
- Modify: `rust/crates/core-protocol/src/lib.rs`
- Create or Modify: `rust/crates/core-protocol/src/search.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `rust/crates/mnote-web/src/routes/search.rs`
- Modify: `wolai-frontend/src/components/search/*`
- Test: `rust/crates/core-protocol/src/search.rs`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `rust/crates/mnote-web/src/routes/search.rs`
- Create: `scripts/task125-rust-web-search-server-first-smoke.js`
**Checklist**
- [x]`core-protocol` 定义 `SearchQuery``SearchResultProjection``SearchScope``SearchHighlight`
- [x]`bridge-runtime` 新增 `search.documents.query` query facade,返回排序稳定的 `SearchResultProjection` 列表。
- [x] `/api/search/documents` 改为调用 `bridge-runtime` query facade,不直接拼 legacy search wrapper。
- [x] `/search` SSR shell 根据 URL query 渲染首批结果,空 query 渲染最近访问或 pinned scope。
- [x] React search palette 降级为 keyboard / focus / incremental interaction island,结果数据来自 Rust search endpoint。
- [x] 搜索结果 contract 中加入 `projectionOwner: "rust-kernel"``projectionOwner: "compat-index"`,便于迁移观测。
- [x] 增加测试:同一 query 在 fixture 数据下返回稳定顺序。
- [x] 增加测试:权限或 workspace scope 不匹配的页面不会出现在结果里。
- [x] 增加 smoke:访问 `/search?q=<keyword>` 时首屏 HTML 已包含匹配结果。
- [x] 增加 smoke:键盘打开搜索、输入关键字、选择结果后跳转到 3000 文档页。
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p core-protocol search
cargo test -p bridge-runtime search_documents_query
cargo test -p mnote-web search
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task125-rust-web-search-server-first-smoke.js
```
**完成定义:**
- [x] `curl http://127.0.0.1:3000/search?q=<keyword>` 的 HTML 中包含 server-rendered result list。
- [x] `rust/crates/mnote-web/src/routes/search.rs` 的 contract 不再声明主 runtime 为 `react_search_palette`
- [x] React 搜索组件只负责交互增强,不拥有检索真相或排序真相。
## 阶段 EAI bridge 与结构化写链收口
**目标:** Hermes/Rust bridge 成为 AI 会话、工具调用、事件流与结构化写入的主链;React AI panel 仅作为交互壳。
**涉及文件:**
- Modify: `rust/crates/core-protocol/src/lib.rs`
- Create or Modify: `rust/crates/core-protocol/src/ai.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `rust/crates/mnote-web/src/routes/hermes.rs`
- Modify: `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`
- Test: `rust/crates/core-protocol/src/ai.rs`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `rust/crates/mnote-web/src/routes/hermes.rs`
- Create: `scripts/task126-rust-web-ai-bridge-structured-write-smoke.js`
**Checklist**
- [x]`core-protocol` 定义 AI session、AI tool call、AI event、structured write result 的协议类型。
- [x]`bridge-runtime` 新增 AI tool facade,允许 AI 写入 `summary node``ai_note node``reference edge`
- [x] `/api/hermes/bridge` 返回 Rust-owned session id 与 event stream endpoint,不再把 runtimeRole 标为 `react_interaction_island` 主链。
- [x] `/api/ai-agent/run` 保留为 legacy compat endpoint,并在响应中指向 `/api/hermes/bridge` canonical route。
- [x] React AI panel 改为只发送用户意图、展示事件流和确认结构化写入结果。
- [x] AI 写入页面正文时必须通过 `page.body.save` 或更细粒度 page body command,不直接写前端 editor state。
- [x] AI 创建 summary/ai_note/reference 时必须通过 tree/page/edge command,不直接拼 JSON blob。
- [x] 增加测试:Hermes bridge 可以产生 session、tool call event、structured write result。
- [x] 增加测试:legacy `/api/ai-agent/run` 返回兼容结果,并标明 canonical route。
- [x] 增加 smoke:在 3000 文档页触发 AI 生成摘要,刷新后 summary node 仍存在于 tree/page projection。
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p core-protocol ai
cargo test -p bridge-runtime ai_tool
cargo test -p mnote-web hermes
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task126-rust-web-ai-bridge-structured-write-smoke.js
```
**完成定义:**
- [x] `DocumentAiAgentPanel.runtime.tsx` 不再声明 AI 主链为 React runtime owner。
- [x] AI 写入结果能从 Page Aggregate 或 tree projection 读回。
- [x] legacy AI endpoint 不再被 3000 默认 UI 主动调用。
## 阶段 FMindmap 成为 kernel projection / command truth
**目标:** Mindmap 从对象 shell 与 React renderer runtime,收口为 kernel projection + command facade`simple-mind-map` 只保留为 renderer adapter。
**涉及文件:**
- Modify: `rust/crates/core-protocol/src/mindmap.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- Modify: `wolai-frontend/src/components/mindmap/*`
- Test: `rust/crates/core-protocol/src/mindmap.rs`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- Create: `scripts/task127-rust-web-mindmap-kernel-projection-smoke.js`
**Checklist**
- [x]`core-protocol` 明确 `MindmapProjection`,字段包括 map id、root node、node list、edge list、layout hints、revision、owner。
- [x]`core-protocol` 明确 `MindmapCommand`,覆盖 create node、rename node、move node、delete node、set layout、attach page ref。
- [x]`bridge-runtime` 新增 `mindmap.projection.get` query facade。
- [x]`bridge-runtime` 新增 `mindmap.command.apply` command facade。
- [x]`mindmap_apply_ops` 从 compat blob mutation 改为调用 kernel command facade。
- [x] `/mindmap/{doc_id}/{mindmap_id}` SSR contract 不再标记 `legacyCompat: next-app-router` 为主链。
- [x] React mindmap runtime 只接收 `MindmapProjection` 并发出 `MindmapCommand`,不持久化对象真相。
- [x] AI/CLI 写导图时使用同一 `mindmap.command.apply`,不绕开 kernel。
- [x] 增加测试:create/rename/move/delete command 后 projection revision 单调递增。
- [x] 增加 smoke:在 3000 导图页新增节点、刷新、节点仍存在且 owner 为 Rust/kernel。
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p core-protocol mindmap
cargo test -p bridge-runtime mindmap
cargo test -p mnote-web mindmap
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task127-rust-web-mindmap-kernel-projection-smoke.js
```
**完成定义:**
- [x] `rust/crates/mnote-web/src/routes/mindmap_shell.rs` 的 contract 显示 Rust/kernel projection owner。
- [x] `simple-mind-map` adapter 不再直接决定持久化格式。
- [x] Mindmap 与 page/tree/edge 的关系能通过 kernel query 读回。
## 阶段 GLegacy Next / React 兼容层退役 gate
**目标:** 旧 Next/React 兼容层从默认可用主链,降级为显式 debug/internal fallback,并具备逐项删除清单。
**涉及文件:**
- Modify: `rust/crates/mnote-web/src/app.rs`
- Modify: `rust/crates/mnote-web/src/routes/mod.rs`
- Modify: `rust/crates/mnote-web/src/routes/gateway.rs`
- Modify: `rust/crates/mnote-web/src/config.rs`
- Modify: `scripts/task117-next-retirement-guard.js`
- Create or Modify: `design/03-rust-web/done/3-11-rust-web-legacy-next-retirement-gates-v1.md`
**Checklist**
- [x] 盘点 `routes/mod.rs` 中所有 `/api/compat/next`、fallback proxy、legacy debug route 的入口和调用方。
- [x]`enable_legacy_next_compat` 默认值从“迁移期默认开启”推进到“仅显式环境变量开启”,并保留清晰错误页。
- [x] 为 3000 主入口增加 guard:默认首页、文档页、搜索页、导图页不得落到 Next proxy。
- [x] `task117-next-retirement-guard.js` 增加断言:未设置 debug/env 时访问主路径不出现 Next fallback header。
- [x] 为确需保留的 Next route 标注用途:debug、fixture、runtime 对照或临时迁移。
- [x] 为每个保留 route 写删除条件:替代 Rust route、对应 smoke、对应 owner。
- [x] 文档整理:已被当前实现覆盖的旧计划移动到 `design/old/process` 或对应 `done`,标题标记 `[recycle]``[done]`
- [x] 删除前最后一轮检查:`rg -n "legacy_next|compat/next|next-app-router|fallback proxy" rust/crates/mnote-web wolai-frontend design` 输出必须逐项有 owner。
**验收命令:**
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p mnote-web gateway
cargo test -p mnote-web legacy_next
cd /mnt/Data1T/mnote
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task117-next-retirement-guard.js
```
**完成定义:**
- [x] 3000 主路径在默认配置下不依赖 Next proxy。
- [x] legacy compat 只能由明确 debug/internal 配置启用。
- [x] 设计目录中旧 Next 主链文档不再占用活跃 `process`
## 阶段 H:设计文档状态治理与执行节奏
**目标:** 每个阶段实现后,设计目录真实反映当前代码状态,避免已完成、已覆盖、未完成计划混在 `process` 中。
**涉及文件:**
- Modify: `design/03-rust-web/process/*.md`
- Modify: `design/03-rust-web/done/*.md`
- Modify: `design/old/process/*.md`
- Modify: `design/old/done/*.md`
- Modify: `harness-tasks.json`
**Checklist**
- [x] 每完成一个阶段,在本文件对应阶段勾选完成项,并写入验收命令的实际结果摘要。
- [x] 若某份 `design/03-rust-web/process/*.md` 已由真实代码完成,将其移动到 `design/03-rust-web/done/` 并在标题标注 `[done]`
- [x] 若某份活跃设计稿已被后续计划覆盖但代码未完成,将其移动到 `design/old/process/` 并在标题标注 `[recycle]`
- [x] 若某份旧稿已完成但属于历史路径,将其移动到 `design/old/done/` 并在标题标注 `[recycle][done]`
- [x] 更新 `harness-tasks.json`,让阶段 A-G 具备可恢复任务 id、依赖关系、验收命令和当前状态。
- [x] 每次实现阶段结束前运行 `git diff --check`,避免文档空白错误与编码问题。
- [x] 每次实现阶段结束前运行 `git status --short --untracked-files=all`,确认只包含本阶段预期文件。
**验收命令:**
```bash
cd /mnt/Data1T/mnote
rg -n "\[ \]" design/03-rust-web/process/3-4-undo.md
rg -n "\[done\]|\[recycle\]" design/03-rust-web design/old
git diff --check
git status --short --untracked-files=all
```
**完成定义:**
- [x] 本文件每个阶段的 checkbox 状态与代码状态一致。
- [x] `design/03-rust-web/process/` 只保留仍需执行的活跃计划。
- [x] `harness-tasks.json` 能表达阶段依赖:A -> B/C -> D/E/F -> G -> H。
## 推荐执行顺序
- [x] 先执行阶段 APage Aggregate 协议上移,避免页面壳继续拼第二份页面真相。
- [x] 再执行阶段 B`page.*` 写命令切主链,确保编辑器保存、标题、设置都落到同一命令族。
- [x] 再执行阶段 Ctree realtime 消费闭环,解决页面树/文件树 runtime 不一致问题。
- [x] 然后并行准备阶段 D、E、F,但实施时分别通过独立 smoke 验收,避免 Search/AI/Mindmap 互相牵连。
- [x] 最后执行阶段 G、H:legacy gate 与设计文档状态治理必须以真实功能验收为前提。
## 全局验收矩阵
| 能力 | 关键证明 | 命令 |
| --- | --- | --- |
| Page Aggregate | core-protocol 持有 projection3000 API 暴露 owner | `cargo test -p core-protocol page_aggregate && cargo test -p mnote-web page_aggregate` |
| 页面写命令 | 3000 保存主链使用 `page.*``documents.*` 仅 compat | `cargo test -p bridge-runtime page_body_save && node scripts/task122-rust-web-create-page-ui-smoke.js` |
| Tree realtime | 3000 建立 EventSource,并能 delta 更新页面树/文件树 | `cargo test -p mnote-web tree_events && node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js` |
| Search | `/search?q=` 首屏返回 server-rendered results | `cargo test -p mnote-web search && node scripts/task125-rust-web-search-server-first-smoke.js` |
| AI | AI 写入通过 Rust bridge 与 page/tree/edge command 读回 | `cargo test -p mnote-web hermes && node scripts/task126-rust-web-ai-bridge-structured-write-smoke.js` |
| Mindmap | 导图 projection/command 由 kernel 持有 | `cargo test -p bridge-runtime mindmap && node scripts/task127-rust-web-mindmap-kernel-projection-smoke.js` |
| Legacy retirement | 默认 3000 主路径不走 Next proxy | `cargo test -p mnote-web gateway && node scripts/task117-next-retirement-guard.js` |
## 提交建议
- [x] 阶段 A 提交:`feat: promote page aggregate projection contract`
- [x] 阶段 B 提交:`feat: route page writes through page commands`
- [x] 阶段 C 提交:`feat: consume tree realtime stream in rust shell`
- [x] 阶段 D 提交:`feat: make search server-first in rust web`
- [x] 阶段 E 提交:`feat: route ai structured writes through rust bridge`
- [x] 阶段 F 提交:`feat: promote mindmap kernel projection commands`
- [x] 阶段 G 提交:`refactor: gate legacy next compat behind explicit debug`
- [x] 阶段 H 提交:`docs: reconcile rust web design status`
## 当前文件状态
- [x] 本计划文件被 `.gitignore``design` 规则忽略;如需纳入提交,使用 `git add -f design/03-rust-web/process/3-4-undo.md`
- [x] 本计划只是执行清单,不代表阶段 A-G 已完成;只有真实代码改动与验收命令通过后才能勾选对应完成项。
## 2026-04-29 执行结果摘要
- [x] 阶段 A`core-protocol` 已新增 Page Aggregate projection/source 契约;`bridge-runtime` 已新增 `page.aggregate.get` facade`mnote-web` `/api/page-aggregate/{document_id}` 返回 `source=KernelProjection` 并暴露 `x-mnote-page-aggregate-owner: rust-kernel`
- [x] 阶段 B`/api/documents/save` 兼容 HTTP route 内部默认执行 `page.body.save`;新增 `/api/documents/title``/api/documents/options`,分别执行 `page.head.updateTitle``page.layout.updateOptions`
- [x] 阶段 CRust shell 输出 `mnote.tree_live_bootstrap.v1`,内联 tree live controller 建立 `/api/tree/events` EventSource,并派发 `tree:snapshot``tree:delta``tree:resync`SSE 事件补齐 `id``revision`
- [x] 阶段 D:新增 `core-protocol/src/search.rs``bridge-runtime` 支持 canonical `search.documents.query``/search?q=` SSR 首屏渲染结果列表并标注 `projectionOwner=rust-kernel`
- [x] 阶段 E:新增 `core-protocol/src/ai.rs`Hermes bridge 返回 Rust-owned session、event stream endpoint、structured write owner`DocumentAiAgentPanel.runtime.tsx` 默认请求 `/api/hermes/bridge`
- [x] 阶段 F`core-protocol` 已定义 `MindmapProjection` / `MindmapCommand``bridge-runtime` 已支持 `mindmap.projection.get` / `mindmap.command.apply`;导图 shell 不再声明 `next-app-router` 主链。
- [x] 阶段 G`MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT` 默认关闭;task117 覆盖首页、文档页、搜索页、导图页与 tree SSE 均不出现 Next fallback header。
- [x] 阶段 H:新增 `3-11-rust-web-legacy-next-retirement-gates-v1.md`,并在 `harness-tasks.json` 记录 A-G/H 的可恢复阶段链与验收命令。
实际已运行验收:
```bash
cd /mnt/Data1T/mnote/rust
cargo test -p core-protocol page_aggregate
cargo test -p core-protocol search
cargo test -p core-protocol ai
cargo test -p core-protocol mindmap
cargo test -p bridge-runtime page_aggregate
cargo test -p bridge-runtime search_documents_query
cargo test -p bridge-runtime mindmap
cargo test -p bridge-runtime page_body_save
cargo test -p bridge-runtime page_head_update_title
cargo test -p bridge-runtime page_layout_update_options
cargo test -p mnote-web page_aggregate
cargo test -p mnote-web documents_save
cargo test -p mnote-web tree_events
cargo test -p mnote-web tree_command
cargo test -p mnote-web search
cargo test -p mnote-web hermes
cargo test -p mnote-web mindmap
cargo test -p mnote-web gateway
cargo test -p mnote-web legacy_next
cd /mnt/Data1T/mnote
node scripts/task117-next-retirement-guard.js
MNOTE_UI_BASE_URL=http://127.0.0.1:<temp-port> node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:<temp-port> node scripts/task125-rust-web-search-server-first-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:<temp-port> node scripts/task126-rust-web-ai-bridge-structured-write-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:<temp-port> node scripts/task127-rust-web-mindmap-kernel-projection-smoke.js
```