# 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`，除非真实代码已完成并通过对应验收命令。

## 阶段 A：Page 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，不再被主链调用。

## 阶段 C：3000 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 contract：workspace 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] 创建、重命名、移动、归档页面后，页面树和文件树都能在不刷新的情况下更新。

## 阶段 D：Search 收口为 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 搜索组件只负责交互增强，不拥有检索真相或排序真相。

## 阶段 E：AI 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 主动调用。

## 阶段 F：Mindmap 成为 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 读回。

## 阶段 G：Legacy 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] 先执行阶段 A：Page Aggregate 协议上移，避免页面壳继续拼第二份页面真相。
- [x] 再执行阶段 B：`page.*` 写命令切主链，确保编辑器保存、标题、设置都落到同一命令族。
- [x] 再执行阶段 C：tree realtime 消费闭环，解决页面树/文件树 runtime 不一致问题。
- [x] 然后并行准备阶段 D、E、F，但实施时分别通过独立 smoke 验收，避免 Search/AI/Mindmap 互相牵连。
- [x] 最后执行阶段 G、H：legacy gate 与设计文档状态治理必须以真实功能验收为前提。

## 全局验收矩阵

| 能力 | 关键证明 | 命令 |
| --- | --- | --- |
| Page Aggregate | core-protocol 持有 projection，3000 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] 阶段 C：Rust 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
```
