From 61ee4a38a27c1e6175371f26cc7a90899162d385 Mon Sep 17 00:00:00 2001 From: lix-2026 Date: Sun, 17 May 2026 23:16:37 +0800 Subject: [PATCH] docs: update architecture review notes - clarify Rust SSR shell, WS/SSE transport status, and Next legacy role - add Reasonix block edit workflow empty block ops bug record Verification: - git diff --check -- CURRENT_ARCHITECTURE.md design/10-review/process/11-current-full-architecture-review-v1.md bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md --- CURRENT_ARCHITECTURE.md | 11 ++- ...empty-block-ops-after-markdown-match-v1.md | 92 +++++++++++++++++++ .../11-current-full-architecture-review-v1.md | 2 +- 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md diff --git a/CURRENT_ARCHITECTURE.md b/CURRENT_ARCHITECTURE.md index d4040bfc..bfaa7ca1 100644 --- a/CURRENT_ARCHITECTURE.md +++ b/CURRENT_ARCHITECTURE.md @@ -40,8 +40,10 @@ ### 2.4 Transport / Realtime 层 -- Rust Web 已同时注册 `/api/tree/events` 与 `/api/realtime/ws`。 -- 但当前前端真实消费链路仍以 `EventSource('/api/tree/events')` 为主,未见主链 WS consumer。 +- Rust Web 已同时注册 `/api/tree/events`(SSE)与 `/api/realtime/ws`(WS)两条实时链路([routes/mod.rs:170-172](rust/crates/mnote-web/src/routes/mod.rs:170))。 +- Rust SSR 主壳([layout.rs](rust/crates/mnote-web/src/ssr/pages/layout.rs:7126))已内置 WS 消费者(`startWithWebSocket`),并在 WS 断开后自动切 SSE fallback(`ws.onclose → startWithSseFallback`)。 +- 当前默认 `bootstrap.transport` 仍为 `convex-command-log-sse`,因此主壳默认走 SSE;WS 路径代码已就绪但尚未设为默认 transport,前端主消费链路仍未切换到 WS 默认。 +- `wolai-frontend`(Next.js legacy 侧)仅使用 SSE(`use-sidebar-tree-stream.ts`),无 WS 消费者。 - SSE 内部还保留 push/polling 两种语义,WS 与 SSE 的 delta / snapshot 结构也未完全收敛。 ### 2.5 Page Aggregate 层 @@ -52,7 +54,8 @@ ### 2.6 Editor Runtime 层 -- `leptos-tiptap` island 已是文档页默认编辑 host。 +- 前端壳:Rust mnote-web 独享 3000 入口,通过 SSR 输出 workspace shell、文档壳、Sidebar、tree 等完整 HTML([gateway.rs](rust/crates/mnote-web/src/routes/gateway.rs:156)、[web_shell.rs](rust/crates/mnote-web/src/routes/web_shell.rs:63))。Next.js 前端代码仍保留在 `wolai-frontend/`,但默认热启动不再作为运行时 daemon。 +- `leptos-tiptap` island 已是文档页默认编辑 host,以 WASM 形式由 Rust SSR 加载。 - 保存正文仍会经过 `documents/save` 兼容面。 - 这意味着编辑器体验已经切主,但写回语义还没有完全切到唯一主命令面。 @@ -69,6 +72,7 @@ - Tree 主链已经从旧兼容入口退向 Rust Web。 - 文档页主编辑器已经切到 `leptos-tiptap` island。 - AI 页面编辑已经不再是纯前端本地逻辑。 +- **前端壳已切换到 Rust**:默认 `desktop:hot` 仅启动 Rust mnote-web 作为 3000 网关 owner,不再启动 Next.js (`wolai-frontend`) 作为运行时 daemon。Next.js 仅保留为 legacy compat fallback(`MNOTE_WEB_SKIP_GATEWAY=1`)与 build toolchain。 ### 3.2 过渡态 @@ -159,3 +163,4 @@ - [7-22 mnote.doc.apply_block_ops 批量写入缺少 revision / conflictDetectionKey 校验](./bugs/07-ai/process/7-22-apply-block-ops-missing-write-preconditions-v1.md) - [7-23 mnote.doc.markdown_edit manifest schema 缺少 required 与写入字段](./bugs/07-ai/process/7-23-markdown-edit-manifest-schema-contract-drift-v1.md) - [7-24 在线 markdown_edit 写回不以最终 Markdown 为真源](./bugs/07-ai/process/7-24-markdown-edit-online-write-does-not-use-final-markdown-v1.md) +- [7-25 ACP Reasonix 页面 AI block_edit_workflow 在 markdown 命中后生成空 block_ops](./bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md) diff --git a/bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md b/bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md new file mode 100644 index 00000000..ce2d7634 --- /dev/null +++ b/bugs/07-ai/process/7-25-reasonix-block-edit-workflow-empty-block-ops-after-markdown-match-v1.md @@ -0,0 +1,92 @@ +# 7-25 [process][bug] ACP Reasonix 页面 AI block_edit_workflow 在 markdown 命中后生成空 block_ops v1 + +> 发现时间:2026-05-17 +> +> 状态:`[process]` +> +> 关联主线:`07-ai` +> +> 关联缺陷:`7-24 在线 markdown_edit 写回不以最终 Markdown 为真源` + +## 1. 用户可见症状 + +在页面 AI 中切换到 `ACP · Reasonix`,输入: + +```text +你 +检查你是否能读取到本页第一段,同时请修改第二段为:测试123 +``` + +工具调用失败: + +```text +mnote.page_ai.block_edit_workflow 失败 · page-ai-fast-mp9wm0qo +结果 mnote.doc.apply_block_ops operations 不能为空 +``` + +## 2. 问题定义 + +`page_ai.block_edit_workflow` 当前已切到: + +```text +模型输出 search/replace +-> mnote.doc.markdown_edit +-> 在线文档再转换为 mnote.doc.apply_block_ops +``` + +但在线写回层没有以最终 Markdown 为真源。它在 markdown 字符串中 search/replace 成功后,又用原始 `operations` 去原始 `blocks` 中反查目标块。如果反查不到块,就生成空 `block_ops`,随后仍调用 `mnote.doc.apply_block_ops`,最终抛出“operations 不能为空”。 + +## 3. 证据 + +- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:69) 调用模型生成 markdown operations。 +- [page_ai_workflow.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/page_ai_workflow.rs:91) 构造 `mnote.doc.markdown_edit` 输入。 +- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:811) 在 `md` 字符串上执行 search/replace。 +- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:857) 调用 `build_block_ops_from_markdown_edit`。 +- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:989) 只用 `block.text.contains(search)` 反查 block。 +- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:1008) 可能返回空 `block_ops`。 +- [doc.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/doc.rs:878) 即使 `block_ops` 为空,也继续调用 `doc_apply_block_ops`。 +- [block.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/hermes_tools/block.rs:405) 对空 operations 报错:`mnote.doc.apply_block_ops operations 不能为空`。 + +## 4. 根因判断 + +这条错误不是最终 root cause,只是下游暴露出来的症状。 + +真正根因是 markdown 层与 block ops 写回层存在二次定位: + +1. `search_replace(&md, search, replace)` 可以通过精确、归一化或 fuzzy 匹配成功。 +2. `build_block_ops_from_markdown_edit` 却只支持 `block.text.contains(search)`。 +3. 两套匹配规则不一致时,markdown 层显示已命中,block 层却生成空 operations。 +4. 空 operations 没有在 `markdown_edit` 层转成语义化错误,而是继续传给 `apply_block_ops`。 + +## 5. 影响 + +- ACP Reasonix 的页面编辑会向用户暴露底层 `apply_block_ops` 错误,而不是说明“markdown 命中但无法映射到块”。 +- “读取第一段 + 修改第二段”这类混合任务可能进入 fast workflow,但该 workflow 只能表达写入,不保证先读后答。 +- 如果模型给出的 `search` 是段落序号、fuzzy 片段、带格式文本或跨块文本,markdown 层可能成功,block 写回层仍失败。 + +## 6. 建议修复 + +短期: + +- `doc_markdown_edit` 在调用 `doc_apply_block_ops` 前,如果 `applied > 0` 但 `block_ops.is_empty()`,应返回明确错误,例如 `mnote_doc_markdown_edit_block_mapping_empty`,并包含 failed operation 的 search 摘要。 +- `page_ai_workflow` 应把该错误翻译为用户可理解的提示,不要暴露 `apply_block_ops operations 不能为空`。 + +中期: + +- `build_block_ops_from_markdown_edit` 必须复用 `search_replace` 的定位结果,或让 `search_replace` 返回原始命中范围 / paragraph / block 映射。 +- 对“第一段/第二段”等序号型指令,应由模型输出对应段落原文作为 `search`,并增加回读校验。 + +长期: + +- 按 `7-24` 收口:在线 `markdown_edit` 应以最终 Markdown 为写回真源,或明确限制只支持可安全映射的单块精确替换。 + +## 7. 复现 / 验证建议 + +- 浏览器 smoke:ACP Reasonix,页面含至少两段正文,输入“检查第一段并修改第二段为:测试123”。 +- 断言失败时错误码不能是 `mnote.doc.apply_block_ops operations 不能为空`,应是 markdown 到 block 映射失败的明确错误。 +- 修复后回读 Page Aggregate,断言第二段实际变为 `测试123`,同时 AI 回复能正常说明已读取到第一段。 + +## 8. 状态 + +已按用户反馈与源码路径确认缺陷,尚未修复。 + diff --git a/design/10-review/process/11-current-full-architecture-review-v1.md b/design/10-review/process/11-current-full-architecture-review-v1.md index 7a0d86e4..c0d81c11 100644 --- a/design/10-review/process/11-current-full-architecture-review-v1.md +++ b/design/10-review/process/11-current-full-architecture-review-v1.md @@ -27,7 +27,7 @@ - Rust Web / realtime / ACP:`bugs/03-rust-web/process/3-16` 到 `3-21` - Tree domain:`bugs/04-tree-domain/process/4-46` - Editor mainline:`bugs/05-editor-mainline/process/5-15` 到 `5-18` -- AI:`bugs/07-ai/process/7-18` 到 `7-24` +- AI:`bugs/07-ai/process/7-18` 到 `7-25` ## 4. 备注