Files
mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md
T
2026-05-16 12:34:48 +08:00

353 lines
11 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/process/5-13-page-block-identity-and-command-contract-v1.md`
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-1-page-block-storage-projection-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/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`
---
## 1. 目的
本 checklist 把 `7-9` 的路线图拆成可验收执行链,避免页面块 AI 工具停留在“工具名已设计、真实页面不可回读”的状态。
执行顺序固定为:
```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:设计与基线冻结
- [ ] `5-13` 已冻结 block identity、command、Tiptap boundary。
- [ ] `2-1` 已冻结 `documents.content``blocks` 表、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-docs``tiptap-main` 可读。
验收:
- [ ] `rg -n "Tiptap AI Toolkit|tiptapRead|tiptapEdit|UniqueID|_hash|blockDocument" design/05-editor-mainline/process design/02-convex-rust-long-term-architecture/process design/07-ai/process` 能找到对应设计。
---
## 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` 或受限能力。
验证命令:
```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。
任务:
- [ ] 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_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`
通过标准:
- [ ] 不读取浏览器 DOM。
- [ ] `doc.find` 返回的 block id 能被 `block.fetch` 读取。
- [ ] 大页面默认分块或限制输出,不默认返回无限正文。
---
## 5. Phase 3`mnote.block.fetch`
目标:
> AI 能读取单块、子块和同父级上下文,形成写入前确认。
任务:
- [ ] tool manifest 增加 `mnote.block.fetch`
- [ ] 支持 `includeChildren`
- [ ] 支持 `contextBefore/contextAfter`
- [ ] 支持 `format=json/markdown/page_xml/text`
- [ ] 返回 `revisionRef``editable``unsupportedReason`
- [ ] 不存在 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 只来自同父级。
通过标准:
- [ ] block 文本与页面显示一致。
- [ ] `revisionRef` 可被后续 dry-run 使用。
- [ ] 复杂块不会伪装成完全可编辑。
---
## 6. Phase 4`mnote.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` 且多重匹配阻断。
- [ ] 缺少 `revision/conflictDetectionKey` 时只允许 dry-run。
- [ ] 返回 `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`
目标:
> 第一条最小精确块写入闭环。
任务:
- [ ] 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。
验证命令:
```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。
任务:
- [ ] tool manifest 增加 `mnote.block.insert_after`
- [ ] 输入必须包含 `anchorBlockId/revision/conflictDetectionKey/idempotencyKey/dryRun`
- [ ] 新块 id 由 Rust runtime 分配。
- [ ] 支持单块和最多 20 个普通块插入。
- [ ] 第一阶段支持 paragraph/heading/todo。
- [ ] 返回 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`
目标:
> 第一阶段只开放同父级普通叶子块移动。
任务:
- [ ] tool manifest 增加 `mnote.block.move_after`
- [ ] `dryRun=true` 支持同父级叶子块 diff。
- [ ] `dryRun=false` 前先继续阻断所有复杂块。
- [ ] 检查 `blockRevisionRef``anchorRevisionRef`
- [ ] 阻断移动到自身、移动到子树、跨页面移动。
- [ ] 返回 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`
- [ ] `page.save` 标记为粗粒度高风险兜底。
- [ ] `block.replace/insert_after/move_after` 展示 changedBlocks。
- [ ] 本地 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` 不再被任何设计描述为精确块编辑主入口。