Files
mnote/design/10-review/done/08-kernel-architecture-next-priority-review-and-checklist.md
T
lix-2026 1882db7681 收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目
- 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线
- 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径

验证:
- cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1
- cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1
- git diff --check
- git diff --cached --check
- codegraph index . --force && codegraph status .
- codegraph sync . && codegraph status .
2026-06-01 09:29:12 +08:00

608 lines
59 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.
# 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 fallbacklive 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。
### P0Page 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,不返回看似成功的旧快照。
### P0Tree Command Cutover
目标:
- `tree.*` 成为正式命令面。
- `documents.*` 只保留为兼容层,不再继续扩写长期业务语义。
- 页面新建、重命名、移动、归档、恢复、删除、资源生命周期都能通过正式 tree/page command 解释。
### P0Tree 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` 返回同类错误 JSONHTML 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 previewyolo 模式也应生成同构 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` 成为主 transportSSE 降级为 fallback。mutation 后通过 `stream_delta_tx` broadcast channel 推送 deltaWS 客户端零 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 当时各自可建立 EventSourcepage 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 rowsubtree 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 contextA 端通过正式 `/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 contextA 端创建同父级 `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 executorPhase 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 证据。
```