Files
mnote/design/old/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md
T
Agent Board b798f628ee chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align
chevron/DOM with restored expansion; keep Sidex-style shallow page-tree
scan and drop the unused recursive scanner that only added cargo noise.

Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi
into a module package, and retire Hermes/ACP/OpenHub recycle + root
harness evidence from the index while gitignoring recycle and local
diag dumps.

Archive superseded design/bugs docs under old/, point architecture at
ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor
regressions so the working tree can stay clean.
2026-07-21 05:13:05 +08:00

34 KiB
Raw Blame History

[recycle] 7-9 [done] 页面/块 AI 工具体系规划 v1

更新时间:2026-05-16

当前状态:DONE

本稿承接 7-6 的 mnote Hermes plugin tool 合同、7-8 的 Hermes Runtime BFF 方向,以及近期页面 AI 工具实测中暴露的问题:当前 mnote.page.get/save/update_title/update_options 已能完成页面级读写,但工具粒度仍偏粗,不能长期代表“AI 能精确编辑页面/块”。

核心参考:

  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-vscode-main
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/ai-toolkit-demos
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-docs
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-main
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-apcore
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-ai-autocomplete

上位依据:

  • /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/05-editor-mainline/done/5-13-page-block-identity-and-command-contract-v1.md
  • /mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-1-page-block-storage-projection-alignment-v1.md
  • /mnt/Data1T/mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md
  • /mnt/Data1T/mnote/design/07-ai/done/7-8-page-ai-hermes-runtime-bff-next-v1.md
  • /mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md

1. 结论

不要把问题理解成二选一:

  • 不是“等 Rust kernel / Page Aggregate / editor module 完全稳定后,才开始设计工具”。
  • 也不是“现在立刻扩一批块级写工具,让 Hermes 直接调用当前 Convex / 前端私有 shape”。

正确路径是:

现在继续写工具体系设计,冻结 AI 面向的稳定工具合同;实现上分阶段推进,先做只读、定位、dry-run、plan 和最小单块写入。复杂块移动、块嵌入、多块批量改写、媒体块编排,要等 Rust block model、Page Aggregate command、Convex 持久化和 editor session 刷新链路达到硬门槛后再开放。

当前 mnote.page.save 可以继续作为页面级兜底工具,但不能继续被描述为长期块编辑方案。长期工具体系必须基于稳定的 Page Aggregate / EditorBlockDocument / Rust runtime command 中间合同,而不是让 AI 直接操作前端 Tiptap JSON、Convex documents.content 私有结构或历史 blocks 表。


2. 为什么参考飞书 CLI

cli-main 的价值不在于“飞书有很多命令,所以 mnote 也应该堆很多工具”,而在于它把文档工具做成了三层:

  1. Shortcut 层:面向人和 Agent 的高层命令,例如 docs +fetchdocs +updatedocs +media-insert
  2. 稳定文档操作协议层:用 doc-formatscopedetailcommandblock_idrevision_iddry-run 表达文档读写。
  3. 底层 API adapter 层:把稳定协议翻译成真实平台 API,例如 docs_ai/v1/documents 或 MCP tool call。

这正好对应 mnote 当前问题:

  • Rust kernel、Page Aggregate、Convex、leptos-tiptap 还在收口。
  • 如果现在让 Hermes 直接调用底层 shape,后续底层一变,skill/tool 就会失效。
  • 如果先冻结一个 AI 面向的稳定文档操作协议,底层变动可以收口在 adapter。

因此 mnote 应参考的是飞书的“稳定投影 DSL + 少量高层命令 + dry-run 诊断层”,而不是照搬 Go CLI 或飞书 API。


3. cli-main 具体参考位置

参考根目录:

/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main

优先参考下表,不需要整仓搬运:

mnote 主题 参考文件 搜索点 可吸收内容 不采用内容
文档工具注册 shortcuts/doc/shortcuts.go Shortcuts()docs +fetchdocs +update 工具分组、命令命名、文档域工具入口 不照搬 CLI 交互层
文档读取 v2 shortcuts/doc/docs_fetch_v2.go executeFetchV2buildReadOption--detail--scope simple/with-ids/fulloutline/range/keyword/section、局部读取 不使用飞书 token/API
文档更新 v2 shortcuts/doc/docs_update_v2.go validCommandsV2buildUpdateBodyrevision-iddry-run str_replace/block_insert_after/block_replace/block_delete/block_move_after/append/overwrite 不直接把飞书 command 当 mnote 内部命令名
v1 兼容更新 shortcuts/doc/docs_update.go CallMCPToolupdate-doc 兼容层与主线层并存时的隔离方式 不保留多套长期真相
更新前诊断 shortcuts/doc/docs_update_check.go CheckDocsUpdateArgswarning 工具调用前给 Agent 的静态语义警告 不只靠 LLM 自觉避免危险编辑
媒体插入编排 shortcuts/doc/doc_media_insert.go dry-runstepsbatch_update 多步工具先 dry-run 展示计划,再执行 第一阶段不做完整媒体工具
文档 XML DSL skills/lark-doc/references/lark-doc-xml.md <title><callout><grid><img><cite> 用 PageXML/PageMarkdown 屏蔽底层块结构 不采用飞书专有块类型作为 mnote 类型
文档更新说明 skills/lark-doc/references/lark-doc-update.md str_replaceblock_insert_afterrevisionwarnings 面向 Agent 的工具使用说明、返回结构 不把说明当实现
文档读取说明 skills/lark-doc/references/lark-doc-fetch.md detailscopewith-ids 读取前先定位、再编辑的 workflow 不让 AI 默认整页读取超大正文
工具抽象 shortcuts/common/types.go ShortcutValidateExecuteRisk 元信息、权限、风险、dry-run、validate 一体化 不复制 Go 框架
dry-run 通用能力 internal/cmdutil/dryrun.go DryRun 所有写工具都能返回计划和风险 不做纯 CLI 文本输出
风险提示 internal/cmdutil/risk.go Risk 工具风险等级进入确认和审计 不用 CLI prompt 作为 Web 确认机制

快速定位命令:

cd /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main
rg -n "validCommandsV2|buildReadOption|buildUpdateBody|revision|dry-run|CheckDocsUpdateArgs|Shortcut|Risk|CallMCPTool|scope|detail" shortcuts internal skills/lark-doc

4. 官方 Tiptap AI Toolkit 与补充参考的吸收边界

Tiptap AI Toolkit 的核心价值是把编辑器能力拆成 AI 可调用工具,而不是让外部系统直接暴露编辑器内部实现。Context7 与本地 tiptap-docs 公开文档能确认的主线能力包括:

  • toolDefinitions() 向 AI SDK 暴露工具定义。
  • tiptapRead 用高效格式读取文档,支持 range/chunk 类读取模型。
  • tiptapEdit 用 operations 列表编辑文档。
  • tiptapReadSelection 读取当前选区。
  • executeTool / streamTool 把 AI 生成的 tool call 应用到 editor,并返回 docChanged、错误和工具结果。
  • review options 支持 disabled/review/preview/trackedChanges,但 preview/suggestions 与持久 tracked changes 是两类语义。

需要修正之前口径:

  • 不能再说 “Tiptap 没有官方 AI 文档工具”。它有官方 AI Toolkit 工具层。
  • 也不能说 “Tiptap AI Toolkit 可以直接解决 mnote 块工具”。公开文档没有给出完整 tiptapEdit.operations schema,且它面向 Tiptap/ProseMirror 文档层,不覆盖 mnote 的 Rust kernel、Page Aggregate、Convex revision/conflict key、Hermes audit。
  • 当前 npm registry 无法直接获取 @tiptap-pro/ai-toolkit* 完整实现源码;本仓可参考的是公开 docs 与 tiptap-main/packages/server-ai-toolkitextension-unique-idextension-drag-handleextension-node-range 等开源代码。

这些能力应映射为 mnote 自己的工具层:

Tiptap AI Toolkit mnote 对应 吸收内容 不吸收内容
tiptapRead mnote.doc.fetch / mnote.block.fetch 先读、带范围、返回适合 AI 的文档表示 不把 Tiptap JSON 作为长期工具格式
tiptapEdit mnote.doc.plan_update + mnote.block.* 操作列表、reviewable edit、meta justification 不让浏览器 editor command 成为事实源
tiptapReadSelection mnote.doc.fetch scope=selection selection-aware workflow 不直接持久化浏览器 selection range
toolDefinitions() Rust Hermes manifest schema、description、capability、annotation 不依赖私有 npm 包作为运行时硬依赖

tiptap-apcore 的补充价值更偏工具基础设施。它已经把 Tiptap command 分成 query、format、content、destructive、selection、history,并为工具提供:

  • inputSchema / outputSchema
  • readonly / destructive / idempotent / requiresApproval
  • ACL rolereadonlyeditoradmin
  • selectionEffect
  • executor 前置检查、ACL、query/command 分发

mnote 应吸收这些元数据,但命名和执行面必须改成 Rust-owned:

{
  "name": "mnote.block.replace",
  "capabilityScope": ["page.write", "block.write"],
  "annotations": {
    "readonly": false,
    "destructive": false,
    "idempotent": false,
    "requiresApproval": true,
    "selectionEffect": "destroy"
  },
  "runtimeOwner": "mnote-web",
  "writeOwner": "rust-runtime-kernel"
}

tiptap-ai-autocomplete 的价值限于交互层:

  • ghost text 定位。
  • 选区 bubble menu。
  • streaming preview。
  • accept/reject 后再写入。

它不应进入 Hermes tool contract 的核心,只能作为页面 AI 面板、选区 AI 菜单和 preview UI 的参考。

tiptap-main 开源代码给 mnote 的补充约束:

  • extension-unique-id 可作为 Tiptap runtime 节点 id 辅助,但不能替代 Rust EditorBlock.block_id
  • server-ai-toolkit_hash 是 AI 编辑定位/变化检测辅助,不能替代业务 blockId;它可以参与 mnote revisionRef 的 hash 部分。
  • extension-drag-handleextension-node-range 说明块选择、拖拽、selection toolbar AI 应共享一套 node range 计算,而不是每个入口重新解析 DOM。
  • schema awareness / editor context 应进入 mnote.doc.fetch 或工具 manifest 的上下文生成,避免 AI 猜测当前可用块类型。

5. 当前 mnote 稳定性判断

5.1 已经可以承载工具设计的边界

  • Page Aggregate 读合同已经稳定为 mnote.page_aggregate.v1
  • 页面主读链已经可以通过 Rust /api/page-aggregate/:id 返回 meta/content/options/body snapshot。
  • Hermes 页面 AI 已有 mnote.* 工具注册、dispatch、trace/audit、idempotency、dryRun 基础。
  • 页面级工具 mnote.page.get/save/update_title/update_options 已能完成最小闭环。
  • Rust core-protocol 已有工具规格方向,bridge-runtime 已出现 doc_insert_blocksdoc_replace_range 等工具/测试雏形。

这些足以支撑“工具合同设计”和“只读/定位/dry-run 工具实现”。

5.2 还不适合大规模开放块级写工具的边界

  • 当前保存主链仍是 documents.content legacy JSON snapshot,不是 EditorBlockDocument 原生落库,也不是 Tiptap JSON 原生落库。
  • 历史 blocks 表仍存在,但没有维护正文主链所需的 order、父子顺序和 documents.content 同步闭环,不能描述为当前正文事实源。
  • Page Aggregate 当前 body.content 仍主要是 documents:getContent 的 projection 包装,不是强类型 block protocol。
  • Page Aggregate 当前仍有 compat join 痕迹,不是所有页面正文语义都已经 kernel-native。
  • 部分 Rust block/editor command 已有 runtime 映射,但 Convex 执行面、editor session 刷新和真实页面可见性还需要逐条验收。
  • 块移动、块复制、块嵌入、块引用维护需要稳定块 id、revision、父子关系、排序、权限和冲突处理;当前不应直接给 Hermes 开生产写入口。
  • mnote.page.save 当前是整页/追加/前置级写入,适合作为过渡和兜底,不适合承诺“精确块编辑”。
  • mnote.page.save 当前要求 dryRun/idempotencyKey,但页面级 revision/conflict key 还不是强制写入门槛;块级写工具开放前必须补上强制 CAS 或等价冲突阻断。

5.3 设计上的硬边界

  • AI 工具不得绕过 Rust runtime 直接写 Convex。
  • AI 工具不得把前端 Tiptap JSON 当长期外部合同。
  • AI 工具不得依赖浏览器临时 DOM id 或 runtime-only selection。
  • 写工具必须有 dryRunidempotencyKeyrevision 或等价冲突键。
  • 写工具必须返回 warnings,并能阻止明显错误的编辑假设。
  • 写工具成功后必须能通过真实页面、/api/page-aggregate 和再次 mnote.*.fetch/get 三处验收。

6. 工具体系分层

L0:内部 kernel / adapter 命令

只给 Rust runtime、Page Aggregate adapter、Convex bridge 使用,不直接暴露给 Hermes。

示例:

  • page.aggregate.get
  • page.body.save
  • page.body.apply_patch
  • editor.block.insert_after
  • editor.block.replace
  • editor.block.delete
  • editor.block.move_after
  • tree.node.create
  • tree.subtree.move

要求:

  • 可以随内核演进调整。
  • 必须有测试覆盖。
  • 由 L1 canonical tools 翻译调用。

L1AI 可调用 canonical tools

Hermes skill/plugin 对外暴露的稳定工具层。第一阶段只做少量、可解释、可验收工具。

建议命名:

  • mnote.doc.fetch
  • mnote.doc.find
  • mnote.doc.plan_update
  • mnote.doc.apply_update
  • mnote.block.fetch
  • mnote.block.insert_after
  • mnote.block.replace
  • mnote.block.delete
  • mnote.block.move_after
  • mnote.page.append
  • mnote.page.overwrite

其中:

  • mnote.doc.fetch/find/plan_update 可先做。
  • mnote.block.insert_after/replace 是最小块写入切片。
  • mnote.block.delete/move_after 等架构稳定后再做。
  • mnote.page.overwrite 是高风险兜底,必须强确认和 dry-run。

L2workflow tools

面向用户任务的编排工具,不应第一阶段优先做。

示例:

  • mnote.workflow.write_weekly_report
  • mnote.workflow.rewrite_section
  • mnote.workflow.extract_action_items
  • mnote.workflow.create_meeting_note
  • mnote.workflow.generate_project_plan

要求:

  • L2 必须调用 L1,不直接写 L0。
  • L2 的产物先走 plan_update / dryRun,用户确认后再执行。
  • 不用 L2 掩盖 L1 工具合同不稳定的问题。

7. 稳定文档表示

7.1 PageMarkdown

适合纯文本、标题、列表、引用、代码、简单表格等常见 AI 输出。

用途:

  • AI 生成大纲、摘要、会议纪要。
  • append/prepend/overwrite
  • str_replace 简单替换。

限制:

  • 不表达复杂属性、块引用、嵌入、资源块、页面块关系。
  • 不作为唯一长期格式。

7.2 PageXML

参考飞书 DocxXML,但定义 mnote 自己的 PageXML。用于需要稳定 block id、属性、资源引用和结构化块的场景。

最小形态示例:

<page title="项目计划">
  <heading level="2" block-id="heading_1">目标</heading>
  <paragraph block-id="p_1">第一段文字</paragraph>
  <todo block-id="todo_1" checked="false">确认方案</todo>
  <callout tone="info">重要说明</callout>
</page>

原则:

  • block-id 只引用 mnote 已存在或本次 dry-run 分配的新 id。
  • PageXML 是 AI 外部合同,内部可翻译为 EditorBlockDocument
  • 不暴露 Tiptap 节点私有字段。
  • 不复制飞书专有块;只取 DSL 思想。

7.3 EditorBlockDocument

Rust 内部结构化文档模型,是 PageXML/PageMarkdown 到 Page Aggregate / editor session 的中间形态。

要求:

  • 持有稳定 block_id
  • 能表达 block type、text、attrs、children、parent、order、path、revisionRef。
  • 能生成 diff plan。
  • 能映射到当前 Convex-backed 保存链路。
  • 能在 Tiptap JSON、legacy documents.content 与 Page Aggregate block projection 之间做受控转换。

8. Canonical tool schema 草案

8.1 mnote.doc.fetch

用途:读取当前文档或局部文档,返回可供 AI 定位和编辑的稳定投影。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "format": "markdown",
  "detail": "with_ids",
  "scope": "full",
  "startBlockId": null,
  "endBlockId": null,
  "keyword": null,
  "sectionTitle": null,
  "contextBefore": 2,
  "contextAfter": 2,
  "maxDepth": 6
}

字段约束:

  • format: markdown | page_xml | text | json
  • detail: simple | with_ids | full
  • scope: full | outline | range | keyword | section | selection | block

返回:

{
  "ok": true,
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "format": "markdown",
  "detail": "with_ids",
  "scope": "section",
  "content": "## 目标 <!-- block:heading_1 -->\n正文 <!-- block:p_1 -->",
  "blocks": [
    { "blockId": "heading_1", "type": "heading", "text": "目标", "depth": 0, "revisionRef": "pageRev:rev_12:block:heading_1:hash:aaa" },
    { "blockId": "p_1", "type": "paragraph", "text": "正文", "depth": 1, "revisionRef": "pageRev:rev_12:block:p_1:hash:bbb" }
  ],
  "warnings": []
}

验收标准:

  • scope=full 能返回当前页面正文,且 with_ids 包含稳定 blockId
  • scope=outline 只返回标题/层级和必要 block id。
  • scope=keyword 返回命中块和前后上下文。
  • 返回内容与 /api/page-aggregate/:id 的 block snapshot 一致。
  • 不依赖浏览器 DOM。

8.2 mnote.doc.find

用途:在当前文档中查找关键词、块类型、标题或引用目标,帮助 AI 先定位再编辑。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "query": "待办",
  "match": "text",
  "limit": 20
}

返回:

{
  "ok": true,
  "matches": [
    {
      "blockId": "todo_1",
      "type": "todo",
      "text": "确认待办",
      "path": ["项目计划", "本周"],
      "score": 0.92
    }
  ]
}

验收标准:

  • 可以按文本查找。
  • 可以按 block type 查找。
  • 返回结果能直接作为 block.replace/insert_after 的 anchor。

8.3 mnote.doc.plan_update

用途:只生成变更计划和诊断,不实际写入。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "command": "block_replace",
  "format": "markdown",
  "blockId": "p_1",
  "content": "替换后的段落",
  "dryRun": true
}

支持命令:

  • str_replace
  • block_insert_after
  • block_replace
  • block_delete
  • block_move_after
  • append
  • overwrite

返回:

{
  "ok": true,
  "dryRun": true,
  "revision": "rev_12",
  "plan": [
    {
      "op": "replace",
      "targetBlockId": "p_1",
      "before": "旧段落",
      "after": "替换后的段落"
    }
  ],
  "warnings": [],
  "requiresConfirmation": true,
  "risk": "medium"
}

诊断规则参考 docs_update_check.go

  • 替换目标匹配多个位置时,提示先用 blockId 精确定位。
  • str_replace 找不到唯一匹配时,不执行。
  • 跨多个块的自然语言替换必须转成 block_delete + block_insert_afterblock_replace
  • overwrite 必须标高风险。
  • 缺少 revision 时只允许 dry-run,不允许真实写入。

验收标准:

  • 所有写命令都能先 dry-run。
  • dry-run 不改变 Convex 内容、不触发 editor 内容变化。
  • 返回的 plan 可被 UI 折叠展示。
  • 明显不安全的编辑返回 warningsblocked=true

8.4 mnote.doc.apply_update

用途:执行已经 dry-run 过的文档变更计划。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "idempotencyKey": "idem_1",
  "planId": "plan_1",
  "command": "block_replace",
  "format": "markdown",
  "blockId": "p_1",
  "content": "替换后的段落",
  "dryRun": false
}

执行要求:

  • 必须校验 revision
  • 必须校验 idempotencyKey
  • 必须落 Rust runtime / Page Aggregate command。
  • 成功后必须返回新 revision 和受影响 block id。

返回:

{
  "ok": true,
  "revision": "rev_13",
  "changedBlocks": [
    { "blockId": "p_1", "op": "replace" }
  ],
  "audit": {
    "effect": "write",
    "commandName": "page.body.apply_update",
    "commandId": "cmd_1"
  }
}

验收标准:

  • 执行后 /api/page-aggregate/:id 能读到更新。
  • 当前打开页面能自动刷新或通过 editor session reload 看到更新。
  • 再次 mnote.doc.fetch 能读到更新。
  • 重复同一 idempotencyKey 不造成重复写入。
  • revision 冲突返回 mnote_tool_conflict

8.5 mnote.block.fetch

用途:读取单个块及可选上下文,作为 tiptapReadSelection / selection-aware editing 的稳定服务端版本。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "blockId": "p_1",
  "revision": "rev_12",
  "includeChildren": true,
  "contextBefore": 1,
  "contextAfter": 1,
  "format": "json"
}

字段约束:

  • blockId 必须来自 mnote.doc.fetch/find 或 Page Aggregate block projection。
  • format: json | markdown | page_xml | text
  • includeChildren 默认 true,但复杂块可返回 unsupportedReason
  • contextBefore/contextAfter 只返回同父级上下文。

返回:

{
  "ok": true,
  "revision": "rev_12",
  "block": {
    "blockId": "p_1",
    "type": "paragraph",
    "text": "正文",
    "attrs": {},
    "path": [2],
    "parentBlockId": null,
    "order": "00020000",
    "revisionRef": "pageRev:rev_12:block:p_1:hash:abc",
    "editable": true
  },
  "context": {
    "before": [],
    "after": [
      { "blockId": "p_2", "type": "paragraph", "text": "下一段" }
    ]
  },
  "warnings": []
}

验收标准:

  • block.fetch 能读取 doc.find 返回的 block id。
  • 返回 revisionRef,可被后续写工具用于冲突检测。
  • 不存在 block 返回 mnote_block_not_found
  • 不可编辑块返回 editable=falseunsupportedReason

8.6 mnote.block.replace

用途:最小精确块写工具,作为第一批块级写入候选。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "blockId": "p_1",
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "blockRevisionRef": "pageRev:rev_12:block:p_1:hash:abc",
  "format": "markdown",
  "content": "替换后的块内容",
  "idempotencyKey": "idem_1",
  "dryRun": true
}

验收标准:

  • 只替换目标 block,不影响相邻 block。
  • block id 稳定,刷新后仍可定位。
  • 支持 dryRun=true
  • 支持真实页面 smoke。

8.7 mnote.block.insert_after

用途:在指定块后插入一个或多个块。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "anchorBlockId": "p_1",
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "anchorRevisionRef": "pageRev:rev_12:block:p_1:hash:abc",
  "format": "markdown",
  "content": "- 新待办",
  "idempotencyKey": "idem_2",
  "dryRun": true
}

验收标准:

  • 插入位置准确。
  • 新 block id 由 Rust/runtime 分配或确认,不由 AI 自造。
  • 当前页面能看到新块。
  • 再次 fetch 能拿到新 block id。

8.8 mnote.block.move_after

用途:移动一个块到同父级 anchor 块后。第一阶段仅作为受限结构性写工具开放。

入参:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "blockId": "p_3",
  "anchorBlockId": "p_1",
  "revision": "rev_12",
  "conflictDetectionKey": "doc_1:12",
  "blockRevisionRef": "pageRev:rev_12:block:p_3:hash:aaa",
  "anchorRevisionRef": "pageRev:rev_12:block:p_1:hash:bbb",
  "idempotencyKey": "idem_move_1",
  "dryRun": true
}

第一阶段允许:

  • blockIdanchorBlockId 同父级。
  • 普通 paragraph。
  • 普通 heading 叶子块。
  • 普通 todo 叶子块。

第一阶段阻断:

  • 跨父级移动。
  • 标题带子块整体移动。
  • 列表项跨层级移动。
  • 表格、resource、mindmap、page reference。
  • 跨页面移动。

返回:

{
  "ok": true,
  "dryRun": true,
  "risk": "medium",
  "diff": [
    {
      "op": "move_after",
      "blockId": "p_3",
      "anchorBlockId": "p_1",
      "from": { "parentBlockId": null, "order": "00030000" },
      "to": { "parentBlockId": null, "afterOrder": "00010000" }
    }
  ],
  "warnings": []
}

验收标准:

  • dry-run 不改变页面。
  • 正式写入后 moving block id 保持不变。
  • 同父级顺序正确。
  • 不支持场景返回 blocked=true 和明确 warning。
  • 再次 mnote.doc.fetch 能读回新顺序。

9. 实施阶段

Phase A:只读和定位

目标:

  • 建立 mnote.doc.fetchmnote.doc.find
  • 支持 formatdetailscope
  • 让 AI 能稳定读到 block id、标题路径、上下文。

可做:

  • 从 Page Aggregate snapshot 生成 PageMarkdown。
  • 从 Page Aggregate snapshot 生成 PageXML 最小子集。
  • 支持 scope=full/outline/keyword/block
  • 支持 detail=simple/with_ids/full
  • Hermes tool event UI 展示 fetch/find 摘要。

验收:

  • 真实登录页面中,Hermes 调用 mnote.doc.fetch 能读出当前页面。
  • with_ids 返回的 block id 与 DOM data-block-id / Page Aggregate 一致。
  • keyword 读取不会返回整页超大正文。
  • 失败时返回权限/不存在/空页面的结构化错误。

Phase Bdry-run / plan

目标:

  • 建立 mnote.doc.plan_update
  • 写入前先给出可解释 diff、风险、warnings。

可做:

  • 支持 str_replace dry-run。
  • 支持 block_replace dry-run。
  • 支持 block_insert_after dry-run。
  • 支持 append dry-run。
  • 实现静态诊断规则。

验收:

  • dry-run 不改变页面内容。
  • UI 可折叠展示 plan。
  • 多重匹配、不存在 block、缺 revision 都返回明确 warning。

Phase C:最小块级写入

目标:

  • 开放 mnote.block.replacemnote.block.insert_after 的真实写入。

前置硬门槛:

  • Page Aggregate 中每个可编辑块都有稳定 blockId
  • Rust runtime 能把 block replace/insert 变更映射到持久化结构。
  • Convex-backed 保存链能保存变更并刷新。
  • 当前 editor session 能 reload 或 live refresh。
  • smoke 覆盖 AI tool -> Rust -> Convex -> 页面可见 -> fetch 回读。

验收:

  • AI 替换单个段落后,页面只变这一段。
  • AI 在指定块后插入待办后,页面位置正确。
  • 刷新页面后内容仍在。
  • 版本冲突不会覆盖用户刚刚输入的内容。

Phase D:多块和结构性写入

目标:

  • 开放 block_deleteblock_move_afterblock_copy_insert_after

前置硬门槛:

  • 块父子关系、排序、缩进、折叠状态稳定。
  • 删除/移动能处理子树。
  • 冲突检测覆盖移动前后的邻居和父节点。
  • 有撤销或可审计回滚策略。

验收:

  • 移动标题块时,其子块处理规则明确且测试覆盖。
  • 删除块需要确认,并返回被删除范围。
  • 复制插入生成新 id,不复用旧 id。

Phase Eworkflow tools

目标:

  • 基于 L1 工具做高层 AI 写作工作流。

可做:

  • 生成会议纪要。
  • 生成周报。
  • 重写某一节。
  • 提取待办并插入当前页。

验收:

  • workflow 只调用 L1 canonical tools。
  • 每个 workflow 都能先 plan,再执行。
  • 不出现直接整页覆盖用户内容的默认行为。

10. 架构优先级判断

当前应并行推进,但优先级要明确:

  1. 先稳定 Page Aggregate / block identity / editor save-refresh 链路:这是块级写工具能否长期可靠的根。
  2. 同时设计并冻结 L1 canonical tools 合同:避免继续把临时页面保存扩展成长期能力。
  3. 先实现只读和 dry-run:这部分对底层写链依赖小,能立即改善 AI 可靠性。
  4. 等硬门槛满足后开放最小块写入:先 replace / insert_after,不要一口气做完整块操作。
  5. 最后做 workflow:工具层不稳定时,workflow 只会放大错误。

因此,回答“先优化稳定架构,还是继续写工具设计”:

架构稳定是实现复杂写工具的前置;工具设计现在就应该继续,而且必须用于反向约束架构稳定的验收标准。


11. 近期不做清单

  • 不让 Hermes 直接调用 Convex mutation。
  • 不把 Tiptap JSON 暴露成 AI 长期工具入参。
  • 不把 mnote.page.save 包装成所有块编辑的长期方案。
  • 不先做大量 workflow.* 工具。
  • 不做无 revision / 无 dry-run / 无 idempotency 的写工具。
  • 不在工具里读取浏览器 DOM 来决定写入位置。
  • 不用页面当前可见文本做唯一定位依据;必须支持 block id 或唯一匹配诊断。

12. 执行 checklist

12.1 设计冻结

  • 确认 mnote.doc.fetch/find/plan_update/apply_update 命名。
  • 确认 PageMarkdown 最小语法。
  • 确认 PageXML 最小语法。
  • 确认 revision 来源。
  • 确认 blockId 来源只来自 Page Aggregate / Rust runtime。
  • 确认 warnings / risk / requiresConfirmation 返回格式。

验收标准:

  • design/07-ai/process 中有稳定工具合同。
  • 7-6 不再把 mnote.page.save 描述为长期块编辑合同。
  • 5-6 的 Page Aggregate checklist 能引用本稿作为 AI 写入门槛。

12.2 只读工具实现

  • Rust tool manifest 增加 mnote.doc.fetch
  • Rust tool manifest 增加 mnote.doc.find
  • Rust tool manifest 增加 mnote.block.fetch
  • dispatch 调用 Page Aggregate snapshot,而不是 Convex 私有 shape。
  • 支持 detail=simple/with_ids/full
  • 支持 scope=full/outline/keyword/block
  • 工具结果进入 Hermes tool event UI。

验收标准:

  • cargo test -p mnote-web hermes 相关测试通过。
  • 真实网页登录后,AI 能通过 mnote.doc.fetch 读取当前页。
  • AI 能通过 mnote.doc.find 找到指定文本所在 block id。
  • /api/page-aggregate/:id 与 tool 返回 block id 一致。

12.3 dry-run 实现

  • 增加 mnote.doc.plan_update
  • 支持 str_replace 计划。
  • 支持 block_replace 计划。
  • 支持 block_insert_after 计划。
  • 返回 plan/warnings/risk/requiresConfirmation
  • 静态诊断阻止多重匹配和缺 revision 的真实写入。

验收标准:

  • dry-run 不改变页面。
  • UI 能显示工具计划。
  • 多重匹配返回 warning。
  • 不存在 block 返回结构化错误。

12.4 最小块写入实现

  • 打通 mnote.block.replace
  • 打通 mnote.block.insert_after
  • 写入统一走 Rust runtime / Page Aggregate command。
  • 成功后触发当前 editor session reload 或 live refresh。
  • 成功后返回新 revision 和 changedBlocks。

验收标准:

  • AI 替换单块,页面立即可见。
  • AI 插入新块,位置准确。
  • 刷新后内容仍在。
  • 再次 fetch 能读回变更。
  • revision 冲突被拦截。

12.5 真实网页 smoke

  • 新建测试页面,写入唯一前缀 TEST-AI-TOOL-<timestamp>
  • mnote.doc.fetch scope=full detail=with_ids 读取页面。
  • mnote.doc.find 定位测试段落。
  • mnote.doc.plan_update command=block_replace dryRun=true 生成计划。
  • mnote.block.replace dryRun=false 替换段落。
  • 页面截图证明内容可见。
  • /api/page-aggregate/:id 证明内容持久化。
  • mnote.doc.fetch 再次证明 AI 可读回。

验收标准:

  • 证据目录写入 tmp/hermes-tester/<run-id>/
  • 失败时记录到 bugs/07-ai/process/ 或真正 owner 分类。
  • 通过后才能把对应 checklist 勾到 done。

13. 与当前页面 AI 的关系

当前已有工具继续保留:

  • mnote.page.get
  • mnote.page.save
  • mnote.page.update_title
  • mnote.page.update_options
  • mnote.artifact.*

但口径调整为:

  • mnote.page.get 是页面级读取,不是长期精确块读取。
  • mnote.page.save 是页面级兜底写入,只适合 append/prepend/replace 等粗粒度操作。
  • 精确编辑应迁移到 mnote.doc.* / mnote.block.*
  • 页面 AI 面板展示工具时,应把 page.save 标为高风险或粗粒度。

14. 迁移完成定义

本文作为页面/块 AI 工具体系路线图与合同已经归档为 DONE;执行验收不再由本文继续承接,而是转入:

  • /mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md

当前已成立的 done 边界:

  • mnote.doc.fetch / mnote.doc.find 已进入 Rust Hermes tool manifest 与 dispatch,并已有最小真实页面 smoke 证据。
  • mnote.doc.plan_update 已进入 dry-run 计划链,可返回 diff、warnings、risk、blocked。
  • mnote.block.replacemnote.block.insert_aftermnote.block.move_after 已形成最小块写入闭环。
  • mnote.page.save 已在 UI/manifest 口径中降为页面级兜底工具,不再作为默认精确块编辑入口。
  • 后续执行项以 7-10 跟踪,仍包括 scope=selectionformat=page_xml/text、多块插入、复杂块移动矩阵和持久审阅 UI。