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

419 lines
19 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.
# 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.fetch``mnote.doc.find``mnote.block.fetch` 已接入 Hermes tool manifest 与 dispatch。
- `mnote.doc.plan_update` 已提供块级 dry-run 计划和移动阻断诊断。
- `mnote.block.replace``mnote.block.insert_after``mnote.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_3``documentId=tree_1778915893346_1``suffix=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 块插件第一段 mp80lbze``Hermes 块插件第三段 mp80lbze``Hermes 插件插入段 mp80lbze``Hermes 插件替换第二段 mp80lbze``errors=[]`
- 修复点: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/text``mnote.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=788ms``usedFastWorkflow=true``usedHermesRun=false`;后端日志 `operation_source=local_rule``model_ms=0``apply_ms=42``total_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=selection``format=page_xml/text` 已进入工具与 PageAIContextBuilder 首版。
- PageAIIntentParser / PageAIOperationPlanner / PageAIOperationValidator / PageAIApplyController 仍需继续建设;当前本地 planner 只覆盖低歧义文本块增删改,不应被视为完整 AI 编辑 runtime。
执行顺序固定为:
```text
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:设计与基线冻结
- [x] `5-13` 已冻结 block identity、command、Tiptap boundary。
- [x] `2-1` 已冻结 `documents.content``blocks` 表、Page Aggregate、revision/conflict key 的关系。
- [x] `7-9` 已更新 Tiptap AI Toolkit 对照,不再写成“Tiptap 没有官方 AI 文档工具”。
- [x] `7-9` 明确 `mnote.page.save` 是粗粒度兜底,不是精确块工具。
- [x] 当前 reference code 已在 `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-docs``tiptap-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 读到稳定身份和冲突辅助信息。
任务:
- [x] `PageBody` 增加 `blockDocument` 或等价稳定字段。
- [x]`documents.content` 生成 `EditorBlockDocument`
- [x] 从 Tiptap JSON 生成 `EditorBlockDocument` 的 bridge 测试覆盖当前主类型。
- [x] 每个块输出 `blockId/type/text/attrs/children/parentBlockId/order/path/depth/revisionRef/editable`
- [x] 对无 id legacy block 生成稳定迁移策略或 warning。
- [x] 复杂块输出 `editable=false` 或受限能力。
验证命令:
```bash
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`
- [ ] 断言所有普通可编辑块有 `blockId``revisionRef`
通过标准:
- [ ] 页面刷新后 block ids 不变化。
- [ ] projection 中 `blockCount` 与页面可编辑块数量大体一致;复杂块允许受限但必须有 warning。
---
## 4. Phase 2`mnote.doc.fetch` / `mnote.doc.find`
目标:
> AI 能先读取和定位,不需要猜整页 `content` shape。
任务:
- [x] tool manifest 增加 `mnote.doc.fetch`
- [x] tool manifest 增加 `mnote.doc.find`
- [x] `doc.fetch` 支持 `scope=full/outline/keyword/block/selection`
- [x] `doc.fetch` 支持 `detail=simple/with_ids/full`
- [x] `doc.find` 支持按 text/type/blockId 查找。
- [x] 返回 page `revision/conflictDetectionKey`
- [x] 返回可直接传入 `block.fetch/replace/insert_after``blockId`
验证命令:
```bash
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 证据:
- [x] `mnote.doc.fetch scope=selection selectedBlockIds=["p_2"] format=page_xml` 读取真实页面选区上下文,只返回 `p_2`,返回 `schema=mnote.page_ai_context.v1``allowedTargetBlockIds=["p_2"]``revision/conflictDetectionKey` 和 block `revisionRef`。证据:`tmp/page-block-ai-context-format-smoke/mp8ddr4n.json`
- [x] `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 3`mnote.block.fetch`
目标:
> AI 能读取单块、子块和同父级上下文,形成写入前确认。
任务:
- [x] tool manifest 增加 `mnote.block.fetch`
- [x] 支持 `includeChildren`
- [x] 支持 `contextBefore/contextAfter`
- [x] 支持 `format=json/markdown/page_xml/text`
- [x] 返回 `revisionRef``editable``unsupportedReason`
- [x] 不存在 block 返回 `mnote_block_not_found`
验证命令:
```bash
cargo test -p mnote-web block_fetch
```
真实页面 smoke
- [ ]`doc.find` 结果选择一个段落 block。
- [ ]`mnote.block.fetch includeChildren=true contextBefore=1 contextAfter=1`
- [ ] 断言 before/after 只来自同父级。
补充 smoke 证据:
- [x] `mnote.block.fetch blockId=p_2 format=page_xml/text contextBefore=1 contextAfter=1` 返回目标块 `p_2``revisionRef` 与同父级 before/after `p_1/p_3`。证据:`tmp/page-block-ai-context-format-smoke/mp8ddr4n.json`
通过标准:
- [x] block 文本与页面显示一致。
- [ ] `revisionRef` 可被后续 dry-run 使用。
- [ ] 复杂块不会伪装成完全可编辑。
---
## 6. Phase 4`mnote.doc.plan_update`
目标:
> 所有写入先 dry-run,返回 diff、warnings、risk,不改变页面。
任务:
- [x] tool manifest 增加 `mnote.doc.plan_update`
- [x] 支持 `command=block_replace`
- [x] 支持 `command=block_insert_after`
- [x] 支持 `command=block_move_after` dry-run。
- [ ] 支持 `command=str_replace` 且多重匹配阻断。(当前只有基础 plan,仍需多重匹配阻断。)
- [x] 缺少 `revision/conflictDetectionKey` 时只允许 dry-run。
- [x] 返回 `planId``diff``warnings``risk``blocked`
验证命令:
```bash
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 5`mnote.block.replace`
目标:
> 第一条最小精确块写入闭环。
任务:
- [x] tool manifest 增加 `mnote.block.replace`
- [x] 输入必须包含 `blockId/revision/conflictDetectionKey/idempotencyKey/dryRun`
- [x] `dryRun=true` 只返回 plan。
- [x] `dryRun=false` 生成 `EditorCommand::ReplaceBlock`
- [x] Rust 应用命令生成 canonical content。
- [x] 通过 `page.body.save -> documents:updateContent` 持久化。
- [x] 返回新 revision、changedBlocks、audit。
验证命令:
```bash
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 6`mnote.block.insert_after`
目标:
> AI 能在指定块后插入新块,并拿到新 block id。
任务:
- [x] tool manifest 增加 `mnote.block.insert_after`
- [x] 输入必须包含 `anchorBlockId/revision/conflictDetectionKey/idempotencyKey/dryRun`
- [x] 新块 id 由 Rust runtime 分配。
- [ ] 支持单块和最多 20 个普通块插入。(当前最小闭环为单块插入。)
- [x] 第一阶段支持 paragraph/heading/todo。
- [x] 返回 inserted block ids 和新 revision。
验证命令:
```bash
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 7`mnote.block.move_after`
目标:
> 第一阶段只开放同父级普通叶子块移动。
任务:
- [x] tool manifest 增加 `mnote.block.move_after`
- [x] `dryRun=true` 支持同父级叶子块 diff。
- [ ] `dryRun=false` 前先继续阻断所有复杂块。(已有同父级/叶子/类型/editable/self 阻断,复杂块矩阵 smoke 待补。)
- [x] 检查 `blockRevisionRef``anchorRevisionRef`
- [x] 阻断移动到自身、移动到子树、跨页面移动。
- [x] 返回 from/to parent/order。
验证命令:
```bash
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`
- [x] `page.save` 标记为粗粒度高风险兜底。
- [x] `block.replace/insert_after/move_after` 展示 changedBlocks。
- [x] manifest annotations 能区分只读 / 粗粒度破坏性写入 / selectionEffect / runtimeOwner / writeOwner`mnote.page.save` 在 manifest 中为 `destructive=true``approvalMode=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` 不再被任何设计描述为精确块编辑主入口。