# 08 [done] Kernel 架构收口与下一阶段优先级 Review / Checklist v1

> 更新时间：2026-05-18
>
> 执行状态：`done`
>
> 关联文档：
> - `/mnt/Data1T/mnote/AGENTS.md`
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md`
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
>
> 2026-06-01 口径补充：本文是 2026-05-18 的历史 review / checklist 快照。文中把 `mnote.doc.markdown_edit` 描述为简单正文编辑主路径的结论已被后续 local-first agent 文件编辑控制面覆盖；当前 local-first 普通 Markdown 编辑主路径以 `AGENTS.md`、`ARCHITECTURE.md` 与 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 为准。`mnote.doc.markdown_edit` 只保留为 remote/cloud/compat fallback 或结构校验辅助。

---

## 1. Review 结论

当前内核架构的骨架已经基本成立，但还不能说“已完善”。

已经成立的部分：

- `tree-first graph kernel` 是长期语义事实源。
- `mnote-web` 已是 `3000` 主 Web 执行面。
- 文档页默认主编辑器已切到页面内 `leptos-tiptap` island。
- Page Aggregate 已进入 Rust-first 读取链，并已输出 `blockDocument / blockProjectionVersion / projectionSource`。
- 页面/块 AI tools 已开始走 `mnote.doc.*` / `mnote.block.*`、Page Aggregate block projection 和 Rust `EditorCommand`。

仍未完善的部分：

- Page Aggregate 仍是过渡态，block projection 主要从 `documents.content` / local markdown content 投影，不是 EditorBlockDocument 原生落库完成态。
- 标题、正文、页面设置、page tree、AI 写入口尚未完全闭环到同一组 projection / command family。
- `tree.*` 已是 preferred command name，但 `documents.*` 兼容命令面仍未完全降级。
- `/api/realtime/ws` 已是 tree realtime 主链，`/api/tree/events` 已降级为 SSE fallback；live cache 的后续重点是减少兼容 fallback 与补偿链。
- AI 块工具已有最小闭环，但当前编辑路径以块操作（blockId）为中心——AI 被迫理解 UI 层概念，且本地 `.md` 文件无 AI 写入路径。`7-14` 提出应将 AI 编辑主路径修正为 markdown 文本级（search/replace），`mnote.block.*` 降级为结构性辅助，在线 Convex 文档与本地文件共用同一条 markdown 写入路径。

补充 BlockNote / Tiptap AI 参考后的判断：

- BlockNote AI 和 Tiptap AI Toolkit 可以参考的是 AI runtime shape 与 tool contract，不是 mnote 的事实源。
- AI 方向下一步只应补基础底座：`PageAIContextBuilder`、`scope=selection`、`page_xml/text`、tool manifest annotations、`PageAIReviewSession`、accept/reject/retry/abort 状态机。
- 2026-05-18 补充（`7-18` 到 `7-25` 收口后）：AI 简单正文编辑主路径已切到 markdown 文本级 search/replace / full_content，`page_ai_workflow.rs` 当前通过模型生成 markdown 编辑意图后调用 `mnote.doc.markdown_edit`，并复用统一 mnote tool executor；`local_rule` planner（`direct_block_edit_operations`）不再作为当前 runtime 口径。在线 Convex 文档和本地 `.md` 文件共用同一条 markdown 写入合同。
- 在非 AI 主架构和 AI 基础合同打牢之前，不应扩展新的 AI agent 工作流、复杂 AI UI 或跨页面智能功能。

因此，下一阶段不应优先“大改架构”或“大量加新功能”，而应优先做：

> **架构收口 + 验收矩阵 + 定向找 bug。**

更具体地说：

> **先打牢除 AI 以外的 Page Aggregate / tree command / tree realtime 主架构，再把 AI 的基础读写、上下文、冲突与审阅底座打牢；之后才进入 AI 功能扩展。**

---

## 2. 下一步总优先级

总原则：

- 非 AI 主架构优先级高于 AI 功能扩展。
- AI 当前只推进基础设施，不推进新功能面。
- AI 基础设施必须服从 Rust kernel / Page Aggregate / EditorCommand / Hermes audit，不引入 BlockNote runtime 或 Tiptap/ProseMirror editor truth。

### P0：Page Aggregate 单一真源收口

目标：

- 标题、正文、页面设置、page tree、AI 读写入口继续收敛到同一组 Page Aggregate projection / command family。
- 前端不在页面壳、island 外侧、Sidebar preferred snapshot 外再拼第二份页面真相。
- 明确当前 block projection 过渡态和长期 EditorBlockDocument 原生落库目标之间的边界。

判断标准：

- 页面读取只走 Rust `/api/page-aggregate/:id` 正式读链。
- 页面标题、正文、页面设置写入后，Page Aggregate、页头、Sidebar、Breadcrumb、File Tree、AI fetch 回读一致。
- 失败时明确 degraded / conflict / stale，不返回看似成功的旧快照。

### P0：Tree Command Cutover

目标：

- `tree.*` 成为正式命令面。
- `documents.*` 只保留为兼容层，不再继续扩写长期业务语义。
- 页面新建、重命名、移动、归档、恢复、删除、资源生命周期都能通过正式 tree/page command 解释。

### P0：Tree Realtime Live Cache 统一

目标：

- `/api/realtime/ws` 的 snapshot / delta / resync 成为 Sidebar、Page Tree、File Tree、page subtree 的共同 live cache 来源，`/api/tree/events` 仅作为 SSE fallback。
- 减少 query/refetch/freshness 补偿链和旧快照回闪。

### P0：AI 基础工具与审阅底座

目标：

- 不继续扩新 AI 功能，先把当前 `mnote.doc.*` / `mnote.block.*` 变成可靠、可测、可审阅、可回滚的基础工具链。
- 参考 BlockNote / Tiptap 的架构形态，但保持 mnote Rust-owned tool runtime。

必须补齐：

- `PageAIContextBuilder` 与 `scope=selection`。
- `format=page_xml/text/json` 的稳定输出边界。
- tool manifest annotations：`readonly/destructive/requiresApproval/selectionEffect/runtimeOwner/writeOwner`。
- `PageAIReviewSession`：preview -> accept/reject/retry/abort。
- 多块插入的边界与 inserted block ids。
- 复杂块移动阻断矩阵。
- stale revision / stale blockRevisionRef / idempotency 重放。

明确不做：

- 不新增跨页面 AI agent 工作流。
- 不新增复杂 AI 自动化功能面。
- 不把 `@blocknote/xl-ai` 或 BlockNote `AIExtension` 作为 runtime dependency。
- 不让 AI 写入绕过 `dryRun/idempotencyKey/revision/conflictDetectionKey/revisionRef`。
- **不把 `direct_block_edit_operations` / `local_rule` planner 当作当前或长期 AI 编辑路径；简单正文编辑当前主路径是 `mnote.doc.markdown_edit`，结构性块操作才使用 `apply_block_ops` / `mnote.block.*`。**
- **不新增 `mnote.block.*` 工具；现有保留为结构性辅助。**

**2026-05-18 状态更新（`7-18` 到 `7-25` 收口后）：**

- **Phase A / B 主路径已完成当前收口**：`mnote.doc.markdown_edit` + `mnote.doc.fetch` 增强、Hermes manifest / guidance、`page_ai_workflow.rs` → `markdown_edit`、统一 mnote tool executor、local / online markdown source 合同已同步。
- **Phase C 仍冻结**：流式 apply + suggest/review 只保留设计边界，当前不实施、不扩新 AI 功能。
- 在线 Convex 文档和本地 `.md` 文件共用同一条 markdown AI 写入路径（`resolve_source` → Convex | LocalFS）。

### P1：定向 Bug Hunt

目标：

- 不做泛泛“找 bug”，只围绕已知架构风险做定向排查。

优先找：

- Page Aggregate 回流不一致。
- AI 块写入 conflict / stale revision。
- File Tree `{title}.md` 与 page title sync。
- tree stream resync / 双浏览器一致性。
- debug / compat / fallback 是否混入主链。

---

## 3. 可执行 Checklist

### 3.1 Page Aggregate 单一真源

- [x] 盘点历史 `wolai-frontend/src/lib/documents/page-aggregate-loader.ts` 是否仍有 runtime fallback 或 TS builder 读取分支。
- [x] 盘点历史 `wolai-frontend/src/lib/documents/page-aggregate-builder*` 的引用，确认只剩 test helper / historical adapter。
- [x] 检查 `/api/documents/page` 仍返回明确 `410`，不参与 runtime 主链。
- [x] 跑文档页打开 smoke，记录 `/api/page-aggregate/:id` 是首要读链。
- [x] 新建页面后检查 Page Aggregate `identity/head/body/tree/stats` 字段完整。
- [x] 修改标题后检查 Page Aggregate、页头、Breadcrumb、Sidebar、File Tree `{title}.md` 同步。
- [x] 修改正文后检查 Page Aggregate `body.revision/conflictDetectionKey/blockDocument` 同步。
- [x] 修改页面设置后检查 Page Aggregate 与 island runtime page options 同步。
- [x] 刷新页面后检查标题、正文、页面设置不回退到旧快照。
- [x] 破坏或暂停 Convex query，检查响应是 degraded/error，不返回伪 fixture。
- [x] 在 `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md` 勾选已验证项，并补证据路径。

2026-05-16 静态审计证据（历史 Next 前端退役前快照）：

- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts` 只通过 `loadPageAggregateFromRustSnapshot` 请求 Rust `/api/page-aggregate/:documentId`，`loadPageAggregate` 不再回退 TS builder；`buildServerBridgeRequest("/documents/page")` 只用于构造转发 header/request，不调用 Next `/api/documents/page` compat route。
- `rg -n "buildPageAggregateFromDocumentPayloads|page-aggregate-builder" wolai-frontend/src rust scripts design --glob '!design/05-editor-mainline/reference-code/**' --glob '!node_modules/**'` 显示 runtime 非测试引用仅剩 builder 定义；代码引用只有 `wolai-frontend/src/lib/documents/page-aggregate-builder.test.ts`。
- `wolai-frontend/src/app/api/documents/page/route.ts` 明确返回 `410`，错误文案指向 `/api/page-aggregate/:documentId`；`route.test.ts` 覆盖该行为。
- 已通过：`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`、`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate`、`cd wolai-frontend && pnpm test src/app/api/documents/page/route.test.ts src/lib/documents/page-aggregate-builder.test.ts`。
- 全仓 `git diff --check` 当前被既有删除文件 `rust/spikes/leptos-tiptap-spike/trunk-8123.err` 阻断：`fatal: unable to generate checkdiff for ...`；本轮未清理该无关脏改动，已改用限定文件 diff check 复核。

2026-05-16 真实 3000 smoke 证据：

- `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task110-page-title-single-truth-smoke.js` 通过，证据：`tmp/page-aggregate-single-truth-smoke/20260516-182244/task110.stdout.json`。该脚本创建两页、修改标题，并验证页头、Breadcrumb、Sidebar、Page Tree、File Tree 与刷新后标题一致。
- 文档页打开与 Page Aggregate snapshot 验证通过，证据：`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-open-snapshot.stdout.json`，截图：`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-open-snapshot.png`。文档响应 `x-mnote-web-owner=mnote-web`、`x-mnote-web-shell=document`，HTML 包含 `data-page-aggregate-snapshot="mnote.page_aggregate.v1"` 与 `data-page-tree-source="page_aggregate.tree.pageSubtree"`，同一临时页 `/api/page-aggregate/:id` 回读 `schema=mnote.page_aggregate.v1`。
- 新建页面 Page Aggregate 字段完整性验证通过，证据：`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-aggregate-fields.stdout.json`。`identity/head/body/tree/stats` 全部为 true，`body.blockProjectionVersion=1`，`projectionSource=documents.content`。
- 说明：曾尝试在浏览器 network 中直接捕获 `/api/page-aggregate/:id`，证据 `page-open-network.stderr.log`；当前 Rust SSR 主入口会直接把 Page Aggregate snapshot 写入 HTML，浏览器侧不必出现该 API 请求，因此该尝试不作为失败验收项。

2026-05-16 正文写入后 body 同步证据：

- 新增并运行 `scripts/task-page-aggregate-body-sync-smoke.js`，通过真实 3000 文档页输入正文，等待 `/api/documents/save` 成功后轮询 `/api/page-aggregate/:id`。
- 证据：`tmp/page-aggregate-body-sync-smoke/mp87mgz7.json`，截图：`tmp/page-aggregate-body-sync-smoke/mp87mgz7.png`，stdout：`tmp/page-aggregate-body-sync-smoke/latest.stdout.json`。
- 验证结果：`body.revision` 从 `0` 更新到 `1`，`body.conflictDetectionKey` 从 `tree_1778927703753_1:0` 更新到 `tree_1778927703753_1:1`，`body.blockProjectionVersion=1`，`projectionSource=documents.content`，`blockDocument.blocks[0]` 回读到文本 `Page Aggregate body sync mp87mgz7` 与 `revisionRef=pageRev:1:block:block_1:hash:fnv1a64:208e0e63c853eef5`。
- 配套测试已通过：`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_save_route_executes_page_body_save_command`、`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate_get_projects_legacy_content_to_block_document`。

2026-05-16 页面设置写入后 pageOptions 同步证据：

- 新增并运行 `scripts/task-page-aggregate-options-sync-smoke.js`，通过真实 3000 文档页打开页面设置，依次修改 `wideLayout=true`、`smallText=true`、`layoutDensity=compact`，等待 `/api/documents/options` 返回 `page.layout.updateOptions` 后轮询 `/api/page-aggregate/:id`。
- 证据：`tmp/page-aggregate-options-sync-smoke/mp87y6j3.json`，截图：`tmp/page-aggregate-options-sync-smoke/mp87y6j3.png`，stdout：`tmp/page-aggregate-options-sync-smoke/latest.stdout.json`。
- 验证结果：Page Aggregate `layout.pageOptions` 依次回读到 `wideLayout=true`、`smallText=true`、`layoutDensity=compact`；运行时 DOM 同步为 `data-page-wide-layout="true"`、`data-page-small-text="true"`、`data-layout-density="compact"`，island `editorRoot` 与 `.editor-surface` 同步，字号从 `16px` 到 `15px`，段落间距从 `8px` 到 `4px`。
- 配套测试已通过：`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_options_route_executes_page_layout_update_options`、`cd wolai-frontend && pnpm test src/lib/documents/page-command-client.test.ts src/components/editor/leptos-tiptap-island-editor-host.test.tsx src/lib/documents/page-option-semantics.test.ts`。

2026-05-16 刷新后标题 / 正文 / 页面设置不回退证据：

- 新增并运行 `scripts/task-page-aggregate-refresh-persistence-smoke.js`，同一临时页内依次通过 UI 写入标题、正文和页面设置，等待 Page Aggregate 回读最新 `head/body/layout` 后刷新页面，再断言页头、正文 DOM、页面设置控件、runtime DOM 与 `/api/page-aggregate/:id` 均保持最新值。
- 证据：`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json`，截图：`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.png`，stdout：`tmp/page-aggregate-refresh-persistence-smoke/latest.stdout.json`。
- 验证结果：刷新前后 `head.title=Page Aggregate refresh mp88fr6k`、`body.revision=1`、`body.conflictDetectionKey=tree_1778929070007_1:1`、`blockDocument.blocks[0].text=Page Aggregate refresh body mp88fr6k`、`layout.pageOptions.wideLayout/smallText/layoutDensity=true/true/compact`；刷新后页头标题、`.ProseMirror` 正文、设置控件、`documentElement`、`.document-shell`、island root 和 `.editor-surface` 均保持最新值。
- 修复点：`rust/crates/mnote-web/src/ssr/pages/layout.rs` 将 `initializePageUiSurfaces` 延后到 `DOMContentLoaded` 后执行，避免 layout 脚本早于嵌入 Page Aggregate JSON / island DOM 完成时把 runtime 属性按默认 pageOptions 应用。
- 2026-05-18 复测并扩展 `scripts/task-page-aggregate-refresh-persistence-smoke.js`，补充 `mnote.doc.fetch` / `tree.pageSubtree` 同组回读证据。证据：`tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.json`，截图：`tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.png`。
- 验证结果：刷新前后 `head.title=Page Aggregate refresh mpagfma1`、`body.revision=1`、`body.conflictDetectionKey=tree_1779063433221_1:1`、`blockDocument.blocks[0].text=Page Aggregate refresh body mpagfma1`、`layout.pageOptions.wideLayout/smallText/layoutDensity=true/true/compact`、`tree.pageSubtree.rootNodeId=tree_1779063433221_1` 均一致；`mnote.doc.fetch` 返回 `schema=mnote.page_ai_context.v1`，`revision/conflictDetectionKey/revisionRef/text` 与同一 Page Aggregate block 完全一致。
- 缺陷记录：`bugs/05-editor-mainline/done/5-12-page-options-refresh-runtime-attrs-v1.md`。
- 配套测试已通过：`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_options_route_executes_page_layout_update_options`。

2026-05-16 Convex query 失败时不返回伪 fixture 证据：

- 在 `rust/crates/mnote-web/src/routes/web_shell.rs` 补充负向 route 测试，构造隔离配置 `convex_url=http://127.0.0.1:9`、`allow_dev_fixtures=false`、`query_fixtures_json=None`，模拟 Convex query 不可达。
- `/api/page-aggregate/doc_1?workspaceId=ws_demo` 返回 `503 SERVICE_UNAVAILABLE`、`x-error-code=convex_unavailable`、`x-error-phase=query_send`、`x-upstream-service=convex`，body 为 `ok=false/code=convex_unavailable`，且没有 `schema` / `result`。
- `/documents/doc_1?workspaceId=ws_demo` 返回同类错误 JSON，HTML body 不包含 `mnote.page_aggregate.v1`、`data-mnote-dev-fixture`、`data-page-aggregate-snapshot`。
- 验证命令已通过：`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_errors_without_convex_or_fixture -- --nocapture`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_errors_without_convex_or_fixture -- --nocapture`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`。

验证命令建议：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate
node scripts/task110-page-title-single-truth-smoke.js
node scripts/task-page-aggregate-body-sync-smoke.js
node scripts/task-page-aggregate-options-sync-smoke.js
node scripts/task-page-aggregate-refresh-persistence-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_errors_without_convex_or_fixture
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_errors_without_convex_or_fixture
```

### 3.2 Page Block AI Tooling

- [x] 以 `7-10` 为工具执行清单、`7-12` 为当前 AI runtime 基础设计，逐 phase 检查已勾选项是否都有代码、测试、smoke 证据。
- [x] 给 `mnote.doc.fetch scope=selection` 补真实选区上下文输入和返回结构。
- [x] 给 `mnote.doc.fetch format=page_xml` 补最小 PageXML 输出。
- [x] 给 `mnote.block.fetch format=text/page_xml` 补格式分支。
- [x] 给 tool manifest 补 `annotations`，区分 readonly / destructive / requiresApproval / selectionEffect。
- [x] 定义 `mnote.page_ai_context.v1`，明确 context 只来自 Page Aggregate projection。
- [x] 定义 `mnote.page_ai_review_session.v1`，明确 accept / reject / retry / abort 语义。
- [x] 给 `mnote.block.insert_after` 补多块插入限制和返回 inserted block ids。
- [x] 给 `mnote.block.move_after` 补标题带子块阻断 smoke。
- [x] 给 `mnote.block.move_after` 补列表项阻断 smoke。
- [x] 给 `mnote.block.move_after` 补表格 / mindmap / resource 阻断 smoke。
- [x] 给 `mnote.block.replace` 补 stale revision 失败用例。
- [x] 给 `mnote.block.replace` 补 stale blockRevisionRef 失败用例。
- [x] 给写工具补重复 idempotencyKey 的端到端用例。
- [x] 检查 `mnote.page.save` 在 manifest / UI 中继续标为页面级兜底，不显示为精确块编辑主入口。
- [x] 检查任何新增 AI surface 是否只是基础 review/context/tooling 验收，不是新功能扩展。
- [x] 更新 `design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` 的每个 phase 证据。
- [x] 更新 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 的 Hermes 工具路由与审阅面 checklist。

2026-05-18 `mnote.block.insert_after` 多块插入证据：

- 代码：`rust/crates/mnote-web/src/hermes_tools/block.rs` 支持 `content` / `block` / `blocks` 输入；`blocks` 限制 `1..=20`；多块写入按输入顺序连续插入，并在结果返回 `insertedBlockIds`。
- manifest：`rust/crates/mnote-web/src/hermes_tools/manifest.rs` 已声明 `blocks.minItems=1/maxItems=20`，并用 `anyOf` 表达 `content | block | blocks` 三种输入。
- 测试：`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_insert_after -- --nocapture` 通过，覆盖多块成功和超过 20 个失败。
- 回归：`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 通过，覆盖 Hermes tools 38 项。

2026-05-18 `mnote.block.move_after` 复杂块阻断证据：

- 代码：`rust/crates/mnote-web/src/hermes_tools/block.rs` 继续以同父级、叶子块、可移动类型、editable、自身移动为真实写入前阻断条件。
- 测试：`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_move_after_blocks_complex_and_nested_blocks -- --nocapture` 通过，覆盖标题带子块、列表项、表格、mindmap、resource 均返回 `blocked=true` / `block_move_after_blocked`。
- 回归：`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 通过，当前 Hermes tools 共 39 项。

**新增（2026-05-18，`7-18` 到 `7-25` 收口状态）：**

**Phase A / B（当前主路径已收口）：**
- [x] `mnote.doc.markdown_edit` 核心实现：resolve_source → search_replace / full_content → write_target
- [x] `mnote.doc.markdown_edit` 搜索替换语义：精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败
- [x] `mnote.doc.markdown_edit` Convex 在线写入 adapter 以最终 markdown 映射写回
- [x] `mnote.doc.markdown_edit` 本地文件写入 adapter 尊重 dryRun / idempotency
- [x] `mnote.doc.fetch format: "markdown"` 增强
- [x] Hermes tool manifest 注册并描述 `mnote.doc.markdown_edit` 写入合同
- [x] 乐观锁：revision / conflictDetectionKey 前置校验
- [x] `direct_block_edit_operations` / `local_rule` 不再作为 `page_ai_workflow.rs` 当前主路径
- [x] `page_ai_workflow.rs` 改为模型产 markdown search/replace / full_content，底层走 `mnote.doc.markdown_edit`
- [x] 单元测试覆盖 markdown_edit / page_ai_workflow / Hermes guidance / ACP tool contract

**Phase C（设计冻结，不实施）：**
- [x] 流式 apply + suggest/review 设计已冻结，不阻塞 Phase A/B 实施

2026-05-18 Review Session 合同定义：

- 已在 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 定义 `mnote.page_ai_review_session.v1` 最小 schema。
- 已明确状态：`draft/planning/previewing/awaiting_user/accepted/rejected/applying/applied/failed/aborted/stale`。
- 已明确动作约束：`accept` 必须重新读 Page Aggregate 并校验 revision / conflictDetectionKey / revisionRef，`reject/abort` 不产生写入，`retry` 必须生成新 proposal 或 dry-run preview，yolo 模式也应生成同构 audit 数据。
- 边界：这里只完成合同定义，不实施 Phase C 流式 apply 或新的审阅 UI。

2026-05-16 Page Block AI context / format focused smoke 证据：

- 新增并运行 `scripts/task-page-block-ai-context-format-smoke.js`，通过真实 3000 + 测试账号创建临时页，用 `mnote.page.save` 初始化 `p_1/p_2/p_3`，再验证 Hermes mnote tools 的 context / format / annotations / selection guard。
- 证据：`tmp/page-block-ai-context-format-smoke/mp8ddr4n.json`，截图：`tmp/page-block-ai-context-format-smoke/mp8ddr4n-page.png`。
- 验证结果：
  - `GET /api/hermes/tools/mnote/manifest` 中 `mnote.doc.fetch`、`mnote.block.fetch`、`mnote.block.replace`、`mnote.page.save` 均包含 `readonly/destructive/idempotent/requiresApproval/approvalMode/runtimeOwner/writeOwner/selectionEffect`；`mnote.page.save` 明确 `destructive=true`、`approvalMode=yolo`，继续定位为页面级粗粒度兜底。
  - `mnote.doc.fetch scope=selection selectedBlockIds=["p_2"] format=page_xml` 返回 `schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds=["p_2"]`、`revision/conflictDetectionKey`、`revisionRef`，`content` 只包含 `p_2` 与 `第二段 mp8ddr4n`，不包含未选中的 `p_1/p_3`。
  - `mnote.doc.fetch scope=selection format=text` 返回 `[p_2] 第二段 mp8ddr4n`，不包含未选中块。
  - `mnote.block.fetch blockId=p_2 format=page_xml/text` 返回目标块、`revisionRef` 和同父级 before/after 上下文 `p_1/p_3`。
  - `mnote.doc.apply_block_ops dryRun=true allowedTargetBlockIds=["p_2"]` 尝试 replace `p_1` 被拒绝，HTTP `400`，错误码 `mnote_block_target_out_of_scope`。
- 边界：本轮只验证 `mnote.doc.apply_block_ops` 的 selection scope guard；单个 `mnote.block.replace/insert_after/move_after` 尚未校验 `allowedTargetBlockIds`，不能据此勾选完整 selection 写保护矩阵。
- 配套验证已通过：`node --check scripts/task-page-block-ai-context-format-smoke.js`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture`、`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-context-format-smoke.js`。

2026-05-16 / 2026-05-18 Page Block AI stale / idempotency focused smoke 证据：

- 新增并运行 `scripts/task-page-block-ai-conflict-idempotency-smoke.js`，通过真实 3000 + 测试账号创建临时页，用 `mnote.page.save` 初始化 `p_1/p_2`，再围绕 `mnote.block.replace` 验证冲突和幂等安全边界。
- 证据：`tmp/page-block-ai-conflict-idempotency-smoke/mp8dyqiq.json`，截图：`tmp/page-block-ai-conflict-idempotency-smoke/mp8dyqiq-page.png`。
- 最新复测证据：`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs.json`，截图：`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs-page.png`。
- 验证结果：
  - 首次 `mnote.block.replace` 携带最新 `revision/conflictDetectionKey/blockRevisionRef/idempotencyKey` 成功写入 `p_2`，返回 `commandName=page.body.save` 与 `commandId=page_body_save_req_1778938353824_11`。
  - 使用同一 `idempotencyKey=idem_conflict_replace_mp8dyqiq` 再次调用 `mnote.block.replace`，即使请求 content 改成不同文本，也 replay 同一 `commandId`，Page Aggregate 回读 `p_2` 仍为首次写入文本，`revisionAfterFirst=2`、`revisionAfterReplay=2`，确认不重复写入。
  - 使用旧 `revision/conflictDetectionKey` 调用 `mnote.block.replace` 返回 HTTP `400`、错误码 `mnote_tool_conflict`，正文保持首次写入结果。
  - 使用最新 `revision/conflictDetectionKey` 但旧 `blockRevisionRef` 调用 `mnote.block.replace` 返回 HTTP `400`、错误码 `mnote_tool_conflict`，正文保持首次写入结果。
  - 2026-05-18 复测中，重复 `idempotencyKey=idem_conflict_replace_mpafrevs` replay 同一 `commandId=page_body_save_req_1779062303997_525`，`revisionAfterFirst=2`、`revisionAfterReplay=2`；旧 revision 与旧 blockRevisionRef 仍均返回 `mnote_tool_conflict`。
- 边界：本轮只覆盖 `mnote.block.replace` 直接 tool executor 的 stale revision / stale blockRevisionRef / idempotency replay；不代表 `mnote.block.insert_after` 幂等矩阵、所有写工具幂等矩阵、review session accept stale 或 accept/reject/retry/abort 已完成。
- 配套验证已通过：`node --check scripts/task-page-block-ai-conflict-idempotency-smoke.js`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture`、`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-conflict-idempotency-smoke.js`。

验证命令建议：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime editor_document
node scripts/task-page-block-ai-tools-smoke.js
```

### 3.3 Tree Command Cutover

- [x] `rg -n "documents\\.create|documents\\.title\\.update|documents\\.move|documents\\.archive|documents\\.restore" rust wolai-frontend` 盘点旧命令引用。
- [x] 将仍在 runtime 主链的旧命令按 owner 分类：必须迁移、兼容保留、测试 fixture。
- [x] 确认页面新建默认输出 `tree.node.create` 或正式 tree command。
- [x] 确认页面重命名默认输出 `tree.node.rename` 或正式 page/tree command。
- [x] 确认移动页面默认输出 `tree.subtree.move`。
- [x] 确认归档 / 恢复 / 永久删除的 command family 与 resource lifecycle 设计一致。
- [x] 对 compat alias 返回增加 owner / deprecated 标识，避免被当作主链。
- [x] 更新 `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md` 或新增后续 process checklist。

2026-05-18 当前 Tree Command Cutover 状态：

- `3000` 当前主壳由 Rust `mnote-web` SSR 承载；主要页面生命周期动作通过 Rust `/api/tree/commands` route 生成 `tree.node.create`、`tree.node.rename`、`tree.subtree.move`、`tree.node.archive`、`tree.node.restore`、`tree.node.purge`、`tree.subtree.copy`。
- `bridge-runtime` 仍接受历史 `documents.*` alias，但 execution plan / artifact 会标记 `preferredCommandName`、`compatCommandName`、`deprecatedAlias`，避免兼容命令被当成当前主链。
- `wolai-frontend` / Next tree command route 只作为退役前历史审计材料，不再作为当前运行时主链依据。

2026-05-16 Tree Command Cutover 静态盘点与 alias 标识证据（历史 Next 前端退役前快照）：

- 只读盘点命令：`rg -n "documents\\.(create|title\\.update|move|archive|restore|delete|purge|copy_tree)" rust wolai-frontend scripts --glob '!node_modules/**'`。
- 退役前 Next 前端主链：`wolai-frontend/src/lib/documents/tree-command-client.ts` 的新建、重命名、移动、归档、恢复、永久删除和复制均 POST `/api/tree/commands`，返回 meta 使用 `TREE_COMMAND_PROTOCOL.*.preferredCommandName`；`TREE_COMMAND_PROTOCOL` 中的 `documents.*` 只保留为 `compatCommandName`。
- 退役前 Next tree command route：`wolai-frontend/src/app/api/tree/commands/route.ts` 在 create / move / rename / archive / restore 分支分别构造 `tree.node.create`、`tree.subtree.move`、`tree.node.rename`、`tree.node.archive`、`tree.node.restore`。
- Rust Web tree route：`rust/crates/mnote-web/src/routes/tree.rs` 的 `create_command_wire` 输出 `tree.node.create`、`tree.node.rename`、`tree.subtree.move`、`tree.node.archive`、`tree.node.restore`、`tree.node.purge`、`tree.subtree.copy`；`tree_command` route 只接收 action，不接收旧 `documents.*` command name 作为主链输入。
- 兼容保留：`bridge-runtime` 仍接受 `documents.create/title.update/move/delete/restore/purge/copy_tree`，但本轮已在对应 execution plan 的 `args_json.commandProtocol` 增加 `family=tree`、`owner=rust-runtime-kernel`、`preferredCommandName`、`compatCommandName`、`deprecatedAlias`；旧 `documents.*` alias 会标记 `deprecatedAlias=true`。
- Transport 边界：`rust/crates/mnote-web/src/transport/convex.rs` 发送给 Convex legacy mutation 前会剥离 `commandProtocol`、`streamDeltaHint`、`domainEventHint`、`domainEventPlan(s)`，避免 legacy validator 把审计字段当写入参数。
- 仍保留为后续兼容收口点：`wolai-frontend/src/app/api/documents/create-child/route.ts` / `page-command-adapter.ts` 仍构造 `documents.create`，当前分类为 compat-only；`mnote-cli` 仍有历史 `documents.*` CLI 构造，不属于 3000 主交互链。

2026-05-16 Tree Command Cutover 验证命令：

```bash
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime tree_ -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_archive_artifacts_for_legacy_mutation -- --nocapture
```

验证命令建议：

```bash
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime tree_command
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command
node scripts/task122-rust-web-create-page-ui-smoke.js
```

### 3.4 Tree Realtime Live Cache

- [x] 2026-05-17 WS push 迁移（`57ec8322`）：`/api/realtime/ws` 成为主 transport，SSE 降级为 fallback。mutation 后通过 `stream_delta_tx` broadcast channel 推送 delta，WS 客户端零 Convex 查询。
- [x] 盘点 Sidebar、Page Tree、File Tree、page subtree 仍依赖 query/refetch/freshness 补偿的位置。
- [x] 确认 `/api/realtime/ws`（主）和 `/api/tree/events` SSE fallback 的 snapshot / delta / resync payload 覆盖 page/file/resource row。
- [x] 新建页面后，双浏览器 A/B 检查另一端无需刷新出现页面。
- [x] 重命名页面后，双浏览器 A/B 检查 Sidebar / Breadcrumb / File Tree 一致更新。
- [x] 移动页面后，双浏览器 A/B 检查 tree order 不回闪。
- [x] 删除 / 恢复后检查 trash 与主树事件一致。
- [x] 断开 WS / SSE fallback 后恢复，检查 resync 能把 UI 拉回正确状态。
- [x] 更新 `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md` 的已验证项。

2026-05-18 当前状态更新：

- `/api/realtime/ws` 已成为 Rust SSR 主壳默认 transport（`convex-command-log-ws`），`/api/tree/events` 仅作为 SSE fallback。
- `CURRENT_ARCHITECTURE.md` 与 `bugs/03-rust-web/done/3-16` 到 `3-18` 已同步：原“WS 文档口径 / 前端 SSE 实现”冲突、SSE push 跳过 polling safety net、WS/SSE delta 合同分裂均已修复。
- 2026-05-16 下面的 SSE smoke 证据保留为迁移前历史验证材料，不再代表当前主链。

2026-05-16 Tree Realtime Live Cache 静态盘点与 payload 覆盖证据（WS 迁移前历史快照）：

- 退役前只读审查确认 React `AppLayoutShell` 同时接入 `useSidebarData` 与 `useSidebarTreeStream`，`usePreferredSidebarSnapshot` 负责 freshness 仲裁；Page Tree / File Tree 通过 preferred snapshot 消费 `kernelSidebarTree` 与 `kernelFileTreeProjection`。
- 退役前未统一的补偿链：`useSidebarData` 还保留 Convex query、HTTP `/api/sidebar` fallback 与手动 `refetch`；mutation 后仍有 `refreshTree` / `sidebarQuery.refetch`；Rust SSR shell 与 React hook 当时各自可建立 EventSource；page subtree 当时仍从 Page Aggregate client state 派生。
- Rust stream 历史盘点确认 workspace snapshot 已同时加载 `KernelProjectionKind::SidebarTree` 与 `KernelProjectionKind::FileTree`，SSE payload 的 `data.dataset.kernel_sidebar_projection` / `data.dataset.kernel_file_tree_projection` 覆盖 page/file row；subtree snapshot 当时只覆盖 `page_tree`，不含 file tree projection。
- 本轮新增 `stream_change_preserves_remove_asset_delta_fields`，确认 `tree.resource.delete` 的 `remove_asset` delta 字段保真；`structural_delta_requires_projection_snapshot` 已覆盖 `remove_asset` 需要 projection snapshot，避免资源删除类事件只靠局部 patch。
- 本轮强化 `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`：解析 SSE `snapshot` data，断言 `kind=snapshot`、`stream=workspace`、`projection=sidebar_tree`、`x-mnote-tree-stream-owner=rust-web`，并确认 workspace snapshot 中有 `kernel_sidebar_projection`、`kernel_file_tree_projection` 和临时页 `doc:<documentId>` file tree row。

2026-05-16 Tree Realtime Live Cache 验证命令：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web stream_change_preserves_remove_asset_delta_fields -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web structural_delta_requires_projection_snapshot -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::stream_support -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_events -- --nocapture
cd wolai-frontend && pnpm test src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/lib/tree-stream/use-sidebar-tree-stream.test.tsx
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
```

3000 smoke 证据：

- `tmp/tree-live-cache-smoke/20260516-task123/task123.stdout.json`，结果 `owner=rust-web`、`stream=/api/tree/events`、`snapshotProjection=sidebar_tree`、`fileTreeRows=59`。

2026-05-16 双浏览器 no-refresh 验证与 purge 修复证据：

- 复用 `scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js` 做真实 3000 双浏览器验证：A 端通过真实 auth + `/api/tree/commands` 创建页面、归档、恢复、彻底删除与清空垃圾箱；B 端同时打开 File Tree 与 Trash，断言目标行无刷新出现 / 消失，且 `navigationEvents` 在初始打开后为空。
- 修复前该 smoke 在 `purge-visible-on-b` 前失败：`documents:purge` 收到 artifact-only 字段 `commandProtocol`，Convex legacy validator 返回 `ArgumentValidationError: Object contains extra field commandProtocol`。
- 修复点：`rust/crates/mnote-web/src/transport/convex.rs` 将 `tree.node.purge` / `documents.purge` 加入 `strip_tree_artifact_fields` 剥离范围，避免 compat mutation 接收 tree command audit 字段。
- 缺陷记录：`bugs/04-tree-domain/done/4-41-tree-node-purge-command-protocol-leaks-to-convex-v1.md`。
- 修复后 `task432` 通过，证据 `tmp/tree-live-cache-smoke/20260516-task432/result.json`：`ok=true`，`create-visible-on-b` 通过 `tree:resync` 出现在 B 端 File Tree；`archive-visible-on-b` 通过 `tree:delta remove_document` 同步 File Tree 与 Trash；`restore-visible-on-b` 通过 `tree:delta upsert_document` 同步恢复；`purge-visible-on-b` 与 `empty-trash-visible-on-b` 通过 `tree:resync` 拉回正确状态。

2026-05-16 purge 修复验证命令：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_purge_artifacts_for_legacy_mutation -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_archive_artifacts_for_legacy_mutation -- --nocapture
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
```

2026-05-16 双浏览器 rename live cache 修复与验证记录：

- 新增 regression smoke：`scripts/task446-tree-rename-dual-browser-live-smoke.js`。脚本使用 A/B 独立 browser context，A 端通过正式 `/api/tree/commands` 创建并执行 `{ action: "rename", workspaceId, documentId, title }`，B 端分别保持目标文档页与 File Tree 页面不刷新，记录 `tree:snapshot/tree:delta/tree:resync`、`/api/tree/events` 请求、DOM 状态、导航事件与截图路径。
- RED 证据：修复前真实 3000 smoke 失败，B 端已收到 `tree:delta`、`op=upsert_document`、`liveApplied=delta`、`liveError=""`，Sidebar / Page Tree 与 File Tree `{renamedTitle}.md` 已更新，但当前打开目标文档页的 `titleInputValue`、Breadcrumb 与 `document.title` 仍停留旧标题，导致 `waitForBDocumentRename` 超时。
- 根因：Rust SSR 文档 shell 的 `updateTitleEverywhere` 已更新 Page Tree / File Tree 行标题，但 title-only `upsert_document` 未同步当前文档页 chrome（页头标题输入框、Breadcrumb、`document.title`）。
- 修复点：`rust/crates/mnote-web/src/ssr/pages/layout.rs` 的 `updateTitleEverywhere(documentId, title)` 现在同步当前文档 chrome，并保持 scoped tree row 更新；字符串合同单测新增当前页 title input 与 Breadcrumb selector 覆盖。
- 构建阻断补齐：当前工作区已有 `AppState.editor_actor` / `AppConfig.enable_editor_actor` 脏改动但缺少真实 `editor_actor.rs` 文件，导致 mnote-web 无法编译启动；本轮把 misplaced 的 `EditorRuntimeActor` 实现补到 `rust/crates/mnote-web/src/editor_actor.rs`，并补齐测试配置中的 `enable_editor_actor` 字段，以恢复 3000 验证入口。
- GREEN 证据：重新启动最新 `desktop:hot` 后运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task446-tree-rename-dual-browser-live-smoke.js` 通过。证据 `tmp/tree-live-cache-smoke/20260516-task446-rename/result.json`，截图 `tmp/tree-live-cache-smoke/20260516-task446-rename/b-document-after-rename.png` 与 `tmp/tree-live-cache-smoke/20260516-task446-rename/b-filetree-after-rename.png`。
- 验证结果：B 文档页 `documentTitle/titleInputValue/breadcrumbTitle/sidebarTitle` 均为新裸标题；B File Tree `fileTreeTitle={renamedTitle}.md`；两端 `liveApplied=delta`、`liveError=""`；rename 后 `navigationEvents=[]`，确认 B 端无刷新 / 无导航。
- 缺陷记录：`bugs/05-editor-mainline/done/5-13-tree-rename-live-document-chrome-stale-v1.md`。

2026-05-16 双浏览器 move order live cache 修复与验证记录：

- 新增 regression smoke：`scripts/task447-tree-move-order-dual-browser-live-smoke.js`。脚本使用 A/B 独立 browser context，A 端创建同父级 `A/B/C` 后执行正式 `/api/tree/commands`：`{ action: "move", documentId: C, parentId: root, sortOrder: 1 }`，B 端分别保持目标文档页与 File Tree 页面不刷新，记录 direct child order、tree events、导航事件与截图路径。
- RED 证据：合法 `sortOrder=1` 下修复前真实 3000 smoke 失败。B 端收到 `tree:delta move_document`，payload 含 `sortOrder=1`，`liveApplied=delta` 且 `liveError=""`，但 Page Tree / File Tree DOM 顺序仍为 `A/B/C`，导致 `waitForExpectedOrder` 超时。
- 根因：Rust SSR 文档 shell 的 `applyMoveDocumentDelta(data)` 未读取 `sortOrder`，`moveDocumentRowForMode(mode, documentId, parentId)` 固定 `appendChild` 到目标父节点末尾；`tree:local-command` optimistic move 分支也未传 `body.sortOrder`。
- 修复点：`rust/crates/mnote-web/src/ssr/pages/layout.rs` 增加 `sortOrderFromDelta` 与 `insertTreeNodeAtSortOrder`，让 live delta 与 local command move 按目标父节点直系 sibling index 插入；`rust/crates/mnote-web/src/routes/tree.rs` 补充 route/artifact 单测断言 `sortOrder` 保留在 result 与 `streamDelta`。
- GREEN 证据：重启最新 `desktop:hot` 后运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task447-tree-move-order-dual-browser-live-smoke.js` 通过。证据 `tmp/tree-live-cache-smoke/20260516-task447-move-order/result.json`，截图 `tmp/tree-live-cache-smoke/20260516-task447-move-order/b-document-after-move.png` 与 `tmp/tree-live-cache-smoke/20260516-task447-move-order/b-filetree-after-move.png`。
- 验证结果：B 文档页与 B File Tree 页面中的 Page Tree / File Tree direct child order 均从 `A/B/C` 变为 `A/C/B`；两端 `liveApplied=delta`、`liveError=""`；move 后 `navigationEvents=[]`，确认无刷新 / 无导航。
- 缺陷记录：`bugs/04-tree-domain/done/4-42-tree-move-live-delta-sort-order-ignored-v1.md`。

2026-05-16 SSE resync / reconnect recovery 验证记录：

- 新增 `scripts/task448-tree-resync-recovery-dual-browser-smoke.js`，通过 B 端 EventSource URL 注入 `pollMs=5000`，A 端在同一 poll 间隔内连续创建两个子页，强制 `/api/tree/events` 进入非单条 delta 的 `event: resync` 分支。
- `task448` 验证结果：B 文档页与 B File Tree 页面均收到 `tree:resync`，`liveApplied=resync`、`liveError=""`，Page Tree / File Tree direct child order 从仅有初始子页恢复为包含新增 `b/c` 两个子页，`navigationEvents=[]`。证据 `tmp/tree-live-cache-smoke/20260516-task448-resync/result.json`，截图 `tmp/tree-live-cache-smoke/20260516-task448-resync/b-document-after-resync.png` 与 `tmp/tree-live-cache-smoke/20260516-task448-resync/b-filetree-after-resync.png`。
- 新增 `scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`，通过 Playwright `context.setOffline(true)` 模拟 B 端 SSE 断线，A 端离线期间连续创建两个子页，B 端恢复在线后等待 EventSource 恢复事件把 UI 拉回最新。
- `task449` 验证结果：B 文档页与 B File Tree 页面恢复后 `liveStatus=connected`、`liveApplied=resync`、`liveError=""`，新增 `b/c` 子页出现在 Page Tree 与 File Tree，`navigationEvents=[]`。证据 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/result.json`，截图 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/b-document-after-reconnect.png` 与 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/b-filetree-after-reconnect.png`。
- 说明：当前 Rust SSR controller 的真实恢复合同是“断线期间错过多条变化后，恢复时通过 snapshot/resync 类完整投影拉回 UI”；本轮实测恢复事件为 `tree:resync`，不是浏览器刷新或导航。

验证命令建议：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_events
node --check scripts/task447-tree-move-order-dual-browser-live-smoke.js
node --check scripts/task448-tree-resync-recovery-dual-browser-smoke.js
node --check scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js
node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
node scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
node scripts/task446-tree-rename-dual-browser-live-smoke.js
node scripts/task447-tree-move-order-dual-browser-live-smoke.js
node scripts/task448-tree-resync-recovery-dual-browser-smoke.js
node scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js
```

### 3.5 定向 Bug Hunt

- [x] 建立 bugs 分类：Page Aggregate 问题归 `bugs/05-editor-mainline/process/`。
- [x] Tree command / realtime / File Tree 问题归 `bugs/04-tree-domain/process/`。
- [x] AI tools 问题归 `bugs/07-ai/process/`。
- [x] 每个 bug 必须包含复现步骤、期望、实际、证据截图或 JSON、owner 判断。
- [x] 先补最小 failing smoke 或定向单测，再修实现。
- [x] 修复后移动到对应 `done/`，并记录验证命令。

重点 bug 方向：

- [x] Page Aggregate 与页头标题不一致。
- [x] Page Aggregate 与 File Tree `{title}.md` 不一致。
- [x] AI 写入成功但 Page Aggregate 回读旧内容。
- [x] stale revision 未阻断写入。
- [x] tree stream 断线恢复后 UI 停在旧快照。
- [x] 旧 compat / debug route 在 3000 首屏被误用。

2026-05-18 定向 bug hunt 收口：

- `find bugs -path '*/process/*' -type f | wc -l` 结果为 `0`。
- 本轮 P0 / 内核优先缺陷已迁入 `bugs/*/done/`，并在 `CURRENT_ARCHITECTURE.md`、`design/10-review/done/10-current-mnote-ai-runtime-review-v1.md`、`design/10-review/done/11-current-full-architecture-review-v1.md` 中同步验证证据。

### 3.6 2026-05-16 暂停交接进展摘要

本轮按用户要求暂停继续实现，只记录当前进展与恢复点；未继续推进 `3.2 Page Block AI Tooling` 的实现或 smoke。

已完成到可继续接力的阶段：

- `3.1 Page Aggregate 单一真源` 已完成本 checklist 中的当前验证项，并补齐真实 3000 smoke、Rust route 测试、前端单测和失败降级证据。关键证据包括 `tmp/page-aggregate-single-truth-smoke/20260516-182244/`、`tmp/page-aggregate-body-sync-smoke/mp87mgz7.json`、`tmp/page-aggregate-options-sync-smoke/mp87y6j3.json`、`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json`。
- `3.3 Tree Command Cutover` 已完成当前收口：3000 主链使用 `tree.*` preferred command，`documents.*` 保留为 compat alias，并在 Rust execution plan / artifact 中标记 owner、preferredCommandName、compatCommandName、deprecatedAlias。
- `3.4 Tree Realtime Live Cache` 已完成本轮重点验证与修复：snapshot/delta/resync payload 覆盖、双浏览器新建 / 归档 / 恢复 / purge、rename 当前文档 chrome 同步、move sortOrder live 排序、SSE resync 与 reconnect recovery。
- 本轮新增或更新的 Tree Realtime 关键证据：
  - `tmp/tree-live-cache-smoke/20260516-task432/result.json`
  - `tmp/tree-live-cache-smoke/20260516-task446-rename/result.json`
  - `tmp/tree-live-cache-smoke/20260516-task447-move-order/result.json`
  - `tmp/tree-live-cache-smoke/20260516-task448-resync/result.json`
  - `tmp/tree-live-cache-smoke/20260516-task449-reconnect/result.json`
- 本轮已归档的缺陷：
  - `bugs/04-tree-domain/done/4-41-tree-node-purge-command-protocol-leaks-to-convex-v1.md`
  - `bugs/04-tree-domain/done/4-42-tree-move-live-delta-sort-order-ignored-v1.md`
  - `bugs/05-editor-mainline/done/5-12-page-options-refresh-runtime-attrs-v1.md`
  - `bugs/05-editor-mainline/done/5-13-tree-rename-live-document-chrome-stale-v1.md`

当前暂停点：

- 已进入 `3.2 Page Block AI Tooling` 的只读审计阶段，但尚未勾选 3.2 的任何新 checkbox。
- 已确认现有代码中 `mnote.doc.fetch` 已支持 `scope=selection`、`format=page_xml/text/markdown/json`、`schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds`、truncation/warnings/continuation 等基础字段；`mnote.block.fetch` 已支持 `format=page_xml/text`；manifest 已包含 `readonly/destructive/idempotent/requiresApproval/approvalMode/runtimeOwner/writeOwner/selectionEffect` 等 annotations。
- 已确认 `scripts/task-page-block-ai-tools-smoke.js` 覆盖基础块工具闭环，但还没有专门覆盖 `scope=selection`、`page_xml/text`、manifest annotations、选区外写入阻断、review session / conflict / idempotency 的完整验收。
- 用户目标中提到的 `design/07-ai/process/7-11-blocknote-tiptap-ai-reference-and-mnote-ai-tool-runtime-v1.md` 当前在 `design/07-ai/process/` 下不存在；实际可读历史参考位于 `design/old/07-ai/process/7-11-blocknote-tiptap-ai-reference-and-mnote-ai-tool-runtime-v1.md`，当前执行口径应继续以 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 为准。

恢复时的下一步建议：

- 继续从 `3.2 Page Block AI Tooling` 开始，不要跳到定向 bug hunt。
- 优先新增一个聚焦 smoke，例如 `scripts/task-page-block-ai-context-format-smoke.js`，只验证基础底座，不扩新 AI 功能：
  - manifest annotations 中 `mnote.doc.fetch` / `mnote.block.fetch` / `mnote.block.replace` / `mnote.page.save` 的 readonly、destructive、requiresApproval、selectionEffect、owner 字段。
  - `mnote.doc.fetch scope=selection selectedBlockIds=[...] format=page_xml` 只返回选区块，返回 `schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds`、`revisionRef`。
  - `mnote.doc.fetch scope=selection format=text` 不包含未选中块。
  - `mnote.block.fetch format=page_xml/text` 返回稳定块内容和 `revisionRef`。
  - 选区冻结后写工具尝试修改 `allowedTargetBlockIds` 外块时返回 `mnote_block_target_out_of_scope`。
- 配套验证建议从这些命令开始：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture
node --check scripts/task-page-block-ai-context-format-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-context-format-smoke.js
```

交接注意事项：

- 当前工作区有大量既有未提交改动和文件迁移，继续执行时必须只碰当前小项相关文件，不要回滚、删除或清理无关脏文件。
- `git diff --check` 全仓可能仍会被既有删除文件阻断；验证本轮改动时可先用限定路径或 `git diff --no-index --check /dev/null <new-file>`。
- `rust/crates/mnote-web/src/ssr/pages/layout.rs` 中已有 Tree Realtime rename / move order 相关改动，同时可能混有先前 editor actor 相关改动；继续改动前必须重新读 diff，避免覆盖用户或前序 agent 的更改。
- `3000` 相关 smoke 必须继续保留 JSON / 截图证据路径，并在本 checklist 与对应 `design/07-ai` 或 `bugs/*` 文档同步记录。

---

## 4. 当前不优先做

- [x] 不优先增加新的编辑器 UI 大功能。
- [x] 不优先新增 AI Agent 工作流功能。
- [x] 不优先扩展 AI 功能面；AI 只做基础上下文、工具合同、审阅会话、冲突与回滚。
- [x] 不优先大规模替换 Convex。
- [x] 不优先重写 `leptos-tiptap` 输入层。
- [x] 不把 `mnote.page.save` 包装成精确块编辑长期方案。
- [x] 不在 compat route 继续扩写长期业务语义。
- [x] 不把 BlockNote / Tiptap AI runtime 作为 mnote runtime 依赖。
- [x] 不把 `direct_block_edit_operations` / `local_rule` planner 当作当前或长期 AI 编辑路径；简单正文编辑当前主路径是 `mnote.doc.markdown_edit`。
- [x] 不新增 `mnote.block.*` 工具；`mnote.block.*` 保留为结构性辅助，不做块操作向新工具的扩张。

2026-05-18 静态边界核验证据：

- `rg -n "mnote\\.block\\.[a-zA-Z0-9_]+" rust/crates/mnote-web/src/hermes_tools rust/crates/mnote-web/src/routes/hermes_tools.rs scripts/reasonix-acp-wrapper.mjs | ... | sort -u` 仅发现既有 `mnote.block.fetch/delete/replace/insert_after/move_after`，本轮未新增 `mnote.block.*` 工具。
- `page_ai_workflow.rs` 注释与当前实现仍指向 `mnote.doc.markdown_edit`；`direct_block_edit_operations` 只保留为历史/测试辅助，不作为当前主路径。
- `mnote.page.save` 在 manifest / 7-10 / 7-12 / 本 checklist 中继续描述为页面级粗粒度兜底，不是精确块编辑主入口。
- 未引入 `@blocknote/xl-ai`、BlockNote `AIExtension` 或新的 AI Agent workflow；本轮改动仅限 Hermes tools 合同、阻断矩阵测试和 checklist 证据。

2026-05-18 `7-12` runtime 口径闭合证据：

- `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 已明确 `7-11` 旧“自有 AI runtime”口径移入 `design/old/07-ai/process/`，当前执行口径以 `7-12` 为准。
- `7-12` 已定义 `PageAIContextBuilder`、`MnoteAIToolManifestProvider`、`PageAICommandRouter`、`PageAIReviewSession` 的边界，并明确 Phase C review / streaming apply 只做设计冻结，当前不实施。
- `7-12` Phase B 到 Phase F 保留 manifest / router / review session / event / smoke 的后续 checklist；其中 selection 外写入 blocked 已有真实 smoke 证据，其余未实施项继续保留未勾。

2026-05-18 `7-10` 剩余真实页面矩阵拆分：

- 新增 `design/07-ai/process/7-16-page-block-ai-real-smoke-followup-matrix-v1.md`，专门承接 `7-10` 中仍未完成的真实 3000 smoke 和 UI/review 边界矩阵。
- `7-10` 继续保留为页面块 AI 工具执行总 checklist；`7-16` 只用于后续真实页面证据补齐，不扩新 AI 功能。

2026-05-18 真实页面 insert_after smoke 补充：

- `scripts/task-page-block-ai-tools-smoke.js` 增加 `PLAYWRIGHT_CHROME_EXECUTABLE` 支持，用系统 Chromium 运行，避免当前系统无法下载 Playwright 官方 chromium 的阻断。
- 命令：`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
- 证据：`tmp/page-block-ai-tools-smoke/mpag4966.json`，截图：`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
- Page Aggregate：`tmp/hermes-tester/page-block-ai-tools-mpag4966/page-aggregate.json`，doc/fetch/find：`tmp/hermes-tester/page-block-ai-tools-mpag4966/doc-fetch-find.json`。
- 覆盖：`mnote.block.insert_after` 插入 `todo` 块、插入顺序、`mnote.block.fetch` 回读新块、重复 `idempotencyKey` 不二次插入、刷新后文本可见。
- 覆盖：`table_1/mindmap_1/resource_1` 在 AI block projection 中返回 `editable=false` 与 `unsupportedReason=复杂块暂不开放 AI 精确写入`；`heading_parent/list_item_1/table_1/mindmap_1/resource_1` 的 `plan_update block_move_after` dry-run 与正式 `mnote.block.move_after` 均阻断，阻断后 revision / 正文不变化。

2026-05-18 真实页面 doc.fetch / doc.find / block.fetch smoke 补充：

- 命令：`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
- 证据：`tmp/page-block-ai-tools-smoke/mpag4966.json`，截图：`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
- 覆盖：`doc.fetch full`、`doc.fetch outline`、`doc.find` 定位 `p_2`、`maxBlocks=2` 截断、`block.fetch includeChildren=true contextBefore=1 contextAfter=1` 返回 `revisionRef` 与同父级 before/after；复杂块投影不伪装完全可编辑。

2026-05-18 真实页面 plan_update / replace / move smoke 补充：

- 命令：`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
- 证据：`tmp/page-block-ai-tools-smoke/mpag4966.json`，截图：`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
- 覆盖：`block_replace/block_insert_after/block_move_after` dry-run、dry-run 不写入、blocked 场景、replace 相邻块不变与 id 保持、replace 后 Page Aggregate 回读持久化、复杂块 move 阻断、普通块 move dry-run 与正式移动、moving block id 保持、刷新后 block ids 稳定。

---

## 5. 完成定义

本 checklist 不能迁入 `done/`，直到：

- [x] Page Aggregate 的标题 / 正文 / 页面设置 / page tree / AI fetch 回读形成同一组可验证真相。
- [x] `7-10` 的 Page Block AI Tooling 剩余矩阵完成或明确拆出后续 process 文档。
- [x] `7-12` 的 AI 基础 runtime 口径完成：context、format、manifest annotations、review session、状态机边界都有代码或明确后续 checklist。
- [x] 7-14 v2 Phase A + B 当前主路径完成（`mnote.doc.markdown_edit` + `mnote.doc.fetch` 增强 + `page_ai_workflow.rs` 收口）；Phase C 设计已冻结。设计稿修正已由 7-14 v2 与 `7-18` 到 `7-25` bug 修复共同完成。
- [x] `tree.*` command 面对主要页面生命周期动作成为唯一 preferred runtime 主链。
- [x] tree realtime live cache 覆盖 Sidebar、Page Tree、File Tree、page subtree 的关键写后更新。
- [x] 至少一轮定向 bug hunt 完成，所有 P0/P1 blocker 已归档到 `bugs/*/done/` 或明确保留为后续 process。

---

## 6. 给后续 /goal 的持续执行 Prompt

```text
/goal objective: 持续核验 /mnt/Data1T/mnote/design/10-review/done/08-kernel-architecture-next-priority-review-and-checklist.md 的已归档结论，按 P0 -> P1 顺序推进 MNOTE 架构防回归、AI 基础底座和定向 bug hunt。每轮开始先读取 /home/lix/.codex/memories/PROFILE.md 与 ACTIVE.md，再读取 AGENTS.md、ARCHITECTURE.md、design/01-05-current-priority-overview.md、本 checklist、design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md、design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md、design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md。必须保护用户已有未提交改动，不回滚、不覆盖、不删除无关文件。优先使用多个 subagent 并行做只读审查和浏览器验证，主线程只整合证据和做小范围实现。执行顺序固定为：1) Page Aggregate 单一真源防回归；2) tree command cutover 防回归；3) tree realtime live cache 防回归；4) AI 基础工具与审阅底座，当前简单正文编辑主路径已是 `mnote.doc.markdown_edit` + `mnote.doc.fetch` + `page_ai_workflow.rs` 统一 tool executor，Phase C（流式 apply + suggest/review）仅设计冻结不实施；继续补 context/selection/page_xml/tool annotations，不扩新 AI 功能；5) 定向 bug hunt。每完成一个小项都要更新对应 design/bugs 文档，补真实验证命令或证据路径。不要优先扩新功能，不要大改架构，不要把 compat/debug/fallback 当主链，不要把 BlockNote/Tiptap AI runtime 作为 mnote runtime 依赖。验证至少包含 scoped git diff --check、相关 cargo test / smoke；如涉及 3000 页面，使用 mnote-tester 或浏览器自动化并保留截图/JSON 证据。
```
