Files
mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md
T
lix-2026 f292c6710a feat: EditorRuntimeActor - 三层缓存/delta/事件架构
Phase A — EditorRuntimeActor 内存缓存层
- 新增 editor_actor.rs: EditorBlockDocument 内存态 + apply_command + load_or_init
- block.rs 四个写工具(replace/insert/delete/move)接入 actor 路径
- editor_actor feature flag(MNOTE_WEB_ENABLE_EDITOR_ACTOR=true 默认开启)
- bridge-runtime 三个核心函数公开化
- rust-toolchain: 1.89 → stable(修复 spike WASM 编译阻塞)

Phase B — 编辑器增量 delta channel
- BlockDelta/DeltaOperation 类型 + actor.build_block_delta()
- leptos-tiptap spike: mnote:editor:block-delta CustomEvent 监听 + JSON patch
- DocumentAiAgentPanel: 拦截 blockDelta → window dispatchEvent
- 工具响应含 blockDelta 字段供前端消费

Phase C — 事件 stream delta
- broadcast channel 在 AppState/actor/SSE 三层贯通
- tree_events SSE 端点发 block.delta 事件
- 旧客户端降级兼容

环境修复
- rustc recursion_limit = 1024(修复 Leptos SSR 类型深度溢出)
- run-convex-deploy.js(封装 Convex function 部署到本地后端 3210)

ref: design/07-ai/process/7-13-page-block-editor-runtime-actor-v1.md
2026-05-16 22:03:30 +08:00

19 KiB
Raw Blame History

7-10 [process] 页面块 AI 工具执行 checklist v1

更新时间:2026-05-16

当前状态:PROCESS

关联文档:

  • /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-9-page-block-ai-tooling-roadmap-v1.md
  • /mnt/Data1T/mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md
  • /mnt/Data1T/mnote/design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md

1. 目的

本 checklist 把 7-9 的路线图拆成可验收执行链,避免页面块 AI 工具停留在“工具名已设计、真实页面不可回读”的状态。

1.0 执行口径修正(2026-05-16

本文继续作为页面块 AI 工具执行 checklist 保留在 process/,不移入 old/。但后续所有“页面 AI runtime / fast workflow / planner / apply controller”相关任务必须按 7-12 的边界解释:

  • Hermes 继续是唯一页面 AI agent runtime。
  • mnote 本地层只提供工具路由、工具提示、上下文冻结、dry-run/review、Rust 写入校验和 readback。
  • PageAIIntentParser 后续读作 PageAICommandRouter,输出 recommendedToolCall,不维护独立对话 runtime。
  • PageAIOperationPlanner 后续只构造 tool args 或 dry-run plan,不能绕过 Hermes tool manifest/profile toggle/audit。
  • PageAIOperationValidator 继续有效,但归属 mnote Rust tool executor / projection validation。
  • PageAIApplyController 后续应收口为 review session / tool executor / readback controller,不能成为第二套 agent 编排中心。
  • 当前 usedHermesRun=false 的快路径只能理解为 deterministic shortcut,不代表 mnote 新建长期 agent runtime。

1.1 当前执行状态(2026-05-16

已完成并有代码/测试/smoke 证据:

  • Page Aggregate 输出 blockDocument/blockProjectionVersion/projectionSource
  • mnote.doc.fetchmnote.doc.findmnote.block.fetch 已接入 Hermes tool manifest 与 dispatch。
  • mnote.doc.plan_update 已提供块级 dry-run 计划和移动阻断诊断。
  • mnote.block.replacemnote.block.insert_aftermnote.block.move_after 已通过 Rust EditorCommand 生成 canonical content,再经 page.body.save -> documents:updateContent 持久化。
  • revision/conflictDetectionKey/revisionRef/idempotencyKey/dryRun 写入前置约束已接入,并修复 content_revision 投影对齐。
  • 真实 smoke/mnt/Data1T/mnote/scripts/task-page-block-ai-tools-smoke.js,证据位于 /mnt/Data1T/mnote/tmp/page-block-ai-tools-smoke/mp7xgyqs.json 与同名截图。
  • 页面 AI Runtime mnote tools 面板已读取真实 manifest,展示 13 个 mnote tools,并支持当前 Hermes profile 下开关工具。
  • /api/hermes/client/tools/toggle 已持久化 mnote.tools.disabled/api/hermes/tools/mnote/call 在执行前按 profile 拦截关闭工具,返回 mnote_tool_disabled
  • Hermes 外部 mnote plugin/skill 已完成,不改 Hermes 应用本体:
    • /home/lix/.hermes/plugins/mnote/plugin.yaml
    • /home/lix/.hermes/plugins/mnote/__init__.py
    • /home/lix/.hermes/skills/note-taking/mnote-block-ai/SKILL.md
  • Hermes 外部 plugin schema 已对齐真实块工具参数:mnote_doc_fetch(scope/detail/format/query/maxBlocks/blockId/selectedBlockIds/allowedTargetBlockIds)mnote_doc_plan_update(command/blockId/anchorBlockId/content)mnote_block_fetch(blockId/includeChildren/contextBefore/contextAfter/format)
  • Hermes CLI 真实 plugin 块操作已通过:
    • 测试页:workspaceId=tree_1777430834634_3documentId=tree_1778915893346_1suffix=mp80lbze
    • 工具链:mnote_doc_fetch -> mnote_block_fetch -> mnote_doc_plan_update(dryRun block_replace) -> mnote_block_replace -> mnote_doc_plan_update(dryRun block_insert_after) -> mnote_block_insert_after -> mnote_doc_plan_update(dryRun block_move_after) -> mnote_block_move_after -> mnote_doc_fetch
    • 结果:finalOrder=["p_1","p_3","ai_block_req_1778916033994_21","p_2"]finalTexts 分别为 Hermes 块插件第一段 mp80lbzeHermes 块插件第三段 mp80lbzeHermes 插件插入段 mp80lbzeHermes 插件替换第二段 mp80lbzeerrors=[]
    • 修复点:Hermes 可能把 doc.fetch 返回的 projection payload/contentNodes 形状传回写工具,rust/crates/mnote-web/src/hermes_tools/block.rs 已补齐 content_to_text 解析并加单测,避免 replace/insert 写成空段。
  • 浏览器回读验证已通过:打开 http://127.0.0.1:3000/documents/tree_1778915893346_1?workspaceId=tree_1777430834634_3 后四段目标文本可见,截图 /mnt/Data1T/mnote/tmp/hermes-plugin-block-ai/mp80lbze-page.png
  • 页面 AI 工具面板浏览器验证:3000 最新 mnote-web 进程下 Runtime 面板可见 13 个工具;关闭 mnote.block.fetch/api/hermes/client/tools 显示 enabled=false/status=disabled,直接调用 mnote.block.fetch 返回 mnote_tool_disabled,随后已恢复开启;截图 /mnt/Data1T/mnote/tmp/page-ai-tools-runtime/mnote-page-ai-tools-runtime-20260516.png
  • 页面 AI context / format focused smoke 已完成:
    • 脚本:/mnt/Data1T/mnote/scripts/task-page-block-ai-context-format-smoke.js
    • 证据:/mnt/Data1T/mnote/tmp/page-block-ai-context-format-smoke/mp8ddr4n.json,截图 /mnt/Data1T/mnote/tmp/page-block-ai-context-format-smoke/mp8ddr4n-page.png
    • 覆盖:mnote.doc.fetch scope=selection selectedBlockIds=["p_2"] format=page_xml/textmnote.block.fetch format=page_xml/text、manifest annotations、mnote.page.save destructive/yolo 粗粒度兜底定位、mnote.doc.apply_block_ops allowedTargetBlockIds 越界阻断 mnote_block_target_out_of_scope
    • 边界:本轮只验证 doc.apply_block_ops 的 selection scope guard;单个 mnote.block.* 写工具尚未完成 allowedTargetBlockIds 矩阵。
  • 页面 AI 快速块编辑第一阶段已完成:
    • 新增 /api/page-ai/block-edit-workflow,简单块增删改不再默认进入 Hermes agent run。
    • 对明确中文指令 把「A」替换为「B」/ 在「C」后插入「D」/ 删除「E」 已先由 mnote 本地 planner 生成 mnote.doc.apply_block_ops operations;无法解析时才进入小模型 operations 路径。
    • 快路径失败时,除 page_ai_workflow_not_block_edit 外不再自动 fallback 到 /api/hermes/client/runs,避免一次请求叠加“快路径失败成本 + Hermes agent 成本”。
    • 真实浏览器 smoke/mnt/Data1T/mnote/scripts/task-page-ai-block-edit-workflow-smoke.js,最新证据 /mnt/Data1T/mnote/tmp/page-ai-block-edit-workflow-smoke/mp86uciu.json
    • 验证结果:pageAiWriteVisible=788msusedFastWorkflow=trueusedHermesRun=false;后端日志 operation_source=local_rulemodel_ms=0apply_ms=42total_ms=42
    • 详细 review/mnt/Data1T/mnote/design/10-review/process/09-page-ai-fast-block-edit-runtime-review.md

仍未完成,本文继续留在 process/

  • 复杂块移动阻断矩阵还需要单独 smoke 覆盖标题带子块、列表项、表格、mindmap/resource。
  • 持久审阅/preview UI 仍是后续可选项;当前默认 yolo 模式不做写入审批,scope=selectionformat=page_xml/text 已进入工具与 PageAIContextBuilder 首版。
  • PageAIIntentParser / PageAIOperationPlanner / PageAIOperationValidator / PageAIApplyController 仍需继续建设;当前本地 planner 只覆盖低歧义文本块增删改,不应被视为完整 AI 编辑 runtime。

执行顺序固定为:

fetch/find
  -> block.fetch
  -> plan_update
  -> block.replace
  -> block.insert_after
  -> block.move_after

每一步都必须满足:

  • Rust command 或 projection 有测试。
  • Hermes tool 有结构化返回。
  • 真实页面 smoke 通过。
  • 写入后能通过 Page Aggregate 与 AI fetch 回读。

2. Phase 0:设计与基线冻结

  • 5-13 已冻结 block identity、command、Tiptap boundary。
  • 2-1 已冻结 documents.contentblocks 表、Page Aggregate、revision/conflict key 的关系。
  • 7-9 已更新 Tiptap AI Toolkit 对照,不再写成“Tiptap 没有官方 AI 文档工具”。
  • 7-9 明确 mnote.page.save 是粗粒度兜底,不是精确块工具。
  • 当前 reference code 已在 /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-docstiptap-main 可读。

验收:

  • rg -n "Tiptap AI Toolkit|tiptapRead|tiptapEdit|UniqueID|_hash|blockDocument" design/05-editor-mainline/{process,done} design/02-convex-rust-long-term-architecture/{process,done} design/07-ai/{process,done} 能找到对应设计。

3. Phase 1Page Aggregate Block Projection

目标:

每个可编辑块都能从 Page Aggregate 读到稳定身份和冲突辅助信息。

任务:

  • PageBody 增加 blockDocument 或等价稳定字段。
  • documents.content 生成 EditorBlockDocument
  • 从 Tiptap JSON 生成 EditorBlockDocument 的 bridge 测试覆盖当前主类型。
  • 每个块输出 blockId/type/text/attrs/children/parentBlockId/order/path/depth/revisionRef/editable
  • 对无 id legacy block 生成稳定迁移策略或 warning。
  • 复杂块输出 editable=false 或受限能力。

验证命令:

cargo test -p core-protocol editor_tiptap_bridge
cargo test -p bridge-runtime editor_document

真实页面 smoke

  • 登录 http://localhost:3000/auth 测试账号。
  • 新建页面 TEST-AI-BLOCK-PROJECTION-<timestamp>
  • 输入段落、标题、todo、列表、mindmap/resource 占位至少各一个。
  • /api/page-aggregate/:id 保存响应到 tmp/hermes-tester/<run-id>/page-aggregate.json
  • 断言所有普通可编辑块有 blockIdrevisionRef

通过标准:

  • 页面刷新后 block ids 不变化。
  • projection 中 blockCount 与页面可编辑块数量大体一致;复杂块允许受限但必须有 warning。

4. Phase 2mnote.doc.fetch / mnote.doc.find

目标:

AI 能先读取和定位,不需要猜整页 content shape。

任务:

  • tool manifest 增加 mnote.doc.fetch
  • tool manifest 增加 mnote.doc.find
  • doc.fetch 支持 scope=full/outline/keyword/block/selection
  • doc.fetch 支持 detail=simple/with_ids/full
  • doc.find 支持按 text/type/blockId 查找。
  • 返回 page revision/conflictDetectionKey
  • 返回可直接传入 block.fetch/replace/insert_afterblockId

验证命令:

cargo test -p mnote-web hermes_tools
cargo test -p bridge-runtime doc_find

真实页面 smoke

  • 使用 TEST-AI-BLOCK-FETCH-<timestamp> 页面。
  • mnote.doc.fetch scope=full detail=with_ids 读取整页。
  • mnote.doc.fetch scope=outline detail=with_ids 只返回标题结构。
  • mnote.doc.find query=<唯一前缀> 定位目标段落。
  • 保存工具返回到 tmp/hermes-tester/<run-id>/doc-fetch-find.json

补充 smoke 证据:

  • mnote.doc.fetch scope=selection selectedBlockIds=["p_2"] format=page_xml 读取真实页面选区上下文,只返回 p_2,返回 schema=mnote.page_ai_context.v1allowedTargetBlockIds=["p_2"]revision/conflictDetectionKey 和 block revisionRef。证据:tmp/page-block-ai-context-format-smoke/mp8ddr4n.json
  • mnote.doc.fetch scope=selection format=text 只返回 [p_2] 第二段 mp8ddr4n,不包含未选中块。证据:tmp/page-block-ai-context-format-smoke/mp8ddr4n.json

通过标准:

  • 不读取浏览器 DOM。
  • doc.find 返回的 block id 能被 block.fetch 读取。
  • 大页面默认分块或限制输出,不默认返回无限正文。

5. Phase 3mnote.block.fetch

目标:

AI 能读取单块、子块和同父级上下文,形成写入前确认。

任务:

  • tool manifest 增加 mnote.block.fetch
  • 支持 includeChildren
  • 支持 contextBefore/contextAfter
  • 支持 format=json/markdown/page_xml/text
  • 返回 revisionRefeditableunsupportedReason
  • 不存在 block 返回 mnote_block_not_found

验证命令:

cargo test -p mnote-web block_fetch

真实页面 smoke

  • doc.find 结果选择一个段落 block。
  • mnote.block.fetch includeChildren=true contextBefore=1 contextAfter=1
  • 断言 before/after 只来自同父级。

补充 smoke 证据:

  • mnote.block.fetch blockId=p_2 format=page_xml/text contextBefore=1 contextAfter=1 返回目标块 p_2revisionRef 与同父级 before/after p_1/p_3。证据:tmp/page-block-ai-context-format-smoke/mp8ddr4n.json

通过标准:

  • block 文本与页面显示一致。
  • revisionRef 可被后续 dry-run 使用。
  • 复杂块不会伪装成完全可编辑。

6. Phase 4mnote.doc.plan_update

目标:

所有写入先 dry-run,返回 diff、warnings、risk,不改变页面。

任务:

  • tool manifest 增加 mnote.doc.plan_update
  • 支持 command=block_replace
  • 支持 command=block_insert_after
  • 支持 command=block_move_after dry-run。
  • 支持 command=str_replace 且多重匹配阻断。(当前只有基础 plan,仍需多重匹配阻断。)
  • 缺少 revision/conflictDetectionKey 时只允许 dry-run。
  • 返回 planIddiffwarningsriskblocked

验证命令:

cargo test -p mnote-web plan_update
cargo test -p bridge-runtime doc_replace_range
cargo test -p bridge-runtime doc_insert_blocks

真实页面 smoke

  • 对目标段落执行 block_replace dryRun=true
  • 对目标段落执行 block_insert_after dryRun=true
  • 对两个同父级普通块执行 block_move_after dryRun=true
  • dry-run 前后分别保存 /api/page-aggregate/:id,确认内容 hash 不变。

通过标准:

  • dry-run 不写 Convex。
  • plan 能解释 before/after。
  • 不支持场景返回 blocked=true

7. Phase 5mnote.block.replace

目标:

第一条最小精确块写入闭环。

任务:

  • tool manifest 增加 mnote.block.replace
  • 输入必须包含 blockId/revision/conflictDetectionKey/idempotencyKey/dryRun
  • dryRun=true 只返回 plan。
  • dryRun=false 生成 EditorCommand::ReplaceBlock
  • Rust 应用命令生成 canonical content。
  • 通过 page.body.save -> documents:updateContent 持久化。
  • 返回新 revision、changedBlocks、audit。

验证命令:

cargo test -p mnote-web block_replace
cargo test -p bridge-runtime doc_replace_range_tool_executes_in_rust_runtime

真实页面 smoke

  • 新建 TEST-AI-BLOCK-REPLACE-<timestamp> 页面。
  • 写入两段不同文本。
  • doc.find 找到第二段 block id。
  • block.replace dryRun=true 查看 plan。
  • block.replace dryRun=false 替换第二段。
  • 页面截图证明只替换第二段。
  • /api/page-aggregate/:id 回读证明持久化。
  • mnote.doc.fetch 再次证明 AI 可读回。

通过标准:

  • 相邻块不变化。
  • 目标 block id 保持不变。
  • 旧 revision 写入返回 conflict。

8. Phase 6mnote.block.insert_after

目标:

AI 能在指定块后插入新块,并拿到新 block id。

任务:

  • tool manifest 增加 mnote.block.insert_after
  • 输入必须包含 anchorBlockId/revision/conflictDetectionKey/idempotencyKey/dryRun
  • 新块 id 由 Rust runtime 分配。
  • 支持单块和最多 20 个普通块插入。(当前最小闭环为单块插入。)
  • 第一阶段支持 paragraph/heading/todo。
  • 返回 inserted block ids 和新 revision。

验证命令:

cargo test -p mnote-web block_insert_after
cargo test -p bridge-runtime doc_insert_blocks_tool_emits_editor_commands

真实页面 smoke

  • 新建 TEST-AI-BLOCK-INSERT-<timestamp> 页面。
  • 在第一段后插入 todo。
  • 截图证明位置准确。
  • 刷新页面后再次确认。
  • mnote.block.fetch 能读取新 block id。

通过标准:

  • 插入位置准确。
  • 新 block id 不是 AI 自造未校验 id。
  • 重复同一 idempotencyKey 不重复插入。

9. Phase 7mnote.block.move_after

目标:

第一阶段只开放同父级普通叶子块移动。

任务:

  • tool manifest 增加 mnote.block.move_after
  • dryRun=true 支持同父级叶子块 diff。
  • dryRun=false 前先继续阻断所有复杂块。(已有同父级/叶子/类型/editable/self 阻断,复杂块矩阵 smoke 待补。)
  • 检查 blockRevisionRefanchorRevisionRef
  • 阻断移动到自身、移动到子树、跨页面移动。
  • 返回 from/to parent/order。

验证命令:

cargo test -p mnote-web block_move_after
cargo test -p mnote-editor-core command_executor

真实页面 smoke

  • 新建 TEST-AI-BLOCK-MOVE-<timestamp> 页面,包含三段普通段落。
  • dry-run 移动第三段到第一段后。
  • 正式执行同父级移动。
  • 截图证明顺序为第一段、第三段、第二段。
  • mnote.doc.fetch 回读顺序一致。
  • 对标题带子块、列表项、表格、mindmap 执行 move dry-run,必须返回 blocked。

通过标准:

  • moving block id 保持不变。
  • 同父级顺序正确。
  • 复杂块不被误移动。

10. Phase 8UI 与 Review Mode

目标:

AI 写入可解释、可确认,不把 preview 当持久审阅事实。

任务:

  • Hermes tool event UI 展示 plan/diff/warnings/risk
  • page.save 标记为粗粒度高风险兜底。
  • block.replace/insert_after/move_after 展示 changedBlocks。
  • manifest annotations 能区分只读 / 粗粒度破坏性写入 / selectionEffect / runtimeOwner / writeOwnermnote.page.save 在 manifest 中为 destructive=trueapprovalMode=yolo,不作为精确块编辑主入口。证据:tmp/page-block-ai-context-format-smoke/mp8ddr4n.json
  • 本地 preview/suggestion 与持久 comment/tracked-change 分开。
  • 协作可见审阅必须另走正式 comment/history/tracked-change 设计。

通过标准:

  • 用户能看到 AI 将改哪个 block。
  • blocked=true 的工具调用不会出现写入按钮。
  • preview 不写入正式 comment/history。

11. DONE 条件

本 checklist 不能移动到 done/,直到:

  • Phase 1 到 Phase 6 全部完成。
  • Phase 7 至少完成 dry-run 和阻断规则;若真实 move 未完成,7-9 必须仍标注受限。
  • 每个写工具都有真实页面 smoke 证据。
  • 失败项已经写入 bugs/07-ai/process/ 或真实 owner 分类。
  • mnote.page.save 不再被任何设计描述为精确块编辑主入口。