# 10 [done] 当前 mnote 项目 AI / Page Aggregate 定向 Review v1 > 更新时间:2026-05-17 > > 执行状态:`done` > > 范围:当前主线中 Page Aggregate 单一真源、页面 AI 快速编辑、`mnote.doc.markdown_edit` 与 ACP / Hermes runtime 的源码级定向审查。 > > 2026-06-01 口径补充:本文是 2026-05-17 的历史 review 快照。文中“`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` 为准,即授权文件引用 + allowed roots/files + agent 原生 patch/diff + watcher/BufferStore/Page Aggregate 同步。`mnote.doc.markdown_edit` 只保留为 remote/cloud/compat fallback 或结构校验辅助。 ## 1. 本轮结论 本轮没有发现 Page Aggregate 读取主链重新回退到 Next compat builder 的证据。后续修复已进一步收口:AI context 的 page subtree 只读 Rust Page Aggregate projection,不再由前端本地构造第二份 page tree 真相。 本轮识别出的 `07-ai` markdown 编辑 P0 风险已修复: - `mnote.doc.markdown_edit` 是简单正文编辑主路径,并被 `/api/page-ai/block-edit-workflow` 通过统一 mnote tool executor 调用。 - 中文归一化匹配不再把 UTF-8 byte index 当 char index。 - 同一块内多次 markdown_edit 会合并到最终 markdown 结果后再映射写回,避免后续 block op 覆盖前序修改。 - 在线写回在无法安全映射最终 markdown 时明确拒绝,避免 `ok=true` 伪成功。 - Hermes guidance、manifest、tool toggle、dryRun / idempotency、revision / conflictDetectionKey 与 Reasonix ACP payload 已同步到当前工具合同。 剩余长期方向仍是 Phase C review session / streaming apply;当前设计冻结,不属于本轮 bug 修复范围。 ## 2. 发现的问题 ### R-01 P0:中文归一化匹配使用 byte index 当 char index 关联缺陷: - `bugs/07-ai/done/7-16-markdown-edit-normalized-search-byte-index-v1.md` 证据: - `rust/crates/mnote-web/src/hermes_tools/doc.rs:920-936` `search_replace` 的 Level 2 先在 `norm_line` 上用 `find()` 得到 UTF-8 byte offset,却把这个 offset 传给 `line.char_indices().nth(start)` 当作字符序号使用。中文、多字节标点或中英混排时,替换范围会偏移,可能替错片段或生成错误内容。 影响: - 页面 AI 对中文正文的“忽略空白 / 全半角差异”替换不可靠。 - 该问题位于 `mnote.doc.markdown_edit` 的基础 search/replace 层,会同时影响在线文档和本地 `.md` 文件。 ### R-02 P0:同一块内多次 markdown_edit 会被后续 block op 覆盖 关联缺陷: - `bugs/07-ai/done/7-17-markdown-edit-same-block-multi-op-overwrite-v1.md` 证据: - `rust/crates/mnote-web/src/hermes_tools/doc.rs:779-824` - `rust/crates/mnote-web/src/hermes_tools/doc.rs:854-878` - `rust/crates/mnote-web/src/hermes_tools/doc.rs:976-1008` `doc_markdown_edit` 已经在 `md` 变量中顺序应用了所有 search/replace,但 Convex 写入时没有使用这个最终 markdown。它重新读取原始 Page Aggregate blocks,并对每个 operation 用原始 block 文本生成一个 replace block op。 如果两个 operation 命中同一个 block,第二个 block op 的 `content` 仍从原始文本计算,会覆盖第一个 operation 的结果。也就是说,markdown 层算出的最终结果与实际落库结果可能不一致。 影响: - AI 一次请求中常见的“把同一段里的 A 改成 B,同时把 C 改成 D”可能只保留最后一次修改。 - 前端快路径仍可能显示“已通过页面 markdown 编辑快路径完成写入”,但正文只部分生效。 ## 3. 修复状态 - `mnote.doc.markdown_edit` manifest 已补齐 required / write contract。 - 本地 `.md` 写入已尊重 dryRun / idempotency。 - 在线写回已使用最终 markdown 作为真源进行映射;无法安全映射时返回明确错误。 - `mnote.doc.apply_block_ops` 批量写入已补 revision / conflictDetectionKey 前置校验。 - `/api/page-ai/block-edit-workflow` 已从绕过 executor 改为复用统一 Hermes mnote tool executor。 - ACP Reasonix wrapper 已透传 actor/session/run/toolCall/trace/dryRun/idempotency/workspace/document 等上下文字段。 ## 4. 后续边界 1. Phase C 的 review session / streaming apply 仍冻结,当前不扩新 AI 功能。 2. 复杂结构编辑继续走 `apply_block_ops` / `mnote.block.*`,不要把正文 search/replace 回退到结构性块工具。 3. Page Aggregate 仍需后续推进 EditorBlockDocument 原生落库真相。 ## 5. 本轮验证 静态检查范围: ```bash rg -n "markdown_edit|apply_block_ops|block-edit-workflow|local_rule|page_ai_workflow|mnote\\.doc\\.fetch" rust wolai-frontend scripts design/07-ai design/10-review --glob '!rust/target/**' --glob '!node_modules/**' rg -n "page-aggregate|pageAggregate|PageAggregate|blockDocument|projectionSource|documents/save|documents/options|/api/page-aggregate" rust/crates/mnote-web rust/crates/bridge-runtime wolai-frontend/src scripts --glob '!rust/target/**' --glob '!node_modules/**' ``` 修复后定向验证: ```bash cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --nocapture cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_client -- --nocapture cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_session_manager -- --nocapture node --check scripts/reasonix-acp-wrapper.mjs MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs ``` 结果:上述验证均通过。